fecde215296c4b729eef823efb43b29dcbcc16be
[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 "BindingState.h"
36 #include "ContentSecurityPolicy.h"
37 #include "DOMWindow.h"
38 #include "Document.h"
39 #include "Event.h"
40 #include "EventListener.h"
41 #include "EventNames.h"
42 #include "Frame.h"
43 #include "FrameLoaderClient.h"
44 #include "HistogramSupport.h"
45 #include "InspectorInstrumentation.h"
46 #include "NPObjectWrapper.h"
47 #include "NPV8Object.h"
48 #include "Node.h"
49 #include "NotImplemented.h"
50 #include "npruntime_impl.h"
51 #include "npruntime_priv.h"
52 #include "PlatformSupport.h"
53 #include "ScriptCallStack.h"
54 #include "ScriptCallStackFactory.h"
55 #include "ScriptRunner.h"
56 #include "ScriptSourceCode.h"
57 #include "ScriptableDocumentParser.h"
58 #include "SecurityOrigin.h"
59 #include "Settings.h"
60 #include "UserGestureIndicator.h"
61 #include "V8Binding.h"
62 #include "V8DOMWindow.h"
63 #include "V8Event.h"
64 #include "V8GCController.h"
65 #include "V8HiddenPropertyName.h"
66 #include "V8HTMLEmbedElement.h"
67 #include "V8NPObject.h"
68 #include "V8RecursionScope.h"
69 #include "Widget.h"
70 #include <wtf/CurrentTime.h>
71 #include <wtf/StdLibExtras.h>
72 #include <wtf/StringExtras.h>
73 #include <wtf/text/CString.h>
74 #include <wtf/text/StringBuilder.h>
75
76 #if PLATFORM(CHROMIUM)
77 #include "TraceEvent.h"
78 #endif
79
80 namespace WebCore {
81
82 void ScriptController::initializeThreading()
83 {
84     static bool initializedThreading = false;
85     if (!initializedThreading) {
86         WTF::initializeThreading();
87         WTF::initializeMainThread();
88         initializedThreading = true;
89     }
90 }
91
92 void ScriptController::setFlags(const char* string, int length)
93 {
94     v8::V8::SetFlagsFromString(string, length);
95 }
96
97 bool ScriptController::canAccessFromCurrentOrigin(Frame *frame)
98 {
99     return !v8::Context::InContext() || BindingSecurity::shouldAllowAccessToFrame(BindingState::instance(), frame);
100 }
101
102 ScriptController::ScriptController(Frame* frame)
103     : m_frame(frame)
104     , m_sourceURL(0)
105     , m_windowShell(V8DOMWindowShell::create(frame, mainThreadNormalWorld()))
106     , m_paused(false)
107 #if ENABLE(NETSCAPE_PLUGIN_API)
108     , m_wrappedWindowScriptNPObject(0)
109 #endif
110 {
111 }
112
113 ScriptController::~ScriptController()
114 {
115     clearForClose(true);
116 }
117
118 void ScriptController::clearScriptObjects()
119 {
120     PluginObjectMap::iterator it = m_pluginObjects.begin();
121     for (; it != m_pluginObjects.end(); ++it) {
122         _NPN_UnregisterObject(it->value);
123         _NPN_ReleaseObject(it->value);
124     }
125     m_pluginObjects.clear();
126
127 #if ENABLE(NETSCAPE_PLUGIN_API)
128     if (m_wrappedWindowScriptNPObject) {
129         NPObjectWrapper* windowScriptObjectWrapper = NPObjectWrapper::getWrapper(m_wrappedWindowScriptNPObject);
130         ASSERT(windowScriptObjectWrapper);
131
132         NPObject* windowScriptNPObject = NPObjectWrapper::getUnderlyingNPObject(m_wrappedWindowScriptNPObject);
133         ASSERT(windowScriptNPObject);
134         // Call _NPN_DeallocateObject() instead of _NPN_ReleaseObject() so that we don't leak if a plugin fails to release the window
135         // script object properly.
136         // This shouldn't cause any problems for plugins since they should have already been stopped and destroyed at this point.
137         _NPN_DeallocateObject(windowScriptNPObject);
138
139         // Clear out the wrapped window script object pointer held by the wrapper.
140         windowScriptObjectWrapper->clear();
141         _NPN_ReleaseObject(m_wrappedWindowScriptNPObject);
142         m_wrappedWindowScriptNPObject = 0;
143     }
144 #endif
145 }
146
147 void ScriptController::clearForOutOfMemory()
148 {
149     clearForClose(true);
150 }
151
152 void ScriptController::clearForClose(bool destroyGlobal)
153 {
154     m_windowShell->clearForClose(destroyGlobal);
155     for (IsolatedWorldMap::iterator iter = m_isolatedWorlds.begin(); iter != m_isolatedWorlds.end(); ++iter)
156         iter->value->clearForClose(destroyGlobal);
157     V8GCController::hintForCollectGarbage();
158 }
159
160 void ScriptController::clearForClose()
161 {
162     double start = currentTime();
163     clearForClose(false);
164     HistogramSupport::histogramCustomCounts("WebCore.ScriptController.clearForClose", (currentTime() - start) * 1000, 0, 10000, 50);
165 }
166
167 void ScriptController::updateSecurityOrigin()
168 {
169     m_windowShell->updateSecurityOrigin();
170 }
171
172 void ScriptController::updatePlatformScriptObjects()
173 {
174     notImplemented();
175 }
176
177 bool ScriptController::processingUserGesture()
178 {
179     return UserGestureIndicator::processingUserGesture();
180 }
181
182 v8::Local<v8::Value> ScriptController::callFunction(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> args[])
183 {
184     // Keep Frame (and therefore ScriptController) alive.
185     RefPtr<Frame> protect(m_frame);
186     return ScriptController::callFunctionWithInstrumentation(m_frame ? m_frame->document() : 0, function, receiver, argc, args);
187 }
188
189 static inline void resourceInfo(const v8::Handle<v8::Function> function, String& resourceName, int& lineNumber)
190 {
191     v8::ScriptOrigin origin = function->GetScriptOrigin();
192     if (origin.ResourceName().IsEmpty()) {
193         resourceName = "undefined";
194         lineNumber = 1;
195     } else {
196         resourceName = toWebCoreString(origin.ResourceName());
197         lineNumber = function->GetScriptLineNumber() + 1;
198     }
199 }
200
201 static inline String resourceString(const v8::Handle<v8::Function> function)
202 {
203     String resourceName;
204     int lineNumber;
205     resourceInfo(function, resourceName, lineNumber);
206
207     StringBuilder builder;
208     builder.append(resourceName);
209     builder.append(':');
210     builder.appendNumber(lineNumber);
211     return builder.toString();
212 }
213
214 v8::Local<v8::Value> ScriptController::callFunctionWithInstrumentation(ScriptExecutionContext* context, v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> args[])
215 {
216     V8GCController::checkMemoryUsage();
217
218     if (V8RecursionScope::recursionLevel() >= kMaxRecursionDepth)
219         return handleMaxRecursionDepthExceeded();
220
221     InspectorInstrumentationCookie cookie;
222     if (InspectorInstrumentation::timelineAgentEnabled(context)) {
223         String resourceName;
224         int lineNumber;
225         resourceInfo(function, resourceName, lineNumber);
226         cookie = InspectorInstrumentation::willCallFunction(context, resourceName, lineNumber);
227     }
228
229     v8::Local<v8::Value> result;
230     {
231         TRACE_EVENT1("v8", "v8.callFunction", "callsite", resourceString(function).utf8());
232         V8RecursionScope recursionScope(context);
233         result = function->Call(receiver, argc, args);
234     }
235
236     InspectorInstrumentation::didCallFunction(cookie);
237     crashIfV8IsDead();
238     return result;
239 }
240
241 ScriptValue ScriptController::callFunctionEvenIfScriptDisabled(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> argv[])
242 {
243     // FIXME: This should probably perform the same isPaused check that happens in ScriptController::executeScript.
244     return ScriptValue(callFunction(function, receiver, argc, argv));
245 }
246
247 v8::Local<v8::Value> ScriptController::compileAndRunScript(const ScriptSourceCode& source)
248 {
249     ASSERT(v8::Context::InContext());
250
251     V8GCController::checkMemoryUsage();
252
253     InspectorInstrumentationCookie cookie = InspectorInstrumentation::willEvaluateScript(m_frame, source.url().isNull() ? String() : source.url().string(), source.startLine());
254
255     v8::Local<v8::Value> result;
256     {
257         // Isolate exceptions that occur when compiling and executing
258         // the code. These exceptions should not interfere with
259         // javascript code we might evaluate from C++ when returning
260         // from here.
261         v8::TryCatch tryCatch;
262         tryCatch.SetVerbose(true);
263
264         // Compile the script.
265         v8::Local<v8::String> code = v8ExternalString(source.source());
266 #if PLATFORM(CHROMIUM)
267         TRACE_EVENT_BEGIN0("v8", "v8.compile");
268 #endif
269         OwnPtr<v8::ScriptData> scriptData = ScriptSourceCode::precompileScript(code, source.cachedScript());
270
271         // NOTE: For compatibility with WebCore, ScriptSourceCode's line starts at
272         // 1, whereas v8 starts at 0.
273         v8::Handle<v8::Script> script = ScriptSourceCode::compileScript(code, source.url(), source.startPosition(), scriptData.get());
274 #if PLATFORM(CHROMIUM)
275         TRACE_EVENT_END0("v8", "v8.compile");
276         TRACE_EVENT0("v8", "v8.run");
277 #endif
278
279         // Keep Frame (and therefore ScriptController) alive.
280         RefPtr<Frame> protect(m_frame);
281         result = ScriptRunner::runCompiledScript(script, m_frame->document());
282         ASSERT(!tryCatch.HasCaught() || result.IsEmpty());
283     }
284
285     InspectorInstrumentation::didEvaluateScript(cookie);
286
287     return result;
288 }
289
290 ScriptValue ScriptController::evaluate(const ScriptSourceCode& sourceCode)
291 {
292     String sourceURL = sourceCode.url();
293     const String* savedSourceURL = m_sourceURL;
294     m_sourceURL = &sourceURL;
295
296     v8::HandleScope handleScope;
297     v8::Handle<v8::Context> v8Context = ScriptController::mainWorldContext(m_frame);
298     if (v8Context.IsEmpty())
299         return ScriptValue();
300
301     v8::Context::Scope scope(v8Context);
302
303     RefPtr<Frame> protect(m_frame);
304
305     v8::Local<v8::Value> object = compileAndRunScript(sourceCode);
306
307     m_sourceURL = savedSourceURL;
308
309     if (object.IsEmpty())
310         return ScriptValue();
311
312     return ScriptValue(object);
313 }
314
315 bool ScriptController::initializeMainWorld()
316 {
317     if (m_windowShell->isContextInitialized())
318         return false;
319     return windowShell(mainThreadNormalWorld())->isContextInitialized();
320 }
321
322 // FIXME: Remove this function. There is currently an issue with the inspector related to the call to dispatchDidClearWindowObjectInWorld in ScriptController::windowShell.
323 static DOMWrapperWorld* existingWindowShellWorkaroundWorld()
324 {
325     DEFINE_STATIC_LOCAL(RefPtr<DOMWrapperWorld>, world, (DOMWrapperWorld::createUninitializedWorld()));
326     return world.get();
327 }
328
329 V8DOMWindowShell* ScriptController::existingWindowShell(DOMWrapperWorld* world)
330 {
331     ASSERT(world);
332
333     if (world->isMainWorld())
334         return m_windowShell->isContextInitialized() ? m_windowShell.get() : 0;
335
336     // FIXME: Remove this block. See comment with existingWindowShellWorkaroundWorld().
337     if (world->worldId() == DOMWrapperWorld::uninitializedWorldId) {
338         ASSERT(world == existingWindowShellWorkaroundWorld());
339         return m_windowShell.get();
340     }
341
342     IsolatedWorldMap::iterator iter = m_isolatedWorlds.find(world->worldId());
343     if (iter == m_isolatedWorlds.end())
344         return 0;
345     return iter->value->isContextInitialized() ? iter->value.get() : 0;
346 }
347
348 V8DOMWindowShell* ScriptController::windowShell(DOMWrapperWorld* world)
349 {
350     ASSERT(world);
351
352     V8DOMWindowShell* shell = 0;
353     if (world->isMainWorld())
354         shell = m_windowShell.get();
355     else {
356         IsolatedWorldMap::iterator iter = m_isolatedWorlds.find(world->worldId());
357         if (iter != m_isolatedWorlds.end())
358             shell = iter->value.get();
359         else {
360             OwnPtr<V8DOMWindowShell> isolatedWorldShell = V8DOMWindowShell::create(m_frame, world);
361             shell = isolatedWorldShell.get();
362             m_isolatedWorlds.set(world->worldId(), isolatedWorldShell.release());
363         }
364     }
365     if (!shell->isContextInitialized() && shell->initializeIfNeeded()) {
366         if (world->isMainWorld()) {
367             // FIXME: Remove this if clause. See comment with existingWindowShellWorkaroundWorld().
368             m_frame->loader()->dispatchDidClearWindowObjectInWorld(existingWindowShellWorkaroundWorld());
369         } else
370             m_frame->loader()->dispatchDidClearWindowObjectInWorld(world);
371     }
372     return shell;
373 }
374
375 void ScriptController::evaluateInIsolatedWorld(int worldID, const Vector<ScriptSourceCode>& sources, int extensionGroup, Vector<ScriptValue>* results)
376 {
377     // Except in the test runner, worldID should be non 0 as it conflicts with the mainWorldId.
378     // FIXME: Change the test runner to perform this swap and make this an ASSERT.
379     if (UNLIKELY(!worldID))
380         worldID = DOMWrapperWorld::uninitializedWorldId;
381
382     v8::HandleScope handleScope;
383     v8::Local<v8::Array> v8Results;
384     {
385         v8::HandleScope evaluateHandleScope;
386         RefPtr<DOMWrapperWorld> world = DOMWrapperWorld::ensureIsolatedWorld(worldID, extensionGroup);
387         V8DOMWindowShell* isolatedWorldShell = windowShell(world.get());
388
389         if (!isolatedWorldShell->isContextInitialized())
390             return;
391
392         v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolatedWorldShell->context());
393         v8::Context::Scope contextScope(context);
394         v8::Local<v8::Array> resultArray = v8::Array::New(sources.size());
395
396         for (size_t i = 0; i < sources.size(); ++i) {
397             v8::Local<v8::Value> evaluationResult = compileAndRunScript(sources[i]);
398             if (evaluationResult.IsEmpty())
399                 evaluationResult = v8::Local<v8::Value>::New(v8::Undefined());
400             resultArray->Set(i, evaluationResult);
401         }
402
403         // Mark temporary shell for weak destruction.
404         if (worldID == DOMWrapperWorld::uninitializedWorldId) {
405             isolatedWorldShell->destroyIsolatedShell();
406             m_isolatedWorlds.remove(world->worldId());
407         }
408
409         v8Results = evaluateHandleScope.Close(resultArray);
410     }
411
412     if (results && !v8Results.IsEmpty()) {
413         for (size_t i = 0; i < v8Results->Length(); ++i)
414             results->append(ScriptValue(v8Results->Get(i)));
415     }
416 }
417
418 bool ScriptController::shouldBypassMainWorldContentSecurityPolicy()
419 {
420     if (DOMWrapperWorld* world = worldForEnteredContextIfIsolated())
421         return world->isolatedWorldHasContentSecurityPolicy();
422     return false;
423 }
424
425 TextPosition ScriptController::eventHandlerPosition() const
426 {
427     ScriptableDocumentParser* parser = m_frame->document()->scriptableDocumentParser();
428     if (parser)
429         return parser->textPosition();
430     return TextPosition::minimumPosition();
431 }
432
433 void ScriptController::finishedWithEvent(Event* event)
434 {
435 }
436
437 v8::Local<v8::Context> ScriptController::currentWorldContext()
438 {
439     if (!v8::Context::InContext())
440         return v8::Local<v8::Context>::New(windowShell(mainThreadNormalWorld())->context());
441
442     v8::Handle<v8::Context> context = v8::Context::GetEntered();
443     DOMWrapperWorld* isolatedWorld = DOMWrapperWorld::isolated(context);
444     if (!isolatedWorld)
445         return v8::Local<v8::Context>::New(windowShell(mainThreadNormalWorld())->context());
446
447     Frame* frame = toFrameIfNotDetached(context);
448     if (!m_frame)
449         return v8::Local<v8::Context>();
450
451     if (m_frame == frame)
452         return v8::Local<v8::Context>::New(context);
453
454     // FIXME: Need to handle weak isolated worlds correctly.
455     if (isolatedWorld->createdFromUnitializedWorld())
456         return v8::Local<v8::Context>();
457
458     return v8::Local<v8::Context>::New(windowShell(isolatedWorld)->context());
459 }
460
461 v8::Local<v8::Context> ScriptController::mainWorldContext()
462 {
463     return v8::Local<v8::Context>::New(windowShell(mainThreadNormalWorld())->context());
464 }
465
466 v8::Local<v8::Context> ScriptController::mainWorldContext(Frame* frame)
467 {
468     if (!frame)
469         return v8::Local<v8::Context>();
470
471     return frame->script()->mainWorldContext();
472 }
473
474 // Create a V8 object with an interceptor of NPObjectPropertyGetter.
475 void ScriptController::bindToWindowObject(Frame* frame, const String& key, NPObject* object)
476 {
477     v8::HandleScope handleScope;
478
479     v8::Handle<v8::Context> v8Context = ScriptController::mainWorldContext(frame);
480     if (v8Context.IsEmpty())
481         return;
482
483     v8::Context::Scope scope(v8Context);
484
485     v8::Handle<v8::Object> value = createV8ObjectForNPObject(object, 0);
486
487     // Attach to the global object.
488     v8::Handle<v8::Object> global = v8Context->Global();
489     global->Set(v8String(key), value);
490 }
491
492 bool ScriptController::haveInterpreter() const
493 {
494     return m_windowShell->isContextInitialized();
495 }
496
497 void ScriptController::enableEval()
498 {
499     if (!m_windowShell->isContextInitialized())
500         return;
501     v8::HandleScope handleScope;
502     m_windowShell->context()->AllowCodeGenerationFromStrings(true);
503 }
504
505 void ScriptController::disableEval(const String& errorMessage)
506 {
507     if (!m_windowShell->isContextInitialized())
508         return;
509     v8::HandleScope handleScope;
510     v8::Handle<v8::Context> v8Context = m_windowShell->context();
511     v8Context->AllowCodeGenerationFromStrings(false);
512     v8Context->SetErrorMessageForCodeGenerationFromStrings(v8String(errorMessage));
513 }
514
515 PassScriptInstance ScriptController::createScriptInstanceForWidget(Widget* widget)
516 {
517     ASSERT(widget);
518
519     if (widget->isFrameView())
520         return 0;
521
522     NPObject* npObject = PlatformSupport::pluginScriptableObject(widget);
523
524     if (!npObject)
525         return 0;
526
527     // Frame Memory Management for NPObjects
528     // -------------------------------------
529     // NPObjects are treated differently than other objects wrapped by JS.
530     // NPObjects can be created either by the browser (e.g. the main
531     // window object) or by the plugin (the main plugin object
532     // for a HTMLEmbedElement). Further, unlike most DOM Objects, the frame
533     // is especially careful to ensure NPObjects terminate at frame teardown because
534     // if a plugin leaks a reference, it could leak its objects (or the browser's objects).
535     //
536     // The Frame maintains a list of plugin objects (m_pluginObjects)
537     // which it can use to quickly find the wrapped embed object.
538     //
539     // Inside the NPRuntime, we've added a few methods for registering
540     // wrapped NPObjects. The purpose of the registration is because
541     // javascript garbage collection is non-deterministic, yet we need to
542     // be able to tear down the plugin objects immediately. When an object
543     // is registered, javascript can use it. When the object is destroyed,
544     // or when the object's "owning" object is destroyed, the object will
545     // be un-registered, and the javascript engine must not use it.
546     //
547     // Inside the javascript engine, the engine can keep a reference to the
548     // NPObject as part of its wrapper. However, before accessing the object
549     // it must consult the _NPN_Registry.
550
551     v8::Local<v8::Object> wrapper = createV8ObjectForNPObject(npObject, 0);
552
553     // Track the plugin object. We've been given a reference to the object.
554     m_pluginObjects.set(widget, npObject);
555
556     return V8ScriptInstance::create(wrapper);
557 }
558
559 void ScriptController::cleanupScriptObjectsForPlugin(Widget* nativeHandle)
560 {
561     PluginObjectMap::iterator it = m_pluginObjects.find(nativeHandle);
562     if (it == m_pluginObjects.end())
563         return;
564     _NPN_UnregisterObject(it->value);
565     _NPN_ReleaseObject(it->value);
566     m_pluginObjects.remove(it);
567 }
568
569 void ScriptController::evaluateInWorld(const ScriptSourceCode& source,
570                                        DOMWrapperWorld* world)
571 {
572     if (world == mainThreadNormalWorld()) {
573         evaluate(source);
574         return;
575     }
576     Vector<ScriptSourceCode> sources;
577     sources.append(source);
578     evaluateInIsolatedWorld(world->worldId(), sources, world->extensionGroup(), 0);
579 }
580
581 V8Extensions& ScriptController::registeredExtensions()
582 {
583     DEFINE_STATIC_LOCAL(V8Extensions, extensions, ());
584     return extensions;
585 }
586
587 void ScriptController::registerExtensionIfNeeded(v8::Extension* extension)
588 {
589     const V8Extensions& extensions = registeredExtensions();
590     for (size_t i = 0; i < extensions.size(); ++i) {
591         if (extensions[i] == extension)
592             return;
593     }
594     v8::RegisterExtension(extension);
595     registeredExtensions().append(extension);
596 }
597
598 static NPObject* createNoScriptObject()
599 {
600     notImplemented();
601     return 0;
602 }
603
604 static NPObject* createScriptObject(Frame* frame)
605 {
606     v8::HandleScope handleScope;
607     v8::Handle<v8::Context> v8Context = ScriptController::mainWorldContext(frame);
608     if (v8Context.IsEmpty())
609         return createNoScriptObject();
610
611     v8::Context::Scope scope(v8Context);
612     DOMWindow* window = frame->document()->domWindow();
613     v8::Handle<v8::Value> global = toV8(window);
614     ASSERT(global->IsObject());
615     return npCreateV8ScriptObject(0, v8::Handle<v8::Object>::Cast(global), window);
616 }
617
618 NPObject* ScriptController::windowScriptNPObject()
619 {
620     if (m_wrappedWindowScriptNPObject)
621         return m_wrappedWindowScriptNPObject;
622
623     NPObject* windowScriptNPObject = 0;
624     if (canExecuteScripts(NotAboutToExecuteScript)) {
625         // JavaScript is enabled, so there is a JavaScript window object.
626         // Return an NPObject bound to the window object.
627         windowScriptNPObject = createScriptObject(m_frame);
628         _NPN_RegisterObject(windowScriptNPObject, 0);
629     } else {
630         // JavaScript is not enabled, so we cannot bind the NPObject to the
631         // JavaScript window object. Instead, we create an NPObject of a
632         // different class, one which is not bound to a JavaScript object.
633         windowScriptNPObject = createNoScriptObject();
634     }
635
636     m_wrappedWindowScriptNPObject = NPObjectWrapper::create(windowScriptNPObject);
637     return m_wrappedWindowScriptNPObject;
638 }
639
640 NPObject* ScriptController::createScriptObjectForPluginElement(HTMLPlugInElement* plugin)
641 {
642     // Can't create NPObjects when JavaScript is disabled.
643     if (!canExecuteScripts(NotAboutToExecuteScript))
644         return createNoScriptObject();
645
646     v8::HandleScope handleScope;
647     v8::Handle<v8::Context> v8Context = ScriptController::mainWorldContext(m_frame);
648     if (v8Context.IsEmpty())
649         return createNoScriptObject();
650     v8::Context::Scope scope(v8Context);
651
652     DOMWindow* window = m_frame->document()->domWindow();
653     v8::Handle<v8::Value> v8plugin = toV8(static_cast<HTMLEmbedElement*>(plugin));
654     if (!v8plugin->IsObject())
655         return createNoScriptObject();
656
657     return npCreateV8ScriptObject(0, v8::Handle<v8::Object>::Cast(v8plugin), window);
658 }
659
660
661 void ScriptController::clearWindowShell(DOMWindow*, bool)
662 {
663     double start = currentTime();
664     // V8 binding expects ScriptController::clearWindowShell only be called
665     // when a frame is loading a new page. This creates a new context for the new page.
666     m_windowShell->clearForNavigation();
667     for (IsolatedWorldMap::iterator iter = m_isolatedWorlds.begin(); iter != m_isolatedWorlds.end(); ++iter)
668         iter->value->clearForNavigation();
669     V8GCController::hintForCollectGarbage();
670     HistogramSupport::histogramCustomCounts("WebCore.ScriptController.clearWindowShell", (currentTime() - start) * 1000, 0, 10000, 50);
671 }
672
673 #if ENABLE(INSPECTOR)
674 void ScriptController::setCaptureCallStackForUncaughtExceptions(bool value)
675 {
676     v8::V8::SetCaptureStackTraceForUncaughtExceptions(value, ScriptCallStack::maxCallStackSizeToCapture, stackTraceOptions);
677 }
678
679 void ScriptController::collectIsolatedContexts(Vector<std::pair<ScriptState*, SecurityOrigin*> >& result)
680 {
681     v8::HandleScope handleScope;
682     for (IsolatedWorldMap::iterator it = m_isolatedWorlds.begin(); it != m_isolatedWorlds.end(); ++it) {
683         V8DOMWindowShell* isolatedWorldShell = it->value.get();
684         SecurityOrigin* origin = isolatedWorldShell->world()->isolatedWorldSecurityOrigin();
685         if (!origin)
686             continue;
687         v8::Handle<v8::Context> v8Context = isolatedWorldShell->context();
688         if (v8Context.IsEmpty())
689             continue;
690         ScriptState* scriptState = ScriptState::forContext(v8::Local<v8::Context>::New(v8Context));
691         result.append(std::pair<ScriptState*, SecurityOrigin*>(scriptState, origin));
692     }
693 }
694 #endif
695
696 bool ScriptController::setContextDebugId(int debugId)
697 {
698     ASSERT(debugId > 0);
699     if (!m_windowShell->isContextInitialized())
700         return false;
701     v8::HandleScope scope;
702     v8::Handle<v8::Context> context = m_windowShell->context();
703     if (!context->GetEmbedderData(0)->IsUndefined())
704         return false;
705
706     v8::Context::Scope contextScope(context);
707
708     char buffer[32];
709     snprintf(buffer, sizeof(buffer), "page,%d", debugId);
710     buffer[31] = 0;
711     context->SetEmbedderData(0, v8::String::New(buffer));
712
713     return true;
714 }
715
716 int ScriptController::contextDebugId(v8::Handle<v8::Context> context)
717 {
718     v8::HandleScope scope;
719     if (!context->GetEmbedderData(0)->IsString())
720         return -1;
721     v8::String::AsciiValue ascii(context->GetEmbedderData(0));
722     char* comma = strnstr(*ascii, ",", ascii.length());
723     if (!comma)
724         return -1;
725     return atoi(comma + 1);
726 }
727
728 void ScriptController::attachDebugger(void*)
729 {
730     notImplemented();
731 }
732
733 void ScriptController::updateDocument()
734 {
735     // For an uninitialized main window shell, do not incur the cost of context initialization during FrameLoader::init().
736     if ((!m_windowShell->isContextInitialized() || !m_windowShell->isGlobalInitialized()) && m_frame->loader()->stateMachine()->creatingInitialEmptyDocument())
737         return;
738
739     if (!initializeMainWorld())
740         windowShell(mainThreadNormalWorld())->updateDocument();
741 }
742
743 void ScriptController::namedItemAdded(HTMLDocument* doc, const AtomicString& name)
744 {
745     windowShell(mainThreadNormalWorld())->namedItemAdded(doc, name);
746 }
747
748 void ScriptController::namedItemRemoved(HTMLDocument* doc, const AtomicString& name)
749 {
750     windowShell(mainThreadNormalWorld())->namedItemRemoved(doc, name);
751 }
752
753 } // namespace WebCore