WebKit Bugzilla
Attachment 341952 Details for
Bug 182444
: ServiceWorker registration should store any script fetched through importScripts
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
Remember
[x]
|
Forgot Password
Login:
[x]
[patch]
Patch
bug-182444-20180604205243.patch (text/plain), 63.14 KB, created by
youenn fablet
on 2018-06-04 20:52:44 PDT
(
hide
)
Description:
Patch
Filename:
MIME Type:
Creator:
youenn fablet
Created:
2018-06-04 20:52:44 PDT
Size:
63.14 KB
patch
obsolete
>Subversion Revision: 232461 >diff --git a/Source/WTF/ChangeLog b/Source/WTF/ChangeLog >index 81d2c51125e4eb892900d7390c1ca3cf2bd83317..01163dd73a711a980cbaf740b407b9ef594025e3 100644 >--- a/Source/WTF/ChangeLog >+++ b/Source/WTF/ChangeLog >@@ -1,3 +1,13 @@ >+2018-06-04 Youenn Fablet <youenn@apple.com> >+ >+ ServiceWorker registration should store any script fetched through importScripts >+ https://bugs.webkit.org/show_bug.cgi?id=182444 >+ <rdar://problem/37164835> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * wtf/persistence/PersistentCoders.h: >+ > 2018-05-31 Commit Queue <commit-queue@webkit.org> > > Unreviewed, rolling out r232212. >diff --git a/Source/WebCore/ChangeLog b/Source/WebCore/ChangeLog >index 64b0f12a6de55cb72ed70ddca1a2e5dbed50fad5..e3825a91b31568bea6d3e397138e820dcce619bb 100644 >--- a/Source/WebCore/ChangeLog >+++ b/Source/WebCore/ChangeLog >@@ -1,3 +1,64 @@ >+2018-06-04 Youenn Fablet <youenn@apple.com> >+ >+ ServiceWorker registration should store any script fetched through importScripts >+ https://bugs.webkit.org/show_bug.cgi?id=182444 >+ <rdar://problem/37164835> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Covered by added test http/wpt/service-workers/persistent-importScripts.html. >+ >+ Update importScripts implementation to look for/update the resource map in case of service worker scope. >+ This resource map is stored persistently and sent when running the service worker. >+ >+ Add support to persistent storage of this resource map. >+ This requires updating the schema database. >+ The schema version is bumped to 2 and we delete any data related to old registrations. >+ >+ When updating the service worker, the resource map is currently flushed so that all scripts will be retrieved from the network. >+ >+ Did some limited refactoring to put more loading handling in WorkerScriptLoader. >+ >+ * workers/WorkerGlobalScope.cpp: >+ (WebCore::WorkerGlobalScope::importScripts): >+ * workers/WorkerScriptLoader.cpp: >+ (WebCore::WorkerScriptLoader::loadSynchronously): >+ (WebCore::WorkerScriptLoader::script): >+ * workers/WorkerScriptLoader.h: >+ * workers/service/ServiceWorkerContextData.cpp: >+ (WebCore::ServiceWorkerContextData::isolatedCopy const): >+ * workers/service/ServiceWorkerContextData.h: >+ (WebCore::ServiceWorkerContextData::ImportedScript::isolatedCopy const): >+ (WebCore::ServiceWorkerContextData::ImportedScript::encode const): >+ (WebCore::ServiceWorkerContextData::ImportedScript::decode): >+ (WebCore::ServiceWorkerContextData::encode const): >+ (WebCore::ServiceWorkerContextData::decode): >+ * workers/service/ServiceWorkerGlobalScope.cpp: >+ (WebCore::ServiceWorkerGlobalScope::scriptResource const): >+ (WebCore::ServiceWorkerGlobalScope::setScriptResource): >+ * workers/service/ServiceWorkerGlobalScope.h: >+ * workers/service/context/SWContextManager.h: >+ * workers/service/server/RegistrationDatabase.cpp: >+ (WebCore::v1RecordsTableSchema): >+ (WebCore::RegistrationDatabase::doPushChanges): >+ (WebCore::RegistrationDatabase::importRecords): >+ * workers/service/server/SWServer.cpp: >+ (WebCore::SWServer::addRegistrationFromStore): >+ (WebCore::SWServer::updateWorker): >+ (WebCore::SWServer::installContextData): >+ * workers/service/server/SWServer.h: >+ * workers/service/server/SWServerJobQueue.cpp: >+ (WebCore::SWServerJobQueue::scriptFetchFinished): >+ * workers/service/server/SWServerToContextConnection.cpp: >+ (WebCore::SWServerToContextConnection::setScriptResource): >+ * workers/service/server/SWServerToContextConnection.h: >+ * workers/service/server/SWServerWorker.cpp: >+ (WebCore::SWServerWorker::SWServerWorker): >+ (WebCore::m_scriptResourceMap): >+ (WebCore::SWServerWorker::contextData const): >+ (WebCore::SWServerWorker::setScriptResource): >+ * workers/service/server/SWServerWorker.h: >+ > 2018-06-03 Michael Catanzaro <mcatanzaro@igalia.com> > > REGRESSION(r232338): [GTK] Broke a few layout tests >diff --git a/Source/WebKit/ChangeLog b/Source/WebKit/ChangeLog >index bf8099683c8958555ffb628a3fe3e4720a01c43e..e4874e8954d9ab33ab479fa380db524c5b3a59e6 100644 >--- a/Source/WebKit/ChangeLog >+++ b/Source/WebKit/ChangeLog >@@ -1,3 +1,24 @@ >+2018-06-04 Youenn Fablet <youenn@apple.com> >+ >+ ServiceWorker registration should store any script fetched through importScripts >+ https://bugs.webkit.org/show_bug.cgi?id=182444 >+ <rdar://problem/37164835> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add C API to kill storage process. >+ In case a Storage Process is closed or crashed, ensure that all its related service worker processes also exit. >+ >+ * StorageProcess/ServiceWorker/WebSWServerToContextConnection.messages.in: >+ * UIProcess/API/C/WKContext.cpp: >+ (WKContextTerminateStorageProcess): >+ * UIProcess/API/C/WKContextPrivate.h: >+ * WebProcess/WebProcess.cpp: >+ (WebKit::WebProcess::webToStorageProcessConnectionClosed): >+ * WebProcess/Storage/WebSWContextManagerConnection.cpp: >+ (WebKit::WebSWContextManagerConnection::setScriptResource): >+ * WebProcess/Storage/WebSWContextManagerConnection.h: >+ > 2018-06-03 Youenn Fablet <youenn@apple.com> > > NetworkCORSPreflightChecker should set the preflight request User-Agent header >diff --git a/Source/WTF/wtf/CrossThreadCopier.h b/Source/WTF/wtf/CrossThreadCopier.h >index 693e2fa06f0c25bd503fc0b8f2066d5e79c0da10..86ca68e83f910e28ba7d09f37283d867fc1a59eb 100644 >--- a/Source/WTF/wtf/CrossThreadCopier.h >+++ b/Source/WTF/wtf/CrossThreadCopier.h >@@ -132,6 +132,18 @@ template<typename T> struct CrossThreadCopierBase<false, false, HashSet<T> > { > } > }; > >+// Default specialization for HashMaps of CrossThreadCopyable classes >+template<typename K, typename V> struct CrossThreadCopierBase<false, false, HashMap<K, V> > { >+ typedef HashMap<K, V> Type; >+ static Type copy(const Type& source) >+ { >+ Type destination; >+ for (auto& keyValue : source) >+ destination.add(CrossThreadCopier<K>::copy(keyValue.key), CrossThreadCopier<V>::copy(keyValue.value)); >+ return destination; >+ } >+}; >+ > // Default specialization for std::optional of CrossThreadCopyable class. > template<typename T> struct CrossThreadCopierBase<false, false, std::optional<T>> { > typedef std::optional<T> Type; >diff --git a/Source/WTF/wtf/persistence/PersistentCoders.h b/Source/WTF/wtf/persistence/PersistentCoders.h >index 741e21dafa958388499d4b07a64fea702f8d7b48..1bd4d6f7cd41ba4ed52f28c084dd81afc7184f70 100644 >--- a/Source/WTF/wtf/persistence/PersistentCoders.h >+++ b/Source/WTF/wtf/persistence/PersistentCoders.h >@@ -155,10 +155,12 @@ template<typename T, size_t inlineCapacity> struct VectorCoder<true, T, inlineCa > > static bool decode(Decoder& decoder, Vector<T, inlineCapacity>& vector) > { >- uint64_t size; >- if (!decoder.decode(size)) >+ uint64_t decodedSize; >+ if (!decoder.decode(decodedSize)) > return false; > >+ auto size = safeCast<size_t>(decodedSize); >+ > // Since we know the total size of the elements, we can allocate the vector in > // one fell swoop. Before allocating we must however make sure that the decoder buffer > // is big enough. >diff --git a/Source/WebCore/workers/WorkerGlobalScope.cpp b/Source/WebCore/workers/WorkerGlobalScope.cpp >index f32ef2e2d7254b9e7d3255abff310e4116e2133c..a5b1b36eda2d68114806aaaed45c5b89796fa1fb 100644 >--- a/Source/WebCore/workers/WorkerGlobalScope.cpp >+++ b/Source/WebCore/workers/WorkerGlobalScope.cpp >@@ -33,7 +33,6 @@ > #include "IDBConnectionProxy.h" > #include "ImageBitmapOptions.h" > #include "InspectorInstrumentation.h" >-#include "MIMETypeRegistry.h" > #include "Performance.h" > #include "ScheduledAction.h" > #include "ScriptSourceCode.h" >@@ -285,16 +284,9 @@ ExceptionOr<void> WorkerGlobalScope::importScripts(const Vector<String>& urls) > return Exception { NetworkError }; > > auto scriptLoader = WorkerScriptLoader::create(); >- scriptLoader->loadSynchronously(this, url, FetchOptions::Mode::NoCors, cachePolicy, shouldBypassMainWorldContentSecurityPolicy ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceScriptSrcDirective, resourceRequestIdentifier()); >- >- // If the fetching attempt failed, throw a NetworkError exception and abort all these steps. >- if (scriptLoader->failed()) >- return Exception { NetworkError, scriptLoader->error().localizedDescription() }; >- >-#if ENABLE(SERVICE_WORKER) >- if (isServiceWorkerGlobalScope && !MIMETypeRegistry::isSupportedJavaScriptMIMEType(scriptLoader->responseMIMEType())) >- return Exception { NetworkError }; >-#endif >+ auto cspEnforcement = shouldBypassMainWorldContentSecurityPolicy ? ContentSecurityPolicyEnforcement::DoNotEnforce : ContentSecurityPolicyEnforcement::EnforceScriptSrcDirective; >+ if (auto exception = scriptLoader->loadSynchronously(this, url, FetchOptions::Mode::NoCors, cachePolicy, cspEnforcement, resourceRequestIdentifier())) >+ return WTFMove(*exception); > > InspectorInstrumentation::scriptImported(*this, scriptLoader->identifier(), scriptLoader->script()); > >diff --git a/Source/WebCore/workers/WorkerScriptLoader.cpp b/Source/WebCore/workers/WorkerScriptLoader.cpp >index 1f91cd5dcaf91ae854e80ab3cb4ffd7cc607d378..16e7f306ca3c4470075331690eadcaf7b04f4c79 100644 >--- a/Source/WebCore/workers/WorkerScriptLoader.cpp >+++ b/Source/WebCore/workers/WorkerScriptLoader.cpp >@@ -29,9 +29,11 @@ > > #include "ContentSecurityPolicy.h" > #include "FetchIdioms.h" >+#include "MIMETypeRegistry.h" > #include "ResourceResponse.h" > #include "ScriptExecutionContext.h" > #include "ServiceWorker.h" >+#include "ServiceWorkerGlobalScope.h" > #include "TextResourceDecoder.h" > #include "WorkerGlobalScope.h" > #include "WorkerScriptLoaderClient.h" >@@ -44,7 +46,7 @@ WorkerScriptLoader::WorkerScriptLoader() = default; > > WorkerScriptLoader::~WorkerScriptLoader() = default; > >-void WorkerScriptLoader::loadSynchronously(ScriptExecutionContext* scriptExecutionContext, const URL& url, FetchOptions::Mode mode, FetchOptions::Cache cachePolicy, ContentSecurityPolicyEnforcement contentSecurityPolicyEnforcement, const String& initiatorIdentifier) >+std::optional<Exception> WorkerScriptLoader::loadSynchronously(ScriptExecutionContext* scriptExecutionContext, const URL& url, FetchOptions::Mode mode, FetchOptions::Cache cachePolicy, ContentSecurityPolicyEnforcement contentSecurityPolicyEnforcement, const String& initiatorIdentifier) > { > ASSERT(scriptExecutionContext); > auto& workerGlobalScope = downcast<WorkerGlobalScope>(*scriptExecutionContext); >@@ -52,9 +54,22 @@ void WorkerScriptLoader::loadSynchronously(ScriptExecutionContext* scriptExecuti > m_url = url; > m_destination = FetchOptions::Destination::Script; > >+ bool isServiceWorkerGlobalScope = is<ServiceWorkerGlobalScope>(workerGlobalScope); >+ >+#if ENABLE(SERVICE_WORKER) >+ if (isServiceWorkerGlobalScope) { >+ if (auto* scriptResource = downcast<ServiceWorkerGlobalScope>(workerGlobalScope).scriptResource(url)) { >+ m_script.append(scriptResource->script); >+ m_responseURL = URL { URL { }, scriptResource->responseURL }; >+ m_responseMIMEType = scriptResource->mimeType; >+ return std::nullopt; >+ } >+ } >+#endif >+ > std::unique_ptr<ResourceRequest> request(createResourceRequest(initiatorIdentifier)); > if (!request) >- return; >+ return std::nullopt; > > ASSERT_WITH_SECURITY_IMPLICATION(is<WorkerGlobalScope>(scriptExecutionContext)); > >@@ -70,11 +85,25 @@ void WorkerScriptLoader::loadSynchronously(ScriptExecutionContext* scriptExecuti > options.contentSecurityPolicyEnforcement = contentSecurityPolicyEnforcement; > options.destination = m_destination; > #if ENABLE(SERVICE_WORKER) >- options.serviceWorkersMode = workerGlobalScope.isServiceWorkerGlobalScope() ? ServiceWorkersMode::None : ServiceWorkersMode::All; >+ options.serviceWorkersMode = isServiceWorkerGlobalScope ? ServiceWorkersMode::None : ServiceWorkersMode::All; > if (auto* activeServiceWorker = workerGlobalScope.activeServiceWorker()) > options.serviceWorkerRegistrationIdentifier = activeServiceWorker->registrationIdentifier(); > #endif > WorkerThreadableLoader::loadResourceSynchronously(workerGlobalScope, WTFMove(*request), *this, options); >+ >+ // If the fetching attempt failed, throw a NetworkError exception and abort all these steps. >+ if (failed()) >+ return Exception { NetworkError, error().localizedDescription() }; >+ >+#if ENABLE(SERVICE_WORKER) >+ if (isServiceWorkerGlobalScope) { >+ if (!MIMETypeRegistry::isSupportedJavaScriptMIMEType(responseMIMEType())) >+ return Exception { NetworkError, ASCIILiteral("mime type is not a supported JavaScript mime type") }; >+ >+ downcast<ServiceWorkerGlobalScope>(workerGlobalScope).setScriptResource(url, ServiceWorkerContextData::ImportedScript { script(), m_responseURL, m_responseMIMEType }); >+ } >+#endif >+ return std::nullopt; > } > > void WorkerScriptLoader::loadAsynchronously(ScriptExecutionContext& scriptExecutionContext, ResourceRequest&& scriptRequest, FetchOptions&& fetchOptions, ContentSecurityPolicyEnforcement contentSecurityPolicyEnforcement, ServiceWorkersMode serviceWorkerMode, WorkerScriptLoaderClient& client) >diff --git a/Source/WebCore/workers/WorkerScriptLoader.h b/Source/WebCore/workers/WorkerScriptLoader.h >index 6f1d852c8870c12eaa6a7e3140b5541e58919473..e57c323cde9e6f4f80af9b25b2cfe1ee2e7374d9 100644 >--- a/Source/WebCore/workers/WorkerScriptLoader.h >+++ b/Source/WebCore/workers/WorkerScriptLoader.h >@@ -54,7 +54,7 @@ public: > return adoptRef(*new WorkerScriptLoader); > } > >- void loadSynchronously(ScriptExecutionContext*, const URL&, FetchOptions::Mode, FetchOptions::Cache, ContentSecurityPolicyEnforcement, const String& initiatorIdentifier); >+ std::optional<Exception> loadSynchronously(ScriptExecutionContext*, const URL&, FetchOptions::Mode, FetchOptions::Cache, ContentSecurityPolicyEnforcement, const String& initiatorIdentifier); > void loadAsynchronously(ScriptExecutionContext&, ResourceRequest&&, FetchOptions&&, ContentSecurityPolicyEnforcement, ServiceWorkersMode, WorkerScriptLoaderClient&); > > void notifyError(); >diff --git a/Source/WebCore/workers/service/ServiceWorkerContextData.cpp b/Source/WebCore/workers/service/ServiceWorkerContextData.cpp >index cd09a97c947977f33f87e7d96f9865dba40eb01c..2bc4e73c063336878ab3666a9d6993e4db8d2df8 100644 >--- a/Source/WebCore/workers/service/ServiceWorkerContextData.cpp >+++ b/Source/WebCore/workers/service/ServiceWorkerContextData.cpp >@@ -25,6 +25,7 @@ > > #include "config.h" > #include "ServiceWorkerContextData.h" >+#include <wtf/CrossThreadCopier.h> > > #if ENABLE(SERVICE_WORKER) > >@@ -32,7 +33,7 @@ namespace WebCore { > > ServiceWorkerContextData ServiceWorkerContextData::isolatedCopy() const > { >- return { jobDataIdentifier, registration.isolatedCopy(), serviceWorkerIdentifier, script.isolatedCopy(), contentSecurityPolicy.isolatedCopy(), scriptURL.isolatedCopy(), workerType, sessionID, loadedFromDisk }; >+ return { jobDataIdentifier, registration.isolatedCopy(), serviceWorkerIdentifier, script.isolatedCopy(), contentSecurityPolicy.isolatedCopy(), scriptURL.isolatedCopy(), workerType, sessionID, loadedFromDisk, crossThreadCopy(scriptResourceMap) }; > } > > } // namespace WebCore >diff --git a/Source/WebCore/workers/service/ServiceWorkerContextData.h b/Source/WebCore/workers/service/ServiceWorkerContextData.h >index b93d922162e0ac794a1e752bb9abe4c1aa31b9d6..3be950425ea780cffd5bbee785fa4532af94a3d7 100644 >--- a/Source/WebCore/workers/service/ServiceWorkerContextData.h >+++ b/Source/WebCore/workers/service/ServiceWorkerContextData.h >@@ -30,14 +30,46 @@ > #include "ServiceWorkerJobDataIdentifier.h" > #include "ServiceWorkerRegistrationData.h" > #include "URL.h" >+#include "URLHash.h" > #include "WorkerType.h" > #include <pal/SessionID.h> >+#include <wtf/HashMap.h> > > #if ENABLE(SERVICE_WORKER) > > namespace WebCore { > > struct ServiceWorkerContextData { >+ >+ struct ImportedScript { >+ String script; >+ URL responseURL; >+ String mimeType; >+ >+ ImportedScript isolatedCopy() const { return { script.isolatedCopy(), responseURL.isolatedCopy(), mimeType.isolatedCopy() }; } >+ >+ template<class Encoder> void encode(Encoder& encoder) const >+ { >+ encoder << script << responseURL << mimeType; >+ } >+ >+ template<class Decoder> static bool decode(Decoder& decoder, ImportedScript& script) >+ { >+ ImportedScript importedScript; >+ if (!decoder.decode(importedScript.script)) >+ return false; >+ >+ if (!decoder.decode(importedScript.responseURL)) >+ return false; >+ >+ if (!decoder.decode(importedScript.mimeType)) >+ return false; >+ >+ script = WTFMove(importedScript); >+ return true; >+ } >+ }; >+ > std::optional<ServiceWorkerJobDataIdentifier> jobDataIdentifier; > ServiceWorkerRegistrationData registration; > ServiceWorkerIdentifier serviceWorkerIdentifier; >@@ -47,10 +79,11 @@ struct ServiceWorkerContextData { > WorkerType workerType; > PAL::SessionID sessionID; > bool loadedFromDisk; >+ HashMap<URL, ImportedScript> scriptResourceMap; > > template<class Encoder> void encode(Encoder&) const; > template<class Decoder> static std::optional<ServiceWorkerContextData> decode(Decoder&); >- >+ > ServiceWorkerContextData isolatedCopy() const; > }; > >@@ -58,6 +91,7 @@ template<class Encoder> > void ServiceWorkerContextData::encode(Encoder& encoder) const > { > encoder << jobDataIdentifier << registration << serviceWorkerIdentifier << script << contentSecurityPolicy << scriptURL << workerType << sessionID << loadedFromDisk; >+ encoder << scriptResourceMap; > } > > template<class Decoder> >@@ -101,7 +135,11 @@ std::optional<ServiceWorkerContextData> ServiceWorkerContextData::decode(Decoder > if (!decoder.decode(loadedFromDisk)) > return std::nullopt; > >- return {{ WTFMove(*jobDataIdentifier), WTFMove(*registration), WTFMove(*serviceWorkerIdentifier), WTFMove(script), WTFMove(contentSecurityPolicy), WTFMove(scriptURL), workerType, sessionID, loadedFromDisk }}; >+ HashMap<URL, ImportedScript> scriptResourceMap; >+ if (!decoder.decode(scriptResourceMap)) >+ return std::nullopt; >+ >+ return {{ WTFMove(*jobDataIdentifier), WTFMove(*registration), WTFMove(*serviceWorkerIdentifier), WTFMove(script), WTFMove(contentSecurityPolicy), WTFMove(scriptURL), workerType, sessionID, loadedFromDisk, WTFMove(scriptResourceMap) }}; > } > > } // namespace WebCore >diff --git a/Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp b/Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp >index 628151149ef0cff12fa82b0bfbe9849f443e60b2..fe429abb92e41ff4ec253301bf5f4bd34ac84bb4 100644 >--- a/Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp >+++ b/Source/WebCore/workers/service/ServiceWorkerGlobalScope.cpp >@@ -130,6 +130,22 @@ void ServiceWorkerGlobalScope::updateExtendedEventsSet(ExtendableEvent* newEvent > }); > } > >+const ServiceWorkerContextData::ImportedScript* ServiceWorkerGlobalScope::scriptResource(const URL& url) const >+{ >+ auto iterator = m_contextData.scriptResourceMap.find(url); >+ return iterator == m_contextData.scriptResourceMap.end() ? nullptr : &iterator->value; >+} >+ >+void ServiceWorkerGlobalScope::setScriptResource(const URL& url, ServiceWorkerContextData::ImportedScript&& script) >+{ >+ callOnMainThread([threadIdentifier = thread().identifier(), url = url.isolatedCopy(), script = script.isolatedCopy()] { >+ if (auto* connection = SWContextManager::singleton().connection()) >+ connection->setScriptResource(threadIdentifier, url, script); >+ }); >+ >+ m_contextData.scriptResourceMap.set(url, WTFMove(script)); >+} >+ > } // namespace WebCore > > #endif // ENABLE(SERVICE_WORKER) >diff --git a/Source/WebCore/workers/service/ServiceWorkerGlobalScope.h b/Source/WebCore/workers/service/ServiceWorkerGlobalScope.h >index 1369499e74fa65b717077d14c1271c481ef90230..0b7f78c7a88ab2bdb7901be25bfcda4eedce8c5b 100644 >--- a/Source/WebCore/workers/service/ServiceWorkerGlobalScope.h >+++ b/Source/WebCore/workers/service/ServiceWorkerGlobalScope.h >@@ -64,6 +64,9 @@ public: > > void updateExtendedEventsSet(ExtendableEvent* newEvent = nullptr); > >+ const ServiceWorkerContextData::ImportedScript* scriptResource(const URL&) const; >+ void setScriptResource(const URL&, ServiceWorkerContextData::ImportedScript&&); >+ > private: > ServiceWorkerGlobalScope(const ServiceWorkerContextData&, const URL&, Ref<SecurityOrigin>&&, const String& identifier, const String& userAgent, bool isOnline, ServiceWorkerThread&, bool shouldBypassMainWorldContentSecurityPolicy, Ref<SecurityOrigin>&& topOrigin, MonotonicTime timeOrigin, IDBClient::IDBConnectionProxy*, SocketProvider*, PAL::SessionID); > >diff --git a/Source/WebCore/workers/service/context/SWContextManager.h b/Source/WebCore/workers/service/context/SWContextManager.h >index 453c6fe962733b90c56b5be03595cfe73b07dab9..b7b52e6b4e5ab66c334fb321b6aa430473d3347c 100644 >--- a/Source/WebCore/workers/service/context/SWContextManager.h >+++ b/Source/WebCore/workers/service/context/SWContextManager.h >@@ -54,12 +54,13 @@ public: > virtual void didFinishActivation(ServiceWorkerIdentifier) = 0; > virtual void setServiceWorkerHasPendingEvents(ServiceWorkerIdentifier, bool) = 0; > virtual void workerTerminated(ServiceWorkerIdentifier) = 0; >- virtual void skipWaiting(ServiceWorkerIdentifier, WTF::Function<void()>&& callback) = 0; >+ virtual void skipWaiting(ServiceWorkerIdentifier, Function<void()>&&) = 0; >+ virtual void setScriptResource(ServiceWorkerIdentifier, const URL&, const ServiceWorkerContextData::ImportedScript&) = 0; > >- using FindClientByIdentifierCallback = WTF::CompletionHandler<void(ExceptionOr<std::optional<ServiceWorkerClientData>>&&)>; >+ using FindClientByIdentifierCallback = CompletionHandler<void(ExceptionOr<std::optional<ServiceWorkerClientData>>&&)>; > virtual void findClientByIdentifier(ServiceWorkerIdentifier, ServiceWorkerClientIdentifier, FindClientByIdentifierCallback&&) = 0; > virtual void matchAll(ServiceWorkerIdentifier, const ServiceWorkerClientQueryOptions&, ServiceWorkerClientsMatchAllCallback&&) = 0; >- virtual void claim(ServiceWorkerIdentifier, WTF::CompletionHandler<void()>&&) = 0; >+ virtual void claim(ServiceWorkerIdentifier, CompletionHandler<void()>&&) = 0; > }; > > WEBCORE_EXPORT void setConnection(std::unique_ptr<Connection>&&); >diff --git a/Source/WebCore/workers/service/server/RegistrationDatabase.cpp b/Source/WebCore/workers/service/server/RegistrationDatabase.cpp >index eb1aaf21e6cd28b984a27369679a4646db05ffa3..7d19f517ca932b91a24376ea19885e3000a56cff 100644 >--- a/Source/WebCore/workers/service/server/RegistrationDatabase.cpp >+++ b/Source/WebCore/workers/service/server/RegistrationDatabase.cpp >@@ -47,31 +47,36 @@ > > namespace WebCore { > >-static const int schemaVersion = 1; >+static const uint64_t schemaVersion = 2; > >-static const String v1RecordsTableSchema(const String& tableName) >+static const String recordsTableSchema(const String& tableName) > { >- return makeString("CREATE TABLE ", tableName, " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, origin TEXT NOT NULL ON CONFLICT FAIL, scopeURL TEXT NOT NULL ON CONFLICT FAIL, topOrigin TEXT NOT NULL ON CONFLICT FAIL, lastUpdateCheckTime DOUBLE NOT NULL ON CONFLICT FAIL, updateViaCache TEXT NOT NULL ON CONFLICT FAIL, scriptURL TEXT NOT NULL ON CONFLICT FAIL, script TEXT NOT NULL ON CONFLICT FAIL, workerType TEXT NOT NULL ON CONFLICT FAIL, contentSecurityPolicy BLOB NOT NULL ON CONFLICT FAIL)"); >+ return makeString("CREATE TABLE ", tableName, " (key TEXT NOT NULL ON CONFLICT FAIL UNIQUE ON CONFLICT REPLACE, origin TEXT NOT NULL ON CONFLICT FAIL, scopeURL TEXT NOT NULL ON CONFLICT FAIL, topOrigin TEXT NOT NULL ON CONFLICT FAIL, lastUpdateCheckTime DOUBLE NOT NULL ON CONFLICT FAIL, updateViaCache TEXT NOT NULL ON CONFLICT FAIL, scriptURL TEXT NOT NULL ON CONFLICT FAIL, script TEXT NOT NULL ON CONFLICT FAIL, workerType TEXT NOT NULL ON CONFLICT FAIL, contentSecurityPolicy BLOB NOT NULL ON CONFLICT FAIL, scriptResourceMap BLOB NOT NULL ON CONFLICT FAIL)"); > } > >-static const String v1RecordsTableSchema() >+static const String recordsTableSchema() > { > ASSERT(!isMainThread()); >- static NeverDestroyed<WTF::String> schema(v1RecordsTableSchema("Records")); >+ static NeverDestroyed<String> schema(recordsTableSchema("Records")); > return schema; > } > >-static const String v1RecordsTableSchemaAlternate() >+static const String recordsTableSchemaAlternate() > { > ASSERT(!isMainThread()); >- static NeverDestroyed<WTF::String> schema(v1RecordsTableSchema("\"Records\"")); >+ static NeverDestroyed<String> schema(recordsTableSchema("\"Records\"")); > return schema; > } > >+static inline String databaseFilenameFromVersion(uint64_t version) >+{ >+ return makeString("ServiceWorkerRegistrations-", String::number(version), ".sqlite3"); >+} >+ > static const String& databaseFilename() > { > ASSERT(isMainThread()); >- static NeverDestroyed<String> filename = makeString("ServiceWorkerRegistrations-", String::number(schemaVersion), ".sqlite3"); >+ static NeverDestroyed<String> filename = databaseFilenameFromVersion(schemaVersion); > return filename; > } > >@@ -80,6 +85,12 @@ String serviceWorkerRegistrationDatabaseFilename(const String& databaseDirectory > return FileSystem::pathByAppendingComponent(databaseDirectory, databaseFilename()); > } > >+static inline void cleanOldDatabases(const String& databaseDirectory) >+{ >+ for (uint64_t version = 1; version < schemaVersion; ++version) >+ SQLiteFileSystem::deleteDatabaseFile(FileSystem::pathByAppendingComponent(databaseDirectory, databaseFilenameFromVersion(version))); >+} >+ > RegistrationDatabase::RegistrationDatabase(RegistrationStore& store, const String& databaseDirectory) > : CrossThreadTaskHandler("ServiceWorker I/O Thread") > , m_store(store) >@@ -104,6 +115,8 @@ void RegistrationDatabase::openSQLiteDatabase(const String& fullFilename) > ASSERT(!isMainThread()); > ASSERT(!m_database); > >+ cleanOldDatabases(m_databaseDirectory); >+ > LOG(ServiceWorker, "ServiceWorker RegistrationDatabase opening file %s", fullFilename.utf8().data()); > > String errorMessage; >@@ -166,7 +179,7 @@ String RegistrationDatabase::ensureValidRecordsTable() > > // If there is no Records table at all, create it and then bail. > if (sqliteResult == SQLITE_DONE) { >- if (!m_database->executeCommand(v1RecordsTableSchema())) >+ if (!m_database->executeCommand(recordsTableSchema())) > return String::format("Could not create Records table in database (%i) - %s", m_database->lastError(), m_database->lastErrorMsg()); > return { }; > } >@@ -179,7 +192,7 @@ String RegistrationDatabase::ensureValidRecordsTable() > > ASSERT(!currentSchema.isEmpty()); > >- if (currentSchema == v1RecordsTableSchema() || currentSchema == v1RecordsTableSchemaAlternate()) >+ if (currentSchema == recordsTableSchema() || currentSchema == recordsTableSchemaAlternate()) > return { }; > > // This database has a Records table but it is not a schema we expect. >@@ -275,7 +288,7 @@ void RegistrationDatabase::doPushChanges(Vector<ServiceWorkerContextData>&& data > SQLiteTransaction transaction(*m_database); > transaction.begin(); > >- SQLiteStatement sql(*m_database, ASCIILiteral("INSERT INTO Records VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); >+ SQLiteStatement sql(*m_database, ASCIILiteral("INSERT INTO Records VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)")); > if (sql.prepare() != SQLITE_OK) { > RELEASE_LOG_ERROR(ServiceWorker, "Failed to prepare statement to store registration data into records table (%i) - %s", m_database->lastError(), m_database->lastErrorMsg()); > return; >@@ -294,8 +307,11 @@ void RegistrationDatabase::doPushChanges(Vector<ServiceWorkerContextData>&& data > continue; > } > >- WTF::Persistence::Encoder encoder; >- data.contentSecurityPolicy.encode(encoder); >+ WTF::Persistence::Encoder cspEncoder; >+ data.contentSecurityPolicy.encode(cspEncoder); >+ >+ WTF::Persistence::Encoder scriptResourceMapEncoder; >+ scriptResourceMapEncoder.encode(data.scriptResourceMap); > > if (sql.bindText(1, data.registration.key.toDatabaseKey()) != SQLITE_OK > || sql.bindText(2, data.registration.scopeURL.protocolHostAndPort()) != SQLITE_OK >@@ -306,7 +322,8 @@ void RegistrationDatabase::doPushChanges(Vector<ServiceWorkerContextData>&& data > || sql.bindText(7, data.scriptURL.string()) != SQLITE_OK > || sql.bindText(8, data.script) != SQLITE_OK > || sql.bindText(9, workerTypeToString(data.workerType)) != SQLITE_OK >- || sql.bindBlob(10, encoder.buffer(), encoder.bufferSize()) != SQLITE_OK >+ || sql.bindBlob(10, cspEncoder.buffer(), cspEncoder.bufferSize()) != SQLITE_OK >+ || sql.bindBlob(11, scriptResourceMapEncoder.buffer(), scriptResourceMapEncoder.bufferSize()) != SQLITE_OK > || sql.step() != SQLITE_DONE) { > RELEASE_LOG_ERROR(ServiceWorker, "Failed to store registration data into records table (%i) - %s", m_database->lastError(), m_database->lastErrorMsg()); > return; >@@ -341,11 +358,21 @@ String RegistrationDatabase::importRecords() > > Vector<uint8_t> contentSecurityPolicyData; > sql.getColumnBlobAsVector(9, contentSecurityPolicyData); >- WTF::Persistence::Decoder decoder(contentSecurityPolicyData.data(), contentSecurityPolicyData.size()); >+ WTF::Persistence::Decoder cspDecoder(contentSecurityPolicyData.data(), contentSecurityPolicyData.size()); > ContentSecurityPolicyResponseHeaders contentSecurityPolicy; >- if (contentSecurityPolicyData.size() && !ContentSecurityPolicyResponseHeaders::decode(decoder, contentSecurityPolicy)) >+ if (contentSecurityPolicyData.size() && !ContentSecurityPolicyResponseHeaders::decode(cspDecoder, contentSecurityPolicy)) > continue; > >+ Vector<uint8_t> scriptResourceMapData; >+ sql.getColumnBlobAsVector(10, scriptResourceMapData); >+ HashMap<URL, ServiceWorkerContextData::ImportedScript> scriptResourceMap; >+ >+ WTF::Persistence::Decoder scriptResourceMapDecoder(scriptResourceMapData.data(), scriptResourceMapData.size()); >+ if (scriptResourceMapData.size()) { >+ if (!scriptResourceMapDecoder.decode(scriptResourceMap)) >+ continue; >+ } >+ > // Validate the input for this registration. > // If any part of this input is invalid, let's skip this registration. > // FIXME: Should we return an error skipping *all* registrations? >@@ -356,7 +383,7 @@ String RegistrationDatabase::importRecords() > auto registrationIdentifier = generateObjectIdentifier<ServiceWorkerRegistrationIdentifierType>(); > auto serviceWorkerData = ServiceWorkerData { workerIdentifier, scriptURL, ServiceWorkerState::Activated, *workerType, registrationIdentifier }; > auto registration = ServiceWorkerRegistrationData { WTFMove(*key), registrationIdentifier, URL(originURL, scopePath), *updateViaCache, lastUpdateCheckTime, std::nullopt, std::nullopt, WTFMove(serviceWorkerData) }; >- auto contextData = ServiceWorkerContextData { std::nullopt, WTFMove(registration), workerIdentifier, WTFMove(script), WTFMove(contentSecurityPolicy), WTFMove(scriptURL), *workerType, m_store.server().sessionID(), true }; >+ auto contextData = ServiceWorkerContextData { std::nullopt, WTFMove(registration), workerIdentifier, WTFMove(script), WTFMove(contentSecurityPolicy), WTFMove(scriptURL), *workerType, m_store.server().sessionID(), true, WTFMove(scriptResourceMap) }; > > postTaskReply(createCrossThreadTask(*this, &RegistrationDatabase::addRegistrationToStore, WTFMove(contextData))); > } >diff --git a/Source/WebCore/workers/service/server/SWServer.cpp b/Source/WebCore/workers/service/server/SWServer.cpp >index a20822aaa9b1152ca9447a058c31fd57a5fd662a..08c31a79029a5dad36526af2e14db787d356a744 100644 >--- a/Source/WebCore/workers/service/server/SWServer.cpp >+++ b/Source/WebCore/workers/service/server/SWServer.cpp >@@ -134,7 +134,7 @@ void SWServer::addRegistrationFromStore(ServiceWorkerContextData&& data) > auto registrationPtr = registration.get(); > addRegistration(WTFMove(registration)); > >- auto worker = SWServerWorker::create(*this, *registrationPtr, data.scriptURL, data.script, data.contentSecurityPolicy, data.workerType, data.serviceWorkerIdentifier); >+ auto worker = SWServerWorker::create(*this, *registrationPtr, data.scriptURL, data.script, data.contentSecurityPolicy, data.workerType, data.serviceWorkerIdentifier, HashMap<URL, ServiceWorkerContextData::ImportedScript> { data.scriptResourceMap }); > registrationPtr->updateRegistrationState(ServiceWorkerRegistrationState::Active, worker.ptr()); > worker->setState(ServiceWorkerState::Activated); > } >@@ -496,9 +496,9 @@ void SWServer::removeClientServiceWorkerRegistration(Connection& connection, Ser > registration->removeClientServiceWorkerRegistration(connection.identifier()); > } > >-void SWServer::updateWorker(Connection&, const ServiceWorkerJobDataIdentifier& jobDataIdentifier, SWServerRegistration& registration, const URL& url, const String& script, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicy, WorkerType type) >+void SWServer::updateWorker(Connection&, const ServiceWorkerJobDataIdentifier& jobDataIdentifier, SWServerRegistration& registration, const URL& url, const String& script, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicy, WorkerType type, HashMap<URL, ServiceWorkerContextData::ImportedScript>&& scriptResourceMap) > { >- tryInstallContextData({ jobDataIdentifier, registration.data(), generateObjectIdentifier<ServiceWorkerIdentifierType>(), script, contentSecurityPolicy, url, type, sessionID(), false }); >+ tryInstallContextData({ jobDataIdentifier, registration.data(), generateObjectIdentifier<ServiceWorkerIdentifierType>(), script, contentSecurityPolicy, url, type, sessionID(), false, WTFMove(scriptResourceMap) }); > } > > void SWServer::tryInstallContextData(ServiceWorkerContextData&& data) >@@ -543,7 +543,7 @@ void SWServer::installContextData(const ServiceWorkerContextData& data) > auto* registration = m_registrations.get(data.registration.key); > RELEASE_ASSERT(registration); > >- auto worker = SWServerWorker::create(*this, *registration, data.scriptURL, data.script, data.contentSecurityPolicy, data.workerType, data.serviceWorkerIdentifier); >+ auto worker = SWServerWorker::create(*this, *registration, data.scriptURL, data.script, data.contentSecurityPolicy, data.workerType, data.serviceWorkerIdentifier, HashMap<URL, ServiceWorkerContextData::ImportedScript> { data.scriptResourceMap }); > > auto* connection = worker->contextConnection(); > ASSERT(connection); >diff --git a/Source/WebCore/workers/service/server/SWServer.h b/Source/WebCore/workers/service/server/SWServer.h >index da8f8f84271095c02ac26b64e742b4ae25f1ad7d..cf5ecc165e48b327a02b24a03ff4c534c68387ca 100644 >--- a/Source/WebCore/workers/service/server/SWServer.h >+++ b/Source/WebCore/workers/service/server/SWServer.h >@@ -135,7 +135,7 @@ public: > void resolveUnregistrationJob(const ServiceWorkerJobData&, const ServiceWorkerRegistrationKey&, bool unregistrationResult); > void startScriptFetch(const ServiceWorkerJobData&, FetchOptions::Cache); > >- void updateWorker(Connection&, const ServiceWorkerJobDataIdentifier&, SWServerRegistration&, const URL&, const String& script, const ContentSecurityPolicyResponseHeaders&, WorkerType); >+ void updateWorker(Connection&, const ServiceWorkerJobDataIdentifier&, SWServerRegistration&, const URL&, const String& script, const ContentSecurityPolicyResponseHeaders&, WorkerType, HashMap<URL, ServiceWorkerContextData::ImportedScript>&&); > void terminateWorker(SWServerWorker&); > void syncTerminateWorker(SWServerWorker&); > void fireInstallEvent(SWServerWorker&); >diff --git a/Source/WebCore/workers/service/server/SWServerJobQueue.cpp b/Source/WebCore/workers/service/server/SWServerJobQueue.cpp >index dc36e5f2c439ec593d7f97626d24efb9d6ac632b..5cab0c8e9be6eccce97da17600d64ce9c7fb2878 100644 >--- a/Source/WebCore/workers/service/server/SWServerJobQueue.cpp >+++ b/Source/WebCore/workers/service/server/SWServerJobQueue.cpp >@@ -97,8 +97,10 @@ void SWServerJobQueue::scriptFetchFinished(SWServer::Connection& connection, con > return; > } > >+ // FIXME: Update all the imported scripts as per spec. For now, we just do as if there is none. >+ > // FIXME: Support the proper worker type (classic vs module) >- m_server.updateWorker(connection, job.identifier(), *registration, job.scriptURL, result.script, result.contentSecurityPolicy, WorkerType::Classic); >+ m_server.updateWorker(connection, job.identifier(), *registration, job.scriptURL, result.script, result.contentSecurityPolicy, WorkerType::Classic, { }); > } > > // https://w3c.github.io/ServiceWorker/#update-algorithm >diff --git a/Source/WebCore/workers/service/server/SWServerToContextConnection.cpp b/Source/WebCore/workers/service/server/SWServerToContextConnection.cpp >index 3c26172f32f5b1019181b77e1e9688c19002f40a..57ce37deda1bb5e38ffbdf42e4bb3188d4e10e69 100644 >--- a/Source/WebCore/workers/service/server/SWServerToContextConnection.cpp >+++ b/Source/WebCore/workers/service/server/SWServerToContextConnection.cpp >@@ -131,6 +131,12 @@ void SWServerToContextConnection::skipWaiting(ServiceWorkerIdentifier serviceWor > didFinishSkipWaiting(callbackID); > } > >+void SWServerToContextConnection::setScriptResource(ServiceWorkerIdentifier serviceWorkerIdentifier, URL&& scriptURL, String&& script, URL&& responseURL, String&& mimeType) >+{ >+ if (auto* worker = SWServerWorker::existingWorkerForIdentifier(serviceWorkerIdentifier)) >+ worker->setScriptResource(WTFMove(scriptURL), ServiceWorkerContextData::ImportedScript { WTFMove(script), WTFMove(responseURL), WTFMove(mimeType) }); >+} >+ > } // namespace WebCore > > #endif // ENABLE(SERVICE_WORKER) >diff --git a/Source/WebCore/workers/service/server/SWServerToContextConnection.h b/Source/WebCore/workers/service/server/SWServerToContextConnection.h >index 2e604ef2cb547c15d538cdd87f85d89ec82eb570..606c389fa18de9c528052903cc8f8bb1a3a0cc3e 100644 >--- a/Source/WebCore/workers/service/server/SWServerToContextConnection.h >+++ b/Source/WebCore/workers/service/server/SWServerToContextConnection.h >@@ -29,6 +29,7 @@ > > #include "SecurityOriginData.h" > #include "ServiceWorkerClientQueryOptions.h" >+#include "ServiceWorkerContextData.h" > #include "ServiceWorkerIdentifier.h" > #include "ServiceWorkerTypes.h" > #include <wtf/RefCounted.h> >@@ -73,6 +74,7 @@ public: > WEBCORE_EXPORT void findClientByIdentifier(uint64_t clientIdRequestIdentifier, ServiceWorkerIdentifier, ServiceWorkerClientIdentifier); > WEBCORE_EXPORT void matchAll(uint64_t requestIdentifier, ServiceWorkerIdentifier, const ServiceWorkerClientQueryOptions&); > WEBCORE_EXPORT void claim(uint64_t requestIdentifier, ServiceWorkerIdentifier); >+ WEBCORE_EXPORT void setScriptResource(ServiceWorkerIdentifier, URL&& scriptURL, String&& script, URL&& responseURL, String&& mimeType); > > static SWServerToContextConnection* connectionForOrigin(const SecurityOriginData&); > >diff --git a/Source/WebCore/workers/service/server/SWServerWorker.cpp b/Source/WebCore/workers/service/server/SWServerWorker.cpp >index 159bce669f1cb9b2e6b94f39c6cd2612712e6c11..7f525cbb616103c0da51f4ac64d221d8174548d7 100644 >--- a/Source/WebCore/workers/service/server/SWServerWorker.cpp >+++ b/Source/WebCore/workers/service/server/SWServerWorker.cpp >@@ -45,12 +45,13 @@ SWServerWorker* SWServerWorker::existingWorkerForIdentifier(ServiceWorkerIdentif > } > > // FIXME: Use r-value references for script and contentSecurityPolicy >-SWServerWorker::SWServerWorker(SWServer& server, SWServerRegistration& registration, const URL& scriptURL, const String& script, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicy, WorkerType type, ServiceWorkerIdentifier identifier) >+SWServerWorker::SWServerWorker(SWServer& server, SWServerRegistration& registration, const URL& scriptURL, const String& script, const ContentSecurityPolicyResponseHeaders& contentSecurityPolicy, WorkerType type, ServiceWorkerIdentifier identifier, HashMap<URL, ServiceWorkerContextData::ImportedScript>&& scriptResourceMap) > : m_server(server) > , m_registrationKey(registration.key()) > , m_data { identifier, scriptURL, ServiceWorkerState::Redundant, type, registration.identifier() } > , m_script(script) > , m_contentSecurityPolicy(contentSecurityPolicy) >+ , m_scriptResourceMap(WTFMove(scriptResourceMap)) > { > m_data.scriptURL.removeFragmentIdentifier(); > >@@ -73,7 +74,7 @@ ServiceWorkerContextData SWServerWorker::contextData() const > auto* registration = m_server.getRegistration(m_registrationKey); > ASSERT(registration); > >- return { std::nullopt, registration->data(), m_data.identifier, m_script, m_contentSecurityPolicy, m_data.scriptURL, m_data.type, m_server.sessionID(), false }; >+ return { std::nullopt, registration->data(), m_data.identifier, m_script, m_contentSecurityPolicy, m_data.scriptURL, m_data.type, m_server.sessionID(), false, m_scriptResourceMap }; > } > > void SWServerWorker::terminate() >@@ -140,6 +141,11 @@ void SWServerWorker::claim() > return m_server.claim(*this); > } > >+void SWServerWorker::setScriptResource(URL&& url, ServiceWorkerContextData::ImportedScript&& script) >+{ >+ m_scriptResourceMap.set(WTFMove(url), WTFMove(script)); >+} >+ > void SWServerWorker::skipWaiting() > { > m_isSkipWaitingFlagSet = true; >diff --git a/Source/WebCore/workers/service/server/SWServerWorker.h b/Source/WebCore/workers/service/server/SWServerWorker.h >index 85b8b0f142a7b3acdb58bbee4c38bab60481cbc8..2fb9dc12ebc6694228375cb74fa9d219fbd19e07 100644 >--- a/Source/WebCore/workers/service/server/SWServerWorker.h >+++ b/Source/WebCore/workers/service/server/SWServerWorker.h >@@ -29,11 +29,11 @@ > > #include "ContentSecurityPolicyResponseHeaders.h" > #include "ServiceWorkerClientData.h" >+#include "ServiceWorkerContextData.h" > #include "ServiceWorkerData.h" > #include "ServiceWorkerIdentifier.h" > #include "ServiceWorkerRegistrationKey.h" > #include "ServiceWorkerTypes.h" >-#include "URL.h" > #include <wtf/RefCounted.h> > > namespace WebCore { >@@ -94,6 +94,7 @@ public: > WEBCORE_EXPORT std::optional<ServiceWorkerClientData> findClientByIdentifier(const ServiceWorkerClientIdentifier&) const; > void matchAll(const ServiceWorkerClientQueryOptions&, const ServiceWorkerClientsMatchAllCallback&); > void claim(); >+ void setScriptResource(URL&&, ServiceWorkerContextData::ImportedScript&&); > > void skipWaiting(); > bool isSkipWaitingFlagSet() const { return m_isSkipWaitingFlagSet; } >@@ -110,7 +111,7 @@ public: > WEBCORE_EXPORT SWServerToContextConnection* contextConnection(); > > private: >- SWServerWorker(SWServer&, SWServerRegistration&, const URL&, const String& script, const ContentSecurityPolicyResponseHeaders&, WorkerType, ServiceWorkerIdentifier); >+ SWServerWorker(SWServer&, SWServerRegistration&, const URL&, const String& script, const ContentSecurityPolicyResponseHeaders&, WorkerType, ServiceWorkerIdentifier, HashMap<URL, ServiceWorkerContextData::ImportedScript>&&); > > void callWhenActivatedHandler(bool success); > >@@ -123,7 +124,8 @@ private: > State m_state { State::NotRunning }; > mutable std::optional<ClientOrigin> m_origin; > bool m_isSkipWaitingFlagSet { false }; >- Vector<WTF::Function<void(bool)>> m_whenActivatedHandlers; >+ Vector<Function<void(bool)>> m_whenActivatedHandlers; >+ HashMap<URL, ServiceWorkerContextData::ImportedScript> m_scriptResourceMap; > }; > > } // namespace WebCore >diff --git a/Source/WebKit/StorageProcess/ServiceWorker/WebSWServerToContextConnection.messages.in b/Source/WebKit/StorageProcess/ServiceWorker/WebSWServerToContextConnection.messages.in >index 3405a5a7dad5ad8b233dc548b659f2bbb1413333..d86ab609397765c5bbfa79354a115b7c7491d27e 100644 >--- a/Source/WebKit/StorageProcess/ServiceWorker/WebSWServerToContextConnection.messages.in >+++ b/Source/WebKit/StorageProcess/ServiceWorker/WebSWServerToContextConnection.messages.in >@@ -35,6 +35,7 @@ messages -> WebSWServerToContextConnection { > FindClientByIdentifier(uint64_t requestIdentifier, WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, struct WebCore::ServiceWorkerClientIdentifier clientIdentifier); > MatchAll(uint64_t matchAllRequestIdentifier, WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier, struct WebCore::ServiceWorkerClientQueryOptions options); > Claim(uint64_t claimRequestIdentifier, WebCore::ServiceWorkerIdentifier serviceWorkerIdentifier); >+ SetScriptResource(WebCore::ServiceWorkerIdentifier identifier, WebCore::URL scriptURL, String script, WebCore::URL responseURL, String mimeType); > } > > #endif // ENABLE(SERVICE_WORKER) >diff --git a/Source/WebKit/UIProcess/API/C/WKContext.cpp b/Source/WebKit/UIProcess/API/C/WKContext.cpp >index 1ae00960196e43696c8402fc615b476c2480da4b..439a502559b544e66448958dc6dcd3e7015f7898 100644 >--- a/Source/WebKit/UIProcess/API/C/WKContext.cpp >+++ b/Source/WebKit/UIProcess/API/C/WKContext.cpp >@@ -611,6 +611,11 @@ void WKContextTerminateServiceWorkerProcess(WKContextRef context) > toImpl(context)->terminateServiceWorkerProcesses(); > } > >+void WKContextTerminateStorageProcess(WKContextRef context) >+{ >+ toImpl(context)->terminateStorageProcess(); >+} >+ > ProcessID WKContextGetNetworkProcessIdentifier(WKContextRef contextRef) > { > return toImpl(contextRef)->networkProcessIdentifier(); >diff --git a/Source/WebKit/UIProcess/API/C/WKContextPrivate.h b/Source/WebKit/UIProcess/API/C/WKContextPrivate.h >index 1eff377f7ef0be78e3b92260ece6cc9b5855de50..f08a43187cc2ee48e8cbabe4ad4c58f6fb730b99 100644 >--- a/Source/WebKit/UIProcess/API/C/WKContextPrivate.h >+++ b/Source/WebKit/UIProcess/API/C/WKContextPrivate.h >@@ -90,6 +90,7 @@ WK_EXPORT void WKContextSetUsesNetworkProcess(WKContextRef, bool); > > WK_EXPORT void WKContextTerminateNetworkProcess(WKContextRef); > WK_EXPORT void WKContextTerminateServiceWorkerProcess(WKContextRef); >+WK_EXPORT void WKContextTerminateStorageProcess(WKContextRef); > > WK_EXPORT void WKContextSetAllowsAnySSLCertificateForWebSocketTesting(WKContextRef, bool); > WK_EXPORT void WKContextSetAllowsAnySSLCertificateForServiceWorkerTesting(WKContextRef, bool); >diff --git a/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp b/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp >index 5bde3d0e2bf4cf23e8f68222f57cebc996693da5..faa7e39da36de366341045457d1e056eba4708fe 100644 >--- a/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp >+++ b/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.cpp >@@ -278,6 +278,11 @@ void WebSWContextManagerConnection::skipWaiting(ServiceWorkerIdentifier serviceW > m_connectionToStorageProcess->send(Messages::WebSWServerToContextConnection::SkipWaiting(serviceWorkerIdentifier, callbackID), 0); > } > >+void WebSWContextManagerConnection::setScriptResource(ServiceWorkerIdentifier serviceWorkerIdentifier, const URL& url, const ServiceWorkerContextData::ImportedScript& script) >+{ >+ m_connectionToStorageProcess->send(Messages::WebSWServerToContextConnection::SetScriptResource { serviceWorkerIdentifier, url, script.script, script.responseURL, script.mimeType }, 0); >+} >+ > void WebSWContextManagerConnection::workerTerminated(ServiceWorkerIdentifier serviceWorkerIdentifier) > { > m_connectionToStorageProcess->send(Messages::WebSWServerToContextConnection::WorkerTerminated(serviceWorkerIdentifier), 0); >diff --git a/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h b/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h >index 491cbda776535d1fbdbaa99fa7d52a8e25b09996..d0421ae73c08d01e143c12e1aad8877f9c9ef132 100644 >--- a/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h >+++ b/Source/WebKit/WebProcess/Storage/WebSWContextManagerConnection.h >@@ -70,8 +70,9 @@ private: > void workerTerminated(WebCore::ServiceWorkerIdentifier) final; > void findClientByIdentifier(WebCore::ServiceWorkerIdentifier, WebCore::ServiceWorkerClientIdentifier, FindClientByIdentifierCallback&&) final; > void matchAll(WebCore::ServiceWorkerIdentifier, const WebCore::ServiceWorkerClientQueryOptions&, WebCore::ServiceWorkerClientsMatchAllCallback&&) final; >- void claim(WebCore::ServiceWorkerIdentifier, WTF::CompletionHandler<void()>&&) final; >- void skipWaiting(WebCore::ServiceWorkerIdentifier, WTF::Function<void()>&& callback) final; >+ void claim(WebCore::ServiceWorkerIdentifier, CompletionHandler<void()>&&) final; >+ void skipWaiting(WebCore::ServiceWorkerIdentifier, Function<void()>&&) final; >+ void setScriptResource(WebCore::ServiceWorkerIdentifier, const WebCore::URL&, const WebCore::ServiceWorkerContextData::ImportedScript&) final; > > // IPC messages. > void serviceWorkerStartedWithMessage(std::optional<WebCore::ServiceWorkerJobDataIdentifier>, WebCore::ServiceWorkerIdentifier, const String& exceptionMessage) final; >diff --git a/Source/WebKit/WebProcess/WebProcess.cpp b/Source/WebKit/WebProcess/WebProcess.cpp >index 3723b6689a72093952baeff3326896f61c9bc54f..4747e93d13d2407e8f3523681cecc9bc8e35559a 100644 >--- a/Source/WebKit/WebProcess/WebProcess.cpp >+++ b/Source/WebKit/WebProcess/WebProcess.cpp >@@ -1180,6 +1180,13 @@ void WebProcess::webToStorageProcessConnectionClosed(WebToStorageProcessConnecti > ASSERT(m_webToStorageProcessConnection == connection); > > m_webToStorageProcessConnection = nullptr; >+ >+#if ENABLE(SERVICE_WORKER) >+ if (SWContextManager::singleton().connection()) { >+ RELEASE_LOG(ServiceWorker, "Service worker process is exiting because its storage process is gone"); >+ _exit(EXIT_SUCCESS); >+ } >+#endif > } > > WebToStorageProcessConnection& WebProcess::ensureWebToStorageProcessConnection(PAL::SessionID initialSessionID) >diff --git a/Tools/ChangeLog b/Tools/ChangeLog >index a2cfb0d64b6129b8e9c27d1e3deb9322d283d6c4..2eaed9efe936e67771154fc8454582e6903a8b83 100644 >--- a/Tools/ChangeLog >+++ b/Tools/ChangeLog >@@ -1,3 +1,23 @@ >+2018-06-04 Youenn Fablet <youenn@apple.com> >+ >+ ServiceWorker registration should store any script fetched through importScripts >+ https://bugs.webkit.org/show_bug.cgi?id=182444 >+ <rdar://problem/37164835> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ Add support to crash the storage process from Internals. >+ >+ * WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl: >+ * WebKitTestRunner/InjectedBundle/TestRunner.cpp: >+ (WTR::TestRunner::terminateStorageProcess): >+ * WebKitTestRunner/InjectedBundle/TestRunner.h: >+ * WebKitTestRunner/TestController.cpp: >+ (WTR::TestController::terminateStorageProcess): >+ * WebKitTestRunner/TestController.h: >+ * WebKitTestRunner/TestInvocation.cpp: >+ (WTR::TestInvocation::didReceiveMessageFromInjectedBundle): >+ > 2018-06-03 Fujii Hironori <Hironori.Fujii@sony.com> > > [Win][MiniBrowser] Remove gMiniBrowser global variable >diff --git a/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl b/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl >index 4e3fd3cd1e1406bdf73b4451db04cec80e3a1b9e..7e7659cc3bffce0aa876dddc9f02d04976a3db9f 100644 >--- a/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl >+++ b/Tools/WebKitTestRunner/InjectedBundle/Bindings/TestRunner.idl >@@ -327,6 +327,7 @@ interface TestRunner { > > void terminateNetworkProcess(); > void terminateServiceWorkerProcess(); >+ void terminateStorageProcess(); > > readonly attribute boolean didCancelClientRedirect; > >diff --git a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp >index 95ffbae7fe625cfa1cc8535830ad9e6983e6dd1a..2831a87b97150279853401943ddc4f9f32a63cc2 100644 >--- a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp >+++ b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.cpp >@@ -1244,6 +1244,12 @@ void TestRunner::terminateServiceWorkerProcess() > WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), nullptr); > } > >+void TestRunner::terminateStorageProcess() >+{ >+ WKRetainPtr<WKStringRef> messageName(AdoptWK, WKStringCreateWithUTF8CString("TerminateStorageProcess")); >+ WKBundlePagePostMessage(InjectedBundle::singleton().page()->page(), messageName.get(), nullptr); >+} >+ > static unsigned nextUIScriptCallbackID() > { > static unsigned callbackID = FirstUIScriptCallbackID; >diff --git a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h >index 41883ceec6a0a3b852c20f1751097a04bdc6238e..f2901bc62e6210f4071f287a432efb17eb3c9d69 100644 >--- a/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h >+++ b/Tools/WebKitTestRunner/InjectedBundle/TestRunner.h >@@ -433,6 +433,7 @@ public: > > void terminateNetworkProcess(); > void terminateServiceWorkerProcess(); >+ void terminateStorageProcess(); > > void removeAllSessionCredentials(JSValueRef); > void callDidRemoveAllSessionCredentialsCallback(); >diff --git a/Tools/WebKitTestRunner/TestController.cpp b/Tools/WebKitTestRunner/TestController.cpp >index 2ac9ec60a7588eca006ab73d1714a6e8b5ff4005..d9c1f427d0e33027a675423b57325eb360789f9e 100644 >--- a/Tools/WebKitTestRunner/TestController.cpp >+++ b/Tools/WebKitTestRunner/TestController.cpp >@@ -2400,6 +2400,11 @@ void TestController::terminateServiceWorkerProcess() > WKContextTerminateServiceWorkerProcess(platformContext()); > } > >+void TestController::terminateStorageProcess() >+{ >+ WKContextTerminateStorageProcess(platformContext()); >+} >+ > #if !PLATFORM(COCOA) > void TestController::platformWillRunTest(const TestInvocation&) > { >diff --git a/Tools/WebKitTestRunner/TestController.h b/Tools/WebKitTestRunner/TestController.h >index bc10f6cbd0cdf2a0717cb44caeffe80e4a98dcc2..7303209e0871f0324fd5248f8841e2a47910f150 100644 >--- a/Tools/WebKitTestRunner/TestController.h >+++ b/Tools/WebKitTestRunner/TestController.h >@@ -199,6 +199,7 @@ public: > > void terminateNetworkProcess(); > void terminateServiceWorkerProcess(); >+ void terminateStorageProcess(); > > void removeAllSessionCredentials(); > >diff --git a/Tools/WebKitTestRunner/TestInvocation.cpp b/Tools/WebKitTestRunner/TestInvocation.cpp >index 94c8a2d967e5ddf0e82d554167583839d0e89621..52475dfe479b06321642caad3e903d03a7695e9b 100644 >--- a/Tools/WebKitTestRunner/TestInvocation.cpp >+++ b/Tools/WebKitTestRunner/TestInvocation.cpp >@@ -735,6 +735,12 @@ void TestInvocation::didReceiveMessageFromInjectedBundle(WKStringRef messageName > return; > } > >+ if (WKStringIsEqualToUTF8CString(messageName, "TerminateStorageProcess")) { >+ ASSERT(!messageBody); >+ TestController::singleton().terminateStorageProcess(); >+ return; >+ } >+ > if (WKStringIsEqualToUTF8CString(messageName, "TerminateNetworkProcess")) { > ASSERT(!messageBody); > TestController::singleton().terminateNetworkProcess(); >diff --git a/LayoutTests/ChangeLog b/LayoutTests/ChangeLog >index 42369ca5063b6d5f7762c0b71940c869f21e4b80..7191de97dd313ed1c8a1694a0bb582154ea20577 100644 >--- a/LayoutTests/ChangeLog >+++ b/LayoutTests/ChangeLog >@@ -1,3 +1,17 @@ >+2018-06-04 Youenn Fablet <youenn@apple.com> >+ >+ ServiceWorker registration should store any script fetched through importScripts >+ https://bugs.webkit.org/show_bug.cgi?id=182444 >+ <rdar://problem/37164835> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * http/wpt/service-workers/persistent-importScripts-expected.txt: Added. >+ * http/wpt/service-workers/persistent-importScripts.html: Added. >+ * http/wpt/service-workers/resources/persistent-importScripts-script.py: Added. >+ * http/wpt/service-workers/resources/persistent-importScripts-worker.js: Added. >+ * http/wpt/service-workers/resources/routines.js: Added. >+ > 2018-06-03 Michael Catanzaro <mcatanzaro@igalia.com> > > More unreviewed GTK test gardening >diff --git a/LayoutTests/imported/w3c/ChangeLog b/LayoutTests/imported/w3c/ChangeLog >index 4918509b388be47e8c07459cc96421a8cb938154..aa81449d4b3d989581d83117199e132e9c952b2d 100644 >--- a/LayoutTests/imported/w3c/ChangeLog >+++ b/LayoutTests/imported/w3c/ChangeLog >@@ -1,3 +1,13 @@ >+2018-06-04 Youenn Fablet <youenn@apple.com> >+ >+ ServiceWorker registration should store any script fetched through importScripts >+ https://bugs.webkit.org/show_bug.cgi?id=182444 >+ <rdar://problem/37164835> >+ >+ Reviewed by NOBODY (OOPS!). >+ >+ * web-platform-tests/service-workers/service-worker/registration-mime-types.https-expected.txt: >+ > 2018-06-03 Youenn Fablet <youenn@apple.com> > > NetworkCORSPreflightChecker should set the preflight request User-Agent header >diff --git a/LayoutTests/http/wpt/service-workers/persistent-importScripts-expected.txt b/LayoutTests/http/wpt/service-workers/persistent-importScripts-expected.txt >new file mode 100644 >index 0000000000000000000000000000000000000000..7ef22e9a431ad0272713b71fdc8794016c8ef12f >--- /dev/null >+++ b/LayoutTests/http/wpt/service-workers/persistent-importScripts-expected.txt >@@ -0,0 +1 @@ >+PASS >diff --git a/LayoutTests/http/wpt/service-workers/persistent-importScripts.html b/LayoutTests/http/wpt/service-workers/persistent-importScripts.html >new file mode 100644 >index 0000000000000000000000000000000000000000..bc616b100898c246919ae356d54564054482ec46 >--- /dev/null >+++ b/LayoutTests/http/wpt/service-workers/persistent-importScripts.html >@@ -0,0 +1,54 @@ >+<html> >+<head> >+<script src="resources/routines.js"></script> >+</head> >+<body> >+<script> >+function getRandomIdFromWorker(worker) >+{ >+ worker.postMessage("getRandomId"); >+ return new Promise(function(resolve) { >+ navigator.serviceWorker.addEventListener('message', function(e) { >+ resolve(e.data); >+ }); >+ }); >+} >+ >+if (window.testRunner) { >+ testRunner.waitUntilDone(); >+ testRunner.dumpAsText(); >+} >+ >+var registration; >+var worker; >+async function doTest() { >+ if (!window.testRunner) >+ return Promise.reject('Internals API needed for this test'); >+ >+ registration = await navigator.serviceWorker.register("resources/persistent-importScripts-worker.js"); >+ if (registration.installing) { >+ worker = registration.installing; >+ await waitForState(worker, "activated"); >+ } else >+ worker = registration.active; >+ >+ let frame = await withIframe("resources"); >+ let randomId = await getRandomIdFromWorker(worker); >+ >+ if (!window.location.hash.length) { >+ if (window.testRunner) >+ testRunner.terminateStorageProcess(); >+ await waitFor(100); >+ window.location += "?test#" + randomId; >+ return; >+ } >+ >+ document.body.innerHTML = ("#" + randomId) == window.location.hash ? "PASS" : "FAIL"; >+ if (window.testRunner) >+ testRunner.notifyDone(); >+} >+doTest(); >+ >+</script> >+</body> >+</html> >diff --git a/LayoutTests/http/wpt/service-workers/resources/persistent-importScripts-script.py b/LayoutTests/http/wpt/service-workers/resources/persistent-importScripts-script.py >new file mode 100644 >index 0000000000000000000000000000000000000000..a9bb2dd98b4f95a4dfe6cfa73849f5f5ea78dce4 >--- /dev/null >+++ b/LayoutTests/http/wpt/service-workers/resources/persistent-importScripts-script.py >@@ -0,0 +1,7 @@ >+import random >+ >+def main(request, response): >+ headers = [("Content-type", "text/javascript"), >+ ("Cache-Control", "no-store") >+ ] >+ return headers, "self.addEventListener('message', function(e) { e.source.postMessage('" + str(random.random()) +"'); });" >diff --git a/LayoutTests/http/wpt/service-workers/resources/persistent-importScripts-worker.js b/LayoutTests/http/wpt/service-workers/resources/persistent-importScripts-worker.js >new file mode 100644 >index 0000000000000000000000000000000000000000..7eb9dd62057f545aff5aa45a175ec0a0f1e68e71 >--- /dev/null >+++ b/LayoutTests/http/wpt/service-workers/resources/persistent-importScripts-worker.js >@@ -0,0 +1,2 @@ >+importScripts("persistent-importScripts-script.py"); >+ >diff --git a/LayoutTests/http/wpt/service-workers/resources/routines.js b/LayoutTests/http/wpt/service-workers/resources/routines.js >new file mode 100644 >index 0000000000000000000000000000000000000000..df2392558422206fad617a5eec7cc596ed4b4152 >--- /dev/null >+++ b/LayoutTests/http/wpt/service-workers/resources/routines.js >@@ -0,0 +1,29 @@ >+function waitFor(duration) >+{ >+ return new Promise((resolve) => setTimeout(resolve, duration)); >+} >+ >+function withIframe(url) { >+ return new Promise(function(resolve) { >+ var frame = document.createElement('iframe'); >+ frame.src = url; >+ frame.onload = function() { resolve(frame); }; >+ document.body.appendChild(frame); >+ }); >+} >+ >+function waitForState(worker, state) >+{ >+ if (!worker || worker.state == undefined) >+ return Promise.reject(new Error('wait_for_state must be passed a ServiceWorker')); >+ >+ if (worker.state === state) >+ return Promise.resolve(state); >+ >+ return new Promise(function(resolve) { >+ worker.addEventListener('statechange', function() { >+ if (worker.state === state) >+ resolve(state); >+ }); >+ }); >+} >diff --git a/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/registration-mime-types.https-expected.txt b/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/registration-mime-types.https-expected.txt >index 1d98128f119ffc694c61fee4f44e664be203ace9..537a2f7bd62124ca491bef1a6f86aeb9f88251ff 100644 >--- a/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/registration-mime-types.https-expected.txt >+++ b/LayoutTests/imported/w3c/web-platform-tests/service-workers/service-worker/registration-mime-types.https-expected.txt >@@ -1,8 +1,8 @@ > > PASS Registering script with no MIME type > PASS Registering script with bad MIME type >-FAIL Registering script that imports script with no MIME type assert_throws: Registration of no MIME type imported script should fail. function "function () { throw e }" threw object "TypeError: NetworkError: A network error occurred." that is not a DOMException SecurityError: property "code" is equal to undefined, expected 18 >-FAIL Registering script that imports script with bad MIME type assert_throws: Registration of plain text imported script should fail. function "function () { throw e }" threw object "TypeError: NetworkError: A network error occurred." that is not a DOMException SecurityError: property "code" is equal to undefined, expected 18 >+FAIL Registering script that imports script with no MIME type assert_throws: Registration of no MIME type imported script should fail. function "function () { throw e }" threw object "TypeError: NetworkError: mime type is not a supported JavaScript mime type" that is not a DOMException SecurityError: property "code" is equal to undefined, expected 18 >+FAIL Registering script that imports script with bad MIME type assert_throws: Registration of plain text imported script should fail. function "function () { throw e }" threw object "TypeError: NetworkError: mime type is not a supported JavaScript mime type" that is not a DOMException SecurityError: property "code" is equal to undefined, expected 18 > PASS Registering script with good MIME type application/ecmascript > PASS Registering script that imports script with good MIME type application/ecmascript > PASS Registering script with good MIME type application/javascript
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Diff
View Attachment As Raw
Actions:
View
|
Formatted Diff
|
Diff
Attachments on
bug 182444
:
341102
|
341764
|
341770
|
341771
|
341942
|
341952
|
341966