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/ContextLifecycleNotifier.h"
     37 #include "core/dom/ErrorEvent.h"
     38 #include "core/dom/Event.h"
     39 #include "core/dom/ExceptionCode.h"
     40 #include "core/dom/MessagePort.h"
     41 #include "core/html/DOMURL.h"
     42 #include "core/inspector/InspectorConsoleInstrumentation.h"
     43 #include "core/inspector/ScriptCallStack.h"
     44 #include "core/inspector/WorkerInspectorController.h"
     45 #include "core/loader/WorkerThreadableLoader.h"
     46 #include "core/page/ContentSecurityPolicy.h"
     47 #include "core/page/DOMWindow.h"
     48 #include "core/page/WorkerNavigator.h"
     49 #include "core/platform/NotImplemented.h"
     50 #include "core/workers/WorkerClients.h"
     51 #include "core/workers/WorkerLocation.h"
     52 #include "core/workers/WorkerObjectProxy.h"
     53 #include "core/workers/WorkerScriptLoader.h"
     54 #include "core/workers/WorkerThread.h"
     55 #include "weborigin/KURL.h"
     56 #include "weborigin/SecurityOrigin.h"
     57 #include "wtf/RefPtr.h"
     58 #include "wtf/UnusedParam.h"
     59 
     60 #if ENABLE(NOTIFICATIONS) || ENABLE(LEGACY_NOTIFICATIONS)
     61 #include "modules/notifications/NotificationCenter.h"
     62 #endif
     63 
     64 
     65 
     66 namespace WebCore {
     67 
     68 class CloseWorkerGlobalScopeTask : public ScriptExecutionContext::Task {
     69 public:
     70     static PassOwnPtr<CloseWorkerGlobalScopeTask> create()
     71     {
     72         return adoptPtr(new CloseWorkerGlobalScopeTask);
     73     }
     74 
     75     virtual void performTask(ScriptExecutionContext *context)
     76     {
     77         WorkerGlobalScope* workerGlobalScope = toWorkerGlobalScope(context);
     78         // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop().
     79         workerGlobalScope->thread()->workerReportingProxy().workerGlobalScopeClosed();
     80     }
     81 
     82     virtual bool isCleanupTask() const { return true; }
     83 };
     84 
     85 WorkerGlobalScope::WorkerGlobalScope(const KURL& url, const String& userAgent, WorkerThread* thread, double timeOrigin, PassOwnPtr<WorkerClients> workerClients)
     86     : m_url(url)
     87     , m_userAgent(userAgent)
     88     , m_script(adoptPtr(new WorkerScriptController(this)))
     89     , m_thread(thread)
     90     , m_workerInspectorController(adoptPtr(new WorkerInspectorController(this)))
     91     , m_closing(false)
     92     , m_eventQueue(WorkerEventQueue::create(this))
     93     , m_timeOrigin(timeOrigin)
     94     , m_workerClients(workerClients)
     95 {
     96     ScriptWrappable::init(this);
     97     setSecurityOrigin(SecurityOrigin::create(url));
     98     m_workerClients->reattachThread();
     99 }
    100 
    101 WorkerGlobalScope::~WorkerGlobalScope()
    102 {
    103     ASSERT(thread()->isCurrentThread());
    104 
    105     // Make sure we have no observers.
    106     notifyObserversOfStop();
    107 
    108     // Notify proxy that we are going away. This can free the WorkerThread object, so do not access it after this.
    109     thread()->workerReportingProxy().workerGlobalScopeDestroyed();
    110 }
    111 
    112 void WorkerGlobalScope::applyContentSecurityPolicyFromString(const String& policy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType)
    113 {
    114     setContentSecurityPolicy(ContentSecurityPolicy::create(this));
    115     contentSecurityPolicy()->didReceiveHeader(policy, contentSecurityPolicyType);
    116 }
    117 
    118 ScriptExecutionContext* WorkerGlobalScope::scriptExecutionContext() const
    119 {
    120     return const_cast<WorkerGlobalScope*>(this);
    121 }
    122 
    123 const KURL& WorkerGlobalScope::virtualURL() const
    124 {
    125     return m_url;
    126 }
    127 
    128 KURL WorkerGlobalScope::virtualCompleteURL(const String& url) const
    129 {
    130     return completeURL(url);
    131 }
    132 
    133 KURL WorkerGlobalScope::completeURL(const String& url) const
    134 {
    135     // Always return a null URL when passed a null string.
    136     // FIXME: Should we change the KURL constructor to have this behavior?
    137     if (url.isNull())
    138         return KURL();
    139     // Always use UTF-8 in Workers.
    140     return KURL(m_url, url);
    141 }
    142 
    143 String WorkerGlobalScope::userAgent(const KURL&) const
    144 {
    145     return m_userAgent;
    146 }
    147 
    148 void WorkerGlobalScope::disableEval(const String& errorMessage)
    149 {
    150     m_script->disableEval(errorMessage);
    151 }
    152 
    153 WorkerLocation* WorkerGlobalScope::location() const
    154 {
    155     if (!m_location)
    156         m_location = WorkerLocation::create(m_url);
    157     return m_location.get();
    158 }
    159 
    160 void WorkerGlobalScope::close()
    161 {
    162     if (m_closing)
    163         return;
    164 
    165     // Let current script run to completion but prevent future script evaluations.
    166     // After m_closing is set, all the tasks in the queue continue to be fetched but only
    167     // tasks with isCleanupTask()==true will be executed.
    168     m_closing = true;
    169     postTask(CloseWorkerGlobalScopeTask::create());
    170 }
    171 
    172 WorkerNavigator* WorkerGlobalScope::navigator() const
    173 {
    174     if (!m_navigator)
    175         m_navigator = WorkerNavigator::create(m_userAgent);
    176     return m_navigator.get();
    177 }
    178 
    179 void WorkerGlobalScope::postTask(PassOwnPtr<Task> task)
    180 {
    181     thread()->runLoop().postTask(task);
    182 }
    183 
    184 void WorkerGlobalScope::clearInspector()
    185 {
    186     m_workerInspectorController.clear();
    187 }
    188 
    189 void WorkerGlobalScope::importScripts(const Vector<String>& urls, ExceptionState& es)
    190 {
    191     ASSERT(contentSecurityPolicy());
    192     Vector<String>::const_iterator urlsEnd = urls.end();
    193     Vector<KURL> completedURLs;
    194     for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) {
    195         const KURL& url = scriptExecutionContext()->completeURL(*it);
    196         if (!url.isValid()) {
    197             es.throwDOMException(SyntaxError);
    198             return;
    199         }
    200         completedURLs.append(url);
    201     }
    202     Vector<KURL>::const_iterator end = completedURLs.end();
    203 
    204     for (Vector<KURL>::const_iterator it = completedURLs.begin(); it != end; ++it) {
    205         RefPtr<WorkerScriptLoader> scriptLoader(WorkerScriptLoader::create());
    206         scriptLoader->setTargetType(ResourceRequest::TargetIsScript);
    207         scriptLoader->loadSynchronously(scriptExecutionContext(), *it, AllowCrossOriginRequests);
    208 
    209         // If the fetching attempt failed, throw a NetworkError exception and abort all these steps.
    210         if (scriptLoader->failed()) {
    211             es.throwDOMException(NetworkError);
    212             return;
    213         }
    214 
    215         InspectorInstrumentation::scriptImported(scriptExecutionContext(), scriptLoader->identifier(), scriptLoader->script());
    216 
    217         RefPtr<ErrorEvent> errorEvent;
    218         m_script->evaluate(ScriptSourceCode(scriptLoader->script(), scriptLoader->responseURL()), &errorEvent);
    219         if (errorEvent) {
    220             m_script->rethrowExceptionFromImportedScript(errorEvent.release());
    221             return;
    222         }
    223     }
    224 }
    225 
    226 EventTarget* WorkerGlobalScope::errorEventTarget()
    227 {
    228     return this;
    229 }
    230 
    231 void WorkerGlobalScope::logExceptionToConsole(const String& errorMessage, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtr<ScriptCallStack>)
    232 {
    233     thread()->workerReportingProxy().postExceptionToWorkerObject(errorMessage, lineNumber, columnNumber, sourceURL);
    234 }
    235 
    236 void WorkerGlobalScope::addConsoleMessage(MessageSource source, MessageLevel level, const String& message, unsigned long requestIdentifier)
    237 {
    238     if (!isContextThread()) {
    239         postTask(AddConsoleMessageTask::create(source, level, message));
    240         return;
    241     }
    242     thread()->workerReportingProxy().postConsoleMessageToWorkerObject(source, level, message, 0, String());
    243 
    244     addMessageToWorkerConsole(source, level, message, String(), 0, 0, 0, requestIdentifier);
    245 }
    246 
    247 void WorkerGlobalScope::addMessage(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, PassRefPtr<ScriptCallStack> callStack, ScriptState* state, unsigned long requestIdentifier)
    248 {
    249     if (!isContextThread()) {
    250         postTask(AddConsoleMessageTask::create(source, level, message));
    251         return;
    252     }
    253     thread()->workerReportingProxy().postConsoleMessageToWorkerObject(source, level, message, lineNumber, sourceURL);
    254     addMessageToWorkerConsole(source, level, message, sourceURL, lineNumber, callStack, state, requestIdentifier);
    255 }
    256 
    257 void WorkerGlobalScope::addMessageToWorkerConsole(MessageSource source, MessageLevel level, const String& message, const String& sourceURL, unsigned lineNumber, PassRefPtr<ScriptCallStack> callStack, ScriptState* state, unsigned long requestIdentifier)
    258 {
    259     ASSERT(isContextThread());
    260     if (callStack)
    261         InspectorInstrumentation::addMessageToConsole(this, source, LogMessageType, level, message, callStack, requestIdentifier);
    262     else
    263         InspectorInstrumentation::addMessageToConsole(this, source, LogMessageType, level, message, sourceURL, lineNumber, 0, state, requestIdentifier);
    264 }
    265 
    266 bool WorkerGlobalScope::isContextThread() const
    267 {
    268     return thread()->isCurrentThread();
    269 }
    270 
    271 bool WorkerGlobalScope::isJSExecutionForbidden() const
    272 {
    273     return m_script->isExecutionForbidden();
    274 }
    275 
    276 EventTargetData* WorkerGlobalScope::eventTargetData()
    277 {
    278     return &m_eventTargetData;
    279 }
    280 
    281 EventTargetData* WorkerGlobalScope::ensureEventTargetData()
    282 {
    283     return &m_eventTargetData;
    284 }
    285 
    286 WorkerGlobalScope::Observer::Observer(WorkerGlobalScope* context)
    287     : m_context(context)
    288 {
    289     ASSERT(m_context && m_context->isContextThread());
    290     m_context->registerObserver(this);
    291 }
    292 
    293 WorkerGlobalScope::Observer::~Observer()
    294 {
    295     if (!m_context)
    296         return;
    297     ASSERT(m_context->isContextThread());
    298     m_context->unregisterObserver(this);
    299 }
    300 
    301 void WorkerGlobalScope::Observer::stopObserving()
    302 {
    303     if (!m_context)
    304         return;
    305     ASSERT(m_context->isContextThread());
    306     m_context->unregisterObserver(this);
    307     m_context = 0;
    308 }
    309 
    310 void WorkerGlobalScope::registerObserver(Observer* observer)
    311 {
    312     ASSERT(observer);
    313     m_workerObservers.add(observer);
    314 }
    315 
    316 void WorkerGlobalScope::unregisterObserver(Observer* observer)
    317 {
    318     ASSERT(observer);
    319     m_workerObservers.remove(observer);
    320 }
    321 
    322 void WorkerGlobalScope::notifyObserversOfStop()
    323 {
    324     HashSet<Observer*>::iterator iter = m_workerObservers.begin();
    325     while (iter != m_workerObservers.end()) {
    326         WorkerGlobalScope::Observer* observer = *iter;
    327         observer->stopObserving();
    328         observer->notifyStop();
    329         iter = m_workerObservers.begin();
    330     }
    331 }
    332 
    333 bool WorkerGlobalScope::idleNotification()
    334 {
    335     return script()->idleNotification();
    336 }
    337 
    338 WorkerEventQueue* WorkerGlobalScope::eventQueue() const
    339 {
    340     return m_eventQueue.get();
    341 }
    342 
    343 } // namespace WebCore
    344