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