1 /*
2 * Copyright (C) 2008, 2009 Google 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 are
6 * met:
7 *
8 * * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 * * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30
31 #include "config.h"
32 #include "V8Proxy.h"
33
34 #include "CSSMutableStyleDeclaration.h"
35 #include "CachedMetadata.h"
36 #include "DateExtension.h"
37 #include "DocumentLoader.h"
38 #include "Frame.h"
39 #include "FrameLoaderClient.h"
40 #include "IDBDatabaseException.h"
41 #include "IDBFactoryBackendInterface.h"
42 #include "IDBPendingTransactionMonitor.h"
43 #include "InspectorInstrumentation.h"
44 #include "Page.h"
45 #include "PageGroup.h"
46 #include "PlatformBridge.h"
47 #include "ScriptController.h"
48 #include "Settings.h"
49 #include "StorageNamespace.h"
50 #include "V8Binding.h"
51 #include "V8BindingState.h"
52 #include "V8Collection.h"
53 #include "V8DOMCoreException.h"
54 #include "V8DOMMap.h"
55 #include "V8DOMWindow.h"
56 #include "V8EventException.h"
57 #include "V8FileException.h"
58 #include "V8HiddenPropertyName.h"
59 #include "V8IsolatedContext.h"
60 #include "V8RangeException.h"
61 #include "V8SQLException.h"
62 #include "V8XMLHttpRequestException.h"
63 #include "V8XPathException.h"
64 #include "WorkerContext.h"
65 #include "WorkerContextExecutionProxy.h"
66
67 #if ENABLE(INDEXED_DATABASE)
68 #include "V8IDBDatabaseException.h"
69 #endif
70
71 #if ENABLE(SVG)
72 #include "V8SVGException.h"
73 #endif
74
75 #include <algorithm>
76 #include <stdio.h>
77 #include <utility>
78 #include <wtf/Assertions.h>
79 #include <wtf/OwnArrayPtr.h>
80 #include <wtf/OwnPtr.h>
81 #include <wtf/StdLibExtras.h>
82 #include <wtf/StringExtras.h>
83 #include <wtf/UnusedParam.h>
84 #include <wtf/text/StringConcatenate.h>
85
86 namespace WebCore {
87
88 // Static list of registered extensions
89 V8Extensions V8Proxy::m_extensions;
90
batchConfigureAttributes(v8::Handle<v8::ObjectTemplate> instance,v8::Handle<v8::ObjectTemplate> proto,const BatchedAttribute * attributes,size_t attributeCount)91 void batchConfigureAttributes(v8::Handle<v8::ObjectTemplate> instance,
92 v8::Handle<v8::ObjectTemplate> proto,
93 const BatchedAttribute* attributes,
94 size_t attributeCount)
95 {
96 for (size_t i = 0; i < attributeCount; ++i)
97 configureAttribute(instance, proto, attributes[i]);
98 }
99
batchConfigureCallbacks(v8::Handle<v8::ObjectTemplate> proto,v8::Handle<v8::Signature> signature,v8::PropertyAttribute attributes,const BatchedCallback * callbacks,size_t callbackCount)100 void batchConfigureCallbacks(v8::Handle<v8::ObjectTemplate> proto,
101 v8::Handle<v8::Signature> signature,
102 v8::PropertyAttribute attributes,
103 const BatchedCallback* callbacks,
104 size_t callbackCount)
105 {
106 for (size_t i = 0; i < callbackCount; ++i) {
107 proto->Set(v8::String::New(callbacks[i].name),
108 v8::FunctionTemplate::New(callbacks[i].callback,
109 v8::Handle<v8::Value>(),
110 signature),
111 attributes);
112 }
113 }
114
batchConfigureConstants(v8::Handle<v8::FunctionTemplate> functionDescriptor,v8::Handle<v8::ObjectTemplate> proto,const BatchedConstant * constants,size_t constantCount)115 void batchConfigureConstants(v8::Handle<v8::FunctionTemplate> functionDescriptor,
116 v8::Handle<v8::ObjectTemplate> proto,
117 const BatchedConstant* constants,
118 size_t constantCount)
119 {
120 for (size_t i = 0; i < constantCount; ++i) {
121 const BatchedConstant* constant = &constants[i];
122 functionDescriptor->Set(v8::String::New(constant->name), v8::Integer::New(constant->value), v8::ReadOnly);
123 proto->Set(v8::String::New(constant->name), v8::Integer::New(constant->value), v8::ReadOnly);
124 }
125 }
126
127 typedef HashMap<Node*, v8::Object*> DOMNodeMap;
128 typedef HashMap<void*, v8::Object*> DOMObjectMap;
129 typedef HashMap<int, v8::FunctionTemplate*> FunctionTemplateMap;
130
131 bool AllowAllocation::m_current = false;
132
addMessageToConsole(Page * page,const String & message,const String & sourceID,unsigned lineNumber)133 static void addMessageToConsole(Page* page, const String& message, const String& sourceID, unsigned lineNumber)
134 {
135 ASSERT(page);
136 Console* console = page->mainFrame()->domWindow()->console();
137 console->addMessage(JSMessageSource, LogMessageType, ErrorMessageLevel, message, lineNumber, sourceID);
138 }
139
logInfo(Frame * frame,const String & message,const String & url)140 void logInfo(Frame* frame, const String& message, const String& url)
141 {
142 Page* page = frame->page();
143 if (!page)
144 return;
145 addMessageToConsole(page, message, url, 0);
146 }
147
reportUnsafeAccessTo(Frame * target)148 void V8Proxy::reportUnsafeAccessTo(Frame* target)
149 {
150 ASSERT(target);
151 Document* targetDocument = target->document();
152 if (!targetDocument)
153 return;
154
155 Frame* source = V8Proxy::retrieveFrameForEnteredContext();
156 if (!source)
157 return;
158 Page* page = source->page();
159 if (!page)
160 return;
161
162 Document* sourceDocument = source->document();
163 if (!sourceDocument)
164 return; // Ignore error if the source document is gone.
165
166 // FIXME: This error message should contain more specifics of why the same
167 // origin check has failed.
168 String str = makeString("Unsafe JavaScript attempt to access frame with URL ", targetDocument->url().string(),
169 " from frame with URL ", sourceDocument->url().string(), ". Domains, protocols and ports must match.\n");
170
171 // Build a console message with fake source ID and line number.
172 const String kSourceID = "";
173 const int kLineNumber = 1;
174
175 // NOTE: Safari prints the message in the target page, but it seems like
176 // it should be in the source page. Even for delayed messages, we put it in
177 // the source page.
178 addMessageToConsole(page, str, kSourceID, kLineNumber);
179 }
180
handleFatalErrorInV8()181 static void handleFatalErrorInV8()
182 {
183 // FIXME: We temporarily deal with V8 internal error situations
184 // such as out-of-memory by crashing the renderer.
185 CRASH();
186 }
187
V8Proxy(Frame * frame)188 V8Proxy::V8Proxy(Frame* frame)
189 : m_frame(frame)
190 , m_windowShell(V8DOMWindowShell::create(frame))
191 , m_inlineCode(false)
192 , m_timerCallback(false)
193 , m_recursion(0)
194 {
195 }
196
~V8Proxy()197 V8Proxy::~V8Proxy()
198 {
199 clearForClose();
200 windowShell()->destroyGlobal();
201 }
202
compileScript(v8::Handle<v8::String> code,const String & fileName,const TextPosition0 & scriptStartPosition,v8::ScriptData * scriptData)203 v8::Handle<v8::Script> V8Proxy::compileScript(v8::Handle<v8::String> code, const String& fileName, const TextPosition0& scriptStartPosition, v8::ScriptData* scriptData)
204 {
205 const uint16_t* fileNameString = fromWebCoreString(fileName);
206 v8::Handle<v8::String> name = v8::String::New(fileNameString, fileName.length());
207 v8::Handle<v8::Integer> line = v8::Integer::New(scriptStartPosition.m_line.zeroBasedInt());
208 v8::Handle<v8::Integer> column = v8::Integer::New(scriptStartPosition.m_column.zeroBasedInt());
209 v8::ScriptOrigin origin(name, line, column);
210 v8::Handle<v8::Script> script = v8::Script::Compile(code, &origin, scriptData);
211 return script;
212 }
213
handleOutOfMemory()214 bool V8Proxy::handleOutOfMemory()
215 {
216 v8::Local<v8::Context> context = v8::Context::GetCurrent();
217
218 if (!context->HasOutOfMemoryException())
219 return false;
220
221 // Warning, error, disable JS for this frame?
222 Frame* frame = V8Proxy::retrieveFrame(context);
223
224 V8Proxy* proxy = V8Proxy::retrieve(frame);
225 if (proxy) {
226 // Clean m_context, and event handlers.
227 proxy->clearForClose();
228
229 proxy->windowShell()->destroyGlobal();
230 }
231
232 #if PLATFORM(CHROMIUM)
233 PlatformBridge::notifyJSOutOfMemory(frame);
234 #endif
235
236 // Disable JS.
237 Settings* settings = frame->settings();
238 ASSERT(settings);
239 settings->setJavaScriptEnabled(false);
240
241 return true;
242 }
243
evaluateInIsolatedWorld(int worldID,const Vector<ScriptSourceCode> & sources,int extensionGroup)244 void V8Proxy::evaluateInIsolatedWorld(int worldID, const Vector<ScriptSourceCode>& sources, int extensionGroup)
245 {
246 // FIXME: This will need to get reorganized once we have a windowShell for the isolated world.
247 windowShell()->initContextIfNeeded();
248
249 v8::HandleScope handleScope;
250 V8IsolatedContext* isolatedContext = 0;
251
252 if (worldID > 0) {
253 IsolatedWorldMap::iterator iter = m_isolatedWorlds.find(worldID);
254 if (iter != m_isolatedWorlds.end()) {
255 isolatedContext = iter->second;
256 } else {
257 isolatedContext = new V8IsolatedContext(this, extensionGroup);
258 if (isolatedContext->context().IsEmpty()) {
259 delete isolatedContext;
260 return;
261 }
262
263 // FIXME: We should change this to using window shells to match JSC.
264 m_isolatedWorlds.set(worldID, isolatedContext);
265
266 // Setup context id for JS debugger.
267 if (!setInjectedScriptContextDebugId(isolatedContext->context())) {
268 m_isolatedWorlds.take(worldID);
269 delete isolatedContext;
270 return;
271 }
272 }
273 } else {
274 isolatedContext = new V8IsolatedContext(this, extensionGroup);
275 if (isolatedContext->context().IsEmpty()) {
276 delete isolatedContext;
277 return;
278 }
279 }
280
281 v8::Local<v8::Context> context = v8::Local<v8::Context>::New(isolatedContext->context());
282 v8::Context::Scope context_scope(context);
283 for (size_t i = 0; i < sources.size(); ++i)
284 evaluate(sources[i], 0);
285
286 if (worldID == 0)
287 isolatedContext->destroy();
288 }
289
setInjectedScriptContextDebugId(v8::Handle<v8::Context> targetContext)290 bool V8Proxy::setInjectedScriptContextDebugId(v8::Handle<v8::Context> targetContext)
291 {
292 // Setup context id for JS debugger.
293 v8::Context::Scope contextScope(targetContext);
294 v8::Handle<v8::Context> context = windowShell()->context();
295 if (context.IsEmpty())
296 return false;
297 int debugId = contextDebugId(context);
298
299 char buffer[32];
300 if (debugId == -1)
301 snprintf(buffer, sizeof(buffer), "injected");
302 else
303 snprintf(buffer, sizeof(buffer), "injected,%d", debugId);
304 targetContext->SetData(v8::String::New(buffer));
305
306 return true;
307 }
308
precompileScript(v8::Handle<v8::String> code,CachedScript * cachedScript)309 PassOwnPtr<v8::ScriptData> V8Proxy::precompileScript(v8::Handle<v8::String> code, CachedScript* cachedScript)
310 {
311 // A pseudo-randomly chosen ID used to store and retrieve V8 ScriptData from
312 // the CachedScript. If the format changes, this ID should be changed too.
313 static const unsigned dataTypeID = 0xECC13BD7;
314
315 // Very small scripts are not worth the effort to preparse.
316 static const int minPreparseLength = 1024;
317
318 if (!cachedScript || code->Length() < minPreparseLength)
319 return 0;
320
321 CachedMetadata* cachedMetadata = cachedScript->cachedMetadata(dataTypeID);
322 if (cachedMetadata)
323 return v8::ScriptData::New(cachedMetadata->data(), cachedMetadata->size());
324
325 OwnPtr<v8::ScriptData> scriptData(v8::ScriptData::PreCompile(code));
326 cachedScript->setCachedMetadata(dataTypeID, scriptData->Data(), scriptData->Length());
327
328 return scriptData.release();
329 }
330
executingScript() const331 bool V8Proxy::executingScript() const
332 {
333 return m_recursion;
334 }
335
evaluate(const ScriptSourceCode & source,Node * node)336 v8::Local<v8::Value> V8Proxy::evaluate(const ScriptSourceCode& source, Node* node)
337 {
338 ASSERT(v8::Context::InContext());
339
340 V8GCController::checkMemoryUsage();
341
342 InspectorInstrumentationCookie cookie = InspectorInstrumentation::willEvaluateScript(m_frame, source.url().isNull() ? String() : source.url().string(), source.startLine());
343
344 v8::Local<v8::Value> result;
345 {
346 // Isolate exceptions that occur when compiling and executing
347 // the code. These exceptions should not interfere with
348 // javascript code we might evaluate from C++ when returning
349 // from here.
350 v8::TryCatch tryCatch;
351 tryCatch.SetVerbose(true);
352
353 // Compile the script.
354 v8::Local<v8::String> code = v8ExternalString(source.source());
355 #if PLATFORM(CHROMIUM)
356 PlatformBridge::traceEventBegin("v8.compile", node, "");
357 #endif
358 OwnPtr<v8::ScriptData> scriptData = precompileScript(code, source.cachedScript());
359
360 // NOTE: For compatibility with WebCore, ScriptSourceCode's line starts at
361 // 1, whereas v8 starts at 0.
362 v8::Handle<v8::Script> script = compileScript(code, source.url(), WTF::toZeroBasedTextPosition(source.startPosition()), scriptData.get());
363 #if PLATFORM(CHROMIUM)
364 PlatformBridge::traceEventEnd("v8.compile", node, "");
365
366 PlatformBridge::traceEventBegin("v8.run", node, "");
367 #endif
368 // Set inlineCode to true for <a href="javascript:doSomething()">
369 // and false for <script>doSomething</script>. We make a rough guess at
370 // this based on whether the script source has a URL.
371 result = runScript(script, source.url().string().isNull());
372 }
373 #if PLATFORM(CHROMIUM)
374 PlatformBridge::traceEventEnd("v8.run", node, "");
375 #endif
376
377 InspectorInstrumentation::didEvaluateScript(cookie);
378
379 return result;
380 }
381
runScript(v8::Handle<v8::Script> script,bool isInlineCode)382 v8::Local<v8::Value> V8Proxy::runScript(v8::Handle<v8::Script> script, bool isInlineCode)
383 {
384 if (script.IsEmpty())
385 return notHandledByInterceptor();
386
387 V8GCController::checkMemoryUsage();
388 // Compute the source string and prevent against infinite recursion.
389 if (m_recursion >= kMaxRecursionDepth) {
390 v8::Local<v8::String> code = v8ExternalString("throw RangeError('Recursion too deep')");
391 // FIXME: Ideally, we should be able to re-use the origin of the
392 // script passed to us as the argument instead of using an empty string
393 // and 0 baseLine.
394 script = compileScript(code, "", TextPosition0::minimumPosition());
395 }
396
397 if (handleOutOfMemory())
398 ASSERT(script.IsEmpty());
399
400 if (script.IsEmpty())
401 return notHandledByInterceptor();
402
403 // Save the previous value of the inlineCode flag and update the flag for
404 // the duration of the script invocation.
405 bool previousInlineCode = inlineCode();
406 setInlineCode(isInlineCode);
407
408 // Run the script and keep track of the current recursion depth.
409 v8::Local<v8::Value> result;
410 v8::TryCatch tryCatch;
411 tryCatch.SetVerbose(true);
412 {
413 // See comment in V8Proxy::callFunction.
414 m_frame->keepAlive();
415
416 m_recursion++;
417 result = script->Run();
418 m_recursion--;
419 }
420
421 // Release the storage mutex if applicable.
422 didLeaveScriptContext();
423
424 if (handleOutOfMemory())
425 ASSERT(result.IsEmpty());
426
427 // Handle V8 internal error situation (Out-of-memory).
428 if (tryCatch.HasCaught()) {
429 ASSERT(result.IsEmpty());
430 return notHandledByInterceptor();
431 }
432
433 if (result.IsEmpty())
434 return notHandledByInterceptor();
435
436 // Restore inlineCode flag.
437 setInlineCode(previousInlineCode);
438
439 if (v8::V8::IsDead())
440 handleFatalErrorInV8();
441
442 return result;
443 }
444
callFunction(v8::Handle<v8::Function> function,v8::Handle<v8::Object> receiver,int argc,v8::Handle<v8::Value> args[])445 v8::Local<v8::Value> V8Proxy::callFunction(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> args[])
446 {
447 V8GCController::checkMemoryUsage();
448 v8::Local<v8::Value> result;
449 {
450 if (m_recursion >= kMaxRecursionDepth) {
451 v8::Local<v8::String> code = v8::String::New("throw new RangeError('Maximum call stack size exceeded.')");
452 if (code.IsEmpty())
453 return result;
454 v8::Local<v8::Script> script = v8::Script::Compile(code);
455 if (script.IsEmpty())
456 return result;
457 script->Run();
458 return result;
459 }
460
461 // Evaluating the JavaScript could cause the frame to be deallocated,
462 // so we start the keep alive timer here.
463 // Frame::keepAlive method adds the ref count of the frame and sets a
464 // timer to decrease the ref count. It assumes that the current JavaScript
465 // execution finishs before firing the timer.
466 m_frame->keepAlive();
467
468 InspectorInstrumentationCookie cookie;
469 if (InspectorInstrumentation::hasFrontends()) {
470 v8::ScriptOrigin origin = function->GetScriptOrigin();
471 String resourceName("undefined");
472 int lineNumber = 1;
473 if (!origin.ResourceName().IsEmpty()) {
474 resourceName = toWebCoreString(origin.ResourceName());
475 lineNumber = function->GetScriptLineNumber() + 1;
476 }
477 cookie = InspectorInstrumentation::willCallFunction(m_frame, resourceName, lineNumber);
478 }
479
480 m_recursion++;
481 result = function->Call(receiver, argc, args);
482 m_recursion--;
483
484 InspectorInstrumentation::didCallFunction(cookie);
485 }
486
487 // Release the storage mutex if applicable.
488 didLeaveScriptContext();
489
490 if (v8::V8::IsDead())
491 handleFatalErrorInV8();
492
493 return result;
494 }
495
callFunctionWithoutFrame(v8::Handle<v8::Function> function,v8::Handle<v8::Object> receiver,int argc,v8::Handle<v8::Value> args[])496 v8::Local<v8::Value> V8Proxy::callFunctionWithoutFrame(v8::Handle<v8::Function> function, v8::Handle<v8::Object> receiver, int argc, v8::Handle<v8::Value> args[])
497 {
498 V8GCController::checkMemoryUsage();
499 v8::Local<v8::Value> result = function->Call(receiver, argc, args);
500
501 if (v8::V8::IsDead())
502 handleFatalErrorInV8();
503
504 return result;
505 }
506
newInstance(v8::Handle<v8::Function> constructor,int argc,v8::Handle<v8::Value> args[])507 v8::Local<v8::Value> V8Proxy::newInstance(v8::Handle<v8::Function> constructor, int argc, v8::Handle<v8::Value> args[])
508 {
509 // No artificial limitations on the depth of recursion, see comment in
510 // V8Proxy::callFunction.
511 v8::Local<v8::Value> result;
512 {
513 // See comment in V8Proxy::callFunction.
514 m_frame->keepAlive();
515
516 result = constructor->NewInstance(argc, args);
517 }
518
519 if (v8::V8::IsDead())
520 handleFatalErrorInV8();
521
522 return result;
523 }
524
retrieveWindow(v8::Handle<v8::Context> context)525 DOMWindow* V8Proxy::retrieveWindow(v8::Handle<v8::Context> context)
526 {
527 v8::Handle<v8::Object> global = context->Global();
528 ASSERT(!global.IsEmpty());
529 global = V8DOMWrapper::lookupDOMWrapper(V8DOMWindow::GetTemplate(), global);
530 ASSERT(!global.IsEmpty());
531 return V8DOMWindow::toNative(global);
532 }
533
retrieveFrame(v8::Handle<v8::Context> context)534 Frame* V8Proxy::retrieveFrame(v8::Handle<v8::Context> context)
535 {
536 DOMWindow* window = retrieveWindow(context);
537 Frame* frame = window->frame();
538 if (frame && frame->domWindow() == window)
539 return frame;
540 // We return 0 here because |context| is detached from the Frame. If we
541 // did return |frame| we could get in trouble because the frame could be
542 // navigated to another security origin.
543 return 0;
544 }
545
retrieveFrameForEnteredContext()546 Frame* V8Proxy::retrieveFrameForEnteredContext()
547 {
548 v8::Handle<v8::Context> context = v8::Context::GetEntered();
549 if (context.IsEmpty())
550 return 0;
551 return retrieveFrame(context);
552 }
553
retrieveFrameForCurrentContext()554 Frame* V8Proxy::retrieveFrameForCurrentContext()
555 {
556 v8::Handle<v8::Context> context = v8::Context::GetCurrent();
557 if (context.IsEmpty())
558 return 0;
559 return retrieveFrame(context);
560 }
561
retrieveFrameForCallingContext()562 Frame* V8Proxy::retrieveFrameForCallingContext()
563 {
564 v8::Handle<v8::Context> context = v8::Context::GetCalling();
565 if (context.IsEmpty())
566 return 0;
567 return retrieveFrame(context);
568 }
569
retrieve()570 V8Proxy* V8Proxy::retrieve()
571 {
572 DOMWindow* window = retrieveWindow(currentContext());
573 ASSERT(window);
574 return retrieve(window->frame());
575 }
576
retrieve(Frame * frame)577 V8Proxy* V8Proxy::retrieve(Frame* frame)
578 {
579 if (!frame)
580 return 0;
581 return frame->script()->canExecuteScripts(NotAboutToExecuteScript) ? frame->script()->proxy() : 0;
582 }
583
retrieve(ScriptExecutionContext * context)584 V8Proxy* V8Proxy::retrieve(ScriptExecutionContext* context)
585 {
586 if (!context || !context->isDocument())
587 return 0;
588 return retrieve(static_cast<Document*>(context)->frame());
589 }
590
disconnectFrame()591 void V8Proxy::disconnectFrame()
592 {
593 }
594
didLeaveScriptContext()595 void V8Proxy::didLeaveScriptContext()
596 {
597 Page* page = m_frame->page();
598 if (!page)
599 return;
600 #if ENABLE(INDEXED_DATABASE)
601 // If we've just left a script context and indexed database has been
602 // instantiated, we must let its transaction coordinator know so it can terminate
603 // any not-yet-started transactions.
604 IDBPendingTransactionMonitor::abortPendingTransactions();
605 #endif // ENABLE(INDEXED_DATABASE)
606 // If we've just left a top level script context and local storage has been
607 // instantiated, we must ensure that any storage locks have been freed.
608 // Per http://dev.w3.org/html5/spec/Overview.html#storage-mutex
609 if (m_recursion != 0)
610 return;
611 if (page->group().hasLocalStorage())
612 page->group().localStorage()->unlock();
613 }
614
resetIsolatedWorlds()615 void V8Proxy::resetIsolatedWorlds()
616 {
617 for (IsolatedWorldMap::iterator iter = m_isolatedWorlds.begin();
618 iter != m_isolatedWorlds.end(); ++iter) {
619 iter->second->destroy();
620 }
621 m_isolatedWorlds.clear();
622 }
623
clearForClose()624 void V8Proxy::clearForClose()
625 {
626 resetIsolatedWorlds();
627 windowShell()->clearForClose();
628 }
629
clearForNavigation()630 void V8Proxy::clearForNavigation()
631 {
632 resetIsolatedWorlds();
633 windowShell()->clearForNavigation();
634 }
635
setDOMException(int exceptionCode)636 void V8Proxy::setDOMException(int exceptionCode)
637 {
638 if (exceptionCode <= 0)
639 return;
640
641 ExceptionCodeDescription description;
642 getExceptionCodeDescription(exceptionCode, description);
643
644 v8::Handle<v8::Value> exception;
645 switch (description.type) {
646 case DOMExceptionType:
647 exception = toV8(DOMCoreException::create(description));
648 break;
649 case RangeExceptionType:
650 exception = toV8(RangeException::create(description));
651 break;
652 case EventExceptionType:
653 exception = toV8(EventException::create(description));
654 break;
655 case XMLHttpRequestExceptionType:
656 exception = toV8(XMLHttpRequestException::create(description));
657 break;
658 #if ENABLE(SVG)
659 case SVGExceptionType:
660 exception = toV8(SVGException::create(description));
661 break;
662 #endif
663 #if ENABLE(XPATH)
664 case XPathExceptionType:
665 exception = toV8(XPathException::create(description));
666 break;
667 #endif
668 #if ENABLE(DATABASE)
669 case SQLExceptionType:
670 exception = toV8(SQLException::create(description));
671 break;
672 #endif
673 #if ENABLE(BLOB) || ENABLE(FILE_SYSTEM)
674 case FileExceptionType:
675 exception = toV8(FileException::create(description));
676 break;
677 #endif
678 #if ENABLE(INDEXED_DATABASE)
679 case IDBDatabaseExceptionType:
680 exception = toV8(IDBDatabaseException::create(description));
681 break;
682 #endif
683 default:
684 ASSERT_NOT_REACHED();
685 }
686
687 if (!exception.IsEmpty())
688 v8::ThrowException(exception);
689 }
690
throwError(ErrorType type,const char * message)691 v8::Handle<v8::Value> V8Proxy::throwError(ErrorType type, const char* message)
692 {
693 switch (type) {
694 case RangeError:
695 return v8::ThrowException(v8::Exception::RangeError(v8String(message)));
696 case ReferenceError:
697 return v8::ThrowException(v8::Exception::ReferenceError(v8String(message)));
698 case SyntaxError:
699 return v8::ThrowException(v8::Exception::SyntaxError(v8String(message)));
700 case TypeError:
701 return v8::ThrowException(v8::Exception::TypeError(v8String(message)));
702 case GeneralError:
703 return v8::ThrowException(v8::Exception::Error(v8String(message)));
704 default:
705 ASSERT_NOT_REACHED();
706 return notHandledByInterceptor();
707 }
708 }
709
throwTypeError()710 v8::Handle<v8::Value> V8Proxy::throwTypeError()
711 {
712 return throwError(TypeError, "Type error");
713 }
714
throwSyntaxError()715 v8::Handle<v8::Value> V8Proxy::throwSyntaxError()
716 {
717 return throwError(SyntaxError, "Syntax error");
718 }
719
context(Frame * frame)720 v8::Local<v8::Context> V8Proxy::context(Frame* frame)
721 {
722 v8::Local<v8::Context> context = V8Proxy::mainWorldContext(frame);
723 if (context.IsEmpty())
724 return v8::Local<v8::Context>();
725
726 if (V8IsolatedContext* isolatedContext = V8IsolatedContext::getEntered()) {
727 context = v8::Local<v8::Context>::New(isolatedContext->context());
728 if (frame != V8Proxy::retrieveFrame(context))
729 return v8::Local<v8::Context>();
730 }
731
732 return context;
733 }
734
context()735 v8::Local<v8::Context> V8Proxy::context()
736 {
737 if (V8IsolatedContext* isolatedContext = V8IsolatedContext::getEntered()) {
738 RefPtr<SharedPersistent<v8::Context> > context = isolatedContext->sharedContext();
739 if (m_frame != V8Proxy::retrieveFrame(context->get()))
740 return v8::Local<v8::Context>();
741 return v8::Local<v8::Context>::New(context->get());
742 }
743 return mainWorldContext();
744 }
745
mainWorldContext()746 v8::Local<v8::Context> V8Proxy::mainWorldContext()
747 {
748 windowShell()->initContextIfNeeded();
749 return v8::Local<v8::Context>::New(windowShell()->context());
750 }
751
mainWorldContext(Frame * frame)752 v8::Local<v8::Context> V8Proxy::mainWorldContext(Frame* frame)
753 {
754 V8Proxy* proxy = retrieve(frame);
755 if (!proxy)
756 return v8::Local<v8::Context>();
757
758 return proxy->mainWorldContext();
759 }
760
currentContext()761 v8::Local<v8::Context> V8Proxy::currentContext()
762 {
763 return v8::Context::GetCurrent();
764 }
765
checkNewLegal(const v8::Arguments & args)766 v8::Handle<v8::Value> V8Proxy::checkNewLegal(const v8::Arguments& args)
767 {
768 if (!AllowAllocation::m_current)
769 return throwError(TypeError, "Illegal constructor");
770
771 return args.This();
772 }
773
registerExtensionWithV8(v8::Extension * extension)774 void V8Proxy::registerExtensionWithV8(v8::Extension* extension)
775 {
776 // If the extension exists in our list, it was already registered with V8.
777 if (!registeredExtensionWithV8(extension))
778 v8::RegisterExtension(extension);
779 }
780
registeredExtensionWithV8(v8::Extension * extension)781 bool V8Proxy::registeredExtensionWithV8(v8::Extension* extension)
782 {
783 for (size_t i = 0; i < m_extensions.size(); ++i) {
784 if (m_extensions[i] == extension)
785 return true;
786 }
787
788 return false;
789 }
790
registerExtension(v8::Extension * extension)791 void V8Proxy::registerExtension(v8::Extension* extension)
792 {
793 registerExtensionWithV8(extension);
794 m_extensions.append(extension);
795 }
796
setContextDebugId(int debugId)797 bool V8Proxy::setContextDebugId(int debugId)
798 {
799 ASSERT(debugId > 0);
800 v8::HandleScope scope;
801 v8::Handle<v8::Context> context = windowShell()->context();
802 if (context.IsEmpty())
803 return false;
804 if (!context->GetData()->IsUndefined())
805 return false;
806
807 v8::Context::Scope contextScope(context);
808
809 char buffer[32];
810 snprintf(buffer, sizeof(buffer), "page,%d", debugId);
811 context->SetData(v8::String::New(buffer));
812
813 return true;
814 }
815
contextDebugId(v8::Handle<v8::Context> context)816 int V8Proxy::contextDebugId(v8::Handle<v8::Context> context)
817 {
818 v8::HandleScope scope;
819 if (!context->GetData()->IsString())
820 return -1;
821 v8::String::AsciiValue ascii(context->GetData());
822 char* comma = strnstr(*ascii, ",", ascii.length());
823 if (!comma)
824 return -1;
825 return atoi(comma + 1);
826 }
827
toV8Context(ScriptExecutionContext * context,const WorldContextHandle & worldContext)828 v8::Local<v8::Context> toV8Context(ScriptExecutionContext* context, const WorldContextHandle& worldContext)
829 {
830 if (context->isDocument()) {
831 if (V8Proxy* proxy = V8Proxy::retrieve(context))
832 return worldContext.adjustedContext(proxy);
833 #if ENABLE(WORKERS)
834 } else if (context->isWorkerContext()) {
835 if (WorkerContextExecutionProxy* proxy = static_cast<WorkerContext*>(context)->script()->proxy())
836 return proxy->context();
837 #endif
838 }
839 return v8::Local<v8::Context>();
840 }
841
842 } // namespace WebCore
843