1 /* 2 * Copyright (C) 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 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' 14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS 17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 23 * THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 26 #include "config.h" 27 #include "bindings/v8/V8Initializer.h" 28 29 #include "V8ErrorEvent.h" 30 #include "V8History.h" 31 #include "V8Location.h" 32 #include "V8Window.h" 33 #include "bindings/v8/DOMWrapperWorld.h" 34 #include "bindings/v8/ScriptCallStackFactory.h" 35 #include "bindings/v8/ScriptController.h" 36 #include "bindings/v8/ScriptProfiler.h" 37 #include "bindings/v8/V8Binding.h" 38 #include "bindings/v8/V8GCController.h" 39 #include "bindings/v8/V8HiddenPropertyName.h" 40 #include "bindings/v8/V8PerContextData.h" 41 #include "core/dom/Document.h" 42 #include "core/dom/ExceptionCode.h" 43 #include "core/inspector/ScriptCallStack.h" 44 #include "core/page/ConsoleTypes.h" 45 #include "core/page/ContentSecurityPolicy.h" 46 #include "core/page/DOMWindow.h" 47 #include "core/page/Frame.h" 48 #include "core/platform/MemoryUsageSupport.h" 49 #include <v8-debug.h> 50 #include "wtf/RefPtr.h" 51 #include "wtf/text/WTFString.h" 52 53 namespace WebCore { 54 55 static Frame* findFrame(v8::Local<v8::Object> host, v8::Local<v8::Value> data, v8::Isolate* isolate) 56 { 57 WrapperTypeInfo* type = WrapperTypeInfo::unwrap(data); 58 59 if (V8Window::info.equals(type)) { 60 v8::Handle<v8::Object> windowWrapper = host->FindInstanceInPrototypeChain(V8Window::GetTemplate(isolate, worldTypeInMainThread(isolate))); 61 if (windowWrapper.IsEmpty()) 62 return 0; 63 return V8Window::toNative(windowWrapper)->frame(); 64 } 65 66 if (V8History::info.equals(type)) 67 return V8History::toNative(host)->frame(); 68 69 if (V8Location::info.equals(type)) 70 return V8Location::toNative(host)->frame(); 71 72 // This function can handle only those types listed above. 73 ASSERT_NOT_REACHED(); 74 return 0; 75 } 76 77 static void reportFatalErrorInMainThread(const char* location, const char* message) 78 { 79 int memoryUsageMB = MemoryUsageSupport::actualMemoryUsageMB(); 80 printf("V8 error: %s (%s). Current memory usage: %d MB\n", message, location, memoryUsageMB); 81 CRASH(); 82 } 83 84 static void messageHandlerInMainThread(v8::Handle<v8::Message> message, v8::Handle<v8::Value> data) 85 { 86 DOMWindow* firstWindow = firstDOMWindow(); 87 if (!firstWindow->isCurrentlyDisplayedInFrame()) 88 return; 89 90 String errorMessage = toWebCoreString(message->Get()); 91 92 v8::Handle<v8::StackTrace> stackTrace = message->GetStackTrace(); 93 RefPtr<ScriptCallStack> callStack; 94 // Currently stack trace is only collected when inspector is open. 95 if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0) 96 callStack = createScriptCallStack(stackTrace, ScriptCallStack::maxCallStackSizeToCapture); 97 98 v8::Handle<v8::Value> resourceName = message->GetScriptResourceName(); 99 bool shouldUseDocumentURL = resourceName.IsEmpty() || !resourceName->IsString(); 100 String resource = shouldUseDocumentURL ? firstWindow->document()->url() : toWebCoreString(resourceName); 101 RefPtr<ErrorEvent> event = ErrorEvent::create(errorMessage, resource, message->GetLineNumber(), message->GetStartColumn()); 102 103 // messageHandlerInMainThread can be called while we're creating a new context. 104 // Since we cannot create a wrapper in the intermediate timing, we need to skip 105 // creating a wrapper for |event|. 106 DOMWrapperWorld* world = DOMWrapperWorld::current(); 107 Frame* frame = firstWindow->document()->frame(); 108 if (world && frame && frame->script()->existingWindowShell(world)) { 109 v8::Local<v8::Value> wrappedEvent = toV8(event.get(), v8::Handle<v8::Object>(), v8::Isolate::GetCurrent()); 110 if (!wrappedEvent.IsEmpty()) { 111 ASSERT(wrappedEvent->IsObject()); 112 v8::Local<v8::Object>::Cast(wrappedEvent)->SetHiddenValue(V8HiddenPropertyName::error(), data); 113 } 114 } 115 AccessControlStatus corsStatus = message->IsSharedCrossOrigin() ? SharableCrossOrigin : NotSharableCrossOrigin; 116 firstWindow->document()->reportException(event.release(), callStack, corsStatus); 117 } 118 119 static void failedAccessCheckCallbackInMainThread(v8::Local<v8::Object> host, v8::AccessType type, v8::Local<v8::Value> data) 120 { 121 Frame* target = findFrame(host, data, v8::Isolate::GetCurrent()); 122 if (!target) 123 return; 124 DOMWindow* targetWindow = target->domWindow(); 125 126 setDOMException(SecurityError, targetWindow->crossDomainAccessErrorMessage(activeDOMWindow()), v8::Isolate::GetCurrent()); 127 } 128 129 static bool codeGenerationCheckCallbackInMainThread(v8::Local<v8::Context> context) 130 { 131 if (ScriptExecutionContext* scriptExecutionContext = toScriptExecutionContext(context)) { 132 if (ContentSecurityPolicy* policy = toDocument(scriptExecutionContext)->contentSecurityPolicy()) 133 return policy->allowEval(ScriptState::forContext(context)); 134 } 135 return false; 136 } 137 138 static void initializeV8Common() 139 { 140 v8::V8::AddGCPrologueCallback(V8GCController::gcPrologue); 141 v8::V8::AddGCEpilogueCallback(V8GCController::gcEpilogue); 142 v8::V8::IgnoreOutOfMemoryException(); 143 144 v8::Debug::SetLiveEditEnabled(false); 145 } 146 147 void V8Initializer::initializeMainThreadIfNeeded(v8::Isolate* isolate) 148 { 149 ASSERT(isMainThread()); 150 151 static bool initialized = false; 152 if (initialized) 153 return; 154 initialized = true; 155 156 initializeV8Common(); 157 158 v8::V8::SetFatalErrorHandler(reportFatalErrorInMainThread); 159 v8::V8::AddMessageListener(messageHandlerInMainThread); 160 v8::V8::SetFailedAccessCheckCallbackFunction(failedAccessCheckCallbackInMainThread); 161 v8::V8::SetAllowCodeGenerationFromStringsCallback(codeGenerationCheckCallbackInMainThread); 162 ScriptProfiler::initialize(); 163 V8PerIsolateData::ensureInitialized(isolate); 164 } 165 166 static void reportFatalErrorInWorker(const char* location, const char* message) 167 { 168 // FIXME: We temporarily deal with V8 internal error situations such as out-of-memory by crashing the worker. 169 CRASH(); 170 } 171 172 static void messageHandlerInWorker(v8::Handle<v8::Message> message, v8::Handle<v8::Value> data) 173 { 174 static bool isReportingException = false; 175 // Exceptions that occur in error handler should be ignored since in that case 176 // WorkerGlobalScope::reportException will send the exception to the worker object. 177 if (isReportingException) 178 return; 179 isReportingException = true; 180 181 // During the frame teardown, there may not be a valid context. 182 if (ScriptExecutionContext* context = getScriptExecutionContext()) { 183 String errorMessage = toWebCoreString(message->Get()); 184 String sourceURL = toWebCoreString(message->GetScriptResourceName()); 185 RefPtr<ErrorEvent> event = ErrorEvent::create(errorMessage, sourceURL, message->GetLineNumber(), message->GetStartColumn()); 186 v8::Local<v8::Value> wrappedEvent = toV8(event.get(), v8::Handle<v8::Object>(), v8::Isolate::GetCurrent()); 187 if (!wrappedEvent.IsEmpty()) { 188 ASSERT(wrappedEvent->IsObject()); 189 v8::Local<v8::Object>::Cast(wrappedEvent)->SetHiddenValue(V8HiddenPropertyName::error(), data); 190 } 191 AccessControlStatus corsStatus = message->IsSharedCrossOrigin() ? SharableCrossOrigin : NotSharableCrossOrigin; 192 context->reportException(event.release(), 0, corsStatus); 193 } 194 195 isReportingException = false; 196 } 197 198 static const int kWorkerMaxStackSize = 500 * 1024; 199 200 void V8Initializer::initializeWorker(v8::Isolate* isolate) 201 { 202 initializeV8Common(); 203 204 v8::V8::AddMessageListener(messageHandlerInWorker); 205 v8::V8::SetFatalErrorHandler(reportFatalErrorInWorker); 206 207 v8::ResourceConstraints resourceConstraints; 208 uint32_t here; 209 resourceConstraints.set_stack_limit(&here - kWorkerMaxStackSize / sizeof(uint32_t*)); 210 v8::SetResourceConstraints(&resourceConstraints); 211 212 V8PerIsolateData::ensureInitialized(isolate); 213 } 214 215 } // namespace WebCore 216