1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "ppapi/proxy/flash_resource.h"
6
7#include <cmath>
8
9#include "base/containers/mru_cache.h"
10#include "base/debug/crash_logging.h"
11#include "base/lazy_instance.h"
12#include "base/time/time.h"
13#include "ppapi/c/pp_errors.h"
14#include "ppapi/c/private/ppb_flash.h"
15#include "ppapi/c/trusted/ppb_browser_font_trusted.h"
16#include "ppapi/proxy/plugin_dispatcher.h"
17#include "ppapi/proxy/plugin_globals.h"
18#include "ppapi/proxy/ppapi_messages.h"
19#include "ppapi/proxy/serialized_structs.h"
20#include "ppapi/shared_impl/ppapi_preferences.h"
21#include "ppapi/shared_impl/scoped_pp_var.h"
22#include "ppapi/shared_impl/time_conversion.h"
23#include "ppapi/shared_impl/var.h"
24#include "ppapi/thunk/enter.h"
25#include "ppapi/thunk/ppb_url_request_info_api.h"
26
27using ppapi::thunk::EnterResourceNoLock;
28
29namespace ppapi {
30namespace proxy {
31
32namespace {
33
34struct LocalTimeZoneOffsetEntry {
35  base::TimeTicks expiration;
36  double offset;
37};
38
39class LocalTimeZoneOffsetCache
40    : public base::MRUCache<PP_Time, LocalTimeZoneOffsetEntry> {
41 public:
42  LocalTimeZoneOffsetCache()
43      : base::MRUCache<PP_Time, LocalTimeZoneOffsetEntry>(kCacheSize) {}
44 private:
45  static const size_t kCacheSize = 100;
46};
47
48base::LazyInstance<LocalTimeZoneOffsetCache>::Leaky
49    g_local_time_zone_offset_cache = LAZY_INSTANCE_INITIALIZER;
50
51} //  namespace
52
53FlashResource::FlashResource(Connection connection,
54                             PP_Instance instance,
55                             PluginDispatcher* plugin_dispatcher)
56    : PluginResource(connection, instance),
57      plugin_dispatcher_(plugin_dispatcher) {
58  SendCreate(RENDERER, PpapiHostMsg_Flash_Create());
59  SendCreate(BROWSER, PpapiHostMsg_Flash_Create());
60}
61
62FlashResource::~FlashResource() {
63}
64
65thunk::PPB_Flash_Functions_API* FlashResource::AsPPB_Flash_Functions_API() {
66  return this;
67}
68
69PP_Var FlashResource::GetProxyForURL(PP_Instance instance,
70                                     const std::string& url) {
71  std::string proxy;
72  int32_t result = SyncCall<PpapiPluginMsg_Flash_GetProxyForURLReply>(RENDERER,
73      PpapiHostMsg_Flash_GetProxyForURL(url), &proxy);
74
75  if (result == PP_OK)
76    return StringVar::StringToPPVar(proxy);
77  return PP_MakeUndefined();
78}
79
80void FlashResource::UpdateActivity(PP_Instance instance) {
81  Post(BROWSER, PpapiHostMsg_Flash_UpdateActivity());
82}
83
84PP_Bool FlashResource::SetCrashData(PP_Instance instance,
85                                    PP_FlashCrashKey key,
86                                    PP_Var value) {
87  StringVar* url_string_var(StringVar::FromPPVar(value));
88  if (!url_string_var)
89    return PP_FALSE;
90  switch (key) {
91    case PP_FLASHCRASHKEY_URL: {
92      PluginGlobals::Get()->SetActiveURL(url_string_var->value());
93      return PP_TRUE;
94    }
95    case PP_FLASHCRASHKEY_RESOURCE_URL: {
96      base::debug::SetCrashKeyValue("subresource_url", url_string_var->value());
97      return PP_TRUE;
98    }
99  }
100  return PP_FALSE;
101}
102
103double FlashResource::GetLocalTimeZoneOffset(PP_Instance instance,
104                                             PP_Time t) {
105  LocalTimeZoneOffsetCache& cache = g_local_time_zone_offset_cache.Get();
106
107  // Get the minimum PP_Time value that shares the same minute as |t|.
108  // Use cached offset if cache hasn't expired and |t| is in the same minute as
109  // the time for the cached offset (assume offsets change on minute
110  // boundaries).
111  PP_Time t_minute_base = floor(t / 60.0) * 60.0;
112  LocalTimeZoneOffsetCache::iterator iter = cache.Get(t_minute_base);
113  base::TimeTicks now = base::TimeTicks::Now();
114  if (iter != cache.end() && now < iter->second.expiration)
115    return iter->second.offset;
116
117  // Cache the local offset for ten seconds, since it's slow on XP and Linux.
118  // Note that TimeTicks does not continue counting across sleep/resume on all
119  // platforms. This may be acceptable for 10 seconds, but if in the future this
120  // is changed to one minute or more, then we should consider using base::Time.
121  const int64 kMaxCachedLocalOffsetAgeInSeconds = 10;
122  base::TimeDelta expiration_delta =
123      base::TimeDelta::FromSeconds(kMaxCachedLocalOffsetAgeInSeconds);
124
125  LocalTimeZoneOffsetEntry cache_entry;
126  cache_entry.expiration = now + expiration_delta;
127  cache_entry.offset = 0.0;
128
129  // We can't do the conversion here on Linux because the localtime calls
130  // require filesystem access prohibited by the sandbox.
131  // TODO(shess): Figure out why OSX needs the access, the sandbox warmup should
132  // handle it.  http://crbug.com/149006
133#if defined(OS_LINUX) || defined(OS_MACOSX)
134  int32_t result = SyncCall<PpapiPluginMsg_Flash_GetLocalTimeZoneOffsetReply>(
135      BROWSER,
136      PpapiHostMsg_Flash_GetLocalTimeZoneOffset(PPTimeToTime(t)),
137      &cache_entry.offset);
138  if (result != PP_OK)
139    cache_entry.offset = 0.0;
140#else
141  cache_entry.offset = PPGetLocalTimeZoneOffset(PPTimeToTime(t));
142#endif
143
144  cache.Put(t_minute_base, cache_entry);
145  return cache_entry.offset;
146}
147
148PP_Var FlashResource::GetSetting(PP_Instance instance,
149                                 PP_FlashSetting setting) {
150  switch (setting) {
151    case PP_FLASHSETTING_3DENABLED:
152      return PP_MakeBool(PP_FromBool(
153          plugin_dispatcher_->preferences().is_3d_supported));
154    case PP_FLASHSETTING_INCOGNITO:
155      return PP_MakeBool(PP_FromBool(plugin_dispatcher_->incognito()));
156    case PP_FLASHSETTING_STAGE3DENABLED:
157      return PP_MakeBool(PP_FromBool(
158          plugin_dispatcher_->preferences().is_stage3d_supported));
159    case PP_FLASHSETTING_STAGE3DBASELINEENABLED:
160      return PP_MakeBool(PP_FromBool(
161          plugin_dispatcher_->preferences().is_stage3d_baseline_supported));
162    case PP_FLASHSETTING_LANGUAGE:
163      return StringVar::StringToPPVar(
164          PluginGlobals::Get()->GetUILanguage());
165    case PP_FLASHSETTING_NUMCORES:
166      return PP_MakeInt32(
167          plugin_dispatcher_->preferences().number_of_cpu_cores);
168    case PP_FLASHSETTING_LSORESTRICTIONS: {
169      int32_t restrictions;
170      int32_t result =
171          SyncCall<PpapiPluginMsg_Flash_GetLocalDataRestrictionsReply>(BROWSER,
172              PpapiHostMsg_Flash_GetLocalDataRestrictions(), &restrictions);
173      if (result != PP_OK)
174        return PP_MakeInt32(PP_FLASHLSORESTRICTIONS_NONE);
175      return PP_MakeInt32(restrictions);
176    }
177  }
178  return PP_MakeUndefined();
179}
180
181void FlashResource::SetInstanceAlwaysOnTop(PP_Instance instance,
182                                           PP_Bool on_top) {
183  Post(RENDERER, PpapiHostMsg_Flash_SetInstanceAlwaysOnTop(PP_ToBool(on_top)));
184}
185
186PP_Bool FlashResource::DrawGlyphs(
187    PP_Instance instance,
188    PP_Resource pp_image_data,
189    const PP_BrowserFont_Trusted_Description* font_desc,
190    uint32_t color,
191    const PP_Point* position,
192    const PP_Rect* clip,
193    const float transformation[3][3],
194    PP_Bool allow_subpixel_aa,
195    uint32_t glyph_count,
196    const uint16_t glyph_indices[],
197    const PP_Point glyph_advances[]) {
198  EnterResourceNoLock<thunk::PPB_ImageData_API> enter(pp_image_data, true);
199  if (enter.failed())
200    return PP_FALSE;
201  // The instance parameter isn't strictly necessary but we check that it
202  // matches anyway.
203  if (enter.resource()->pp_instance() != instance)
204    return PP_FALSE;
205
206  PPBFlash_DrawGlyphs_Params params;
207  params.image_data = enter.resource()->host_resource();
208  params.font_desc.SetFromPPBrowserFontDescription(*font_desc);
209  params.color = color;
210  params.position = *position;
211  params.clip = *clip;
212  for (int i = 0; i < 3; i++) {
213    for (int j = 0; j < 3; j++)
214      params.transformation[i][j] = transformation[i][j];
215  }
216  params.allow_subpixel_aa = allow_subpixel_aa;
217
218  params.glyph_indices.insert(params.glyph_indices.begin(),
219                              &glyph_indices[0],
220                              &glyph_indices[glyph_count]);
221  params.glyph_advances.insert(params.glyph_advances.begin(),
222                               &glyph_advances[0],
223                               &glyph_advances[glyph_count]);
224
225  // This has to be synchronous because the caller may want to composite on
226  // top of the resulting text after the call is complete.
227  int32_t result = SyncCall<IPC::Message>(RENDERER,
228      PpapiHostMsg_Flash_DrawGlyphs(params));
229  return PP_FromBool(result == PP_OK);
230}
231
232int32_t FlashResource::Navigate(PP_Instance instance,
233                                PP_Resource request_info,
234                                const char* target,
235                                PP_Bool from_user_action) {
236  EnterResourceNoLock<thunk::PPB_URLRequestInfo_API> enter(request_info,
237                                                                  true);
238  if (enter.failed())
239    return PP_ERROR_BADRESOURCE;
240  return SyncCall<IPC::Message>(RENDERER, PpapiHostMsg_Flash_Navigate(
241      enter.object()->GetData(), target, PP_ToBool(from_user_action)));
242}
243
244PP_Bool FlashResource::IsRectTopmost(PP_Instance instance,
245                                     const PP_Rect* rect) {
246  int32_t result = SyncCall<IPC::Message>(RENDERER,
247      PpapiHostMsg_Flash_IsRectTopmost(*rect));
248  return PP_FromBool(result == PP_OK);
249}
250
251void FlashResource::InvokePrinting(PP_Instance instance) {
252  Post(RENDERER, PpapiHostMsg_Flash_InvokePrinting());
253}
254
255}  // namespace proxy
256}  // namespace ppapi
257