Home | History | Annotate | Download | only in workers
      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 
     63 namespace blink {
     64 
     65 class CloseWorkerGlobalScopeTask : public ExecutionContextTask {
     66 public:
     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 
     82 WorkerGlobalScope::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 
     99 WorkerGlobalScope::~WorkerGlobalScope()
    100 {
    101 }
    102 
    103 void 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 
    112 ExecutionContext* WorkerGlobalScope::executionContext() const
    113 {
    114     return const_cast<WorkerGlobalScope*>(this);
    115 }
    116 
    117 const KURL& WorkerGlobalScope::virtualURL() const
    118 {
    119     return m_url;
    120 }
    121 
    122 KURL WorkerGlobalScope::virtualCompleteURL(const String& url) const
    123 {
    124     return completeURL(url);
    125 }
    126 
    127 KURL 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 
    137 String WorkerGlobalScope::userAgent(const KURL&) const
    138 {
    139     return m_userAgent;
    140 }
    141 
    142 void WorkerGlobalScope::disableEval(const String& errorMessage)
    143 {
    144     m_script->disableEval(errorMessage);
    145 }
    146 
    147 double WorkerGlobalScope::timerAlignmentInterval() const
    148 {
    149     return DOMTimer::visiblePageAlignmentInterval();
    150 }
    151 
    152 WorkerLocation* WorkerGlobalScope::location() const
    153 {
    154     if (!m_location)
    155         m_location = WorkerLocation::create(m_url);
    156     return m_location.get();
    157 }
    158 
    159 void 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 
    171 WorkerConsole* WorkerGlobalScope::console()
    172 {
    173     if (!m_console)
    174         m_console = WorkerConsole::create(this);
    175     return m_console.get();
    176 }
    177 
    178 WorkerNavigator* WorkerGlobalScope::navigator() const
    179 {
    180     if (!m_navigator)
    181         m_navigator = WorkerNavigator::create(m_userAgent);
    182     return m_navigator.get();
    183 }
    184 
    185 void WorkerGlobalScope::postTask(PassOwnPtr<ExecutionContextTask> task)
    186 {
    187     thread()->postTask(task);
    188 }
    189 
    190 // FIXME: Called twice, from WorkerThreadShutdownFinishTask and WorkerGlobalScope::dispose.
    191 void WorkerGlobalScope::clearInspector()
    192 {
    193     if (!m_workerInspectorController)
    194         return;
    195     thread()->setWorkerInspectorController(nullptr);
    196     m_workerInspectorController->dispose();
    197     m_workerInspectorController.clear();
    198 }
    199 
    200 void 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 
    215 void 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 
    260 EventTarget* WorkerGlobalScope::errorEventTarget()
    261 {
    262     return this;
    263 }
    264 
    265 void 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 
    270 void WorkerGlobalScope::reportBlockedScriptExecutionToInspector(const String& directiveText)
    271 {
    272     InspectorInstrumentation::scriptExecutionBlockedByCSP(this, directiveText);
    273 }
    274 
    275 void 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 
    286 void WorkerGlobalScope::addMessageToWorkerConsole(PassRefPtrWillBeRawPtr<ConsoleMessage> consoleMessage)
    287 {
    288     ASSERT(isContextThread());
    289     m_messageStorage->reportMessage(consoleMessage);
    290 }
    291 
    292 bool WorkerGlobalScope::isContextThread() const
    293 {
    294     return thread()->isCurrentThread();
    295 }
    296 
    297 bool WorkerGlobalScope::isJSExecutionForbidden() const
    298 {
    299     return m_script->isExecutionForbidden();
    300 }
    301 
    302 bool WorkerGlobalScope::idleNotification()
    303 {
    304     return script()->idleNotification();
    305 }
    306 
    307 WorkerEventQueue* WorkerGlobalScope::eventQueue() const
    308 {
    309     return m_eventQueue.get();
    310 }
    311 
    312 void WorkerGlobalScope::countFeature(UseCounter::Feature) const
    313 {
    314     // FIXME: How should we count features for shared/service workers?
    315 }
    316 
    317 void WorkerGlobalScope::countDeprecation(UseCounter::Feature) const
    318 {
    319     // FIXME: How should we count features for shared/service workers?
    320 }
    321 
    322 ConsoleMessageStorage* WorkerGlobalScope::messageStorage()
    323 {
    324     return m_messageStorage.get();
    325 }
    326 
    327 void 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