зеркало из https://github.com/mozilla/gecko-dev.git
Merge mozilla-inbound to mozilla-central. a=merge
This commit is contained in:
Коммит
ad04ab5446
|
@ -339,7 +339,7 @@ function openLinkIn(url, where, params) {
|
|||
userContextId: aUserContextId,
|
||||
privateBrowsingId: aIsPrivate || (w && PrivateBrowsingUtils.isWindowPrivate(w)),
|
||||
};
|
||||
return Services.scriptSecurityManager.createCodebasePrincipal(principal.URI, attrs);
|
||||
return Services.scriptSecurityManager.principalWithOA(principal, attrs);
|
||||
}
|
||||
return principal;
|
||||
}
|
||||
|
|
|
@ -23,6 +23,7 @@
|
|||
#include "mozilla/dom/BlobURLProtocolHandler.h"
|
||||
#include "mozilla/dom/ChromeUtils.h"
|
||||
#include "mozilla/dom/CSPDictionariesBinding.h"
|
||||
#include "mozilla/dom/nsCSPContext.h"
|
||||
#include "mozilla/dom/ToJSValue.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
@ -494,6 +495,34 @@ void BasePrincipal::FinishInit(const nsACString& aOriginNoSuffix,
|
|||
mOriginNoSuffix = NS_Atomize(aOriginNoSuffix);
|
||||
}
|
||||
|
||||
void BasePrincipal::FinishInit(BasePrincipal* aOther,
|
||||
const OriginAttributes& aOriginAttributes) {
|
||||
mInitialized = true;
|
||||
mOriginAttributes = aOriginAttributes;
|
||||
|
||||
// First compute the origin suffix since it's infallible.
|
||||
nsAutoCString originSuffix;
|
||||
mOriginAttributes.CreateSuffix(originSuffix);
|
||||
mOriginSuffix = NS_Atomize(originSuffix);
|
||||
|
||||
mOriginNoSuffix = aOther->mOriginNoSuffix;
|
||||
mHasExplicitDomain = aOther->mHasExplicitDomain;
|
||||
|
||||
if (aOther->mPreloadCSP) {
|
||||
mPreloadCSP = do_CreateInstance("@mozilla.org/cspcontext;1");
|
||||
nsCSPContext* preloadCSP = static_cast<nsCSPContext*>(mPreloadCSP.get());
|
||||
preloadCSP->InitFromOther(
|
||||
static_cast<nsCSPContext*>(aOther->mPreloadCSP.get()), nullptr, this);
|
||||
}
|
||||
|
||||
if (aOther->mCSP) {
|
||||
mCSP = do_CreateInstance("@mozilla.org/cspcontext;1");
|
||||
nsCSPContext* csp = static_cast<nsCSPContext*>(mCSP.get());
|
||||
csp->InitFromOther(static_cast<nsCSPContext*>(aOther->mCSP.get()), nullptr,
|
||||
this);
|
||||
}
|
||||
}
|
||||
|
||||
bool SiteIdentifier::Equals(const SiteIdentifier& aOther) const {
|
||||
MOZ_ASSERT(IsInitialized());
|
||||
MOZ_ASSERT(aOther.IsInitialized());
|
||||
|
|
|
@ -241,11 +241,13 @@ class BasePrincipal : public nsJSPrincipals {
|
|||
|
||||
void SetHasExplicitDomain() { mHasExplicitDomain = true; }
|
||||
|
||||
// This function should be called as the last step of the initialization of
|
||||
// the principal objects. It's typically called as the last step from the
|
||||
// Init() method of the child classes.
|
||||
// Either of these functions should be called as the last step of the
|
||||
// initialization of the principal objects. It's typically called as the
|
||||
// last step from the Init() method of the child classes.
|
||||
void FinishInit(const nsACString& aOriginNoSuffix,
|
||||
const OriginAttributes& aOriginAttributes);
|
||||
void FinishInit(BasePrincipal* aOther,
|
||||
const OriginAttributes& aOriginAttributes);
|
||||
|
||||
nsCOMPtr<nsIContentSecurityPolicy> mCSP;
|
||||
nsCOMPtr<nsIContentSecurityPolicy> mPreloadCSP;
|
||||
|
|
|
@ -81,6 +81,18 @@ nsresult ContentPrincipal::Init(nsIURI* aCodebase,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult ContentPrincipal::Init(ContentPrincipal* aOther,
|
||||
const OriginAttributes& aOriginAttributes) {
|
||||
NS_ENSURE_ARG(aOther);
|
||||
|
||||
mCodebase = aOther->mCodebase;
|
||||
FinishInit(aOther, aOriginAttributes);
|
||||
|
||||
mDomain = aOther->mDomain;
|
||||
mAddon = aOther->mAddon;
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult ContentPrincipal::GetScriptLocation(nsACString& aStr) {
|
||||
return mCodebase->GetSpec(aStr);
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ class ContentPrincipal final : public BasePrincipal {
|
|||
// Init() must be called before the principal is in a usable state.
|
||||
nsresult Init(nsIURI* aCodebase, const OriginAttributes& aOriginAttributes,
|
||||
const nsACString& aOriginNoSuffix);
|
||||
nsresult Init(ContentPrincipal* aOther,
|
||||
const OriginAttributes& aOriginAttributes);
|
||||
|
||||
virtual nsresult GetScriptLocation(nsACString& aStr) override;
|
||||
|
||||
|
|
|
@ -154,6 +154,14 @@ interface nsIScriptSecurityManager : nsISupports
|
|||
nsIPrincipal getDocShellCodebasePrincipal(in nsIURI uri,
|
||||
in nsIDocShell docShell);
|
||||
|
||||
/**
|
||||
* If this is a codebase principal, return a copy with different
|
||||
* origin attributes.
|
||||
*/
|
||||
[implicit_jscontext]
|
||||
nsIPrincipal principalWithOA(in nsIPrincipal principal,
|
||||
in jsval originAttributes);
|
||||
|
||||
/**
|
||||
* Returns a principal whose origin is composed of |uri| and |originAttributes|.
|
||||
* See nsIPrincipal.idl for a description of origin attributes, and
|
||||
|
|
|
@ -120,9 +120,10 @@ JS_PUBLIC_API void JSPrincipals::dump() {
|
|||
return ReadKnownPrincipalType(aCx, aReader, tag, aOutPrincipals);
|
||||
}
|
||||
|
||||
static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader,
|
||||
OriginAttributes& aAttrs, nsACString& aSpec,
|
||||
nsACString& aOriginNoSuffix) {
|
||||
static bool ReadPrincipalInfo(
|
||||
JSStructuredCloneReader* aReader, OriginAttributes& aAttrs,
|
||||
nsACString& aSpec, nsACString& aOriginNoSuffix,
|
||||
nsTArray<ContentSecurityPolicy>* aPolicies = nullptr) {
|
||||
uint32_t suffixLength, specLength;
|
||||
if (!JS_ReadUint32Pair(aReader, &suffixLength, &specLength)) {
|
||||
return false;
|
||||
|
@ -149,12 +150,14 @@ static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader,
|
|||
return false;
|
||||
}
|
||||
|
||||
uint32_t originNoSuffixLength, dummy;
|
||||
if (!JS_ReadUint32Pair(aReader, &originNoSuffixLength, &dummy)) {
|
||||
uint32_t originNoSuffixLength, policyCount;
|
||||
if (!JS_ReadUint32Pair(aReader, &originNoSuffixLength, &policyCount)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(dummy == 0);
|
||||
if (!aPolicies) {
|
||||
MOZ_ASSERT(policyCount == 0);
|
||||
}
|
||||
|
||||
if (!aOriginNoSuffix.SetLength(originNoSuffixLength, fallible)) {
|
||||
return false;
|
||||
|
@ -165,6 +168,29 @@ static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader,
|
|||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < policyCount; i++) {
|
||||
uint32_t policyLength, reportAndMeta;
|
||||
if (!JS_ReadUint32Pair(aReader, &policyLength, &reportAndMeta)) {
|
||||
return false;
|
||||
}
|
||||
bool reportOnly = reportAndMeta & 1;
|
||||
bool deliveredViaMetaTag = reportAndMeta & 2;
|
||||
|
||||
nsAutoCString policyStr;
|
||||
if (!policyStr.SetLength(policyLength, fallible)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!JS_ReadBytes(aReader, policyStr.BeginWriting(), policyLength)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (aPolicies) {
|
||||
aPolicies->AppendElement(ContentSecurityPolicy(
|
||||
NS_ConvertUTF8toUTF16(policyStr), reportOnly, deliveredViaMetaTag));
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -206,7 +232,8 @@ static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader, uint32_t aTag,
|
|||
OriginAttributes attrs;
|
||||
nsAutoCString spec;
|
||||
nsAutoCString originNoSuffix;
|
||||
if (!ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix)) {
|
||||
nsTArray<ContentSecurityPolicy> policies;
|
||||
if (!ReadPrincipalInfo(aReader, attrs, spec, originNoSuffix, &policies)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -218,7 +245,8 @@ static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader, uint32_t aTag,
|
|||
|
||||
MOZ_DIAGNOSTIC_ASSERT(!originNoSuffix.IsEmpty());
|
||||
|
||||
aInfo = ContentPrincipalInfo(attrs, originNoSuffix, spec);
|
||||
aInfo =
|
||||
ContentPrincipalInfo(attrs, originNoSuffix, spec, std::move(policies));
|
||||
} else {
|
||||
#ifdef FUZZING
|
||||
return false;
|
||||
|
@ -259,19 +287,37 @@ static bool ReadPrincipalInfo(JSStructuredCloneReader* aReader, uint32_t aTag,
|
|||
return true;
|
||||
}
|
||||
|
||||
static bool WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
|
||||
const OriginAttributes& aAttrs,
|
||||
const nsCString& aSpec,
|
||||
const nsCString& aOriginNoSuffix) {
|
||||
static bool WritePrincipalInfo(
|
||||
JSStructuredCloneWriter* aWriter, const OriginAttributes& aAttrs,
|
||||
const nsCString& aSpec, const nsCString& aOriginNoSuffix,
|
||||
const nsTArray<ContentSecurityPolicy>* aPolicies = nullptr) {
|
||||
nsAutoCString suffix;
|
||||
aAttrs.CreateSuffix(suffix);
|
||||
size_t policyCount = aPolicies ? aPolicies->Length() : 0;
|
||||
|
||||
return JS_WriteUint32Pair(aWriter, suffix.Length(), aSpec.Length()) &&
|
||||
JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
|
||||
JS_WriteBytes(aWriter, aSpec.get(), aSpec.Length()) &&
|
||||
JS_WriteUint32Pair(aWriter, aOriginNoSuffix.Length(), 0) &&
|
||||
JS_WriteBytes(aWriter, aOriginNoSuffix.get(),
|
||||
aOriginNoSuffix.Length());
|
||||
if (!(JS_WriteUint32Pair(aWriter, suffix.Length(), aSpec.Length()) &&
|
||||
JS_WriteBytes(aWriter, suffix.get(), suffix.Length()) &&
|
||||
JS_WriteBytes(aWriter, aSpec.get(), aSpec.Length()) &&
|
||||
JS_WriteUint32Pair(aWriter, aOriginNoSuffix.Length(), policyCount) &&
|
||||
JS_WriteBytes(aWriter, aOriginNoSuffix.get(),
|
||||
aOriginNoSuffix.Length()))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < policyCount; i++) {
|
||||
nsCString policy;
|
||||
CopyUTF16toUTF8((*aPolicies)[i].policy(), policy);
|
||||
uint32_t reportAndMeta =
|
||||
((*aPolicies)[i].reportOnlyFlag() ? 1 : 0) |
|
||||
((*aPolicies)[i].deliveredViaMetaTagFlag() ? 2 : 0);
|
||||
if (!(JS_WriteUint32Pair(aWriter, policy.Length(), reportAndMeta) &&
|
||||
JS_WriteBytes(aWriter, PromiseFlatCString(policy).get(),
|
||||
policy.Length()))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
|
||||
|
@ -304,7 +350,8 @@ static bool WritePrincipalInfo(JSStructuredCloneWriter* aWriter,
|
|||
const ContentPrincipalInfo& cInfo = aInfo;
|
||||
return JS_WriteUint32Pair(aWriter, SCTAG_DOM_CONTENT_PRINCIPAL, 0) &&
|
||||
WritePrincipalInfo(aWriter, cInfo.attrs(), cInfo.spec(),
|
||||
cInfo.originNoSuffix());
|
||||
cInfo.originNoSuffix(),
|
||||
&(cInfo.securityPolicies()));
|
||||
}
|
||||
|
||||
bool nsJSPrincipals::write(JSContext* aCx, JSStructuredCloneWriter* aWriter) {
|
||||
|
|
|
@ -1257,6 +1257,35 @@ nsScriptSecurityManager::GetDocShellCodebasePrincipal(
|
|||
return *aPrincipal ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::PrincipalWithOA(
|
||||
nsIPrincipal* aPrincipal, JS::Handle<JS::Value> aOriginAttributes,
|
||||
JSContext* aCx, nsIPrincipal** aReturnPrincipal) {
|
||||
if (!aPrincipal) {
|
||||
return NS_OK;
|
||||
}
|
||||
if (aPrincipal->GetIsCodebasePrincipal()) {
|
||||
OriginAttributes attrs;
|
||||
if (!aOriginAttributes.isObject() || !attrs.Init(aCx, aOriginAttributes)) {
|
||||
return NS_ERROR_INVALID_ARG;
|
||||
}
|
||||
RefPtr<ContentPrincipal> copy = new ContentPrincipal();
|
||||
ContentPrincipal* contentPrincipal =
|
||||
static_cast<ContentPrincipal*>(aPrincipal);
|
||||
nsresult rv = copy->Init(contentPrincipal, attrs);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
copy.forget(aReturnPrincipal);
|
||||
} else {
|
||||
// We do this for null principals, system principals (both fine)
|
||||
// ... and expanded principals, where we should probably do something
|
||||
// cleverer, but I also don't think we care too much.
|
||||
nsCOMPtr<nsIPrincipal> prin = aPrincipal;
|
||||
prin.forget(aReturnPrincipal);
|
||||
}
|
||||
|
||||
return *aReturnPrincipal ? NS_OK : NS_ERROR_FAILURE;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsScriptSecurityManager::CanCreateWrapper(JSContext* cx, const nsIID& aIID,
|
||||
nsISupports* aObj,
|
||||
|
|
Двоичный файл не отображается.
|
@ -54,6 +54,7 @@ support-files =
|
|||
browser_timelineMarkers-frame-05.js
|
||||
head.js
|
||||
frame-head.js
|
||||
file_data_load_inherit_csp.html
|
||||
file_click_link_within_view_source.html
|
||||
onload_message.html
|
||||
onpageshow_message.html
|
||||
|
@ -92,6 +93,7 @@ skip-if = verify
|
|||
[browser_bug852909.js]
|
||||
skip-if = (verify && debug && (os == 'win'))
|
||||
[browser_bug92473.js]
|
||||
[browser_data_load_inherit_csp.js]
|
||||
[browser_dataURI_unique_opaque_origin.js]
|
||||
[browser_uriFixupIntegration.js]
|
||||
[browser_uriFixupAlternateRedirects.js]
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
"use strict";
|
||||
|
||||
const TEST_PATH = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
|
||||
const HTML_URI = TEST_PATH + "file_data_load_inherit_csp.html";
|
||||
const DATA_URI = "data:text/html;html,<html><body>foo</body></html>";
|
||||
|
||||
function setDataHrefOnLink(aBrowser, aDataURI) {
|
||||
return ContentTask.spawn(aBrowser, aDataURI, function(uri) {
|
||||
let link = content.document.getElementById("testlink");
|
||||
link.href = uri;
|
||||
});
|
||||
};
|
||||
|
||||
function verifyCSP(aTestName, aBrowser, aDataURI) {
|
||||
return ContentTask.spawn(aBrowser, {aTestName, aDataURI}, async function ({aTestName, aDataURI}) {
|
||||
let channel = content.docShell.currentDocumentChannel;
|
||||
is(channel.URI.spec, aDataURI, "testing CSP for " + aTestName);
|
||||
let principal = channel.loadInfo.triggeringPrincipal;
|
||||
let cspJSON = principal.cspJSON;
|
||||
let cspOBJ = JSON.parse(cspJSON);
|
||||
let policies = cspOBJ["csp-policies"];
|
||||
is(policies.length, 1, "should be one policy");
|
||||
let policy = policies[0];
|
||||
is(policy['script-src'], "'unsafe-inline'", "script-src directive matches");
|
||||
});
|
||||
};
|
||||
|
||||
add_task(async function setup() {
|
||||
// allow top level data: URI navigations, otherwise clicking data: link fails
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
"set": [["security.data_uri.block_toplevel_data_uri_navigations", false]],
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_data_csp_inheritance_regular_click() {
|
||||
await BrowserTestUtils.withNewTab(HTML_URI, async function(browser) {
|
||||
let loadPromise = BrowserTestUtils.browserLoaded(browser, false, DATA_URI);
|
||||
// set the data href + simulate click
|
||||
await setDataHrefOnLink(gBrowser.selectedBrowser, DATA_URI);
|
||||
BrowserTestUtils.synthesizeMouseAtCenter("#testlink", {},
|
||||
gBrowser.selectedBrowser);
|
||||
await loadPromise;
|
||||
await verifyCSP("click()", gBrowser.selectedBrowser, DATA_URI);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_data_csp_inheritance_ctrl_click() {
|
||||
await BrowserTestUtils.withNewTab(HTML_URI, async function(browser) {
|
||||
let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, DATA_URI);
|
||||
// set the data href + simulate ctrl+click
|
||||
await setDataHrefOnLink(gBrowser.selectedBrowser, DATA_URI);
|
||||
BrowserTestUtils.synthesizeMouseAtCenter("#testlink",
|
||||
{ ctrlKey: true, metaKey: true },
|
||||
gBrowser.selectedBrowser);
|
||||
let tab = await loadPromise;
|
||||
gBrowser.selectTabAtIndex(2);
|
||||
await verifyCSP("ctrl-click()", gBrowser.selectedBrowser, DATA_URI);
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
});
|
||||
|
||||
add_task(async function test_data_csp_inheritance_right_click_open_link_in_new_tab() {
|
||||
await BrowserTestUtils.withNewTab(HTML_URI, async function(browser) {
|
||||
let loadPromise = BrowserTestUtils.waitForNewTab(gBrowser, DATA_URI);
|
||||
// set the data href + simulate right-click open link in tab
|
||||
await setDataHrefOnLink(gBrowser.selectedBrowser, DATA_URI);
|
||||
BrowserTestUtils.waitForEvent(document, "popupshown", false, event => {
|
||||
// These are operations that must be executed synchronously with the event.
|
||||
document.getElementById("context-openlinkintab").doCommand();
|
||||
event.target.hidePopup();
|
||||
return true;
|
||||
});
|
||||
BrowserTestUtils.synthesizeMouseAtCenter("#testlink",
|
||||
{ type: "contextmenu", button: 2 },
|
||||
gBrowser.selectedBrowser);
|
||||
|
||||
let tab = await loadPromise;
|
||||
gBrowser.selectTabAtIndex(2);
|
||||
await verifyCSP("right-click-open-in-new-tab()", gBrowser.selectedBrowser, DATA_URI);
|
||||
await BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,11 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Bug 1358009 - Inherit CSP into data URI</title>
|
||||
<meta http-equiv="Content-Security-Policy" content="script-src 'unsafe-inline'">
|
||||
</head>
|
||||
<body>
|
||||
<a id="testlink">testlink</a>
|
||||
</body>
|
||||
</html>
|
|
@ -2490,8 +2490,11 @@ nsresult ReadResponse(mozIStorageConnection* aConn, EntryId aEntryId,
|
|||
nsCString origin;
|
||||
url->Origin(origin);
|
||||
|
||||
// CSP is recovered from the headers, no need to initialise it here.
|
||||
nsTArray<mozilla::ipc::ContentSecurityPolicy> policies;
|
||||
aSavedResponseOut->mValue.principalInfo() =
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, origin, specNoSuffix);
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, origin, specNoSuffix,
|
||||
std::move(policies));
|
||||
}
|
||||
|
||||
bool nullPadding = false;
|
||||
|
|
|
@ -261,6 +261,22 @@ nsCSPContext::~nsCSPContext() {
|
|||
}
|
||||
}
|
||||
|
||||
nsresult nsCSPContext::InitFromOther(nsCSPContext* aOtherContext,
|
||||
Document* aDoc, nsIPrincipal* aPrincipal) {
|
||||
NS_ENSURE_ARG(aOtherContext);
|
||||
|
||||
nsresult rv = SetRequestContext(aDoc, aPrincipal);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
||||
for (auto policy : aOtherContext->mPolicies) {
|
||||
nsAutoString policyStr;
|
||||
policy->toString(policyStr);
|
||||
AppendPolicy(policyStr, policy->getReportOnlyFlag(),
|
||||
policy->getDeliveredViaMetaTagFlag());
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsCSPContext::GetPolicyString(uint32_t aIndex, nsAString& outStr) {
|
||||
outStr.Truncate();
|
||||
|
|
|
@ -51,6 +51,10 @@ class nsCSPContext : public nsIContentSecurityPolicy {
|
|||
public:
|
||||
nsCSPContext();
|
||||
|
||||
nsresult InitFromOther(nsCSPContext* otherContext,
|
||||
mozilla::dom::Document* aDoc,
|
||||
nsIPrincipal* aPrincipal);
|
||||
|
||||
/**
|
||||
* SetRequestContext() needs to be called before the innerWindowID
|
||||
* is initialized on the document. Use this function to call back to
|
||||
|
|
|
@ -21,6 +21,7 @@
|
|||
#include "nsIScriptError.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsContentPolicyUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
using namespace mozilla;
|
||||
|
||||
|
@ -250,6 +251,17 @@ CSPService::AsyncOnChannelRedirect(nsIChannel *oldChannel,
|
|||
nsIAsyncVerifyRedirectCallback *callback) {
|
||||
net::nsAsyncRedirectAutoCallback autoCallback(callback);
|
||||
|
||||
if (XRE_IsE10sParentProcess()) {
|
||||
nsCOMPtr<nsIParentChannel> parentChannel;
|
||||
NS_QueryNotificationCallbacks(oldChannel, parentChannel);
|
||||
if (parentChannel) {
|
||||
// This is an IPC'd channel. Don't check it here, because we won't have
|
||||
// access to the request context; we'll check them in the content
|
||||
// process instead. Bug 1509738 covers fixing this.
|
||||
return NS_OK;
|
||||
}
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> newUri;
|
||||
nsresult rv = newChannel->GetURI(getter_AddRefs(newUri));
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
|
|
|
@ -10,4 +10,7 @@ support-files =
|
|||
!/dom/security/test/csp/file_web_manifest_remote.html
|
||||
[browser_test_web_manifest.js]
|
||||
[browser_test_web_manifest_mixed_content.js]
|
||||
[browser_test_uir_optional_clicks.js]
|
||||
support-files =
|
||||
file_csp_meta_uir.html
|
||||
[browser_manifest-src-override-default-src.js]
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
"use strict"
|
||||
|
||||
const TEST_PATH_HTTP = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "http://example.com");
|
||||
const TEST_PATH_HTTPS = getRootDirectory(gTestPath).replace("chrome://mochitests/content", "https://example.com");
|
||||
|
||||
add_task(async function() {
|
||||
await BrowserTestUtils.withNewTab(TEST_PATH_HTTPS + "file_csp_meta_uir.html", async function(browser) {
|
||||
let newTabPromise = BrowserTestUtils.waitForNewTab(gBrowser, null, true);
|
||||
BrowserTestUtils.synthesizeMouse("#mylink", 2, 2, {accelKey: true}, browser);
|
||||
let tab = await newTabPromise;
|
||||
is(tab.linkedBrowser.currentURI.scheme, "https", "Should have opened https page.");
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,13 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Hello World</title>
|
||||
<meta http-equiv="Content-Security-Policy" content="upgrade-insecure-requests">
|
||||
</head>
|
||||
<body>
|
||||
<script>
|
||||
document.write("<a href='" + document.location.href.replace(/^https/, "http") + "' id='mylink'>Click me</a>");
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -111,8 +111,10 @@ nsresult CreatePrincipalInfo(nsILineInputStream* aStream,
|
|||
return rv;
|
||||
}
|
||||
|
||||
aEntry->principal() =
|
||||
mozilla::ipc::ContentPrincipalInfo(attrs, origin, aEntry->scope());
|
||||
// CSP will be applied during the script load.
|
||||
nsTArray<mozilla::ipc::ContentSecurityPolicy> policies;
|
||||
aEntry->principal() = mozilla::ipc::ContentPrincipalInfo(
|
||||
attrs, origin, aEntry->scope(), std::move(policies));
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
|
|
@ -271,8 +271,10 @@ TEST(ServiceWorkerRegistrar, TestWriteData) {
|
|||
|
||||
nsAutoCString spec;
|
||||
spec.AppendPrintf("spec write %d", i);
|
||||
|
||||
nsTArray<mozilla::ipc::ContentSecurityPolicy> policies;
|
||||
reg.principal() = mozilla::ipc::ContentPrincipalInfo(
|
||||
mozilla::OriginAttributes(i, i % 2), spec, spec);
|
||||
mozilla::OriginAttributes(i, i % 2), spec, spec, std::move(policies));
|
||||
|
||||
swr->TestRegisterServiceWorker(reg);
|
||||
}
|
||||
|
@ -858,8 +860,10 @@ TEST(ServiceWorkerRegistrar, TestDedupeWrite) {
|
|||
|
||||
nsAutoCString spec;
|
||||
spec.AppendPrintf("spec write dedupe/%d", i);
|
||||
|
||||
nsTArray<mozilla::ipc::ContentSecurityPolicy> policies;
|
||||
reg.principal() = mozilla::ipc::ContentPrincipalInfo(
|
||||
mozilla::OriginAttributes(0, false), spec, spec);
|
||||
mozilla::OriginAttributes(0, false), spec, spec, std::move(policies));
|
||||
|
||||
swr->TestRegisterServiceWorker(reg);
|
||||
}
|
||||
|
|
|
@ -11,13 +11,6 @@ using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
|||
namespace mozilla {
|
||||
namespace dom {
|
||||
|
||||
struct ContentSecurityPolicy
|
||||
{
|
||||
nsString policy;
|
||||
bool reportOnlyFlag;
|
||||
bool deliveredViaMetaTagFlag;
|
||||
};
|
||||
|
||||
struct RemoteWorkerData
|
||||
{
|
||||
// This should only be used for devtools.
|
||||
|
|
|
@ -38,34 +38,6 @@ using namespace mozilla::ipc;
|
|||
|
||||
namespace {
|
||||
|
||||
nsresult PopulateContentSecurityPolicies(
|
||||
nsIContentSecurityPolicy* aCSP,
|
||||
nsTArray<ContentSecurityPolicy>& aPolicies) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
MOZ_ASSERT(aCSP);
|
||||
MOZ_ASSERT(aPolicies.IsEmpty());
|
||||
|
||||
uint32_t count = 0;
|
||||
nsresult rv = aCSP->GetPolicyCount(&count);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
const nsCSPPolicy* policy = aCSP->GetPolicy(i);
|
||||
MOZ_ASSERT(policy);
|
||||
|
||||
nsAutoString policyString;
|
||||
policy->toString(policyString);
|
||||
|
||||
aPolicies.AppendElement(
|
||||
ContentSecurityPolicy(policyString, policy->getReportOnlyFlag(),
|
||||
policy->getDeliveredViaMetaTagFlag()));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult PopulateContentSecurityPolicyArray(
|
||||
nsIPrincipal* aPrincipal, nsTArray<ContentSecurityPolicy>& policies,
|
||||
nsTArray<ContentSecurityPolicy>& preloadPolicies) {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
commit 6e82c7c389888603f0de84ffe5c60f43f11ee844
|
||||
commit 26eea4948c743417c8aa9d1e7fd900d1fb57fbdc
|
||||
Author: Yoshito Umaoka <yoshito_umaoka@us.ibm.com>
|
||||
Date: Wed Nov 7 19:23:35 2018 -0500
|
||||
Date: Wed Jan 2 17:39:20 2019 -0500
|
||||
|
||||
ICU-20260 Fix CR/LF issue
|
||||
ICU time zone data updates for tzdata 2018i (including 2018h changes)
|
||||
|
|
|
@ -1 +1 @@
|
|||
2018g
|
||||
2018i
|
||||
|
|
Двоичные данные
intl/tzdata/source/be/metaZones.res
Двоичные данные
intl/tzdata/source/be/metaZones.res
Двоичный файл не отображается.
Двоичные данные
intl/tzdata/source/be/timezoneTypes.res
Двоичные данные
intl/tzdata/source/be/timezoneTypes.res
Двоичный файл не отображается.
Двоичные данные
intl/tzdata/source/be/windowsZones.res
Двоичные данные
intl/tzdata/source/be/windowsZones.res
Двоичный файл не отображается.
Двоичные данные
intl/tzdata/source/be/zoneinfo64.res
Двоичные данные
intl/tzdata/source/be/zoneinfo64.res
Двоичный файл не отображается.
Двоичные данные
intl/tzdata/source/ee/metaZones.res
Двоичные данные
intl/tzdata/source/ee/metaZones.res
Двоичный файл не отображается.
Двоичные данные
intl/tzdata/source/ee/timezoneTypes.res
Двоичные данные
intl/tzdata/source/ee/timezoneTypes.res
Двоичный файл не отображается.
Двоичные данные
intl/tzdata/source/ee/windowsZones.res
Двоичные данные
intl/tzdata/source/ee/windowsZones.res
Двоичный файл не отображается.
Двоичные данные
intl/tzdata/source/ee/zoneinfo64.res
Двоичные данные
intl/tzdata/source/ee/zoneinfo64.res
Двоичный файл не отображается.
Двоичные данные
intl/tzdata/source/le/metaZones.res
Двоичные данные
intl/tzdata/source/le/metaZones.res
Двоичный файл не отображается.
Двоичные данные
intl/tzdata/source/le/timezoneTypes.res
Двоичные данные
intl/tzdata/source/le/timezoneTypes.res
Двоичный файл не отображается.
Двоичные данные
intl/tzdata/source/le/windowsZones.res
Двоичные данные
intl/tzdata/source/le/windowsZones.res
Двоичный файл не отображается.
Двоичные данные
intl/tzdata/source/le/zoneinfo64.res
Двоичные данные
intl/tzdata/source/le/zoneinfo64.res
Двоичный файл не отображается.
|
@ -813,12 +813,7 @@ metaZones:table(nofallback){
|
|||
{
|
||||
"Europe_Western",
|
||||
"1985-12-31 23:00",
|
||||
"2018-10-26 23:00",
|
||||
}
|
||||
{
|
||||
"Europe_Central",
|
||||
"2018-10-26 23:00",
|
||||
"9999-12-31 23:59",
|
||||
"2018-10-28 02:00",
|
||||
}
|
||||
}
|
||||
"Africa:Ceuta"{
|
||||
|
@ -867,12 +862,7 @@ metaZones:table(nofallback){
|
|||
{
|
||||
"Europe_Western",
|
||||
"1976-04-14 01:00",
|
||||
"2018-10-26 23:00",
|
||||
}
|
||||
{
|
||||
"Europe_Central",
|
||||
"2018-10-26 23:00",
|
||||
"9999-12-31 23:59",
|
||||
"2018-10-28 02:00",
|
||||
}
|
||||
}
|
||||
"Africa:Freetown"{
|
||||
|
@ -1045,6 +1035,11 @@ metaZones:table(nofallback){
|
|||
{
|
||||
"Africa_Western",
|
||||
"2018-01-01 01:00",
|
||||
"2019-01-01 01:00",
|
||||
}
|
||||
{
|
||||
"GMT",
|
||||
"2019-01-01 01:00",
|
||||
"9999-12-31 23:59",
|
||||
}
|
||||
}
|
||||
|
@ -2086,6 +2081,16 @@ metaZones:table(nofallback){
|
|||
{
|
||||
"Alaska",
|
||||
"2015-11-01 10:00",
|
||||
"2018-11-04 10:00",
|
||||
}
|
||||
{
|
||||
"America_Pacific",
|
||||
"2018-11-04 10:00",
|
||||
"2019-03-10 11:00",
|
||||
}
|
||||
{
|
||||
"Alaska",
|
||||
"2019-03-10 11:00",
|
||||
"9999-12-31 23:59",
|
||||
}
|
||||
}
|
||||
|
@ -3162,6 +3167,13 @@ metaZones:table(nofallback){
|
|||
"9999-12-31 23:59",
|
||||
}
|
||||
}
|
||||
"Asia:Qostanay"{
|
||||
{
|
||||
"Kazakhstan_Eastern",
|
||||
"2004-10-30 21:00",
|
||||
"9999-12-31 23:59",
|
||||
}
|
||||
}
|
||||
"Asia:Qyzylorda"{
|
||||
{
|
||||
"Kizilorda",
|
||||
|
@ -3176,6 +3188,11 @@ metaZones:table(nofallback){
|
|||
{
|
||||
"Kazakhstan_Eastern",
|
||||
"2004-10-30 21:00",
|
||||
"2018-12-20 18:00",
|
||||
}
|
||||
{
|
||||
"Kazakhstan_Western",
|
||||
"2018-12-20 18:00",
|
||||
"9999-12-31 23:59",
|
||||
}
|
||||
}
|
||||
|
@ -4226,11 +4243,11 @@ metaZones:table(nofallback){
|
|||
{
|
||||
"Kwajalein",
|
||||
"1970-01-01 00:00",
|
||||
"1993-08-20 12:00",
|
||||
"1993-08-21 12:00",
|
||||
}
|
||||
{
|
||||
"Marshall_Islands",
|
||||
"1993-08-20 12:00",
|
||||
"1993-08-21 12:00",
|
||||
"9999-12-31 23:59",
|
||||
}
|
||||
}
|
||||
|
|
|
@ -416,6 +416,7 @@ timezoneTypes:table(nofallback){
|
|||
"Asia:Pontianak"{"idpnk"}
|
||||
"Asia:Pyongyang"{"kpfnj"}
|
||||
"Asia:Qatar"{"qadoh"}
|
||||
"Asia:Qostanay"{"kzksn"}
|
||||
"Asia:Qyzylorda"{"kzkzo"}
|
||||
"Asia:Rangoon"{"mmrgn"}
|
||||
"Asia:Riyadh"{"saruh"}
|
||||
|
|
|
@ -17,8 +17,8 @@ windowsZones:table(nofallback){
|
|||
"Alaskan Standard Time"{
|
||||
001{"America/Anchorage"}
|
||||
US{
|
||||
"America/Anchorage America/Juneau America/Metlakatla America/Nome Ame"
|
||||
"rica/Sitka America/Yakutat"
|
||||
"America/Anchorage America/Juneau America/Nome America/Sitka America/"
|
||||
"Yakutat"
|
||||
}
|
||||
}
|
||||
"Aleutian Standard Time"{
|
||||
|
@ -130,7 +130,7 @@ windowsZones:table(nofallback){
|
|||
CN{"Asia/Urumqi"}
|
||||
IO{"Indian/Chagos"}
|
||||
KG{"Asia/Bishkek"}
|
||||
KZ{"Asia/Almaty Asia/Qyzylorda"}
|
||||
KZ{"Asia/Almaty Asia/Qostanay"}
|
||||
ZZ{"Etc/GMT-6"}
|
||||
}
|
||||
"Central Brazilian Standard Time"{
|
||||
|
@ -468,7 +468,7 @@ windowsZones:table(nofallback){
|
|||
"Pacific Standard Time"{
|
||||
001{"America/Los_Angeles"}
|
||||
CA{"America/Vancouver America/Dawson America/Whitehorse"}
|
||||
US{"America/Los_Angeles"}
|
||||
US{"America/Los_Angeles America/Metlakatla"}
|
||||
ZZ{"PST8PDT"}
|
||||
}
|
||||
"Pakistan Standard Time"{
|
||||
|
@ -779,7 +779,7 @@ windowsZones:table(nofallback){
|
|||
"West Asia Standard Time"{
|
||||
001{"Asia/Tashkent"}
|
||||
AQ{"Antarctica/Mawson"}
|
||||
KZ{"Asia/Oral Asia/Aqtau Asia/Aqtobe Asia/Atyrau"}
|
||||
KZ{"Asia/Oral Asia/Aqtau Asia/Aqtobe Asia/Atyrau Asia/Qyzylorda"}
|
||||
MV{"Indian/Maldives"}
|
||||
TF{"Indian/Kerguelen"}
|
||||
TJ{"Asia/Dushanbe"}
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -8,7 +8,6 @@
|
|||
|
||||
#include "MainThreadUtils.h"
|
||||
#include "mozilla/Assertions.h"
|
||||
#include "mozilla/BasePrincipal.h"
|
||||
#include "mozilla/ContentPrincipal.h"
|
||||
#include "mozilla/NullPrincipal.h"
|
||||
#include "mozilla/ipc/PBackgroundSharedTypes.h"
|
||||
|
@ -24,6 +23,8 @@
|
|||
#include "nsTArray.h"
|
||||
#include "mozilla/nsRedirectHistoryEntry.h"
|
||||
#include "URIUtils.h"
|
||||
#include "mozilla/dom/nsCSPUtils.h"
|
||||
#include "mozilla/dom/nsCSPContext.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
@ -103,6 +104,29 @@ already_AddRefed<nsIPrincipal> PrincipalInfoToPrincipal(
|
|||
MOZ_CRASH("Origin must be available when deserialized");
|
||||
}
|
||||
|
||||
if (info.securityPolicies().Length() > 0) {
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp =
|
||||
do_CreateInstance(NS_CSPCONTEXT_CONTRACTID, &rv);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
rv = csp->SetRequestContext(nullptr, principal);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
for (auto policy : info.securityPolicies()) {
|
||||
rv = csp->AppendPolicy(policy.policy(), policy.reportOnlyFlag(),
|
||||
policy.deliveredViaMetaTagFlag());
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
principal->SetCsp(csp);
|
||||
}
|
||||
|
||||
return principal.forget();
|
||||
}
|
||||
|
||||
|
@ -140,6 +164,34 @@ already_AddRefed<nsIPrincipal> PrincipalInfoToPrincipal(
|
|||
MOZ_CRASH("Should never get here!");
|
||||
}
|
||||
|
||||
nsresult PopulateContentSecurityPolicies(
|
||||
nsIContentSecurityPolicy* aCSP,
|
||||
nsTArray<ContentSecurityPolicy>& aPolicies) {
|
||||
MOZ_ASSERT(aCSP);
|
||||
MOZ_ASSERT(aPolicies.IsEmpty());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
uint32_t count = 0;
|
||||
nsresult rv = aCSP->GetPolicyCount(&count);
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < count; ++i) {
|
||||
const nsCSPPolicy* policy = aCSP->GetPolicy(i);
|
||||
MOZ_ASSERT(policy);
|
||||
|
||||
nsAutoString policyString;
|
||||
policy->toString(policyString);
|
||||
|
||||
aPolicies.AppendElement(
|
||||
ContentSecurityPolicy(policyString, policy->getReportOnlyFlag(),
|
||||
policy->getDeliveredViaMetaTagFlag()));
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult PrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
|
||||
PrincipalInfo* aPrincipalInfo) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
@ -231,8 +283,20 @@ nsresult PrincipalToPrincipalInfo(nsIPrincipal* aPrincipal,
|
|||
return rv;
|
||||
}
|
||||
|
||||
*aPrincipalInfo = ContentPrincipalInfo(aPrincipal->OriginAttributesRef(),
|
||||
originNoSuffix, spec);
|
||||
nsCOMPtr<nsIContentSecurityPolicy> csp;
|
||||
rv = aPrincipal->GetCsp(getter_AddRefs(csp));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return rv;
|
||||
}
|
||||
|
||||
nsTArray<ContentSecurityPolicy> policies;
|
||||
if (csp) {
|
||||
PopulateContentSecurityPolicies(csp, policies);
|
||||
}
|
||||
|
||||
*aPrincipalInfo =
|
||||
ContentPrincipalInfo(aPrincipal->OriginAttributesRef(), originNoSuffix,
|
||||
spec, std::move(policies));
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
|
|
|
@ -55,6 +55,7 @@ class RedirectHistoryEntryInfo;
|
|||
|
||||
namespace ipc {
|
||||
|
||||
class ContentSecurityPolicy;
|
||||
class PrincipalInfo;
|
||||
|
||||
/**
|
||||
|
@ -65,6 +66,14 @@ class PrincipalInfo;
|
|||
already_AddRefed<nsIPrincipal> PrincipalInfoToPrincipal(
|
||||
const PrincipalInfo& aPrincipalInfo, nsresult* aOptionalResult = nullptr);
|
||||
|
||||
/**
|
||||
* Populate an array of ContentSecurityPolicy objects from a CSP object.
|
||||
*
|
||||
* MUST be called on the main thread only.
|
||||
*/
|
||||
nsresult PopulateContentSecurityPolicies(
|
||||
nsIContentSecurityPolicy* aCSP, nsTArray<ContentSecurityPolicy>& aPolicies);
|
||||
|
||||
/**
|
||||
* Convert an nsIPrincipal to a PrincipalInfo.
|
||||
*
|
||||
|
|
|
@ -8,6 +8,13 @@ using struct mozilla::void_t from "ipc/IPCMessageUtils.h";
|
|||
namespace mozilla {
|
||||
namespace ipc {
|
||||
|
||||
struct ContentSecurityPolicy
|
||||
{
|
||||
nsString policy;
|
||||
bool reportOnlyFlag;
|
||||
bool deliveredViaMetaTagFlag;
|
||||
};
|
||||
|
||||
struct ContentPrincipalInfo
|
||||
{
|
||||
OriginAttributes attrs;
|
||||
|
@ -22,6 +29,8 @@ struct ContentPrincipalInfo
|
|||
nsCString originNoSuffix;
|
||||
|
||||
nsCString spec;
|
||||
|
||||
ContentSecurityPolicy[] securityPolicies;
|
||||
};
|
||||
|
||||
struct SystemPrincipalInfo
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// Generated by make_intl_data.py. DO NOT EDIT.
|
||||
// tzdata version = 2018g
|
||||
// tzdata version = 2018i
|
||||
|
||||
#ifndef builtin_intl_TimeZoneDataGenerated_h
|
||||
#define builtin_intl_TimeZoneDataGenerated_h
|
||||
|
|
|
@ -256,6 +256,33 @@ void LIRGenerator::visitWasmUnsignedToFloat32(MWasmUnsignedToFloat32* ins) {
|
|||
define(lir, ins);
|
||||
}
|
||||
|
||||
// If the base is a constant, and it is zero or its offset is zero, then
|
||||
// code generation will fold the values into the access. Allocate the
|
||||
// pointer to a register only if that can't happen.
|
||||
|
||||
static bool OptimizableConstantAccess(MDefinition* base,
|
||||
const wasm::MemoryAccessDesc& access) {
|
||||
MOZ_ASSERT(base->isConstant());
|
||||
MOZ_ASSERT(base->type() == MIRType::Int32);
|
||||
|
||||
if (!(base->toConstant()->isInt32(0) || access.offset() == 0)) {
|
||||
return false;
|
||||
}
|
||||
if (access.type() == Scalar::Int64) {
|
||||
// For int64 accesses on 32-bit systems we will need to add another offset
|
||||
// of 4 to access the high part of the value; make sure this does not
|
||||
// overflow the value.
|
||||
int32_t v;
|
||||
if (base->toConstant()->isInt32(0)) {
|
||||
v = access.offset();
|
||||
} else {
|
||||
v = base->toConstant()->toInt32();
|
||||
}
|
||||
return v <= int32_t(INT32_MAX - INT64HIGH_OFFSET);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void LIRGenerator::visitWasmLoad(MWasmLoad* ins) {
|
||||
MDefinition* base = ins->base();
|
||||
MOZ_ASSERT(base->type() == MIRType::Int32);
|
||||
|
@ -273,13 +300,8 @@ void LIRGenerator::visitWasmLoad(MWasmLoad* ins) {
|
|||
return;
|
||||
}
|
||||
|
||||
// If the base is a constant, and it is zero or its offset is zero, then
|
||||
// code generation will fold the values into the access. Allocate the
|
||||
// pointer to a register only if that can't happen.
|
||||
|
||||
LAllocation baseAlloc;
|
||||
if (!base->isConstant() ||
|
||||
!(base->toConstant()->isInt32(0) || ins->access().offset() == 0)) {
|
||||
if (!base->isConstant() || !OptimizableConstantAccess(base, ins->access())) {
|
||||
baseAlloc = ins->type() == MIRType::Int64 ? useRegister(base)
|
||||
: useRegisterAtStart(base);
|
||||
}
|
||||
|
@ -326,13 +348,8 @@ void LIRGenerator::visitWasmStore(MWasmStore* ins) {
|
|||
return;
|
||||
}
|
||||
|
||||
// If the base is a constant, and it is zero or its offset is zero, then
|
||||
// code generation will fold the values into the access. Allocate the
|
||||
// pointer to a register only if that can't happen.
|
||||
|
||||
LAllocation baseAlloc;
|
||||
if (!base->isConstant() ||
|
||||
!(base->toConstant()->isInt32(0) || ins->access().offset() == 0)) {
|
||||
if (!base->isConstant() || !OptimizableConstantAccess(base, ins->access())) {
|
||||
baseAlloc = useRegisterAtStart(base);
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
|
||||
|
||||
// Generated by make_intl_data.py. DO NOT EDIT.
|
||||
// tzdata version = 2018g
|
||||
// tzdata version = 2018i
|
||||
|
||||
const tzMapper = [
|
||||
x => x,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
|
||||
|
||||
// Generated by make_intl_data.py. DO NOT EDIT.
|
||||
// tzdata version = 2018g
|
||||
// tzdata version = 2018i
|
||||
|
||||
const tzMapper = [
|
||||
x => x,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
|
||||
|
||||
// Generated by make_intl_data.py. DO NOT EDIT.
|
||||
// tzdata version = 2018g
|
||||
// tzdata version = 2018i
|
||||
|
||||
const tzMapper = [
|
||||
x => x,
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
// |reftest| skip-if(!this.hasOwnProperty("Intl"))
|
||||
|
||||
// Generated by make_intl_data.py. DO NOT EDIT.
|
||||
// tzdata version = 2018g
|
||||
// tzdata version = 2018i
|
||||
|
||||
const tzMapper = [
|
||||
x => x,
|
||||
|
|
|
@ -164,8 +164,8 @@ struct JSContext : public JS::RootingContext,
|
|||
return thing->compartment() == compartment();
|
||||
}
|
||||
|
||||
void* onOutOfMemory(js::AllocFunction allocFunc, size_t nbytes,
|
||||
arena_id_t arena, void* reallocPtr = nullptr) {
|
||||
void* onOutOfMemory(js::AllocFunction allocFunc, arena_id_t arena,
|
||||
size_t nbytes, void* reallocPtr = nullptr) {
|
||||
if (helperThread()) {
|
||||
addPendingOutOfMemory();
|
||||
return nullptr;
|
||||
|
|
|
@ -162,7 +162,10 @@ static SystemClass ClassifySystem() {
|
|||
}
|
||||
|
||||
// Code sizes in machine code bytes per bytecode byte, again empirical except
|
||||
// where marked as "Guess".
|
||||
// where marked.
|
||||
//
|
||||
// The Ion estimate for ARM64 is the measured Baseline value scaled by a
|
||||
// plausible factor for optimized code.
|
||||
|
||||
static const double x64Tox86Inflation = 1.25;
|
||||
|
||||
|
@ -170,15 +173,14 @@ static const double x64IonBytesPerBytecode = 2.45;
|
|||
static const double x86IonBytesPerBytecode =
|
||||
x64IonBytesPerBytecode * x64Tox86Inflation;
|
||||
static const double arm32IonBytesPerBytecode = 3.3;
|
||||
static const double arm64IonBytesPerBytecode = 3.0; // Guess
|
||||
static const double arm64IonBytesPerBytecode = 3.0 / 1.4; // Estimate
|
||||
|
||||
static const double x64BaselineBytesPerBytecode = x64IonBytesPerBytecode * 1.43;
|
||||
static const double x86BaselineBytesPerBytecode =
|
||||
x64BaselineBytesPerBytecode * x64Tox86Inflation;
|
||||
static const double arm32BaselineBytesPerBytecode =
|
||||
arm32IonBytesPerBytecode * 1.39;
|
||||
static const double arm64BaselineBytesPerBytecode =
|
||||
arm64IonBytesPerBytecode * 1.39; // Guess
|
||||
static const double arm64BaselineBytesPerBytecode = 3.0;
|
||||
|
||||
static double OptimizedBytesPerBytecode(SystemClass cls) {
|
||||
switch (cls) {
|
||||
|
@ -239,25 +241,32 @@ static const double tierCutoffMs = 250;
|
|||
// Compilation rate values are empirical except when noted, the reference
|
||||
// systems are:
|
||||
//
|
||||
// Late-2013 MacBook Pro (2.6GHz quad hyperthreaded Haswell)
|
||||
// Late-2015 Nexus 5X (1.4GHz quad Cortex-A53 + 1.8GHz dual Cortex-A57)
|
||||
// Late-2013 MacBook Pro (2.6GHz 4 x hyperthreaded Haswell, Mac OS X)
|
||||
// Late-2015 Nexus 5X (1.4GHz 4 x Cortex-A53 + 1.8GHz 2 x Cortex-A57, Android)
|
||||
// Ca-2016 SoftIron Overdrive 1000 (1.7GHz 4 x Cortex-A57, Fedora)
|
||||
//
|
||||
// The rates are always per core.
|
||||
//
|
||||
// The estimate for ARM64 is the Baseline compilation rate on the SoftIron
|
||||
// (because we have no Ion yet), divided by 5 to estimate Ion compile rate and
|
||||
// then divided by 2 to make it more reasonable for consumer ARM64 systems.
|
||||
|
||||
static const double x64BytecodesPerMs = 2100;
|
||||
static const double x86BytecodesPerMs = 1500;
|
||||
static const double arm32BytecodesPerMs = 450;
|
||||
static const double arm64BytecodesPerMs = 650; // Guess
|
||||
static const double x64IonBytecodesPerMs = 2100;
|
||||
static const double x86IonBytecodesPerMs = 1500;
|
||||
static const double arm32IonBytecodesPerMs = 450;
|
||||
static const double arm64IonBytecodesPerMs = 750; // Estimate
|
||||
|
||||
// Tiering cutoff values: if code section sizes are below these values (when
|
||||
// divided by the effective number of cores) we do not tier, because we guess
|
||||
// that parallel Ion compilation will be fast enough.
|
||||
|
||||
static const double x64DesktopTierCutoff = x64BytecodesPerMs * tierCutoffMs;
|
||||
static const double x86DesktopTierCutoff = x86BytecodesPerMs * tierCutoffMs;
|
||||
static const double x64DesktopTierCutoff = x64IonBytecodesPerMs * tierCutoffMs;
|
||||
static const double x86DesktopTierCutoff = x86IonBytecodesPerMs * tierCutoffMs;
|
||||
static const double x86MobileTierCutoff = x86DesktopTierCutoff / 2; // Guess
|
||||
static const double arm32MobileTierCutoff = arm32BytecodesPerMs * tierCutoffMs;
|
||||
static const double arm64MobileTierCutoff = arm64BytecodesPerMs * tierCutoffMs;
|
||||
static const double arm32MobileTierCutoff = arm32IonBytecodesPerMs * tierCutoffMs;
|
||||
static const double arm64MobileTierCutoff = arm64IonBytecodesPerMs * tierCutoffMs;
|
||||
|
||||
static double CodesizeCutoff(SystemClass cls, uint32_t codeSize) {
|
||||
static double CodesizeCutoff(SystemClass cls) {
|
||||
switch (cls) {
|
||||
case SystemClass::DesktopX86:
|
||||
case SystemClass::DesktopUnknown32:
|
||||
|
@ -335,7 +344,7 @@ static bool TieringBeneficial(uint32_t codeSize) {
|
|||
// Ion compilation on available cores must take long enough to be worth the
|
||||
// bother.
|
||||
|
||||
double cutoffSize = CodesizeCutoff(cls, codeSize);
|
||||
double cutoffSize = CodesizeCutoff(cls);
|
||||
double effectiveCores = EffectiveCores(cls, cores);
|
||||
|
||||
if ((codeSize / effectiveCores) < cutoffSize) {
|
||||
|
|
|
@ -7814,7 +7814,10 @@ static bool IsAcceptableCaretPosition(const gfxSkipCharsIterator& aIter,
|
|||
(NS_IS_LOW_SURROGATE(ch) && offs > 0 &&
|
||||
NS_IS_HIGH_SURROGATE(frag->CharAt(offs - 1))) ||
|
||||
(!aTextRun->IsLigatureGroupStart(index) &&
|
||||
unicode::GetEmojiPresentation(ch) == unicode::EmojiDefault)) {
|
||||
(unicode::GetEmojiPresentation(ch) == unicode::EmojiDefault ||
|
||||
(unicode::GetEmojiPresentation(ch) == unicode::TextDefault &&
|
||||
offs + 1 < frag->GetLength() &&
|
||||
frag->CharAt(offs + 1) == gfxFontUtils::kUnicodeVS16)))) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -1784,6 +1784,20 @@ VARCACHE_PREF(
|
|||
bool, true
|
||||
)
|
||||
|
||||
// Block 3rd party fingerprinting resources.
|
||||
VARCACHE_PREF(
|
||||
"privacy.trackingprotection.fingerprinting.enabled",
|
||||
privacy_trackingprotection_fingerprinting_enabled,
|
||||
bool, true
|
||||
)
|
||||
|
||||
// Block 3rd party cryptomining resources.
|
||||
VARCACHE_PREF(
|
||||
"privacy.trackingprotection.cryptomining.enabled",
|
||||
privacy_trackingprotection_cryptomining_enabled,
|
||||
bool, true
|
||||
)
|
||||
|
||||
// Lower the priority of network loads for resources on the tracking protection
|
||||
// list. Note that this requires the
|
||||
// privacy.trackingprotection.annotate_channels pref to be on in order to have
|
||||
|
|
|
@ -87,13 +87,14 @@ LazyLogModule UrlClassifierCommon::sLog("nsChannelClassifier");
|
|||
pwin->NotifyContentBlockingState(aBlockedReason, aChannel, true, uri);
|
||||
}
|
||||
|
||||
/* static */ bool
|
||||
UrlClassifierCommon::ShouldEnableTrackingProtectionOrAnnotation(
|
||||
/* static */ bool UrlClassifierCommon::ShouldEnableClassifier(
|
||||
nsIChannel* aChannel,
|
||||
AntiTrackingCommon::ContentBlockingAllowListPurpose aBlockingPurpose) {
|
||||
MOZ_ASSERT(aChannel);
|
||||
MOZ_ASSERT(aBlockingPurpose == AntiTrackingCommon::eTrackingProtection ||
|
||||
aBlockingPurpose == AntiTrackingCommon::eTrackingAnnotations);
|
||||
aBlockingPurpose == AntiTrackingCommon::eTrackingAnnotations ||
|
||||
aBlockingPurpose == AntiTrackingCommon::eFingerprinting ||
|
||||
aBlockingPurpose == AntiTrackingCommon::eCryptomining);
|
||||
|
||||
nsCOMPtr<nsIHttpChannelInternal> channel = do_QueryInterface(aChannel);
|
||||
if (!channel) {
|
||||
|
|
|
@ -33,7 +33,7 @@ class UrlClassifierCommon final {
|
|||
static void NotifyChannelBlocked(nsIChannel* aChannel,
|
||||
unsigned aBlockedReason);
|
||||
|
||||
static bool ShouldEnableTrackingProtectionOrAnnotation(
|
||||
static bool ShouldEnableClassifier(
|
||||
nsIChannel* aChannel,
|
||||
AntiTrackingCommon::ContentBlockingAllowListPurpose aBlockingPurpose);
|
||||
|
||||
|
|
|
@ -0,0 +1,175 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "UrlClassifierFeatureCryptomining.h"
|
||||
|
||||
#include "mozilla/AntiTrackingCommon.h"
|
||||
#include "mozilla/net/UrlClassifierCommon.h"
|
||||
#include "mozilla/StaticPrefs.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
namespace {
|
||||
|
||||
#define CRYPTOMINING_FEATURE_NAME "cryptomining"
|
||||
|
||||
#define URLCLASSIFIER_CRYPTOMINING_BLACKLIST \
|
||||
"urlclassifier.features.cryptomining.blacklistTables"
|
||||
#define URLCLASSIFIER_CRYPTOMINING_BLACKLIST_TEST_ENTRIES \
|
||||
"urlclassifier.features.cryptomining.blacklistHosts"
|
||||
#define URLCLASSIFIER_CRYPTOMINING_WHITELIST \
|
||||
"urlclassifier.features.cryptomining.whitelistTables"
|
||||
#define URLCLASSIFIER_CRYPTOMINING_WHITELIST_TEST_ENTRIES \
|
||||
"urlclassifier.features.cryptomining.whitelistHosts"
|
||||
#define TABLE_CRYPTOMINING_BLACKLIST_PREF "cryptomining-blacklist-pref"
|
||||
#define TABLE_CRYPTOMINING_WHITELIST_PREF "cryptomining-whitelist-pref"
|
||||
|
||||
StaticRefPtr<UrlClassifierFeatureCryptomining> gFeatureCryptomining;
|
||||
|
||||
} // namespace
|
||||
|
||||
UrlClassifierFeatureCryptomining::UrlClassifierFeatureCryptomining()
|
||||
: UrlClassifierFeatureBase(
|
||||
NS_LITERAL_CSTRING(CRYPTOMINING_FEATURE_NAME),
|
||||
NS_LITERAL_CSTRING(URLCLASSIFIER_CRYPTOMINING_BLACKLIST),
|
||||
NS_LITERAL_CSTRING(URLCLASSIFIER_CRYPTOMINING_WHITELIST),
|
||||
NS_LITERAL_CSTRING(URLCLASSIFIER_CRYPTOMINING_BLACKLIST_TEST_ENTRIES),
|
||||
NS_LITERAL_CSTRING(URLCLASSIFIER_CRYPTOMINING_WHITELIST_TEST_ENTRIES),
|
||||
NS_LITERAL_CSTRING(TABLE_CRYPTOMINING_BLACKLIST_PREF),
|
||||
NS_LITERAL_CSTRING(TABLE_CRYPTOMINING_WHITELIST_PREF),
|
||||
EmptyCString()) {}
|
||||
|
||||
/* static */ const char* UrlClassifierFeatureCryptomining::Name() {
|
||||
return CRYPTOMINING_FEATURE_NAME;
|
||||
}
|
||||
|
||||
/* static */ void UrlClassifierFeatureCryptomining::MaybeInitialize() {
|
||||
UC_LOG(("UrlClassifierFeatureCryptomining: MaybeInitialize"));
|
||||
|
||||
if (!gFeatureCryptomining) {
|
||||
gFeatureCryptomining = new UrlClassifierFeatureCryptomining();
|
||||
gFeatureCryptomining->InitializePreferences();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void UrlClassifierFeatureCryptomining::MaybeShutdown() {
|
||||
UC_LOG(("UrlClassifierFeatureCryptomining: MaybeShutdown"));
|
||||
|
||||
if (gFeatureCryptomining) {
|
||||
gFeatureCryptomining->ShutdownPreferences();
|
||||
gFeatureCryptomining = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<UrlClassifierFeatureCryptomining>
|
||||
UrlClassifierFeatureCryptomining::MaybeCreate(nsIChannel* aChannel) {
|
||||
MOZ_ASSERT(aChannel);
|
||||
|
||||
UC_LOG(("UrlClassifierFeatureCryptomining: MaybeCreate for channel %p",
|
||||
aChannel));
|
||||
|
||||
if (!StaticPrefs::privacy_trackingprotection_cryptomining_enabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> chanURI;
|
||||
nsresult rv = aChannel->GetURI(getter_AddRefs(chanURI));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool isThirdParty =
|
||||
nsContentUtils::IsThirdPartyWindowOrChannel(nullptr, aChannel, chanURI);
|
||||
if (!isThirdParty) {
|
||||
if (UC_LOG_ENABLED()) {
|
||||
nsCString spec = chanURI->GetSpecOrDefault();
|
||||
spec.Truncate(
|
||||
std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
|
||||
UC_LOG(
|
||||
("UrlClassifierFeatureCryptomining: Skipping cryptomining checks "
|
||||
"for first party or top-level load channel[%p] "
|
||||
"with uri %s",
|
||||
aChannel, spec.get()));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!UrlClassifierCommon::ShouldEnableClassifier(
|
||||
aChannel, AntiTrackingCommon::eCryptomining)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MaybeInitialize();
|
||||
MOZ_ASSERT(gFeatureCryptomining);
|
||||
|
||||
RefPtr<UrlClassifierFeatureCryptomining> self = gFeatureCryptomining;
|
||||
return self.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsIUrlClassifierFeature>
|
||||
UrlClassifierFeatureCryptomining::GetIfNameMatches(const nsACString& aName) {
|
||||
if (!aName.EqualsLiteral(CRYPTOMINING_FEATURE_NAME)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MaybeInitialize();
|
||||
MOZ_ASSERT(gFeatureCryptomining);
|
||||
|
||||
RefPtr<UrlClassifierFeatureCryptomining> self = gFeatureCryptomining;
|
||||
return self.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierFeatureCryptomining::ProcessChannel(nsIChannel* aChannel,
|
||||
const nsACString& aList,
|
||||
bool* aShouldContinue) {
|
||||
NS_ENSURE_ARG_POINTER(aChannel);
|
||||
NS_ENSURE_ARG_POINTER(aShouldContinue);
|
||||
|
||||
// This is a blocking feature.
|
||||
*aShouldContinue = false;
|
||||
|
||||
UrlClassifierCommon::SetBlockedContent(aChannel, NS_ERROR_TRACKING_URI, aList,
|
||||
EmptyCString(), EmptyCString());
|
||||
|
||||
UC_LOG(
|
||||
("UrlClassifierFeatureCryptomining::ProcessChannel, cancelling "
|
||||
"channel[%p]",
|
||||
aChannel));
|
||||
nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aChannel);
|
||||
|
||||
// FIXME: the way we cancel the channel depends on what the UI wants to show.
|
||||
// This needs to change, at some point.
|
||||
if (httpChannel) {
|
||||
Unused << httpChannel->CancelForTrackingProtection();
|
||||
} else {
|
||||
Unused << aChannel->Cancel(NS_ERROR_TRACKING_URI);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierFeatureCryptomining::GetURIByListType(
|
||||
nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
|
||||
nsIURI** aURI) {
|
||||
NS_ENSURE_ARG_POINTER(aChannel);
|
||||
NS_ENSURE_ARG_POINTER(aURI);
|
||||
|
||||
if (aListType == nsIUrlClassifierFeature::blacklist) {
|
||||
return aChannel->GetURI(aURI);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
|
||||
return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,45 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_net_UrlClassifierFeatureCryptomining_h
|
||||
#define mozilla_net_UrlClassifierFeatureCryptomining_h
|
||||
|
||||
#include "UrlClassifierFeatureBase.h"
|
||||
|
||||
class nsIChannel;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class UrlClassifierFeatureCryptomining final : public UrlClassifierFeatureBase {
|
||||
public:
|
||||
static const char* Name();
|
||||
|
||||
static void MaybeShutdown();
|
||||
|
||||
static already_AddRefed<UrlClassifierFeatureCryptomining> MaybeCreate(
|
||||
nsIChannel* aChannel);
|
||||
|
||||
static already_AddRefed<nsIUrlClassifierFeature> GetIfNameMatches(
|
||||
const nsACString& aName);
|
||||
|
||||
NS_IMETHOD ProcessChannel(nsIChannel* aChannel, const nsACString& aList,
|
||||
bool* aShouldContinue) override;
|
||||
|
||||
NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
|
||||
nsIUrlClassifierFeature::listType aListType,
|
||||
nsIURI** aURI) override;
|
||||
|
||||
private:
|
||||
UrlClassifierFeatureCryptomining();
|
||||
|
||||
static void MaybeInitialize();
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_UrlClassifierFeatureCryptomining_h
|
|
@ -7,6 +7,8 @@
|
|||
#include "mozilla/net/UrlClassifierFeatureFactory.h"
|
||||
|
||||
// List of Features
|
||||
#include "UrlClassifierFeatureCryptomining.h"
|
||||
#include "UrlClassifierFeatureFingerprinting.h"
|
||||
#include "UrlClassifierFeatureFlash.h"
|
||||
#include "UrlClassifierFeatureLoginReputation.h"
|
||||
#include "UrlClassifierFeatureTrackingProtection.h"
|
||||
|
@ -24,6 +26,8 @@ namespace net {
|
|||
return;
|
||||
}
|
||||
|
||||
UrlClassifierFeatureCryptomining::MaybeShutdown();
|
||||
UrlClassifierFeatureFingerprinting::MaybeShutdown();
|
||||
UrlClassifierFeatureFlash::MaybeShutdown();
|
||||
UrlClassifierFeatureLoginReputation::MaybeShutdown();
|
||||
UrlClassifierFeatureTrackingAnnotation::MaybeShutdown();
|
||||
|
@ -43,6 +47,18 @@ namespace net {
|
|||
// feature order, and this could produce different results with a different
|
||||
// feature ordering.
|
||||
|
||||
// Cryptomining
|
||||
feature = UrlClassifierFeatureCryptomining::MaybeCreate(aChannel);
|
||||
if (feature) {
|
||||
aFeatures.AppendElement(feature);
|
||||
}
|
||||
|
||||
// Fingerprinting
|
||||
feature = UrlClassifierFeatureFingerprinting::MaybeCreate(aChannel);
|
||||
if (feature) {
|
||||
aFeatures.AppendElement(feature);
|
||||
}
|
||||
|
||||
// Tracking Protection
|
||||
feature = UrlClassifierFeatureTrackingProtection::MaybeCreate(aChannel);
|
||||
if (feature) {
|
||||
|
@ -75,6 +91,18 @@ UrlClassifierFeatureFactory::GetFeatureByName(const nsACString& aName) {
|
|||
|
||||
nsCOMPtr<nsIUrlClassifierFeature> feature;
|
||||
|
||||
// Cryptomining
|
||||
feature = UrlClassifierFeatureCryptomining::GetIfNameMatches(aName);
|
||||
if (feature) {
|
||||
return feature.forget();
|
||||
}
|
||||
|
||||
// Fingerprinting
|
||||
feature = UrlClassifierFeatureFingerprinting::GetIfNameMatches(aName);
|
||||
if (feature) {
|
||||
return feature.forget();
|
||||
}
|
||||
|
||||
// Tracking Protection
|
||||
feature = UrlClassifierFeatureTrackingProtection::GetIfNameMatches(aName);
|
||||
if (feature) {
|
||||
|
@ -102,6 +130,49 @@ UrlClassifierFeatureFactory::GetFeatureByName(const nsACString& aName) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
/* static */ void UrlClassifierFeatureFactory::GetFeatureNames(
|
||||
nsTArray<nsCString>& aArray) {
|
||||
if (!XRE_IsParentProcess()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Cryptomining
|
||||
nsAutoCString name;
|
||||
name.Assign(UrlClassifierFeatureCryptomining::Name());
|
||||
if (!name.IsEmpty()) {
|
||||
aArray.AppendElement(name);
|
||||
}
|
||||
|
||||
// Fingerprinting
|
||||
name.Assign(UrlClassifierFeatureFingerprinting::Name());
|
||||
if (!name.IsEmpty()) {
|
||||
aArray.AppendElement(name);
|
||||
}
|
||||
|
||||
// Tracking Protection
|
||||
name.Assign(UrlClassifierFeatureTrackingProtection::Name());
|
||||
if (!name.IsEmpty()) {
|
||||
aArray.AppendElement(name);
|
||||
}
|
||||
|
||||
// Tracking Annotation
|
||||
name.Assign(UrlClassifierFeatureTrackingAnnotation::Name());
|
||||
if (!name.IsEmpty()) {
|
||||
aArray.AppendElement(name);
|
||||
}
|
||||
|
||||
// Login reputation
|
||||
name.Assign(UrlClassifierFeatureLoginReputation::Name());
|
||||
if (!name.IsEmpty()) {
|
||||
aArray.AppendElement(name);
|
||||
}
|
||||
|
||||
// Flash features
|
||||
nsTArray<nsCString> features;
|
||||
UrlClassifierFeatureFlash::GetFeatureNames(features);
|
||||
aArray.AppendElements(features);
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsIUrlClassifierFeature>
|
||||
UrlClassifierFeatureFactory::CreateFeatureWithTables(
|
||||
const nsACString& aName, const nsTArray<nsCString>& aBlacklistTables,
|
||||
|
|
|
@ -29,6 +29,8 @@ class UrlClassifierFeatureFactory final {
|
|||
static already_AddRefed<nsIUrlClassifierFeature> GetFeatureByName(
|
||||
const nsACString& aFeatureName);
|
||||
|
||||
static void GetFeatureNames(nsTArray<nsCString>& aArray);
|
||||
|
||||
static already_AddRefed<nsIUrlClassifierFeature> CreateFeatureWithTables(
|
||||
const nsACString& aName, const nsTArray<nsCString>& aBlacklistTables,
|
||||
const nsTArray<nsCString>& aWhitelistTables);
|
||||
|
|
|
@ -0,0 +1,177 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* 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 "UrlClassifierFeatureFingerprinting.h"
|
||||
|
||||
#include "mozilla/AntiTrackingCommon.h"
|
||||
#include "mozilla/net/UrlClassifierCommon.h"
|
||||
#include "mozilla/StaticPrefs.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsNetUtil.h"
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
namespace {
|
||||
|
||||
#define FINGERPRINTING_FEATURE_NAME "fingerprinting"
|
||||
|
||||
#define URLCLASSIFIER_FINGERPRINTING_BLACKLIST \
|
||||
"urlclassifier.features.fingerprinting.blacklistTables"
|
||||
#define URLCLASSIFIER_FINGERPRINTING_BLACKLIST_TEST_ENTRIES \
|
||||
"urlclassifier.features.fingerprinting.blacklistHosts"
|
||||
#define URLCLASSIFIER_FINGERPRINTING_WHITELIST \
|
||||
"urlclassifier.features.fingerprinting.whitelistTables"
|
||||
#define URLCLASSIFIER_FINGERPRINTING_WHITELIST_TEST_ENTRIES \
|
||||
"urlclassifier.features.fingerprinting.whitelistHosts"
|
||||
#define TABLE_FINGERPRINTING_BLACKLIST_PREF "fingerprinting-blacklist-pref"
|
||||
#define TABLE_FINGERPRINTING_WHITELIST_PREF "fingerprinting-whitelist-pref"
|
||||
|
||||
StaticRefPtr<UrlClassifierFeatureFingerprinting> gFeatureFingerprinting;
|
||||
|
||||
} // namespace
|
||||
|
||||
UrlClassifierFeatureFingerprinting::UrlClassifierFeatureFingerprinting()
|
||||
: UrlClassifierFeatureBase(
|
||||
NS_LITERAL_CSTRING(FINGERPRINTING_FEATURE_NAME),
|
||||
NS_LITERAL_CSTRING(URLCLASSIFIER_FINGERPRINTING_BLACKLIST),
|
||||
NS_LITERAL_CSTRING(URLCLASSIFIER_FINGERPRINTING_WHITELIST),
|
||||
NS_LITERAL_CSTRING(
|
||||
URLCLASSIFIER_FINGERPRINTING_BLACKLIST_TEST_ENTRIES),
|
||||
NS_LITERAL_CSTRING(
|
||||
URLCLASSIFIER_FINGERPRINTING_WHITELIST_TEST_ENTRIES),
|
||||
NS_LITERAL_CSTRING(TABLE_FINGERPRINTING_BLACKLIST_PREF),
|
||||
NS_LITERAL_CSTRING(TABLE_FINGERPRINTING_WHITELIST_PREF),
|
||||
EmptyCString()) {}
|
||||
|
||||
/* static */ const char* UrlClassifierFeatureFingerprinting::Name() {
|
||||
return FINGERPRINTING_FEATURE_NAME;
|
||||
}
|
||||
|
||||
/* static */ void UrlClassifierFeatureFingerprinting::MaybeInitialize() {
|
||||
UC_LOG(("UrlClassifierFeatureFingerprinting: MaybeInitialize"));
|
||||
|
||||
if (!gFeatureFingerprinting) {
|
||||
gFeatureFingerprinting = new UrlClassifierFeatureFingerprinting();
|
||||
gFeatureFingerprinting->InitializePreferences();
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void UrlClassifierFeatureFingerprinting::MaybeShutdown() {
|
||||
UC_LOG(("UrlClassifierFeatureFingerprinting: MaybeShutdown"));
|
||||
|
||||
if (gFeatureFingerprinting) {
|
||||
gFeatureFingerprinting->ShutdownPreferences();
|
||||
gFeatureFingerprinting = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<UrlClassifierFeatureFingerprinting>
|
||||
UrlClassifierFeatureFingerprinting::MaybeCreate(nsIChannel* aChannel) {
|
||||
MOZ_ASSERT(aChannel);
|
||||
|
||||
UC_LOG(("UrlClassifierFeatureFingerprinting: MaybeCreate for channel %p",
|
||||
aChannel));
|
||||
|
||||
if (!StaticPrefs::privacy_trackingprotection_fingerprinting_enabled()) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIURI> chanURI;
|
||||
nsresult rv = aChannel->GetURI(getter_AddRefs(chanURI));
|
||||
if (NS_WARN_IF(NS_FAILED(rv))) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool isThirdParty =
|
||||
nsContentUtils::IsThirdPartyWindowOrChannel(nullptr, aChannel, chanURI);
|
||||
if (!isThirdParty) {
|
||||
if (UC_LOG_ENABLED()) {
|
||||
nsCString spec = chanURI->GetSpecOrDefault();
|
||||
spec.Truncate(
|
||||
std::min(spec.Length(), UrlClassifierCommon::sMaxSpecLength));
|
||||
UC_LOG(
|
||||
("UrlClassifierFeatureFingerprinting: Skipping fingerprinting checks "
|
||||
"for first party or top-level load channel[%p] "
|
||||
"with uri %s",
|
||||
aChannel, spec.get()));
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (!UrlClassifierCommon::ShouldEnableClassifier(
|
||||
aChannel, AntiTrackingCommon::eFingerprinting)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MaybeInitialize();
|
||||
MOZ_ASSERT(gFeatureFingerprinting);
|
||||
|
||||
RefPtr<UrlClassifierFeatureFingerprinting> self = gFeatureFingerprinting;
|
||||
return self.forget();
|
||||
}
|
||||
|
||||
/* static */ already_AddRefed<nsIUrlClassifierFeature>
|
||||
UrlClassifierFeatureFingerprinting::GetIfNameMatches(const nsACString& aName) {
|
||||
if (!aName.EqualsLiteral(FINGERPRINTING_FEATURE_NAME)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
MaybeInitialize();
|
||||
MOZ_ASSERT(gFeatureFingerprinting);
|
||||
|
||||
RefPtr<UrlClassifierFeatureFingerprinting> self = gFeatureFingerprinting;
|
||||
return self.forget();
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierFeatureFingerprinting::ProcessChannel(nsIChannel* aChannel,
|
||||
const nsACString& aList,
|
||||
bool* aShouldContinue) {
|
||||
NS_ENSURE_ARG_POINTER(aChannel);
|
||||
NS_ENSURE_ARG_POINTER(aShouldContinue);
|
||||
|
||||
// This is a blocking feature.
|
||||
*aShouldContinue = false;
|
||||
|
||||
UrlClassifierCommon::SetBlockedContent(aChannel, NS_ERROR_TRACKING_URI, aList,
|
||||
EmptyCString(), EmptyCString());
|
||||
|
||||
UC_LOG(
|
||||
("UrlClassifierFeatureFingerprinting::ProcessChannel, cancelling "
|
||||
"channel[%p]",
|
||||
aChannel));
|
||||
nsCOMPtr<nsIHttpChannelInternal> httpChannel = do_QueryInterface(aChannel);
|
||||
|
||||
// FIXME: the way we cancel the channel depends on what the UI wants to show.
|
||||
// This needs to change, at some point.
|
||||
if (httpChannel) {
|
||||
Unused << httpChannel->CancelForTrackingProtection();
|
||||
} else {
|
||||
Unused << aChannel->Cancel(NS_ERROR_TRACKING_URI);
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierFeatureFingerprinting::GetURIByListType(
|
||||
nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
|
||||
nsIURI** aURI) {
|
||||
NS_ENSURE_ARG_POINTER(aChannel);
|
||||
NS_ENSURE_ARG_POINTER(aURI);
|
||||
|
||||
if (aListType == nsIUrlClassifierFeature::blacklist) {
|
||||
return aChannel->GetURI(aURI);
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist);
|
||||
return UrlClassifierCommon::CreatePairwiseWhiteListURI(aChannel, aURI);
|
||||
}
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
|
@ -0,0 +1,46 @@
|
|||
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
||||
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifndef mozilla_net_UrlClassifierFeatureFingerprinting_h
|
||||
#define mozilla_net_UrlClassifierFeatureFingerprinting_h
|
||||
|
||||
#include "UrlClassifierFeatureBase.h"
|
||||
|
||||
class nsIChannel;
|
||||
|
||||
namespace mozilla {
|
||||
namespace net {
|
||||
|
||||
class UrlClassifierFeatureFingerprinting final
|
||||
: public UrlClassifierFeatureBase {
|
||||
public:
|
||||
static const char* Name();
|
||||
|
||||
static void MaybeShutdown();
|
||||
|
||||
static already_AddRefed<UrlClassifierFeatureFingerprinting> MaybeCreate(
|
||||
nsIChannel* aChannel);
|
||||
|
||||
static already_AddRefed<nsIUrlClassifierFeature> GetIfNameMatches(
|
||||
const nsACString& aName);
|
||||
|
||||
NS_IMETHOD ProcessChannel(nsIChannel* aChannel, const nsACString& aList,
|
||||
bool* aShouldContinue) override;
|
||||
|
||||
NS_IMETHOD GetURIByListType(nsIChannel* aChannel,
|
||||
nsIUrlClassifierFeature::listType aListType,
|
||||
nsIURI** aURI) override;
|
||||
|
||||
private:
|
||||
UrlClassifierFeatureFingerprinting();
|
||||
|
||||
static void MaybeInitialize();
|
||||
};
|
||||
|
||||
} // namespace net
|
||||
} // namespace mozilla
|
||||
|
||||
#endif // mozilla_net_UrlClassifierFeatureFingerprinting_h
|
|
@ -55,6 +55,15 @@ UrlClassifierFeatureFlash::UrlClassifierFeatureFlash(uint32_t aId)
|
|||
"nsIHttpChannel::FlashPluginLastValue is out-of-sync!");
|
||||
}
|
||||
|
||||
/* static */ void UrlClassifierFeatureFlash::GetFeatureNames(
|
||||
nsTArray<nsCString>& aArray) {
|
||||
uint32_t numFeatures =
|
||||
(sizeof(sFlashFeaturesMap) / sizeof(sFlashFeaturesMap[0]));
|
||||
for (uint32_t i = 0; i < numFeatures; ++i) {
|
||||
aArray.AppendElement(nsDependentCString(sFlashFeaturesMap[i].mName));
|
||||
}
|
||||
}
|
||||
|
||||
/* static */ void UrlClassifierFeatureFlash::MaybeInitialize() {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@ namespace net {
|
|||
|
||||
class UrlClassifierFeatureFlash final : public UrlClassifierFeatureBase {
|
||||
public:
|
||||
static void GetFeatureNames(nsTArray<nsCString>& aNames);
|
||||
|
||||
static void MaybeShutdown();
|
||||
|
||||
static void MaybeCreate(
|
||||
|
|
|
@ -33,6 +33,12 @@ UrlClassifierFeatureLoginReputation::UrlClassifierFeatureLoginReputation()
|
|||
EmptyCString()) // skip host pref
|
||||
{}
|
||||
|
||||
/* static */ const char* UrlClassifierFeatureLoginReputation::Name() {
|
||||
return StaticPrefs::browser_safebrowsing_passwords_enabled()
|
||||
? LOGIN_REPUTATION_FEATURE_NAME
|
||||
: "";
|
||||
}
|
||||
|
||||
/* static */ void UrlClassifierFeatureLoginReputation::MaybeShutdown() {
|
||||
UC_LOG(("UrlClassifierFeatureLoginReputation: MaybeShutdown"));
|
||||
|
||||
|
@ -76,36 +82,6 @@ UrlClassifierFeatureLoginReputation::ProcessChannel(nsIChannel* aChannel,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierFeatureLoginReputation::GetTables(
|
||||
nsIUrlClassifierFeature::listType aListType, nsTArray<nsCString>& aTables) {
|
||||
MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist,
|
||||
"UrlClassifierFeatureLoginReputation is meant to be used just to "
|
||||
"whitelist URLs");
|
||||
return UrlClassifierFeatureBase::GetTables(aListType, aTables);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierFeatureLoginReputation::HasTable(
|
||||
const nsACString& aTable, nsIUrlClassifierFeature::listType aListType,
|
||||
bool* aResult) {
|
||||
MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist,
|
||||
"UrlClassifierFeatureLoginReputation is meant to be used just to "
|
||||
"whitelist URLs");
|
||||
return UrlClassifierFeatureBase::HasTable(aTable, aListType, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierFeatureLoginReputation::HasHostInPreferences(
|
||||
const nsACString& aHost, nsIUrlClassifierFeature::listType aListType,
|
||||
nsACString& aPrefTableName, bool* aResult) {
|
||||
MOZ_ASSERT(aListType == nsIUrlClassifierFeature::whitelist,
|
||||
"UrlClassifierFeatureLoginReputation is meant to be used just to "
|
||||
"whitelist URLs");
|
||||
return UrlClassifierFeatureBase::HasHostInPreferences(
|
||||
aHost, aListType, aPrefTableName, aResult);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
UrlClassifierFeatureLoginReputation::GetURIByListType(
|
||||
nsIChannel* aChannel, nsIUrlClassifierFeature::listType aListType,
|
||||
|
|
|
@ -17,6 +17,8 @@ namespace net {
|
|||
class UrlClassifierFeatureLoginReputation final
|
||||
: public UrlClassifierFeatureBase {
|
||||
public:
|
||||
static const char* Name();
|
||||
|
||||
static void MaybeShutdown();
|
||||
|
||||
static nsIUrlClassifierFeature* MaybeGetOrCreate();
|
||||
|
@ -24,19 +26,6 @@ class UrlClassifierFeatureLoginReputation final
|
|||
static already_AddRefed<nsIUrlClassifierFeature> GetIfNameMatches(
|
||||
const nsACString& aName);
|
||||
|
||||
NS_IMETHOD
|
||||
GetTables(nsIUrlClassifierFeature::listType aListType,
|
||||
nsTArray<nsCString>& aResult) override;
|
||||
|
||||
NS_IMETHOD
|
||||
HasTable(const nsACString& aTable,
|
||||
nsIUrlClassifierFeature::listType aListType, bool* aResult) override;
|
||||
|
||||
NS_IMETHOD
|
||||
HasHostInPreferences(const nsACString& aHost,
|
||||
nsIUrlClassifierFeature::listType aListType,
|
||||
nsACString& aPrefTableName, bool* aResult) override;
|
||||
|
||||
NS_IMETHOD ProcessChannel(nsIChannel* aChannel, const nsACString& aList,
|
||||
bool* aShouldContinue) override;
|
||||
|
||||
|
|
|
@ -120,6 +120,10 @@ UrlClassifierFeatureTrackingAnnotation::UrlClassifierFeatureTrackingAnnotation()
|
|||
NS_LITERAL_CSTRING(TABLE_ANNOTATION_WHITELIST_PREF),
|
||||
NS_LITERAL_CSTRING(URLCLASSIFIER_TRACKING_ANNOTATION_SKIP_URLS)) {}
|
||||
|
||||
/* static */ const char* UrlClassifierFeatureTrackingAnnotation::Name() {
|
||||
return TRACKING_ANNOTATION_FEATURE_NAME;
|
||||
}
|
||||
|
||||
/* static */ void UrlClassifierFeatureTrackingAnnotation::MaybeInitialize() {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
UC_LOG(("UrlClassifierFeatureTrackingAnnotation: MaybeInitialize"));
|
||||
|
@ -150,7 +154,7 @@ UrlClassifierFeatureTrackingAnnotation::MaybeCreate(nsIChannel* aChannel) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (!UrlClassifierCommon::ShouldEnableTrackingProtectionOrAnnotation(
|
||||
if (!UrlClassifierCommon::ShouldEnableClassifier(
|
||||
aChannel, AntiTrackingCommon::eTrackingAnnotations)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ namespace net {
|
|||
class UrlClassifierFeatureTrackingAnnotation final
|
||||
: public UrlClassifierFeatureBase {
|
||||
public:
|
||||
static const char* Name();
|
||||
|
||||
static void MaybeShutdown();
|
||||
|
||||
static already_AddRefed<UrlClassifierFeatureTrackingAnnotation> MaybeCreate(
|
||||
|
|
|
@ -42,6 +42,10 @@ UrlClassifierFeatureTrackingProtection::UrlClassifierFeatureTrackingProtection()
|
|||
NS_LITERAL_CSTRING(TABLE_TRACKING_BLACKLIST_PREF),
|
||||
NS_LITERAL_CSTRING(TABLE_TRACKING_WHITELIST_PREF), EmptyCString()) {}
|
||||
|
||||
/* static */ const char* UrlClassifierFeatureTrackingProtection::Name() {
|
||||
return TRACKING_PROTECTION_FEATURE_NAME;
|
||||
}
|
||||
|
||||
/* static */ void UrlClassifierFeatureTrackingProtection::MaybeInitialize() {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
UC_LOG(("UrlClassifierFeatureTrackingProtection: MaybeInitialize"));
|
||||
|
@ -97,7 +101,7 @@ UrlClassifierFeatureTrackingProtection::MaybeCreate(nsIChannel* aChannel) {
|
|||
return nullptr;
|
||||
}
|
||||
|
||||
if (!UrlClassifierCommon::ShouldEnableTrackingProtectionOrAnnotation(
|
||||
if (!UrlClassifierCommon::ShouldEnableClassifier(
|
||||
aChannel, AntiTrackingCommon::eTrackingProtection)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
@ -17,6 +17,8 @@ namespace net {
|
|||
class UrlClassifierFeatureTrackingProtection final
|
||||
: public UrlClassifierFeatureBase {
|
||||
public:
|
||||
static const char* Name();
|
||||
|
||||
static void MaybeShutdown();
|
||||
|
||||
static already_AddRefed<UrlClassifierFeatureTrackingProtection> MaybeCreate(
|
||||
|
|
|
@ -22,8 +22,10 @@ UNIFIED_SOURCES += [
|
|||
'nsChannelClassifier.cpp',
|
||||
'UrlClassifierCommon.cpp',
|
||||
'UrlClassifierFeatureBase.cpp',
|
||||
'UrlClassifierFeatureCryptomining.cpp',
|
||||
'UrlClassifierFeatureCustomTables.cpp',
|
||||
'UrlClassifierFeatureFactory.cpp',
|
||||
'UrlClassifierFeatureFingerprinting.cpp',
|
||||
'UrlClassifierFeatureFlash.cpp',
|
||||
'UrlClassifierFeatureLoginReputation.cpp',
|
||||
'UrlClassifierFeatureResult.cpp',
|
||||
|
|
|
@ -95,6 +95,11 @@ interface nsIURIClassifier : nsISupports
|
|||
*/
|
||||
nsIUrlClassifierFeature getFeatureByName(in ACString aFeatureName);
|
||||
|
||||
/**
|
||||
* Returns all the feature names.
|
||||
*/
|
||||
Array<ACString> getFeatureNames();
|
||||
|
||||
/**
|
||||
* Create a new feature with a list of tables. This method is just for
|
||||
* testing! Don't use it elsewhere.
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -17,98 +17,11 @@
|
|||
@import url("chrome://global/skin/in-content/common.css");
|
||||
|
||||
html {
|
||||
--aboutSupport-table-background: #ebebeb;
|
||||
background-color: var(--in-content-page-background);
|
||||
}
|
||||
body {
|
||||
margin: 40px 48px;
|
||||
}
|
||||
.hidden {
|
||||
display: none;
|
||||
}
|
||||
.summary .title {
|
||||
font-weight: bold;
|
||||
}
|
||||
a {
|
||||
text-decoration: none;
|
||||
}
|
||||
a.more {
|
||||
margin-left: 2ch;
|
||||
}
|
||||
ul.hidden_additional_items {
|
||||
padding-top: 0;
|
||||
margin-top: 0;
|
||||
}
|
||||
ul.visible_items {
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
li.delta {
|
||||
margin-top: .5em;
|
||||
}
|
||||
h2 {
|
||||
font-size: 1.46em;
|
||||
font-weight: 300;
|
||||
line-height: 1.3em;
|
||||
margin: 16px 0;
|
||||
-moz-user-select: none;
|
||||
}
|
||||
button.show_all_items {
|
||||
margin-top: .5cm;
|
||||
margin-left: 1cm;
|
||||
}
|
||||
body {
|
||||
margin-left: 1cm;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
div.measuring {
|
||||
background: url(chrome://global/skin/media/throbber.png) no-repeat center;
|
||||
min-width: 36px;
|
||||
min-height: 36px;
|
||||
}
|
||||
li.delta {
|
||||
border-left-width: 5px;
|
||||
border-left-style: solid;
|
||||
padding-left: 1em;
|
||||
list-style: none;
|
||||
}
|
||||
li.delta[impact="0"] {
|
||||
border-left-color: rgb(0, 255, 0);
|
||||
}
|
||||
li.delta[impact="1"] {
|
||||
border-left-color: rgb(24, 231, 0);
|
||||
}
|
||||
li.delta[impact="2"] {
|
||||
border-left-color: rgb(48, 207, 0);
|
||||
}
|
||||
li.delta[impact="3"] {
|
||||
border-left-color: rgb(72, 183, 0);
|
||||
}
|
||||
li.delta[impact="4"] {
|
||||
border-left-color: rgb(96, 159, 0);
|
||||
}
|
||||
li.delta[impact="5"] {
|
||||
border-left-color: rgb(120, 135, 0);
|
||||
}
|
||||
li.delta[impact="6"] {
|
||||
border-left-color: rgb(144, 111, 0);
|
||||
}
|
||||
li.delta[impact="7"] {
|
||||
border-left-color: rgb(168, 87, 0);
|
||||
}
|
||||
li.delta[impact="8"] {
|
||||
border-left-color: rgb(192, 63, 0);
|
||||
}
|
||||
li.delta[impact="9"] {
|
||||
border-left-color: rgb(216, 39, 0);
|
||||
}
|
||||
li.delta[impact="10"] {
|
||||
border-left-color: rgb(240, 15, 0);
|
||||
}
|
||||
li.delta[impact="11"] {
|
||||
border-left-color: rgb(255, 0, 0);
|
||||
}
|
||||
|
||||
#dispatch-table {
|
||||
-moz-user-select: none;
|
||||
font-size: 1em;
|
||||
|
@ -135,24 +48,24 @@
|
|||
border-bottom: 1px solid var(--in-content-border-color);
|
||||
min-width: 40em;
|
||||
}
|
||||
#dispatch-thead, #dispatch-tbody tr {
|
||||
tr {
|
||||
display: table;
|
||||
table-layout: fixed;
|
||||
width: 100%;
|
||||
}
|
||||
#dispatch-table td:nth-child(2) {
|
||||
td:nth-child(2) {
|
||||
width: 8em;
|
||||
}
|
||||
#dispatch-table td:nth-child(3) {
|
||||
td:nth-child(3) {
|
||||
width: 9em;
|
||||
}
|
||||
#dispatch-table td:nth-child(4) {
|
||||
td:nth-child(4) {
|
||||
width: 5em;
|
||||
}
|
||||
#dispatch-tbody td:nth-child(4) {
|
||||
text-align: end;
|
||||
}
|
||||
#dispatch-table td:nth-child(5) {
|
||||
td:nth-child(5) {
|
||||
width: 20px;
|
||||
}
|
||||
|
||||
|
@ -219,12 +132,12 @@
|
|||
transform: scale(1.2);
|
||||
}
|
||||
|
||||
#dispatch-table > thead > tr > td {
|
||||
#dispatch-thead > tr > td {
|
||||
border: none;
|
||||
background-color: var(--in-content-box-background-hover);
|
||||
padding: 5px 10px;
|
||||
}
|
||||
#dispatch-table > thead > tr > td:not(:first-child) {
|
||||
#dispatch-thead > tr > td:not(:first-child) {
|
||||
border-inline-start-width: 1px;
|
||||
border-inline-start-style: solid;
|
||||
border-image: linear-gradient(transparent 0%, transparent 20%, #c1c1c1 20%, #c1c1c1 80%, transparent 80%, transparent 100%) 1 1;
|
||||
|
@ -304,47 +217,20 @@
|
|||
#dispatch-tbody > tr:hover {
|
||||
background-color: var(--in-content-item-hover);
|
||||
}
|
||||
|
||||
.options {
|
||||
width: 100%;
|
||||
}
|
||||
.options > .toggle-container-with-text {
|
||||
display: inline-flex;
|
||||
}
|
||||
.options > .toggle-container-with-text:not(:first-child) {
|
||||
margin-inline-start: 2ch;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body onload="go()">
|
||||
<div class="options">
|
||||
<div class="toggle-container-with-text">
|
||||
<input type="checkbox" checked="false" id="check-display-recent" role="checkbox"></input>
|
||||
<label for="check-display-recent" id="label-display-recent">Display only the last few seconds.</label>
|
||||
</div>
|
||||
<div class="toggle-container-with-text">
|
||||
<input type="checkbox" checked="true" id="check-autorefresh" role="checkbox"></input>
|
||||
<label for="check-autorefresh">Refresh automatically</label>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h2>Performance of Web pages</h2>
|
||||
<div id="webpages" class="measuring">
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<table id="dispatch-table">
|
||||
<thead id="dispatch-thead">
|
||||
<tr>
|
||||
<td data-l10n-id="column-name"/>
|
||||
<td data-l10n-id="column-type"/>
|
||||
<td data-l10n-id="column-energy-impact"/>
|
||||
<td data-l10n-id="column-memory"/>
|
||||
<td></td><!-- actions -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dispatch-tbody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<table id="dispatch-table">
|
||||
<thead id="dispatch-thead">
|
||||
<tr>
|
||||
<td data-l10n-id="column-name"/>
|
||||
<td data-l10n-id="column-type"/>
|
||||
<td data-l10n-id="column-energy-impact"/>
|
||||
<td data-l10n-id="column-memory"/>
|
||||
<td></td><!-- actions -->
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="dispatch-tbody"></tbody>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,9 +1,7 @@
|
|||
[DEFAULT]
|
||||
head = head.js
|
||||
support-files =
|
||||
browser_compartments.html
|
||||
browser_compartments_frame.html
|
||||
browser_compartments_script.js
|
||||
|
||||
[browser_aboutperformance.js]
|
||||
skip-if = true
|
||||
|
|
|
@ -1,308 +1,64 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
/* eslint-env mozilla/frame-script */
|
||||
/* eslint-disable mozilla/no-arbitrary-setTimeout */
|
||||
|
||||
"use strict";
|
||||
|
||||
ChromeUtils.import("resource://testing-common/ContentTask.jsm", this);
|
||||
|
||||
const URL = "http://example.com/browser/toolkit/components/aboutperformance/tests/browser/browser_compartments.html?test=" + Math.random();
|
||||
|
||||
// This function is injected as source as a frameScript
|
||||
function frameScript() {
|
||||
"use strict";
|
||||
|
||||
addMessageListener("aboutperformance-test:done", () => {
|
||||
content.postMessage("stop", "*");
|
||||
sendAsyncMessage("aboutperformance-test:done", null);
|
||||
});
|
||||
addMessageListener("aboutperformance-test:setTitle", ({data: title}) => {
|
||||
content.document.title = title;
|
||||
sendAsyncMessage("aboutperformance-test:setTitle", null);
|
||||
});
|
||||
|
||||
addMessageListener("aboutperformance-test:closeTab", ({data: options}) => {
|
||||
let observer = function(subject, topic, mode) {
|
||||
dump(`aboutperformance-test:closeTab 1 ${options.url}\n`);
|
||||
Services.obs.removeObserver(observer, "about:performance-update-complete");
|
||||
|
||||
let exn;
|
||||
let found = false;
|
||||
try {
|
||||
for (let eltContent of content.document.querySelectorAll("li.delta")) {
|
||||
let eltName = eltContent.querySelector("li.name");
|
||||
if (!eltName.textContent.includes(options.url)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
found = true;
|
||||
let [eltCloseTab, eltReloadTab] = eltContent.querySelectorAll("button");
|
||||
let button;
|
||||
if (options.mode == "reload") {
|
||||
button = eltReloadTab;
|
||||
} else if (options.mode == "close") {
|
||||
button = eltCloseTab;
|
||||
} else {
|
||||
throw new TypeError(options.mode);
|
||||
}
|
||||
dump(`aboutperformance-test:closeTab clicking on ${button.textContent}\n`);
|
||||
button.click();
|
||||
return;
|
||||
}
|
||||
} catch (ex) {
|
||||
dump(`aboutperformance-test:closeTab: error ${ex}\n`);
|
||||
exn = ex;
|
||||
} finally {
|
||||
if (exn) {
|
||||
sendAsyncMessage("aboutperformance-test:closeTab", { error: {message: exn.message, lineNumber: exn.lineNumber, fileName: exn.fileName}, found});
|
||||
} else {
|
||||
sendAsyncMessage("aboutperformance-test:closeTab", { ok: true, found });
|
||||
}
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(observer, "about:performance-update-complete");
|
||||
Services.obs.notifyObservers(null, "test-about:performance-test-driver", JSON.stringify(options));
|
||||
});
|
||||
|
||||
addMessageListener("aboutperformance-test:checkSanity", ({data: options}) => {
|
||||
let exn = null;
|
||||
try {
|
||||
let reFullname = /Full name: (.+)/;
|
||||
let reFps = /Impact on framerate: ((\d+) high-impacts, (\d+) medium-impact|(\d+)\/10)?/;
|
||||
let reCPU = /CPU usage: (\d+)%/;
|
||||
let reCpow = /Blocking process calls: (\d+)%( \((\d+) alerts\))?/;
|
||||
|
||||
let getContentOfSelector = function(eltContainer, selector, re) {
|
||||
let elt = eltContainer.querySelector(selector);
|
||||
if (!elt) {
|
||||
throw new Error(`No item ${selector}`);
|
||||
}
|
||||
|
||||
if (!re) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
let match = elt.textContent.match(re);
|
||||
if (!match) {
|
||||
throw new Error(`Item ${selector} doesn't match regexp ${re}: ${elt.textContent}`);
|
||||
}
|
||||
return match;
|
||||
};
|
||||
|
||||
// Additional sanity check
|
||||
let deltas = content.document.querySelectorAll(".delta");
|
||||
if (!deltas.length) {
|
||||
throw new Error("No deltas found to check!");
|
||||
}
|
||||
|
||||
for (let eltContent of deltas) {
|
||||
// Do we have an attribute "impact"? Is it a number between 0 and 10?
|
||||
let impact = eltContent.getAttribute("impact");
|
||||
let value = Number.parseInt(impact);
|
||||
if (isNaN(value) || value < 0 || value > 10) {
|
||||
throw new Error(`Incorrect value ${value}`);
|
||||
}
|
||||
|
||||
// Do we have a button "more"?
|
||||
getContentOfSelector(eltContent, "a.more");
|
||||
|
||||
// Do we have details?
|
||||
getContentOfSelector(eltContent, "ul.details");
|
||||
|
||||
// Do we have a full name? Does it make sense?
|
||||
getContentOfSelector(eltContent, "li.name", reFullname);
|
||||
|
||||
let eltDetails = eltContent.querySelector("ul.details");
|
||||
|
||||
// Do we have an impact on framerate? Does it make sense?
|
||||
if (!eltDetails.querySelector("li.fps").textContent.includes("no impact")) {
|
||||
let [, jankStr,, alertsStr] = getContentOfSelector(eltDetails, "li.fps", reFps);
|
||||
let jank = Number.parseInt(jankStr);
|
||||
if (jank < 0 || jank > 10 || isNaN(jank)) {
|
||||
throw new Error(`Invalid jank ${jankStr}`);
|
||||
}
|
||||
if (alertsStr) {
|
||||
let alerts = Number.parseInt(alertsStr);
|
||||
if (alerts < 0 || isNaN(alerts)) {
|
||||
throw new Error(`Invalid alerts ${alertsStr}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Do we have a CPU usage? Does it make sense?
|
||||
let [, cpuStr] = getContentOfSelector(eltDetails, "li.cpu", reCPU);
|
||||
let cpu = Number.parseInt(cpuStr);
|
||||
if (cpu < 0 || isNaN(cpu)) { // Note that cpu can be > 100%.
|
||||
throw new Error(`Invalid CPU ${cpuStr}`);
|
||||
}
|
||||
|
||||
// Do we have CPOW? Does it make sense?
|
||||
let [, cpowStr,, alertsStr2] = getContentOfSelector(eltDetails, "li.cpow", reCpow);
|
||||
let cpow = Number.parseInt(cpowStr);
|
||||
if (cpow < 0 || isNaN(cpow)) {
|
||||
throw new Error(`Invalid cpow ${cpowStr}`);
|
||||
}
|
||||
if (alertsStr2) {
|
||||
let alerts = Number.parseInt(alertsStr2);
|
||||
if (alerts < 0 || isNaN(alerts)) {
|
||||
throw new Error(`Invalid alerts ${alertsStr2}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (ex) {
|
||||
dump(`aboutperformance-test:checkSanity: error ${ex}\n`);
|
||||
exn = ex;
|
||||
}
|
||||
if (exn) {
|
||||
sendAsyncMessage("aboutperformance-test:checkSanity", { error: {message: exn.message, lineNumber: exn.lineNumber, fileName: exn.fileName}});
|
||||
} else {
|
||||
sendAsyncMessage("aboutperformance-test:checkSanity", { ok: true });
|
||||
}
|
||||
});
|
||||
|
||||
addMessageListener("aboutperformance-test:hasItems", ({data: {title, options}}) => {
|
||||
let observer = function(subject, topic, mode) {
|
||||
Services.obs.removeObserver(observer, "about:performance-update-complete");
|
||||
let hasTitleInWebpages = false;
|
||||
|
||||
try {
|
||||
let eltWeb = content.document.getElementById("webpages");
|
||||
if (!eltWeb) {
|
||||
dump(`aboutperformance-test:hasItems: the page is not ready yet webpages:${eltWeb}\n`);
|
||||
return;
|
||||
}
|
||||
|
||||
let webTitles = Array.from(eltWeb.querySelectorAll("span.title"), elt => elt.textContent);
|
||||
|
||||
hasTitleInWebpages = webTitles.includes(title);
|
||||
} catch (ex) {
|
||||
Cu.reportError("Error in content: " + ex);
|
||||
Cu.reportError(ex.stack);
|
||||
} finally {
|
||||
sendAsyncMessage("aboutperformance-test:hasItems", {hasTitleInWebpages, mode});
|
||||
}
|
||||
};
|
||||
Services.obs.addObserver(observer, "about:performance-update-complete");
|
||||
Services.obs.notifyObservers(null, "test-about:performance-test-driver", JSON.stringify(options));
|
||||
});
|
||||
}
|
||||
|
||||
var gTabAboutPerformance = null;
|
||||
var gTabContent = null;
|
||||
|
||||
add_task(async function init() {
|
||||
info("Setting up about:performance");
|
||||
gTabAboutPerformance = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:performance");
|
||||
await ContentTask.spawn(gTabAboutPerformance.linkedBrowser, null, frameScript);
|
||||
let tabAboutPerformance = gBrowser.selectedTab = BrowserTestUtils.addTab(gBrowser, "about:performance");
|
||||
|
||||
await BrowserTestUtils.browserLoaded(tabAboutPerformance.linkedBrowser);
|
||||
|
||||
info(`Setting up ${URL}`);
|
||||
gTabContent = BrowserTestUtils.addTab(gBrowser, URL);
|
||||
await ContentTask.spawn(gTabContent.linkedBrowser, null, frameScript);
|
||||
});
|
||||
|
||||
var promiseExpectContent = async function(options) {
|
||||
let title = "Testing about:performance " + Math.random();
|
||||
for (let i = 0; i < 30; ++i) {
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
await promiseContentResponse(gTabContent.linkedBrowser, "aboutperformance-test:setTitle", title);
|
||||
let {hasTitleInWebpages, mode} = (await promiseContentResponse(gTabAboutPerformance.linkedBrowser, "aboutperformance-test:hasItems", {title, options}));
|
||||
|
||||
info(`aboutperformance-test:hasItems ${hasTitleInWebpages}, ${mode}, ${options.displayRecent}`);
|
||||
if (!hasTitleInWebpages) {
|
||||
info(`Title not found in webpages`);
|
||||
continue;
|
||||
}
|
||||
if ((mode == "recent") != options.displayRecent) {
|
||||
info(`Wrong mode`);
|
||||
continue;
|
||||
}
|
||||
|
||||
let { ok, error } = await promiseContentResponse(gTabAboutPerformance.linkedBrowser, "aboutperformance-test:checkSanity", {options});
|
||||
if (ok) {
|
||||
info("aboutperformance-test:checkSanity: success");
|
||||
}
|
||||
if (error) {
|
||||
Assert.ok(false, `aboutperformance-test:checkSanity error: ${JSON.stringify(error)}`);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// Test that we can find the title of a webpage in about:performance
|
||||
add_task(async function test_find_title() {
|
||||
for (let displayRecent of [true, false]) {
|
||||
info(`Testing with autoRefresh, in ${displayRecent ? "recent" : "global"} mode`);
|
||||
let found = await promiseExpectContent({autoRefresh: 100, displayRecent});
|
||||
Assert.ok(found, `The page title appears when about:performance is set to auto-refresh`);
|
||||
}
|
||||
});
|
||||
|
||||
// Test that we can close/reload tabs using the corresponding buttons
|
||||
add_task(async function test_close_tab() {
|
||||
let tabs = new Map();
|
||||
let closeObserver = function({type, originalTarget: tab}) {
|
||||
dump(`closeObserver: ${tab}, ${tab.constructor.name}, ${tab.tagName}, ${type}\n`);
|
||||
let cb = tabs.get(tab);
|
||||
if (cb) {
|
||||
cb(type);
|
||||
}
|
||||
};
|
||||
let promiseTabClosed = function(tab) {
|
||||
return new Promise(resolve => tabs.set(tab, resolve));
|
||||
};
|
||||
window.gBrowser.tabContainer.addEventListener("TabClose", closeObserver);
|
||||
let promiseTabReloaded = function(tab) {
|
||||
return new Promise(resolve =>
|
||||
tab.linkedBrowser.contentDocument.addEventListener("readystatechange", resolve)
|
||||
);
|
||||
};
|
||||
for (let displayRecent of [true, false]) {
|
||||
for (let mode of ["close", "reload"]) {
|
||||
let URL = `about:about?display-recent=${displayRecent}&mode=${mode}&salt=${Math.random()}`;
|
||||
info(`Setting up ${URL}`);
|
||||
let tab = BrowserTestUtils.addTab(gBrowser, URL);
|
||||
await ContentTask.spawn(tab.linkedBrowser, null, frameScript);
|
||||
let promiseClosed = promiseTabClosed(tab);
|
||||
let promiseReloaded = promiseTabReloaded(tab);
|
||||
|
||||
info(`Requesting close`);
|
||||
do {
|
||||
await new Promise(resolve => setTimeout(resolve, 100));
|
||||
await promiseContentResponse(tab.linkedBrowser, "aboutperformance-test:setTitle", URL);
|
||||
|
||||
let {ok, found, error} = await promiseContentResponse(gTabAboutPerformance.linkedBrowser, "aboutperformance-test:closeTab", {url: URL, autoRefresh: true, mode, displayRecent});
|
||||
Assert.ok(ok, `Message aboutperformance-test:closeTab was handled correctly ${JSON.stringify(error)}`);
|
||||
info(`URL ${URL} ${found ? "found" : "hasn't been found yet"}`);
|
||||
if (found) {
|
||||
break;
|
||||
}
|
||||
} while (true);
|
||||
|
||||
if (mode == "close") {
|
||||
info(`Waiting for close`);
|
||||
await promiseClosed;
|
||||
} else {
|
||||
info(`Waiting for reload`);
|
||||
await promiseReloaded;
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
add_task(async function cleanup() {
|
||||
// Cleanup
|
||||
info("Cleaning up");
|
||||
await promiseContentResponse(gTabAboutPerformance.linkedBrowser, "aboutperformance-test:done", null);
|
||||
|
||||
info("Closing tabs");
|
||||
for (let tab of gBrowser.tabs) {
|
||||
BrowserTestUtils.removeTab(tab);
|
||||
}
|
||||
|
||||
info("Done");
|
||||
gBrowser.selectedTab = null;
|
||||
let tabContent = BrowserTestUtils.addTab(gBrowser, URL);
|
||||
await BrowserTestUtils.browserLoaded(tabContent.linkedBrowser);
|
||||
|
||||
let doc = tabAboutPerformance.linkedBrowser.contentDocument;
|
||||
let tbody = doc.getElementById("dispatch-tbody");
|
||||
|
||||
// Wait until the table has first been populated.
|
||||
await TestUtils.waitForCondition(() => tbody.childElementCount);
|
||||
|
||||
// And wait for another update using a mutation observer, to give our newly created test tab some time
|
||||
// to burn some CPU.
|
||||
await new Promise(resolve => {
|
||||
let observer = new doc.ownerGlobal.MutationObserver(() => {
|
||||
observer.disconnect();
|
||||
resolve();
|
||||
});
|
||||
observer.observe(tbody, {childList: true});
|
||||
});
|
||||
|
||||
// Find the row for our test tab.
|
||||
let row = tbody.firstChild;
|
||||
while (row && row.firstChild.textContent != "Main frame for test browser_aboutperformance.js")
|
||||
row = row.nextSibling;
|
||||
Assert.ok(row, "found a table row for our test tab");
|
||||
Assert.equal(row.windowId, tabContent.linkedBrowser.outerWindowID, "the correct window id is set");
|
||||
|
||||
// Ensure it is reported as a medium or high energy impact.
|
||||
let l10nId = row.children[2].getAttribute("data-l10n-id");
|
||||
Assert.ok(["energy-impact-medium", "energy-impact-high"].includes(l10nId),
|
||||
"our test tab is medium or high energy impact");
|
||||
|
||||
// Verify selecting a row works.
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(row, {}, tabAboutPerformance.linkedBrowser);
|
||||
Assert.equal(row.getAttribute("selected"), "true", "doing a single click selects the row");
|
||||
|
||||
// Verify selecting a tab with a double click.
|
||||
Assert.equal(gBrowser.selectedTab, tabAboutPerformance, "the about:performance tab is selected");
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(row, {clickCount: 2}, tabAboutPerformance.linkedBrowser);
|
||||
Assert.equal(gBrowser.selectedTab, tabContent, "after a double click the test tab is selected");
|
||||
|
||||
// Verify we can close a tab using the X button.
|
||||
// Switch back to about:performance...
|
||||
await BrowserTestUtils.switchTab(gBrowser, tabAboutPerformance);
|
||||
// ... and click the X button at the end of the row.
|
||||
let tabClosing = BrowserTestUtils.waitForTabClosing(tabContent);
|
||||
await BrowserTestUtils.synthesizeMouseAtCenter(row.children[4], {}, tabAboutPerformance.linkedBrowser);
|
||||
await tabClosing;
|
||||
|
||||
BrowserTestUtils.removeTab(tabAboutPerformance);
|
||||
});
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>
|
||||
Main frame for test browser_aboutperformance.js
|
||||
</title>
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>
|
||||
Subframe for test browser_compartments.html (do not change this title)
|
||||
</title>
|
||||
|
|
|
@ -1,23 +1,6 @@
|
|||
|
||||
var carryOn = true;
|
||||
|
||||
window.addEventListener("message", e => {
|
||||
console.log("frame content", "message", e);
|
||||
if ("title" in e.data) {
|
||||
document.title = e.data.title;
|
||||
}
|
||||
if ("stop" in e.data) {
|
||||
carryOn = false;
|
||||
}
|
||||
});
|
||||
|
||||
// Use some CPU.
|
||||
var interval = window.setInterval(() => {
|
||||
if (!carryOn) {
|
||||
window.clearInterval(interval);
|
||||
return;
|
||||
}
|
||||
|
||||
// Compute an arbitrary value, print it out to make sure that the JS
|
||||
// engine doesn't discard all our computation.
|
||||
var date = Date.now();
|
||||
|
|
|
@ -1,50 +0,0 @@
|
|||
/* Any copyright is dedicated to the Public Domain.
|
||||
* http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
ChromeUtils.import("resource://gre/modules/Services.jsm", this);
|
||||
|
||||
function promiseContentResponse(browser, name, message) {
|
||||
let mm = browser.messageManager;
|
||||
let promise = new Promise(resolve => {
|
||||
function removeListener() {
|
||||
mm.removeMessageListener(name, listener);
|
||||
}
|
||||
|
||||
function listener(msg) {
|
||||
removeListener();
|
||||
resolve(msg.data);
|
||||
}
|
||||
|
||||
mm.addMessageListener(name, listener);
|
||||
registerCleanupFunction(removeListener);
|
||||
});
|
||||
mm.sendAsyncMessage(name, message);
|
||||
return promise;
|
||||
}
|
||||
function promiseContentResponseOrNull(browser, name, message) {
|
||||
if (!browser.messageManager) {
|
||||
return null;
|
||||
}
|
||||
return promiseContentResponse(browser, name, message);
|
||||
}
|
||||
|
||||
/**
|
||||
* `true` if we are running an OS in which the OS performance
|
||||
* clock has a low precision and might unpredictably
|
||||
* never be updated during the execution of the test.
|
||||
*/
|
||||
function hasLowPrecision() {
|
||||
let [sysName, sysVersion] = [Services.sysinfo.getPropertyAsAString("name"), Services.sysinfo.getPropertyAsDouble("version")];
|
||||
info(`Running ${sysName} version ${sysVersion}`);
|
||||
|
||||
if (sysName == "Windows_NT" && sysVersion < 6) {
|
||||
info("Running old Windows, need to deactivate tests due to bad precision.");
|
||||
return true;
|
||||
}
|
||||
if (sysName == "Linux" && sysVersion <= 2.6) {
|
||||
info("Running old Linux, need to deactivate tests due to bad precision.");
|
||||
return true;
|
||||
}
|
||||
info("This platform has good precision.");
|
||||
return false;
|
||||
}
|
|
@ -1372,7 +1372,11 @@ nsresult AntiTrackingCommon::IsOnContentBlockingAllowList(
|
|||
if ((aPurpose == eStorageChecks &&
|
||||
!StaticPrefs::browser_contentblocking_allowlist_storage_enabled()) ||
|
||||
(aPurpose == eTrackingAnnotations &&
|
||||
!StaticPrefs::browser_contentblocking_allowlist_annotations_enabled())) {
|
||||
!StaticPrefs::browser_contentblocking_allowlist_annotations_enabled()) ||
|
||||
(aPurpose == eFingerprinting &&
|
||||
!StaticPrefs::privacy_trackingprotection_fingerprinting_enabled()) ||
|
||||
(aPurpose == eCryptomining &&
|
||||
!StaticPrefs::privacy_trackingprotection_cryptomining_enabled())) {
|
||||
LOG(
|
||||
("Attempting to check the content blocking allow list aborted because "
|
||||
"the third-party cookies UI has been disabled."));
|
||||
|
|
|
@ -120,6 +120,8 @@ class AntiTrackingCommon final {
|
|||
eStorageChecks,
|
||||
eTrackingProtection,
|
||||
eTrackingAnnotations,
|
||||
eFingerprinting,
|
||||
eCryptomining,
|
||||
};
|
||||
|
||||
// Check whether a top window URI is on the content blocking allow list.
|
||||
|
|
|
@ -2699,6 +2699,12 @@ nsUrlClassifierDBService::GetFeatureByName(const nsACString& aFeatureName,
|
|||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierDBService::GetFeatureNames(nsTArray<nsCString>& aArray) {
|
||||
mozilla::net::UrlClassifierFeatureFactory::GetFeatureNames(aArray);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsUrlClassifierDBService::CreateFeatureWithTables(
|
||||
const nsACString& aName, const nsTArray<nsCString>& aBlacklistTables,
|
||||
|
|
|
@ -45,3 +45,5 @@ skip-if = (verify && debug && (os == 'win' || os == 'mac'))
|
|||
[test_cachemiss.html]
|
||||
skip-if = verify
|
||||
[test_annotation_vs_TP.html]
|
||||
[test_fingerprinting.html]
|
||||
[test_cryptomining.html]
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test the cryptomining classifier</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/* eslint-env mozilla/frame-script */
|
||||
|
||||
var tests = [
|
||||
// All disabled.
|
||||
{ config: [ false, false ], loadExpected: true },
|
||||
|
||||
// Just whitelisted.
|
||||
{ config: [ false, true ], loadExpected: true },
|
||||
|
||||
// Just blacklisted.
|
||||
{ config: [ true, false ], loadExpected: false },
|
||||
|
||||
// whitelist + blacklist: whitelist wins
|
||||
{ config: [ true, true ], loadExpected: true },
|
||||
];
|
||||
|
||||
function prefValue(value, what) {
|
||||
return value ? what : "";
|
||||
}
|
||||
|
||||
async function runTest(test) {
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[ "urlclassifier.features.cryptomining.blacklistHosts", prefValue(test.config[0], "example.com") ],
|
||||
[ "urlclassifier.features.cryptomining.whitelistHosts", prefValue(test.config[1], "mochi.test") ],
|
||||
[ "urlclassifier.features.cryptomining.blacklistTables", prefValue(test.config[0], "mochitest1-track-simple") ],
|
||||
[ "urlclassifier.features.cryptomining.whitelistTables", "" ],
|
||||
[ "privacy.trackingprotection.enabled", false ],
|
||||
[ "privacy.trackingprotection.annotate_channels", false ],
|
||||
[ "privacy.trackingprotection.cryptomining.enabled", true ],
|
||||
[ "privacy.trackingprotection.fingerprinting.enabled", false ],
|
||||
]});
|
||||
|
||||
info("Testing: " + test.config.toSource() + "\n");
|
||||
|
||||
// Let's load an image with a random query string, just to avoid network cache.
|
||||
let result = await new Promise(resolve => {
|
||||
let image = new Image();
|
||||
image.src = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random();
|
||||
image.onload = _ => resolve(true);
|
||||
image.onerror = _ => resolve(false);
|
||||
});
|
||||
|
||||
is(result, test.loadExpected, "The loading happened correctly");
|
||||
|
||||
// Let's load an image with a random query string, just to avoid network cache.
|
||||
result = await new Promise(resolve => {
|
||||
let image = new Image();
|
||||
image.src = "http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random();
|
||||
image.onload = _ => resolve(true);
|
||||
image.onerror = _ => resolve(false);
|
||||
});
|
||||
|
||||
is(result, test.loadExpected, "The loading happened correctly (by table)");
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
let chromeScript = SpecialPowers.loadChromeScript(_ => {
|
||||
ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
|
||||
|
||||
addMessageListener("loadTrackers", __ => {
|
||||
UrlClassifierTestUtils.addTestTrackers().then(___ => {
|
||||
sendAsyncMessage("trackersLoaded");
|
||||
});
|
||||
});
|
||||
|
||||
addMessageListener("unloadTrackers", __ => {
|
||||
UrlClassifierTestUtils.cleanupTestTrackers();
|
||||
sendAsyncMessage("trackersUnloaded");
|
||||
});
|
||||
});
|
||||
|
||||
await new Promise(resolve => {
|
||||
chromeScript.addMessageListener("trackersLoaded", resolve);
|
||||
chromeScript.sendAsyncMessage("loadTrackers");
|
||||
});
|
||||
|
||||
for (let test in tests) {
|
||||
await runTest(tests[test]);
|
||||
}
|
||||
|
||||
await new Promise(resolve => {
|
||||
chromeScript.addMessageListener("trackersUnloaded", resolve);
|
||||
chromeScript.sendSyncMessage("unloadTrackers");
|
||||
});
|
||||
|
||||
chromeScript.destroy();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTests();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -0,0 +1,106 @@
|
|||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<head>
|
||||
<title>Test the fingerprinting classifier</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<script class="testbody" type="text/javascript">
|
||||
|
||||
/* eslint-env mozilla/frame-script */
|
||||
|
||||
var tests = [
|
||||
// All disabled.
|
||||
{ config: [ false, false ], loadExpected: true },
|
||||
|
||||
// Just whitelisted.
|
||||
{ config: [ false, true ], loadExpected: true },
|
||||
|
||||
// Just blacklisted.
|
||||
{ config: [ true, false ], loadExpected: false },
|
||||
|
||||
// whitelist + blacklist: whitelist wins
|
||||
{ config: [ true, true ], loadExpected: true },
|
||||
];
|
||||
|
||||
function prefValue(value, what) {
|
||||
return value ? what : "";
|
||||
}
|
||||
|
||||
async function runTest(test) {
|
||||
await SpecialPowers.pushPrefEnv({set: [
|
||||
[ "urlclassifier.features.fingerprinting.blacklistHosts", prefValue(test.config[0], "example.com") ],
|
||||
[ "urlclassifier.features.fingerprinting.whitelistHosts", prefValue(test.config[1], "mochi.test") ],
|
||||
[ "urlclassifier.features.fingerprinting.blacklistTables", prefValue(test.config[0], "mochitest1-track-simple") ],
|
||||
[ "urlclassifier.features.fingerprinting.whitelistTables", "" ],
|
||||
[ "privacy.trackingprotection.enabled", false ],
|
||||
[ "privacy.trackingprotection.annotate_channels", false ],
|
||||
[ "privacy.trackingprotection.cryptomining.enabled", false ],
|
||||
[ "privacy.trackingprotection.fingerprinting.enabled", true ],
|
||||
]});
|
||||
|
||||
info("Testing: " + test.config.toSource() + "\n");
|
||||
|
||||
// Let's load an image with a random query string, just to avoid network cache.
|
||||
let result = await new Promise(resolve => {
|
||||
let image = new Image();
|
||||
image.src = "http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random();
|
||||
image.onload = _ => resolve(true);
|
||||
image.onerror = _ => resolve(false);
|
||||
});
|
||||
|
||||
is(result, test.loadExpected, "The loading happened correctly");
|
||||
|
||||
// Let's load an image with a random query string, just to avoid network cache.
|
||||
result = await new Promise(resolve => {
|
||||
let image = new Image();
|
||||
image.src = "http://tracking.example.org/tests/toolkit/components/url-classifier/tests/mochitest/raptor.jpg?" + Math.random();
|
||||
image.onload = _ => resolve(true);
|
||||
image.onerror = _ => resolve(false);
|
||||
});
|
||||
|
||||
is(result, test.loadExpected, "The loading happened correctly (by table)");
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
let chromeScript = SpecialPowers.loadChromeScript(_ => {
|
||||
ChromeUtils.import("resource://testing-common/UrlClassifierTestUtils.jsm");
|
||||
|
||||
addMessageListener("loadTrackers", __ => {
|
||||
UrlClassifierTestUtils.addTestTrackers().then(___ => {
|
||||
sendAsyncMessage("trackersLoaded");
|
||||
});
|
||||
});
|
||||
|
||||
addMessageListener("unloadTrackers", __ => {
|
||||
UrlClassifierTestUtils.cleanupTestTrackers();
|
||||
sendAsyncMessage("trackersUnloaded");
|
||||
});
|
||||
});
|
||||
|
||||
await new Promise(resolve => {
|
||||
chromeScript.addMessageListener("trackersLoaded", resolve);
|
||||
chromeScript.sendAsyncMessage("loadTrackers");
|
||||
});
|
||||
|
||||
for (let test in tests) {
|
||||
await runTest(tests[test]);
|
||||
}
|
||||
|
||||
await new Promise(resolve => {
|
||||
chromeScript.addMessageListener("trackersUnloaded", resolve);
|
||||
chromeScript.sendSyncMessage("unloadTrackers");
|
||||
});
|
||||
|
||||
chromeScript.destroy();
|
||||
SimpleTest.finish();
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
runTests();
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
|
@ -11,6 +11,7 @@ const JSLOG_PREF = "browser.safebrowsing.debug";
|
|||
function unLoad() {
|
||||
window.removeEventListener("unload", unLoad);
|
||||
|
||||
Search.uninit();
|
||||
Provider.uninit();
|
||||
Cache.uninit();
|
||||
Debug.uninit();
|
||||
|
@ -20,11 +21,160 @@ function onLoad() {
|
|||
window.removeEventListener("load", onLoad);
|
||||
window.addEventListener("unload", unLoad);
|
||||
|
||||
Search.init();
|
||||
Provider.init();
|
||||
Cache.init();
|
||||
Debug.init();
|
||||
}
|
||||
|
||||
/*
|
||||
* Search
|
||||
*/
|
||||
var Search = {
|
||||
init() {
|
||||
let classifier = Cc["@mozilla.org/url-classifier/dbservice;1"]
|
||||
.getService(Ci.nsIURIClassifier);
|
||||
let featureNames = classifier.getFeatureNames();
|
||||
|
||||
let fragment = document.createDocumentFragment();
|
||||
featureNames.forEach(featureName => {
|
||||
let div = document.createElement("div");
|
||||
fragment.appendChild(div);
|
||||
|
||||
let checkbox = document.createElement("input");
|
||||
checkbox.id = "feature_" + featureName;
|
||||
checkbox.type = "checkbox";
|
||||
checkbox.checked = true;
|
||||
div.appendChild(checkbox);
|
||||
|
||||
let label = document.createElement("label");
|
||||
label.for = checkbox.id;
|
||||
div.appendChild(label);
|
||||
|
||||
let text = document.createTextNode(featureName);
|
||||
label.appendChild(text);
|
||||
});
|
||||
|
||||
let list = document.getElementById("search-features");
|
||||
list.appendChild(fragment);
|
||||
|
||||
let btn = document.getElementById("search-button");
|
||||
btn.addEventListener("click", this.search);
|
||||
|
||||
this.hideError();
|
||||
this.hideResults();
|
||||
},
|
||||
|
||||
uninit() {
|
||||
let list = document.getElementById("search-features");
|
||||
while (list.firstChild) {
|
||||
list.firstChild.remove();
|
||||
}
|
||||
|
||||
let btn = document.getElementById("search-button");
|
||||
btn.removeEventListener("click", this.search);
|
||||
},
|
||||
|
||||
search() {
|
||||
Search.hideError();
|
||||
Search.hideResults();
|
||||
|
||||
let input = document.getElementById("search-input").value;
|
||||
|
||||
let uri;
|
||||
try {
|
||||
uri = Services.io.newURI(input);
|
||||
if (!uri) {
|
||||
Search.reportError("url-classifier-search-error-invalid-url");
|
||||
return;
|
||||
}
|
||||
} catch (ex) {
|
||||
Search.reportError("url-classifier-search-error-invalid-url");
|
||||
return;
|
||||
}
|
||||
|
||||
let classifier = Cc["@mozilla.org/url-classifier/dbservice;1"]
|
||||
.getService(Ci.nsIURIClassifier);
|
||||
|
||||
let featureNames = classifier.getFeatureNames();
|
||||
let features = [];
|
||||
featureNames.forEach(featureName => {
|
||||
if (document.getElementById("feature_" + featureName).checked) {
|
||||
let feature = classifier.getFeatureByName(featureName);
|
||||
if (feature) {
|
||||
features.push(feature);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!features.length) {
|
||||
Search.reportError("url-classifier-search-error-no-features");
|
||||
return;
|
||||
}
|
||||
|
||||
let listType = document.getElementById("search-listtype").value == 0
|
||||
? Ci.nsIUrlClassifierFeature.blacklist
|
||||
: Ci.nsIUrlClassifierFeature.whitelist;
|
||||
classifier.asyncClassifyLocalWithFeatures(uri, features, listType,
|
||||
list => Search.showResults(list));
|
||||
|
||||
Search.hideError();
|
||||
},
|
||||
|
||||
hideError() {
|
||||
let errorMessage = document.getElementById("search-error-message");
|
||||
errorMessage.style.display = "none";
|
||||
},
|
||||
|
||||
reportError(msg) {
|
||||
let errorMessage = document.getElementById("search-error-message");
|
||||
document.l10n.setAttributes(errorMessage, msg);
|
||||
errorMessage.style.display = "";
|
||||
},
|
||||
|
||||
hideResults() {
|
||||
let resultTitle = document.getElementById("result-title");
|
||||
resultTitle.style.display = "none";
|
||||
|
||||
let resultTable = document.getElementById("result-table");
|
||||
resultTable.style.display = "none";
|
||||
},
|
||||
|
||||
showResults(results) {
|
||||
let fragment = document.createDocumentFragment();
|
||||
results.forEach(result => {
|
||||
let tr = document.createElement("tr");
|
||||
fragment.appendChild(tr);
|
||||
|
||||
let th = document.createElement("th");
|
||||
tr.appendChild(th);
|
||||
th.appendChild(document.createTextNode(result.feature.name));
|
||||
|
||||
let td = document.createElement("td");
|
||||
tr.appendChild(td);
|
||||
|
||||
let featureName = document.createElement("div");
|
||||
document.l10n.setAttributes(featureName, "url-classifier-search-result-uri", {uri: result.uri.spec});
|
||||
td.appendChild(featureName);
|
||||
|
||||
let list = document.createElement("div");
|
||||
document.l10n.setAttributes(list, "url-classifier-search-result-list", {list: result.list});
|
||||
td.appendChild(list);
|
||||
});
|
||||
|
||||
let resultTable = document.getElementById("result-table");
|
||||
while (resultTable.firstChild) {
|
||||
resultTable.firstChild.remove();
|
||||
}
|
||||
|
||||
resultTable.appendChild(fragment);
|
||||
resultTable.style.display = "";
|
||||
|
||||
let resultTitle = document.getElementById("result-title");
|
||||
resultTitle.style.display = "";
|
||||
},
|
||||
};
|
||||
|
||||
/*
|
||||
* Provider
|
||||
*/
|
||||
|
|
|
@ -18,6 +18,49 @@
|
|||
|
||||
<body onload="onLoad()" class="wide-container">
|
||||
<h1 data-l10n-id="url-classifier-title"></h1>
|
||||
<div id="search">
|
||||
<h2 class="major-section" data-l10n-id="url-classifier-search-title"></h2>
|
||||
<div class="options">
|
||||
<table id="search-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th class="column" data-l10n-id="url-classifier-search-input"></th>
|
||||
<td>
|
||||
<input id="search-input" type="text" value=""/>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="column" data-l10n-id="url-classifier-search-listType"></th>
|
||||
<td>
|
||||
<select id="search-listtype">
|
||||
<option value="0">Blacklist</option>
|
||||
<option value="1">Whitelist</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th class="column" data-l10n-id="url-classifier-search-features"></th>
|
||||
<td id="search-features"></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th></th>
|
||||
<td>
|
||||
<button id="search-button" data-l10n-id="url-classifier-search-btn"></button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p id="search-error-message"></p>
|
||||
<h2 class="major-section" id="result-title" data-l10n-id="url-classifier-search-result-title"></h2>
|
||||
<table id="result-table">
|
||||
<tr>
|
||||
<td>
|
||||
<input id="search-input" type="text" value=""/>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div id="provider">
|
||||
<h2 class="major-section" data-l10n-id="url-classifier-provider-title"></h2>
|
||||
<table id="provider-table">
|
||||
|
|
|
@ -3,6 +3,16 @@
|
|||
# file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
||||
|
||||
url-classifier-title = URL Classifier Information
|
||||
url-classifier-search-title = Search
|
||||
url-classifier-search-result-title = Results
|
||||
url-classifier-search-result-uri = URI: { $uri }
|
||||
url-classifier-search-result-list = Table list: { $list }
|
||||
url-classifier-search-input = URL
|
||||
url-classifier-search-error-invalid-url = Invalid URL
|
||||
url-classifier-search-error-no-features = No features selected
|
||||
url-classifier-search-btn = Start searching
|
||||
url-classifier-search-features = Features
|
||||
url-classifier-search-listType = List type
|
||||
url-classifier-provider-title = Provider
|
||||
url-classifier-provider = Provider
|
||||
url-classifier-provider-last-update-time = Last update time
|
||||
|
|
Загрузка…
Ссылка в новой задаче