Commit 1eedddee authored by horo's avatar horo Committed by Commit bot

Bypass ServiceWorker when the request originates from isolated world.

After https://codereview.chromium.org/1192013003, when Chrome extension
sends XHR request to "chrome-extension://*" to get the contents in the
extension, the request goes to the page's ServiceWorker.

This is breaking some extensions. (crbug.com/517181)
And also it is causing ASSERT failure. (crbug.com/528818)

The requests initiated from extensions should not go to the ServiceWorker.
So this CL checks DOMWrapperWorld.isIsolatedWorld and sets the
skipServiceWorker flag to bypass the SW.

BUG=528818,517181

Review URL: https://codereview.chromium.org/1390943003

Cr-Commit-Position: refs/heads/master@{#360533}
parent d8b2f233
......@@ -4,6 +4,7 @@
#include "base/bind_helpers.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
......@@ -20,6 +21,7 @@
#include "extensions/browser/process_manager.h"
#include "extensions/test/background_page_watcher.h"
#include "extensions/test/extension_test_message_listener.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
namespace extensions {
......@@ -391,4 +393,24 @@ IN_PROC_BROWSER_TEST_F(ServiceWorkerBackgroundSyncTest, Sync) {
EXPECT_TRUE(sync_listener.WaitUntilSatisfied());
}
IN_PROC_BROWSER_TEST_F(ServiceWorkerTest,
FetchFromContentScriptShouldNotGoToServiceWorkerOfPage) {
ASSERT_TRUE(StartEmbeddedTestServer());
GURL page_url = embedded_test_server()->GetURL(
"/extensions/api_test/service_worker/content_script_fetch/"
"controlled_page/index.html");
content::WebContents* tab =
browser()->tab_strip_model()->GetActiveWebContents();
ui_test_utils::NavigateToURL(browser(), page_url);
content::WaitForLoadStop(tab);
std::string value;
ASSERT_TRUE(
content::ExecuteScriptAndExtractString(tab, "register();", &value));
EXPECT_EQ("SW controlled", value);
ASSERT_TRUE(RunExtensionTest("service_worker/content_script_fetch"))
<< message_;
}
} // namespace extensions
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
chrome.runtime.onConnect.addListener(function(port) {
chrome.test.log('got connect');
port.onMessage.addListener(function(msg) {
chrome.test.log('got message: ' + msg);
chrome.test.assertEq('Success', msg);
chrome.test.notifyPass();
});
});
chrome.test.getConfig(function(config) {
chrome.test.log('Creating tab...');
chrome.tabs.create({
url: 'http://127.0.0.1:PORT/extensions/api_test/service_worker/content_script_fetch/controlled_page/index.html'
.replace(/PORT/, config.testServer.port)
});
});
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
fetch(chrome.runtime.getURL('data_for_content_script'))
.then(function(res) { return res.text(); })
.then(function(txt) {
if (txt != 'original data\n')
throw 'Fetch() result error: ' + txt;
return new Promise(function(resolve) {
var xhr = new XMLHttpRequest();
xhr.addEventListener('load', function() {
resolve(xhr.response);
});
xhr.open('GET', chrome.runtime.getURL('data_for_content_script'));
xhr.send();
});
})
.then(function(txt) {
if (txt != 'original data\n')
throw 'XMLHttpRequest result error: ' + txt;
chrome.runtime.connect().postMessage('Success');
})
.catch(function(e) {
chrome.runtime.connect().postMessage('Failure: ' + e);
});
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
function register() {
var script = './sw.js';
var scope = './';
navigator.serviceWorker.register(script, {scope: scope})
.then(function() { return navigator.serviceWorker.ready; })
.then(function(registration) {
var channel = new MessageChannel();
var saw_message = new Promise(function(resolve, reject) {
channel.port1.onmessage = function (e) {
if (e.data == 'clients claimed')
resolve();
else
reject(e.data)
};
});
registration.active.postMessage({port: channel.port2}, [channel.port2]);
return saw_message;
})
.then(function() { return fetch('./sw_controlled_check'); })
.then(function(res) { return res.text(); })
.then(function(txt) { window.domAutomationController.send(txt); })
.catch(function(e) { window.domAutomationController.send('Fail: ' + e); });
}
// Copyright 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
self.addEventListener('fetch', function(event) {
if (event.request.url.indexOf('sw_controlled_check') != -1) {
event.respondWith(new Response('SW controlled'));
} else if (event.request.url.indexOf('data_for_content_script') != -1) {
event.respondWith(new Response('SW served data'));
}
});
self.addEventListener('message', function(event) {
self.clients.claim()
.then(function(result) {
event.data.port.postMessage('clients claimed');
})
.catch(function(error) {
event.data.port.postMessage('FAIL: exception: ' + error.name);
});
});
{
"name": "content script fetch",
"version": "0.1",
"manifest_version": 2,
"description": "tests that content script initiated fetch should not go to the SW of page",
"background": {
"scripts": ["background.js"]
},
"permissions": ["http://*/*", "tabs"],
"content_scripts": [
{
"matches": ["http://*/*"],
"js": ["content_script.js"]
}
],
"web_accessible_resources": [
"data_for_content_script"
]
}
......@@ -191,8 +191,8 @@ XMLHttpRequest* XMLHttpRequest::create(ScriptState* scriptState)
{
ExecutionContext* context = scriptState->executionContext();
DOMWrapperWorld& world = scriptState->world();
RefPtr<SecurityOrigin> securityOrigin = world.isIsolatedWorld() ? world.isolatedWorldSecurityOrigin() : nullptr;
XMLHttpRequest* xmlHttpRequest = new XMLHttpRequest(context, securityOrigin);
RefPtr<SecurityOrigin> isolatedWorldSecurityOrigin = world.isIsolatedWorld() ? world.isolatedWorldSecurityOrigin() : nullptr;
XMLHttpRequest* xmlHttpRequest = new XMLHttpRequest(context, isolatedWorldSecurityOrigin);
xmlHttpRequest->suspendIfNeeded();
return xmlHttpRequest;
......@@ -206,7 +206,7 @@ XMLHttpRequest* XMLHttpRequest::create(ExecutionContext* context)
return xmlHttpRequest;
}
XMLHttpRequest::XMLHttpRequest(ExecutionContext* context, PassRefPtr<SecurityOrigin> securityOrigin)
XMLHttpRequest::XMLHttpRequest(ExecutionContext* context, PassRefPtr<SecurityOrigin> isolatedWorldSecurityOrigin)
: ActiveDOMObject(context)
, m_timeoutMilliseconds(0)
, m_state(UNSENT)
......@@ -215,7 +215,7 @@ XMLHttpRequest::XMLHttpRequest(ExecutionContext* context, PassRefPtr<SecurityOri
, m_exceptionCode(0)
, m_progressEventThrottle(XMLHttpRequestProgressEventThrottle::create(this))
, m_responseTypeCode(ResponseTypeDefault)
, m_securityOrigin(securityOrigin)
, m_isolatedWorldSecurityOrigin(isolatedWorldSecurityOrigin)
, m_eventDispatchRecursionLevel(0)
, m_async(true)
, m_includeCredentials(false)
......@@ -246,7 +246,7 @@ Document* XMLHttpRequest::document() const
SecurityOrigin* XMLHttpRequest::securityOrigin() const
{
return m_securityOrigin ? m_securityOrigin.get() : executionContext()->securityOrigin();
return m_isolatedWorldSecurityOrigin ? m_isolatedWorldSecurityOrigin.get() : executionContext()->securityOrigin();
}
XMLHttpRequest::State XMLHttpRequest::readyState() const
......@@ -893,6 +893,7 @@ void XMLHttpRequest::createRequest(PassRefPtr<EncodedFormData> httpBody, Excepti
request.setHTTPMethod(m_method);
request.setRequestContext(WebURLRequest::RequestContextXMLHttpRequest);
request.setFetchCredentialsMode(m_includeCredentials ? WebURLRequest::FetchCredentialsModeInclude : WebURLRequest::FetchCredentialsModeSameOrigin);
request.setSkipServiceWorker(m_isolatedWorldSecurityOrigin);
InspectorInstrumentation::willLoadXHR(&executionContext, this, this, m_method, m_url, m_async, httpBody ? httpBody->deepCopy() : nullptr, m_requestHeaders, m_includeCredentials);
......
......@@ -293,7 +293,7 @@ private:
// An enum corresponding to the allowed string values for the responseType attribute.
ResponseTypeCode m_responseTypeCode;
RefPtr<SecurityOrigin> m_securityOrigin;
RefPtr<SecurityOrigin> m_isolatedWorldSecurityOrigin;
// This blob loader will be used if |m_downloadingToFile| is true and
// |m_responseTypeCode| is NOT ResponseTypeBlob.
......
......@@ -56,9 +56,9 @@ bool IsRedirectStatusCode(int statusCode)
class FetchManager::Loader final : public GarbageCollectedFinalized<FetchManager::Loader>, public ThreadableLoaderClient, public ContextLifecycleObserver {
WILL_BE_USING_GARBAGE_COLLECTED_MIXIN(FetchManager::Loader);
public:
static Loader* create(ExecutionContext* executionContext, FetchManager* fetchManager, ScriptPromiseResolver* resolver, FetchRequestData* request)
static Loader* create(ExecutionContext* executionContext, FetchManager* fetchManager, ScriptPromiseResolver* resolver, FetchRequestData* request, bool isIsolatedWorld)
{
return new Loader(executionContext, fetchManager, resolver, request);
return new Loader(executionContext, fetchManager, resolver, request, isIsolatedWorld);
}
~Loader() override;
......@@ -151,7 +151,7 @@ public:
};
private:
Loader(ExecutionContext*, FetchManager*, ScriptPromiseResolver*, FetchRequestData*);
Loader(ExecutionContext*, FetchManager*, ScriptPromiseResolver*, FetchRequestData*, bool isIsolatedWorld);
void performBasicFetch();
void performNetworkError(const String& message);
......@@ -171,9 +171,10 @@ private:
int m_responseHttpStatusCode;
Member<SRIVerifier> m_integrityVerifier;
bool m_didFinishLoading;
bool m_isIsolatedWorld;
};
FetchManager::Loader::Loader(ExecutionContext* executionContext, FetchManager* fetchManager, ScriptPromiseResolver* resolver, FetchRequestData* request)
FetchManager::Loader::Loader(ExecutionContext* executionContext, FetchManager* fetchManager, ScriptPromiseResolver* resolver, FetchRequestData* request, bool isIsolatedWorld)
: ContextLifecycleObserver(executionContext)
, m_fetchManager(fetchManager)
, m_resolver(resolver)
......@@ -183,6 +184,7 @@ FetchManager::Loader::Loader(ExecutionContext* executionContext, FetchManager* f
, m_responseHttpStatusCode(0)
, m_integrityVerifier(nullptr)
, m_didFinishLoading(false)
, m_isIsolatedWorld(isIsolatedWorld)
{
}
......@@ -561,6 +563,7 @@ void FetchManager::Loader::performHTTPFetch(bool corsFlag, bool corsPreflightFla
// referrer string (i.e. String()).
request.setHTTPReferrer(SecurityPolicy::generateReferrer(policy, m_request->url(), m_request->referrerString()));
}
request.setSkipServiceWorker(m_isIsolatedWorld);
// "3. Append `Host`, ..."
// FIXME: Implement this when the spec is fixed.
......@@ -691,7 +694,7 @@ ScriptPromise FetchManager::fetch(ScriptState* scriptState, FetchRequestData* re
request->setContext(WebURLRequest::RequestContextFetch);
Loader* loader = Loader::create(m_executionContext, this, resolver, request);
Loader* loader = Loader::create(m_executionContext, this, resolver, request, scriptState->world().isIsolatedWorld());
m_loaders.add(loader);
loader->start();
return promise;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment