Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / bindings / core / v8 / V8Initializer.cpp
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/core/v8/V8Initializer.h"
28
29 #include "bindings/core/v8/DOMWrapperWorld.h"
30 #include "bindings/core/v8/ScriptCallStackFactory.h"
31 #include "bindings/core/v8/ScriptController.h"
32 #include "bindings/core/v8/ScriptProfiler.h"
33 #include "bindings/core/v8/ScriptValue.h"
34 #include "bindings/core/v8/V8Binding.h"
35 #include "bindings/core/v8/V8DOMException.h"
36 #include "bindings/core/v8/V8ErrorEvent.h"
37 #include "bindings/core/v8/V8ErrorHandler.h"
38 #include "bindings/core/v8/V8GCController.h"
39 #include "bindings/core/v8/V8History.h"
40 #include "bindings/core/v8/V8Location.h"
41 #include "bindings/core/v8/V8PerContextData.h"
42 #include "bindings/core/v8/V8Window.h"
43 #include "core/dom/Document.h"
44 #include "core/dom/ExceptionCode.h"
45 #include "core/frame/ConsoleTypes.h"
46 #include "core/frame/LocalDOMWindow.h"
47 #include "core/frame/LocalFrame.h"
48 #include "core/frame/csp/ContentSecurityPolicy.h"
49 #include "core/inspector/ConsoleMessage.h"
50 #include "core/inspector/ScriptArguments.h"
51 #include "core/inspector/ScriptCallStack.h"
52 #include "platform/EventDispatchForbiddenScope.h"
53 #include "platform/RuntimeEnabledFeatures.h"
54 #include "platform/TraceEvent.h"
55 #include "platform/scheduler/Scheduler.h"
56 #include "public/platform/Platform.h"
57 #include "wtf/RefPtr.h"
58 #include "wtf/ThreadSpecific.h"
59 #include "wtf/text/WTFString.h"
60 #include <v8-debug.h>
61
62 namespace blink {
63
64 static LocalFrame* findFrame(v8::Local<v8::Object> host, v8::Local<v8::Value> data, v8::Isolate* isolate)
65 {
66     const WrapperTypeInfo* type = WrapperTypeInfo::unwrap(data);
67
68     if (V8Window::wrapperTypeInfo.equals(type)) {
69         v8::Handle<v8::Object> windowWrapper = V8Window::findInstanceInPrototypeChain(host, isolate);
70         if (windowWrapper.IsEmpty())
71             return 0;
72         return V8Window::toImpl(windowWrapper)->frame();
73     }
74
75     if (V8History::wrapperTypeInfo.equals(type))
76         return V8History::toImpl(host)->frame();
77
78     if (V8Location::wrapperTypeInfo.equals(type))
79         return V8Location::toImpl(host)->frame();
80
81     // This function can handle only those types listed above.
82     ASSERT_NOT_REACHED();
83     return 0;
84 }
85
86 static void reportFatalErrorInMainThread(const char* location, const char* message)
87 {
88     int memoryUsageMB = blink::Platform::current()->actualMemoryUsageMB();
89     printf("V8 error: %s (%s).  Current memory usage: %d MB\n", message, location, memoryUsageMB);
90     CRASH();
91 }
92
93 static void messageHandlerInMainThread(v8::Handle<v8::Message> message, v8::Handle<v8::Value> data)
94 {
95     ASSERT(isMainThread());
96     // It's possible that messageHandlerInMainThread() is invoked while we're initializing a window.
97     // In that half-baked situation, we don't have a valid context nor a valid world,
98     // so just return immediately.
99     if (DOMWrapperWorld::windowIsBeingInitialized())
100         return;
101
102     v8::Isolate* isolate = v8::Isolate::GetCurrent();
103     // If called during context initialization, there will be no entered window.
104     LocalDOMWindow* enteredWindow = enteredDOMWindow(isolate);
105     if (!enteredWindow || !enteredWindow->isCurrentlyDisplayedInFrame())
106         return;
107
108     String errorMessage = toCoreString(message->Get());
109
110     v8::Handle<v8::StackTrace> stackTrace = message->GetStackTrace();
111     RefPtrWillBeRawPtr<ScriptCallStack> callStack = nullptr;
112     int scriptId = message->GetScriptOrigin().ScriptID()->Value();
113     // Currently stack trace is only collected when inspector is open.
114     if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0) {
115         callStack = createScriptCallStack(stackTrace, ScriptCallStack::maxCallStackSizeToCapture, isolate);
116         bool success = false;
117         int topScriptId = callStack->at(0).scriptId().toInt(&success);
118         if (success && topScriptId == scriptId)
119             scriptId = 0;
120     } else {
121         Vector<ScriptCallFrame> callFrames;
122         callStack = ScriptCallStack::create(callFrames);
123     }
124
125     v8::Handle<v8::Value> resourceName = message->GetScriptOrigin().ResourceName();
126     bool shouldUseDocumentURL = resourceName.IsEmpty() || !resourceName->IsString();
127     String resource = shouldUseDocumentURL ? enteredWindow->document()->url() : toCoreString(resourceName.As<v8::String>());
128     AccessControlStatus corsStatus = message->IsSharedCrossOrigin() ? SharableCrossOrigin : NotSharableCrossOrigin;
129
130     ScriptState* scriptState = ScriptState::current(isolate);
131     RefPtrWillBeRawPtr<ErrorEvent> event = ErrorEvent::create(errorMessage, resource, message->GetLineNumber(), message->GetStartColumn() + 1, &scriptState->world());
132     if (V8DOMWrapper::isDOMWrapper(data)) {
133         v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(data);
134         const WrapperTypeInfo* type = toWrapperTypeInfo(obj);
135         if (V8DOMException::wrapperTypeInfo.isSubclass(type)) {
136             DOMException* exception = V8DOMException::toImpl(obj);
137             if (exception && !exception->messageForConsole().isEmpty())
138                 event->setUnsanitizedMessage("Uncaught " + exception->toStringForConsole());
139         }
140     }
141
142     // This method might be called while we're creating a new context. In this case, we
143     // avoid storing the exception object, as we can't create a wrapper during context creation.
144     // FIXME: Can we even get here during initialization now that we bail out when GetEntered returns an empty handle?
145     LocalFrame* frame = enteredWindow->document()->frame();
146     if (frame && frame->script().existingWindowProxy(scriptState->world())) {
147         V8ErrorHandler::storeExceptionOnErrorEventWrapper(isolate, event.get(), data, scriptState->context()->Global());
148     }
149
150     if (scriptState->world().isPrivateScriptIsolatedWorld()) {
151         // We allow a private script to dispatch error events even in a EventDispatchForbiddenScope scope.
152         // Without having this ability, it's hard to debug the private script because syntax errors
153         // in the private script are not reported to console (the private script just crashes silently).
154         // Allowing error events in private scripts is safe because error events don't propagate to
155         // other isolated worlds (which means that the error events won't fire any event listeners
156         // in user's scripts).
157         EventDispatchForbiddenScope::AllowUserAgentEvents allowUserAgentEvents;
158         enteredWindow->document()->reportException(event.release(), scriptId, callStack, corsStatus);
159     } else {
160         enteredWindow->document()->reportException(event.release(), scriptId, callStack, corsStatus);
161     }
162 }
163
164 namespace {
165
166 class PromiseRejectMessage {
167     ALLOW_ONLY_INLINE_ALLOCATION();
168 public:
169     PromiseRejectMessage(const ScriptValue& promise, PassRefPtrWillBeRawPtr<ScriptCallStack> callStack)
170         : m_promise(promise)
171         , m_callStack(callStack)
172     {
173     }
174
175     void trace(Visitor* visitor)
176     {
177         visitor->trace(m_callStack);
178     }
179
180     const ScriptValue m_promise;
181     const RefPtrWillBeMember<ScriptCallStack> m_callStack;
182 };
183
184 } // namespace
185
186 typedef Deque<PromiseRejectMessage> PromiseRejectMessageQueue;
187
188 static PromiseRejectMessageQueue& promiseRejectMessageQueue()
189 {
190     AtomicallyInitializedStatic(ThreadSpecific<PromiseRejectMessageQueue>*, queue = new ThreadSpecific<PromiseRejectMessageQueue>);
191     return **queue;
192 }
193
194 void V8Initializer::reportRejectedPromises()
195 {
196     PromiseRejectMessageQueue& queue = promiseRejectMessageQueue();
197     while (!queue.isEmpty()) {
198         PromiseRejectMessage message = queue.takeFirst();
199         ScriptState* scriptState = message.m_promise.scriptState();
200         if (!scriptState->contextIsValid())
201             continue;
202         // If execution termination has been triggered, quietly bail out.
203         if (v8::V8::IsExecutionTerminating(scriptState->isolate()))
204             continue;
205         ExecutionContext* executionContext = scriptState->executionContext();
206         if (!executionContext)
207             continue;
208
209         ScriptState::Scope scope(scriptState);
210
211         ASSERT(!message.m_promise.isEmpty());
212         v8::Handle<v8::Value> value = message.m_promise.v8Value();
213         ASSERT(!value.IsEmpty() && value->IsPromise());
214         if (v8::Handle<v8::Promise>::Cast(value)->HasHandler())
215             continue;
216
217         const String errorMessage = "Unhandled promise rejection";
218         Vector<ScriptValue> args;
219         args.append(ScriptValue(scriptState, v8String(scriptState->isolate(), errorMessage)));
220         args.append(message.m_promise);
221         RefPtrWillBeRawPtr<ScriptArguments> arguments = ScriptArguments::create(scriptState, args);
222
223         RefPtrWillBeRawPtr<ConsoleMessage> consoleMessage = ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage);
224         consoleMessage->setScriptArguments(arguments);
225         consoleMessage->setCallStack(message.m_callStack);
226         executionContext->addConsoleMessage(consoleMessage.release());
227     }
228 }
229
230 static void promiseRejectHandlerInMainThread(v8::PromiseRejectMessage message)
231 {
232     ASSERT(isMainThread());
233
234     if (message.GetEvent() != v8::kPromiseRejectWithNoHandler)
235         return;
236
237     // It's possible that promiseRejectHandlerInMainThread() is invoked while we're initializing a window.
238     // In that half-baked situation, we don't have a valid context nor a valid world,
239     // so just return immediately.
240     if (DOMWrapperWorld::windowIsBeingInitialized())
241         return;
242
243     v8::Handle<v8::Promise> promise = message.GetPromise();
244
245     // Bail out if called during context initialization.
246     v8::Isolate* isolate = promise->GetIsolate();
247     v8::Handle<v8::Context> context = isolate->GetCurrentContext();
248     if (context.IsEmpty())
249         return;
250     v8::Handle<v8::Value> global = V8Window::findInstanceInPrototypeChain(context->Global(), context->GetIsolate());
251     if (global.IsEmpty())
252         return;
253     if (!toFrameIfNotDetached(context))
254         return;
255
256     RefPtrWillBeRawPtr<ScriptCallStack> callStack = nullptr;
257     v8::Handle<v8::StackTrace> stackTrace = message.GetStackTrace();
258     if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0)
259         callStack = createScriptCallStack(stackTrace, ScriptCallStack::maxCallStackSizeToCapture, isolate);
260
261     if (!callStack && V8DOMWrapper::isDOMWrapper(message.GetValue())) {
262         // Try to get the stack from a wrapped exception object (e.g. DOMException).
263         v8::Handle<v8::Object> obj = v8::Handle<v8::Object>::Cast(message.GetValue());
264         v8::Handle<v8::Value> error = V8HiddenValue::getHiddenValue(isolate, obj, V8HiddenValue::error(isolate));
265         if (!error.IsEmpty()) {
266             stackTrace = v8::Exception::GetStackTrace(error);
267             if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0)
268                 callStack = createScriptCallStack(stackTrace, ScriptCallStack::maxCallStackSizeToCapture, isolate);
269         }
270     }
271
272     ScriptState* scriptState = ScriptState::from(context);
273     promiseRejectMessageQueue().append(PromiseRejectMessage(ScriptValue(scriptState, promise), callStack));
274 }
275
276 static void promiseRejectHandlerInWorker(v8::PromiseRejectMessage message)
277 {
278     if (message.GetEvent() != v8::kPromiseRejectWithNoHandler)
279         return;
280
281     v8::Handle<v8::Promise> promise = message.GetPromise();
282
283     // Bail out if called during context initialization.
284     v8::Isolate* isolate = promise->GetIsolate();
285     v8::Handle<v8::Context> context = isolate->GetCurrentContext();
286     if (context.IsEmpty())
287         return;
288
289     RefPtrWillBeRawPtr<ScriptCallStack> callStack = nullptr;
290     v8::Handle<v8::StackTrace> stackTrace = message.GetStackTrace();
291     if (!stackTrace.IsEmpty() && stackTrace->GetFrameCount() > 0)
292         callStack = createScriptCallStack(stackTrace, ScriptCallStack::maxCallStackSizeToCapture, isolate);
293
294     ScriptState* scriptState = ScriptState::from(context);
295     promiseRejectMessageQueue().append(PromiseRejectMessage(ScriptValue(scriptState, promise), callStack));
296 }
297
298 static void failedAccessCheckCallbackInMainThread(v8::Local<v8::Object> host, v8::AccessType type, v8::Local<v8::Value> data)
299 {
300     v8::Isolate* isolate = v8::Isolate::GetCurrent();
301     LocalFrame* target = findFrame(host, data, isolate);
302     if (!target)
303         return;
304     LocalDOMWindow* targetWindow = target->domWindow();
305
306     // FIXME: We should modify V8 to pass in more contextual information (context, property, and object).
307     ExceptionState exceptionState(ExceptionState::UnknownContext, 0, 0, isolate->GetCurrentContext()->Global(), isolate);
308     exceptionState.throwSecurityError(targetWindow->sanitizedCrossDomainAccessErrorMessage(callingDOMWindow(isolate)), targetWindow->crossDomainAccessErrorMessage(callingDOMWindow(isolate)));
309     exceptionState.throwIfNeeded();
310 }
311
312 static bool codeGenerationCheckCallbackInMainThread(v8::Local<v8::Context> context)
313 {
314     if (ExecutionContext* executionContext = toExecutionContext(context)) {
315         if (ContentSecurityPolicy* policy = toDocument(executionContext)->contentSecurityPolicy())
316             return policy->allowEval(ScriptState::from(context));
317     }
318     return false;
319 }
320
321 static void idleGCTaskInMainThread(double deadlineSeconds);
322
323 static void postIdleGCTaskMainThread()
324 {
325     if (RuntimeEnabledFeatures::v8IdleTasksEnabled()) {
326         Scheduler* scheduler = Scheduler::shared();
327         if (scheduler)
328             scheduler->postIdleTask(FROM_HERE, WTF::bind<double>(idleGCTaskInMainThread));
329     }
330 }
331
332 static void idleGCTaskInMainThread(double deadlineSeconds)
333 {
334     ASSERT(isMainThread());
335     ASSERT(RuntimeEnabledFeatures::v8IdleTasksEnabled());
336     v8::Isolate* isolate = v8::Isolate::GetCurrent();
337     // FIXME: Change V8's API to take a deadline - http://crbug.com/417668
338     double idleTimeInSeconds = deadlineSeconds - Platform::current()->monotonicallyIncreasingTime();
339     int idleTimeInMillis = static_cast<int>(idleTimeInSeconds * 1000);
340     if (idleTimeInMillis > 0)
341         isolate->IdleNotification(idleTimeInMillis);
342     // FIXME: only repost if there is more work to do.
343     postIdleGCTaskMainThread();
344 }
345
346 static void timerTraceProfilerInMainThread(const char* name, int status)
347 {
348     if (!status) {
349         TRACE_EVENT_BEGIN0("v8", name);
350     } else {
351         TRACE_EVENT_END0("v8", name);
352     }
353 }
354
355 static void initializeV8Common(v8::Isolate* isolate)
356 {
357     v8::V8::AddGCPrologueCallback(V8GCController::gcPrologue);
358     v8::V8::AddGCEpilogueCallback(V8GCController::gcEpilogue);
359
360     v8::Debug::SetLiveEditEnabled(isolate, false);
361
362     isolate->SetAutorunMicrotasks(false);
363 }
364
365 void V8Initializer::initializeMainThreadIfNeeded()
366 {
367     ASSERT(isMainThread());
368
369     static bool initialized = false;
370     if (initialized)
371         return;
372     initialized = true;
373
374     gin::IsolateHolder::Initialize(gin::IsolateHolder::kNonStrictMode, v8ArrayBufferAllocator());
375
376     v8::Isolate* isolate = V8PerIsolateData::initialize();
377
378     initializeV8Common(isolate);
379
380     v8::V8::SetFatalErrorHandler(reportFatalErrorInMainThread);
381     v8::V8::AddMessageListener(messageHandlerInMainThread);
382     v8::V8::SetFailedAccessCheckCallbackFunction(failedAccessCheckCallbackInMainThread);
383     v8::V8::SetAllowCodeGenerationFromStringsCallback(codeGenerationCheckCallbackInMainThread);
384
385     postIdleGCTaskMainThread();
386
387     isolate->SetEventLogger(timerTraceProfilerInMainThread);
388     isolate->SetPromiseRejectCallback(promiseRejectHandlerInMainThread);
389
390     ScriptProfiler::initialize();
391 }
392
393 static void reportFatalErrorInWorker(const char* location, const char* message)
394 {
395     // FIXME: We temporarily deal with V8 internal error situations such as out-of-memory by crashing the worker.
396     CRASH();
397 }
398
399 static void messageHandlerInWorker(v8::Handle<v8::Message> message, v8::Handle<v8::Value> data)
400 {
401     v8::Isolate* isolate = v8::Isolate::GetCurrent();
402     V8PerIsolateData* perIsolateData = V8PerIsolateData::from(isolate);
403     // Exceptions that occur in error handler should be ignored since in that case
404     // WorkerGlobalScope::reportException will send the exception to the worker object.
405     if (perIsolateData->isReportingException())
406         return;
407     perIsolateData->setReportingException(true);
408
409     ScriptState* scriptState = ScriptState::current(isolate);
410     // During the frame teardown, there may not be a valid context.
411     if (ExecutionContext* context = scriptState->executionContext()) {
412         String errorMessage = toCoreString(message->Get());
413         TOSTRING_VOID(V8StringResource<>, sourceURL, message->GetScriptOrigin().ResourceName());
414         int scriptId = message->GetScriptOrigin().ScriptID()->Value();
415
416         RefPtrWillBeRawPtr<ErrorEvent> event = ErrorEvent::create(errorMessage, sourceURL, message->GetLineNumber(), message->GetStartColumn() + 1, &DOMWrapperWorld::current(isolate));
417         AccessControlStatus corsStatus = message->IsSharedCrossOrigin() ? SharableCrossOrigin : NotSharableCrossOrigin;
418
419         // If execution termination has been triggered as part of constructing
420         // the error event from the v8::Message, quietly leave.
421         if (!v8::V8::IsExecutionTerminating(isolate)) {
422             V8ErrorHandler::storeExceptionOnErrorEventWrapper(isolate, event.get(), data, scriptState->context()->Global());
423             context->reportException(event.release(), scriptId, nullptr, corsStatus);
424         }
425     }
426
427     perIsolateData->setReportingException(false);
428 }
429
430 static const int kWorkerMaxStackSize = 500 * 1024;
431
432 void V8Initializer::initializeWorker(v8::Isolate* isolate)
433 {
434     initializeV8Common(isolate);
435
436     v8::V8::AddMessageListener(messageHandlerInWorker);
437     v8::V8::SetFatalErrorHandler(reportFatalErrorInWorker);
438
439     uint32_t here;
440     isolate->SetStackLimit(reinterpret_cast<uintptr_t>(&here - kWorkerMaxStackSize / sizeof(uint32_t*)));
441     isolate->SetPromiseRejectCallback(promiseRejectHandlerInWorker);
442 }
443
444 } // namespace blink