Upstream version 5.34.98.0
[platform/framework/web/crosswalk.git] / src / third_party / WebKit / Source / bindings / v8 / WorkerScriptController.cpp
1 /*
2  * Copyright (C) 2009, 2012 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
33 #include "bindings/v8/WorkerScriptController.h"
34
35 #include "V8DedicatedWorkerGlobalScope.h"
36 #include "V8ServiceWorkerGlobalScope.h"
37 #include "V8SharedWorkerGlobalScope.h"
38 #include "V8WorkerGlobalScope.h"
39 #include "bindings/v8/ScriptSourceCode.h"
40 #include "bindings/v8/ScriptValue.h"
41 #include "bindings/v8/V8ErrorHandler.h"
42 #include "bindings/v8/V8GCController.h"
43 #include "bindings/v8/V8Initializer.h"
44 #include "bindings/v8/V8ObjectConstructor.h"
45 #include "bindings/v8/V8ScriptRunner.h"
46 #include "bindings/v8/WrapperTypeInfo.h"
47 #include "core/inspector/ScriptCallStack.h"
48 #include "core/frame/DOMTimer.h"
49 #include "core/workers/SharedWorkerGlobalScope.h"
50 #include "core/workers/WorkerGlobalScope.h"
51 #include "core/workers/WorkerObjectProxy.h"
52 #include "core/workers/WorkerThread.h"
53 #include "heap/ThreadState.h"
54 #include <v8.h>
55
56 #include "public/platform/Platform.h"
57 #include "public/platform/WebWorkerRunLoop.h"
58
59 namespace WebCore {
60
61 WorkerScriptController::WorkerScriptController(WorkerGlobalScope& workerGlobalScope)
62     : m_workerGlobalScope(workerGlobalScope)
63     , m_executionForbidden(false)
64     , m_executionScheduledToTerminate(false)
65 {
66     v8::Isolate* isolate = v8::Isolate::New();
67     isolate->Enter();
68     V8Initializer::initializeWorker(isolate);
69     v8::V8::Initialize();
70     m_isolateHolder = adoptPtr(new gin::IsolateHolder(isolate));
71     V8PerIsolateData* data = V8PerIsolateData::create(isolate);
72     m_domDataStore = adoptPtr(new DOMDataStore(WorkerWorld));
73     data->setWorkerDOMDataStore(m_domDataStore.get());
74 #if ENABLE(OILPAN)
75     m_interruptor = adoptPtr(new V8IsolateInterruptor(isolate));
76     ThreadState::current()->addInterruptor(m_interruptor.get());
77 #endif
78 }
79
80 WorkerScriptController::~WorkerScriptController()
81 {
82 #if ENABLE(OILPAN)
83     ThreadState::current()->removeInterruptor(m_interruptor.get());
84 #endif
85
86     m_domDataStore.clear();
87
88     // The corresponding call to didStartWorkerRunLoop is in
89     // WorkerThread::workerThread().
90     // See http://webkit.org/b/83104#c14 for why this is here.
91     blink::Platform::current()->didStopWorkerRunLoop(blink::WebWorkerRunLoop(&m_workerGlobalScope.thread()->runLoop()));
92
93     disposeContext();
94     V8PerIsolateData::dispose(isolate());
95     v8::Isolate* v8Isolate = isolate();
96     v8Isolate->Exit();
97     m_isolateHolder.clear();
98     v8Isolate->Dispose();
99 }
100
101 void WorkerScriptController::disposeContext()
102 {
103     m_perContextData.clear();
104     m_contextHolder.clear();
105 }
106
107 bool WorkerScriptController::initializeContextIfNeeded()
108 {
109     if (m_contextHolder)
110         return true;
111
112     v8::Handle<v8::Context> context = v8::Context::New(isolate());
113     if (context.IsEmpty())
114         return false;
115
116     m_contextHolder = adoptPtr(new gin::ContextHolder(isolate()));
117     m_contextHolder->SetContext(context);
118
119     v8::Context::Scope scope(context);
120
121     V8PerContextDataHolder::install(context);
122
123     m_perContextData = V8PerContextData::create(context);
124     if (!m_perContextData->init()) {
125         disposeContext();
126         return false;
127     }
128
129     // Set DebugId for the new context.
130     context->SetEmbedderData(0, v8AtomicString(isolate(), "worker"));
131
132     // Create a new JS object and use it as the prototype for the shadow global object.
133     const WrapperTypeInfo* contextType = &V8DedicatedWorkerGlobalScope::wrapperTypeInfo;
134     if (m_workerGlobalScope.isServiceWorkerGlobalScope())
135         contextType = &V8ServiceWorkerGlobalScope::wrapperTypeInfo;
136     else if (!m_workerGlobalScope.isDedicatedWorkerGlobalScope())
137         contextType = &V8SharedWorkerGlobalScope::wrapperTypeInfo;
138     v8::Handle<v8::Function> workerGlobalScopeConstructor = m_perContextData->constructorForType(contextType);
139     v8::Local<v8::Object> jsWorkerGlobalScope = V8ObjectConstructor::newInstance(workerGlobalScopeConstructor);
140     if (jsWorkerGlobalScope.IsEmpty()) {
141         disposeContext();
142         return false;
143     }
144
145     V8DOMWrapper::associateObjectWithWrapper<V8WorkerGlobalScope>(PassRefPtr<WorkerGlobalScope>(m_workerGlobalScope), contextType, jsWorkerGlobalScope, isolate(), WrapperConfiguration::Dependent);
146
147     // Insert the object instance as the prototype of the shadow object.
148     v8::Handle<v8::Object> globalObject = v8::Handle<v8::Object>::Cast(m_contextHolder->context()->Global()->GetPrototype());
149     globalObject->SetPrototype(jsWorkerGlobalScope);
150
151     return true;
152 }
153
154 ScriptValue WorkerScriptController::evaluate(const String& script, const String& fileName, const TextPosition& scriptStartPosition, WorkerGlobalScopeExecutionState* state)
155 {
156     v8::HandleScope handleScope(isolate());
157
158     if (!initializeContextIfNeeded())
159         return ScriptValue();
160
161     v8::Handle<v8::Context> context = m_contextHolder->context();
162     if (!m_disableEvalPending.isEmpty()) {
163         context->AllowCodeGenerationFromStrings(false);
164         context->SetErrorMessageForCodeGenerationFromStrings(v8String(isolate(), m_disableEvalPending));
165         m_disableEvalPending = String();
166     }
167
168     v8::Context::Scope scope(context);
169
170     v8::TryCatch block;
171
172     v8::Handle<v8::String> scriptString = v8String(isolate(), script);
173     v8::Handle<v8::Script> compiledScript = V8ScriptRunner::compileScript(scriptString, fileName, scriptStartPosition, 0, isolate());
174     v8::Local<v8::Value> result = V8ScriptRunner::runCompiledScript(compiledScript, &m_workerGlobalScope, isolate());
175
176     if (!block.CanContinue()) {
177         m_workerGlobalScope.script()->forbidExecution();
178         return ScriptValue();
179     }
180
181     if (block.HasCaught()) {
182         v8::Local<v8::Message> message = block.Message();
183         state->hadException = true;
184         state->errorMessage = toCoreString(message->Get());
185         state->lineNumber = message->GetLineNumber();
186         state->columnNumber = message->GetStartColumn() + 1;
187         V8TRYCATCH_FOR_V8STRINGRESOURCE_RETURN(V8StringResource<>, sourceURL, message->GetScriptResourceName(), ScriptValue());
188         state->sourceURL = sourceURL;
189         state->exception = ScriptValue(block.Exception(), isolate());
190         block.Reset();
191     } else
192         state->hadException = false;
193
194     if (result.IsEmpty() || result->IsUndefined())
195         return ScriptValue();
196
197     return ScriptValue(result, isolate());
198 }
199
200 void WorkerScriptController::evaluate(const ScriptSourceCode& sourceCode, RefPtr<ErrorEvent>* errorEvent)
201 {
202     if (isExecutionForbidden())
203         return;
204
205     WorkerGlobalScopeExecutionState state;
206     evaluate(sourceCode.source(), sourceCode.url().string(), sourceCode.startPosition(), &state);
207     if (state.hadException) {
208         if (errorEvent) {
209             *errorEvent = m_workerGlobalScope.shouldSanitizeScriptError(state.sourceURL, NotSharableCrossOrigin) ?
210                 ErrorEvent::createSanitizedError(0) : ErrorEvent::create(state.errorMessage, state.sourceURL, state.lineNumber, state.columnNumber, 0);
211             V8ErrorHandler::storeExceptionOnErrorEventWrapper(errorEvent->get(), state.exception.v8Value(), isolate());
212         } else {
213             ASSERT(!m_workerGlobalScope.shouldSanitizeScriptError(state.sourceURL, NotSharableCrossOrigin));
214             RefPtr<ErrorEvent> event = m_errorEventFromImportedScript ? m_errorEventFromImportedScript.release() : ErrorEvent::create(state.errorMessage, state.sourceURL, state.lineNumber, state.columnNumber, 0);
215             m_workerGlobalScope.reportException(event, 0, NotSharableCrossOrigin);
216         }
217     }
218 }
219
220 void WorkerScriptController::scheduleExecutionTermination()
221 {
222     // The mutex provides a memory barrier to ensure that once
223     // termination is scheduled, isExecutionTerminating will
224     // accurately reflect that state when called from another thread.
225     {
226         MutexLocker locker(m_scheduledTerminationMutex);
227         m_executionScheduledToTerminate = true;
228     }
229     v8::V8::TerminateExecution(isolate());
230 }
231
232 bool WorkerScriptController::isExecutionTerminating() const
233 {
234     // See comments in scheduleExecutionTermination regarding mutex usage.
235     MutexLocker locker(m_scheduledTerminationMutex);
236     return m_executionScheduledToTerminate;
237 }
238
239 void WorkerScriptController::forbidExecution()
240 {
241     ASSERT(m_workerGlobalScope.isContextThread());
242     m_executionForbidden = true;
243 }
244
245 bool WorkerScriptController::isExecutionForbidden() const
246 {
247     ASSERT(m_workerGlobalScope.isContextThread());
248     return m_executionForbidden;
249 }
250
251 void WorkerScriptController::disableEval(const String& errorMessage)
252 {
253     m_disableEvalPending = errorMessage;
254 }
255
256 void WorkerScriptController::rethrowExceptionFromImportedScript(PassRefPtr<ErrorEvent> errorEvent)
257 {
258     m_errorEventFromImportedScript = errorEvent;
259     throwError(V8ThrowException::createError(v8GeneralError, m_errorEventFromImportedScript->message(), isolate()), isolate());
260 }
261
262 WorkerScriptController* WorkerScriptController::controllerForContext()
263 {
264     v8::Isolate* isolate = v8::Isolate::GetCurrent();
265     // Happens on frame destruction, check otherwise GetCurrent() will crash.
266     if (!isolate || !isolate->InContext())
267         return 0;
268     v8::Handle<v8::Context> context = isolate->GetCurrentContext();
269     v8::Handle<v8::Object> global = context->Global();
270     global = global->FindInstanceInPrototypeChain(V8WorkerGlobalScope::domTemplate(isolate, WorkerWorld));
271     // Return 0 if the current executing context is not the worker context.
272     if (global.IsEmpty())
273         return 0;
274     WorkerGlobalScope* workerGlobalScope = V8WorkerGlobalScope::toNative(global);
275     return workerGlobalScope->script();
276 }
277
278 } // namespace WebCore