Home | History | Annotate | Download | only in workers
      1 /*
      2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
      3  * Copyright (C) 2009 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 
     30 #if ENABLE(WORKERS)
     31 
     32 #include "WorkerContext.h"
     33 
     34 #include "ActiveDOMObject.h"
     35 #include "Database.h"
     36 #include "DOMTimer.h"
     37 #include "DOMWindow.h"
     38 #include "Event.h"
     39 #include "EventException.h"
     40 #include "MessagePort.h"
     41 #include "NotImplemented.h"
     42 #include "ScriptSourceCode.h"
     43 #include "ScriptValue.h"
     44 #include "SecurityOrigin.h"
     45 #include "WorkerLocation.h"
     46 #include "WorkerNavigator.h"
     47 #include "WorkerObjectProxy.h"
     48 #include "WorkerScriptLoader.h"
     49 #include "WorkerThread.h"
     50 #include "WorkerThreadableLoader.h"
     51 #include "XMLHttpRequestException.h"
     52 #include <wtf/RefPtr.h>
     53 #include <wtf/UnusedParam.h>
     54 
     55 #if ENABLE(NOTIFICATIONS)
     56 #include "NotificationCenter.h"
     57 #endif
     58 
     59 namespace WebCore {
     60 
     61 WorkerContext::WorkerContext(const KURL& url, const String& userAgent, WorkerThread* thread)
     62     : m_url(url)
     63     , m_userAgent(userAgent)
     64     , m_script(new WorkerScriptController(this))
     65     , m_thread(thread)
     66     , m_closing(false)
     67 {
     68     setSecurityOrigin(SecurityOrigin::create(url));
     69 }
     70 
     71 WorkerContext::~WorkerContext()
     72 {
     73     ASSERT(currentThread() == thread()->threadID());
     74 #if ENABLE(NOTIFICATIONS)
     75     m_notifications.clear();
     76 #endif
     77     // Notify proxy that we are going away. This can free the WorkerThread object, so do not access it after this.
     78     thread()->workerReportingProxy().workerContextDestroyed();
     79 }
     80 
     81 ScriptExecutionContext* WorkerContext::scriptExecutionContext() const
     82 {
     83     return const_cast<WorkerContext*>(this);
     84 }
     85 
     86 const KURL& WorkerContext::virtualURL() const
     87 {
     88     return m_url;
     89 }
     90 
     91 KURL WorkerContext::virtualCompleteURL(const String& url) const
     92 {
     93     return completeURL(url);
     94 }
     95 
     96 KURL WorkerContext::completeURL(const String& url) const
     97 {
     98     // Always return a null URL when passed a null string.
     99     // FIXME: Should we change the KURL constructor to have this behavior?
    100     if (url.isNull())
    101         return KURL();
    102     // Always use UTF-8 in Workers.
    103     return KURL(m_url, url);
    104 }
    105 
    106 String WorkerContext::userAgent(const KURL&) const
    107 {
    108     return m_userAgent;
    109 }
    110 
    111 WorkerLocation* WorkerContext::location() const
    112 {
    113     if (!m_location)
    114         m_location = WorkerLocation::create(m_url);
    115     return m_location.get();
    116 }
    117 
    118 void WorkerContext::close()
    119 {
    120     if (m_closing)
    121         return;
    122 
    123     m_closing = true;
    124     // Notify parent that this context is closed. Parent is responsible for calling WorkerThread::stop().
    125     thread()->workerReportingProxy().workerContextClosed();
    126 }
    127 
    128 WorkerNavigator* WorkerContext::navigator() const
    129 {
    130     if (!m_navigator)
    131         m_navigator = WorkerNavigator::create(m_userAgent);
    132     return m_navigator.get();
    133 }
    134 
    135 bool WorkerContext::hasPendingActivity() const
    136 {
    137     ActiveDOMObjectsMap& activeObjects = activeDOMObjects();
    138     ActiveDOMObjectsMap::const_iterator activeObjectsEnd = activeObjects.end();
    139     for (ActiveDOMObjectsMap::const_iterator iter = activeObjects.begin(); iter != activeObjectsEnd; ++iter) {
    140         if (iter->first->hasPendingActivity())
    141             return true;
    142     }
    143 
    144     // Keep the worker active as long as there is a MessagePort with pending activity or that is remotely entangled.
    145     HashSet<MessagePort*>::const_iterator messagePortsEnd = messagePorts().end();
    146     for (HashSet<MessagePort*>::const_iterator iter = messagePorts().begin(); iter != messagePortsEnd; ++iter) {
    147         if ((*iter)->hasPendingActivity() || ((*iter)->isEntangled() && !(*iter)->locallyEntangledPort()))
    148             return true;
    149     }
    150 
    151     return false;
    152 }
    153 
    154 void WorkerContext::resourceRetrievedByXMLHttpRequest(unsigned long, const ScriptString&)
    155 {
    156     // FIXME: The implementation is pending the fixes in https://bugs.webkit.org/show_bug.cgi?id=23175
    157     notImplemented();
    158 }
    159 
    160 void WorkerContext::scriptImported(unsigned long, const String&)
    161 {
    162     // FIXME: The implementation is pending the fixes in https://bugs.webkit.org/show_bug.cgi?id=23175
    163     notImplemented();
    164 }
    165 
    166 void WorkerContext::postTask(PassOwnPtr<Task> task)
    167 {
    168     thread()->runLoop().postTask(task);
    169 }
    170 
    171 int WorkerContext::setTimeout(ScheduledAction* action, int timeout)
    172 {
    173     return DOMTimer::install(scriptExecutionContext(), action, timeout, true);
    174 }
    175 
    176 void WorkerContext::clearTimeout(int timeoutId)
    177 {
    178     DOMTimer::removeById(scriptExecutionContext(), timeoutId);
    179 }
    180 
    181 int WorkerContext::setInterval(ScheduledAction* action, int timeout)
    182 {
    183     return DOMTimer::install(scriptExecutionContext(), action, timeout, false);
    184 }
    185 
    186 void WorkerContext::clearInterval(int timeoutId)
    187 {
    188     DOMTimer::removeById(scriptExecutionContext(), timeoutId);
    189 }
    190 
    191 void WorkerContext::importScripts(const Vector<String>& urls, const String& callerURL, int callerLine, ExceptionCode& ec)
    192 {
    193 #if !ENABLE(INSPECTOR)
    194     UNUSED_PARAM(callerURL);
    195     UNUSED_PARAM(callerLine);
    196 #endif
    197     ec = 0;
    198     Vector<String>::const_iterator urlsEnd = urls.end();
    199     Vector<KURL> completedURLs;
    200     for (Vector<String>::const_iterator it = urls.begin(); it != urlsEnd; ++it) {
    201         const KURL& url = scriptExecutionContext()->completeURL(*it);
    202         if (!url.isValid()) {
    203             ec = SYNTAX_ERR;
    204             return;
    205         }
    206         completedURLs.append(url);
    207     }
    208     Vector<KURL>::const_iterator end = completedURLs.end();
    209 
    210     for (Vector<KURL>::const_iterator it = completedURLs.begin(); it != end; ++it) {
    211         WorkerScriptLoader scriptLoader;
    212         scriptLoader.loadSynchronously(scriptExecutionContext(), *it, AllowCrossOriginRequests);
    213 
    214         // If the fetching attempt failed, throw a NETWORK_ERR exception and abort all these steps.
    215         if (scriptLoader.failed()) {
    216             ec = XMLHttpRequestException::NETWORK_ERR;
    217             return;
    218         }
    219 
    220         scriptExecutionContext()->scriptImported(scriptLoader.identifier(), scriptLoader.script());
    221 #if ENABLE(INSPECTOR)
    222         scriptExecutionContext()->addMessage(InspectorControllerDestination, JSMessageSource, LogMessageType, LogMessageLevel, "Worker script imported: \"" + *it + "\".", callerLine, callerURL);
    223 #endif
    224 
    225         ScriptValue exception;
    226         m_script->evaluate(ScriptSourceCode(scriptLoader.script(), *it), &exception);
    227         if (!exception.hasNoValue()) {
    228             m_script->setException(exception);
    229             return;
    230         }
    231     }
    232 }
    233 
    234 void WorkerContext::reportException(const String& errorMessage, int lineNumber, const String& sourceURL)
    235 {
    236     bool errorHandled = false;
    237     if (onerror())
    238         errorHandled = onerror()->reportError(this, errorMessage, sourceURL, lineNumber);
    239 
    240     if (!errorHandled)
    241         thread()->workerReportingProxy().postExceptionToWorkerObject(errorMessage, lineNumber, sourceURL);
    242 }
    243 
    244 void WorkerContext::addMessage(MessageDestination destination, MessageSource source, MessageType type, MessageLevel level, const String& message, unsigned lineNumber, const String& sourceURL)
    245 {
    246     thread()->workerReportingProxy().postConsoleMessageToWorkerObject(destination, source, type, level, message, lineNumber, sourceURL);
    247 }
    248 
    249 #if ENABLE(NOTIFICATIONS)
    250 NotificationCenter* WorkerContext::webkitNotifications() const
    251 {
    252     if (!m_notifications)
    253         m_notifications = NotificationCenter::create(scriptExecutionContext(), m_thread->getNotificationPresenter());
    254     return m_notifications.get();
    255 }
    256 #endif
    257 
    258 #if ENABLE(DATABASE)
    259 PassRefPtr<Database> WorkerContext::openDatabase(const String& name, const String& version, const String& displayName, unsigned long estimatedSize, ExceptionCode& ec)
    260 {
    261     if (!securityOrigin()->canAccessDatabase()) {
    262         ec = SECURITY_ERR;
    263         return 0;
    264     }
    265 
    266     ASSERT(Database::isAvailable());
    267     if (!Database::isAvailable())
    268         return 0;
    269 
    270     return Database::openDatabase(this, name, version, displayName, estimatedSize, ec);
    271 }
    272 #endif
    273 
    274 bool WorkerContext::isContextThread() const
    275 {
    276     return currentThread() == thread()->threadID();
    277 }
    278 
    279 EventTargetData* WorkerContext::eventTargetData()
    280 {
    281     return &m_eventTargetData;
    282 }
    283 
    284 EventTargetData* WorkerContext::ensureEventTargetData()
    285 {
    286     return &m_eventTargetData;
    287 }
    288 
    289 } // namespace WebCore
    290 
    291 #endif // ENABLE(WORKERS)
    292