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/v8/ExceptionState.h"
     32 #include "bindings/v8/ScheduledAction.h"
     33 #include "bindings/v8/ScriptSourceCode.h"
     34 #include "bindings/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/InspectorConsoleInstrumentation.h"
     44 #include "core/inspector/ScriptCallStack.h"
     45 #include "core/inspector/WorkerInspectorController.h"
     46 #include "core/loader/WorkerThreadableLoader.h"
     47 #include "core/frame/LocalDOMWindow.h"
     48 #include "core/workers/WorkerNavigator.h"
     49 #include "core/workers/WorkerClients.h"
     50 #include "core/workers/WorkerConsole.h"
     51 #include "core/workers/WorkerLocation.h"
     52 #include "core/workers/WorkerNavigator.h"
     53 #include "core/workers/WorkerReportingProxy.h"
     54 #include "core/workers/WorkerScriptLoader.h"
     55 #include "core/workers/WorkerThread.h"
     56 #include "platform/network/ContentSecurityPolicyParsers.h"
     57 #include "platform/weborigin/KURL.h"
     58 #include "platform/weborigin/SecurityOrigin.h"
     59 
     60 namespace WebCore {
     61 
     62 class CloseWorkerGlobalScopeTask : public ExecutionContextTask {
     63 public:
     64     static PassOwnPtr<CloseWorkerGlobalScopeTask> create()
     65     {
     66         return adoptPtr(new CloseWorkerGlobalScopeTask);
     67     }
     68 
     69     virtual void performTask(ExecutionContext *context)
     70     {
     71         WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
     72         // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop().
     73         workerGlobalScope->thread()->workerReportingProxy().workerGlobalScopeClosed();
     74     }
     75 
     76     virtual bool isCleanupTask() const { return true; }
     77 };
     78 
     79 WorkerGlobalScope::WorkerGlobalScope(const KURL& url, const String& userAgent, WorkerThread* thread, double timeOrigin, PassOwnPtrWillBeRawPtr<WorkerClients> workerClients)
     80     : m_url(url)
     81     , m_userAgent(userAgent)
     82     , m_script(adoptPtr(new WorkerScriptController(*this)))
     83     , m_thread(thread)
     84     , m_workerInspectorController(adoptPtr(new WorkerInspectorController(this)))
     85     , m_closing(false)
     86     , m_eventQueue(WorkerEventQueue::create(this))
     87     , m_workerClients(workerClients)
     88     , m_timeOrigin(timeOrigin)
     89     , m_terminationObserver(0)
     90 {
     91     ScriptWrappable::init(this);
     92     setClient(this);
     93     setSecurityOrigin(SecurityOrigin::create(url));
     94     m_workerClients->reattachThread();
     95 }
     96 
     97 WorkerGlobalScope::~WorkerGlobalScope()
     98 {
     99 }
    100 
    101 void WorkerGlobalScope::applyContentSecurityPolicyFromString(const String& policy, ContentSecurityPolicyHeaderType contentSecurityPolicyType)
    102 {
    103     setContentSecurityPolicy(ContentSecurityPolicy::create(this));
    104     contentSecurityPolicy()->didReceiveHeader(policy, contentSecurityPolicyType, ContentSecurityPolicyHeaderSourceHTTP);
    105 }
    106 
    107 ExecutionContext* WorkerGlobalScope::executionContext() const
    108 {
    109     return const_cast<WorkerGlobalScope*>(this);
    110 }
    111 
    112 const KURL& WorkerGlobalScope::virtualURL() const
    113 {
    114     return m_url;
    115 }
    116 
    117 KURL WorkerGlobalScope::virtualCompleteURL(const String& url) const
    118 {
    119     return completeURL(url);
    120 }
    121 
    122 KURL WorkerGlobalScope::completeURL(const String& url) const
    123 {
    124     // Always return a null URL when passed a null string.
    125     // FIXME: Should we change the KURL constructor to have this behavior?
    126     if (url.isNull())
    127         return KURL();
    128     // Always use UTF-8 in Workers.
    129     return KURL(m_url, url);
    130 }
    131 
    132 String WorkerGlobalScope::userAgent(const KURL&) const
    133 {
    134     return m_userAgent;
    135 }
    136 
    137 void WorkerGlobalScope::disableEval(const String& errorMessage)
    138 {
    139     m_script->disableEval(errorMessage);
    140 }
    141 
    142 double WorkerGlobalScope::timerAlignmentInterval() const
    143 {
    144     return DOMTimer::visiblePageAlignmentInterval();
    145 }
    146 
    147 WorkerLocation* WorkerGlobalScope::location() const
    148 {
    149     if (!m_location)
    150         m_location = WorkerLocation::create(m_url);
    151     return m_location.get();
    152 }
    153 
    154 void WorkerGlobalScope::close()
    155 {
    156     if (m_closing)
    157         return;
    158 
    159     // Let current script run to completion but prevent future script evaluations.
    160     // After m_closing is set, all the tasks in the queue continue to be fetched but only
    161     // tasks with isCleanupTask()==true will be executed.
    162     m_closing = true;
    163     postTask(CloseWorkerGlobalScopeTask::create());
    164 }
    165 
    166 WorkerConsole* WorkerGlobalScope::console()
    167 {
    168     if (!m_console)
    169         m_console = WorkerConsole::create(this);
    170     return m_console.get();
    171 }
    172 
    173 WorkerNavigator* WorkerGlobalScope::navigator() const
    174 {
    175     if (!m_navigator)
    176         m_navigator = WorkerNavigator::create(m_userAgent);
    177     return m_navigator.get();
    178 }
    179 
    180 void WorkerGlobalScope::postTask(PassOwnPtr<ExecutionContextTask> task)
    181 {
    182     thread()->runLoop().postTask(task);
    183 }
    184 
    185 void WorkerGlobalScope::clearInspector()
    186 {
    187     m_workerInspectorController.clear();
    188 }
    189 
    190 void WorkerGlobalScope::registerTerminationObserver(TerminationObserver* observer)
    191 {
    192     ASSERT(!m_terminationObserver);
    193     ASSERT(observer);
    194     m_terminationObserver = observer;
    195 }
    196 
    197 void WorkerGlobalScope::unregisterTerminationObserver(TerminationObserver* observer)
    198 {
    199     ASSERT(observer);
    200     ASSERT(m_terminationObserver == observer);
    201     m_terminationObserver = 0;
    202 }
    203 
    204 void WorkerGlobalScope::wasRequestedToTerminate()
    205 {
    206     if (m_terminationObserver)
    207         m_terminationObserver->wasRequestedToTerminate();
    208 }
    209 
    210 void WorkerGlobalScope::dispose()
    211 {
    212     ASSERT(thread()->isCurrentThread());
    213 
    214     m_eventQueue->close();
    215     clearScript();
    216     clearInspector();
    217     setClient(0);
    218 
    219     // We do not clear the thread field of the
    220     // WorkerGlobalScope. Other objects keep the worker global scope
    221     // alive because they need its thread field to check that work is
    222     // being carried out on the right thread. We therefore cannot clear
    223     // the thread field before all references to the worker global
    224     // scope are gone.
    225 }
    226 
    227 void WorkerGlobalScope::importScripts(const Vector<String>& urls, ExceptionState& exceptionState)
    228 {
    229     ASSERT(contentSecurityPolicy());
    230     ASSERT(executionContext());
    231 
    232     ExecutionContext& executionContext = *this->executionContext();
    233 
    234     Vector<String>::const_iterator urlsEnd = urls.end();
    235     Vector<KURL> completedURLs;
    236     for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) {
    237         const KURL& url = executionContext.completeURL(*it);
    238         if (!url.isValid()) {
    239             exceptionState.throwDOMException(SyntaxError, "The URL '" + *it + "' is invalid.");
    240             return;
    241         }
    242         if (!contentSecurityPolicy()->allowScriptFromSource(url)) {
    243             exceptionState.throwDOMException(NetworkError, "The script at '" + url.elidedString() + "' failed to load.");
    244             return;
    245         }
    246         completedURLs.append(url);
    247     }
    248     Vector<KURL>::const_iterator end = completedURLs.end();
    249 
    250     for (Vector<KURL>::const_iterator it = completedURLs.begin(); it != end; ++it) {
    251         RefPtr<WorkerScriptLoader> scriptLoader(WorkerScriptLoader::create());
    252         scriptLoader->setTargetType(ResourceRequest::TargetIsScript);
    253         scriptLoader->loadSynchronously(executionContext, *it, AllowCrossOriginRequests);
    254 
    255         // If the fetching attempt failed, throw a NetworkError exception and abort all these steps.
    256         if (scriptLoader->failed()) {
    257             exceptionState.throwDOMException(NetworkError, "The script at '" + it->elidedString() + "' failed to load.");
    258             return;
    259         }
    260 
    261         InspectorInstrumentation::scriptImported(&executionContext, scriptLoader->identifier(), scriptLoader->script());
    262 
    263         RefPtrWillBeRawPtr<ErrorEvent> errorEvent = nullptr;
    264         m_script->evaluate(ScriptSourceCode(scriptLoader->script(), scriptLoader->responseURL()), &errorEvent);
    265         if (errorEvent) {
    266             m_script->rethrowExceptionFromImportedScript(errorEvent.release());
    267             return;
    268         }
    269     }
    270 }
    271 
    272 EventTarget* WorkerGlobalScope::errorEventTarget()
    273 {
    274     return this;
    275 }
    276 
    277 void WorkerGlobalScope::logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtrWillBeRawPtr<ScriptCallStack>)
    278 {
    279     thread()->workerReportingProxy().reportException(errorMessage, lineNumber, columnNumber, sourceURL);
    280 }
    281 
    282 void WorkerGlobalScope::reportBlockedScriptExecutionToInspector(const String& directiveText)
    283 {
    284     InspectorInstrumentation::scriptExecutionBlockedByCSP(this, directiveText);
    285 }
    286 
    287 void WorkerGlobalScope::addMessage(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, ScriptState* scriptState)
    288 {
    289     if (!isContextThread()) {
    290         postTask(AddConsoleMessageTask::create(source, level, message));
    291         return;
    292     }
    293     thread()->workerReportingProxy().reportConsoleMessage(source, level, message, lineNumber, sourceURL);
    294     addMessageToWorkerConsole(source, level, message, sourceURL, lineNumber, nullptr, scriptState);
    295 }
    296 
    297 void WorkerGlobalScope::addMessageToWorkerConsole(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, PassRefPtrWillBeRawPtr<ScriptCallStack> callStack, ScriptState* scriptState)
    298 {
    299     ASSERT(isContextThread());
    300     if (callStack)
    301         InspectorInstrumentation::addMessageToConsole(this, source, LogMessageType, level, message, callStack);
    302     else
    303         InspectorInstrumentation::addMessageToConsole(this, source, LogMessageType, level, message, sourceURL, lineNumber, 0, scriptState);
    304 }
    305 
    306 bool WorkerGlobalScope::isContextThread() const
    307 {
    308     return thread()->isCurrentThread();
    309 }
    310 
    311 bool WorkerGlobalScope::isJSExecutionForbidden() const
    312 {
    313     return m_script->isExecutionForbidden();
    314 }
    315 
    316 bool WorkerGlobalScope::idleNotification()
    317 {
    318     return script()->idleNotification();
    319 }
    320 
    321 WorkerEventQueue* WorkerGlobalScope::eventQueue() const
    322 {
    323     return m_eventQueue.get();
    324 }
    325 
    326 void WorkerGlobalScope::countFeature(UseCounter::Feature) const
    327 {
    328     // FIXME: How should we count features for shared/service workers?
    329 }
    330 
    331 void WorkerGlobalScope::countDeprecation(UseCounter::Feature) const
    332 {
    333     // FIXME: How should we count features for shared/service workers?
    334 }
    335 
    336 void WorkerGlobalScope::trace(Visitor* visitor)
    337 {
    338     visitor->trace(m_console);
    339     visitor->trace(m_location);
    340     visitor->trace(m_navigator);
    341     visitor->trace(m_eventQueue);
    342     visitor->trace(m_workerClients);
    343     WillBeHeapSupplementable<WorkerGlobalScope>::trace(visitor);
    344     ExecutionContext::trace(visitor);
    345     EventTargetWithInlineData::trace(visitor);
    346 }
    347 
    348 } // namespace WebCore
    349