1/* 2 * Copyright (C) 2010 Apple Inc. All rights reserved. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26#include "config.h" 27#include "NPRuntimeObjectMap.h" 28 29#if ENABLE(NETSCAPE_PLUGIN_API) 30 31#include "JSNPObject.h" 32#include "NPJSObject.h" 33#include "NPRuntimeUtilities.h" 34#include "PluginView.h" 35#include "WebProcess.h" 36#include <JavaScriptCore/Completion.h> 37#include <JavaScriptCore/Error.h> 38#include <JavaScriptCore/JSLock.h> 39#include <JavaScriptCore/SourceCode.h> 40#include <JavaScriptCore/Strong.h> 41#include <JavaScriptCore/StrongInlines.h> 42#include <WebCore/AudioHardwareListener.h> 43#include <WebCore/DOMWrapperWorld.h> 44#include <WebCore/Frame.h> 45#include <WebCore/Page.h> 46#include <WebCore/PageThrottler.h> 47#include <WebCore/ScriptController.h> 48#include <wtf/NeverDestroyed.h> 49 50using namespace JSC; 51using namespace WebCore; 52 53namespace WebKit { 54 55 56NPRuntimeObjectMap::NPRuntimeObjectMap(PluginView* pluginView) 57 : m_pluginView(pluginView) 58 , m_finalizationTimer(RunLoop::main(), this, &NPRuntimeObjectMap::invalidateQueuedObjects) 59{ 60} 61 62NPRuntimeObjectMap::PluginProtector::PluginProtector(NPRuntimeObjectMap* npRuntimeObjectMap) 63{ 64 // If we're already in the plug-in view destructor, we shouldn't try to keep it alive. 65 if (!npRuntimeObjectMap->m_pluginView->isBeingDestroyed()) 66 m_pluginView = npRuntimeObjectMap->m_pluginView; 67} 68 69NPRuntimeObjectMap::PluginProtector::~PluginProtector() 70{ 71} 72 73NPObject* NPRuntimeObjectMap::getOrCreateNPObject(VM& vm, JSObject* jsObject) 74{ 75 // If this is a JSNPObject, we can just get its underlying NPObject. 76 if (jsObject->classInfo() == JSNPObject::info()) { 77 JSNPObject* jsNPObject = jsCast<JSNPObject*>(jsObject); 78 NPObject* npObject = jsNPObject->npObject(); 79 80 retainNPObject(npObject); 81 return npObject; 82 } 83 84 // First, check if we already know about this object. 85 if (NPJSObject* npJSObject = m_npJSObjects.get(jsObject)) { 86 retainNPObject(npJSObject); 87 return npJSObject; 88 } 89 90 NPJSObject* npJSObject = NPJSObject::create(vm, this, jsObject); 91 m_npJSObjects.set(jsObject, npJSObject); 92 93 return npJSObject; 94} 95 96void NPRuntimeObjectMap::npJSObjectDestroyed(NPJSObject* npJSObject) 97{ 98 // Remove the object from the map. 99 ASSERT(m_npJSObjects.contains(npJSObject->jsObject())); 100 m_npJSObjects.remove(npJSObject->jsObject()); 101} 102 103JSObject* NPRuntimeObjectMap::getOrCreateJSObject(JSGlobalObject* globalObject, NPObject* npObject) 104{ 105 // If this is an NPJSObject, we can just get the JSObject that it's wrapping. 106 if (NPJSObject::isNPJSObject(npObject)) 107 return NPJSObject::toNPJSObject(npObject)->jsObject(); 108 109 if (JSNPObject* jsNPObject = m_jsNPObjects.get(npObject)) 110 return jsNPObject; 111 112 JSNPObject* jsNPObject = JSNPObject::create(globalObject, this, npObject); 113 weakAdd(m_jsNPObjects, npObject, JSC::Weak<JSNPObject>(jsNPObject, this, npObject)); 114 return jsNPObject; 115} 116 117JSValue NPRuntimeObjectMap::convertNPVariantToJSValue(JSC::ExecState* exec, JSC::JSGlobalObject* globalObject, const NPVariant& variant) 118{ 119 switch (variant.type) { 120 case NPVariantType_Void: 121 return jsUndefined(); 122 123 case NPVariantType_Null: 124 return jsNull(); 125 126 case NPVariantType_Bool: 127 return jsBoolean(variant.value.boolValue); 128 129 case NPVariantType_Int32: 130 return jsNumber(variant.value.intValue); 131 132 case NPVariantType_Double: 133 return jsNumber(variant.value.doubleValue); 134 135 case NPVariantType_String: 136 return jsString(exec, String::fromUTF8WithLatin1Fallback(variant.value.stringValue.UTF8Characters, 137 variant.value.stringValue.UTF8Length)); 138 case NPVariantType_Object: 139 return getOrCreateJSObject(globalObject, variant.value.objectValue); 140 } 141 142 ASSERT_NOT_REACHED(); 143 return jsUndefined(); 144} 145 146void NPRuntimeObjectMap::convertJSValueToNPVariant(ExecState* exec, JSValue value, NPVariant& variant) 147{ 148 JSLockHolder lock(exec); 149 150 VOID_TO_NPVARIANT(variant); 151 152 if (value.isNull()) { 153 NULL_TO_NPVARIANT(variant); 154 return; 155 } 156 157 if (value.isUndefined()) { 158 VOID_TO_NPVARIANT(variant); 159 return; 160 } 161 162 if (value.isBoolean()) { 163 BOOLEAN_TO_NPVARIANT(value.toBoolean(exec), variant); 164 return; 165 } 166 167 if (value.isNumber()) { 168 DOUBLE_TO_NPVARIANT(value.toNumber(exec), variant); 169 return; 170 } 171 172 if (value.isString()) { 173 NPString npString = createNPString(value.toString(exec)->value(exec).utf8()); 174 STRINGN_TO_NPVARIANT(npString.UTF8Characters, npString.UTF8Length, variant); 175 return; 176 } 177 178 if (value.isObject()) { 179 NPObject* npObject = getOrCreateNPObject(exec->vm(), asObject(value)); 180 OBJECT_TO_NPVARIANT(npObject, variant); 181 return; 182 } 183 184 ASSERT_NOT_REACHED(); 185} 186 187bool NPRuntimeObjectMap::evaluate(NPObject* npObject, const String& scriptString, NPVariant* result) 188{ 189 Strong<JSGlobalObject> globalObject(this->globalObject()->vm(), this->globalObject()); 190 if (!globalObject) 191 return false; 192 193#if PLATFORM(COCOA) 194 if (m_pluginView && !m_pluginView->isBeingDestroyed()) { 195 if (Page* page = m_pluginView->frame()->page()) { 196 if (m_pluginView->audioHardwareActivity() != WebCore::AudioHardwareActivityType::IsInactive && page->pageThrottler()) 197 page->pageThrottler()->pluginDidEvaluateWhileAudioIsPlaying(); 198 } 199 } 200#endif 201 202 ExecState* exec = globalObject->globalExec(); 203 204 JSLockHolder lock(exec); 205 JSValue thisValue = getOrCreateJSObject(globalObject.get(), npObject); 206 207 JSValue resultValue = JSC::evaluate(exec, makeSource(scriptString), thisValue); 208 209 convertJSValueToNPVariant(exec, resultValue, *result); 210 return true; 211} 212 213void NPRuntimeObjectMap::invalidate() 214{ 215 Vector<NPJSObject*> npJSObjects; 216 copyValuesToVector(m_npJSObjects, npJSObjects); 217 218 // Deallocate all the object wrappers so we won't leak any JavaScript objects. 219 for (size_t i = 0; i < npJSObjects.size(); ++i) 220 deallocateNPObject(npJSObjects[i]); 221 222 // We shouldn't have any NPJSObjects left now. 223 ASSERT(m_npJSObjects.isEmpty()); 224 225 Vector<NPObject*> objects; 226 227 for (HashMap<NPObject*, JSC::Weak<JSNPObject>>::iterator ptr = m_jsNPObjects.begin(), end = m_jsNPObjects.end(); ptr != end; ++ptr) { 228 JSNPObject* jsNPObject = ptr->value.get(); 229 if (!jsNPObject) // Skip zombies. 230 continue; 231 objects.append(jsNPObject->leakNPObject()); 232 } 233 234 m_jsNPObjects.clear(); 235 236 for (size_t i = 0; i < objects.size(); ++i) 237 releaseNPObject(objects[i]); 238 239 // Deal with any objects that were scheduled for delayed destruction 240 if (m_npObjectsToFinalize.isEmpty()) 241 return; 242 ASSERT(m_finalizationTimer.isActive()); 243 m_finalizationTimer.stop(); 244 invalidateQueuedObjects(); 245} 246 247JSGlobalObject* NPRuntimeObjectMap::globalObject() const 248{ 249 Frame* frame = m_pluginView->frame(); 250 if (!frame) 251 return 0; 252 253 return frame->script().globalObject(pluginWorld()); 254} 255 256ExecState* NPRuntimeObjectMap::globalExec() const 257{ 258 JSGlobalObject* globalObject = this->globalObject(); 259 if (!globalObject) 260 return 0; 261 262 return globalObject->globalExec(); 263} 264 265static String& globalExceptionString() 266{ 267 static NeverDestroyed<String> exceptionString; 268 return exceptionString; 269} 270 271void NPRuntimeObjectMap::setGlobalException(const String& exceptionString) 272{ 273 globalExceptionString() = exceptionString; 274} 275 276void NPRuntimeObjectMap::moveGlobalExceptionToExecState(ExecState* exec) 277{ 278 if (globalExceptionString().isNull()) 279 return; 280 281 { 282 JSLockHolder lock(exec); 283 exec->vm().throwException(exec, createError(exec, globalExceptionString())); 284 } 285 286 globalExceptionString() = String(); 287} 288 289void NPRuntimeObjectMap::invalidateQueuedObjects() 290{ 291 ASSERT(m_npObjectsToFinalize.size()); 292 // We deliberately re-request m_npObjectsToFinalize.size() as custom dealloc 293 // functions may execute JS and so get more objects added to the dealloc queue 294 for (size_t i = 0; i < m_npObjectsToFinalize.size(); ++i) 295 deallocateNPObject(m_npObjectsToFinalize[i]); 296 m_npObjectsToFinalize.clear(); 297} 298 299void NPRuntimeObjectMap::addToInvalidationQueue(NPObject* npObject) 300{ 301 if (trySafeReleaseNPObject(npObject)) 302 return; 303 if (m_npObjectsToFinalize.isEmpty()) 304 m_finalizationTimer.startOneShot(0); 305 ASSERT(m_finalizationTimer.isActive()); 306 m_npObjectsToFinalize.append(npObject); 307} 308 309void NPRuntimeObjectMap::finalize(JSC::Handle<JSC::Unknown> handle, void* context) 310{ 311 JSNPObject* object = jsCast<JSNPObject*>(handle.get().asCell()); 312 weakRemove(m_jsNPObjects, static_cast<NPObject*>(context), object); 313 addToInvalidationQueue(object->leakNPObject()); 314} 315 316} // namespace WebKit 317 318#endif // ENABLE(NETSCAPE_PLUGIN_API) 319