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 "content/renderer/pepper/host_globals.h"
6
7#include <limits>
8
9#include "base/command_line.h"
10#include "base/logging.h"
11#include "base/rand_util.h"
12#include "base/strings/utf_string_conversions.h"
13#include "base/task_runner.h"
14#include "content/public/common/content_switches.h"
15#include "content/renderer/pepper/pepper_plugin_instance_impl.h"
16#include "content/renderer/pepper/plugin_module.h"
17#include "content/renderer/render_thread_impl.h"
18#include "ppapi/shared_impl/api_id.h"
19#include "ppapi/shared_impl/id_assignment.h"
20#include "ppapi/shared_impl/proxy_lock.h"
21#include "third_party/WebKit/public/platform/WebString.h"
22#include "third_party/WebKit/public/web/WebConsoleMessage.h"
23#include "third_party/WebKit/public/web/WebDocument.h"
24#include "third_party/WebKit/public/web/WebElement.h"
25#include "third_party/WebKit/public/web/WebLocalFrame.h"
26#include "third_party/WebKit/public/web/WebPluginContainer.h"
27
28using ppapi::CheckIdType;
29using ppapi::MakeTypedId;
30using ppapi::PPIdType;
31using ppapi::ResourceTracker;
32using blink::WebConsoleMessage;
33using blink::WebLocalFrame;
34using blink::WebPluginContainer;
35using blink::WebString;
36
37namespace content {
38
39namespace {
40
41typedef std::set<WebPluginContainer*> ContainerSet;
42
43// Adds all WebPluginContainers associated with the given module to the set.
44void GetAllContainersForModule(PluginModule* module, ContainerSet* containers) {
45  const PluginModule::PluginInstanceSet& instances = module->GetAllInstances();
46  for (PluginModule::PluginInstanceSet::const_iterator i = instances.begin();
47       i != instances.end();
48       ++i) {
49    WebPluginContainer* container = (*i)->container();
50    // If "Delete" is called on an instance, the instance sets its container to
51    // NULL, but the instance may actually outlive its container. Callers of
52    // GetAllContainersForModule only want to know about valid containers.
53    if (container)
54      containers->insert(container);
55  }
56}
57
58WebConsoleMessage::Level LogLevelToWebLogLevel(PP_LogLevel level) {
59  switch (level) {
60    case PP_LOGLEVEL_TIP:
61      return WebConsoleMessage::LevelDebug;
62    case PP_LOGLEVEL_LOG:
63      return WebConsoleMessage::LevelLog;
64    case PP_LOGLEVEL_WARNING:
65      return WebConsoleMessage::LevelWarning;
66    case PP_LOGLEVEL_ERROR:
67    default:
68      return WebConsoleMessage::LevelError;
69  }
70}
71
72WebConsoleMessage MakeLogMessage(PP_LogLevel level,
73                                 const std::string& source,
74                                 const std::string& message) {
75  std::string result = source;
76  if (!result.empty())
77    result.append(": ");
78  result.append(message);
79  return WebConsoleMessage(LogLevelToWebLogLevel(level),
80                           WebString(base::UTF8ToUTF16(result)));
81}
82
83}  // namespace
84
85HostGlobals* HostGlobals::host_globals_ = NULL;
86
87HostGlobals::HostGlobals()
88    : ppapi::PpapiGlobals(),
89      resource_tracker_(ResourceTracker::SINGLE_THREADED) {
90  DCHECK(!host_globals_);
91  host_globals_ = this;
92  // We do not support calls off of the main thread on the host side, and thus
93  // do not lock.
94  ppapi::ProxyLock::DisableLocking();
95}
96
97HostGlobals::~HostGlobals() {
98  DCHECK(host_globals_ == this || !host_globals_);
99  host_globals_ = NULL;
100}
101
102ppapi::ResourceTracker* HostGlobals::GetResourceTracker() {
103  return &resource_tracker_;
104}
105
106ppapi::VarTracker* HostGlobals::GetVarTracker() { return &host_var_tracker_; }
107
108ppapi::CallbackTracker* HostGlobals::GetCallbackTrackerForInstance(
109    PP_Instance instance) {
110  InstanceMap::iterator found = instance_map_.find(instance);
111  if (found == instance_map_.end())
112    return NULL;
113  return found->second->module()->GetCallbackTracker().get();
114}
115
116ppapi::thunk::PPB_Instance_API* HostGlobals::GetInstanceAPI(
117    PP_Instance instance) {
118  // The InstanceAPI is just implemented by the PluginInstance object.
119  return GetInstance(instance);
120}
121
122ppapi::thunk::ResourceCreationAPI* HostGlobals::GetResourceCreationAPI(
123    PP_Instance pp_instance) {
124  PepperPluginInstanceImpl* instance = GetInstance(pp_instance);
125  if (!instance)
126    return NULL;
127  return &instance->resource_creation();
128}
129
130PP_Module HostGlobals::GetModuleForInstance(PP_Instance instance) {
131  PepperPluginInstanceImpl* inst = GetInstance(instance);
132  if (!inst)
133    return 0;
134  return inst->module()->pp_module();
135}
136
137std::string HostGlobals::GetCmdLine() {
138  return CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
139      switches::kPpapiFlashArgs);
140}
141
142void HostGlobals::PreCacheFontForFlash(const void* logfontw) {
143  // Not implemented in-process.
144}
145
146void HostGlobals::LogWithSource(PP_Instance instance,
147                                PP_LogLevel level,
148                                const std::string& source,
149                                const std::string& value) {
150  PepperPluginInstanceImpl* instance_object =
151      HostGlobals::Get()->GetInstance(instance);
152  if (instance_object) {
153    instance_object->container()
154        ->element()
155        .document()
156        .frame()
157        ->addMessageToConsole(MakeLogMessage(level, source, value));
158  } else {
159    BroadcastLogWithSource(0, level, source, value);
160  }
161}
162
163void HostGlobals::BroadcastLogWithSource(PP_Module pp_module,
164                                         PP_LogLevel level,
165                                         const std::string& source,
166                                         const std::string& value) {
167  // Get the unique containers associated with the broadcast. This prevents us
168  // from sending the same message to the same console when there are two
169  // instances on the page.
170  ContainerSet containers;
171  PluginModule* module = GetModule(pp_module);
172  if (module) {
173    GetAllContainersForModule(module, &containers);
174  } else {
175    // Unknown module, get containers for all modules.
176    for (ModuleMap::const_iterator i = module_map_.begin();
177         i != module_map_.end();
178         ++i) {
179      GetAllContainersForModule(i->second, &containers);
180    }
181  }
182
183  WebConsoleMessage message = MakeLogMessage(level, source, value);
184  for (ContainerSet::iterator i = containers.begin(); i != containers.end();
185       ++i) {
186    WebLocalFrame* frame = (*i)->element().document().frame();
187    if (frame)
188      frame->addMessageToConsole(message);
189  }
190}
191
192base::TaskRunner* HostGlobals::GetFileTaskRunner() {
193  return RenderThreadImpl::current()->GetFileThreadMessageLoopProxy().get();
194}
195
196ppapi::MessageLoopShared* HostGlobals::GetCurrentMessageLoop() { return NULL; }
197
198PP_Module HostGlobals::AddModule(PluginModule* module) {
199#ifndef NDEBUG
200  // Make sure we're not adding one more than once.
201  for (ModuleMap::const_iterator i = module_map_.begin();
202       i != module_map_.end();
203       ++i)
204    DCHECK(i->second != module);
205#endif
206
207  // See AddInstance.
208  PP_Module new_module;
209  do {
210    new_module = MakeTypedId(static_cast<PP_Module>(base::RandUint64()),
211                             ppapi::PP_ID_TYPE_MODULE);
212  } while (!new_module || module_map_.find(new_module) != module_map_.end());
213  module_map_[new_module] = module;
214  return new_module;
215}
216
217void HostGlobals::ModuleDeleted(PP_Module module) {
218  DLOG_IF(ERROR, !CheckIdType(module, ppapi::PP_ID_TYPE_MODULE))
219      << module << " is not a PP_Module.";
220  ModuleMap::iterator found = module_map_.find(module);
221  if (found == module_map_.end()) {
222    NOTREACHED();
223    return;
224  }
225  module_map_.erase(found);
226}
227
228PluginModule* HostGlobals::GetModule(PP_Module module) {
229  DLOG_IF(ERROR, !CheckIdType(module, ppapi::PP_ID_TYPE_MODULE))
230      << module << " is not a PP_Module.";
231  ModuleMap::iterator found = module_map_.find(module);
232  if (found == module_map_.end())
233    return NULL;
234  return found->second;
235}
236
237PP_Instance HostGlobals::AddInstance(PepperPluginInstanceImpl* instance) {
238  DCHECK(instance_map_.find(instance->pp_instance()) == instance_map_.end());
239
240  // Use a random number for the instance ID. This helps prevent some
241  // accidents. See also AddModule below.
242  //
243  // Need to make sure the random number isn't a duplicate or 0.
244  PP_Instance new_instance;
245  do {
246    new_instance = MakeTypedId(static_cast<PP_Instance>(base::RandUint64()),
247                               ppapi::PP_ID_TYPE_INSTANCE);
248  } while (!new_instance ||
249           instance_map_.find(new_instance) != instance_map_.end() ||
250           !instance->module()->ReserveInstanceID(new_instance));
251
252  instance_map_[new_instance] = instance;
253
254  resource_tracker_.DidCreateInstance(new_instance);
255  return new_instance;
256}
257
258void HostGlobals::InstanceDeleted(PP_Instance instance) {
259  resource_tracker_.DidDeleteInstance(instance);
260  host_var_tracker_.DidDeleteInstance(instance);
261  instance_map_.erase(instance);
262}
263
264void HostGlobals::InstanceCrashed(PP_Instance instance) {
265  resource_tracker_.DidDeleteInstance(instance);
266  host_var_tracker_.DidDeleteInstance(instance);
267}
268
269PepperPluginInstanceImpl* HostGlobals::GetInstance(PP_Instance instance) {
270  DLOG_IF(ERROR, !CheckIdType(instance, ppapi::PP_ID_TYPE_INSTANCE))
271      << instance << " is not a PP_Instance.";
272  InstanceMap::iterator found = instance_map_.find(instance);
273  if (found == instance_map_.end())
274    return NULL;
275  return found->second;
276}
277
278bool HostGlobals::IsHostGlobals() const { return true; }
279
280}  // namespace content
281