• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2008, 2009 Google Inc. All rights reserved.
3  * Copyright (C) 2009 Apple Inc. All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions are
7  * met:
8  *
9  *     * Redistributions of source code must retain the above copyright
10  * notice, this list of conditions and the following disclaimer.
11  *     * Redistributions in binary form must reproduce the above
12  * copyright notice, this list of conditions and the following disclaimer
13  * in the documentation and/or other materials provided with the
14  * distribution.
15  *     * Neither the name of Google Inc. nor the names of its
16  * contributors may be used to endorse or promote products derived from
17  * this software without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30  */
31 
32 #include "config.h"
33 #include "ScriptController.h"
34 
35 #if PLATFORM(CHROMIUM)
36 #include "ChromiumBridge.h"
37 #elif PLATFORM(ANDROID)
38 #include "PluginView.h"
39 #endif
40 
41 #include "CString.h"
42 #include "Document.h"
43 #include "DOMWindow.h"
44 #include "Event.h"
45 #include "EventListener.h"
46 #include "EventNames.h"
47 #include "Frame.h"
48 #include "Node.h"
49 #include "NotImplemented.h"
50 #include "npruntime_impl.h"
51 #include "npruntime_priv.h"
52 #include "NPV8Object.h"
53 #include "ScriptSourceCode.h"
54 #include "ScriptState.h"
55 #include "Widget.h"
56 #include "XSSAuditor.h"
57 
58 #include "V8Binding.h"
59 #include "V8NPObject.h"
60 #include "V8Proxy.h"
61 
62 namespace WebCore {
63 
setFlags(const char * string,int length)64 void ScriptController::setFlags(const char* string, int length)
65 {
66     v8::V8::SetFlagsFromString(string, length);
67 }
68 
retrieveFrameForEnteredContext()69 Frame* ScriptController::retrieveFrameForEnteredContext()
70 {
71     return V8Proxy::retrieveFrameForEnteredContext();
72 }
73 
retrieveFrameForCurrentContext()74 Frame* ScriptController::retrieveFrameForCurrentContext()
75 {
76     return V8Proxy::retrieveFrameForCurrentContext();
77 }
78 
isSafeScript(Frame * target)79 bool ScriptController::isSafeScript(Frame* target)
80 {
81     return V8Proxy::canAccessFrame(target, true);
82 }
83 
gcProtectJSWrapper(void * domObject)84 void ScriptController::gcProtectJSWrapper(void* domObject)
85 {
86     V8GCController::gcProtect(domObject);
87 }
88 
gcUnprotectJSWrapper(void * domObject)89 void ScriptController::gcUnprotectJSWrapper(void* domObject)
90 {
91     V8GCController::gcUnprotect(domObject);
92 }
93 
ScriptController(Frame * frame)94 ScriptController::ScriptController(Frame* frame)
95     : m_frame(frame)
96     , m_sourceURL(0)
97     , m_processingTimerCallback(false)
98     , m_paused(false)
99     , m_scriptState(new ScriptState(frame))
100     , m_proxy(new V8Proxy(frame))
101 #if ENABLE(NETSCAPE_PLUGIN_API)
102     , m_windowScriptNPObject(0)
103 #endif
104     , m_XSSAuditor(new XSSAuditor(frame))
105 {
106 }
107 
~ScriptController()108 ScriptController::~ScriptController()
109 {
110     m_proxy->disconnectFrame();
111 }
112 
clearScriptObjects()113 void ScriptController::clearScriptObjects()
114 {
115     PluginObjectMap::iterator it = m_pluginObjects.begin();
116     for (; it != m_pluginObjects.end(); ++it) {
117         _NPN_UnregisterObject(it->second);
118         _NPN_ReleaseObject(it->second);
119     }
120     m_pluginObjects.clear();
121 
122 #if ENABLE(NETSCAPE_PLUGIN_API)
123     if (m_windowScriptNPObject) {
124         // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
125         // script object properly.
126         // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
127         _NPN_DeallocateObject(m_windowScriptNPObject);
128         m_windowScriptNPObject = 0;
129     }
130 #endif
131 }
132 
updateSecurityOrigin()133 void ScriptController::updateSecurityOrigin()
134 {
135     m_proxy->updateSecurityOrigin();
136 }
137 
updatePlatformScriptObjects()138 void ScriptController::updatePlatformScriptObjects()
139 {
140     notImplemented();
141 }
142 
processingUserGesture() const143 bool ScriptController::processingUserGesture() const
144 {
145     Frame* activeFrame = V8Proxy::retrieveFrameForEnteredContext();
146     // No script is running, so it must be run by users.
147     if (!activeFrame)
148         return true;
149 
150     V8Proxy* activeProxy = activeFrame->script()->proxy();
151     LOCK_V8;
152     v8::HandleScope handleScope;
153     v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(activeFrame);
154     // FIXME: find all cases context can be empty:
155     //  1) JS is disabled;
156     //  2) page is NULL;
157     if (v8Context.IsEmpty())
158         return true;
159 
160     v8::Context::Scope scope(v8Context);
161 
162     v8::Handle<v8::Object> global = v8Context->Global();
163     v8::Handle<v8::Value> jsEvent = global->Get(v8::String::NewSymbol("event"));
164     Event* event = V8DOMWrapper::convertToNativeEvent(jsEvent);
165 
166     // Based on code from kjs_bindings.cpp.
167     // Note: This is more liberal than Firefox's implementation.
168     if (event) {
169         const AtomicString& type = event->type();
170         bool eventOk =
171             // mouse events
172             type == eventNames().clickEvent || type == eventNames().mousedownEvent || type == eventNames().mouseupEvent || type == eventNames().dblclickEvent
173             // keyboard events
174             || type == eventNames().keydownEvent || type == eventNames().keypressEvent || type == eventNames().keyupEvent
175             // other accepted events
176             || type == eventNames().selectEvent || type == eventNames().changeEvent || type == eventNames().focusEvent || type == eventNames().blurEvent || type == eventNames().submitEvent;
177 
178         if (eventOk)
179             return true;
180     } else if (activeProxy->inlineCode() && !activeProxy->timerCallback()) {
181         // This is the <a href="javascript:window.open('...')> case -> we let it through.
182         return true;
183     }
184 
185     // This is the <script>window.open(...)</script> case or a timer callback -> block it.
186     return false;
187 }
188 
evaluateInNewWorld(const Vector<ScriptSourceCode> & sources,int extensionGroup)189 void ScriptController::evaluateInNewWorld(const Vector<ScriptSourceCode>& sources, int extensionGroup)
190 {
191     m_proxy->evaluateInNewWorld(sources, extensionGroup);
192 }
193 
evaluateInNewContext(const Vector<ScriptSourceCode> & sources,int extensionGroup)194 void ScriptController::evaluateInNewContext(const Vector<ScriptSourceCode>& sources, int extensionGroup)
195 {
196     m_proxy->evaluateInNewContext(sources, extensionGroup);
197 }
198 
199 // Evaluate a script file in the environment of this proxy.
evaluate(const ScriptSourceCode & sourceCode)200 ScriptValue ScriptController::evaluate(const ScriptSourceCode& sourceCode)
201 {
202     LOCK_V8;
203     String sourceURL = sourceCode.url();
204 
205     if (sourceURL.isNull() && !m_XSSAuditor->canEvaluateJavaScriptURL(sourceCode.source())) {
206         // This JavaScript URL is not safe to be evaluated.
207         return ScriptValue();
208     }
209 
210     if (!sourceURL.isNull() && !m_XSSAuditor->canEvaluate(sourceCode.source())) {
211         // This script is not safe to be evaluated.
212         return ScriptValue();
213     }
214 
215     v8::HandleScope handleScope;
216     v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(m_proxy->frame());
217     if (v8Context.IsEmpty())
218         return ScriptValue();
219 
220     v8::Context::Scope scope(v8Context);
221 
222     RefPtr<Frame> protect(m_frame);
223 
224     v8::Local<v8::Value> object = m_proxy->evaluate(sourceCode, 0);
225 
226     // Evaluating the JavaScript could cause the frame to be deallocated
227     // so we start the keep alive timer here.
228     m_frame->keepAlive();
229 
230     if (object.IsEmpty() || object->IsUndefined())
231         return ScriptValue();
232 
233     return ScriptValue(object);
234 }
235 
setEventHandlerLineNumber(int lineNumber)236 void ScriptController::setEventHandlerLineNumber(int lineNumber)
237 {
238     m_proxy->setEventHandlerLineNumber(lineNumber);
239 }
240 
finishedWithEvent(Event * event)241 void ScriptController::finishedWithEvent(Event* event)
242 {
243     m_proxy->finishedWithEvent(event);
244 }
245 
246 // Create a V8 object with an interceptor of NPObjectPropertyGetter.
bindToWindowObject(Frame * frame,const String & key,NPObject * object)247 void ScriptController::bindToWindowObject(Frame* frame, const String& key, NPObject* object)
248 {
249     LOCK_V8;
250     v8::HandleScope handleScope;
251 
252     v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(frame);
253     if (v8Context.IsEmpty())
254         return;
255 
256     v8::Context::Scope scope(v8Context);
257 
258     v8::Handle<v8::Object> value = createV8ObjectForNPObject(object, 0);
259 
260     // Attach to the global object.
261     v8::Handle<v8::Object> global = v8Context->Global();
262     global->Set(v8String(key), value);
263 }
264 
collectGarbage()265 void ScriptController::collectGarbage()
266 {
267     LOCK_V8;
268     v8::HandleScope handleScope;
269     v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(m_proxy->frame());
270     if (v8Context.IsEmpty())
271         return;
272 
273     v8::Context::Scope scope(v8Context);
274     m_proxy->evaluate(ScriptSourceCode("if (window.gc) void(gc());"), 0);
275 }
276 
lowMemoryNotification()277 void ScriptController::lowMemoryNotification()
278 {
279     v8::V8::LowMemoryNotification();
280 }
281 
haveInterpreter() const282 bool ScriptController::haveInterpreter() const
283 {
284     return m_proxy->isContextInitialized();
285 }
286 
isEnabled() const287 bool ScriptController::isEnabled() const
288 {
289     return m_proxy->isEnabled();
290 }
291 
createScriptInstanceForWidget(Widget * widget)292 PassScriptInstance ScriptController::createScriptInstanceForWidget(Widget* widget)
293 {
294     ASSERT(widget);
295 
296 #if PLATFORM(CHROMIUM)
297     if (widget->isFrameView())
298         return 0;
299 
300     NPObject* npObject = ChromiumBridge::pluginScriptableObject(widget);
301     if (!npObject)
302         return 0;
303 
304 #elif PLATFORM(ANDROID)
305     if (!widget->isPluginView())
306         return 0;
307 
308     PluginView* pluginView = static_cast<PluginView*>(widget);
309     NPObject* npObject = pluginView->getNPObject();
310     if (!npObject)
311         return 0;
312 #endif
313 
314     // Frame Memory Management for NPObjects
315     // -------------------------------------
316     // NPObjects are treated differently than other objects wrapped by JS.
317     // NPObjects can be created either by the browser (e.g. the main
318     // window object) or by the plugin (the main plugin object
319     // for a HTMLEmbedElement). Further, unlike most DOM Objects, the frame
320     // is especially careful to ensure NPObjects terminate at frame teardown because
321     // if a plugin leaks a reference, it could leak its objects (or the browser's objects).
322     //
323     // The Frame maintains a list of plugin objects (m_pluginObjects)
324     // which it can use to quickly find the wrapped embed object.
325     //
326     // Inside the NPRuntime, we've added a few methods for registering
327     // wrapped NPObjects. The purpose of the registration is because
328     // javascript garbage collection is non-deterministic, yet we need to
329     // be able to tear down the plugin objects immediately. When an object
330     // is registered, javascript can use it. When the object is destroyed,
331     // or when the object's "owning" object is destroyed, the object will
332     // be un-registered, and the javascript engine must not use it.
333     //
334     // Inside the javascript engine, the engine can keep a reference to the
335     // NPObject as part of its wrapper. However, before accessing the object
336     // it must consult the _NPN_Registry.
337 
338     v8::Local<v8::Object> wrapper = createV8ObjectForNPObject(npObject, 0);
339 
340     // Track the plugin object. We've been given a reference to the object.
341     m_pluginObjects.set(widget, npObject);
342 
343     return V8ScriptInstance::create(wrapper);
344 }
345 
cleanupScriptObjectsForPlugin(void * nativeHandle)346 void ScriptController::cleanupScriptObjectsForPlugin(void* nativeHandle)
347 {
348     PluginObjectMap::iterator it = m_pluginObjects.find(nativeHandle);
349     if (it == m_pluginObjects.end())
350         return;
351     _NPN_UnregisterObject(it->second);
352     _NPN_ReleaseObject(it->second);
353     m_pluginObjects.remove(it);
354 }
355 
createNoScriptObject()356 static NPObject* createNoScriptObject()
357 {
358     notImplemented();
359     return 0;
360 }
361 
createScriptObject(Frame * frame)362 static NPObject* createScriptObject(Frame* frame)
363 {
364     LOCK_V8;
365     v8::HandleScope handleScope;
366     v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(frame);
367     if (v8Context.IsEmpty())
368         return createNoScriptObject();
369 
370     v8::Context::Scope scope(v8Context);
371     DOMWindow* window = frame->domWindow();
372     v8::Handle<v8::Value> global = V8DOMWrapper::convertToV8Object(V8ClassIndex::DOMWINDOW, window);
373     ASSERT(global->IsObject());
374     return npCreateV8ScriptObject(0, v8::Handle<v8::Object>::Cast(global), window);
375 }
376 
windowScriptNPObject()377 NPObject* ScriptController::windowScriptNPObject()
378 {
379     if (m_windowScriptNPObject)
380         return m_windowScriptNPObject;
381 
382     if (isEnabled()) {
383         // JavaScript is enabled, so there is a JavaScript window object.
384         // Return an NPObject bound to the window object.
385         m_windowScriptNPObject = createScriptObject(m_frame);
386         _NPN_RegisterObject(m_windowScriptNPObject, 0);
387     } else {
388         // JavaScript is not enabled, so we cannot bind the NPObject to the
389         // JavaScript window object. Instead, we create an NPObject of a
390         // different class, one which is not bound to a JavaScript object.
391         m_windowScriptNPObject = createNoScriptObject();
392     }
393     return m_windowScriptNPObject;
394 }
395 
createScriptObjectForPluginElement(HTMLPlugInElement * plugin)396 NPObject* ScriptController::createScriptObjectForPluginElement(HTMLPlugInElement* plugin)
397 {
398     // Can't create NPObjects when JavaScript is disabled.
399     if (!isEnabled())
400         return createNoScriptObject();
401 
402     LOCK_V8;
403     v8::HandleScope handleScope;
404     v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(m_frame);
405     if (v8Context.IsEmpty())
406         return createNoScriptObject();
407     v8::Context::Scope scope(v8Context);
408 
409     DOMWindow* window = m_frame->domWindow();
410     v8::Handle<v8::Value> v8plugin = V8DOMWrapper::convertToV8Object(V8ClassIndex::HTMLEMBEDELEMENT, plugin);
411     if (!v8plugin->IsObject())
412         return createNoScriptObject();
413 
414     return npCreateV8ScriptObject(0, v8::Handle<v8::Object>::Cast(v8plugin), window);
415 }
416 
417 
clearWindowShell()418 void ScriptController::clearWindowShell()
419 {
420     // V8 binding expects ScriptController::clearWindowShell only be called
421     // when a frame is loading a new page. V8Proxy::clearForNavigation
422     // creates a new context for the new page.
423     m_proxy->clearForNavigation();
424 }
425 
attachDebugger(void *)426 void ScriptController::attachDebugger(void*)
427 {
428     notImplemented();
429 }
430 
updateDocument()431 void ScriptController::updateDocument()
432 {
433     m_proxy->updateDocument();
434 }
435 
436 } // namespace WebCore
437