1/*
2 * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3 * Copyright (C) 2009, 2011 Google 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
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 *
26 */
27
28#include "config.h"
29#include "core/workers/WorkerGlobalScope.h"
30
31#include "bindings/core/v8/ExceptionState.h"
32#include "bindings/core/v8/ScheduledAction.h"
33#include "bindings/core/v8/ScriptSourceCode.h"
34#include "bindings/core/v8/ScriptValue.h"
35#include "core/dom/ActiveDOMObject.h"
36#include "core/dom/AddConsoleMessageTask.h"
37#include "core/dom/ContextLifecycleNotifier.h"
38#include "core/dom/DOMURL.h"
39#include "core/dom/ExceptionCode.h"
40#include "core/dom/MessagePort.h"
41#include "core/events/ErrorEvent.h"
42#include "core/events/Event.h"
43#include "core/inspector/ConsoleMessage.h"
44#include "core/inspector/ConsoleMessageStorage.h"
45#include "core/inspector/InspectorConsoleInstrumentation.h"
46#include "core/inspector/ScriptCallStack.h"
47#include "core/inspector/WorkerInspectorController.h"
48#include "core/loader/WorkerThreadableLoader.h"
49#include "core/frame/LocalDOMWindow.h"
50#include "core/workers/WorkerNavigator.h"
51#include "core/workers/WorkerClients.h"
52#include "core/workers/WorkerConsole.h"
53#include "core/workers/WorkerLocation.h"
54#include "core/workers/WorkerNavigator.h"
55#include "core/workers/WorkerReportingProxy.h"
56#include "core/workers/WorkerScriptLoader.h"
57#include "core/workers/WorkerThread.h"
58#include "platform/network/ContentSecurityPolicyParsers.h"
59#include "platform/weborigin/KURL.h"
60#include "platform/weborigin/SecurityOrigin.h"
61#include "public/platform/WebURLRequest.h"
62
63namespace blink {
64
65class CloseWorkerGlobalScopeTask : public ExecutionContextTask {
66public:
67    static PassOwnPtr<CloseWorkerGlobalScopeTask> create()
68    {
69        return adoptPtr(new CloseWorkerGlobalScopeTask);
70    }
71
72    virtual void performTask(ExecutionContext *context)
73    {
74        WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
75        // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop().
76        workerGlobalScope->thread()->workerReportingProxy().workerGlobalScopeClosed();
77    }
78
79    virtual bool isCleanupTask() const { return true; }
80};
81
82WorkerGlobalScope::WorkerGlobalScope(const KURL& url, const String& userAgent, WorkerThread* thread, double timeOrigin, PassOwnPtrWillBeRawPtr<WorkerClients> workerClients)
83    : m_url(url)
84    , m_userAgent(userAgent)
85    , m_script(adoptPtr(new WorkerScriptController(*this)))
86    , m_thread(thread)
87    , m_workerInspectorController(adoptRefWillBeNoop(new WorkerInspectorController(this)))
88    , m_closing(false)
89    , m_eventQueue(WorkerEventQueue::create(this))
90    , m_workerClients(workerClients)
91    , m_timeOrigin(timeOrigin)
92    , m_messageStorage(ConsoleMessageStorage::createForWorker(this))
93{
94    setSecurityOrigin(SecurityOrigin::create(url));
95    m_workerClients->reattachThread();
96    m_thread->setWorkerInspectorController(m_workerInspectorController.get());
97}
98
99WorkerGlobalScope::~WorkerGlobalScope()
100{
101}
102
103void WorkerGlobalScope::applyContentSecurityPolicyFromString(const String& policy, ContentSecurityPolicyHeaderType contentSecurityPolicyType)
104{
105    // FIXME: This doesn't match the CSP2 spec's Worker behavior (see https://w3c.github.io/webappsec/specs/content-security-policy/#processing-model-workers)
106    RefPtr<ContentSecurityPolicy> csp = ContentSecurityPolicy::create();
107    csp->didReceiveHeader(policy, contentSecurityPolicyType, ContentSecurityPolicyHeaderSourceHTTP);
108    csp->bindToExecutionContext(executionContext());
109    setContentSecurityPolicy(csp);
110}
111
112ExecutionContext* WorkerGlobalScope::executionContext() const
113{
114    return const_cast<WorkerGlobalScope*>(this);
115}
116
117const KURL& WorkerGlobalScope::virtualURL() const
118{
119    return m_url;
120}
121
122KURL WorkerGlobalScope::virtualCompleteURL(const String& url) const
123{
124    return completeURL(url);
125}
126
127KURL WorkerGlobalScope::completeURL(const String& url) const
128{
129    // Always return a null URL when passed a null string.
130    // FIXME: Should we change the KURL constructor to have this behavior?
131    if (url.isNull())
132        return KURL();
133    // Always use UTF-8 in Workers.
134    return KURL(m_url, url);
135}
136
137String WorkerGlobalScope::userAgent(const KURL&) const
138{
139    return m_userAgent;
140}
141
142void WorkerGlobalScope::disableEval(const String& errorMessage)
143{
144    m_script->disableEval(errorMessage);
145}
146
147double WorkerGlobalScope::timerAlignmentInterval() const
148{
149    return DOMTimer::visiblePageAlignmentInterval();
150}
151
152WorkerLocation* WorkerGlobalScope::location() const
153{
154    if (!m_location)
155        m_location = WorkerLocation::create(m_url);
156    return m_location.get();
157}
158
159void WorkerGlobalScope::close()
160{
161    if (m_closing)
162        return;
163
164    // Let current script run to completion but prevent future script evaluations.
165    // After m_closing is set, all the tasks in the queue continue to be fetched but only
166    // tasks with isCleanupTask()==true will be executed.
167    m_closing = true;
168    postTask(CloseWorkerGlobalScopeTask::create());
169}
170
171WorkerConsole* WorkerGlobalScope::console()
172{
173    if (!m_console)
174        m_console = WorkerConsole::create(this);
175    return m_console.get();
176}
177
178WorkerNavigator* WorkerGlobalScope::navigator() const
179{
180    if (!m_navigator)
181        m_navigator = WorkerNavigator::create(m_userAgent);
182    return m_navigator.get();
183}
184
185void WorkerGlobalScope::postTask(PassOwnPtr<ExecutionContextTask> task)
186{
187    thread()->postTask(task);
188}
189
190// FIXME: Called twice, from WorkerThreadShutdownFinishTask and WorkerGlobalScope::dispose.
191void WorkerGlobalScope::clearInspector()
192{
193    if (!m_workerInspectorController)
194        return;
195    thread()->setWorkerInspectorController(nullptr);
196    m_workerInspectorController->dispose();
197    m_workerInspectorController.clear();
198}
199
200void WorkerGlobalScope::dispose()
201{
202    ASSERT(thread()->isCurrentThread());
203
204    m_eventQueue->close();
205    clearScript();
206    clearInspector();
207    // We do not clear the thread field of the
208    // WorkerGlobalScope. Other objects keep the worker global scope
209    // alive because they need its thread field to check that work is
210    // being carried out on the right thread. We therefore cannot clear
211    // the thread field before all references to the worker global
212    // scope are gone.
213}
214
215void WorkerGlobalScope::importScripts(const Vector<String>& urls, ExceptionState& exceptionState)
216{
217    ASSERT(contentSecurityPolicy());
218    ASSERT(executionContext());
219
220    ExecutionContext& executionContext = *this->executionContext();
221
222    Vector<String>::const_iterator urlsEnd = urls.end();
223    Vector<KURL> completedURLs;
224    for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) {
225        const KURL& url = executionContext.completeURL(*it);
226        if (!url.isValid()) {
227            exceptionState.throwDOMException(SyntaxError, "The URL '" + *it + "' is invalid.");
228            return;
229        }
230        if (!contentSecurityPolicy()->allowScriptFromSource(url)) {
231            exceptionState.throwDOMException(NetworkError, "The script at '" + url.elidedString() + "' failed to load.");
232            return;
233        }
234        completedURLs.append(url);
235    }
236    Vector<KURL>::const_iterator end = completedURLs.end();
237
238    for (Vector<KURL>::const_iterator it = completedURLs.begin(); it != end; ++it) {
239        RefPtr<WorkerScriptLoader> scriptLoader(WorkerScriptLoader::create());
240        scriptLoader->setRequestContext(blink::WebURLRequest::RequestContextScript);
241        scriptLoader->loadSynchronously(executionContext, *it, AllowCrossOriginRequests);
242
243        // If the fetching attempt failed, throw a NetworkError exception and abort all these steps.
244        if (scriptLoader->failed()) {
245            exceptionState.throwDOMException(NetworkError, "The script at '" + it->elidedString() + "' failed to load.");
246            return;
247        }
248
249        InspectorInstrumentation::scriptImported(&executionContext, scriptLoader->identifier(), scriptLoader->script());
250
251        RefPtrWillBeRawPtr<ErrorEvent> errorEvent = nullptr;
252        m_script->evaluate(ScriptSourceCode(scriptLoader->script(), scriptLoader->responseURL()), &errorEvent);
253        if (errorEvent) {
254            m_script->rethrowExceptionFromImportedScript(errorEvent.release(), exceptionState);
255            return;
256        }
257    }
258}
259
260EventTarget* WorkerGlobalScope::errorEventTarget()
261{
262    return this;
263}
264
265void WorkerGlobalScope::logExceptionToConsole(const String& errorMessage, int, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtrWillBeRawPtr<ScriptCallStack>)
266{
267    thread()->workerReportingProxy().reportException(errorMessage, lineNumber, columnNumber, sourceURL);
268}
269
270void WorkerGlobalScope::reportBlockedScriptExecutionToInspector(const String& directiveText)
271{
272    InspectorInstrumentation::scriptExecutionBlockedByCSP(this, directiveText);
273}
274
275void WorkerGlobalScope::addConsoleMessage(PassRefPtrWillBeRawPtr<ConsoleMessage> prpConsoleMessage)
276{
277    RefPtrWillBeRawPtr<ConsoleMessage> consoleMessage = prpConsoleMessage;
278    if (!isContextThread()) {
279        postTask(AddConsoleMessageTask::create(consoleMessage->source(), consoleMessage->level(), consoleMessage->message()));
280        return;
281    }
282    thread()->workerReportingProxy().reportConsoleMessage(consoleMessage);
283    addMessageToWorkerConsole(consoleMessage.release());
284}
285
286void WorkerGlobalScope::addMessageToWorkerConsole(PassRefPtrWillBeRawPtr<ConsoleMessage> consoleMessage)
287{
288    ASSERT(isContextThread());
289    m_messageStorage->reportMessage(consoleMessage);
290}
291
292bool WorkerGlobalScope::isContextThread() const
293{
294    return thread()->isCurrentThread();
295}
296
297bool WorkerGlobalScope::isJSExecutionForbidden() const
298{
299    return m_script->isExecutionForbidden();
300}
301
302bool WorkerGlobalScope::idleNotification()
303{
304    return script()->idleNotification();
305}
306
307WorkerEventQueue* WorkerGlobalScope::eventQueue() const
308{
309    return m_eventQueue.get();
310}
311
312void WorkerGlobalScope::countFeature(UseCounter::Feature) const
313{
314    // FIXME: How should we count features for shared/service workers?
315}
316
317void WorkerGlobalScope::countDeprecation(UseCounter::Feature) const
318{
319    // FIXME: How should we count features for shared/service workers?
320}
321
322ConsoleMessageStorage* WorkerGlobalScope::messageStorage()
323{
324    return m_messageStorage.get();
325}
326
327void WorkerGlobalScope::trace(Visitor* visitor)
328{
329#if ENABLE(OILPAN)
330    visitor->trace(m_console);
331    visitor->trace(m_location);
332    visitor->trace(m_navigator);
333    visitor->trace(m_workerInspectorController);
334    visitor->trace(m_eventQueue);
335    visitor->trace(m_workerClients);
336    visitor->trace(m_messageStorage);
337    HeapSupplementable<WorkerGlobalScope>::trace(visitor);
338#endif
339    ExecutionContext::trace(visitor);
340    EventTargetWithInlineData::trace(visitor);
341}
342
343} // namespace blink
344