import changes from `dev' branch of rmottola/Arctic-Fox:

- Bug 1164292 - Tests. r=gabor (83c906b70)
- Bug 1164292 - Order the nsEP whitelist array. r=gabor (9ca334913)
- Bug 1165162 - Tests. r=gabor (5079f112f)
- Bug 1165162 - Introduce a helper for converting from origin strings to a principal. rpending=Yoshi (6d3dc9d23)
- Bug 1165162 - Fix typo in comment. r=me DONTBUILD (737bbcae7)
- Bug 1165162 - Fix up nsScriptSecurityManager::AppStatusForPrincipal to compare principals rather than origins. r=gabor, sr=sicking (17db467fb)
- Bug 1165162 - Add nsIPrincipal::cookieJar. r=sicking (92090c96a)
- Bug 1152033 - Expose CSS lexer to js. r=heycam, r=bz (d4103b799)
- Bug 1129999 - Implement CSP devtool using GCLI; CSP to JSON (r=sstamm,bholley) (3c2099d79)
- Bug 1156847 - Part 1: Add the Response::GetSecurityInfo() helper method; r=nsm (b4973d6b3)
- Bug 1156847 - Part 2: Store the security info for a service worker on its WorkerPrivate; r=bent,khuey (46cad4537)
This commit is contained in:
2021-02-17 14:49:08 +08:00
parent fd2bb43e4b
commit 6b21c00205
32 changed files with 950 additions and 34 deletions
+24
View File
@@ -6,6 +6,7 @@
#include "mozilla/BasePrincipal.h"
#include "nsIContentSecurityPolicy.h"
#include "nsIObjectInputStream.h"
#include "nsIObjectOutputStream.h"
@@ -14,6 +15,7 @@
#include "nsNullPrincipal.h"
#include "nsScriptSecurityManager.h"
#include "mozilla/dom/CSPDictionariesBinding.h"
#include "mozilla/dom/ToJSValue.h"
namespace mozilla {
@@ -117,6 +119,19 @@ BasePrincipal::SetCsp(nsIContentSecurityPolicy* aCsp)
return NS_OK;
}
NS_IMETHODIMP
BasePrincipal::GetCspJSON(nsAString& outCSPinJSON)
{
outCSPinJSON.Truncate();
dom::CSPPolicies jsonPolicies;
if (!mCSP) {
jsonPolicies.ToJSON(outCSPinJSON);
return NS_OK;
}
return mCSP->ToJSON(outCSPinJSON);
}
NS_IMETHODIMP
BasePrincipal::GetIsNullPrincipal(bool* aIsNullPrincipal)
{
@@ -149,6 +164,15 @@ BasePrincipal::GetOriginSuffix(nsACString& aOriginAttributes)
return NS_OK;
}
NS_IMETHODIMP
BasePrincipal::GetCookieJar(nsACString& aCookieJar)
{
// We just forward to .jarPrefix for now, which is a nice compact
// stringification of the (appId, inBrowser) tuple. This will eventaully be
// swapped out for an origin attribute - see the comment in nsIPrincipal.idl.
return GetJarPrefix(aCookieJar);
}
NS_IMETHODIMP
BasePrincipal::GetAppStatus(uint16_t* aAppStatus)
{
+2
View File
@@ -69,10 +69,12 @@ public:
NS_IMETHOD SubsumesConsideringDomain(nsIPrincipal* other, bool* _retval) final;
NS_IMETHOD GetCsp(nsIContentSecurityPolicy** aCsp) override;
NS_IMETHOD SetCsp(nsIContentSecurityPolicy* aCsp) override;
NS_IMETHOD GetCspJSON(nsAString& outCSPinJSON) override;
NS_IMETHOD GetIsNullPrincipal(bool* aIsNullPrincipal) override;
NS_IMETHOD GetJarPrefix(nsACString& aJarPrefix) final;
NS_IMETHOD GetOriginAttributes(JSContext* aCx, JS::MutableHandle<JS::Value> aVal) final;
NS_IMETHOD GetOriginSuffix(nsACString& aOriginSuffix) final;
NS_IMETHOD GetCookieJar(nsACString& aCookieJar) final;
NS_IMETHOD GetAppStatus(uint16_t* aAppStatus) final;
NS_IMETHOD GetAppId(uint32_t* aAppStatus) final;
NS_IMETHOD GetIsInBrowserElement(bool* aIsInBrowserElement) final;
+2
View File
@@ -6,6 +6,8 @@
TEST_DIRS += ['tests/mochitest']
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
XPIDL_SOURCES += [
'nsIDomainPolicy.idl',
'nsIPrincipal.idl',
+32 -1
View File
@@ -20,7 +20,7 @@ interface nsIContentSecurityPolicy;
[ptr] native JSPrincipals(JSPrincipals);
[ptr] native PrincipalArray(nsTArray<nsCOMPtr<nsIPrincipal> >);
[scriptable, builtinclass, uuid(74fb6760-4ae7-4ec7-8ac7-06817c60a93a)]
[scriptable, builtinclass, uuid(49c2faf0-b6de-4640-8d0f-e0217baa8627)]
interface nsIPrincipal : nsISerializable
{
/**
@@ -142,6 +142,13 @@ interface nsIPrincipal : nsISerializable
*/
[noscript] attribute nsIContentSecurityPolicy csp;
/**
* The CSP of the principal in JSON notation.
* Note, that the CSP itself is not exposed to JS, but script
* should be able to obtain a JSON representation of the CSP.
*/
readonly attribute AString cspJSON;
/**
* Returns the jar prefix of the principal.
* The jar prefix is a string that can be used to isolate data or
@@ -188,6 +195,30 @@ interface nsIPrincipal : nsISerializable
*/
readonly attribute AUTF8String originSuffix;
/**
* Opaque string token representing the "cookie jar" associated with this
* principal. Cookie jars are intended to be a tag associated with persistent
* data (like cookies, localStorage data, etc) such that all data associated
* with a given cookie jar can be quickly located and (for example) deleted.
* Code from many origins may share a given cookie jar, so callers still need
* to consult .origin (or equivalent) to compartmentalize data - the cookie
* jar should _only_ be used as a tag in the manner described above.
*
* If two principals are in different cookie jars, they must be cross-origin.
* As such, the information making up the cookie jar token must be contained
* in the originAttributes (i.e. cookieJar must be a function of / derivable
* from originAttributes). Long term, the intention is for the cookie jar
* identifier to simply be an origin attribute. But we don't have that
* attribute yet, and we also need to concatenate the appId and inBrowser
* attributes until those go away.
*
* This getter is designed to hide these details from consumers so that they
* don't need to be updated when we swap out the implementation. For that
* reason, callers should treat the string as opaque and not rely on the
* current format.
*/
readonly attribute ACString cookieJar;
/**
* The base domain of the codebase URI to which this principal pertains
* (generally the document URI), handling null principals and
+31 -1
View File
@@ -604,9 +604,39 @@ NS_IMPL_CI_INTERFACE_GETTER(nsExpandedPrincipal,
nsIPrincipal,
nsIExpandedPrincipal)
struct OriginComparator
{
bool LessThan(nsIPrincipal* a, nsIPrincipal* b) const
{
nsAutoCString originA;
nsresult rv = a->GetOrigin(originA);
NS_ENSURE_SUCCESS(rv, false);
nsAutoCString originB;
rv = b->GetOrigin(originB);
NS_ENSURE_SUCCESS(rv, false);
return originA < originB;
}
bool Equals(nsIPrincipal* a, nsIPrincipal* b) const
{
nsAutoCString originA;
nsresult rv = a->GetOrigin(originA);
NS_ENSURE_SUCCESS(rv, false);
nsAutoCString originB;
rv = b->GetOrigin(originB);
NS_ENSURE_SUCCESS(rv, false);
return a == b;
}
};
nsExpandedPrincipal::nsExpandedPrincipal(nsTArray<nsCOMPtr <nsIPrincipal> > &aWhiteList)
{
mPrincipals.AppendElements(aWhiteList);
// We force the principals to be sorted by origin so that nsExpandedPrincipal
// origins can have a canonical form.
OriginComparator c;
for (size_t i = 0; i < aWhiteList.Length(); ++i) {
mPrincipals.InsertElementSorted(aWhiteList[i], c);
}
}
nsExpandedPrincipal::~nsExpandedPrincipal()
+8 -16
View File
@@ -280,29 +280,21 @@ nsScriptSecurityManager::AppStatusForPrincipal(nsIPrincipal *aPrin)
NS_ENSURE_SUCCESS(app->GetAppStatus(&status),
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
nsAutoCString origin;
NS_ENSURE_SUCCESS(aPrin->GetOrigin(origin),
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
nsString appOrigin;
NS_ENSURE_SUCCESS(app->GetOrigin(appOrigin),
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
// We go from string -> nsIURI -> origin to be sure we
// compare two punny-encoded origins.
nsCOMPtr<nsIURI> appURI;
NS_ENSURE_SUCCESS(NS_NewURI(getter_AddRefs(appURI), appOrigin),
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
nsAutoCString appOriginPunned;
NS_ENSURE_SUCCESS(nsPrincipal::GetOriginForURI(appURI, appOriginPunned),
nsIPrincipal::APP_STATUS_NOT_INSTALLED);
if (!appOriginPunned.Equals(origin)) {
return nsIPrincipal::APP_STATUS_NOT_INSTALLED;
}
return status;
// The app could contain a cross-origin iframe - make sure that the content
// is actually same-origin with the app.
MOZ_ASSERT(inMozBrowser == false, "Checked this above");
OriginAttributes attrs(appId, false);
nsCOMPtr<nsIPrincipal> appPrin = BasePrincipal::CreateCodebasePrincipal(appURI, attrs);
NS_ENSURE_TRUE(appPrin, nsIPrincipal::APP_STATUS_NOT_INSTALLED);
return aPrin->Equals(appPrin) ? status
: nsIPrincipal::APP_STATUS_NOT_INSTALLED;
}
NS_IMETHODIMP
+101
View File
@@ -0,0 +1,101 @@
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/BrowserUtils.jsm");
var ssm = Services.scriptSecurityManager;
function makeURI(uri) { return Services.io.newURI(uri, null, null); }
function checkThrows(f) {
var threw = false;
try { f(); } catch (e) { threw = true }
do_check_true(threw);
}
function checkCrossOrigin(a, b) {
do_check_false(a.equals(b));
do_check_false(a.equalsConsideringDomain(b));
do_check_false(a.subsumes(b));
do_check_false(a.subsumesConsideringDomain(b));
do_check_false(b.subsumes(a));
do_check_false(b.subsumesConsideringDomain(a));
do_check_eq(a.cookieJar === b.cookieJar,
a.originAttributes.appId == b.originAttributes.appId &&
a.originAttributes.inBrowser == b.originAttributes.inBrowser);
}
function checkOriginAttributes(prin, appId, inBrowser, suffix) {
do_check_eq(prin.originAttributes.appId, appId || 0);
do_check_eq(prin.originAttributes.inBrowser, inBrowser || false);
do_check_eq(prin.originSuffix, suffix || '');
if (!prin.isNullPrincipal && !prin.origin.startsWith('[')) {
do_check_true(BrowserUtils.principalFromOrigin(prin.origin).equals(prin));
} else {
checkThrows(() => BrowserUtils.principalFromOrigin(prin.origin));
}
}
function run_test() {
// Attributeless origins.
do_check_eq(ssm.getSystemPrincipal().origin, '[System Principal]');
checkOriginAttributes(ssm.getSystemPrincipal());
var exampleOrg = ssm.createCodebasePrincipal(makeURI('http://example.org'), {});
do_check_eq(exampleOrg.origin, 'http://example.org');
checkOriginAttributes(exampleOrg);
var exampleCom = ssm.createCodebasePrincipal(makeURI('https://www.example.com:123'), {});
do_check_eq(exampleCom.origin, 'https://www.example.com:123');
checkOriginAttributes(exampleCom);
var nullPrin = Cu.getObjectPrincipal(new Cu.Sandbox(null));
do_check_true(/^moz-nullprincipal:\{([0-9]|[a-z]|\-){36}\}$/.test(nullPrin.origin));
checkOriginAttributes(nullPrin);
var ep = Cu.getObjectPrincipal(new Cu.Sandbox([exampleCom, nullPrin, exampleOrg]));
checkOriginAttributes(ep);
checkCrossOrigin(exampleCom, exampleOrg);
checkCrossOrigin(exampleOrg, nullPrin);
// nsEP origins should be in lexical order.
do_check_eq(ep.origin, `[Expanded Principal [${exampleOrg.origin}, ${exampleCom.origin}, ${nullPrin.origin}]]`);
// Make sure createCodebasePrincipal does what the rest of gecko does.
do_check_true(exampleOrg.equals(Cu.getObjectPrincipal(new Cu.Sandbox('http://example.org'))));
//
// Test origin attributes.
//
// Just app.
var exampleOrg_app = ssm.createCodebasePrincipal(makeURI('http://example.org'), {appId: 42});
var nullPrin_app = ssm.createNullPrincipal({appId: 42});
checkOriginAttributes(exampleOrg_app, 42, false, '!appId=42');
checkOriginAttributes(nullPrin_app, 42, false, '!appId=42');
do_check_eq(exampleOrg_app.origin, 'http://example.org!appId=42');
// Just browser.
var exampleOrg_browser = ssm.createCodebasePrincipal(makeURI('http://example.org'), {inBrowser: true});
var nullPrin_browser = ssm.createNullPrincipal({inBrowser: true});
checkOriginAttributes(exampleOrg_browser, 0, true, '!inBrowser=1');
checkOriginAttributes(nullPrin_browser, 0, true, '!inBrowser=1');
do_check_eq(exampleOrg_browser.origin, 'http://example.org!inBrowser=1');
// App and browser.
var exampleOrg_appBrowser = ssm.createCodebasePrincipal(makeURI('http://example.org'), {inBrowser: true, appId: 42});
var nullPrin_appBrowser = ssm.createNullPrincipal({inBrowser: true, appId: 42});
checkOriginAttributes(exampleOrg_appBrowser, 42, true, '!appId=42&inBrowser=1');
checkOriginAttributes(nullPrin_appBrowser, 42, true, '!appId=42&inBrowser=1');
do_check_eq(exampleOrg_appBrowser.origin, 'http://example.org!appId=42&inBrowser=1');
// App and browser, different domain.
var exampleCom_appBrowser = ssm.createCodebasePrincipal(makeURI('https://www.example.com:123'), {appId: 42, inBrowser: true});
checkOriginAttributes(exampleCom_appBrowser, 42, true, '!appId=42&inBrowser=1');
do_check_eq(exampleCom_appBrowser.origin, 'https://www.example.com:123!appId=42&inBrowser=1');
// Check that all of the above are cross-origin.
checkCrossOrigin(exampleOrg_app, exampleOrg);
checkCrossOrigin(exampleOrg_app, nullPrin_app);
checkCrossOrigin(exampleOrg_browser, exampleOrg_app);
checkCrossOrigin(exampleOrg_browser, nullPrin_browser);
checkCrossOrigin(exampleOrg_appBrowser, exampleOrg_app);
checkCrossOrigin(exampleOrg_appBrowser, nullPrin_appBrowser);
checkCrossOrigin(exampleOrg_appBrowser, exampleCom_appBrowser);
}
+5
View File
@@ -0,0 +1,5 @@
[DEFAULT]
head =
tail =
[test_origin.js]
+4
View File
@@ -316,6 +316,10 @@ DOMInterfaces = {
'nativeType': 'nsDOMCSSDeclaration'
},
'CSSLexer': {
'wrapperCache': False
},
'CSSPrimitiveValue': {
'nativeType': 'nsROCSSPrimitiveValue',
},
+6
View File
@@ -78,6 +78,12 @@ public:
return mInternalResponse->Headers();
}
const nsCString&
GetSecurityInfo() const
{
return mInternalResponse->GetSecurityInfo();
}
Headers* Headers_();
void
@@ -20,7 +20,7 @@ interface nsIURI;
typedef unsigned short CSPDirective;
[scriptable, uuid(1bf8394b-2bdd-47ab-9b75-b75a5a37b3e1)]
[scriptable, uuid(459fe61a-203e-4460-9ced-352a9bd3aa71)]
interface nsIContentSecurityPolicy : nsISerializable
{
/**
@@ -283,4 +283,9 @@ interface nsIContentSecurityPolicy : nsISerializable
#define CSP_VIOLATION_TOPIC "csp-on-violate-policy"
%}
/**
* Returns the CSP in JSON notation.
*/
AString toJSON();
};
+21
View File
@@ -37,6 +37,7 @@
#include "nsString.h"
#include "mozilla/Logging.h"
#include "mozilla/dom/CSPReportBinding.h"
#include "mozilla/dom/CSPDictionariesBinding.h"
#include "mozilla/net/ReferrerPolicy.h"
#include "nsINetworkInterceptController.h"
@@ -1212,6 +1213,26 @@ nsCSPContext::Permits(nsIURI* aURI,
return NS_OK;
}
NS_IMETHODIMP
nsCSPContext::ToJSON(nsAString& outCSPinJSON)
{
outCSPinJSON.Truncate();
dom::CSPPolicies jsonPolicies;
jsonPolicies.mCsp_policies.Construct();
for (uint32_t p = 0; p < mPolicies.Length(); p++) {
dom::CSP jsonCSP;
mPolicies[p]->toDomCSPStruct(jsonCSP);
jsonPolicies.mCsp_policies.Value().AppendElement(jsonCSP);
}
// convert the gathered information to JSON
if (!jsonPolicies.ToJSON(outCSPinJSON)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
/* ========== CSPViolationReportListener implementation ========== */
NS_IMPL_ISUPPORTS(CSPViolationReportListener, nsIStreamListener, nsIRequestObserver, nsISupports);
+104
View File
@@ -804,6 +804,93 @@ nsCSPDirective::toString(nsAString& outStr) const
}
}
void
nsCSPDirective::toDomCSPStruct(mozilla::dom::CSP& outCSP) const
{
mozilla::dom::Sequence<nsString> srcs;
nsString src;
for (uint32_t i = 0; i < mSrcs.Length(); i++) {
src.Truncate();
mSrcs[i]->toString(src);
srcs.AppendElement(src);
}
switch(mDirective) {
case nsIContentSecurityPolicy::DEFAULT_SRC_DIRECTIVE:
outCSP.mDefault_src.Construct();
outCSP.mDefault_src.Value() = srcs;
return;
case nsIContentSecurityPolicy::SCRIPT_SRC_DIRECTIVE:
outCSP.mScript_src.Construct();
outCSP.mScript_src.Value() = srcs;
return;
case nsIContentSecurityPolicy::OBJECT_SRC_DIRECTIVE:
outCSP.mObject_src.Construct();
outCSP.mObject_src.Value() = srcs;
return;
case nsIContentSecurityPolicy::STYLE_SRC_DIRECTIVE:
outCSP.mStyle_src.Construct();
outCSP.mStyle_src.Value() = srcs;
return;
case nsIContentSecurityPolicy::IMG_SRC_DIRECTIVE:
outCSP.mImg_src.Construct();
outCSP.mImg_src.Value() = srcs;
return;
case nsIContentSecurityPolicy::MEDIA_SRC_DIRECTIVE:
outCSP.mMedia_src.Construct();
outCSP.mMedia_src.Value() = srcs;
return;
case nsIContentSecurityPolicy::FRAME_SRC_DIRECTIVE:
outCSP.mFrame_src.Construct();
outCSP.mFrame_src.Value() = srcs;
return;
case nsIContentSecurityPolicy::FONT_SRC_DIRECTIVE:
outCSP.mFont_src.Construct();
outCSP.mFont_src.Value() = srcs;
return;
case nsIContentSecurityPolicy::CONNECT_SRC_DIRECTIVE:
outCSP.mConnect_src.Construct();
outCSP.mConnect_src.Value() = srcs;
return;
case nsIContentSecurityPolicy::REPORT_URI_DIRECTIVE:
outCSP.mReport_uri.Construct();
outCSP.mReport_uri.Value() = srcs;
return;
case nsIContentSecurityPolicy::FRAME_ANCESTORS_DIRECTIVE:
outCSP.mFrame_ancestors.Construct();
outCSP.mFrame_ancestors.Value() = srcs;
return;
// not supporting REFLECTED_XSS_DIRECTIVE
case nsIContentSecurityPolicy::BASE_URI_DIRECTIVE:
outCSP.mBase_uri.Construct();
outCSP.mBase_uri.Value() = srcs;
return;
case nsIContentSecurityPolicy::FORM_ACTION_DIRECTIVE:
outCSP.mForm_action.Construct();
outCSP.mForm_action.Value() = srcs;
return;
// REFERRER_DIRECTIVE is handled in nsCSPPolicy::toDomCSPStruct()
default:
NS_ASSERTION(false, "cannot find directive to convert CSP to JSON");
}
}
bool
nsCSPDirective::restrictsContentType(nsContentPolicyType aContentType) const
{
@@ -973,6 +1060,23 @@ nsCSPPolicy::toString(nsAString& outStr) const
}
}
void
nsCSPPolicy::toDomCSPStruct(mozilla::dom::CSP& outCSP) const
{
outCSP.mReport_only = mReportOnly;
for (uint32_t i = 0; i < mDirectives.Length(); ++i) {
if (mDirectives[i]->equals(nsIContentSecurityPolicy::REFERRER_DIRECTIVE)) {
mozilla::dom::Sequence<nsString> srcs;
srcs.AppendElement(mReferrerPolicy);
outCSP.mReferrer.Construct();
outCSP.mReferrer.Value() = srcs;
} else {
mDirectives[i]->toDomCSPStruct(outCSP);
}
}
}
bool
nsCSPPolicy::hasDirective(CSPDirective aDir) const
{
+10 -1
View File
@@ -16,6 +16,12 @@
#include "nsUnicharUtils.h"
#include "mozilla/Logging.h"
namespace mozilla {
namespace dom {
struct CSP;
}
}
/* =============== Logging =================== */
void CSP_LogLocalizedStr(const char16_t* aName,
@@ -58,7 +64,8 @@ void CSP_LogMessage(const nsAString& aMessage,
// these strings map to the CSPDirectives in nsIContentSecurityPolicy
// NOTE: When implementing a new directive, you will need to add it here but also
// add a corresponding entry to the constants in nsIContentSecurityPolicy.idl
// add a corresponding entry to the constants in nsIContentSecurityPolicy.idl and
// also create an entry for the new directive in nsCSPDirective::toDomCSPStruct().
static const char* CSPStrDirectives[] = {
"-error-", // NO_DIRECTIVE
"default-src", // DEFAULT_SRC_DIRECTIVE
@@ -282,6 +289,7 @@ class nsCSPDirective {
bool permits(nsIURI* aUri) const;
bool allows(enum CSPKeyword aKeyword, const nsAString& aHashOrNonce) const;
void toString(nsAString& outStr) const;
void toDomCSPStruct(mozilla::dom::CSP& outCSP) const;
inline void addSrcs(const nsTArray<nsCSPBaseSrc*>& aSrcs)
{ mSrcs = aSrcs; }
@@ -323,6 +331,7 @@ class nsCSPPolicy {
bool allows(nsContentPolicyType aContentType,
enum CSPKeyword aKeyword) const;
void toString(nsAString& outStr) const;
void toDomCSPStruct(mozilla::dom::CSP& outCSP) const;
inline void addDirective(nsCSPDirective* aDir)
{ mDirectives.AppendElement(aDir); }
+31
View File
@@ -0,0 +1,31 @@
/* 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/. */
/**
* Dictionary used to display CSP info.
*/
dictionary CSP {
boolean report-only = false;
sequence<DOMString> default-src;
sequence<DOMString> script-src;
sequence<DOMString> object-src;
sequence<DOMString> style-src;
sequence<DOMString> img-src;
sequence<DOMString> media-src;
sequence<DOMString> frame-src;
sequence<DOMString> font-src;
sequence<DOMString> connect-src;
sequence<DOMString> report-uri;
sequence<DOMString> frame-ancestors;
// sequence<DOMString> reflected-xss; // not suppored in Firefox
sequence<DOMString> base-uri;
sequence<DOMString> form-action;
sequence<DOMString> referrer;
};
dictionary CSPPolicies {
sequence<CSP> csp-policies;
};
+132
View File
@@ -0,0 +1,132 @@
/* -*- Mode: IDL; 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/. */
// The possible values for CSSToken.tokenType.
enum CSSTokenType {
// Whitespace.
"whitespace",
// A CSS comment.
"comment",
// An identifier. |text| holds the identifier text.
"ident",
// A function token. |text| holds the function name. Note that the
// function token includes (i.e., consumes) the "(" -- but this is
// not included in |text|.
"function",
// "@word". |text| holds "word", without the "@".
"at",
// "#word". |text| holds "word", without the "#".
"id",
// "#word". ID is used when "word" would have been a valid IDENT
// token without the "#"; otherwise, HASH is used.
"hash",
// A number.
"number",
// A dimensioned number.
"dimension",
// A percentage.
"percentage",
// A string.
"string",
// A "bad string". This can only be returned when a string is
// unterminated at EOF. (However, currently the lexer returns
// ordinary STRING tokens in this situation.)
"bad_string",
// A URL. |text| holds the URL.
"url",
// A "bad URL". This is a URL that is unterminated at EOF. |text|
// holds the URL.
"bad_url",
// A "symbol" is any one-character symbol. This corresponds to the
// DELIM token in the CSS specification.
"symbol",
// The "~=" token.
"includes",
// The "|=" token.
"dashmatch",
// The "^=" token.
"beginsmatch",
// The "$=" token.
"endsmatch",
// The "*=" token.
"containsmatch",
// A unicode-range token. This is currently not fully represented
// by CSSToken.
"urange",
// HTML comment delimiters, either "<!--" or "-->". Note that each
// is emitted as a separate token, and the intervening text is lexed
// as normal; whereas ordinary CSS comments are lexed as a unit.
"htmlcomment"
};
dictionary CSSToken {
// The token type.
CSSTokenType tokenType = "whitespace";
// Offset of the first character of the token.
unsigned long startOffset = 0;
// Offset of the character after the final character of the token.
// This is chosen so that the offsets can be passed to |substring|
// to yield the exact contents of the token.
unsigned long endOffset = 0;
// If the token is a number, percentage, or dimension, this holds
// the value. This is not present for other token types.
double number;
// If the token is a number, percentage, or dimension, this is true
// iff the number had an explicit sign. This is not present for
// other token types.
boolean hasSign;
// If the token is a number, percentage, or dimension, this is true
// iff the number was specified as an integer. This is not present
// for other token types.
boolean isInteger;
// Text associated with the token. This is not present for all
// token types. In particular it is:
//
// Token type Meaning
// ===============================
// ident The identifier.
// function The function name. Note that the "(" is part
// of the token but is not present in |text|.
// at The word.
// id The word.
// hash The word.
// dimension The dimension.
// string The string contents after escape processing.
// bad_string Ditto.
// url The URL after escape processing.
// bad_url Ditto.
// symbol The symbol text.
DOMString text;
};
/**
* CSSLexer is an interface to the CSS lexer. It tokenizes an
* input stream and returns CSS tokens.
*
* @see inIDOMUtils.getCSSLexer to create an instance of the lexer.
*/
[ChromeOnly]
interface CSSLexer
{
/**
* The line number of the most recently returned token. Line
* numbers are 0-based.
*/
readonly attribute unsigned long lineNumber;
/**
* The column number of the most recently returned token. Column
* numbers are 0-based.
*/
readonly attribute unsigned long columnNumber;
/**
* Return the next token, or null at EOF.
*/
CSSToken? nextToken();
};
+2
View File
@@ -88,8 +88,10 @@ WEBIDL_FILES = [
'ConvolverNode.webidl',
'Coordinates.webidl',
'Crypto.webidl',
'CSPDictionaries.webidl',
'CSPReport.webidl',
'CSS.webidl',
'CSSLexer.webidl',
'CSSPrimitiveValue.webidl',
'CSSRuleList.webidl',
'CSSStyleDeclaration.webidl',
+12
View File
@@ -32,6 +32,7 @@
#include "nsIXPConnect.h"
#include "nsPerformance.h"
#include "nsPIDOMWindow.h"
#include "nsSerializationHelper.h"
#include <algorithm>
#include "jsfriendapi.h"
@@ -4037,6 +4038,17 @@ WorkerPrivateParent<Derived>::SetPrincipal(nsIPrincipal* aPrincipal,
PrincipalToPrincipalInfo(aPrincipal, mLoadInfo.mPrincipalInfo)));
}
template <class Derived>
void
WorkerPrivateParent<Derived>::SetSecurityInfo(nsISerializable* aSerializable)
{
MOZ_ASSERT(IsServiceWorker());
AssertIsOnMainThread();
nsAutoCString securityInfo;
NS_SerializeToString(aSerializable, securityInfo);
SetSecurityInfo(securityInfo);
}
template <class Derived>
JSContext*
WorkerPrivateParent<Derived>::ParentJSContext() const
+20
View File
@@ -37,6 +37,7 @@ class nsIDocument;
class nsIEventTarget;
class nsIPrincipal;
class nsIScriptContext;
class nsISerializable;
class nsIThread;
class nsIThreadInternal;
class nsITimer;
@@ -488,6 +489,25 @@ public:
return mLoadInfo.mServiceWorkerCacheName;
}
const nsCString&
GetSecurityInfo() const
{
MOZ_ASSERT(IsServiceWorker());
return mLoadInfo.mSecurityInfo;
}
void
SetSecurityInfo(const nsCString& aSecurityInfo)
{
MOZ_ASSERT(IsServiceWorker());
AssertIsOnMainThread();
MOZ_ASSERT(mLoadInfo.mSecurityInfo.IsEmpty());
mLoadInfo.mSecurityInfo = aSecurityInfo;
}
void
SetSecurityInfo(nsISerializable* aSerializable);
// This is used to handle importScripts(). When the worker is first loaded
// and executed, it happens in a sync loop. At this point it sets
// mLoadingWorkerScript to true. importScripts() calls that occur during the
+2
View File
@@ -244,6 +244,8 @@ struct WorkerLoadInfo
nsString mServiceWorkerCacheName;
nsCString mSecurityInfo;
uint64_t mWindowID;
bool mFromWindow;
+14
View File
@@ -37,6 +37,7 @@
#include "nsRuleWalker.h"
#include "nsRuleProcessorData.h"
#include "nsCSSRuleProcessor.h"
#include "mozilla/dom/CSSLexer.h"
#include "mozilla/dom/InspectorUtilsBinding.h"
#include "mozilla/dom/ToJSValue.h"
#include "nsCSSParser.h"
@@ -289,6 +290,19 @@ inDOMUtils::GetRuleColumn(nsIDOMCSSRule* aRule, uint32_t* _retval)
return NS_OK;
}
NS_IMETHODIMP
inDOMUtils::GetCSSLexer(const nsAString& aText, JSContext* aCx,
JS::MutableHandleValue aResult)
{
MOZ_ASSERT(JS::CurrentGlobalOrNull(aCx));
JS::Rooted<JSObject*> scope(aCx, JS::CurrentGlobalOrNull(aCx));
nsAutoPtr<CSSLexer> lexer(new CSSLexer(aText));
if (!WrapNewBindingNonWrapperCachedObject(aCx, scope, lexer, aResult)) {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
inDOMUtils::GetSelectorCount(nsIDOMCSSStyleRule* aRule, uint32_t *aCount)
{
+4 -1
View File
@@ -17,7 +17,7 @@ interface nsIDOMFontFaceList;
interface nsIDOMRange;
interface nsIDOMCSSStyleSheet;
[scriptable, uuid(1f5b7f08-fa80-49e9-b881-888f081240da)]
[scriptable, uuid(60b4cbf7-2a08-4419-8937-6ef495417824)]
interface inIDOMUtils : nsISupports
{
// CSS utilities
@@ -28,6 +28,9 @@ interface inIDOMUtils : nsISupports
unsigned long getRuleLine(in nsIDOMCSSRule aRule);
unsigned long getRuleColumn(in nsIDOMCSSRule aRule);
[implicit_jscontext]
jsval getCSSLexer(in DOMString aText);
// Utilities for working with selectors. We don't have a JS OM representation
// of a single selector or a selector list yet, but given a rule we can index
// into the selector list.
+142
View File
@@ -0,0 +1,142 @@
/* -*- 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/. */
#include "mozilla/dom/CSSLexer.h"
#include "js/Value.h"
#include "mozilla/dom/CSSLexerBinding.h"
#include "mozilla/dom/ToJSValue.h"
namespace mozilla {
namespace dom {
// Ensure that constants are consistent.
#define CHECK(X, Y) \
static_assert(static_cast<int>(X) == static_cast<int>(Y), \
"nsCSSToken and CSSTokenType should have identical values")
CHECK(eCSSToken_Whitespace, CSSTokenType::Whitespace);
CHECK(eCSSToken_Comment, CSSTokenType::Comment);
CHECK(eCSSToken_Ident, CSSTokenType::Ident);
CHECK(eCSSToken_Function, CSSTokenType::Function);
CHECK(eCSSToken_AtKeyword, CSSTokenType::At);
CHECK(eCSSToken_ID, CSSTokenType::Id);
CHECK(eCSSToken_Hash, CSSTokenType::Hash);
CHECK(eCSSToken_Number, CSSTokenType::Number);
CHECK(eCSSToken_Dimension, CSSTokenType::Dimension);
CHECK(eCSSToken_Percentage, CSSTokenType::Percentage);
CHECK(eCSSToken_String, CSSTokenType::String);
CHECK(eCSSToken_Bad_String, CSSTokenType::Bad_string);
CHECK(eCSSToken_URL, CSSTokenType::Url);
CHECK(eCSSToken_Bad_URL, CSSTokenType::Bad_url);
CHECK(eCSSToken_Symbol, CSSTokenType::Symbol);
CHECK(eCSSToken_Includes, CSSTokenType::Includes);
CHECK(eCSSToken_Dashmatch, CSSTokenType::Dashmatch);
CHECK(eCSSToken_Beginsmatch, CSSTokenType::Beginsmatch);
CHECK(eCSSToken_Endsmatch, CSSTokenType::Endsmatch);
CHECK(eCSSToken_Containsmatch, CSSTokenType::Containsmatch);
CHECK(eCSSToken_URange, CSSTokenType::Urange);
CHECK(eCSSToken_HTMLComment, CSSTokenType::Htmlcomment);
#undef CHECK
CSSLexer::CSSLexer(const nsAString& aText)
: mInput(aText)
, mScanner(mInput, 1)
{
}
CSSLexer::~CSSLexer()
{
}
bool
CSSLexer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector)
{
return CSSLexerBinding::Wrap(aCx, this, aGivenProto, aReflector);
}
uint32_t
CSSLexer::LineNumber()
{
// The scanner uses 1-based line numbers, but our callers expect
// 0-based.
return mScanner.GetLineNumber() - 1;
}
uint32_t
CSSLexer::ColumnNumber()
{
return mScanner.GetColumnNumber();
}
void
CSSLexer::NextToken(Nullable<CSSToken>& aResult)
{
nsCSSToken token;
if (!mScanner.Next(token, eCSSScannerExclude_None)) {
return;
}
CSSToken& resultToken(aResult.SetValue());
resultToken.mTokenType = static_cast<CSSTokenType>(token.mType);
resultToken.mStartOffset = mScanner.GetTokenOffset();
resultToken.mEndOffset = mScanner.GetTokenEndOffset();
switch (token.mType) {
case eCSSToken_Whitespace:
break;
case eCSSToken_Ident:
case eCSSToken_Function:
case eCSSToken_AtKeyword:
case eCSSToken_ID:
case eCSSToken_Hash:
resultToken.mText.Construct(token.mIdent);
break;
case eCSSToken_Dimension:
resultToken.mText.Construct(token.mIdent);
/* FALLTHROUGH */
case eCSSToken_Number:
case eCSSToken_Percentage:
resultToken.mNumber.Construct(token.mNumber);
resultToken.mHasSign.Construct(token.mHasSign);
resultToken.mIsInteger.Construct(token.mIntegerValid);
break;
case eCSSToken_String:
case eCSSToken_Bad_String:
case eCSSToken_URL:
case eCSSToken_Bad_URL:
resultToken.mText.Construct(token.mIdent);
/* Don't bother emitting the delimiter, as it is readily extracted
from the source string when needed. */
break;
case eCSSToken_Symbol:
resultToken.mText.Construct(nsString(&token.mSymbol, 1));
break;
case eCSSToken_Includes:
case eCSSToken_Dashmatch:
case eCSSToken_Beginsmatch:
case eCSSToken_Endsmatch:
case eCSSToken_Containsmatch:
case eCSSToken_URange:
break;
case eCSSToken_Comment:
case eCSSToken_HTMLComment:
/* The comment text is easily extracted from the source string,
and is rarely useful. */
break;
}
}
} // namespace dom
} // namespace mozilla
+37
View File
@@ -0,0 +1,37 @@
/* -*- 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 CSSLexer_h___
#define CSSLexer_h___
#include "mozilla/UniquePtr.h"
#include "nsCSSScanner.h"
#include "mozilla/dom/CSSLexerBinding.h"
namespace mozilla {
namespace dom {
class CSSLexer : public NonRefcountedDOMObject
{
public:
explicit CSSLexer(const nsAString&);
~CSSLexer();
bool WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto,
JS::MutableHandle<JSObject*> aReflector);
uint32_t LineNumber();
uint32_t ColumnNumber();
void NextToken(Nullable<CSSToken>& aResult);
private:
nsString mInput;
nsCSSScanner mScanner;
};
} // namespace dom
} // namespace mozilla
#endif /* CSSLexer_h___ */
+2
View File
@@ -91,6 +91,7 @@ EXPORTS.mozilla += [
EXPORTS.mozilla.dom += [
'CSS.h',
'CSSLexer.h',
'CSSRuleList.h',
'CSSValue.h',
'FontFace.h',
@@ -115,6 +116,7 @@ UNIFIED_SOURCES += [
'AnimationCommon.cpp',
'CounterStyleManager.cpp',
'CSS.cpp',
'CSSLexer.cpp',
'CSSRuleList.cpp',
'CSSStyleSheet.cpp',
'CSSVariableDeclarations.cpp',
+3 -1
View File
@@ -2761,7 +2761,9 @@ CSSParserImpl::GetToken(bool aSkipWS)
return true;
}
}
return mScanner->Next(mToken, aSkipWS);
return mScanner->Next(mToken, aSkipWS ?
eCSSScannerExclude_WhitespaceAndComments :
eCSSScannerExclude_Comments);
}
void
+14 -8
View File
@@ -552,7 +552,8 @@ nsCSSScanner::SkipComment()
for (;;) {
int32_t ch = Peek();
if (ch < 0) {
mReporter->ReportUnexpectedEOF("PECommentEOF");
if (mReporter)
mReporter->ReportUnexpectedEOF("PECommentEOF");
SetEOFCharacters(eEOFCharacters_Asterisk | eEOFCharacters_Slash);
return;
}
@@ -560,7 +561,8 @@ nsCSSScanner::SkipComment()
Advance();
ch = Peek();
if (ch < 0) {
mReporter->ReportUnexpectedEOF("PECommentEOF");
if (mReporter)
mReporter->ReportUnexpectedEOF("PECommentEOF");
SetEOFCharacters(eEOFCharacters_Slash);
return;
}
@@ -985,7 +987,8 @@ nsCSSScanner::ScanString(nsCSSToken& aToken)
mSeenBadToken = true;
aToken.mType = eCSSToken_Bad_String;
mReporter->ReportUnexpected("SEUnterminatedString", aToken);
if (mReporter)
mReporter->ReportUnexpected("SEUnterminatedString", aToken);
break;
}
return true;
@@ -1192,15 +1195,15 @@ nsCSSScanner::NextURL(nsCSSToken& aToken)
/**
* Primary scanner entry point. Consume one token and fill in
* |aToken| accordingly. Will skip over any number of comments first,
* and will also skip over rather than return whitespace tokens if
* |aSkipWS| is true.
* and will also skip over rather than return whitespace and comment
* tokens, depending on the value of |aSkip|.
*
* Returns true if it successfully consumed a token, false if EOF has
* been reached. Will always advance the current read position by at
* least one character unless called when already at EOF.
*/
bool
nsCSSScanner::Next(nsCSSToken& aToken, bool aSkipWS)
nsCSSScanner::Next(nsCSSToken& aToken, nsCSSScannerExclude aSkip)
{
int32_t ch;
@@ -1218,15 +1221,18 @@ nsCSSScanner::Next(nsCSSToken& aToken, bool aSkipWS)
ch = Peek();
if (IsWhitespace(ch)) {
SkipWhitespace();
if (!aSkipWS) {
if (aSkip != eCSSScannerExclude_WhitespaceAndComments) {
aToken.mType = eCSSToken_Whitespace;
return true;
}
continue; // start again at the beginning
}
if (ch == '/' && !IsSVGMode() && Peek(1) == '*') {
// FIXME: Editor wants comments to be preserved (bug 60290).
SkipComment();
if (aSkip == eCSSScannerExclude_None) {
aToken.mType = eCSSToken_Comment;
return true;
}
continue; // start again at the beginning
}
break;
+23 -4
View File
@@ -27,6 +27,8 @@ enum nsCSSTokenType {
// comments do *not* count as white space; comments separate tokens
// but are not themselves tokens.
eCSSToken_Whitespace, //
// A comment.
eCSSToken_Comment, // /*...*/
// Identifier-like tokens. mIdent is the text of the identifier.
// The difference between ID and Hash is: if the text after the #
@@ -182,13 +184,24 @@ private:
bool mInitialized;
};
enum nsCSSScannerExclude {
// Return all tokens, including whitespace and comments.
eCSSScannerExclude_None,
// Include whitespace but exclude comments.
eCSSScannerExclude_Comments,
// Exclude whitespace and comments.
eCSSScannerExclude_WhitespaceAndComments
};
// nsCSSScanner tokenizes an input stream using the CSS2.1 forward
// compatible tokenization rules. Used internally by nsCSSParser;
// not available for use by other code.
class nsCSSScanner {
public:
// |aLineNumber == 1| is the beginning of a file, use |aLineNumber == 0|
// when the line number is unknown.
// when the line number is unknown. The scanner does not take
// ownership of |aBuffer|, so the caller must be sure to keep it
// alive for the lifetime of the scanner.
nsCSSScanner(const nsAString& aBuffer, uint32_t aLineNumber);
~nsCSSScanner();
@@ -220,14 +233,20 @@ class nsCSSScanner {
uint32_t GetColumnNumber() const
{ return mTokenOffset - mTokenLineOffset; }
uint32_t GetTokenOffset() const
{ return mTokenOffset; }
uint32_t GetTokenEndOffset() const
{ return mOffset; }
// Get the text of the line containing the first character of
// the most recently processed token.
nsDependentSubstring GetCurrentLine() const;
// Get the next token. Return false on EOF. aTokenResult is filled
// in with the data for the token. If aSkipWS is true, skip over
// eCSSToken_Whitespace tokens rather than returning them.
bool Next(nsCSSToken& aTokenResult, bool aSkipWS);
// in with the data for the token. aSkip controls whether
// whitespace and/or comment tokens are ever returned.
bool Next(nsCSSToken& aTokenResult, nsCSSScannerExclude aSkip);
// Get the body of an URL token (everything after the 'url(').
// This is exposed for use by nsCSSParser::ParseMozDocumentRule,
+1
View File
@@ -15,6 +15,7 @@ MOCHITEST_MANIFESTS += [
'css-visited/mochitest.ini',
'mochitest.ini',
]
XPCSHELL_TESTS_MANIFESTS += ['xpcshell.ini']
BROWSER_CHROME_MANIFESTS += ['browser.ini']
MOCHITEST_CHROME_MANIFESTS += ['chrome/chrome.ini']
+125
View File
@@ -0,0 +1,125 @@
/* 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/.
*/
function test_lexer(domutils, cssText, tokenTypes) {
let lexer = domutils.getCSSLexer(cssText);
let reconstructed = '';
let lastTokenEnd = 0;
let i = 0;
while (true) {
let token = lexer.nextToken();
if (!token) {
break;
}
let combined = token.tokenType;
if (token.text)
combined += ":" + token.text;
equal(combined, tokenTypes[i]);
ok(token.endOffset > token.startOffset);
equal(token.startOffset, lastTokenEnd);
lastTokenEnd = token.endOffset;
reconstructed += cssText.substring(token.startOffset, token.endOffset);
++i;
}
// Ensure that we saw the correct number of tokens.
equal(i, tokenTypes.length);
// Ensure that the reported offsets cover all the text.
equal(reconstructed, cssText);
}
let LEX_TESTS = [
["simple", ["ident:simple"]],
["simple: { hi; }",
["ident:simple", "symbol::",
"whitespace", "symbol:{",
"whitespace", "ident:hi",
"symbol:;", "whitespace",
"symbol:}"]],
["/* whatever */", ["comment"]],
["'string'", ["string:string"]],
['"string"', ["string:string"]],
["rgb(1,2,3)", ["function:rgb", "number",
"symbol:,", "number",
"symbol:,", "number",
"symbol:)"]],
["@media", ["at:media"]],
["#hibob", ["id:hibob"]],
["#123", ["hash:123"]],
["23px", ["dimension:px"]],
["23%", ["percentage"]],
["url(http://example.com)", ["url:http://example.com"]],
["url('http://example.com')", ["url:http://example.com"]],
["url( 'http://example.com' )",
["url:http://example.com"]],
// In CSS Level 3, this is an ordinary URL, not a BAD_URL.
["url(http://example.com", ["url:http://example.com"]],
// See bug 1153981 to understand why this gets a SYMBOL token.
["url(http://example.com @", ["bad_url:http://example.com", "symbol:@"]],
["quo\\ting", ["ident:quoting"]],
["'bad string\n", ["bad_string:bad string", "whitespace"]],
["~=", ["includes"]],
["|=", ["dashmatch"]],
["^=", ["beginsmatch"]],
["$=", ["endsmatch"]],
["*=", ["containsmatch"]],
// URANGE may be on the way out, and it isn't used by devutils, so
// let's skip it.
["<!-- html comment -->", ["htmlcomment", "whitespace", "ident:html",
"whitespace", "ident:comment", "whitespace",
"htmlcomment"]],
// earlier versions of CSS had "bad comment" tokens, but in level 3,
// unterminated comments are just comments.
["/* bad comment", ["comment"]]
];
function test_lexer_linecol(domutils, cssText, locations) {
let lexer = domutils.getCSSLexer(cssText);
let i = 0;
while (true) {
let token = lexer.nextToken();
let startLine = lexer.lineNumber;
let startColumn = lexer.columnNumber;
// We do this in a bit of a funny way so that we can also test the
// location of the EOF.
let combined = ":" + startLine + ":" + startColumn;
if (token)
combined = token.tokenType + combined;
equal(combined, locations[i]);
++i;
if (!token) {
break;
}
}
// Ensure that we saw the correct number of tokens.
equal(i, locations.length);
}
let LINECOL_TESTS = [
["simple", ["ident:0:0", ":0:6"]],
["\n stuff", ["whitespace:0:0", "ident:1:4", ":1:9"]],
['"string with \\\nnewline" \r\n', ["string:0:0", "whitespace:1:8",
":2:0"]]
];
function run_test()
{
let domutils = Components.classes["@mozilla.org/inspector/dom-utils;1"]
.getService(Components.interfaces.inIDOMUtils);
let text, result;
for ([text, result] of LEX_TESTS) {
test_lexer(domutils, text, result);
}
for ([text, result] of LINECOL_TESTS) {
test_lexer_linecol(domutils, text, result);
}
}
+5
View File
@@ -0,0 +1,5 @@
[DEFAULT]
head =
tail =
[test_csslexer.js]
+25
View File
@@ -10,6 +10,7 @@ this.EXPORTED_SYMBOLS = [ "BrowserUtils" ];
const {interfaces: Ci, utils: Cu, classes: Cc} = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.importGlobalProperties(['URL']);
this.BrowserUtils = {
@@ -103,6 +104,30 @@ this.BrowserUtils = {
return [elt, window];
},
// Creates a codebase principal from a canonical origin string. This is
// the inverse operation of .origin on a codebase principal.
principalFromOrigin: function(aOriginString) {
if (aOriginString.startsWith('[')) {
throw new Error("principalFromOrigin does not support System and Expanded principals");
}
if (aOriginString.startsWith("moz-nullprincipal:")) {
throw new Error("principalFromOrigin does not support nsNullPrincipal");
}
var parts = aOriginString.split('!');
if (parts.length > 2) {
throw new Error("bad origin string: " + aOriginString);
}
var uri = Services.io.newURI(parts[0], null, null);
var attrs = {};
// Parse the parameters string into a dictionary.
(parts[1] || "").split("&").map((x) => x.split('=')).forEach((x) => attrs[x[0]] = x[1]);
return Services.scriptSecurityManager.createCodebasePrincipal(uri, attrs);
},
/**
* For a given DOM element, returns its position in "screen"
* coordinates. In a content process, the coordinates returned will