1/*
2 * Copyright (C) 2013 Google Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 *
8 *     * Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 *     * Redistributions in binary form must reproduce the above
11 * copyright notice, this list of conditions and the following disclaimer
12 * in the documentation and/or other materials provided with the
13 * distribution.
14 *     * Neither the name of Google Inc. nor the names of its
15 * contributors may be used to endorse or promote products derived from
16 * this software without specific prior written permission.
17 *
18 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29 */
30#include "config.h"
31#include "ServiceWorkerGlobalScope.h"
32
33#include "bindings/core/v8/ScriptPromise.h"
34#include "bindings/core/v8/ScriptState.h"
35#include "bindings/core/v8/V8ThrowException.h"
36#include "core/fetch/MemoryCache.h"
37#include "core/fetch/ResourceLoaderOptions.h"
38#include "core/inspector/ScriptCallStack.h"
39#include "core/loader/ThreadableLoader.h"
40#include "core/workers/WorkerClients.h"
41#include "core/workers/WorkerThreadStartupData.h"
42#include "modules/EventTargetModules.h"
43#include "modules/serviceworkers/CacheStorage.h"
44#include "modules/serviceworkers/FetchManager.h"
45#include "modules/serviceworkers/Request.h"
46#include "modules/serviceworkers/ServiceWorkerClients.h"
47#include "modules/serviceworkers/ServiceWorkerGlobalScopeClient.h"
48#include "modules/serviceworkers/ServiceWorkerThread.h"
49#include "platform/network/ResourceRequest.h"
50#include "platform/weborigin/KURL.h"
51#include "public/platform/WebURL.h"
52#include "wtf/CurrentTime.h"
53
54namespace blink {
55
56PassRefPtrWillBeRawPtr<ServiceWorkerGlobalScope> ServiceWorkerGlobalScope::create(ServiceWorkerThread* thread, PassOwnPtrWillBeRawPtr<WorkerThreadStartupData> startupData)
57{
58    RefPtrWillBeRawPtr<ServiceWorkerGlobalScope> context = adoptRefWillBeNoop(new ServiceWorkerGlobalScope(startupData->m_scriptURL, startupData->m_userAgent, thread, monotonicallyIncreasingTime(), startupData->m_workerClients.release()));
59
60    context->applyContentSecurityPolicyFromString(startupData->m_contentSecurityPolicy, startupData->m_contentSecurityPolicyType);
61
62    return context.release();
63}
64
65ServiceWorkerGlobalScope::ServiceWorkerGlobalScope(const KURL& url, const String& userAgent, ServiceWorkerThread* thread, double timeOrigin, PassOwnPtrWillBeRawPtr<WorkerClients> workerClients)
66    : WorkerGlobalScope(url, userAgent, thread, timeOrigin, workerClients)
67    , m_fetchManager(adoptPtr(new FetchManager(this)))
68{
69}
70
71ServiceWorkerGlobalScope::~ServiceWorkerGlobalScope()
72{
73}
74
75void ServiceWorkerGlobalScope::stopFetch()
76{
77    m_fetchManager.clear();
78}
79
80String ServiceWorkerGlobalScope::scope(ExecutionContext* context)
81{
82    return ServiceWorkerGlobalScopeClient::from(context)->scope().string();
83}
84
85CacheStorage* ServiceWorkerGlobalScope::caches(ExecutionContext* context)
86{
87    if (!m_caches)
88        m_caches = CacheStorage::create(ServiceWorkerGlobalScopeClient::from(context)->cacheStorage());
89    return m_caches;
90}
91
92ScriptPromise ServiceWorkerGlobalScope::fetch(ScriptState* scriptState, Request* request)
93{
94    if (!m_fetchManager)
95        return ScriptPromise::reject(scriptState, V8ThrowException::createTypeError("ServiceWorkerGlobalScope is shutting down.", scriptState->isolate()));
96    // "Let |r| be the associated request of the result of invoking the initial
97    // value of Request as constructor with |input| and |init| as arguments. If
98    // this throws an exception, reject |p| with it."
99    TrackExceptionState exceptionState;
100    Request* r = Request::create(this, request, exceptionState);
101    if (exceptionState.hadException()) {
102        // FIXME: We should throw the caught error.
103        return ScriptPromise::reject(scriptState, V8ThrowException::createTypeError(exceptionState.message(), scriptState->isolate()));
104    }
105    return m_fetchManager->fetch(scriptState, r->request());
106}
107
108ScriptPromise ServiceWorkerGlobalScope::fetch(ScriptState* scriptState, Request* request, const Dictionary& requestInit)
109{
110    if (!m_fetchManager)
111        return ScriptPromise::reject(scriptState, V8ThrowException::createTypeError("ServiceWorkerGlobalScope is shutting down.", scriptState->isolate()));
112    // "Let |r| be the associated request of the result of invoking the initial
113    // value of Request as constructor with |input| and |init| as arguments. If
114    // this throws an exception, reject |p| with it."
115    TrackExceptionState exceptionState;
116    Request* r = Request::create(this, request, requestInit, exceptionState);
117    if (exceptionState.hadException()) {
118        // FIXME: We should throw the caught error.
119        return ScriptPromise::reject(scriptState, V8ThrowException::createTypeError(exceptionState.message(), scriptState->isolate()));
120    }
121    return m_fetchManager->fetch(scriptState, r->request());
122}
123
124ScriptPromise ServiceWorkerGlobalScope::fetch(ScriptState* scriptState, const String& urlstring)
125{
126    if (!m_fetchManager)
127        return ScriptPromise::reject(scriptState, V8ThrowException::createTypeError("ServiceWorkerGlobalScope is shutting down.", scriptState->isolate()));
128    // "Let |r| be the associated request of the result of invoking the initial
129    // value of Request as constructor with |input| and |init| as arguments. If
130    // this throws an exception, reject |p| with it."
131    TrackExceptionState exceptionState;
132    Request* r = Request::create(this, urlstring, exceptionState);
133    if (exceptionState.hadException()) {
134        // FIXME: We should throw the caught error.
135        return ScriptPromise::reject(scriptState, V8ThrowException::createTypeError(exceptionState.message(), scriptState->isolate()));
136    }
137    return m_fetchManager->fetch(scriptState, r->request());
138}
139
140ScriptPromise ServiceWorkerGlobalScope::fetch(ScriptState* scriptState, const String& urlstring, const Dictionary& requestInit)
141{
142    if (!m_fetchManager)
143        return ScriptPromise::reject(scriptState, V8ThrowException::createTypeError("ServiceWorkerGlobalScope is shutting down.", scriptState->isolate()));
144    // "Let |r| be the associated request of the result of invoking the initial
145    // value of Request as constructor with |input| and |init| as arguments. If
146    // this throws an exception, reject |p| with it."
147    TrackExceptionState exceptionState;
148    Request* r = Request::create(this, urlstring, requestInit, exceptionState);
149    if (exceptionState.hadException()) {
150        // FIXME: We should throw the caught error.
151        return ScriptPromise::reject(scriptState, V8ThrowException::createTypeError(exceptionState.message(), scriptState->isolate()));
152    }
153    return m_fetchManager->fetch(scriptState, r->request());
154}
155
156ServiceWorkerClients* ServiceWorkerGlobalScope::clients()
157{
158    if (!m_clients)
159        m_clients = ServiceWorkerClients::create();
160    return m_clients;
161}
162
163void ServiceWorkerGlobalScope::close(ExceptionState& exceptionState)
164{
165    exceptionState.throwDOMException(InvalidAccessError, "Not supported.");
166}
167
168const AtomicString& ServiceWorkerGlobalScope::interfaceName() const
169{
170    return EventTargetNames::ServiceWorkerGlobalScope;
171}
172
173void ServiceWorkerGlobalScope::trace(Visitor* visitor)
174{
175    visitor->trace(m_clients);
176    visitor->trace(m_caches);
177    WorkerGlobalScope::trace(visitor);
178}
179
180void ServiceWorkerGlobalScope::importScripts(const Vector<String>& urls, ExceptionState& exceptionState)
181{
182    // Bust the MemoryCache to ensure script requests reach the browser-side
183    // and get added to and retrieved from the ServiceWorker's script cache.
184    // FIXME: Revisit in light of the solution to crbug/388375.
185    for (Vector<String>::const_iterator it = urls.begin(); it != urls.end(); ++it)
186        MemoryCache::removeURLFromCache(this->executionContext(), completeURL(*it));
187    WorkerGlobalScope::importScripts(urls, exceptionState);
188}
189
190void ServiceWorkerGlobalScope::logExceptionToConsole(const String& errorMessage, int scriptId, const String& sourceURL, int lineNumber, int columnNumber, PassRefPtrWillBeRawPtr<ScriptCallStack> callStack)
191{
192    WorkerGlobalScope::logExceptionToConsole(errorMessage, scriptId, sourceURL, lineNumber, columnNumber, callStack);
193    RefPtrWillBeRawPtr<ConsoleMessage> consoleMessage = ConsoleMessage::create(JSMessageSource, ErrorMessageLevel, errorMessage, sourceURL, lineNumber);
194    consoleMessage->setScriptId(scriptId);
195    consoleMessage->setCallStack(callStack);
196    addMessageToWorkerConsole(consoleMessage.release());
197}
198
199} // namespace blink
200