mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:18:48 +00:00
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:
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -6,6 +6,8 @@
|
||||
|
||||
TEST_DIRS += ['tests/mochitest']
|
||||
|
||||
XPCSHELL_TESTS_MANIFESTS += ['tests/unit/xpcshell.ini']
|
||||
|
||||
XPIDL_SOURCES += [
|
||||
'nsIDomainPolicy.idl',
|
||||
'nsIPrincipal.idl',
|
||||
|
||||
+32
-1
@@ -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
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
head =
|
||||
tail =
|
||||
|
||||
[test_origin.js]
|
||||
@@ -316,6 +316,10 @@ DOMInterfaces = {
|
||||
'nativeType': 'nsDOMCSSDeclaration'
|
||||
},
|
||||
|
||||
'CSSLexer': {
|
||||
'wrapperCache': False
|
||||
},
|
||||
|
||||
'CSSPrimitiveValue': {
|
||||
'nativeType': 'nsROCSSPrimitiveValue',
|
||||
},
|
||||
|
||||
@@ -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();
|
||||
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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); }
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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();
|
||||
};
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -244,6 +244,8 @@ struct WorkerLoadInfo
|
||||
|
||||
nsString mServiceWorkerCacheName;
|
||||
|
||||
nsCString mSecurityInfo;
|
||||
|
||||
uint64_t mWindowID;
|
||||
|
||||
bool mFromWindow;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
@@ -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___ */
|
||||
@@ -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',
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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']
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
[DEFAULT]
|
||||
head =
|
||||
tail =
|
||||
|
||||
[test_csslexer.js]
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user