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 28 using ppapi::CheckIdType; 29 using ppapi::MakeTypedId; 30 using ppapi::PPIdType; 31 using ppapi::ResourceTracker; 32 using blink::WebConsoleMessage; 33 using blink::WebLocalFrame; 34 using blink::WebPluginContainer; 35 using blink::WebString; 36 37 namespace content { 38 39 namespace { 40 41 typedef std::set<WebPluginContainer*> ContainerSet; 42 43 // Adds all WebPluginContainers associated with the given module to the set. 44 void 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 58 WebConsoleMessage::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 72 WebConsoleMessage 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 85 HostGlobals* HostGlobals::host_globals_ = NULL; 86 87 HostGlobals::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 97 HostGlobals::~HostGlobals() { 98 DCHECK(host_globals_ == this || !host_globals_); 99 host_globals_ = NULL; 100 } 101 102 ppapi::ResourceTracker* HostGlobals::GetResourceTracker() { 103 return &resource_tracker_; 104 } 105 106 ppapi::VarTracker* HostGlobals::GetVarTracker() { return &host_var_tracker_; } 107 108 ppapi::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 116 ppapi::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 122 ppapi::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 130 PP_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 137 std::string HostGlobals::GetCmdLine() { 138 return CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 139 switches::kPpapiFlashArgs); 140 } 141 142 void HostGlobals::PreCacheFontForFlash(const void* logfontw) { 143 // Not implemented in-process. 144 } 145 146 void 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 163 void 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 192 base::TaskRunner* HostGlobals::GetFileTaskRunner() { 193 return RenderThreadImpl::current()->GetFileThreadMessageLoopProxy().get(); 194 } 195 196 ppapi::MessageLoopShared* HostGlobals::GetCurrentMessageLoop() { return NULL; } 197 198 PP_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 217 void 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 228 PluginModule* 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 237 PP_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 258 void HostGlobals::InstanceDeleted(PP_Instance instance) { 259 resource_tracker_.DidDeleteInstance(instance); 260 host_var_tracker_.DidDeleteInstance(instance); 261 instance_map_.erase(instance); 262 } 263 264 void HostGlobals::InstanceCrashed(PP_Instance instance) { 265 resource_tracker_.DidDeleteInstance(instance); 266 host_var_tracker_.DidDeleteInstance(instance); 267 } 268 269 PepperPluginInstanceImpl* 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 278 bool HostGlobals::IsHostGlobals() const { return true; } 279 280 } // namespace content 281