Move WebCore into Source
[WebKit-https.git] / Source / WebCore / bindings / v8 / ScriptController.cpp
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 #include "PlatformBridge.h"
36 #include "Document.h"
37 #include "ScriptCallStack.h"
38 #include "ScriptCallStackFactory.h"
39 #include "ScriptableDocumentParser.h"
40 #include "DOMWindow.h"
41 #include "Event.h"
42 #include "EventListener.h"
43 #include "EventNames.h"
44 #include "Frame.h"
45 #include "FrameLoaderClient.h"
46 #include "Node.h"
47 #include "NotImplemented.h"
48 #include "npruntime_impl.h"
49 #include "npruntime_priv.h"
50 #include "NPV8Object.h"
51 #include "ScriptSourceCode.h"
52 #include "Settings.h"
53 #include "UserGestureIndicator.h"
54 #include "V8Binding.h"
55 #include "V8BindingState.h"
56 #include "V8DOMWindow.h"
57 #include "V8Event.h"
58 #include "V8HiddenPropertyName.h"
59 #include "V8HTMLEmbedElement.h"
60 #include "V8IsolatedContext.h"
61 #include "V8NPObject.h"
62 #include "V8Proxy.h"
63 #include "Widget.h"
64 #include "XSSAuditor.h"
65 #include <wtf/StdLibExtras.h>
66 #include <wtf/text/CString.h>
67
68 #if PLATFORM(QT)
69 #include <QScriptEngine>
70 #endif
71
72 namespace WebCore {
73
74 void ScriptController::initializeThreading()
75 {
76     static bool initializedThreading = false;
77     if (!initializedThreading) {
78         WTF::initializeThreading();
79         WTF::initializeMainThread();
80         initializedThreading = true;
81     }
82 }
83
84 void ScriptController::setFlags(const char* string, int length)
85 {
86     v8::V8::SetFlagsFromString(string, length);
87 }
88
89 Frame* ScriptController::retrieveFrameForEnteredContext()
90 {
91     return V8Proxy::retrieveFrameForEnteredContext();
92 }
93
94 Frame* ScriptController::retrieveFrameForCurrentContext()
95 {
96     return V8Proxy::retrieveFrameForCurrentContext();
97 }
98
99 bool ScriptController::canAccessFromCurrentOrigin(Frame *frame)
100 {
101     return !v8::Context::InContext() || V8BindingSecurity::canAccessFrame(V8BindingState::Only(), frame, true);
102 }
103
104 bool ScriptController::isSafeScript(Frame* target)
105 {
106     return V8BindingSecurity::canAccessFrame(V8BindingState::Only(), target, true);
107 }
108
109 ScriptController::ScriptController(Frame* frame)
110     : m_frame(frame)
111     , m_sourceURL(0)
112     , m_inExecuteScript(false)
113     , m_processingTimerCallback(false)
114     , m_paused(false)
115     , m_allowPopupsFromPlugin(false)
116     , m_proxy(new V8Proxy(frame))
117 #if ENABLE(NETSCAPE_PLUGIN_API)
118     , m_windowScriptNPObject(0)
119 #endif
120     , m_XSSAuditor(new XSSAuditor(frame))
121 {
122 }
123
124 ScriptController::~ScriptController()
125 {
126     m_proxy->disconnectFrame();
127 }
128
129 void ScriptController::clearScriptObjects()
130 {
131     PluginObjectMap::iterator it = m_pluginObjects.begin();
132     for (; it != m_pluginObjects.end(); ++it) {
133         _NPN_UnregisterObject(it->second);
134         _NPN_ReleaseObject(it->second);
135     }
136     m_pluginObjects.clear();
137
138 #if ENABLE(NETSCAPE_PLUGIN_API)
139     if (m_windowScriptNPObject) {
140         // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
141         // script object properly.
142         // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
143         _NPN_DeallocateObject(m_windowScriptNPObject);
144         m_windowScriptNPObject = 0;
145     }
146 #endif
147 }
148
149 void ScriptController::updateSecurityOrigin()
150 {
151     m_proxy->windowShell()->updateSecurityOrigin();
152 }
153
154 void ScriptController::updatePlatformScriptObjects()
155 {
156     notImplemented();
157 }
158
159 bool ScriptController::processingUserGesture()
160 {
161     Frame* activeFrame = V8Proxy::retrieveFrameForEnteredContext();
162     // No script is running, so it is user-initiated unless the gesture stack
163     // explicitly says it is not.
164     if (!activeFrame)
165         return UserGestureIndicator::getUserGestureState() != DefinitelyNotProcessingUserGesture;
166
167     V8Proxy* activeProxy = activeFrame->script()->proxy();
168
169     v8::HandleScope handleScope;
170     v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(activeFrame);
171     // FIXME: find all cases context can be empty:
172     //  1) JS is disabled;
173     //  2) page is NULL;
174     if (v8Context.IsEmpty())
175         return true;
176
177     v8::Context::Scope scope(v8Context);
178
179     v8::Handle<v8::Object> global = v8Context->Global();
180     v8::Handle<v8::String> eventSymbol = V8HiddenPropertyName::event();
181     v8::Handle<v8::Value> jsEvent = global->GetHiddenValue(eventSymbol);
182     Event* event = V8DOMWrapper::isValidDOMObject(jsEvent) ? V8Event::toNative(v8::Handle<v8::Object>::Cast(jsEvent)) : 0;
183
184     // Based on code from JSC's ScriptController::processingUserGesture.
185     // Note: This is more liberal than Firefox's implementation.
186     if (event) {
187         // Event::fromUserGesture will return false when UserGestureIndicator::processingUserGesture() returns false.
188         return event->fromUserGesture();
189     }
190     // FIXME: We check the javascript anchor navigation from the last entered
191     // frame becuase it should only be initiated on the last entered frame in
192     // which execution began if it does happen.    
193     const String* sourceURL = activeFrame->script()->sourceURL();
194     if (sourceURL && sourceURL->isNull() && !activeProxy->timerCallback()) {
195         // This is the <a href="javascript:window.open('...')> case -> we let it through.
196         return true;
197     }
198     if (activeFrame->script()->allowPopupsFromPlugin())
199         return true;
200     // This is the <script>window.open(...)</script> case or a timer callback -> block it.
201     // Based on JSC version, use returned value of UserGestureIndicator::processingUserGesture for all other situations. 
202     return UserGestureIndicator::processingUserGesture();
203 }
204
205 bool ScriptController::anyPageIsProcessingUserGesture() const
206 {
207     // FIXME: is this right?
208     return ScriptController::processingUserGesture();
209 }
210
211 void ScriptController::evaluateInIsolatedWorld(unsigned worldID, const Vector<ScriptSourceCode>& sources)
212 {
213     m_proxy->evaluateInIsolatedWorld(worldID, sources, 0);
214 }
215
216 void ScriptController::evaluateInIsolatedWorld(unsigned worldID, const Vector<ScriptSourceCode>& sources, int extensionGroup)
217 {
218     m_proxy->evaluateInIsolatedWorld(worldID, sources, extensionGroup);
219 }
220
221 // Evaluate a script file in the environment of this proxy.
222 ScriptValue ScriptController::evaluate(const ScriptSourceCode& sourceCode, ShouldAllowXSS shouldAllowXSS)
223 {
224     String sourceURL = sourceCode.url();
225     const String* savedSourceURL = m_sourceURL;
226     m_sourceURL = &sourceURL;
227
228     if (shouldAllowXSS == DoNotAllowXSS && !m_XSSAuditor->canEvaluate(sourceCode.source())) {
229         // This script is not safe to be evaluated.
230         return ScriptValue();
231     }
232
233     v8::HandleScope handleScope;
234     v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(m_proxy->frame());
235     if (v8Context.IsEmpty())
236         return ScriptValue();
237
238     v8::Context::Scope scope(v8Context);
239
240     RefPtr<Frame> protect(m_frame);
241
242     v8::Local<v8::Value> object = m_proxy->evaluate(sourceCode, 0);
243
244     // Evaluating the JavaScript could cause the frame to be deallocated
245     // so we start the keep alive timer here.
246     m_frame->keepAlive();
247
248     m_sourceURL = savedSourceURL;
249
250     if (object.IsEmpty() || object->IsUndefined())
251         return ScriptValue();
252
253     return ScriptValue(object);
254 }
255
256 TextPosition0 ScriptController::eventHandlerPosition() const
257 {
258     ScriptableDocumentParser* parser = m_frame->document()->scriptableDocumentParser();
259     if (parser)
260         return parser->textPosition();
261     return TextPosition0::minimumPosition();
262 }
263
264 void ScriptController::finishedWithEvent(Event* event)
265 {
266     m_proxy->finishedWithEvent(event);
267 }
268
269 // Create a V8 object with an interceptor of NPObjectPropertyGetter.
270 void ScriptController::bindToWindowObject(Frame* frame, const String& key, NPObject* object)
271 {
272     v8::HandleScope handleScope;
273
274     v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(frame);
275     if (v8Context.IsEmpty())
276         return;
277
278     v8::Context::Scope scope(v8Context);
279
280     v8::Handle<v8::Object> value = createV8ObjectForNPObject(object, 0);
281
282     // Attach to the global object.
283     v8::Handle<v8::Object> global = v8Context->Global();
284     global->Set(v8String(key), value);
285 }
286
287 void ScriptController::collectGarbage()
288 {
289     v8::HandleScope handleScope;
290
291     v8::Persistent<v8::Context> v8Context = v8::Context::New();
292     if (v8Context.IsEmpty())
293         return;
294     {
295         v8::Context::Scope scope(v8Context);
296         v8::Local<v8::String> source = v8::String::New("if (gc) gc();");
297         v8::Local<v8::String> name = v8::String::New("gc");
298         v8::Handle<v8::Script> script = v8::Script::Compile(source, name);
299         if (!script.IsEmpty())
300             script->Run();
301     }
302     v8Context.Dispose();
303 }
304
305 void ScriptController::lowMemoryNotification()
306 {
307     v8::V8::LowMemoryNotification();
308 }
309
310 bool ScriptController::haveInterpreter() const
311 {
312     return m_proxy->windowShell()->isContextInitialized();
313 }
314
315 PassScriptInstance ScriptController::createScriptInstanceForWidget(Widget* widget)
316 {
317     ASSERT(widget);
318
319     if (widget->isFrameView())
320         return 0;
321
322     NPObject* npObject = PlatformBridge::pluginScriptableObject(widget);
323
324     if (!npObject)
325         return 0;
326
327     // Frame Memory Management for NPObjects
328     // -------------------------------------
329     // NPObjects are treated differently than other objects wrapped by JS.
330     // NPObjects can be created either by the browser (e.g. the main
331     // window object) or by the plugin (the main plugin object
332     // for a HTMLEmbedElement). Further, unlike most DOM Objects, the frame
333     // is especially careful to ensure NPObjects terminate at frame teardown because
334     // if a plugin leaks a reference, it could leak its objects (or the browser's objects).
335     //
336     // The Frame maintains a list of plugin objects (m_pluginObjects)
337     // which it can use to quickly find the wrapped embed object.
338     //
339     // Inside the NPRuntime, we've added a few methods for registering
340     // wrapped NPObjects. The purpose of the registration is because
341     // javascript garbage collection is non-deterministic, yet we need to
342     // be able to tear down the plugin objects immediately. When an object
343     // is registered, javascript can use it. When the object is destroyed,
344     // or when the object's "owning" object is destroyed, the object will
345     // be un-registered, and the javascript engine must not use it.
346     //
347     // Inside the javascript engine, the engine can keep a reference to the
348     // NPObject as part of its wrapper. However, before accessing the object
349     // it must consult the _NPN_Registry.
350
351     v8::Local<v8::Object> wrapper = createV8ObjectForNPObject(npObject, 0);
352
353     // Track the plugin object. We've been given a reference to the object.
354     m_pluginObjects.set(widget, npObject);
355
356     return V8ScriptInstance::create(wrapper);
357 }
358
359 void ScriptController::cleanupScriptObjectsForPlugin(Widget* nativeHandle)
360 {
361     PluginObjectMap::iterator it = m_pluginObjects.find(nativeHandle);
362     if (it == m_pluginObjects.end())
363         return;
364     _NPN_UnregisterObject(it->second);
365     _NPN_ReleaseObject(it->second);
366     m_pluginObjects.remove(it);
367 }
368
369 void ScriptController::getAllWorlds(Vector<DOMWrapperWorld*>& worlds)
370 {
371     worlds.append(mainThreadNormalWorld());
372 }
373
374 void ScriptController::evaluateInWorld(const ScriptSourceCode& source,
375                                        DOMWrapperWorld* world)
376 {
377     Vector<ScriptSourceCode> sources;
378     sources.append(source);
379     // FIXME: Get an ID from the world param.
380     evaluateInIsolatedWorld(0, sources);
381 }
382
383 static NPObject* createNoScriptObject()
384 {
385     notImplemented();
386     return 0;
387 }
388
389 static NPObject* createScriptObject(Frame* frame)
390 {
391     v8::HandleScope handleScope;
392     v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(frame);
393     if (v8Context.IsEmpty())
394         return createNoScriptObject();
395
396     v8::Context::Scope scope(v8Context);
397     DOMWindow* window = frame->domWindow();
398     v8::Handle<v8::Value> global = toV8(window);
399     ASSERT(global->IsObject());
400     return npCreateV8ScriptObject(0, v8::Handle<v8::Object>::Cast(global), window);
401 }
402
403 NPObject* ScriptController::windowScriptNPObject()
404 {
405     if (m_windowScriptNPObject)
406         return m_windowScriptNPObject;
407
408     if (canExecuteScripts(NotAboutToExecuteScript)) {
409         // JavaScript is enabled, so there is a JavaScript window object.
410         // Return an NPObject bound to the window object.
411         m_windowScriptNPObject = createScriptObject(m_frame);
412         _NPN_RegisterObject(m_windowScriptNPObject, 0);
413     } else {
414         // JavaScript is not enabled, so we cannot bind the NPObject to the
415         // JavaScript window object. Instead, we create an NPObject of a
416         // different class, one which is not bound to a JavaScript object.
417         m_windowScriptNPObject = createNoScriptObject();
418     }
419     return m_windowScriptNPObject;
420 }
421
422 NPObject* ScriptController::createScriptObjectForPluginElement(HTMLPlugInElement* plugin)
423 {
424     // Can't create NPObjects when JavaScript is disabled.
425     if (!canExecuteScripts(NotAboutToExecuteScript))
426         return createNoScriptObject();
427
428     v8::HandleScope handleScope;
429     v8::Handle<v8::Context> v8Context = V8Proxy::mainWorldContext(m_frame);
430     if (v8Context.IsEmpty())
431         return createNoScriptObject();
432     v8::Context::Scope scope(v8Context);
433
434     DOMWindow* window = m_frame->domWindow();
435     v8::Handle<v8::Value> v8plugin = toV8(static_cast<HTMLEmbedElement*>(plugin));
436     if (!v8plugin->IsObject())
437         return createNoScriptObject();
438
439     return npCreateV8ScriptObject(0, v8::Handle<v8::Object>::Cast(v8plugin), window);
440 }
441
442
443 void ScriptController::clearWindowShell(bool)
444 {
445     // V8 binding expects ScriptController::clearWindowShell only be called
446     // when a frame is loading a new page. V8Proxy::clearForNavigation
447     // creates a new context for the new page.
448     m_proxy->clearForNavigation();
449 }
450
451 #if ENABLE(INSPECTOR)
452 void ScriptController::setCaptureCallStackForUncaughtExceptions(bool value)
453 {
454     v8::V8::SetCaptureStackTraceForUncaughtExceptions(value, ScriptCallStack::maxCallStackSizeToCapture, stackTraceOptions);
455 }
456 #endif
457
458 void ScriptController::attachDebugger(void*)
459 {
460     notImplemented();
461 }
462
463 void ScriptController::updateDocument()
464 {
465     m_proxy->windowShell()->updateDocument();
466 }
467
468 void ScriptController::namedItemAdded(HTMLDocument* doc, const AtomicString& name)
469 {
470     m_proxy->windowShell()->namedItemAdded(doc, name);
471 }
472
473 void ScriptController::namedItemRemoved(HTMLDocument* doc, const AtomicString& name)
474 {
475     m_proxy->windowShell()->namedItemRemoved(doc, name);
476 }
477
478 } // namespace WebCore