Bug 1368102: Part 2 - Add WebExtensionContentScript bindings. r=billm,mixedpuppy
Bill, can you please review the binding code? Shane and zombie, can you please review the content script matching? MozReview-Commit-ID: IJB5s0a7r7S
This commit is contained in:
@@ -2011,6 +2011,7 @@ GK_ATOM(mozinputrangeignorepreventdefault, "mozinputrangeignorepreventdefault")
|
|||||||
|
|
||||||
// WebExtensions
|
// WebExtensions
|
||||||
GK_ATOM(moz_extension, "moz-extension")
|
GK_ATOM(moz_extension, "moz-extension")
|
||||||
|
GK_ATOM(all_urlsPermission, "<all_urls>")
|
||||||
GK_ATOM(http, "http")
|
GK_ATOM(http, "http")
|
||||||
GK_ATOM(https, "https")
|
GK_ATOM(https, "https")
|
||||||
|
|
||||||
|
|||||||
@@ -1101,6 +1101,10 @@ DOMInterfaces = {
|
|||||||
'implicitJSContext': 'makeCredential',
|
'implicitJSContext': 'makeCredential',
|
||||||
},
|
},
|
||||||
|
|
||||||
|
'WebExtensionContentScript': {
|
||||||
|
'nativeType': 'mozilla::extensions::WebExtensionContentScript',
|
||||||
|
},
|
||||||
|
|
||||||
'WebExtensionPolicy': {
|
'WebExtensionPolicy': {
|
||||||
'nativeType': 'mozilla::extensions::WebExtensionPolicy',
|
'nativeType': 'mozilla::extensions::WebExtensionPolicy',
|
||||||
},
|
},
|
||||||
@@ -1713,6 +1717,8 @@ addExternalIface('RTCDataChannel', nativeType='nsIDOMDataChannel')
|
|||||||
addExternalIface('HitRegionOptions', nativeType='nsISupports')
|
addExternalIface('HitRegionOptions', nativeType='nsISupports')
|
||||||
addExternalIface('imgINotificationObserver', nativeType='imgINotificationObserver')
|
addExternalIface('imgINotificationObserver', nativeType='imgINotificationObserver')
|
||||||
addExternalIface('imgIRequest', nativeType='imgIRequest', notflattened=True)
|
addExternalIface('imgIRequest', nativeType='imgIRequest', notflattened=True)
|
||||||
|
addExternalIface('LoadInfo', nativeType='nsILoadInfo',
|
||||||
|
headerFile='nsILoadInfo.h', notflattened=True)
|
||||||
addExternalIface('MenuBuilder', nativeType='nsIMenuBuilder', notflattened=True)
|
addExternalIface('MenuBuilder', nativeType='nsIMenuBuilder', notflattened=True)
|
||||||
addExternalIface('MozControllers', nativeType='nsIControllers')
|
addExternalIface('MozControllers', nativeType='nsIControllers')
|
||||||
addExternalIface('MozFrameLoader', nativeType='nsIFrameLoader', notflattened=True)
|
addExternalIface('MozFrameLoader', nativeType='nsIFrameLoader', notflattened=True)
|
||||||
|
|||||||
161
dom/webidl/WebExtensionContentScript.webidl
Normal file
161
dom/webidl/WebExtensionContentScript.webidl
Normal file
@@ -0,0 +1,161 @@
|
|||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
interface LoadInfo;
|
||||||
|
interface URI;
|
||||||
|
interface WindowProxy;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the earliest point in the load cycle at which a script should
|
||||||
|
* run.
|
||||||
|
*/
|
||||||
|
enum ContentScriptRunAt {
|
||||||
|
/**
|
||||||
|
* The point in the load cycle just after the document element has been
|
||||||
|
* inserted, before any page scripts have been allowed to run.
|
||||||
|
*/
|
||||||
|
"document_start",
|
||||||
|
/**
|
||||||
|
* The point after which the page DOM has fully loaded, but before all page
|
||||||
|
* resources have necessarily been loaded. Corresponds approximately to the
|
||||||
|
* DOMContentLoaded event.
|
||||||
|
*/
|
||||||
|
"document_end",
|
||||||
|
/**
|
||||||
|
* The first point after the page and all of its resources has fully loaded
|
||||||
|
* when the event loop is idle, and can run scripts without delaying a paint
|
||||||
|
* event.
|
||||||
|
*/
|
||||||
|
"document_idle",
|
||||||
|
};
|
||||||
|
|
||||||
|
[Constructor(WebExtensionPolicy extension, WebExtensionContentScriptInit options), ChromeOnly, Exposed=System]
|
||||||
|
interface WebExtensionContentScript {
|
||||||
|
/**
|
||||||
|
* Returns true if the script's match and exclude patterns match the given
|
||||||
|
* URI, without reference to attributes such as `allFrames`.
|
||||||
|
*/
|
||||||
|
boolean matchesURI(URI uri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the script matches the given URI and LoadInfo objects.
|
||||||
|
* This should be used to determine whether to begin pre-loading a content
|
||||||
|
* script based on network events.
|
||||||
|
*/
|
||||||
|
boolean matchesLoadInfo(URI uri, LoadInfo loadInfo);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns true if the script matches the given window. This should be used
|
||||||
|
* to determine whether to run a script in a window at load time.
|
||||||
|
*/
|
||||||
|
boolean matchesWindow(WindowProxy window);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The policy object for the extension that this script belongs to.
|
||||||
|
*/
|
||||||
|
[Constant]
|
||||||
|
readonly attribute WebExtensionPolicy extension;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, this script runs in all frames. If false, it only runs in
|
||||||
|
* top-level frames.
|
||||||
|
*/
|
||||||
|
[Constant]
|
||||||
|
readonly attribute boolean allFrames;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If true, this (misleadingly-named, but inherited from Chrome) attribute
|
||||||
|
* causes the script to run in frames with URLs which inherit a principal
|
||||||
|
* that matches one of the match patterns, such as about:blank or
|
||||||
|
* about:srcdoc. If false, the script only runs in frames with an explicit
|
||||||
|
* matching URL.
|
||||||
|
*/
|
||||||
|
[Constant]
|
||||||
|
readonly attribute boolean matchAboutBlank;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The earliest point in the load cycle at which this script should run. For
|
||||||
|
* static content scripts, in extensions which were present at browser
|
||||||
|
* startup, the browser makes every effort to make sure that the script runs
|
||||||
|
* no later than this point in the load cycle. For dynamic content scripts,
|
||||||
|
* and scripts from extensions installed during this session, the scripts
|
||||||
|
* may run at a later point.
|
||||||
|
*/
|
||||||
|
[Constant]
|
||||||
|
readonly attribute ContentScriptRunAt runAt;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The outer window ID of the frame in which to run the script, or 0 if it
|
||||||
|
* should run in the top-level frame. Should only be used for
|
||||||
|
* dynamically-injected scripts.
|
||||||
|
*/
|
||||||
|
[Constant]
|
||||||
|
readonly attribute unsigned long long? frameID;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The set of match patterns for URIs of pages in which this script should
|
||||||
|
* run. This attribute is mandatory, and is a prerequisite for all other
|
||||||
|
* match patterns.
|
||||||
|
*/
|
||||||
|
[Constant]
|
||||||
|
readonly attribute MatchPatternSet matches;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of match patterns for URLs in which this script should not run,
|
||||||
|
* even if they match other include patterns or globs.
|
||||||
|
*/
|
||||||
|
[Constant]
|
||||||
|
readonly attribute MatchPatternSet? excludeMatches;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of glob matchers for URLs in which this script should run. If this
|
||||||
|
* list is present, the script will only run in URLs which match the
|
||||||
|
* `matches` pattern as well as one of these globs.
|
||||||
|
*/
|
||||||
|
[Cached, Constant, Frozen]
|
||||||
|
readonly attribute sequence<MatchGlob>? includeGlobs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of glob matchers for URLs in which this script should not run, even
|
||||||
|
* if they match other include patterns or globs.
|
||||||
|
*/
|
||||||
|
[Cached, Constant, Frozen]
|
||||||
|
readonly attribute sequence<MatchGlob>? excludeGlobs;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of paths, relative to the extension root, of CSS sheets to inject
|
||||||
|
* into matching pages.
|
||||||
|
*/
|
||||||
|
[Cached, Constant, Frozen]
|
||||||
|
readonly attribute sequence<DOMString> cssPaths;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A set of paths, relative to the extension root, of JavaScript scripts to
|
||||||
|
* execute in matching pages.
|
||||||
|
*/
|
||||||
|
[Cached, Constant, Frozen]
|
||||||
|
readonly attribute sequence<DOMString> jsPaths;
|
||||||
|
};
|
||||||
|
|
||||||
|
dictionary WebExtensionContentScriptInit {
|
||||||
|
boolean allFrames = false;
|
||||||
|
|
||||||
|
boolean matchAboutBlank = false;
|
||||||
|
|
||||||
|
ContentScriptRunAt runAt = "document_idle";
|
||||||
|
|
||||||
|
unsigned long long? frameID = null;
|
||||||
|
|
||||||
|
required MatchPatternSet matches;
|
||||||
|
|
||||||
|
MatchPatternSet? excludeMatches = null;
|
||||||
|
|
||||||
|
sequence<MatchGlob>? includeGlobs = null;
|
||||||
|
|
||||||
|
sequence<MatchGlob>? excludeGlobs = null;
|
||||||
|
|
||||||
|
sequence<DOMString> cssPaths = [];
|
||||||
|
|
||||||
|
sequence<DOMString> jsPaths = [];
|
||||||
|
};
|
||||||
@@ -58,6 +58,12 @@ interface WebExtensionPolicy {
|
|||||||
[Pure]
|
[Pure]
|
||||||
attribute MatchPatternSet allowedOrigins;
|
attribute MatchPatternSet allowedOrigins;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The set of content scripts active for this extension.
|
||||||
|
*/
|
||||||
|
[Cached, Constant, Frozen]
|
||||||
|
readonly attribute sequence<WebExtensionContentScript> contentScripts;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* True if the extension is currently active, false otherwise. When active,
|
* True if the extension is currently active, false otherwise. When active,
|
||||||
* the extension's moz-extension: protocol will point to the given baseURI,
|
* the extension's moz-extension: protocol will point to the given baseURI,
|
||||||
@@ -141,6 +147,8 @@ dictionary WebExtensionInit {
|
|||||||
|
|
||||||
sequence<MatchGlob> webAccessibleResources = [];
|
sequence<MatchGlob> webAccessibleResources = [];
|
||||||
|
|
||||||
|
sequence<WebExtensionContentScriptInit> contentScripts = [];
|
||||||
|
|
||||||
DOMString? contentSecurityPolicy = null;
|
DOMString? contentSecurityPolicy = null;
|
||||||
|
|
||||||
sequence<DOMString>? backgroundScripts = null;
|
sequence<DOMString>? backgroundScripts = null;
|
||||||
|
|||||||
@@ -944,6 +944,7 @@ WEBIDL_FILES = [
|
|||||||
'WaveShaperNode.webidl',
|
'WaveShaperNode.webidl',
|
||||||
'WebAuthentication.webidl',
|
'WebAuthentication.webidl',
|
||||||
'WebComponents.webidl',
|
'WebComponents.webidl',
|
||||||
|
'WebExtensionContentScript.webidl',
|
||||||
'WebExtensionPolicy.webidl',
|
'WebExtensionPolicy.webidl',
|
||||||
'WebGL2RenderingContext.webidl',
|
'WebGL2RenderingContext.webidl',
|
||||||
'WebGLRenderingContext.webidl',
|
'WebGLRenderingContext.webidl',
|
||||||
|
|||||||
@@ -13,7 +13,9 @@ module.exports = {
|
|||||||
"MatchGlob": false,
|
"MatchGlob": false,
|
||||||
"MatchPattern": true,
|
"MatchPattern": true,
|
||||||
"MatchPatternSet": false,
|
"MatchPatternSet": false,
|
||||||
|
"WebExtensionContentScript": false,
|
||||||
"WebExtensionPolicy": false,
|
"WebExtensionPolicy": false,
|
||||||
|
|
||||||
// Specific to WebExtensions:
|
// Specific to WebExtensions:
|
||||||
"AppConstants": true,
|
"AppConstants": true,
|
||||||
"Extension": true,
|
"Extension": true,
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "mozilla/ExtensionPolicyService.h"
|
#include "mozilla/ExtensionPolicyService.h"
|
||||||
|
#include "mozilla/extensions/WebExtensionContentScript.h"
|
||||||
#include "mozilla/extensions/WebExtensionPolicy.h"
|
#include "mozilla/extensions/WebExtensionPolicy.h"
|
||||||
|
|
||||||
#include "mozilla/ClearOnShutdown.h"
|
#include "mozilla/ClearOnShutdown.h"
|
||||||
|
|||||||
@@ -185,10 +185,16 @@ bool
|
|||||||
URLInfo::InheritsPrincipal() const
|
URLInfo::InheritsPrincipal() const
|
||||||
{
|
{
|
||||||
if (!mInheritsPrincipal.isSome()) {
|
if (!mInheritsPrincipal.isSome()) {
|
||||||
bool inherits = false;
|
// For our purposes, about:blank and about:srcdoc are treated as URIs that
|
||||||
nsresult rv = NS_URIChainHasFlags(mURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
|
// inherit principals.
|
||||||
&inherits);
|
bool inherits = Spec().EqualsLiteral("about:blank") || Spec().EqualsLiteral("about:srcdoc");
|
||||||
Unused << NS_WARN_IF(NS_FAILED(rv));
|
|
||||||
|
if (!inherits) {
|
||||||
|
nsresult rv = NS_URIChainHasFlags(mURI, nsIProtocolHandler::URI_INHERITS_SECURITY_CONTEXT,
|
||||||
|
&inherits);
|
||||||
|
Unused << NS_WARN_IF(NS_FAILED(rv));
|
||||||
|
}
|
||||||
|
|
||||||
mInheritsPrincipal.emplace(inherits);
|
mInheritsPrincipal.emplace(inherits);
|
||||||
}
|
}
|
||||||
return mInheritsPrincipal.ref();
|
return mInheritsPrincipal.ref();
|
||||||
|
|||||||
178
toolkit/components/extensions/WebExtensionContentScript.h
Normal file
178
toolkit/components/extensions/WebExtensionContentScript.h
Normal file
@@ -0,0 +1,178 @@
|
|||||||
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2; -*- */
|
||||||
|
/* 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/. */
|
||||||
|
|
||||||
|
#ifndef mozilla_extensions_WebExtensionContentScript_h
|
||||||
|
#define mozilla_extensions_WebExtensionContentScript_h
|
||||||
|
|
||||||
|
#include "mozilla/dom/BindingDeclarations.h"
|
||||||
|
#include "mozilla/dom/WebExtensionContentScriptBinding.h"
|
||||||
|
|
||||||
|
#include "jspubtd.h"
|
||||||
|
|
||||||
|
#include "mozilla/Maybe.h"
|
||||||
|
#include "mozilla/Variant.h"
|
||||||
|
#include "mozilla/extensions/MatchGlob.h"
|
||||||
|
#include "mozilla/extensions/MatchPattern.h"
|
||||||
|
#include "nsCOMPtr.h"
|
||||||
|
#include "nsCycleCollectionParticipant.h"
|
||||||
|
#include "nsISupports.h"
|
||||||
|
#include "nsWrapperCache.h"
|
||||||
|
|
||||||
|
class nsILoadInfo;
|
||||||
|
class nsPIDOMWindowOuter;
|
||||||
|
|
||||||
|
namespace mozilla {
|
||||||
|
namespace extensions {
|
||||||
|
|
||||||
|
using dom::Nullable;
|
||||||
|
using ContentScriptInit = dom::WebExtensionContentScriptInit;
|
||||||
|
|
||||||
|
class WebExtensionPolicy;
|
||||||
|
|
||||||
|
class MOZ_STACK_CLASS DocInfo final
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
DocInfo(const URLInfo& aURL, nsILoadInfo* aLoadInfo);
|
||||||
|
|
||||||
|
MOZ_IMPLICIT DocInfo(nsPIDOMWindowOuter* aWindow);
|
||||||
|
|
||||||
|
const URLInfo& URL() const { return mURL; }
|
||||||
|
|
||||||
|
nsIPrincipal* Principal() const;
|
||||||
|
|
||||||
|
const URLInfo& PrincipalURL() const;
|
||||||
|
|
||||||
|
bool IsTopLevel() const;
|
||||||
|
|
||||||
|
uint64_t FrameID() const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
void SetURL(const URLInfo& aURL);
|
||||||
|
|
||||||
|
const URLInfo mURL;
|
||||||
|
mutable Maybe<const URLInfo> mPrincipalURL;
|
||||||
|
|
||||||
|
mutable Maybe<bool> mIsTopLevel;
|
||||||
|
mutable Maybe<nsCOMPtr<nsIPrincipal>> mPrincipal;
|
||||||
|
mutable Maybe<uint64_t> mFrameID;
|
||||||
|
|
||||||
|
using Window = nsPIDOMWindowOuter*;
|
||||||
|
using LoadInfo = nsILoadInfo*;
|
||||||
|
|
||||||
|
const Variant<LoadInfo, Window> mObj;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
class WebExtensionContentScript final : public nsISupports
|
||||||
|
, public nsWrapperCache
|
||||||
|
{
|
||||||
|
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
|
||||||
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebExtensionContentScript)
|
||||||
|
|
||||||
|
|
||||||
|
using MatchGlobArray = nsTArray<RefPtr<MatchGlob>>;
|
||||||
|
using RunAtEnum = dom::ContentScriptRunAt;
|
||||||
|
|
||||||
|
static already_AddRefed<WebExtensionContentScript>
|
||||||
|
Constructor(dom::GlobalObject& aGlobal,
|
||||||
|
WebExtensionPolicy& aExtension,
|
||||||
|
const ContentScriptInit& aInit,
|
||||||
|
ErrorResult& aRv);
|
||||||
|
|
||||||
|
|
||||||
|
bool Matches(const DocInfo& aDoc) const;
|
||||||
|
bool MatchesURI(const URLInfo& aURL) const;
|
||||||
|
|
||||||
|
bool MatchesLoadInfo(const URLInfo& aURL, nsILoadInfo* aLoadInfo) const
|
||||||
|
{
|
||||||
|
return Matches({aURL, aLoadInfo});
|
||||||
|
}
|
||||||
|
bool MatchesWindow(nsPIDOMWindowOuter* aWindow) const
|
||||||
|
{
|
||||||
|
return Matches(aWindow);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WebExtensionPolicy* Extension() { return mExtension; }
|
||||||
|
const WebExtensionPolicy* Extension() const { return mExtension; }
|
||||||
|
|
||||||
|
bool AllFrames() const { return mAllFrames; }
|
||||||
|
bool MatchAboutBlank() const { return mMatchAboutBlank; }
|
||||||
|
RunAtEnum RunAt() const { return mRunAt; }
|
||||||
|
|
||||||
|
Nullable<uint64_t> GetFrameID() const { return mFrameID; }
|
||||||
|
|
||||||
|
MatchPatternSet* Matches() { return mMatches; }
|
||||||
|
const MatchPatternSet* GetMatches() const { return mMatches; }
|
||||||
|
|
||||||
|
MatchPatternSet* GetExcludeMatches() { return mExcludeMatches; }
|
||||||
|
const MatchPatternSet* GetExcludeMatches() const { return mExcludeMatches; }
|
||||||
|
|
||||||
|
void GetIncludeGlobs(Nullable<MatchGlobArray>& aGlobs)
|
||||||
|
{
|
||||||
|
ToNullable(mExcludeGlobs, aGlobs);
|
||||||
|
}
|
||||||
|
void GetExcludeGlobs(Nullable<MatchGlobArray>& aGlobs)
|
||||||
|
{
|
||||||
|
ToNullable(mExcludeGlobs, aGlobs);
|
||||||
|
}
|
||||||
|
|
||||||
|
void GetCssPaths(nsTArray<nsString>& aPaths) const
|
||||||
|
{
|
||||||
|
aPaths.AppendElements(mCssPaths);
|
||||||
|
}
|
||||||
|
void GetJsPaths(nsTArray<nsString>& aPaths) const
|
||||||
|
{
|
||||||
|
aPaths.AppendElements(mJsPaths);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
WebExtensionPolicy* GetParentObject() const { return mExtension; }
|
||||||
|
|
||||||
|
virtual JSObject* WrapObject(JSContext* aCx, JS::HandleObject aGivenProto) override;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
friend class WebExtensionPolicy;
|
||||||
|
|
||||||
|
virtual ~WebExtensionContentScript() = default;
|
||||||
|
|
||||||
|
WebExtensionContentScript(WebExtensionPolicy& aExtension,
|
||||||
|
const ContentScriptInit& aInit,
|
||||||
|
ErrorResult& aRv);
|
||||||
|
|
||||||
|
private:
|
||||||
|
RefPtr<WebExtensionPolicy> mExtension;
|
||||||
|
|
||||||
|
RefPtr<MatchPatternSet> mMatches;
|
||||||
|
RefPtr<MatchPatternSet> mExcludeMatches;
|
||||||
|
|
||||||
|
Nullable<MatchGlobSet> mIncludeGlobs;
|
||||||
|
Nullable<MatchGlobSet> mExcludeGlobs;
|
||||||
|
|
||||||
|
nsTArray<nsString> mCssPaths;
|
||||||
|
nsTArray<nsString> mJsPaths;
|
||||||
|
|
||||||
|
RunAtEnum mRunAt;
|
||||||
|
|
||||||
|
bool mAllFrames;
|
||||||
|
Nullable<uint64_t> mFrameID;
|
||||||
|
bool mMatchAboutBlank;
|
||||||
|
|
||||||
|
template <typename T, typename U>
|
||||||
|
void
|
||||||
|
ToNullable(const Nullable<T>& aInput, Nullable<U>& aOutput)
|
||||||
|
{
|
||||||
|
if (aInput.IsNull()) {
|
||||||
|
aOutput.SetNull();
|
||||||
|
} else {
|
||||||
|
aOutput.SetValue(aInput.Value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace extensions
|
||||||
|
} // namespace mozilla
|
||||||
|
|
||||||
|
#endif // mozilla_extensions_WebExtensionContentScript_h
|
||||||
@@ -4,8 +4,10 @@
|
|||||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
#include "mozilla/ExtensionPolicyService.h"
|
#include "mozilla/ExtensionPolicyService.h"
|
||||||
|
#include "mozilla/extensions/WebExtensionContentScript.h"
|
||||||
#include "mozilla/extensions/WebExtensionPolicy.h"
|
#include "mozilla/extensions/WebExtensionPolicy.h"
|
||||||
|
|
||||||
|
#include "mozilla/AddonManagerWebAPI.h"
|
||||||
#include "nsEscape.h"
|
#include "nsEscape.h"
|
||||||
#include "nsISubstitutingProtocolHandler.h"
|
#include "nsISubstitutingProtocolHandler.h"
|
||||||
#include "nsNetUtil.h"
|
#include "nsNetUtil.h"
|
||||||
@@ -111,6 +113,16 @@ WebExtensionPolicy::WebExtensionPolicy(GlobalObject& aGlobal,
|
|||||||
EPS().DefaultCSP(mContentSecurityPolicy);
|
EPS().DefaultCSP(mContentSecurityPolicy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mContentScripts.SetCapacity(aInit.mContentScripts.Length());
|
||||||
|
for (const auto& scriptInit : aInit.mContentScripts) {
|
||||||
|
RefPtr<WebExtensionContentScript> contentScript =
|
||||||
|
new WebExtensionContentScript(*this, scriptInit, aRv);
|
||||||
|
if (aRv.Failed()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
mContentScripts.AppendElement(Move(contentScript));
|
||||||
|
}
|
||||||
|
|
||||||
nsresult rv = NS_NewURI(getter_AddRefs(mBaseURI), aInit.mBaseURL);
|
nsresult rv = NS_NewURI(getter_AddRefs(mBaseURI), aInit.mBaseURL);
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
aRv.Throw(rv);
|
aRv.Throw(rv);
|
||||||
@@ -262,11 +274,18 @@ WebExtensionPolicy::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
|
|||||||
return WebExtensionPolicyBinding::Wrap(aCx, this, aGivenProto);
|
return WebExtensionPolicyBinding::Wrap(aCx, this, aGivenProto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
WebExtensionPolicy::GetContentScripts(nsTArray<RefPtr<WebExtensionContentScript>>& aScripts) const
|
||||||
|
{
|
||||||
|
aScripts.AppendElements(mContentScripts);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebExtensionPolicy, mParent,
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebExtensionPolicy, mParent,
|
||||||
mLocalizeCallback,
|
mLocalizeCallback,
|
||||||
mHostPermissions,
|
mHostPermissions,
|
||||||
mWebAccessiblePaths)
|
mWebAccessiblePaths,
|
||||||
|
mContentScripts)
|
||||||
|
|
||||||
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebExtensionPolicy)
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebExtensionPolicy)
|
||||||
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||||
@@ -276,5 +295,202 @@ NS_INTERFACE_MAP_END
|
|||||||
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebExtensionPolicy)
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebExtensionPolicy)
|
||||||
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebExtensionPolicy)
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebExtensionPolicy)
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* WebExtensionContentScript
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
/* static */ already_AddRefed<WebExtensionContentScript>
|
||||||
|
WebExtensionContentScript::Constructor(GlobalObject& aGlobal,
|
||||||
|
WebExtensionPolicy& aExtension,
|
||||||
|
const ContentScriptInit& aInit,
|
||||||
|
ErrorResult& aRv)
|
||||||
|
{
|
||||||
|
RefPtr<WebExtensionContentScript> script = new WebExtensionContentScript(aExtension, aInit, aRv);
|
||||||
|
if (aRv.Failed()) {
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
return script.forget();
|
||||||
|
}
|
||||||
|
|
||||||
|
WebExtensionContentScript::WebExtensionContentScript(WebExtensionPolicy& aExtension,
|
||||||
|
const ContentScriptInit& aInit,
|
||||||
|
ErrorResult& aRv)
|
||||||
|
: mExtension(&aExtension)
|
||||||
|
, mMatches(aInit.mMatches)
|
||||||
|
, mExcludeMatches(aInit.mExcludeMatches)
|
||||||
|
, mCssPaths(aInit.mCssPaths)
|
||||||
|
, mJsPaths(aInit.mJsPaths)
|
||||||
|
, mRunAt(aInit.mRunAt)
|
||||||
|
, mAllFrames(aInit.mAllFrames)
|
||||||
|
, mFrameID(aInit.mFrameID)
|
||||||
|
, mMatchAboutBlank(aInit.mMatchAboutBlank)
|
||||||
|
{
|
||||||
|
if (!aInit.mIncludeGlobs.IsNull()) {
|
||||||
|
mIncludeGlobs.SetValue().AppendElements(aInit.mIncludeGlobs.Value());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!aInit.mExcludeGlobs.IsNull()) {
|
||||||
|
mExcludeGlobs.SetValue().AppendElements(aInit.mExcludeGlobs.Value());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
WebExtensionContentScript::Matches(const DocInfo& aDoc) const
|
||||||
|
{
|
||||||
|
if (!mFrameID.IsNull()) {
|
||||||
|
if (aDoc.FrameID() != mFrameID.Value()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!mAllFrames && !aDoc.IsTopLevel()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mMatchAboutBlank && aDoc.URL().InheritsPrincipal()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!MatchesURI(aDoc.PrincipalURL())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
WebExtensionContentScript::MatchesURI(const URLInfo& aURL) const
|
||||||
|
{
|
||||||
|
if (!mMatches->Matches(aURL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mExcludeMatches && mExcludeMatches->Matches(aURL)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mIncludeGlobs.IsNull() && !mIncludeGlobs.Value().Matches(aURL.Spec())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!mExcludeGlobs.IsNull() && mExcludeGlobs.Value().Matches(aURL.Spec())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (AddonManagerWebAPI::IsValidSite(aURL.URI())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
JSObject*
|
||||||
|
WebExtensionContentScript::WrapObject(JSContext* aCx, JS::HandleObject aGivenProto)
|
||||||
|
{
|
||||||
|
return WebExtensionContentScriptBinding::Wrap(aCx, this, aGivenProto);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(WebExtensionContentScript,
|
||||||
|
mMatches, mExcludeMatches,
|
||||||
|
mIncludeGlobs, mExcludeGlobs,
|
||||||
|
mExtension)
|
||||||
|
|
||||||
|
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(WebExtensionContentScript)
|
||||||
|
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
|
||||||
|
NS_INTERFACE_MAP_ENTRY(nsISupports)
|
||||||
|
NS_INTERFACE_MAP_END
|
||||||
|
|
||||||
|
NS_IMPL_CYCLE_COLLECTING_ADDREF(WebExtensionContentScript)
|
||||||
|
NS_IMPL_CYCLE_COLLECTING_RELEASE(WebExtensionContentScript)
|
||||||
|
|
||||||
|
|
||||||
|
/*****************************************************************************
|
||||||
|
* DocInfo
|
||||||
|
*****************************************************************************/
|
||||||
|
|
||||||
|
DocInfo::DocInfo(const URLInfo& aURL, nsILoadInfo* aLoadInfo)
|
||||||
|
: mURL(aURL)
|
||||||
|
, mObj(AsVariant(aLoadInfo))
|
||||||
|
{}
|
||||||
|
|
||||||
|
DocInfo::DocInfo(nsPIDOMWindowOuter* aWindow)
|
||||||
|
: mURL(aWindow->GetDocumentURI())
|
||||||
|
, mObj(AsVariant(aWindow))
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool
|
||||||
|
DocInfo::IsTopLevel() const
|
||||||
|
{
|
||||||
|
if (mIsTopLevel.isNothing()) {
|
||||||
|
struct Matcher
|
||||||
|
{
|
||||||
|
bool match(Window aWin) { return aWin->IsTopLevelWindow(); }
|
||||||
|
bool match(LoadInfo aLoadInfo) { return aLoadInfo->GetIsTopLevelLoad(); }
|
||||||
|
};
|
||||||
|
mIsTopLevel.emplace(mObj.match(Matcher()));
|
||||||
|
}
|
||||||
|
return mIsTopLevel.ref();
|
||||||
|
}
|
||||||
|
|
||||||
|
uint64_t
|
||||||
|
DocInfo::FrameID() const
|
||||||
|
{
|
||||||
|
if (mFrameID.isNothing()) {
|
||||||
|
if (IsTopLevel()) {
|
||||||
|
mFrameID.emplace(0);
|
||||||
|
} else {
|
||||||
|
struct Matcher
|
||||||
|
{
|
||||||
|
uint64_t match(Window aWin) { return aWin->GetCurrentInnerWindow()->WindowID(); }
|
||||||
|
uint64_t match(LoadInfo aLoadInfo) { return aLoadInfo->GetInnerWindowID(); }
|
||||||
|
};
|
||||||
|
mFrameID.emplace(mObj.match(Matcher()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mFrameID.ref();
|
||||||
|
}
|
||||||
|
|
||||||
|
nsIPrincipal*
|
||||||
|
DocInfo::Principal() const
|
||||||
|
{
|
||||||
|
if (mPrincipal.isNothing()) {
|
||||||
|
struct Matcher
|
||||||
|
{
|
||||||
|
nsIPrincipal* match(Window aWin)
|
||||||
|
{
|
||||||
|
nsCOMPtr<nsIDocument> doc = aWin->GetDoc();
|
||||||
|
return doc->NodePrincipal();
|
||||||
|
}
|
||||||
|
nsIPrincipal* match(LoadInfo aLoadInfo) { return aLoadInfo->PrincipalToInherit(); }
|
||||||
|
};
|
||||||
|
mPrincipal.emplace(mObj.match(Matcher()));
|
||||||
|
}
|
||||||
|
return mPrincipal.ref();
|
||||||
|
}
|
||||||
|
|
||||||
|
const URLInfo&
|
||||||
|
DocInfo::PrincipalURL() const
|
||||||
|
{
|
||||||
|
if (!URL().InheritsPrincipal()) {
|
||||||
|
return URL();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mPrincipalURL.isNothing()) {
|
||||||
|
nsIPrincipal* prin = Principal();
|
||||||
|
nsCOMPtr<nsIURI> uri;
|
||||||
|
if (prin && NS_SUCCEEDED(prin->GetURI(getter_AddRefs(uri)))) {
|
||||||
|
mPrincipalURL.emplace(uri);
|
||||||
|
} else {
|
||||||
|
mPrincipalURL.emplace(URL());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mPrincipalURL.ref();
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace extensions
|
} // namespace extensions
|
||||||
} // namespace mozilla
|
} // namespace mozilla
|
||||||
|
|||||||
@@ -25,6 +25,8 @@ namespace extensions {
|
|||||||
using dom::WebExtensionInit;
|
using dom::WebExtensionInit;
|
||||||
using dom::WebExtensionLocalizeCallback;
|
using dom::WebExtensionLocalizeCallback;
|
||||||
|
|
||||||
|
class WebExtensionContentScript;
|
||||||
|
|
||||||
class WebExtensionPolicy final : public nsISupports
|
class WebExtensionPolicy final : public nsISupports
|
||||||
, public nsWrapperCache
|
, public nsWrapperCache
|
||||||
, public SupportsWeakPtr<WebExtensionPolicy>
|
, public SupportsWeakPtr<WebExtensionPolicy>
|
||||||
@@ -34,6 +36,8 @@ public:
|
|||||||
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebExtensionPolicy)
|
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(WebExtensionPolicy)
|
||||||
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebExtensionPolicy)
|
MOZ_DECLARE_WEAKREFERENCE_TYPENAME(WebExtensionPolicy)
|
||||||
|
|
||||||
|
using ScriptArray = nsTArray<RefPtr<WebExtensionContentScript>>;
|
||||||
|
|
||||||
static already_AddRefed<WebExtensionPolicy>
|
static already_AddRefed<WebExtensionPolicy>
|
||||||
Constructor(dom::GlobalObject& aGlobal, const WebExtensionInit& aInit, ErrorResult& aRv);
|
Constructor(dom::GlobalObject& aGlobal, const WebExtensionInit& aInit, ErrorResult& aRv);
|
||||||
|
|
||||||
@@ -106,6 +110,9 @@ public:
|
|||||||
mPermissions = new AtomSet(aPermissions);
|
mPermissions = new AtomSet(aPermissions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GetContentScripts(ScriptArray& aScripts) const;
|
||||||
|
const ScriptArray& ContentScripts() const { return mContentScripts; }
|
||||||
|
|
||||||
|
|
||||||
bool Active() const { return mActive; }
|
bool Active() const { return mActive; }
|
||||||
void SetActive(bool aActive, ErrorResult& aRv);
|
void SetActive(bool aActive, ErrorResult& aRv);
|
||||||
@@ -154,6 +161,8 @@ private:
|
|||||||
MatchGlobSet mWebAccessiblePaths;
|
MatchGlobSet mWebAccessiblePaths;
|
||||||
|
|
||||||
Nullable<nsTArray<nsString>> mBackgroundScripts;
|
Nullable<nsTArray<nsString>> mBackgroundScripts;
|
||||||
|
|
||||||
|
nsTArray<RefPtr<WebExtensionContentScript>> mContentScripts;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace extensions
|
} // namespace extensions
|
||||||
|
|||||||
@@ -52,6 +52,7 @@ EXPORTS.mozilla = [
|
|||||||
EXPORTS.mozilla.extensions = [
|
EXPORTS.mozilla.extensions = [
|
||||||
'MatchGlob.h',
|
'MatchGlob.h',
|
||||||
'MatchPattern.h',
|
'MatchPattern.h',
|
||||||
|
'WebExtensionContentScript.h',
|
||||||
'WebExtensionPolicy.h',
|
'WebExtensionPolicy.h',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Iframe document</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<title>Top-level frame document</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<iframe src="file_iframe.html"></iframe>
|
||||||
|
<iframe src="about:blank"></iframe>
|
||||||
|
<iframe srcdoc="Iframe srcdoc"></iframe>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -0,0 +1,176 @@
|
|||||||
|
/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
||||||
|
/* vim: set sts=2 sw=2 et tw=80: */
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
const {newURI} = Services.io;
|
||||||
|
|
||||||
|
const server = createHttpServer();
|
||||||
|
server.registerDirectory("/data/", do_get_file("data"));
|
||||||
|
|
||||||
|
let policy = new WebExtensionPolicy({
|
||||||
|
id: "foo@bar.baz",
|
||||||
|
mozExtensionHostname: "88fb51cd-159f-4859-83db-7065485bc9b2",
|
||||||
|
baseURL: "file:///foo",
|
||||||
|
|
||||||
|
allowedOrigins: new MatchPatternSet([]),
|
||||||
|
localizeCallback() {},
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(async function test_WebExtensinonContentScript_url_matching() {
|
||||||
|
let contentScript = new WebExtensionContentScript(policy, {
|
||||||
|
matches: new MatchPatternSet(["http://foo.com/bar", "*://bar.com/baz/*"]),
|
||||||
|
|
||||||
|
excludeMatches: new MatchPatternSet(["*://bar.com/baz/quux"]),
|
||||||
|
|
||||||
|
includeGlobs: ["*flerg*", "*.com/bar", "*/quux"].map(glob => new MatchGlob(glob)),
|
||||||
|
|
||||||
|
excludeGlobs: ["*glorg*"].map(glob => new MatchGlob(glob)),
|
||||||
|
});
|
||||||
|
|
||||||
|
ok(contentScript.matchesURI(newURI("http://foo.com/bar")),
|
||||||
|
"Simple matches include should match");
|
||||||
|
|
||||||
|
ok(contentScript.matchesURI(newURI("https://bar.com/baz/xflergx")),
|
||||||
|
"Simple matches include should match");
|
||||||
|
|
||||||
|
ok(!contentScript.matchesURI(newURI("https://bar.com/baz/xx")),
|
||||||
|
"Failed includeGlobs match pattern should not match");
|
||||||
|
|
||||||
|
ok(!contentScript.matchesURI(newURI("https://bar.com/baz/quux")),
|
||||||
|
"Excluded match pattern should not match");
|
||||||
|
|
||||||
|
ok(!contentScript.matchesURI(newURI("https://bar.com/baz/xflergxglorgx")),
|
||||||
|
"Excluded match glob should not match");
|
||||||
|
});
|
||||||
|
|
||||||
|
async function loadURL(url, {frameCount}) {
|
||||||
|
let windows = new Map();
|
||||||
|
let requests = new Map();
|
||||||
|
|
||||||
|
let resolveLoad;
|
||||||
|
let loadPromise = new Promise(resolve => { resolveLoad = resolve; });
|
||||||
|
|
||||||
|
function requestObserver(request) {
|
||||||
|
request.QueryInterface(Ci.nsIChannel);
|
||||||
|
if (request.isDocument) {
|
||||||
|
requests.set(request.name, request);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function loadObserver(window) {
|
||||||
|
windows.set(window.location.href, window);
|
||||||
|
if (windows.size == frameCount) {
|
||||||
|
resolveLoad();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Services.obs.addObserver(requestObserver, "http-on-examine-response");
|
||||||
|
Services.obs.addObserver(loadObserver, "content-document-global-created");
|
||||||
|
|
||||||
|
let webNav = Services.appShell.createWindowlessBrowser(false);
|
||||||
|
webNav.loadURI(url, 0, null, null, null);
|
||||||
|
|
||||||
|
await loadPromise;
|
||||||
|
|
||||||
|
Services.obs.removeObserver(requestObserver, "http-on-examine-response");
|
||||||
|
Services.obs.removeObserver(loadObserver, "content-document-global-created");
|
||||||
|
|
||||||
|
return {webNav, windows, requests};
|
||||||
|
}
|
||||||
|
|
||||||
|
add_task(async function test_WebExtensinonContentScript_frame_matching() {
|
||||||
|
if (AppConstants.platform == "linux") {
|
||||||
|
// The windowless browser currently does not load correctly on Linux on
|
||||||
|
// infra.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let baseURL = `http://localhost:${server.identity.primaryPort}/data`;
|
||||||
|
let urls = {
|
||||||
|
topLevel: `${baseURL}/file_toplevel.html`,
|
||||||
|
iframe: `${baseURL}/file_iframe.html`,
|
||||||
|
srcdoc: "about:srcdoc",
|
||||||
|
aboutBlank: "about:blank",
|
||||||
|
};
|
||||||
|
|
||||||
|
let {webNav, windows, requests} = await loadURL(urls.topLevel, {frameCount: 4});
|
||||||
|
|
||||||
|
let tests = [
|
||||||
|
{
|
||||||
|
contentScript: {
|
||||||
|
matches: new MatchPatternSet(["http://localhost/data/*"]),
|
||||||
|
},
|
||||||
|
topLevel: true,
|
||||||
|
iframe: false,
|
||||||
|
aboutBlank: false,
|
||||||
|
srcdoc: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
contentScript: {
|
||||||
|
matches: new MatchPatternSet(["http://localhost/data/*"]),
|
||||||
|
frameID: 0,
|
||||||
|
},
|
||||||
|
topLevel: true,
|
||||||
|
iframe: false,
|
||||||
|
aboutBlank: false,
|
||||||
|
srcdoc: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
contentScript: {
|
||||||
|
matches: new MatchPatternSet(["http://localhost/data/*"]),
|
||||||
|
allFrames: true,
|
||||||
|
},
|
||||||
|
topLevel: true,
|
||||||
|
iframe: true,
|
||||||
|
aboutBlank: false,
|
||||||
|
srcdoc: false,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
contentScript: {
|
||||||
|
matches: new MatchPatternSet(["http://localhost/data/*"]),
|
||||||
|
allFrames: true,
|
||||||
|
matchAboutBlank: true,
|
||||||
|
},
|
||||||
|
topLevel: true,
|
||||||
|
iframe: true,
|
||||||
|
aboutBlank: true,
|
||||||
|
srcdoc: true,
|
||||||
|
},
|
||||||
|
|
||||||
|
{
|
||||||
|
contentScript: {
|
||||||
|
matches: new MatchPatternSet(["http://foo.com/data/*"]),
|
||||||
|
allFrames: true,
|
||||||
|
matchAboutBlank: true,
|
||||||
|
},
|
||||||
|
topLevel: false,
|
||||||
|
iframe: false,
|
||||||
|
aboutBlank: false,
|
||||||
|
srcdoc: false,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
for (let [i, test] of tests.entries()) {
|
||||||
|
let contentScript = new WebExtensionContentScript(policy, test.contentScript);
|
||||||
|
|
||||||
|
for (let [frame, url] of Object.entries(urls)) {
|
||||||
|
let should = test[frame] ? "should" : "should not";
|
||||||
|
|
||||||
|
equal(contentScript.matchesWindow(windows.get(url)),
|
||||||
|
test[frame],
|
||||||
|
`Script ${i} ${should} match the ${frame} frame`);
|
||||||
|
|
||||||
|
if (url.startsWith("http")) {
|
||||||
|
let request = requests.get(url);
|
||||||
|
|
||||||
|
equal(contentScript.matchesLoadInfo(request.URI, request.loadInfo),
|
||||||
|
test[frame],
|
||||||
|
`Script ${i} ${should} match the request LoadInfo for ${frame} frame`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
webNav.close();
|
||||||
|
});
|
||||||
@@ -10,6 +10,7 @@ support-files =
|
|||||||
tags = webextensions
|
tags = webextensions
|
||||||
|
|
||||||
[test_MatchPattern.js]
|
[test_MatchPattern.js]
|
||||||
|
[test_WebExtensionContentScript.js]
|
||||||
[test_WebExtensionPolicy.js]
|
[test_WebExtensionPolicy.js]
|
||||||
|
|
||||||
[test_csp_custom_policies.js]
|
[test_csp_custom_policies.js]
|
||||||
|
|||||||
Reference in New Issue
Block a user