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
66namespace WebCore {
67
68class CloseWorkerGlobalScopeTask : public ScriptExecutionContext::Task {
69public:
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
85WorkerGlobalScope::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
101WorkerGlobalScope::~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
112void WorkerGlobalScope::applyContentSecurityPolicyFromString(const String& policy, ContentSecurityPolicy::HeaderType contentSecurityPolicyType)
113{
114    setContentSecurityPolicy(ContentSecurityPolicy::create(this));
115    contentSecurityPolicy()->didReceiveHeader(policy, contentSecurityPolicyType);
116}
117
118ScriptExecutionContext* WorkerGlobalScope::scriptExecutionContext() const
119{
120    return const_cast<WorkerGlobalScope*>(this);
121}
122
123const KURL& WorkerGlobalScope::virtualURL() const
124{
125    return m_url;
126}
127
128KURL WorkerGlobalScope::virtualCompleteURL(const String& url) const
129{
130    return completeURL(url);
131}
132
133KURL 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
143String WorkerGlobalScope::userAgent(const KURL&) const
144{
145    return m_userAgent;
146}
147
148void WorkerGlobalScope::disableEval(const String& errorMessage)
149{
150    m_script->disableEval(errorMessage);
151}
152
153WorkerLocation* WorkerGlobalScope::location() const
154{
155    if (!m_location)
156        m_location = WorkerLocation::create(m_url);
157    return m_location.get();
158}
159
160void 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
172WorkerNavigator* WorkerGlobalScope::navigator() const
173{
174    if (!m_navigator)
175        m_navigator = WorkerNavigator::create(m_userAgent);
176    return m_navigator.get();
177}
178
179void WorkerGlobalScope::postTask(PassOwnPtr<Task> task)
180{
181    thread()->runLoop().postTask(task);
182}
183
184void WorkerGlobalScope::clearInspector()
185{
186    m_workerInspectorController.clear();
187}
188
189void 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
226EventTarget* WorkerGlobalScope::errorEventTarget()
227{
228    return this;
229}
230
231void 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
236void 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
247void 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
257void 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
266bool WorkerGlobalScope::isContextThread() const
267{
268    return thread()->isCurrentThread();
269}
270
271bool WorkerGlobalScope::isJSExecutionForbidden() const
272{
273    return m_script->isExecutionForbidden();
274}
275
276EventTargetData* WorkerGlobalScope::eventTargetData()
277{
278    return &m_eventTargetData;
279}
280
281EventTargetData* WorkerGlobalScope::ensureEventTargetData()
282{
283    return &m_eventTargetData;
284}
285
286WorkerGlobalScope::Observer::Observer(WorkerGlobalScope* context)
287    : m_context(context)
288{
289    ASSERT(m_context && m_context->isContextThread());
290    m_context->registerObserver(this);
291}
292
293WorkerGlobalScope::Observer::~Observer()
294{
295    if (!m_context)
296        return;
297    ASSERT(m_context->isContextThread());
298    m_context->unregisterObserver(this);
299}
300
301void 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
310void WorkerGlobalScope::registerObserver(Observer* observer)
311{
312    ASSERT(observer);
313    m_workerObservers.add(observer);
314}
315
316void WorkerGlobalScope::unregisterObserver(Observer* observer)
317{
318    ASSERT(observer);
319    m_workerObservers.remove(observer);
320}
321
322void 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
333bool WorkerGlobalScope::idleNotification()
334{
335    return script()->idleNotification();
336}
337
338WorkerEventQueue* WorkerGlobalScope::eventQueue() const
339{
340    return m_eventQueue.get();
341}
342
343} // namespace WebCore
344