mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-10 03:45:46 +00:00
Bug 1334550 - Part 2 - Add nsISubstitutionObserver and update test_extensionURL.html; r=jimm
Adds nsISubstitutionObserver so that substitutions set on a parent nsISubstitutingProtocolHandler which are then propagated to child processes can be observed in the child. Updates test_extensionURL.html to set substitutions on the parent ExtensionProtocolHandler before trying to load moz-extension URI's using those substitutions. MozReview-Commit-ID: JaW1A3uZpoO --HG-- extra : rebase_source : ca1b89b9a6ea29ea464979c26c8c48c61d9d7e00
This commit is contained in:
parent
008a6c80f9
commit
10c9b9af41
@ -18,14 +18,105 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1161831
|
||||
|
||||
var policy1, policy2;
|
||||
|
||||
var XPCOMUtils = SpecialPowers.Cu.import("resource://gre/modules/XPCOMUtils.jsm").XPCOMUtils;
|
||||
var resourceHandler = SpecialPowers.Services.io.getProtocolHandler("resource")
|
||||
.QueryInterface(SpecialPowers.Ci.nsISubstitutingProtocolHandler);
|
||||
var extensionHandler = SpecialPowers.Services.io.getProtocolHandler("moz-extension")
|
||||
.QueryInterface(SpecialPowers.Ci.nsISubstitutingProtocolHandler);
|
||||
var fileHandler = SpecialPowers.Cc["@mozilla.org/network/protocol;1?name=file"]
|
||||
.getService(SpecialPowers.Ci.nsIFileProtocolHandler);
|
||||
|
||||
// Chrome script that adds handles for inserting substitutions and
|
||||
// resolving symlinked paths in the parent process.
|
||||
var script = SpecialPowers.loadChromeScript(() => {
|
||||
const Ci = Components.interfaces;
|
||||
const Cc = Components.classes;
|
||||
const Cu = Components.utils;
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
|
||||
// Sets up a substitution in the parent process
|
||||
this.addMessageListener("SetSubstitution", ({from, to}) => {
|
||||
// Convert the passed |to| string to a URI.
|
||||
// A null |to| value clears the substitution
|
||||
if (to != null) {
|
||||
var uri = Services.io.newURI(to);
|
||||
}
|
||||
Services.io.getProtocolHandler("moz-extension")
|
||||
.QueryInterface(Ci.nsISubstitutingProtocolHandler)
|
||||
.setSubstitution(from, uri);
|
||||
});
|
||||
|
||||
// Gets a normalized (de-symlinked) path in the parent process
|
||||
this.addMessageListener("ResolvePath", (path) => {
|
||||
let file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
||||
file.initWithPath(path);
|
||||
file.normalize();
|
||||
return file.path;
|
||||
});
|
||||
});
|
||||
|
||||
// An array of objects each containing a callback and a host
|
||||
// for a substitution that has been set in either the parent
|
||||
// or local child process and for which we are waiting for the
|
||||
// observer notification in the child process.
|
||||
var pendingSubstitutions = [];
|
||||
|
||||
// Adds a new callback to |pendingSubstitutions|.
|
||||
function pushSubstitutionCallback(callback, host) {
|
||||
let entry = {callback, host};
|
||||
pendingSubstitutions.push(entry);
|
||||
}
|
||||
|
||||
// Invoke the first callback found in |pendingSubstitutions|
|
||||
// with a matching host.
|
||||
function popSubstitutionCallback(host) {
|
||||
for (let i = 0; i < pendingSubstitutions.length; i++) {
|
||||
let entry = pendingSubstitutions[i];
|
||||
if (host === entry.host) {
|
||||
entry.callback();
|
||||
pendingSubstitutions.splice(i, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// This indicates we installed a mapping in either the
|
||||
// parent or the local child process, but never received an
|
||||
// observer notification in the child for a mapping with
|
||||
// a matching host.
|
||||
ok(false, `popSubstitutionCallback(${host}) no match found`);
|
||||
}
|
||||
|
||||
// Define an implementation of nsISubstitutionObserver and add it
|
||||
// to this process' ExtensionProtocolHandler to observe substitutions
|
||||
// as they are propagated to the child.
|
||||
function SubstitutionObserver() {}
|
||||
SubstitutionObserver.prototype = {
|
||||
onSetSubstitution: SpecialPowers.wrapCallback(function(root, uri) {
|
||||
popSubstitutionCallback(root);
|
||||
}),
|
||||
QueryInterface:
|
||||
XPCOMUtils.generateQI([SpecialPowers.Ci.nsISupports,
|
||||
SpecialPowers.Ci.nsISubstitutionObserver]),
|
||||
};
|
||||
var observer = new SubstitutionObserver();
|
||||
var wrappedObserver = SpecialPowers.wrap(observer);
|
||||
extensionHandler.addObserver(wrappedObserver);
|
||||
|
||||
// Set a substitution in the parent. The parent
|
||||
// propagates all substitutions to child processes.
|
||||
function globalSetSubstitution(chromeScript, from, to) {
|
||||
var p = new Promise(function(resolve, reject) {
|
||||
pushSubstitutionCallback(resolve, from);
|
||||
chromeScript.sendSyncMessage("SetSubstitution", {from, to});
|
||||
});
|
||||
return p;
|
||||
}
|
||||
|
||||
SimpleTest.registerCleanupFunction(function() {
|
||||
extensionHandler.removeObserver(wrappedObserver);
|
||||
policy1.active = false;
|
||||
policy2.active = false;
|
||||
script.sendSyncMessage("SetSubstitution", {from: "cherise", to: null});
|
||||
script.sendSyncMessage("SetSubstitution", {from: "liebchen", to: null});
|
||||
});
|
||||
|
||||
addLoadEvent(function() {
|
||||
@ -35,24 +126,48 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1161831
|
||||
var resURI = SpecialPowers.Services.io.newURI("resource://testing-common/resource_test_file.html");
|
||||
var filePath = resourceHandler.resolveURI(resURI);
|
||||
ok(filePath.startsWith("file://"), "resource:// URI resolves where we expect: " + filePath);
|
||||
var fileURI = SpecialPowers.Services.io.newURI(filePath);
|
||||
var resFile = fileHandler.getFileFromURLSpec(filePath);
|
||||
|
||||
// Get normalized path to the test file. We already have a file object
|
||||
// |resFile|, but its underlying path may contain a symlink we can't
|
||||
// resolve in the child process.
|
||||
let resolvedPath = script.sendSyncMessage("ResolvePath", resFile.path);
|
||||
let file = SpecialPowers.Cc["@mozilla.org/file/local;1"]
|
||||
.createInstance(SpecialPowers.Ci.nsILocalFile);
|
||||
info(`resolved test file path: ${resolvedPath}`);
|
||||
file.initWithPath(resolvedPath);
|
||||
|
||||
// Setup the base directory URI string and a URI string to refer to
|
||||
// the test file within that directory.
|
||||
let cheriseURIStr = "moz-extension://cherise/" + file.leafName;
|
||||
let liebchenURIStr = "moz-extension://liebchen/" + file.leafName;
|
||||
let cheriseBaseDirURIStr = "file://" + file.parent.path + "/";
|
||||
info(`cheriseURIStr: ${cheriseURIStr}`);
|
||||
info(`liebchenURIStr: ${liebchenURIStr}`);
|
||||
info(`cheriseBaseDirURIStr: ${cheriseBaseDirURIStr}`);
|
||||
|
||||
function StubPolicy(id, accessible) {
|
||||
let policy = new WebExtensionPolicy(SpecialPowers.Cu.cloneInto({
|
||||
id: `imaginaryaddon-${id[0]}`,
|
||||
mozExtensionHostname: id,
|
||||
baseURL: fileURI.spec,
|
||||
baseURL: cheriseBaseDirURIStr,
|
||||
|
||||
allowedOrigins: SpecialPowers.unwrap(new MatchPatternSet([])),
|
||||
webAccessibleResources: accessible ? [SpecialPowers.unwrap(new MatchGlob("*"))] : [],
|
||||
localizeCallback(string) {},
|
||||
}, module, {cloneFunctions: true, wrapReflectors: true}));
|
||||
|
||||
// Activating the policy results in a substitution being added,
|
||||
// which triggers SubstitutionObserver.onSetSubstitution().
|
||||
// All observer notifications must be accounted for (in this
|
||||
// test to validate they are working correctly) so ignore this
|
||||
// substitution when the observer gets notified.
|
||||
pushSubstitutionCallback(() => {}, id);
|
||||
policy.active = true;
|
||||
return policy;
|
||||
}
|
||||
|
||||
// Register a moz-extension:// URI.
|
||||
// Register a moz-extension:// URI locally.
|
||||
policy1 = StubPolicy("cherise", false);
|
||||
policy2 = StubPolicy("liebchen", false);
|
||||
|
||||
@ -89,7 +204,10 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1161831
|
||||
|
||||
|
||||
function setWhitelistCallback(paths) {
|
||||
pushSubstitutionCallback(() => {}, policy1.mozExtensionHostname);
|
||||
policy1.active = false;
|
||||
|
||||
pushSubstitutionCallback(() => {}, policy2.mozExtensionHostname);
|
||||
policy2.active = false;
|
||||
|
||||
policy1 = StubPolicy("cherise", paths.includes("cherise"));
|
||||
@ -147,19 +265,21 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1161831
|
||||
//
|
||||
// Perform some loads and make sure they work correctly.
|
||||
//
|
||||
testLoad.bind(null, "moz-extension://cherise", navigateFromChromeWithLocation)()
|
||||
.then(testLoad.bind(null, "moz-extension://cherise", navigateFromChromeWithWebNav))
|
||||
.then(testLoad.bind(null, "moz-extension://cherise", navigateWithLocation, /* shouldThrow = */ true))
|
||||
.then(testXHR.bind(null, "moz-extension://cherise", /* shouldError = */ true))
|
||||
globalSetSubstitution(script, "cherise", cheriseBaseDirURIStr)
|
||||
.then(testLoad.bind(null, cheriseURIStr, navigateFromChromeWithLocation))
|
||||
.then(testLoad.bind(null, cheriseURIStr, navigateFromChromeWithWebNav))
|
||||
.then(testLoad.bind(null, cheriseURIStr, navigateWithLocation, /* shouldThrow = */ true))
|
||||
.then(testXHR.bind(null, cheriseURIStr, /* shouldError = */ true))
|
||||
.then(setWhitelistCallback.bind(null, ["cherise"]))
|
||||
.then(testLoad.bind(null, "moz-extension://cherise", navigateWithLocation))
|
||||
.then(testXHR.bind(null, "moz-extension://cherise"))
|
||||
.then(testLoad.bind(null, "moz-extension://liebchen", navigateWithLocation, /* shouldThrow = */ true))
|
||||
.then(testXHR.bind(null, "moz-extension://liebchen", /* shouldError = */ true))
|
||||
.then(testLoad.bind(null, cheriseURIStr, navigateWithLocation))
|
||||
.then(testXHR.bind(null, cheriseURIStr))
|
||||
.then(globalSetSubstitution(script, "liebchen", "moz-extension://cherise"))
|
||||
.then(testLoad.bind(null, liebchenURIStr, navigateWithLocation, /* shouldThrow = */ true))
|
||||
.then(testXHR.bind(null, liebchenURIStr, /* shouldError = */ true))
|
||||
.then(setWhitelistCallback.bind(null, ["cherise", "liebchen"]))
|
||||
.then(testLoad.bind(null, "moz-extension://liebchen", navigateWithLocation))
|
||||
.then(testLoad.bind(null, "moz-extension://liebchen", navigateWithSrc))
|
||||
.then(testLoad.bind(null, "moz-extension://cherise", navigateWithSrc))
|
||||
.then(testLoad.bind(null, liebchenURIStr, navigateWithLocation))
|
||||
.then(testLoad.bind(null, liebchenURIStr, navigateWithSrc))
|
||||
.then(testLoad.bind(null, cheriseURIStr, navigateWithSrc))
|
||||
.then(testLoad.bind(null, "moz-extension://cherise/_blank.html", navigateWithSrc))
|
||||
.then(SimpleTest.finish.bind(SimpleTest),
|
||||
function(e) { ok(false, "rejected promise: " + e); SimpleTest.finish() }
|
||||
|
@ -297,6 +297,7 @@ SubstitutingProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *bas
|
||||
{
|
||||
if (!baseURI) {
|
||||
mSubstitutions.Remove(root);
|
||||
NotifyObservers(root, baseURI);
|
||||
return SendSubstitution(root, baseURI);
|
||||
}
|
||||
|
||||
@ -312,6 +313,7 @@ SubstitutingProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *bas
|
||||
}
|
||||
|
||||
mSubstitutions.Put(root, baseURI);
|
||||
NotifyObservers(root, baseURI);
|
||||
return SendSubstitution(root, baseURI);
|
||||
}
|
||||
|
||||
@ -325,6 +327,7 @@ SubstitutingProtocolHandler::SetSubstitution(const nsACString& root, nsIURI *bas
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
mSubstitutions.Put(root, newBaseURI);
|
||||
NotifyObservers(root, baseURI);
|
||||
return SendSubstitution(root, newBaseURI);
|
||||
}
|
||||
|
||||
@ -408,5 +411,38 @@ SubstitutingProtocolHandler::ResolveURI(nsIURI *uri, nsACString &result)
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SubstitutingProtocolHandler::AddObserver(nsISubstitutionObserver* aObserver)
|
||||
{
|
||||
NS_ENSURE_ARG(aObserver);
|
||||
if (mObservers.Contains(aObserver)) {
|
||||
return NS_ERROR_DUPLICATE_HANDLE;
|
||||
}
|
||||
|
||||
mObservers.AppendElement(aObserver);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
SubstitutingProtocolHandler::RemoveObserver(nsISubstitutionObserver* aObserver)
|
||||
{
|
||||
NS_ENSURE_ARG(aObserver);
|
||||
if (!mObservers.Contains(aObserver)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
|
||||
mObservers.RemoveElement(aObserver);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
SubstitutingProtocolHandler::NotifyObservers(const nsACString& aRoot,
|
||||
nsIURI* aBaseURI)
|
||||
{
|
||||
for (size_t i = 0; i < mObservers.Length(); ++i) {
|
||||
mObservers[i]->OnSetSubstitution(aRoot, aBaseURI);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
#include "nsInterfaceHashtable.h"
|
||||
#include "nsIOService.h"
|
||||
#include "nsISubstitutionObserver.h"
|
||||
#include "nsStandardURL.h"
|
||||
#include "mozilla/chrome/RegistryMessageUtils.h"
|
||||
#include "mozilla/Maybe.h"
|
||||
@ -73,11 +74,19 @@ protected:
|
||||
nsIIOService* IOService() { return mIOService; }
|
||||
|
||||
private:
|
||||
// Notifies all observers that a new substitution from |aRoot| to
|
||||
// |aBaseURI| has been set/installed for this protocol handler.
|
||||
void NotifyObservers(const nsACString& aRoot, nsIURI* aBaseURI);
|
||||
|
||||
nsCString mScheme;
|
||||
Maybe<uint32_t> mFlags;
|
||||
nsInterfaceHashtable<nsCStringHashKey,nsIURI> mSubstitutions;
|
||||
nsCOMPtr<nsIIOService> mIOService;
|
||||
|
||||
// The list of observers added with AddObserver that will be
|
||||
// notified when substitutions are set or unset.
|
||||
nsTArray<nsCOMPtr<nsISubstitutionObserver>> mObservers;
|
||||
|
||||
// In general, we expect the principal of a document loaded from a
|
||||
// substituting URI to be a codebase principal for that URI (rather than
|
||||
// a principal for whatever is underneath). However, this only works if
|
||||
|
@ -7,6 +7,7 @@
|
||||
XPIDL_SOURCES += [
|
||||
'nsIResProtocolHandler.idl',
|
||||
'nsISubstitutingProtocolHandler.idl',
|
||||
'nsISubstitutionObserver.idl',
|
||||
]
|
||||
|
||||
XPIDL_MODULE = 'necko_res'
|
||||
|
@ -5,6 +5,8 @@
|
||||
|
||||
#include "nsIProtocolHandler.idl"
|
||||
|
||||
interface nsISubstitutionObserver;
|
||||
|
||||
/**
|
||||
* Protocol handler superinterface for a protocol which performs substitutions
|
||||
* from URIs of its scheme to URIs of another scheme.
|
||||
@ -43,4 +45,18 @@ interface nsISubstitutingProtocolHandler : nsIProtocolHandler
|
||||
* @throws NS_ERROR_NOT_AVAILABLE if resURI.host() is an unknown root key.
|
||||
*/
|
||||
[must_use] AUTF8String resolveURI(in nsIURI resURI);
|
||||
|
||||
/**
|
||||
* Adds an observer that will be notified on the main thread whenever a
|
||||
* substitition is set or unset. Notifications are not sent for substitutions
|
||||
* that were set prior to the observer being added. Holds an owning reference
|
||||
* to the observer until removeObserver is called or the protocol handler is
|
||||
* destroyed.
|
||||
*/
|
||||
[must_use] void addObserver(in nsISubstitutionObserver observer);
|
||||
|
||||
/**
|
||||
* Removes the observer.
|
||||
*/
|
||||
[must_use] void removeObserver(in nsISubstitutionObserver observer);
|
||||
};
|
||||
|
30
netwerk/protocol/res/nsISubstitutionObserver.idl
Normal file
30
netwerk/protocol/res/nsISubstitutionObserver.idl
Normal file
@ -0,0 +1,30 @@
|
||||
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIURI;
|
||||
|
||||
/**
|
||||
* An observer of substitutions being set or unset on a
|
||||
* SubstitutingProtocolHandler. Useful for receiving asynchronous notification
|
||||
* in a child process after a substitution has been set in the parent process
|
||||
* and is propagated to the child.
|
||||
*/
|
||||
[scriptable, uuid(492c9192-3803-4e2b-8373-d25fe55f5588)]
|
||||
interface nsISubstitutionObserver : nsISupports
|
||||
{
|
||||
/**
|
||||
* To be called when a substition has been set or unset on a protocol
|
||||
* handler. Unset operations are identified by a null URI argument.
|
||||
*
|
||||
* @param aRoot the root key of the mapping
|
||||
* @param aBaseURI the base URI to be substituted for the root key by the
|
||||
* protocol handler. For notifications triggered by unset
|
||||
* operations (i.e., when is a substitution is removed from the
|
||||
* protocol handler) this argument is null.
|
||||
*/
|
||||
void onSetSubstitution(in ACString aRoot, in nsIURI aBaseURI);
|
||||
};
|
@ -13,6 +13,8 @@
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsStandardURL.h"
|
||||
|
||||
class nsISubstitutionObserver;
|
||||
|
||||
struct SubstitutionMapping;
|
||||
class nsResProtocolHandler final : public nsIResProtocolHandler,
|
||||
public mozilla::SubstitutingProtocolHandler,
|
||||
@ -48,6 +50,16 @@ public:
|
||||
return mozilla::SubstitutingProtocolHandler::ResolveURI(aResURI, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHOD AddObserver(nsISubstitutionObserver *aObserver) override
|
||||
{
|
||||
return mozilla::SubstitutingProtocolHandler::AddObserver(aObserver);
|
||||
}
|
||||
|
||||
NS_IMETHOD RemoveObserver(nsISubstitutionObserver *aObserver) override
|
||||
{
|
||||
return mozilla::SubstitutingProtocolHandler::RemoveObserver(aObserver);
|
||||
}
|
||||
|
||||
protected:
|
||||
MOZ_MUST_USE nsresult GetSubstitutionInternal(const nsACString& aRoot, nsIURI** aResult) override;
|
||||
virtual ~nsResProtocolHandler() {}
|
||||
|
Loading…
Reference in New Issue
Block a user