mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 05:37:11 +00:00
import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 524674, nsIEventListenerService: tracking of dynamically added and removed event listeners, r=masayuki (a1779bcaf)
- Bug 418986 - Resist fingerprinting by preventing exposure of screen and system info. r=mrbkap, r=heycam (97710708a)
- bug 1171785 create nsContentUtils::RunInStableState helper r=bholley (c71678a0e)
- bug 1172377 change RunInStableState API to receive ownership of nsIRunnable r=roc (3d5c72cac)
- bug 1171785 use nsContentUtils::RunInStableState() r=bholley (1c0acca87)
- Bug 1172784 (part 1) - Remove unused argument from UnmarkGrayJSListenersInCCGenerationDocuments(). r=mccr8. (a3a26ec9f)
- Bug 1172784 (part 2) - Remove PL_DHashTableEnumerate use from nsContentUtils. r=mccr8. (792b83f8e)
- Bug 1171832 - Remove PL_DHashTableEnumerator use from nsDocument. r=smaug. (eb265bb3a)
- Bug 819090 - Convert nsRefMapEntry::mRefContentList to nsTArray. r=froydnj (509060d38)
- Bug 819090 - Convert BroadcasterMapEntry::mListeners to nsTArray. r=froydnj (6b1bd7232)
- Bug 819090 - Convert nsDocument::mIdContentList to nsTArray. r=froydnj (41ebb3a64)
- Bug 819090 - Remove nsVoidArray.{cpp,h}. r=froydnj (6038b4fc7)
- Bug 1199143 - Inline heavyweight functions. r=shu (798a24e44)
- bug 1150136 - rel=preconnect from html parser r=hsivonen (1a078efcb)
- Bug 1135812 - Make picture element react to viewport changes. r=dbaron,johns (4b405d69c)
- add missing testcase (cb6c04747)
This commit is contained in:
@@ -0,0 +1,159 @@
|
||||
const gTestRoot = getRootDirectory(gTestPath);
|
||||
const gStyleSheet = "bug839103.css";
|
||||
|
||||
var gTab = null;
|
||||
var needsInitialApplicableStateEvent = false;
|
||||
var needsInitialApplicableStateEventFor = null;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
gBrowser.addEventListener("StyleSheetAdded", initialStylesheetAdded, true);
|
||||
gTab = gBrowser.selectedTab = gBrowser.addTab(gTestRoot + "test_bug839103.html");
|
||||
gTab.linkedBrowser.addEventListener("load", tabLoad, true);
|
||||
}
|
||||
|
||||
function initialStylesheetAdded(evt) {
|
||||
gBrowser.removeEventListener("StyleSheetAdded", initialStylesheetAdded, true);
|
||||
ok(true, "received initial style sheet event");
|
||||
is(evt.type, "StyleSheetAdded", "evt.type has expected value");
|
||||
is(evt.target, gBrowser.contentDocument, "event targets correct document");
|
||||
ok(evt.stylesheet, "evt.stylesheet is defined");
|
||||
ok(evt.stylesheet.toString().includes("CSSStyleSheet"), "evt.stylesheet is a stylesheet");
|
||||
ok(evt.documentSheet, "style sheet is a document sheet");
|
||||
}
|
||||
|
||||
function tabLoad(evt) {
|
||||
gTab.linkedBrowser.removeEventListener(evt.type, tabLoad, true);
|
||||
executeSoon(continueTest);
|
||||
}
|
||||
|
||||
var gLinkElement = null;
|
||||
|
||||
function unexpectedContentEvent(evt) {
|
||||
ok(false, "Received a " + evt.type + " event on content");
|
||||
}
|
||||
|
||||
// We've seen the original stylesheet in the document.
|
||||
// Now add a stylesheet on the fly and make sure we see it.
|
||||
function continueTest() {
|
||||
info("continuing test");
|
||||
|
||||
let doc = gBrowser.contentDocument;
|
||||
doc.styleSheetChangeEventsEnabled = true;
|
||||
doc.addEventListener("StyleSheetAdded", unexpectedContentEvent, false);
|
||||
doc.addEventListener("StyleSheetRemoved", unexpectedContentEvent, false);
|
||||
doc.addEventListener("StyleSheetApplicableStateChanged", unexpectedContentEvent, false);
|
||||
doc.defaultView.addEventListener("StyleSheetAdded", unexpectedContentEvent, false);
|
||||
doc.defaultView.addEventListener("StyleSheetRemoved", unexpectedContentEvent, false);
|
||||
doc.defaultView.addEventListener("StyleSheetApplicableStateChanged", unexpectedContentEvent, false);
|
||||
let link = doc.createElement('link');
|
||||
link.setAttribute('rel', 'stylesheet');
|
||||
link.setAttribute('type', 'text/css');
|
||||
link.setAttribute('href', gTestRoot + gStyleSheet);
|
||||
gLinkElement = link;
|
||||
|
||||
gBrowser.addEventListener("StyleSheetAdded", dynamicStylesheetAdded, true);
|
||||
gBrowser.addEventListener("StyleSheetApplicableStateChanged", dynamicStylesheetApplicableStateChanged, true);
|
||||
doc.body.appendChild(link);
|
||||
}
|
||||
|
||||
function dynamicStylesheetAdded(evt) {
|
||||
gBrowser.removeEventListener("StyleSheetAdded", dynamicStylesheetAdded, true);
|
||||
ok(true, "received dynamic style sheet event");
|
||||
is(evt.type, "StyleSheetAdded", "evt.type has expected value");
|
||||
is(evt.target, gBrowser.contentDocument, "event targets correct document");
|
||||
ok(evt.stylesheet, "evt.stylesheet is defined");
|
||||
ok(evt.stylesheet.toString().includes("CSSStyleSheet"), "evt.stylesheet is a stylesheet");
|
||||
ok(evt.documentSheet, "style sheet is a document sheet");
|
||||
}
|
||||
|
||||
function dynamicStylesheetApplicableStateChanged(evt) {
|
||||
gBrowser.removeEventListener("StyleSheetApplicableStateChanged", dynamicStylesheetApplicableStateChanged, true);
|
||||
ok(true, "received dynamic style sheet applicable state change event");
|
||||
is(evt.type, "StyleSheetApplicableStateChanged", "evt.type has expected value");
|
||||
is(evt.target, gBrowser.contentDocument, "event targets correct document");
|
||||
is(evt.stylesheet, gLinkElement.sheet, "evt.stylesheet has the right value");
|
||||
is(evt.applicable, true, "evt.applicable has the right value");
|
||||
|
||||
gBrowser.addEventListener("StyleSheetApplicableStateChanged", dynamicStylesheetApplicableStateChangedToFalse, true);
|
||||
gLinkElement.disabled = true;
|
||||
}
|
||||
|
||||
function dynamicStylesheetApplicableStateChangedToFalse(evt) {
|
||||
gBrowser.removeEventListener("StyleSheetApplicableStateChanged", dynamicStylesheetApplicableStateChangedToFalse, true);
|
||||
is(evt.type, "StyleSheetApplicableStateChanged", "evt.type has expected value");
|
||||
ok(true, "received dynamic style sheet applicable state change event after media=\"\" changed");
|
||||
is(evt.target, gBrowser.contentDocument, "event targets correct document");
|
||||
is(evt.stylesheet, gLinkElement.sheet, "evt.stylesheet has the right value");
|
||||
is(evt.applicable, false, "evt.applicable has the right value");
|
||||
|
||||
gBrowser.addEventListener("StyleSheetRemoved", dynamicStylesheetRemoved, true);
|
||||
gBrowser.contentDocument.body.removeChild(gLinkElement);
|
||||
}
|
||||
|
||||
function dynamicStylesheetRemoved(evt) {
|
||||
gBrowser.removeEventListener("StyleSheetRemoved", dynamicStylesheetRemoved, true);
|
||||
ok(true, "received dynamic style sheet removal");
|
||||
is(evt.type, "StyleSheetRemoved", "evt.type has expected value");
|
||||
is(evt.target, gBrowser.contentDocument, "event targets correct document");
|
||||
ok(evt.stylesheet, "evt.stylesheet is defined");
|
||||
ok(evt.stylesheet.toString().includes("CSSStyleSheet"), "evt.stylesheet is a stylesheet");
|
||||
ok(evt.stylesheet.href.includes(gStyleSheet), "evt.stylesheet is the removed stylesheet");
|
||||
|
||||
gBrowser.addEventListener("StyleRuleAdded", styleRuleAdded, true);
|
||||
gBrowser.contentDocument.querySelector("style").sheet.insertRule("*{color:black}", 0);
|
||||
}
|
||||
|
||||
function styleRuleAdded(evt) {
|
||||
gBrowser.removeEventListener("StyleRuleAdded", styleRuleAdded, true);
|
||||
ok(true, "received style rule added event");
|
||||
is(evt.type, "StyleRuleAdded", "evt.type has expected value");
|
||||
is(evt.target, gBrowser.contentDocument, "event targets correct document");
|
||||
ok(evt.stylesheet, "evt.stylesheet is defined");
|
||||
ok(evt.stylesheet.toString().includes("CSSStyleSheet"), "evt.stylesheet is a stylesheet");
|
||||
ok(evt.rule, "evt.rule is defined");
|
||||
is(evt.rule.cssText, "* { color: black; }", "evt.rule.cssText has expected value");
|
||||
|
||||
gBrowser.addEventListener("StyleRuleChanged", styleRuleChanged, true);
|
||||
evt.rule.style.cssText = "color:green";
|
||||
}
|
||||
|
||||
function styleRuleChanged(evt) {
|
||||
gBrowser.removeEventListener("StyleRuleChanged", styleRuleChanged, true);
|
||||
ok(true, "received style rule changed event");
|
||||
is(evt.type, "StyleRuleChanged", "evt.type has expected value");
|
||||
is(evt.target, gBrowser.contentDocument, "event targets correct document");
|
||||
ok(evt.stylesheet, "evt.stylesheet is defined");
|
||||
ok(evt.stylesheet.toString().includes("CSSStyleSheet"), "evt.stylesheet is a stylesheet");
|
||||
ok(evt.rule, "evt.rule is defined");
|
||||
is(evt.rule.cssText, "* { color: green; }", "evt.rule.cssText has expected value");
|
||||
|
||||
gBrowser.addEventListener("StyleRuleRemoved", styleRuleRemoved, true);
|
||||
evt.stylesheet.deleteRule(0);
|
||||
}
|
||||
|
||||
function styleRuleRemoved(evt) {
|
||||
gBrowser.removeEventListener("StyleRuleRemoved", styleRuleRemoved, true);
|
||||
ok(true, "received style rule removed event");
|
||||
is(evt.type, "StyleRuleRemoved", "evt.type has expected value");
|
||||
is(evt.target, gBrowser.contentDocument, "event targets correct document");
|
||||
ok(evt.stylesheet, "evt.stylesheet is defined");
|
||||
ok(evt.stylesheet.toString().includes("CSSStyleSheet"), "evt.stylesheet is a stylesheet");
|
||||
ok(evt.rule, "evt.rule is defined");
|
||||
|
||||
executeSoon(concludeTest);
|
||||
}
|
||||
|
||||
function concludeTest() {
|
||||
let doc = gBrowser.contentDocument;
|
||||
doc.removeEventListener("StyleSheetAdded", unexpectedContentEvent, false);
|
||||
doc.removeEventListener("StyleSheetRemoved", unexpectedContentEvent, false);
|
||||
doc.removeEventListener("StyleSheetApplicableStateChanged", unexpectedContentEvent, false);
|
||||
doc.defaultView.removeEventListener("StyleSheetAdded", unexpectedContentEvent, false);
|
||||
doc.defaultView.removeEventListener("StyleSheetRemoved", unexpectedContentEvent, false);
|
||||
doc.defaultView.removeEventListener("StyleSheetApplicableStateChanged", unexpectedContentEvent, false);
|
||||
gBrowser.removeCurrentTab();
|
||||
gLinkElement = null;
|
||||
gTab = null;
|
||||
finish();
|
||||
}
|
||||
@@ -437,7 +437,7 @@ nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic,
|
||||
|
||||
switch(sFSState) {
|
||||
case eUnmarkJSEventListeners: {
|
||||
nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(sGeneration);
|
||||
nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments();
|
||||
break;
|
||||
}
|
||||
case eUnmarkMessageManagers: {
|
||||
|
||||
@@ -33,7 +33,6 @@
|
||||
#include "nsIApplicationCacheContainer.h"
|
||||
#include "nsIApplicationCacheChannel.h"
|
||||
#include "nsIScriptSecurityManager.h"
|
||||
#include "nsISpeculativeConnect.h"
|
||||
#include "nsICookieService.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsNodeInfoManager.h"
|
||||
@@ -876,20 +875,15 @@ nsContentSink::PrefetchDNS(const nsAString &aHref)
|
||||
void
|
||||
nsContentSink::Preconnect(const nsAString &aHref)
|
||||
{
|
||||
nsCOMPtr<nsISpeculativeConnect>
|
||||
speculator(do_QueryInterface(nsContentUtils::GetIOService()));
|
||||
if (!speculator) {
|
||||
return;
|
||||
}
|
||||
|
||||
// construct URI using document charset
|
||||
const nsACString& charset = mDocument->GetDocumentCharacterSet();
|
||||
nsCOMPtr<nsIURI> uri;
|
||||
NS_NewURI(getter_AddRefs(uri), aHref,
|
||||
charset.IsEmpty() ? nullptr : PromiseFlatCString(charset).get(),
|
||||
mDocument->GetDocBaseURI());
|
||||
if (uri) {
|
||||
speculator->SpeculativeConnect(uri, nullptr);
|
||||
|
||||
if (uri && mDocument) {
|
||||
mDocument->MaybePreconnect(uri);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -163,10 +163,9 @@ protected:
|
||||
void PrefetchHref(const nsAString &aHref, nsINode *aSource,
|
||||
bool aExplicit);
|
||||
|
||||
// For both PrefetchDNS() and Preconnect() aHref can either be the usual
|
||||
// For PrefetchDNS() aHref can either be the usual
|
||||
// URI format or of the form "//www.hostname.com" without a scheme.
|
||||
void PrefetchDNS(const nsAString &aHref);
|
||||
void Preconnect(const nsAString &aHref);
|
||||
|
||||
// Gets the cache key (used to identify items in a cache) of the channel.
|
||||
nsresult GetChannelCacheKey(nsIChannel* aChannel, nsACString& aCacheKey);
|
||||
@@ -224,6 +223,10 @@ public:
|
||||
// element and calls the above method.
|
||||
void ProcessOfflineManifest(nsIContent *aElement);
|
||||
|
||||
// For Preconnect() aHref can either be the usual
|
||||
// URI format or of the form "//www.hostname.com" without a scheme.
|
||||
void Preconnect(const nsAString &aHref);
|
||||
|
||||
protected:
|
||||
// Tries to scroll to the URI's named anchor. Once we've successfully
|
||||
// done that, further calls to this method will be ignored.
|
||||
|
||||
+40
-16
@@ -93,6 +93,7 @@
|
||||
#include "nsHostObjectProtocolHandler.h"
|
||||
#include "nsHtml5Module.h"
|
||||
#include "nsHtml5StringParser.h"
|
||||
#include "nsIAppShell.h"
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "nsICategoryManager.h"
|
||||
#include "nsIChannelEventSink.h"
|
||||
@@ -183,6 +184,7 @@
|
||||
#include "nsUnicodeProperties.h"
|
||||
#include "nsViewManager.h"
|
||||
#include "nsViewportInfo.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
#include "nsWrapperCacheInlines.h"
|
||||
#include "nsXULPopupManager.h"
|
||||
#include "xpcprivate.h" // nsXPConnect
|
||||
@@ -256,6 +258,7 @@ bool nsContentUtils::sIsUserTimingLoggingEnabled = false;
|
||||
bool nsContentUtils::sIsExperimentalAutocompleteEnabled = false;
|
||||
bool nsContentUtils::sEncodeDecodeURLHash = false;
|
||||
bool nsContentUtils::sGettersDecodeURLHash = false;
|
||||
bool nsContentUtils::sPrivacyResistFingerprinting = false;
|
||||
|
||||
uint32_t nsContentUtils::sHandlingInputTimeout = 1000;
|
||||
|
||||
@@ -339,6 +342,7 @@ namespace {
|
||||
|
||||
static NS_DEFINE_CID(kParserServiceCID, NS_PARSERSERVICE_CID);
|
||||
static NS_DEFINE_CID(kCParserCID, NS_PARSER_CID);
|
||||
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
|
||||
|
||||
static PLDHashTable* sEventListenerManagersHash;
|
||||
|
||||
@@ -545,6 +549,9 @@ nsContentUtils::Init()
|
||||
Preferences::AddBoolVarCache(&sGettersDecodeURLHash,
|
||||
"dom.url.getters_decode_hash", false);
|
||||
|
||||
Preferences::AddBoolVarCache(&sPrivacyResistFingerprinting,
|
||||
"privacy.resistFingerprinting", false);
|
||||
|
||||
Preferences::AddUintVarCache(&sHandlingInputTimeout,
|
||||
"dom.event.handling-user-input-time-limit",
|
||||
1000);
|
||||
@@ -2010,6 +2017,16 @@ nsContentUtils::IsCallerChrome()
|
||||
return xpc::IsUniversalXPConnectEnabled(GetCurrentJSContext());
|
||||
}
|
||||
|
||||
bool
|
||||
nsContentUtils::ShouldResistFingerprinting(nsIDocShell* aDocShell)
|
||||
{
|
||||
if (!aDocShell) {
|
||||
return false;
|
||||
}
|
||||
bool isChrome = nsContentUtils::IsChromeDoc(aDocShell->GetDocument());
|
||||
return !isChrome && sPrivacyResistFingerprinting;
|
||||
}
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
namespace workers {
|
||||
@@ -3993,29 +4010,22 @@ nsContentUtils::MaybeFireNodeRemoved(nsINode* aChild, nsINode* aParent,
|
||||
}
|
||||
}
|
||||
|
||||
PLDHashOperator
|
||||
ListenerEnumerator(PLDHashTable* aTable, PLDHashEntryHdr* aEntry,
|
||||
uint32_t aNumber, void* aArg)
|
||||
void
|
||||
nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments()
|
||||
{
|
||||
EventListenerManagerMapEntry* entry =
|
||||
static_cast<EventListenerManagerMapEntry*>(aEntry);
|
||||
if (entry) {
|
||||
if (!sEventListenerManagersHash) {
|
||||
return;
|
||||
}
|
||||
|
||||
PLDHashTable::Iterator iter(sEventListenerManagersHash);
|
||||
while (iter.HasMoreEntries()) {
|
||||
auto entry = static_cast<EventListenerManagerMapEntry*>(iter.NextEntry());
|
||||
nsINode* n = static_cast<nsINode*>(entry->mListenerManager->GetTarget());
|
||||
if (n && n->IsInDoc() &&
|
||||
nsCCUncollectableMarker::InGeneration(n->OwnerDoc()->GetMarkedCCGeneration())) {
|
||||
entry->mListenerManager->MarkForCC();
|
||||
}
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(uint32_t aGeneration)
|
||||
{
|
||||
if (sEventListenerManagersHash) {
|
||||
PL_DHashTableEnumerate(sEventListenerManagersHash, ListenerEnumerator,
|
||||
&aGeneration);
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
@@ -5165,6 +5175,20 @@ nsContentUtils::AddScriptRunner(nsIRunnable* aRunnable)
|
||||
return true;
|
||||
}
|
||||
|
||||
/* static */
|
||||
void
|
||||
nsContentUtils::RunInStableState(already_AddRefed<nsIRunnable> aRunnable,
|
||||
DispatchFailureHandling aHandling)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> runnable = aRunnable;
|
||||
nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID));
|
||||
if (!appShell) {
|
||||
MOZ_ASSERT(aHandling == DispatchFailureHandling::IgnoreFailure);
|
||||
return;
|
||||
}
|
||||
appShell->RunInStableState(runnable.forget());
|
||||
}
|
||||
|
||||
void
|
||||
nsContentUtils::EnterMicroTask()
|
||||
{
|
||||
|
||||
@@ -198,6 +198,9 @@ public:
|
||||
JS::Handle<jsid> aId,
|
||||
JS::MutableHandle<JSPropertyDescriptor> aDesc);
|
||||
|
||||
// Check whether we should avoid leaking distinguishing information to JS/CSS.
|
||||
static bool ShouldResistFingerprinting(nsIDocShell* aDocShell);
|
||||
|
||||
/**
|
||||
* Returns the parent node of aChild crossing document boundaries.
|
||||
* Uses the parent node in the composed document.
|
||||
@@ -1180,7 +1183,7 @@ public:
|
||||
static mozilla::EventListenerManager*
|
||||
GetExistingListenerManagerForNode(const nsINode* aNode);
|
||||
|
||||
static void UnmarkGrayJSListenersInCCGenerationDocuments(uint32_t aGeneration);
|
||||
static void UnmarkGrayJSListenersInCCGenerationDocuments();
|
||||
|
||||
/**
|
||||
* Remove the eventlistener manager for aNode.
|
||||
@@ -1633,6 +1636,27 @@ public:
|
||||
*/
|
||||
static void WarnScriptWasIgnored(nsIDocument* aDocument);
|
||||
|
||||
/**
|
||||
* Whether to assert that RunInStableState() succeeds, or ignore failure,
|
||||
* which may happen late in shutdown.
|
||||
*/
|
||||
enum class DispatchFailureHandling { AssertSuccess, IgnoreFailure };
|
||||
|
||||
/**
|
||||
* Add a "synchronous section", in the form of an nsIRunnable run once the
|
||||
* event loop has reached a "stable state". |aRunnable| must not cause any
|
||||
* queued events to be processed (i.e. must not spin the event loop).
|
||||
* We've reached a stable state when the currently executing task/event has
|
||||
* finished, see
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section
|
||||
* In practice this runs aRunnable once the currently executing event
|
||||
* finishes. If called multiple times per task/event, all the runnables will
|
||||
* be executed, in the order in which RunInStableState() was called.
|
||||
*/
|
||||
static void RunInStableState(already_AddRefed<nsIRunnable> aRunnable,
|
||||
DispatchFailureHandling aHandling =
|
||||
DispatchFailureHandling::AssertSuccess);
|
||||
|
||||
/**
|
||||
* Retrieve information about the viewport as a data structure.
|
||||
* This will return information in the viewport META data section
|
||||
@@ -2006,6 +2030,16 @@ public:
|
||||
return sGettersDecodeURLHash && sEncodeDecodeURLHash;
|
||||
}
|
||||
|
||||
/*
|
||||
* Returns true if the browser should attempt to prevent content scripts
|
||||
* from collecting distinctive information about the browser that could
|
||||
* be used to "fingerprint" and track the user across websites.
|
||||
*/
|
||||
static bool ResistFingerprinting()
|
||||
{
|
||||
return sPrivacyResistFingerprinting;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the doc tree branch which contains aDoc contains any
|
||||
* plugins which we don't control event dispatch for, i.e. do any plugins
|
||||
@@ -2548,6 +2582,7 @@ private:
|
||||
static bool sIsExperimentalAutocompleteEnabled;
|
||||
static bool sEncodeDecodeURLHash;
|
||||
static bool sGettersDecodeURLHash;
|
||||
static bool sPrivacyResistFingerprinting;
|
||||
|
||||
static nsHtml5StringParser* sHTMLFragmentParser;
|
||||
static nsIParser* sXMLFragmentParser;
|
||||
|
||||
@@ -3432,16 +3432,14 @@ nsDOMWindowUtils::DispatchEventToChromeOnly(nsIDOMEventTarget* aTarget,
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsDOMWindowUtils::RunInStableState(nsIRunnable *runnable)
|
||||
nsDOMWindowUtils::RunInStableState(nsIRunnable *aRunnable)
|
||||
{
|
||||
MOZ_RELEASE_ASSERT(nsContentUtils::IsCallerChrome());
|
||||
|
||||
nsCOMPtr<nsIAppShell> appShell(do_GetService(kAppShellCID));
|
||||
if (!appShell) {
|
||||
return NS_ERROR_NOT_AVAILABLE;
|
||||
}
|
||||
nsCOMPtr<nsIRunnable> runnable = aRunnable;
|
||||
nsContentUtils::RunInStableState(runnable.forget());
|
||||
|
||||
return appShell->RunInStableState(runnable);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
||||
+115
-105
@@ -229,6 +229,8 @@
|
||||
#include "mozilla/dom/BoxObject.h"
|
||||
#include "gfxVR.h"
|
||||
|
||||
#include "nsISpeculativeConnect.h"
|
||||
|
||||
#ifdef MOZ_MEDIA_NAVIGATOR
|
||||
#include "mozilla/MediaManager.h"
|
||||
#endif // MOZ_MEDIA_NAVIGATOR
|
||||
@@ -268,14 +270,14 @@ nsIdentifierMapEntry::Traverse(nsCycleCollectionTraversalCallback* aCallback)
|
||||
bool
|
||||
nsIdentifierMapEntry::IsEmpty()
|
||||
{
|
||||
return mIdContentList.Count() == 0 && !mNameContentList &&
|
||||
return mIdContentList.IsEmpty() && !mNameContentList &&
|
||||
!mChangeCallbacks && !mImageElement;
|
||||
}
|
||||
|
||||
Element*
|
||||
nsIdentifierMapEntry::GetIdElement()
|
||||
{
|
||||
return static_cast<Element*>(mIdContentList.SafeElementAt(0));
|
||||
return mIdContentList.SafeElementAt(0);
|
||||
}
|
||||
|
||||
Element*
|
||||
@@ -287,8 +289,8 @@ nsIdentifierMapEntry::GetImageIdElement()
|
||||
void
|
||||
nsIdentifierMapEntry::AppendAllIdContent(nsCOMArray<nsIContent>* aElements)
|
||||
{
|
||||
for (int32_t i = 0; i < mIdContentList.Count(); ++i) {
|
||||
aElements->AppendObject(static_cast<Element*>(mIdContentList[i]));
|
||||
for (size_t i = 0; i < mIdContentList.Length(); ++i) {
|
||||
aElements->AppendObject(mIdContentList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -575,16 +577,15 @@ bool
|
||||
nsIdentifierMapEntry::AddIdElement(Element* aElement)
|
||||
{
|
||||
NS_PRECONDITION(aElement, "Must have element");
|
||||
NS_PRECONDITION(mIdContentList.IndexOf(nullptr) < 0,
|
||||
NS_PRECONDITION(!mIdContentList.Contains(nullptr),
|
||||
"Why is null in our list?");
|
||||
|
||||
#ifdef DEBUG
|
||||
Element* currentElement =
|
||||
static_cast<Element*>(mIdContentList.SafeElementAt(0));
|
||||
Element* currentElement = mIdContentList.SafeElementAt(0);
|
||||
#endif
|
||||
|
||||
// Common case
|
||||
if (mIdContentList.Count() == 0) {
|
||||
if (mIdContentList.IsEmpty()) {
|
||||
if (!mIdContentList.AppendElement(aElement))
|
||||
return false;
|
||||
NS_ASSERTION(currentElement == nullptr, "How did that happen?");
|
||||
@@ -596,7 +597,7 @@ nsIdentifierMapEntry::AddIdElement(Element* aElement)
|
||||
// with us. Search for the right place to insert the content.
|
||||
|
||||
size_t idx;
|
||||
if (BinarySearchIf(mIdContentList, 0, mIdContentList.Count(),
|
||||
if (BinarySearchIf(mIdContentList, 0, mIdContentList.Length(),
|
||||
PositionComparator(aElement), &idx)) {
|
||||
// Already in the list, so already in the right spot. Get out of here.
|
||||
// XXXbz this only happens because XUL does all sorts of random
|
||||
@@ -604,12 +605,12 @@ nsIdentifierMapEntry::AddIdElement(Element* aElement)
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!mIdContentList.InsertElementAt(aElement, idx))
|
||||
if (!mIdContentList.InsertElementAt(idx, aElement)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (idx == 0) {
|
||||
Element* oldElement =
|
||||
static_cast<Element*>(mIdContentList.SafeElementAt(1));
|
||||
Element* oldElement = mIdContentList.SafeElementAt(1);
|
||||
NS_ASSERTION(currentElement == oldElement, "How did that happen?");
|
||||
FireChangeCallbacks(oldElement, aElement);
|
||||
}
|
||||
@@ -628,17 +629,15 @@ nsIdentifierMapEntry::RemoveIdElement(Element* aElement)
|
||||
// Only assert this in HTML documents for now as XUL does all sorts of weird
|
||||
// crap.
|
||||
NS_ASSERTION(!aElement->OwnerDoc()->IsHTMLDocument() ||
|
||||
mIdContentList.IndexOf(aElement) >= 0,
|
||||
mIdContentList.Contains(aElement),
|
||||
"Removing id entry that doesn't exist");
|
||||
|
||||
// XXXbz should this ever Compact() I guess when all the content is gone
|
||||
// we'll just get cleaned up in the natural order of things...
|
||||
Element* currentElement =
|
||||
static_cast<Element*>(mIdContentList.SafeElementAt(0));
|
||||
Element* currentElement = mIdContentList.SafeElementAt(0);
|
||||
mIdContentList.RemoveElement(aElement);
|
||||
if (currentElement == aElement) {
|
||||
FireChangeCallbacks(currentElement,
|
||||
static_cast<Element*>(mIdContentList.SafeElementAt(0)));
|
||||
FireChangeCallbacks(currentElement, mIdContentList.SafeElementAt(0));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -695,17 +694,6 @@ public:
|
||||
nsIDocument *mSubDocument;
|
||||
};
|
||||
|
||||
struct FindContentData
|
||||
{
|
||||
explicit FindContentData(nsIDocument* aSubDoc)
|
||||
: mSubDocument(aSubDoc), mResult(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
nsISupports *mSubDocument;
|
||||
Element *mResult;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* A struct that holds all the information about a radio group.
|
||||
@@ -1839,22 +1827,6 @@ NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_BEGIN(nsDocument)
|
||||
return Element::CanSkipThis(tmp);
|
||||
NS_IMPL_CYCLE_COLLECTION_CAN_SKIP_THIS_END
|
||||
|
||||
static PLDHashOperator
|
||||
SubDocTraverser(PLDHashTable *table, PLDHashEntryHdr *hdr, uint32_t number,
|
||||
void *arg)
|
||||
{
|
||||
SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
|
||||
nsCycleCollectionTraversalCallback *cb =
|
||||
static_cast<nsCycleCollectionTraversalCallback*>(arg);
|
||||
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mKey");
|
||||
cb->NoteXPCOMChild(entry->mKey);
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(*cb, "mSubDocuments entry->mSubDocument");
|
||||
cb->NoteXPCOMChild(entry->mSubDocument);
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
RadioGroupsTraverser(const nsAString& aKey, nsRadioGroupStruct* aData,
|
||||
void* aClosure)
|
||||
@@ -2014,7 +1986,17 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN_INTERNAL(nsDocument)
|
||||
}
|
||||
|
||||
if (tmp->mSubDocuments) {
|
||||
PL_DHashTableEnumerate(tmp->mSubDocuments, SubDocTraverser, &cb);
|
||||
PLDHashTable::Iterator iter(tmp->mSubDocuments);
|
||||
while (iter.HasMoreEntries()) {
|
||||
auto entry = static_cast<SubDocMapEntry*>(iter.NextEntry());
|
||||
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mSubDocuments entry->mKey");
|
||||
cb.NoteXPCOMChild(entry->mKey);
|
||||
NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb,
|
||||
"mSubDocuments entry->mSubDocument");
|
||||
cb.NoteXPCOMChild(entry->mSubDocument);
|
||||
}
|
||||
}
|
||||
|
||||
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mCSSLoader)
|
||||
@@ -4085,22 +4067,6 @@ nsDocument::GetSubDocumentFor(nsIContent *aContent) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
FindContentEnumerator(PLDHashTable *table, PLDHashEntryHdr *hdr,
|
||||
uint32_t number, void *arg)
|
||||
{
|
||||
SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
|
||||
FindContentData *data = static_cast<FindContentData*>(arg);
|
||||
|
||||
if (entry->mSubDocument == data->mSubDocument) {
|
||||
data->mResult = entry->mKey;
|
||||
|
||||
return PL_DHASH_STOP;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
Element*
|
||||
nsDocument::FindContentForSubDocument(nsIDocument *aDocument) const
|
||||
{
|
||||
@@ -4110,10 +4076,14 @@ nsDocument::FindContentForSubDocument(nsIDocument *aDocument) const
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
FindContentData data(aDocument);
|
||||
PL_DHashTableEnumerate(mSubDocuments, FindContentEnumerator, &data);
|
||||
|
||||
return data.mResult;
|
||||
PLDHashTable::Iterator iter(mSubDocuments);
|
||||
while (iter.HasMoreEntries()) {
|
||||
auto entry = static_cast<SubDocMapEntry*>(iter.NextEntry());
|
||||
if (entry->mSubDocument == aDocument) {
|
||||
return entry->mKey;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -5018,7 +4988,7 @@ nsDocument::GetElementById(const nsAString& aElementId)
|
||||
return entry ? entry->GetIdElement() : nullptr;
|
||||
}
|
||||
|
||||
const nsSmallVoidArray*
|
||||
const nsTArray<Element*>*
|
||||
nsDocument::GetAllElementsForId(const nsAString& aElementId) const
|
||||
{
|
||||
if (aElementId.IsEmpty()) {
|
||||
@@ -5026,7 +4996,7 @@ nsDocument::GetAllElementsForId(const nsAString& aElementId) const
|
||||
}
|
||||
|
||||
nsIdentifierMapEntry *entry = mIdentifierMap.GetEntry(aElementId);
|
||||
return entry ? entry->GetIdElements() : nullptr;
|
||||
return entry ? &entry->GetIdElements() : nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@@ -5123,6 +5093,10 @@ nsDocument::DispatchContentLoadedEvents()
|
||||
// Unpin references to preloaded images
|
||||
mPreloadingImages.Clear();
|
||||
|
||||
// DOM manipulation after content loaded should not care if the element
|
||||
// came from the preloader.
|
||||
mPreloadedPreconnects.Clear();
|
||||
|
||||
if (mTiming) {
|
||||
mTiming->NotifyDOMContentLoadedStart(nsIDocument::GetDocumentURI());
|
||||
}
|
||||
@@ -8741,45 +8715,22 @@ struct SubDocEnumArgs
|
||||
void *data;
|
||||
};
|
||||
|
||||
static PLDHashOperator
|
||||
SubDocHashEnum(PLDHashTable *table, PLDHashEntryHdr *hdr,
|
||||
uint32_t number, void *arg)
|
||||
{
|
||||
SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
|
||||
SubDocEnumArgs *args = static_cast<SubDocEnumArgs*>(arg);
|
||||
|
||||
nsIDocument *subdoc = entry->mSubDocument;
|
||||
bool next = subdoc ? args->callback(subdoc, args->data) : true;
|
||||
|
||||
return next ? PL_DHASH_NEXT : PL_DHASH_STOP;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::EnumerateSubDocuments(nsSubDocEnumFunc aCallback, void *aData)
|
||||
{
|
||||
if (mSubDocuments) {
|
||||
SubDocEnumArgs args = { aCallback, aData };
|
||||
PL_DHashTableEnumerate(mSubDocuments, SubDocHashEnum, &args);
|
||||
}
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
CanCacheSubDocument(PLDHashTable *table, PLDHashEntryHdr *hdr,
|
||||
uint32_t number, void *arg)
|
||||
{
|
||||
SubDocMapEntry *entry = static_cast<SubDocMapEntry*>(hdr);
|
||||
bool *canCacheArg = static_cast<bool*>(arg);
|
||||
|
||||
nsIDocument *subdoc = entry->mSubDocument;
|
||||
|
||||
// The aIgnoreRequest we were passed is only for us, so don't pass it on.
|
||||
bool canCache = subdoc ? subdoc->CanSavePresentation(nullptr) : false;
|
||||
if (!canCache) {
|
||||
*canCacheArg = false;
|
||||
return PL_DHASH_STOP;
|
||||
if (!mSubDocuments) {
|
||||
return;
|
||||
}
|
||||
|
||||
return PL_DHASH_NEXT;
|
||||
PLDHashTable::Iterator iter(mSubDocuments);
|
||||
while (iter.HasMoreEntries()) {
|
||||
auto entry = static_cast<SubDocMapEntry*>(iter.NextEntry());
|
||||
nsIDocument* subdoc = entry->mSubDocument;
|
||||
bool next = subdoc ? aCallback(subdoc, aData) : true;
|
||||
if (!next) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef DEBUG_bryner
|
||||
@@ -8872,11 +8823,21 @@ nsDocument::CanSavePresentation(nsIRequest *aNewRequest)
|
||||
return false;
|
||||
}
|
||||
|
||||
bool canCache = true;
|
||||
if (mSubDocuments)
|
||||
PL_DHashTableEnumerate(mSubDocuments, CanCacheSubDocument, &canCache);
|
||||
if (mSubDocuments) {
|
||||
PLDHashTable::Iterator iter(mSubDocuments);
|
||||
while (iter.HasMoreEntries()) {
|
||||
auto entry = static_cast<SubDocMapEntry*>(iter.NextEntry());
|
||||
nsIDocument* subdoc = entry->mSubDocument;
|
||||
|
||||
return canCache;
|
||||
// The aIgnoreRequest we were passed is only for us, so don't pass it on.
|
||||
bool canCache = subdoc ? subdoc->CanSavePresentation(nullptr) : false;
|
||||
if (!canCache) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void
|
||||
@@ -9804,6 +9765,23 @@ nsDocument::MaybePreLoadImage(nsIURI* uri, const nsAString &aCrossOriginAttr,
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::MaybePreconnect(nsIURI* uri)
|
||||
{
|
||||
if (mPreloadedPreconnects.Contains(uri)) {
|
||||
return;
|
||||
}
|
||||
mPreloadedPreconnects.Put(uri, true);
|
||||
|
||||
nsCOMPtr<nsISpeculativeConnect>
|
||||
speculator(do_QueryInterface(nsContentUtils::GetIOService()));
|
||||
if (!speculator) {
|
||||
return;
|
||||
}
|
||||
|
||||
speculator->SpeculativeConnect(uri, nullptr);
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::ForgetImagePreload(nsIURI* aURI)
|
||||
{
|
||||
@@ -10580,6 +10558,38 @@ nsDocument::GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins)
|
||||
EnumerateSubDocuments(AllSubDocumentPluginEnum, &aPlugins);
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsDocument::AddResponsiveContent(nsIContent* aContent)
|
||||
{
|
||||
MOZ_ASSERT(aContent);
|
||||
MOZ_ASSERT(aContent->IsHTMLElement(nsGkAtoms::img));
|
||||
mResponsiveContent.PutEntry(aContent);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::RemoveResponsiveContent(nsIContent* aContent)
|
||||
{
|
||||
MOZ_ASSERT(aContent);
|
||||
mResponsiveContent.RemoveEntry(aContent);
|
||||
}
|
||||
|
||||
static PLDHashOperator
|
||||
NotifyMediaFeatureEnum(nsPtrHashKey<nsIContent>* aContent, void* userArg)
|
||||
{
|
||||
nsCOMPtr<nsIContent> content = aContent->GetKey();
|
||||
if (content->IsHTMLElement(nsGkAtoms::img)) {
|
||||
static_cast<HTMLImageElement*>(content.get())->MediaFeatureValuesChanged();
|
||||
}
|
||||
return PL_DHASH_NEXT;
|
||||
}
|
||||
|
||||
void
|
||||
nsDocument::NotifyMediaFeatureValuesChanged()
|
||||
{
|
||||
mResponsiveContent.EnumerateEntries(NotifyMediaFeatureEnum, nullptr);
|
||||
}
|
||||
|
||||
PLDHashOperator LockEnumerator(imgIRequest* aKey,
|
||||
uint32_t aData,
|
||||
void* userArg)
|
||||
|
||||
+24
-5
@@ -18,7 +18,6 @@
|
||||
#include "nsCRT.h"
|
||||
#include "nsWeakReference.h"
|
||||
#include "nsWeakPtr.h"
|
||||
#include "nsVoidArray.h"
|
||||
#include "nsTArray.h"
|
||||
#include "nsIDOMXMLDocument.h"
|
||||
#include "nsIDOMDocumentXBL.h"
|
||||
@@ -151,8 +150,8 @@ public:
|
||||
/**
|
||||
* Returns the list of all elements associated with this id.
|
||||
*/
|
||||
const nsSmallVoidArray* GetIdElements() const {
|
||||
return &mIdContentList;
|
||||
const nsTArray<Element*>& GetIdElements() const {
|
||||
return mIdContentList;
|
||||
}
|
||||
/**
|
||||
* If this entry has a non-null image element set (using SetImageElement),
|
||||
@@ -228,7 +227,7 @@ private:
|
||||
|
||||
// empty if there are no elements with this ID.
|
||||
// The elements are stored as weak pointers.
|
||||
nsSmallVoidArray mIdContentList;
|
||||
nsTArray<Element*> mIdContentList;
|
||||
nsRefPtr<nsBaseContentList> mNameContentList;
|
||||
nsAutoPtr<nsTHashtable<ChangeCallbackEntry> > mChangeCallbacks;
|
||||
nsRefPtr<Element> mImageElement;
|
||||
@@ -1119,6 +1118,8 @@ public:
|
||||
ReferrerPolicy aReferrerPolicy) override;
|
||||
virtual void ForgetImagePreload(nsIURI* aURI) override;
|
||||
|
||||
virtual void MaybePreconnect(nsIURI* uri) override;
|
||||
|
||||
virtual void PreloadStyle(nsIURI* uri, const nsAString& charset,
|
||||
const nsAString& aCrossOriginAttr,
|
||||
ReferrerPolicy aReferrerPolicy) override;
|
||||
@@ -1139,7 +1140,7 @@ public:
|
||||
virtual void SetChangeScrollPosWhenScrollingToRef(bool aValue) override;
|
||||
|
||||
virtual Element *GetElementById(const nsAString& aElementId) override;
|
||||
virtual const nsSmallVoidArray* GetAllElementsForId(const nsAString& aElementId) const override;
|
||||
virtual const nsTArray<Element*>* GetAllElementsForId(const nsAString& aElementId) const override;
|
||||
|
||||
virtual Element *LookupImageElement(const nsAString& aElementId) override;
|
||||
virtual void MozSetImageElement(const nsAString& aImageElementId,
|
||||
@@ -1159,6 +1160,16 @@ public:
|
||||
// the frame and any subframes.
|
||||
virtual void GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins) override;
|
||||
|
||||
// Adds an element to mResponsiveContent when the element is
|
||||
// added to the tree.
|
||||
virtual nsresult AddResponsiveContent(nsIContent* aContent) override;
|
||||
// Removes an element from mResponsiveContent when the element is
|
||||
// removed from the tree.
|
||||
virtual void RemoveResponsiveContent(nsIContent* aContent) override;
|
||||
// Notifies any responsive content added by AddResponsiveContent upon media
|
||||
// features values changing.
|
||||
virtual void NotifyMediaFeatureValuesChanged() override;
|
||||
|
||||
virtual nsresult GetStateObject(nsIVariant** aResult) override;
|
||||
|
||||
virtual nsDOMNavigationTiming* GetNavigationTiming() const override;
|
||||
@@ -1756,6 +1767,9 @@ private:
|
||||
bool mStyledLinksCleared;
|
||||
#endif
|
||||
|
||||
// A set of responsive images keyed by address pointer.
|
||||
nsTHashtable< nsPtrHashKey<nsIContent> > mResponsiveContent;
|
||||
|
||||
// Member to store out last-selected stylesheet set.
|
||||
nsString mLastStyleSheetSet;
|
||||
|
||||
@@ -1774,6 +1788,11 @@ private:
|
||||
// about it anymore.
|
||||
nsRefPtrHashtable<nsURIHashKey, imgIRequest> mPreloadingImages;
|
||||
|
||||
// A list of preconnects initiated by the preloader. This prevents
|
||||
// the same uri from being used more than once, and allows the dom
|
||||
// builder to not repeat the work of the preloader.
|
||||
nsDataHashtable< nsURIHashKey, bool> mPreloadedPreconnects;
|
||||
|
||||
// Current depth of picture elements from parser
|
||||
int32_t mPreloadPictureDepth;
|
||||
|
||||
|
||||
@@ -5012,6 +5012,12 @@ nsGlobalWindow::GetOuterSize(ErrorResult& aError)
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
if (nsContentUtils::ShouldResistFingerprinting(mDocShell)) {
|
||||
CSSIntSize size;
|
||||
aError = GetInnerSize(size);
|
||||
return nsIntSize(size.width, size.height);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
|
||||
if (!treeOwnerAsWin) {
|
||||
aError.Throw(NS_ERROR_FAILURE);
|
||||
@@ -5176,6 +5182,11 @@ nsGlobalWindow::GetScreenXY(ErrorResult& aError)
|
||||
{
|
||||
MOZ_ASSERT(IsOuterWindow());
|
||||
|
||||
// When resisting fingerprinting, always return (0,0)
|
||||
if (nsContentUtils::ShouldResistFingerprinting(mDocShell)) {
|
||||
return nsIntPoint(0, 0);
|
||||
}
|
||||
|
||||
nsCOMPtr<nsIBaseWindow> treeOwnerAsWin = GetTreeOwnerWindow();
|
||||
if (!treeOwnerAsWin) {
|
||||
aError.Throw(NS_ERROR_FAILURE);
|
||||
@@ -5249,6 +5260,11 @@ nsGlobalWindow::GetMozInnerScreenX(ErrorResult& aError)
|
||||
{
|
||||
FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenX, (aError), aError, 0);
|
||||
|
||||
// When resisting fingerprinting, always return 0.
|
||||
if (nsContentUtils::ShouldResistFingerprinting(mDocShell)) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
nsRect r = GetInnerScreenRect();
|
||||
return nsPresContext::AppUnitsToFloatCSSPixels(r.x);
|
||||
}
|
||||
@@ -5267,6 +5283,11 @@ nsGlobalWindow::GetMozInnerScreenY(ErrorResult& aError)
|
||||
{
|
||||
FORWARD_TO_OUTER_OR_THROW(GetMozInnerScreenY, (aError), aError, 0);
|
||||
|
||||
// Return 0 to prevent fingerprinting.
|
||||
if (nsContentUtils::ShouldResistFingerprinting(mDocShell)) {
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
nsRect r = GetInnerScreenRect();
|
||||
return nsPresContext::AppUnitsToFloatCSSPixels(r.y);
|
||||
}
|
||||
@@ -5295,6 +5316,10 @@ nsGlobalWindow::GetDevicePixelRatio(ErrorResult& aError)
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
if (nsContentUtils::ShouldResistFingerprinting(mDocShell)) {
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
return float(nsPresContext::AppUnitsPerCSSPixel())/
|
||||
presContext->AppUnitsPerDevPixel();
|
||||
}
|
||||
|
||||
@@ -1051,6 +1051,8 @@ public:
|
||||
bool aShowDialog, mozilla::ErrorResult& aError);
|
||||
uint64_t GetMozPaintCount(mozilla::ErrorResult& aError);
|
||||
|
||||
bool ShouldResistFingerprinting();
|
||||
|
||||
mozilla::dom::MozSelfSupport* GetMozSelfSupport(mozilla::ErrorResult& aError);
|
||||
|
||||
already_AddRefed<nsIDOMWindow> OpenDialog(JSContext* aCx,
|
||||
|
||||
+10
-3
@@ -80,7 +80,6 @@ class nsSMILAnimationController;
|
||||
class nsStyleSet;
|
||||
class nsTextNode;
|
||||
class nsWindowSizes;
|
||||
class nsSmallVoidArray;
|
||||
class nsDOMCaretPosition;
|
||||
class nsViewportInfo;
|
||||
class nsIGlobalObject;
|
||||
@@ -2075,6 +2074,11 @@ public:
|
||||
*/
|
||||
virtual bool IsDocumentRightToLeft() { return false; }
|
||||
|
||||
/**
|
||||
* Called by Parser for link rel=preconnect
|
||||
*/
|
||||
virtual void MaybePreconnect(nsIURI* uri) = 0;
|
||||
|
||||
enum DocumentTheme {
|
||||
Doc_Theme_Uninitialized, // not determined yet
|
||||
Doc_Theme_None,
|
||||
@@ -2122,9 +2126,8 @@ public:
|
||||
/**
|
||||
* This method returns _all_ the elements in this document which
|
||||
* have id aElementId, if there are any. Otherwise it returns null.
|
||||
* The entries of the nsSmallVoidArray are Element*
|
||||
*/
|
||||
virtual const nsSmallVoidArray* GetAllElementsForId(const nsAString& aElementId) const = 0;
|
||||
virtual const nsTArray<Element*>* GetAllElementsForId(const nsAString& aElementId) const = 0;
|
||||
|
||||
/**
|
||||
* Lookup an image element using its associated ID, which is usually provided
|
||||
@@ -2191,6 +2194,10 @@ public:
|
||||
virtual void RemovePlugin(nsIObjectLoadingContent* aPlugin) = 0;
|
||||
virtual void GetPlugins(nsTArray<nsIObjectLoadingContent*>& aPlugins) = 0;
|
||||
|
||||
virtual nsresult AddResponsiveContent(nsIContent* aContent) = 0;
|
||||
virtual void RemoveResponsiveContent(nsIContent* aContent) = 0;
|
||||
virtual void NotifyMediaFeatureValuesChanged() = 0;
|
||||
|
||||
virtual nsresult GetStateObject(nsIVariant** aResult) = 0;
|
||||
|
||||
virtual nsDOMNavigationTiming* GetNavigationTiming() const = 0;
|
||||
|
||||
@@ -2716,8 +2716,7 @@ FindMatchingElementsWithId(const nsAString& aId, nsINode* aRoot,
|
||||
"document if it's in the document. Note that document fragments "
|
||||
"can't be IsInDoc(), so should never show up here.");
|
||||
|
||||
const nsSmallVoidArray* elements = aRoot->OwnerDoc()->GetAllElementsForId(aId);
|
||||
|
||||
const nsTArray<Element*>* elements = aRoot->OwnerDoc()->GetAllElementsForId(aId);
|
||||
if (!elements) {
|
||||
// Nothing to do; we're done
|
||||
return;
|
||||
@@ -2725,8 +2724,8 @@ FindMatchingElementsWithId(const nsAString& aId, nsINode* aRoot,
|
||||
|
||||
// XXXbz: Should we fall back to the tree walk if aRoot is not the
|
||||
// document and |elements| is long, for some value of "long"?
|
||||
for (int32_t i = 0; i < elements->Count(); ++i) {
|
||||
Element *element = static_cast<Element*>(elements->ElementAt(i));
|
||||
for (size_t i = 0; i < elements->Length(); ++i) {
|
||||
Element* element = (*elements)[i];
|
||||
if (!aRoot->IsElement() ||
|
||||
(element != aRoot &&
|
||||
nsContentUtils::ContentIsDescendantOf(element, aRoot))) {
|
||||
|
||||
+58
-15
@@ -68,6 +68,11 @@ NS_IMPL_RELEASE_INHERITED(nsScreen, DOMEventTargetHelper)
|
||||
int32_t
|
||||
nsScreen::GetPixelDepth(ErrorResult& aRv)
|
||||
{
|
||||
// Return 24 to prevent fingerprinting.
|
||||
if (ShouldResistFingerprinting()) {
|
||||
return 24;
|
||||
}
|
||||
|
||||
nsDeviceContext* context = GetDeviceContext();
|
||||
|
||||
if (!context) {
|
||||
@@ -111,6 +116,11 @@ nsScreen::GetDeviceContext()
|
||||
nsresult
|
||||
nsScreen::GetRect(nsRect& aRect)
|
||||
{
|
||||
// Return window inner rect to prevent fingerprinting.
|
||||
if (ShouldResistFingerprinting()) {
|
||||
return GetWindowInnerRect(aRect);
|
||||
}
|
||||
|
||||
nsDeviceContext *context = GetDeviceContext();
|
||||
|
||||
if (!context) {
|
||||
@@ -130,6 +140,11 @@ nsScreen::GetRect(nsRect& aRect)
|
||||
nsresult
|
||||
nsScreen::GetAvailRect(nsRect& aRect)
|
||||
{
|
||||
// Return window inner rect to prevent fingerprinting.
|
||||
if (ShouldResistFingerprinting()) {
|
||||
return GetWindowInnerRect(aRect);
|
||||
}
|
||||
|
||||
nsDeviceContext *context = GetDeviceContext();
|
||||
|
||||
if (!context) {
|
||||
@@ -166,22 +181,26 @@ nsScreen::Notify(const hal::ScreenConfiguration& aConfiguration)
|
||||
void
|
||||
nsScreen::GetMozOrientation(nsString& aOrientation)
|
||||
{
|
||||
switch (mOrientation) {
|
||||
case eScreenOrientation_PortraitPrimary:
|
||||
aOrientation.AssignLiteral("portrait-primary");
|
||||
break;
|
||||
case eScreenOrientation_PortraitSecondary:
|
||||
aOrientation.AssignLiteral("portrait-secondary");
|
||||
break;
|
||||
case eScreenOrientation_LandscapePrimary:
|
||||
if (ShouldResistFingerprinting()) {
|
||||
aOrientation.AssignLiteral("landscape-primary");
|
||||
break;
|
||||
case eScreenOrientation_LandscapeSecondary:
|
||||
aOrientation.AssignLiteral("landscape-secondary");
|
||||
break;
|
||||
case eScreenOrientation_None:
|
||||
default:
|
||||
MOZ_CRASH("Unacceptable mOrientation value");
|
||||
} else {
|
||||
switch (mOrientation) {
|
||||
case eScreenOrientation_PortraitPrimary:
|
||||
aOrientation.AssignLiteral("portrait-primary");
|
||||
break;
|
||||
case eScreenOrientation_PortraitSecondary:
|
||||
aOrientation.AssignLiteral("portrait-secondary");
|
||||
break;
|
||||
case eScreenOrientation_LandscapePrimary:
|
||||
aOrientation.AssignLiteral("landscape-primary");
|
||||
break;
|
||||
case eScreenOrientation_LandscapeSecondary:
|
||||
aOrientation.AssignLiteral("landscape-secondary");
|
||||
break;
|
||||
case eScreenOrientation_None:
|
||||
default:
|
||||
MOZ_CRASH("Unacceptable mOrientation value");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -373,3 +392,27 @@ nsScreen::FullScreenEventListener::HandleEvent(nsIDOMEvent* aEvent)
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
nsScreen::GetWindowInnerRect(nsRect& aRect)
|
||||
{
|
||||
aRect.x = 0;
|
||||
aRect.y = 0;
|
||||
nsCOMPtr<nsIDOMWindow> win = GetOwner();
|
||||
if (!win) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
nsresult rv = win->GetInnerWidth(&aRect.width);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
return win->GetInnerHeight(&aRect.height);
|
||||
}
|
||||
|
||||
bool nsScreen::ShouldResistFingerprinting() const
|
||||
{
|
||||
bool resist = false;
|
||||
nsCOMPtr<nsPIDOMWindow> owner = GetOwner();
|
||||
if (owner) {
|
||||
resist = nsContentUtils::ShouldResistFingerprinting(owner->GetDocShell());
|
||||
}
|
||||
return resist;
|
||||
}
|
||||
|
||||
@@ -131,6 +131,7 @@ protected:
|
||||
nsDeviceContext* GetDeviceContext();
|
||||
nsresult GetRect(nsRect& aRect);
|
||||
nsresult GetAvailRect(nsRect& aRect);
|
||||
nsresult GetWindowInnerRect(nsRect& aRect);
|
||||
|
||||
mozilla::dom::ScreenOrientation mOrientation;
|
||||
|
||||
@@ -158,6 +159,8 @@ private:
|
||||
|
||||
bool IsDeviceSizePageSize();
|
||||
|
||||
bool ShouldResistFingerprinting() const;
|
||||
|
||||
nsRefPtr<FullScreenEventListener> mEventListener;
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,71 @@
|
||||
// The main test function.
|
||||
let test = function (isContent) {
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
let { ww } = SpecialPowers.Services;
|
||||
window.chromeWindow = ww.activeWindow;
|
||||
|
||||
// The pairs of values expected to be the same when
|
||||
// fingerprinting resistance is enabled.
|
||||
let pairs = [
|
||||
["screenX", 0],
|
||||
["screenY", 0],
|
||||
["mozInnerScreenX", 0],
|
||||
["mozInnerScreenY", 0],
|
||||
["screen.pixelDepth", 24],
|
||||
["screen.colorDepth", 24],
|
||||
["screen.availWidth", "innerWidth"],
|
||||
["screen.availHeight", "innerHeight"],
|
||||
["screen.left", 0],
|
||||
["screen.top", 0],
|
||||
["screen.availLeft", 0],
|
||||
["screen.availTop", 0],
|
||||
["screen.width", "innerWidth"],
|
||||
["screen.height", "innerHeight"],
|
||||
["screen.mozOrientation", "'landscape-primary'"],
|
||||
["devicePixelRatio", 1]
|
||||
];
|
||||
|
||||
// checkPair: tests if members of pair [a, b] are equal when evaluated.
|
||||
let checkPair = function (a, b) {
|
||||
is(eval(a), eval(b), a + " should be equal to " + b);
|
||||
};
|
||||
|
||||
// Returns generator object that iterates through pref values.
|
||||
let prefVals = (for (prefVal of [false, true]) prefVal);
|
||||
|
||||
// The main test function, runs until all pref values are exhausted.
|
||||
let nextTest = function () {
|
||||
let {value : prefValue, done} = prefVals.next();
|
||||
if (done) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
SpecialPowers.pushPrefEnv({set : [["privacy.resistFingerprinting", prefValue]]},
|
||||
function () {
|
||||
// We will be resisting fingerprinting if the pref is enabled,
|
||||
// and we are in a content script (not chrome).
|
||||
let resisting = prefValue && isContent;
|
||||
// Check each of the pairs.
|
||||
pairs.map(function ([item, onVal]) {
|
||||
if (resisting) {
|
||||
checkPair("window." + item, onVal);
|
||||
} else {
|
||||
if (!item.startsWith("moz")) {
|
||||
checkPair("window." + item, "chromeWindow." + item);
|
||||
}
|
||||
}
|
||||
});
|
||||
if (!resisting) {
|
||||
// Hard to predict these values, but we can enforce constraints:
|
||||
ok(window.mozInnerScreenX >= chromeWindow.mozInnerScreenX,
|
||||
"mozInnerScreenX");
|
||||
ok(window.mozInnerScreenY >= chromeWindow.mozInnerScreenY,
|
||||
"mozInnerScreenY");
|
||||
}
|
||||
nextTest();
|
||||
});
|
||||
}
|
||||
|
||||
nextTest();
|
||||
}
|
||||
@@ -3,6 +3,7 @@ skip-if = buildapp == 'b2g'
|
||||
support-files =
|
||||
blockNoPlugins.xml
|
||||
blockPluginHard.xml
|
||||
bug418986-1.js
|
||||
cpows_child.js
|
||||
cpows_parent.xul
|
||||
file_bug391728.html
|
||||
@@ -29,6 +30,7 @@ support-files =
|
||||
[test_bug380418.html^headers^]
|
||||
[test_bug383430.html]
|
||||
[test_bug391728.html]
|
||||
[test_bug418986-1.xul]
|
||||
[test_bug421622.xul]
|
||||
[test_bug429785.xul]
|
||||
[test_bug430050.xul]
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css"
|
||||
type="text/css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=418986-1
|
||||
-->
|
||||
<window title="Mozilla Bug 418986 (Part 1)"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript"
|
||||
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=418986-1"
|
||||
target="_blank">Mozilla Bug 418986 (Part 1)</a>
|
||||
|
||||
<script type="application/javascript;version=1.7" src="bug418986-1.js"></script>
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript"><![CDATA[
|
||||
window.onload = function() {
|
||||
test(false);
|
||||
};
|
||||
]]></script>
|
||||
</body>
|
||||
</window>
|
||||
@@ -56,6 +56,7 @@ support-files =
|
||||
bug704320.sjs
|
||||
bug704320_counter.sjs
|
||||
bug819051.sjs
|
||||
chrome/bug418986-1.js
|
||||
copypaste.js
|
||||
delayedServerEvents.sjs
|
||||
echo.sjs
|
||||
@@ -446,6 +447,7 @@ support-files = test_bug402150.html^headers^
|
||||
[test_bug417255.html]
|
||||
[test_bug417384.html]
|
||||
[test_bug418214.html]
|
||||
[test_bug418986-1.html]
|
||||
[test_bug419132.html]
|
||||
[test_bug419527.xhtml]
|
||||
[test_bug420609.xhtml]
|
||||
|
||||
@@ -0,0 +1,24 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=418986
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test 1/3 for Bug 418986 - Resist fingerprinting by preventing exposure of screen and system info</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<script type="application/javascript;version=1.7" src="chrome/bug418986-1.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418986">Bug 418986</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none"></div>
|
||||
<pre id="test"></pre>
|
||||
<script>
|
||||
window.onload = function() {
|
||||
test(true);
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -883,6 +883,13 @@ Event::GetScreenCoords(nsPresContext* aPresContext,
|
||||
WidgetEvent* aEvent,
|
||||
LayoutDeviceIntPoint aPoint)
|
||||
{
|
||||
if (!nsContentUtils::IsCallerChrome() &&
|
||||
nsContentUtils::ResistFingerprinting()) {
|
||||
// When resisting fingerprinting, return client coordinates instead.
|
||||
CSSIntPoint clientCoords = GetClientCoords(aPresContext, aEvent, aPoint, CSSIntPoint(0, 0));
|
||||
return LayoutDeviceIntPoint(clientCoords.x, clientCoords.y);
|
||||
}
|
||||
|
||||
if (EventStateManager::sIsPointerLocked) {
|
||||
return EventStateManager::sLastScreenPoint;
|
||||
}
|
||||
|
||||
@@ -380,6 +380,10 @@ EventListenerManager::AddEventListenerInternal(
|
||||
if (aTypeAtom && mTarget) {
|
||||
mTarget->EventListenerAdded(aTypeAtom);
|
||||
}
|
||||
|
||||
if (mIsMainThreadELM && mTarget) {
|
||||
EventListenerService::NotifyAboutMainThreadListenerChange(mTarget);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -497,6 +501,9 @@ EventListenerManager::RemoveEventListenerInternal(
|
||||
if (mTarget && aUserType) {
|
||||
mTarget->EventListenerRemoved(aUserType);
|
||||
}
|
||||
if (mIsMainThreadELM && mTarget) {
|
||||
EventListenerService::NotifyAboutMainThreadListenerChange(mTarget);
|
||||
}
|
||||
|
||||
if (!deviceType
|
||||
#ifdef MOZ_B2G
|
||||
@@ -630,6 +637,9 @@ EventListenerManager::SetEventHandlerInternal(
|
||||
mTarget->EventListenerRemoved(aName);
|
||||
mTarget->EventListenerAdded(aName);
|
||||
}
|
||||
if (mIsMainThreadELM && mTarget) {
|
||||
EventListenerService::NotifyAboutMainThreadListenerChange(mTarget);
|
||||
}
|
||||
}
|
||||
|
||||
// Set flag to indicate possible need for compilation later
|
||||
@@ -757,6 +767,9 @@ EventListenerManager::RemoveEventHandler(nsIAtom* aName,
|
||||
if (mTarget && aName) {
|
||||
mTarget->EventListenerRemoved(aName);
|
||||
}
|
||||
if (mIsMainThreadELM && mTarget) {
|
||||
EventListenerService::NotifyAboutMainThreadListenerChange(mTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,8 @@
|
||||
#include "nsJSUtils.h"
|
||||
#include "nsMemory.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsArray.h"
|
||||
#include "nsThreadUtils.h"
|
||||
|
||||
namespace mozilla {
|
||||
|
||||
@@ -128,6 +130,21 @@ EventListenerInfo::ToSource(nsAString& aResult)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
EventListenerService*
|
||||
EventListenerService::sInstance = nullptr;
|
||||
|
||||
EventListenerService::EventListenerService()
|
||||
{
|
||||
MOZ_ASSERT(!sInstance);
|
||||
sInstance = this;
|
||||
}
|
||||
|
||||
EventListenerService::~EventListenerService()
|
||||
{
|
||||
MOZ_ASSERT(sInstance == this);
|
||||
sInstance = nullptr;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EventListenerService::GetListenerInfoFor(nsIDOMEventTarget* aEventTarget,
|
||||
uint32_t* aCount,
|
||||
@@ -289,6 +306,58 @@ EventListenerService::RemoveListenerForAllEvents(nsIDOMEventTarget* aTarget,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
EventListenerService::AddListenerChangeListener(nsIListenerChangeListener* aListener)
|
||||
{
|
||||
if (!mChangeListeners.Contains(aListener)) {
|
||||
mChangeListeners.AppendElement(aListener);
|
||||
}
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
NS_IMETHODIMP
|
||||
EventListenerService::RemoveListenerChangeListener(nsIListenerChangeListener* aListener)
|
||||
{
|
||||
mChangeListeners.RemoveElement(aListener);
|
||||
return NS_OK;
|
||||
};
|
||||
|
||||
void
|
||||
EventListenerService::NotifyAboutMainThreadListenerChangeInternal(dom::EventTarget* aTarget)
|
||||
{
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
if (mChangeListeners.IsEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mPendingListenerChanges) {
|
||||
mPendingListenerChanges = nsArrayBase::Create();
|
||||
nsCOMPtr<nsIRunnable> runnable = NS_NewRunnableMethod(this,
|
||||
&EventListenerService::NotifyPendingChanges);
|
||||
NS_DispatchToCurrentThread(runnable);
|
||||
}
|
||||
|
||||
if (!mPendingListenerChangesSet.Get(aTarget)) {
|
||||
mPendingListenerChanges->AppendElement(aTarget, false);
|
||||
mPendingListenerChangesSet.Put(aTarget, true);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
EventListenerService::NotifyPendingChanges()
|
||||
{
|
||||
nsCOMPtr<nsIMutableArray> changes;
|
||||
mPendingListenerChanges.swap(changes);
|
||||
mPendingListenerChangesSet.Clear();
|
||||
|
||||
nsTObserverArray<nsCOMPtr<nsIListenerChangeListener>>::EndLimitedIterator
|
||||
iter(mChangeListeners);
|
||||
while (iter.HasMore()) {
|
||||
nsCOMPtr<nsIListenerChangeListener> listener = iter.GetNext();
|
||||
listener->ListenersChanged(changes);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
nsresult
|
||||
|
||||
@@ -14,8 +14,15 @@
|
||||
#include "nsIDOMEventListener.h"
|
||||
#include "nsIEventListenerService.h"
|
||||
#include "nsString.h"
|
||||
#include "nsTObserverArray.h"
|
||||
#include "nsDataHashtable.h"
|
||||
|
||||
class nsIMutableArray;
|
||||
|
||||
namespace mozilla {
|
||||
namespace dom {
|
||||
class EventTarget;
|
||||
};
|
||||
|
||||
template<typename T>
|
||||
class Maybe;
|
||||
@@ -57,10 +64,27 @@ protected:
|
||||
|
||||
class EventListenerService final : public nsIEventListenerService
|
||||
{
|
||||
~EventListenerService() {}
|
||||
~EventListenerService();
|
||||
public:
|
||||
EventListenerService();
|
||||
NS_DECL_ISUPPORTS
|
||||
NS_DECL_NSIEVENTLISTENERSERVICE
|
||||
|
||||
static void NotifyAboutMainThreadListenerChange(dom::EventTarget* aTarget)
|
||||
{
|
||||
if (sInstance) {
|
||||
sInstance->NotifyAboutMainThreadListenerChangeInternal(aTarget);
|
||||
}
|
||||
}
|
||||
|
||||
void NotifyPendingChanges();
|
||||
private:
|
||||
void NotifyAboutMainThreadListenerChangeInternal(dom::EventTarget* aTarget);
|
||||
nsTObserverArray<nsCOMPtr<nsIListenerChangeListener>> mChangeListeners;
|
||||
nsCOMPtr<nsIMutableArray> mPendingListenerChanges;
|
||||
nsDataHashtable<nsISupportsHashKey, bool> mPendingListenerChangesSet;
|
||||
|
||||
static EventListenerService* sInstance;
|
||||
};
|
||||
|
||||
} // namespace mozilla
|
||||
|
||||
@@ -7,6 +7,13 @@
|
||||
|
||||
interface nsIDOMEventListener;
|
||||
interface nsIDOMEventTarget;
|
||||
interface nsIArray;
|
||||
|
||||
[scriptable, function, uuid(8d5b5a6b-dec0-473d-86c4-591801dfaac1)]
|
||||
interface nsIListenerChangeListener : nsISupports
|
||||
{
|
||||
void listenersChanged(in nsIArray aEventTargets);
|
||||
};
|
||||
|
||||
/**
|
||||
* An instance of this interface describes how an event listener
|
||||
@@ -39,7 +46,7 @@ interface nsIEventListenerInfo : nsISupports
|
||||
AString toSource();
|
||||
};
|
||||
|
||||
[scriptable, uuid(f6964bfb-dabe-4cab-9733-be0ee2bf8171)]
|
||||
[scriptable, uuid(77aab5f7-213d-4db4-9f22-e46dfb774f15)]
|
||||
interface nsIEventListenerService : nsISupports
|
||||
{
|
||||
/**
|
||||
@@ -96,5 +103,8 @@ interface nsIEventListenerService : nsISupports
|
||||
in nsIDOMEventListener listener,
|
||||
[optional] in boolean aUseCapture,
|
||||
[optional] in boolean aSystemEventGroup);
|
||||
|
||||
void addListenerChangeListener(in nsIListenerChangeListener aListener);
|
||||
void removeListenerChangeListener(in nsIListenerChangeListener aListener);
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// The main testing function.
|
||||
let test = function (isContent) {
|
||||
// Each definition is [eventType, prefSetting]
|
||||
// Where we are setting the "privacy.resistFingerprinting" pref.
|
||||
let eventDefs = [["mousedown", true],
|
||||
["mouseup", true],
|
||||
["mousedown", false],
|
||||
["mouseup", false]];
|
||||
|
||||
let testCounter = 0;
|
||||
|
||||
// Declare ahead of time.
|
||||
let setup;
|
||||
|
||||
// This function is called when the event handler fires.
|
||||
let handleEvent = function (event, prefVal) {
|
||||
let resisting = prefVal && isContent;
|
||||
if (resisting) {
|
||||
is(event.screenX, event.clientX, "event.screenX and event.clientX should be the same");
|
||||
is(event.screenY, event.clientY, "event.screenY and event.clientY should be the same");
|
||||
} else {
|
||||
// We can't be sure about X coordinates not being equal, but we can test Y.
|
||||
isnot(event.screenY, event.clientY, "event.screenY !== event.clientY");
|
||||
}
|
||||
++testCounter;
|
||||
if (testCounter < eventDefs.length) {
|
||||
nextTest();
|
||||
} else {
|
||||
SimpleTest.finish();
|
||||
}
|
||||
};
|
||||
|
||||
// In this function, we set up the nth div and event handler,
|
||||
// and then synthesize a mouse event in the div, to test
|
||||
// whether the resulting events resist fingerprinting by
|
||||
// suppressing absolute screen coordinates.
|
||||
nextTest = function () {
|
||||
let [eventType, prefVal] = eventDefs[testCounter];
|
||||
SpecialPowers.pushPrefEnv({set:[["privacy.resistFingerprinting", prefVal]]},
|
||||
function () {
|
||||
// The following code creates a new div for each event in eventDefs,
|
||||
// attaches a listener to listen for the event, and then generates
|
||||
// a fake event at the center of the div.
|
||||
let div = document.createElement("div");
|
||||
div.style.width = "10px";
|
||||
div.style.height = "10px";
|
||||
div.style.backgroundColor = "red";
|
||||
// Name the div after the event we're listening for.
|
||||
div.id = eventType;
|
||||
document.getElementById("body").appendChild(div);
|
||||
// Seems we can't add an event listener in chrome unless we run
|
||||
// it in a later task.
|
||||
window.setTimeout(function() {
|
||||
div.addEventListener(eventType, event => handleEvent(event, prefVal), false);
|
||||
// For some reason, the following synthesizeMouseAtCenter call only seems to run if we
|
||||
// wrap it in a window.setTimeout(..., 0).
|
||||
window.setTimeout(function () {
|
||||
synthesizeMouseAtCenter(div, {type : eventType});
|
||||
}, 0);
|
||||
}, 0);
|
||||
});
|
||||
};
|
||||
|
||||
// Now run by starting with the 0th event.
|
||||
nextTest();
|
||||
|
||||
};
|
||||
@@ -3,6 +3,7 @@ skip-if = buildapp == 'b2g'
|
||||
support-files =
|
||||
bug415498-doc1.html
|
||||
bug415498-doc2.html
|
||||
bug418986-3.js
|
||||
bug591249_iframe.xul
|
||||
bug602962.xul
|
||||
file_bug679494.html
|
||||
@@ -12,6 +13,8 @@ support-files =
|
||||
[test_bug336682_2.xul]
|
||||
[test_bug368835.html]
|
||||
[test_bug415498.xul]
|
||||
[test_bug418986-3.xul]
|
||||
[test_bug524674.xul]
|
||||
[test_bug586961.xul]
|
||||
[test_bug591249.xul]
|
||||
[test_bug602962.xul]
|
||||
|
||||
@@ -7,6 +7,7 @@ support-files =
|
||||
bug426082.html
|
||||
bug648573.html
|
||||
bug656379-1.html
|
||||
bug418986-3.js
|
||||
error_event_worker.js
|
||||
empty.js
|
||||
window_bug493251.html
|
||||
@@ -38,6 +39,9 @@ support-files = test_bug336682.js
|
||||
[test_bug409604.html]
|
||||
skip-if = buildapp == 'b2g' || toolkit == 'android' #TIMED_OUT
|
||||
[test_bug412567.html]
|
||||
[test_bug418986-3.html]
|
||||
# Sometimes fails to finish after tests pass on 'B2G ICS Emulator'.
|
||||
skip-if = (os == 'b2g')
|
||||
[test_bug422132.html]
|
||||
skip-if = buildapp == 'b2g' || e10s # b2g(2 failures out of 8, mousewheel test) b2g-debug(2 failures out of 8, mousewheel test) b2g-desktop(2 failures out of 8, mousewheel test)
|
||||
[test_bug426082.html]
|
||||
|
||||
@@ -0,0 +1,25 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=418986
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test 3/3 for Bug 418986 - Resist fingerprinting by preventing exposure of screen and system info</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
</head>
|
||||
<body id="body">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418986">Bug 418986</a>
|
||||
<p id="display"></p>
|
||||
<pre id="test"></pre>
|
||||
<script type="application/javascript;version=1.7" src="bug418986-3.js"></script>
|
||||
<script type="application/javascript;version=1.7">
|
||||
// This test produces fake mouse events and checks that the screenX and screenY
|
||||
// properties of the received event objects provide client window coordinates.
|
||||
// Run the test once the window has loaded.
|
||||
window.onload = () => test(true);
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet href="chrome://global/skin" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://mochikit/content/tests/SimpleTest/test.css" type="text/css"?>
|
||||
<!--
|
||||
Bug 418986
|
||||
-->
|
||||
<window title="Mozilla Bug 418986"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
|
||||
|
||||
<body id="body" xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418986">
|
||||
Mozilla Bug 418986</a>
|
||||
</body>
|
||||
|
||||
<script type="application/javascript;version=1.7" src="bug418986-3.js"></script>
|
||||
<script type="application/javascript;version=1.7"><![CDATA[
|
||||
// This test produces fake mouse events and checks that the screenX and screenY
|
||||
// properties of the received event objects provide client window coordinates.
|
||||
// Run the test once the window has loaded.
|
||||
test(false);
|
||||
]]></script>
|
||||
|
||||
</window>
|
||||
@@ -0,0 +1,106 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=524674
|
||||
-->
|
||||
<window title="Mozilla Bug 524674"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=524674"
|
||||
target="_blank">Mozilla Bug 524674</a>
|
||||
</body>
|
||||
|
||||
<!-- test code goes here -->
|
||||
<script type="application/javascript">
|
||||
<![CDATA[
|
||||
/** Test for Bug 524674 **/
|
||||
|
||||
var els = Components.classes["@mozilla.org/eventlistenerservice;1"]
|
||||
.getService(Components.interfaces.nsIEventListenerService);
|
||||
|
||||
function dummyListener() {}
|
||||
|
||||
var runningTest = null;
|
||||
var d = document.createElementNS("http://www.w3.org/1999/xhtml", "div");
|
||||
var xhr = new XMLHttpRequest();
|
||||
|
||||
// Test also double removals and such.
|
||||
var tests = [
|
||||
function() {
|
||||
els.addListenerChangeListener(changeListener);
|
||||
d.addEventListener("foo", dummyListener);
|
||||
d.addEventListener("foo", dummyListener);
|
||||
xhr.addEventListener("foo", dummyListener);
|
||||
tests[0] = [d, xhr];
|
||||
},
|
||||
function() {
|
||||
d.onclick = dummyListener;
|
||||
d.onclick = dummyListener;
|
||||
xhr.onload = dummyListener;
|
||||
tests[0] = [d, xhr];
|
||||
},
|
||||
function() {
|
||||
d.onclick = function() {};
|
||||
tests[0] = [d];
|
||||
},
|
||||
function() {
|
||||
d.removeEventListener("foo", dummyListener);
|
||||
d.removeEventListener("foo", dummyListener);
|
||||
xhr.removeEventListener("foo", dummyListener);
|
||||
tests[0] = [d, xhr];
|
||||
},
|
||||
function() {
|
||||
d.onclick = null;
|
||||
d.onclick = null;
|
||||
xhr.onload = null;
|
||||
tests[0] = [d, xhr];
|
||||
},
|
||||
function() {
|
||||
els.removeListenerChangeListener(changeListener);
|
||||
// Check that once we've removed the change listener, it isn't called anymore.
|
||||
d.addEventListener("foo", dummyListener);
|
||||
xhr.addEventListener("foo", dummyListener);
|
||||
SimpleTest.executeSoon(function() {
|
||||
SimpleTest.finish();
|
||||
});
|
||||
}
|
||||
];
|
||||
|
||||
SimpleTest.executeSoon(tests[0]);
|
||||
|
||||
function changeListener(array) {
|
||||
if (typeof tests[0] == "function") {
|
||||
return;
|
||||
}
|
||||
var expectedEventTargets = tests[0];
|
||||
var e = array.enumerate();
|
||||
var i = 0;
|
||||
while (e.hasMoreElements()) {
|
||||
var current = e.getNext();
|
||||
var expected = expectedEventTargets[i];
|
||||
if (current == expected) {
|
||||
is(current, expected, current + " = " + expected);
|
||||
// We may get random other event listener changes here too, not just the one from the
|
||||
// test.
|
||||
++i
|
||||
}
|
||||
}
|
||||
if (expectedEventTargets.length != i) {
|
||||
return;
|
||||
}
|
||||
|
||||
is(expectedEventTargets.length, i, "Should have got notification for all the changes.");
|
||||
tests.shift();
|
||||
|
||||
ok(tests.length);
|
||||
SimpleTest.executeSoon(tests[0]);
|
||||
}
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
]]>
|
||||
</script>
|
||||
</window>
|
||||
@@ -17,8 +17,6 @@
|
||||
#include "nsIURL.h"
|
||||
#include "nsIIOService.h"
|
||||
#include "nsIServiceManager.h"
|
||||
#include "nsIAppShell.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
#include "nsNetUtil.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsContainerFrame.h"
|
||||
@@ -53,8 +51,6 @@
|
||||
#include "mozilla/Preferences.h"
|
||||
static const char *kPrefSrcsetEnabled = "dom.image.srcset.enabled";
|
||||
|
||||
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
|
||||
|
||||
NS_IMPL_NS_NEW_HTML_ELEMENT(Image)
|
||||
|
||||
#ifdef DEBUG
|
||||
@@ -591,6 +587,9 @@ HTMLImageElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
|
||||
bool addedToPicture = aParent && aParent->IsHTMLElement(nsGkAtoms::picture) &&
|
||||
HTMLPictureElement::IsPictureEnabled();
|
||||
if (addedToPicture) {
|
||||
if (aDocument) {
|
||||
aDocument->AddResponsiveContent(this);
|
||||
}
|
||||
QueueImageLoadTask();
|
||||
} else if (!InResponsiveMode() &&
|
||||
HasAttr(kNameSpaceID_None, nsGkAtoms::src)) {
|
||||
@@ -632,12 +631,18 @@ HTMLImageElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
}
|
||||
}
|
||||
|
||||
if (aNullParent && GetParent() &&
|
||||
if (GetParent() &&
|
||||
GetParent()->IsHTMLElement(nsGkAtoms::picture) &&
|
||||
HTMLPictureElement::IsPictureEnabled()) {
|
||||
nsIDocument* doc = GetOurOwnerDoc();
|
||||
if (doc) {
|
||||
doc->RemoveResponsiveContent(this);
|
||||
}
|
||||
// Being removed from picture re-triggers selection, even if we
|
||||
// weren't using a <source> peer
|
||||
QueueImageLoadTask();
|
||||
if (aNullParent) {
|
||||
QueueImageLoadTask();
|
||||
}
|
||||
}
|
||||
|
||||
nsImageLoadingContent::UnbindFromTree(aDeep, aNullParent);
|
||||
@@ -888,15 +893,11 @@ HTMLImageElement::QueueImageLoadTask()
|
||||
return;
|
||||
}
|
||||
|
||||
// The task checks this to determine if it was the last queued event, so this
|
||||
// implicitly cancels earlier tasks
|
||||
mPendingImageLoadTask = new ImageLoadTask(this);
|
||||
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
|
||||
if (appShell) {
|
||||
appShell->RunInStableState(mPendingImageLoadTask);
|
||||
} else {
|
||||
MOZ_ASSERT(false, "expect appshell for HTMLImageElement");
|
||||
}
|
||||
nsCOMPtr<nsIRunnable> task = new ImageLoadTask(this);
|
||||
// The task checks this to determine if it was the last
|
||||
// queued event, and so earlier tasks are implicitly canceled.
|
||||
mPendingImageLoadTask = task;
|
||||
nsContentUtils::RunInStableState(task.forget());
|
||||
}
|
||||
|
||||
bool
|
||||
@@ -1082,7 +1083,18 @@ HTMLImageElement::UpdateResponsiveSource()
|
||||
// that and keep it if it's still usable.
|
||||
mResponsiveSelector->SelectImage(true);
|
||||
if (mResponsiveSelector->NumCandidates()) {
|
||||
break;
|
||||
bool isUsableCandidate = true;
|
||||
|
||||
// an otherwise-usable source element may still have a media query that may not
|
||||
// match any more.
|
||||
if (candidateSource->IsHTMLElement(nsGkAtoms::source) &&
|
||||
!SourceElementMatches(candidateSource->AsContent())) {
|
||||
isUsableCandidate = false;
|
||||
}
|
||||
|
||||
if (isUsableCandidate) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// no longer valid
|
||||
@@ -1120,6 +1132,31 @@ HTMLImageElement::SupportedPictureSourceType(const nsAString& aType)
|
||||
AcceptedMimeTypes::IMAGES_AND_DOCUMENTS);
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLImageElement::SourceElementMatches(nsIContent* aSourceNode)
|
||||
{
|
||||
MOZ_ASSERT(aSourceNode->IsHTMLElement(nsGkAtoms::source));
|
||||
|
||||
DebugOnly<Element *> parent(nsINode::GetParentElement());
|
||||
MOZ_ASSERT(parent && parent->IsHTMLElement(nsGkAtoms::picture));
|
||||
MOZ_ASSERT(IsPreviousSibling(aSourceNode, this));
|
||||
MOZ_ASSERT(HTMLPictureElement::IsPictureEnabled());
|
||||
|
||||
// Check media and type
|
||||
HTMLSourceElement *src = static_cast<HTMLSourceElement*>(aSourceNode);
|
||||
if (!src->MatchesCurrentMedia()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoString type;
|
||||
if (aSourceNode->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) &&
|
||||
!SupportedPictureSourceType(type)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode,
|
||||
const nsAString *aSrcset,
|
||||
@@ -1133,20 +1170,7 @@ HTMLImageElement::TryCreateResponsiveSelector(nsIContent *aSourceNode,
|
||||
// Skip if this is not a <source> with matching media query
|
||||
bool isSourceTag = aSourceNode->IsHTMLElement(nsGkAtoms::source);
|
||||
if (isSourceTag) {
|
||||
DebugOnly<Element *> parent(nsINode::GetParentElement());
|
||||
MOZ_ASSERT(parent && parent->IsHTMLElement(nsGkAtoms::picture));
|
||||
MOZ_ASSERT(IsPreviousSibling(aSourceNode, this));
|
||||
MOZ_ASSERT(pictureEnabled);
|
||||
|
||||
// Check media and type
|
||||
HTMLSourceElement *src = static_cast<HTMLSourceElement*>(aSourceNode);
|
||||
if (!src->MatchesCurrentMedia()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
nsAutoString type;
|
||||
if (aSourceNode->GetAttr(kNameSpaceID_None, nsGkAtoms::type, type) &&
|
||||
!SupportedPictureSourceType(type)) {
|
||||
if (!SourceElementMatches(aSourceNode)) {
|
||||
return false;
|
||||
}
|
||||
} else if (aSourceNode->IsHTMLElement(nsGkAtoms::img)) {
|
||||
@@ -1267,6 +1291,12 @@ HTMLImageElement::DestroyContent()
|
||||
mResponsiveSelector = nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
HTMLImageElement::MediaFeatureValuesChanged()
|
||||
{
|
||||
QueueImageLoadTask();
|
||||
}
|
||||
|
||||
} // namespace dom
|
||||
} // namespace mozilla
|
||||
|
||||
|
||||
@@ -206,6 +206,8 @@ public:
|
||||
|
||||
virtual void DestroyContent() override;
|
||||
|
||||
void MediaFeatureValuesChanged();
|
||||
|
||||
/**
|
||||
* Given a hypothetical <img> or <source> tag with the given parameters,
|
||||
* return what URI we would attempt to use, if any. Used by the preloader to
|
||||
@@ -335,6 +337,8 @@ protected:
|
||||
nsRefPtr<ResponsiveImageSelector> mResponsiveSelector;
|
||||
|
||||
private:
|
||||
bool SourceElementMatches(nsIContent* aSourceNode);
|
||||
|
||||
static void MapAttributesIntoRule(const nsMappedAttributes* aAttributes,
|
||||
nsRuleData* aData);
|
||||
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include "nsIDOMEvent.h"
|
||||
#include "nsIDOMStyleSheet.h"
|
||||
#include "nsINode.h"
|
||||
#include "nsISpeculativeConnect.h"
|
||||
#include "nsIStyleSheet.h"
|
||||
#include "nsIStyleSheetLinkingElement.h"
|
||||
#include "nsIURL.h"
|
||||
@@ -182,6 +181,8 @@ HTMLLinkElement::UnbindFromTree(bool aDeep, bool aNullParent)
|
||||
// be under a different xml:base, so forget the cached state now.
|
||||
Link::ResetLinkState(false, Link::ElementHasHref());
|
||||
|
||||
// If this is reinserted back into the document it will not be
|
||||
// from the parser.
|
||||
nsCOMPtr<nsIDocument> oldDoc = GetUncomposedDoc();
|
||||
|
||||
// Check for a ShadowRoot because link elements are inert in a
|
||||
@@ -311,12 +312,11 @@ HTMLLinkElement::UpdatePreconnect()
|
||||
return;
|
||||
}
|
||||
|
||||
nsCOMPtr<nsISpeculativeConnect>
|
||||
speculator(do_QueryInterface(nsContentUtils::GetIOService()));
|
||||
if (speculator) {
|
||||
nsIDocument *owner = OwnerDoc();
|
||||
if (owner) {
|
||||
nsCOMPtr<nsIURI> uri = GetHrefURI();
|
||||
if (uri) {
|
||||
speculator->SpeculativeConnect(uri, nullptr);
|
||||
owner->MaybePreconnect(uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -60,8 +60,6 @@
|
||||
#include "Layers.h"
|
||||
#include <limits>
|
||||
#include "nsIAsyncVerifyRedirectCallback.h"
|
||||
#include "nsIAppShell.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
#include "nsMediaFragmentURIParser.h"
|
||||
#include "nsURIHashKey.h"
|
||||
#include "nsJSUtils.h"
|
||||
@@ -741,13 +739,10 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
|
||||
|
||||
void HTMLMediaElement::RunInStableState(nsIRunnable* aRunnable)
|
||||
{
|
||||
nsCOMPtr<nsIRunnable> event = new nsSyncSection(this, aRunnable);
|
||||
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
|
||||
appShell->RunInStableState(event);
|
||||
nsContentUtils::RunInStableState(event.forget());
|
||||
}
|
||||
|
||||
void HTMLMediaElement::QueueLoadFromSourceTask()
|
||||
|
||||
@@ -591,5 +591,6 @@ support-files = file_bug871161-1.html file_bug871161-2.html
|
||||
skip-if = buildapp == 'b2g' # bug 1129014
|
||||
[test_img_complete.html]
|
||||
[test_extapp.html]
|
||||
[test_viewport_resize.html]
|
||||
[test_image_clone_load.html]
|
||||
[test_bug1203668.html]
|
||||
|
||||
@@ -0,0 +1,44 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=1135812
|
||||
-->
|
||||
<head>
|
||||
<title>Test for Bug 1135812</title>
|
||||
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=1135812">Mozilla Bug 1135812</a>
|
||||
<p id="display"></p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
|
||||
<iframe style="width: 50px;"
|
||||
srcdoc='<picture><source srcset="data:,a" media="(min-width: 150px)" /><source srcset="data:,b" media="(min-width: 100px)" /><img src="data:,c" /></picture>'></iframe>
|
||||
<script>
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
addEventListener('load', function() {
|
||||
var iframe = document.querySelector('iframe');
|
||||
var img = iframe.contentDocument.querySelector('img');
|
||||
is(img.currentSrc, 'data:,c');
|
||||
|
||||
img.onload = function() {
|
||||
is(img.currentSrc, 'data:,a');
|
||||
img.onload = function() {
|
||||
is(img.currentSrc, 'data:,b');
|
||||
SimpleTest.finish();
|
||||
}
|
||||
img.onerror = img.onload;
|
||||
iframe.style.width = '120px';
|
||||
};
|
||||
img.onerror = img.onload;
|
||||
|
||||
iframe.style.width = '200px';
|
||||
}, true);
|
||||
</script>
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1697,9 +1697,11 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
attribute boolean paintFlashing;
|
||||
|
||||
/**
|
||||
* Allows running of a "synchronous section", in the form of an nsIRunnable
|
||||
* once the event loop has reached a "stable state". We've reached a stable
|
||||
* state when the currently executing task/event has finished, see:
|
||||
* Add a "synchronous section", in the form of an nsIRunnable run once the
|
||||
* event loop has reached a "stable state". |runnable| must not cause any
|
||||
* queued events to be processed (i.e. must not spin the event loop).
|
||||
* We've reached a stable state when the currently executing task/event has
|
||||
* finished, see:
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section
|
||||
* In practice this runs aRunnable once the currently executing event
|
||||
* finishes. If called multiple times per task/event, all the runnables will
|
||||
|
||||
@@ -10,8 +10,7 @@
|
||||
#include "nsThreadUtils.h"
|
||||
#include "TaskDispatcher.h"
|
||||
|
||||
#include "nsIAppShell.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
|
||||
#include "mozilla/ClearOnShutdown.h"
|
||||
@@ -24,8 +23,6 @@ namespace mozilla {
|
||||
StaticRefPtr<AbstractThread> sMainThread;
|
||||
ThreadLocal<AbstractThread*> AbstractThread::sCurrentThreadTLS;
|
||||
|
||||
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
|
||||
|
||||
class XPCOMThreadWrapper : public AbstractThread
|
||||
{
|
||||
public:
|
||||
@@ -87,8 +84,7 @@ public:
|
||||
mTailDispatcher.emplace(/* aIsTailDispatcher = */ true);
|
||||
|
||||
nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(this, &XPCOMThreadWrapper::FireTailDispatcher);
|
||||
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
|
||||
appShell->RunInStableState(event);
|
||||
nsContentUtils::RunInStableState(event.forget());
|
||||
}
|
||||
|
||||
return mTailDispatcher.ref();
|
||||
|
||||
@@ -11,10 +11,7 @@
|
||||
#include "mozilla/Mutex.h"
|
||||
#include "mozilla/dom/CanvasCaptureMediaStreamBinding.h"
|
||||
#include "mozilla/dom/HTMLCanvasElement.h"
|
||||
#include "nsIAppShell.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
|
||||
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
|
||||
#include "nsContentUtils.h"
|
||||
|
||||
using namespace mozilla::layers;
|
||||
using namespace mozilla::gfx;
|
||||
@@ -113,8 +110,7 @@ OutputStreamDriver::Start()
|
||||
// Run StartInternal() in stable state to allow it to directly capture a frame
|
||||
nsCOMPtr<nsIRunnable> runnable =
|
||||
NS_NewRunnableMethod(this, &OutputStreamDriver::StartInternal);
|
||||
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
|
||||
appShell->RunInStableState(runnable);
|
||||
nsContentUtils::RunInStableState(runnable.forget());
|
||||
|
||||
mStarted = true;
|
||||
return NS_OK;
|
||||
|
||||
@@ -10,11 +10,9 @@
|
||||
#include "AudioSegment.h"
|
||||
#include "VideoSegment.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIAppShell.h"
|
||||
#include "nsIObserver.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
#include "prerror.h"
|
||||
#include "mozilla/Logging.h"
|
||||
#include "mozilla/Attributes.h"
|
||||
@@ -1853,8 +1851,6 @@ MediaStreamGraphImpl::RunInStableState(bool aSourceIsMSG)
|
||||
}
|
||||
|
||||
|
||||
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
|
||||
|
||||
void
|
||||
MediaStreamGraphImpl::EnsureRunInStableState()
|
||||
{
|
||||
@@ -1864,12 +1860,7 @@ MediaStreamGraphImpl::EnsureRunInStableState()
|
||||
return;
|
||||
mPostedRunInStableState = true;
|
||||
nsCOMPtr<nsIRunnable> event = new MediaStreamGraphStableStateRunnable(this, false);
|
||||
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
|
||||
if (appShell) {
|
||||
appShell->RunInStableState(event);
|
||||
} else {
|
||||
NS_ERROR("Appshell already destroyed?");
|
||||
}
|
||||
nsContentUtils::RunInStableState(event.forget());
|
||||
}
|
||||
|
||||
void
|
||||
|
||||
@@ -16,13 +16,12 @@
|
||||
#include "AudioNodeStream.h"
|
||||
#include "MediaStreamGraph.h"
|
||||
#include "OfflineAudioCompletionEvent.h"
|
||||
#include "nsContentUtils.h"
|
||||
#include "nsIInterfaceRequestorUtils.h"
|
||||
#include "nsIDocShell.h"
|
||||
#include "nsIPermissionManager.h"
|
||||
#include "nsIScriptObjectPrincipal.h"
|
||||
#include "nsServiceManagerUtils.h"
|
||||
#include "nsIAppShell.h"
|
||||
#include "nsWidgetsCID.h"
|
||||
#include "mozilla/dom/Promise.h"
|
||||
|
||||
namespace mozilla {
|
||||
@@ -686,17 +685,16 @@ AudioDestinationNode::NotifyStableState()
|
||||
mExtraCurrentTimeUpdatedSinceLastStableState = false;
|
||||
}
|
||||
|
||||
static NS_DEFINE_CID(kAppShellCID, NS_APPSHELL_CID);
|
||||
|
||||
void
|
||||
AudioDestinationNode::ScheduleStableStateNotification()
|
||||
{
|
||||
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
|
||||
if (appShell) {
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &AudioDestinationNode::NotifyStableState);
|
||||
appShell->RunInStableState(event);
|
||||
}
|
||||
nsCOMPtr<nsIRunnable> event =
|
||||
NS_NewRunnableMethod(this, &AudioDestinationNode::NotifyStableState);
|
||||
// Dispatch will fail if this is called on AudioNode destruction during
|
||||
// shutdown, in which case failure can be ignored.
|
||||
nsContentUtils::RunInStableState(event.forget(),
|
||||
nsContentUtils::
|
||||
DispatchFailureHandling::IgnoreFailure);
|
||||
}
|
||||
|
||||
double
|
||||
|
||||
@@ -163,25 +163,27 @@
|
||||
expectEvents(1, 0, function() {
|
||||
is(img.currentSrc, testPNG200, "Should have selected testPNG200");
|
||||
|
||||
// Switch DPI to match the first source, then add a source
|
||||
// *also* wanting that DPI *just before* the selected
|
||||
// source. Properly re-running the algorithm should
|
||||
// re-consider all sources and thus go back to the first
|
||||
// source, not just the valid source just inserted before us.
|
||||
SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "2.0"] ] },
|
||||
function() {
|
||||
// When we add dynamic reaction to MQs, this test will need to be updated.
|
||||
is(img.currentSrc, testPNG200, "Should still have testPNG200");
|
||||
expectEvents(1, 0, function() {
|
||||
is(img.currentSrc, testPNG50, "Should have switched to testPNG50");
|
||||
Math.sin(90);
|
||||
|
||||
// Now add a source *also* wanting that DPI *just before* the
|
||||
// selected source. Properly re-running the algorithm should
|
||||
// re-consider all sources and thus go back to the first
|
||||
// source, not just the valid source just inserted before us.
|
||||
source3.media = source1.media;
|
||||
source3.srcset = testPNG100;
|
||||
picture.insertBefore(source3, source2);
|
||||
// This should trigger a reload, but we should re-consider
|
||||
// source1 and select that, not just the newly added source2
|
||||
// source1 and remain with that, not just the newly added source2
|
||||
expectEvents(1, 0, function() {
|
||||
is(img.currentSrc, testPNG50, "Should have selected testPNG50");
|
||||
is(img.currentSrc, testPNG50, "Should have remained on testPNG50");
|
||||
expectEvents(0, 0, nextTest);
|
||||
});
|
||||
});
|
||||
|
||||
// Switch DPI to match the first source.
|
||||
SpecialPowers.pushPrefEnv({'set': [ ["layout.css.devPixelsPerPx", "2.0"] ] });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -283,6 +285,10 @@
|
||||
(tests.shift())();
|
||||
}, 0);
|
||||
} else {
|
||||
// We'll get a flood of load events due to prefs being popped while cleaning up.
|
||||
// Ignore it all.
|
||||
img.removeEventListener("load", onImgLoad);
|
||||
img.removeEventListener("error", onImgError);
|
||||
SimpleTest.finish();
|
||||
}
|
||||
}
|
||||
|
||||
+29
-37
@@ -135,35 +135,37 @@ PRLogModuleInfo* XULDocument::gXULLog;
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
||||
struct BroadcasterMapEntry : public PLDHashEntryHdr {
|
||||
Element* mBroadcaster; // [WEAK]
|
||||
nsSmallVoidArray mListeners; // [OWNING] of BroadcastListener objects
|
||||
};
|
||||
|
||||
struct BroadcastListener {
|
||||
nsWeakPtr mListener;
|
||||
nsCOMPtr<nsIAtom> mAttribute;
|
||||
};
|
||||
|
||||
struct BroadcasterMapEntry : public PLDHashEntryHdr
|
||||
{
|
||||
Element* mBroadcaster; // [WEAK]
|
||||
nsTArray<BroadcastListener*> mListeners; // [OWNING] of BroadcastListener objects
|
||||
};
|
||||
|
||||
Element*
|
||||
nsRefMapEntry::GetFirstElement()
|
||||
{
|
||||
return static_cast<Element*>(mRefContentList.SafeElementAt(0));
|
||||
return mRefContentList.SafeElementAt(0);
|
||||
}
|
||||
|
||||
void
|
||||
nsRefMapEntry::AppendAll(nsCOMArray<nsIContent>* aElements)
|
||||
{
|
||||
for (int32_t i = 0; i < mRefContentList.Count(); ++i) {
|
||||
aElements->AppendObject(static_cast<nsIContent*>(mRefContentList[i]));
|
||||
for (size_t i = 0; i < mRefContentList.Length(); ++i) {
|
||||
aElements->AppendObject(mRefContentList[i]);
|
||||
}
|
||||
}
|
||||
|
||||
bool
|
||||
nsRefMapEntry::AddElement(Element* aElement)
|
||||
{
|
||||
if (mRefContentList.IndexOf(aElement) >= 0)
|
||||
if (mRefContentList.Contains(aElement)) {
|
||||
return true;
|
||||
}
|
||||
return mRefContentList.AppendElement(aElement);
|
||||
}
|
||||
|
||||
@@ -171,7 +173,7 @@ bool
|
||||
nsRefMapEntry::RemoveElement(Element* aElement)
|
||||
{
|
||||
mRefContentList.RemoveElement(aElement);
|
||||
return mRefContentList.Count() == 0;
|
||||
return mRefContentList.IsEmpty();
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
@@ -612,13 +614,14 @@ ClearBroadcasterMapEntry(PLDHashTable* aTable, PLDHashEntryHdr* aEntry)
|
||||
{
|
||||
BroadcasterMapEntry* entry =
|
||||
static_cast<BroadcasterMapEntry*>(aEntry);
|
||||
for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) {
|
||||
delete (BroadcastListener*)entry->mListeners[i];
|
||||
for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
|
||||
delete entry->mListeners[i];
|
||||
}
|
||||
entry->mListeners.Clear();
|
||||
|
||||
// N.B. that we need to manually run the dtor because we
|
||||
// constructed the nsSmallVoidArray object in-place.
|
||||
entry->mListeners.~nsSmallVoidArray();
|
||||
// constructed the nsTArray object in-place.
|
||||
entry->mListeners.~nsTArray<BroadcastListener*>();
|
||||
}
|
||||
|
||||
static bool
|
||||
@@ -780,26 +783,22 @@ XULDocument::AddBroadcastListenerFor(Element& aBroadcaster, Element& aListener,
|
||||
|
||||
entry->mBroadcaster = &aBroadcaster;
|
||||
|
||||
// N.B. placement new to construct the nsSmallVoidArray object
|
||||
// in-place
|
||||
new (&entry->mListeners) nsSmallVoidArray();
|
||||
// N.B. placement new to construct the nsTArray object in-place
|
||||
new (&entry->mListeners) nsTArray<BroadcastListener*>();
|
||||
}
|
||||
|
||||
// Only add the listener if it's not there already!
|
||||
nsCOMPtr<nsIAtom> attr = do_GetAtom(aAttr);
|
||||
|
||||
BroadcastListener* bl;
|
||||
for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) {
|
||||
bl = static_cast<BroadcastListener*>(entry->mListeners[i]);
|
||||
|
||||
for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
|
||||
BroadcastListener* bl = entry->mListeners[i];
|
||||
nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
|
||||
|
||||
if (blListener == &aListener && bl->mAttribute == attr)
|
||||
return;
|
||||
}
|
||||
|
||||
bl = new BroadcastListener;
|
||||
|
||||
BroadcastListener* bl = new BroadcastListener;
|
||||
bl->mListener = do_GetWeakReference(&aListener);
|
||||
bl->mAttribute = attr;
|
||||
|
||||
@@ -836,17 +835,15 @@ XULDocument::RemoveBroadcastListenerFor(Element& aBroadcaster,
|
||||
|
||||
if (entry) {
|
||||
nsCOMPtr<nsIAtom> attr = do_GetAtom(aAttr);
|
||||
for (int32_t i = entry->mListeners.Count() - 1; i >= 0; --i) {
|
||||
BroadcastListener* bl =
|
||||
static_cast<BroadcastListener*>(entry->mListeners[i]);
|
||||
|
||||
for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
|
||||
BroadcastListener* bl = entry->mListeners[i];
|
||||
nsCOMPtr<Element> blListener = do_QueryReferent(bl->mListener);
|
||||
|
||||
if (blListener == &aListener && bl->mAttribute == attr) {
|
||||
entry->mListeners.RemoveElementAt(i);
|
||||
delete bl;
|
||||
|
||||
if (entry->mListeners.Count() == 0)
|
||||
if (entry->mListeners.IsEmpty())
|
||||
PL_DHashTableRemove(mBroadcasterMap, &aBroadcaster);
|
||||
|
||||
break;
|
||||
@@ -961,11 +958,8 @@ XULDocument::AttributeChanged(nsIDocument* aDocument,
|
||||
nsAutoString value;
|
||||
bool attrSet = aElement->GetAttr(kNameSpaceID_None, aAttribute, value);
|
||||
|
||||
int32_t i;
|
||||
for (i = entry->mListeners.Count() - 1; i >= 0; --i) {
|
||||
BroadcastListener* bl =
|
||||
static_cast<BroadcastListener*>(entry->mListeners[i]);
|
||||
|
||||
for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
|
||||
BroadcastListener* bl = entry->mListeners[i];
|
||||
if ((bl->mAttribute == aAttribute) ||
|
||||
(bl->mAttribute == nsGkAtoms::_asterisk)) {
|
||||
nsCOMPtr<Element> listenerEl
|
||||
@@ -4139,10 +4133,8 @@ XULDocument::BroadcastAttributeChangeFromOverlay(nsIContent* aNode,
|
||||
return rv;
|
||||
|
||||
// We've got listeners: push the value.
|
||||
int32_t i;
|
||||
for (i = entry->mListeners.Count() - 1; i >= 0; --i) {
|
||||
BroadcastListener* bl = static_cast<BroadcastListener*>
|
||||
(entry->mListeners[i]);
|
||||
for (size_t i = entry->mListeners.Length() - 1; i != (size_t)-1; --i) {
|
||||
BroadcastListener* bl = entry->mListeners[i];
|
||||
|
||||
if ((bl->mAttribute != aAttribute) &&
|
||||
(bl->mAttribute != nsGkAtoms::_asterisk))
|
||||
|
||||
@@ -75,7 +75,7 @@ public:
|
||||
bool RemoveElement(mozilla::dom::Element* aElement);
|
||||
|
||||
private:
|
||||
nsSmallVoidArray mRefContentList;
|
||||
nsTArray<mozilla::dom::Element*> mRefContentList;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -150,7 +150,6 @@ namespace JS {
|
||||
_(CantInlineClassConstructor) \
|
||||
_(CantInlineDisabledIon) \
|
||||
_(CantInlineTooManyArgs) \
|
||||
_(CantInlineHeavyweight) \
|
||||
_(CantInlineNeedsArgsObj) \
|
||||
_(CantInlineDebuggee) \
|
||||
_(CantInlineUnknownProps) \
|
||||
|
||||
@@ -25,13 +25,14 @@ withJitOptions(Opts_Ion2NoOffthreadCompilation, function () {
|
||||
}
|
||||
};
|
||||
|
||||
g.eval("" + function f(d, x) { "use strict"; g(d, x); });
|
||||
g.eval("" + function f(d, x) {
|
||||
"use strict";
|
||||
eval("g(d, x)"); // `eval` to avoid inlining g.
|
||||
});
|
||||
|
||||
g.eval("" + function g(d, x) {
|
||||
"use strict";
|
||||
for (var i = 0; i < 200; i++);
|
||||
// Hack to prevent inlining.
|
||||
function inner() { i = 42; };
|
||||
toggle(d);
|
||||
});
|
||||
|
||||
|
||||
@@ -527,11 +527,6 @@ IonBuilder::canInlineTarget(JSFunction* target, CallInfo& callInfo)
|
||||
return DontInline(inlineScript, "Common inlining path");
|
||||
}
|
||||
|
||||
if (target->isHeavyweight()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineHeavyweight);
|
||||
return DontInline(inlineScript, "Heavyweight function");
|
||||
}
|
||||
|
||||
if (inlineScript->uninlineable()) {
|
||||
trackOptimizationOutcome(TrackedOutcome::CantInlineGeneric);
|
||||
return DontInline(inlineScript, "Uninlineable script");
|
||||
|
||||
@@ -1893,6 +1893,8 @@ nsPresContext::MediaFeatureValuesChanged(nsRestyleHint aRestyleHint,
|
||||
return;
|
||||
}
|
||||
|
||||
mDocument->NotifyMediaFeatureValuesChanged();
|
||||
|
||||
MOZ_ASSERT(nsContentUtils::IsSafeToRunScript());
|
||||
|
||||
// Media query list listeners should be notified from a queued task
|
||||
|
||||
@@ -1447,6 +1447,10 @@ nsComputedDOMStyle::DoGetFontSizeAdjust()
|
||||
CSSValue*
|
||||
nsComputedDOMStyle::DoGetOsxFontSmoothing()
|
||||
{
|
||||
if (nsContentUtils::ShouldResistFingerprinting(
|
||||
mPresShell->GetPresContext()->GetDocShell()))
|
||||
return nullptr;
|
||||
|
||||
nsROCSSPrimitiveValue* val = new nsROCSSPrimitiveValue;
|
||||
val->SetIdent(nsCSSProps::ValueToKeywordEnum(StyleFont()->mFont.smoothing,
|
||||
nsCSSProps::kFontSmoothingKTable));
|
||||
|
||||
@@ -104,13 +104,19 @@ GetDeviceContextFor(nsPresContext* aPresContext)
|
||||
return aPresContext->DeviceContext();
|
||||
}
|
||||
|
||||
static bool
|
||||
ShouldResistFingerprinting(nsPresContext* aPresContext)
|
||||
{
|
||||
return nsContentUtils::ShouldResistFingerprinting(aPresContext->GetDocShell());
|
||||
}
|
||||
|
||||
// A helper for three features below.
|
||||
static nsSize
|
||||
GetDeviceSize(nsPresContext* aPresContext)
|
||||
{
|
||||
nsSize size;
|
||||
|
||||
if (aPresContext->IsDeviceSizePageSize()) {
|
||||
if (ShouldResistFingerprinting(aPresContext) || aPresContext->IsDeviceSizePageSize()) {
|
||||
size = GetSize(aPresContext);
|
||||
} else if (aPresContext->IsRootPaginatedDocument()) {
|
||||
// We want the page size, including unprintable areas and margins.
|
||||
@@ -218,13 +224,17 @@ static nsresult
|
||||
GetColor(nsPresContext* aPresContext, const nsMediaFeature*,
|
||||
nsCSSValue& aResult)
|
||||
{
|
||||
// FIXME: This implementation is bogus. nsDeviceContext
|
||||
// doesn't provide reliable information (should be fixed in bug
|
||||
// 424386).
|
||||
// FIXME: On a monochrome device, return 0!
|
||||
nsDeviceContext *dx = GetDeviceContextFor(aPresContext);
|
||||
uint32_t depth;
|
||||
dx->GetDepth(depth);
|
||||
uint32_t depth = 24; // Use depth of 24 when resisting fingerprinting.
|
||||
|
||||
if (!ShouldResistFingerprinting(aPresContext)) {
|
||||
// FIXME: This implementation is bogus. nsDeviceContext
|
||||
// doesn't provide reliable information (should be fixed in bug
|
||||
// 424386).
|
||||
// FIXME: On a monochrome device, return 0!
|
||||
nsDeviceContext *dx = GetDeviceContextFor(aPresContext);
|
||||
dx->GetDepth(depth);
|
||||
}
|
||||
|
||||
// The spec says to use bits *per color component*, so divide by 3,
|
||||
// and round down, since the spec says to use the smallest when the
|
||||
// color components differ.
|
||||
@@ -262,10 +272,15 @@ static nsresult
|
||||
GetResolution(nsPresContext* aPresContext, const nsMediaFeature*,
|
||||
nsCSSValue& aResult)
|
||||
{
|
||||
// Resolution measures device pixels per CSS (inch/cm/pixel). We
|
||||
// return it in device pixels per CSS inches.
|
||||
float dpi = float(nsPresContext::AppUnitsPerCSSInch()) /
|
||||
float(aPresContext->AppUnitsPerDevPixel());
|
||||
float dpi = 96; // Use 96 when resisting fingerprinting.
|
||||
|
||||
if (!ShouldResistFingerprinting(aPresContext)) {
|
||||
// Resolution measures device pixels per CSS (inch/cm/pixel). We
|
||||
// return it in device pixels per CSS inches.
|
||||
dpi = float(nsPresContext::AppUnitsPerCSSInch()) /
|
||||
float(aPresContext->AppUnitsPerDevPixel());
|
||||
}
|
||||
|
||||
aResult.SetFloatValue(dpi, eCSSUnit_Inch);
|
||||
return NS_OK;
|
||||
}
|
||||
@@ -294,15 +309,26 @@ static nsresult
|
||||
GetDevicePixelRatio(nsPresContext* aPresContext, const nsMediaFeature*,
|
||||
nsCSSValue& aResult)
|
||||
{
|
||||
float ratio = aPresContext->CSSPixelsToDevPixels(1.0f);
|
||||
aResult.SetFloatValue(ratio, eCSSUnit_Number);
|
||||
return NS_OK;
|
||||
if (!ShouldResistFingerprinting(aPresContext)) {
|
||||
float ratio = aPresContext->CSSPixelsToDevPixels(1.0f);
|
||||
aResult.SetFloatValue(ratio, eCSSUnit_Number);
|
||||
} else {
|
||||
aResult.SetFloatValue(1.0, eCSSUnit_Number);
|
||||
}
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
static nsresult
|
||||
GetSystemMetric(nsPresContext* aPresContext, const nsMediaFeature* aFeature,
|
||||
nsCSSValue& aResult)
|
||||
{
|
||||
aResult.Reset();
|
||||
if (ShouldResistFingerprinting(aPresContext)) {
|
||||
// If "privacy.resistFingerprinting" is enabled, then we simply don't
|
||||
// return any system-backed media feature values. (No spoofed values returned.)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
MOZ_ASSERT(aFeature->mValueType == nsMediaFeature::eBoolInteger,
|
||||
"unexpected type");
|
||||
nsIAtom *metricAtom = *aFeature->mData.mMetric;
|
||||
@@ -316,6 +342,10 @@ GetWindowsTheme(nsPresContext* aPresContext, const nsMediaFeature* aFeature,
|
||||
nsCSSValue& aResult)
|
||||
{
|
||||
aResult.Reset();
|
||||
if (ShouldResistFingerprinting(aPresContext)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
uint8_t windowsThemeId =
|
||||
nsCSSRuleProcessor::GetWindowsThemeIdentifier();
|
||||
@@ -341,6 +371,10 @@ GetOperatinSystemVersion(nsPresContext* aPresContext, const nsMediaFeature* aFea
|
||||
nsCSSValue& aResult)
|
||||
{
|
||||
aResult.Reset();
|
||||
if (ShouldResistFingerprinting(aPresContext)) {
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
#ifdef XP_WIN
|
||||
int32_t metricResult;
|
||||
if (NS_SUCCEEDED(
|
||||
|
||||
@@ -0,0 +1,274 @@
|
||||
// # Bug 418986, part 2.
|
||||
|
||||
/* jshint esnext:true */
|
||||
/* jshint loopfunc:true */
|
||||
/* global window, screen, ok, SpecialPowers, matchMedia */
|
||||
|
||||
SimpleTest.waitForExplicitFinish();
|
||||
|
||||
// Expected values. Format: [name, pref_off_value, pref_on_value]
|
||||
// If pref_*_value is an array with two values, then we will match
|
||||
// any value in between those two values. If a value is null, then
|
||||
// we skip the media query.
|
||||
let expected_values = [
|
||||
["color", null, 8],
|
||||
["color-index", null, 0],
|
||||
["aspect-ratio", null, window.innerWidth + "/" + window.innerHeight],
|
||||
["device-aspect-ratio", screen.width + "/" + screen.height,
|
||||
window.innerWidth + "/" + window.innerHeight],
|
||||
["device-height", screen.height + "px", window.innerHeight + "px"],
|
||||
["device-width", screen.width + "px", window.innerWidth + "px"],
|
||||
["grid", null, 0],
|
||||
["height", window.innerHeight + "px", window.innerHeight + "px"],
|
||||
["monochrome", null, 0],
|
||||
// Square is defined as portrait:
|
||||
["orientation", null,
|
||||
window.innerWidth > window.innerHeight ?
|
||||
"landscape" : "portrait"],
|
||||
["resolution", null, "96dpi"],
|
||||
["resolution", [0.999 * window.devicePixelRatio + "dppx",
|
||||
1.001 * window.devicePixelRatio + "dppx"], "1dppx"],
|
||||
["width", window.innerWidth + "px", window.innerWidth + "px"],
|
||||
["-moz-device-pixel-ratio", window.devicePixelRatio, 1],
|
||||
["-moz-device-orientation", screen.width > screen.height ?
|
||||
"landscape" : "portrait",
|
||||
window.innerWidth > window.innerHeight ?
|
||||
"landscape" : "portrait"]
|
||||
];
|
||||
|
||||
// These media queries return value 0 or 1 when the pref is off.
|
||||
// When the pref is on, they should not match.
|
||||
let suppressed_toggles = [
|
||||
"-moz-images-in-menus",
|
||||
"-moz-mac-graphite-theme",
|
||||
// Not available on most OSs.
|
||||
// "-moz-maemo-classic",
|
||||
"-moz-scrollbar-end-backward",
|
||||
"-moz-scrollbar-end-forward",
|
||||
"-moz-scrollbar-start-backward",
|
||||
"-moz-scrollbar-start-forward",
|
||||
"-moz-scrollbar-thumb-proportional",
|
||||
"-moz-touch-enabled",
|
||||
"-moz-windows-compositor",
|
||||
"-moz-windows-default-theme",
|
||||
"-moz-windows-glass",
|
||||
];
|
||||
|
||||
// Possible values for '-moz-os-version'
|
||||
let windows_versions = [
|
||||
"windows-xp",
|
||||
"windows-vista",
|
||||
"windows-win7",
|
||||
"windows-win8"];
|
||||
|
||||
// Possible values for '-moz-windows-theme'
|
||||
let windows_themes = [
|
||||
"aero",
|
||||
"luna-blue",
|
||||
"luna-olive",
|
||||
"luna-silver",
|
||||
"royale",
|
||||
"generic",
|
||||
"zune"
|
||||
];
|
||||
|
||||
// Read the current OS.
|
||||
let OS = SpecialPowers.Services.appinfo.OS;
|
||||
|
||||
// If we are using Windows, add an extra toggle only
|
||||
// available on that OS.
|
||||
if (OS === "WINNT") {
|
||||
suppressed_toggles.push("-moz-windows-classic");
|
||||
}
|
||||
|
||||
// __keyValMatches(key, val)__.
|
||||
// Runs a media query and returns true if key matches to val.
|
||||
let keyValMatches = (key, val) => matchMedia("(" + key + ":" + val +")").matches;
|
||||
|
||||
// __testMatch(key, val)__.
|
||||
// Attempts to run a media query match for the given key and value.
|
||||
// If value is an array of two elements [min max], then matches any
|
||||
// value in-between.
|
||||
let testMatch = function (key, val) {
|
||||
if (val === null) {
|
||||
return;
|
||||
} else if (Array.isArray(val)) {
|
||||
ok(keyValMatches("min-" + key, val[0]) && keyValMatches("max-" + key, val[1]),
|
||||
"Expected " + key + " between " + val[0] + " and " + val[1]);
|
||||
} else {
|
||||
ok(keyValMatches(key, val), "Expected " + key + ":" + val);
|
||||
}
|
||||
};
|
||||
|
||||
// __testToggles(resisting)__.
|
||||
// Test whether we are able to match the "toggle" media queries.
|
||||
let testToggles = function (resisting) {
|
||||
suppressed_toggles.forEach(
|
||||
function (key) {
|
||||
var exists = keyValMatches(key, 0) || keyValMatches(key, 1);
|
||||
if (resisting) {
|
||||
ok(!exists, key + " should not exist.");
|
||||
} else {
|
||||
ok(exists, key + " should exist.");
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// __testWindowsSpecific__.
|
||||
// Runs a media query on the queryName with the given possible matching values.
|
||||
let testWindowsSpecific = function (resisting, queryName, possibleValues) {
|
||||
let found = false;
|
||||
possibleValues.forEach(function (val) {
|
||||
found = found || keyValMatches(queryName, val);
|
||||
});
|
||||
if (resisting) {
|
||||
ok(!found, queryName + " should have no match");
|
||||
} else {
|
||||
ok(found, queryName + " should match");
|
||||
}
|
||||
};
|
||||
|
||||
// __generateHtmlLines(resisting)__.
|
||||
// Create a series of div elements that look like:
|
||||
// `<div class='spoof' id='resolution'>resolution</div>`,
|
||||
// where each line corresponds to a different media query.
|
||||
let generateHtmlLines = function (resisting) {
|
||||
let lines = "";
|
||||
expected_values.forEach(
|
||||
function ([key, offVal, onVal]) {
|
||||
let val = resisting ? onVal : offVal;
|
||||
if (val) {
|
||||
lines += "<div class='spoof' id='" + key + "'>" + key + "</div>\n";
|
||||
}
|
||||
});
|
||||
suppressed_toggles.forEach(
|
||||
function (key) {
|
||||
lines += "<div class='suppress' id='" + key + "'>" + key + "</div>\n";
|
||||
});
|
||||
if (OS === "WINNT") {
|
||||
lines += "<div class='windows' id='-moz-os-version'>-moz-os-version</div>";
|
||||
lines += "<div class='windows' id='-moz-windows-theme'>-moz-windows-theme</div>";
|
||||
}
|
||||
return lines;
|
||||
};
|
||||
|
||||
// __cssLine__.
|
||||
// Creates a line of css that looks something like
|
||||
// `@media (resolution: 1ppx) { .spoof#resolution { background-color: green; } }`.
|
||||
let cssLine = function (query, clazz, id, color) {
|
||||
return "@media " + query + " { ." + clazz + "#" + id +
|
||||
" { background-color: " + color + "; } }\n";
|
||||
};
|
||||
|
||||
// __mediaQueryCSSLine(key, val, color)__.
|
||||
// Creates a line containing a CSS media query and a CSS expression.
|
||||
let mediaQueryCSSLine = function (key, val, color) {
|
||||
if (val === null) {
|
||||
return "";
|
||||
}
|
||||
let query;
|
||||
if (Array.isArray(val)) {
|
||||
query = "(min-" + key + ": " + val[0] + ") and (max-" + key + ": " + val[1] + ")";
|
||||
} else {
|
||||
query = "(" + key + ": " + val + ")";
|
||||
}
|
||||
return cssLine(query, "spoof", key, color);
|
||||
};
|
||||
|
||||
// __suppressedMediaQueryCSSLine(key, color)__.
|
||||
// Creates a CSS line that matches the existence of a
|
||||
// media query that is supposed to be suppressed.
|
||||
let suppressedMediaQueryCSSLine = function (key, color, suppressed) {
|
||||
let query = "(" + key + ": 0), (" + key + ": 1)";
|
||||
return cssLine(query, "suppress", key, color);
|
||||
};
|
||||
|
||||
// __generateCSSLines(resisting)__.
|
||||
// Creates a series of lines of CSS, each of which corresponds to
|
||||
// a different media query. If the query produces a match to the
|
||||
// expected value, then the element will be colored green.
|
||||
let generateCSSLines = function (resisting) {
|
||||
let lines = ".spoof { background-color: red;}\n";
|
||||
expected_values.forEach(
|
||||
function ([key, offVal, onVal]) {
|
||||
lines += mediaQueryCSSLine(key, resisting ? onVal : offVal, "green");
|
||||
});
|
||||
lines += ".suppress { background-color: " + (resisting ? "green" : "red") + ";}\n";
|
||||
suppressed_toggles.forEach(
|
||||
function (key) {
|
||||
lines += suppressedMediaQueryCSSLine(key, resisting ? "red" : "green");
|
||||
});
|
||||
if (OS === "WINNT") {
|
||||
lines += ".windows { background-color: " + (resisting ? "green" : "red") + ";}\n";
|
||||
lines += windows_versions.map(val => "(-moz-os-version: " + val + ")").join(", ") +
|
||||
" { #-moz-os-version { background-color: " + (resisting ? "red" : "green") + ";} }\n";
|
||||
lines += windows_themes.map(val => "(-moz-windows-theme: " + val + ")").join(",") +
|
||||
" { #-moz-windows-theme { background-color: " + (resisting ? "red" : "green") + ";} }\n";
|
||||
}
|
||||
return lines;
|
||||
};
|
||||
|
||||
// __green__.
|
||||
// Returns the computed color style corresponding to green.
|
||||
let green = (function () {
|
||||
let temp = document.createElement("span");
|
||||
temp.style.backgroundColor = "green";
|
||||
return getComputedStyle(temp).backgroundColor;
|
||||
})();
|
||||
|
||||
// __testCSS(resisting)__.
|
||||
// Creates a series of divs and CSS using media queries to set their
|
||||
// background color. If all media queries match as expected, then
|
||||
// all divs should have a green background color.
|
||||
let testCSS = function (resisting) {
|
||||
document.getElementById("display").innerHTML = generateHtmlLines(resisting);
|
||||
document.getElementById("test-css").innerHTML = generateCSSLines(resisting);
|
||||
let cssTestDivs = document.querySelectorAll(".spoof,.suppress");
|
||||
for (let div of cssTestDivs) {
|
||||
let color = window.getComputedStyle(div).backgroundColor;
|
||||
ok(color === green, "CSS for '" + div.id + "'");
|
||||
}
|
||||
};
|
||||
|
||||
// __testOSXFontSmoothing(resisting)__.
|
||||
// When fingerprinting resistance is enabled, the `getComputedStyle`
|
||||
// should always return `undefined` for `MozOSXFontSmoothing`.
|
||||
let testOSXFontSmoothing = function (resisting) {
|
||||
let div = document.createElement("div");
|
||||
div.style.MozOsxFontSmoothing = "unset";
|
||||
let readBack = window.getComputedStyle(div).MozOsxFontSmoothing;
|
||||
let smoothingPref = SpecialPowers.getBoolPref("layout.css.osx-font-smoothing.enabled", false);
|
||||
is(readBack, resisting ? "" : (smoothingPref ? "auto" : ""),
|
||||
"-moz-osx-font-smoothing");
|
||||
};
|
||||
|
||||
// An iterator yielding pref values for two consecutive tests.
|
||||
let prefVals = (for (prefVal of [false, true]) prefVal);
|
||||
|
||||
// __test(isContent)__.
|
||||
// Run all tests.
|
||||
let test = function(isContent) {
|
||||
let {value: prefValue, done} = prefVals.next();
|
||||
if (done) {
|
||||
SimpleTest.finish();
|
||||
return;
|
||||
}
|
||||
SpecialPowers.pushPrefEnv({set: [["privacy.resistFingerprinting", prefValue]]},
|
||||
function () {
|
||||
let resisting = prefValue && isContent;
|
||||
expected_values.forEach(
|
||||
function ([key, offVal, onVal]) {
|
||||
testMatch(key, resisting ? onVal : offVal);
|
||||
});
|
||||
testToggles(resisting);
|
||||
if (OS === "WINNT") {
|
||||
testWindowsSpecific(resisting, "-moz-os-version", windows_versions);
|
||||
testWindowsSpecific(resisting, "-moz-windows-theme", windows_themes);
|
||||
}
|
||||
testCSS(resisting);
|
||||
if (OS === "Darwin") {
|
||||
testOSXFontSmoothing(resisting);
|
||||
}
|
||||
test(isContent);
|
||||
});
|
||||
};
|
||||
@@ -1,6 +1,7 @@
|
||||
[DEFAULT]
|
||||
skip-if = buildapp == 'b2g'
|
||||
support-files =
|
||||
bug418986-2.js
|
||||
bug535806-css.css
|
||||
bug535806-html.html
|
||||
bug535806-xul.xul
|
||||
@@ -9,6 +10,7 @@ support-files =
|
||||
[test_addSheet.html]
|
||||
[test_additional_sheets.html]
|
||||
[test_author_specified_style.html]
|
||||
[test_bug418986-2.xul]
|
||||
[test_bug1157097.html]
|
||||
[test_bug1160724.xul]
|
||||
[test_bug535806.xul]
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
<?xml version="1.0"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://global/skin"?>
|
||||
<?xml-stylesheet type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css"?>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=418986
|
||||
-->
|
||||
<window title="Mozilla Bug 418986"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"/>
|
||||
<!-- test results are displayed in the html:body -->
|
||||
<body xmlns="http://www.w3.org/1999/xhtml">
|
||||
<style id="test-css" scoped="true"></style>
|
||||
<a href="https://bugzilla.mozilla.org/show_bug.cgi?id=418986"
|
||||
target="_blank">Mozilla Bug 418986</a>
|
||||
<p id="display"></p>
|
||||
|
||||
</body>
|
||||
|
||||
<script type="text/javascript;version=1.7" src="bug418986-2.js"></script>
|
||||
<!-- test code goes here -->
|
||||
<script type="text/javascript;version=1.7">
|
||||
// Run all tests now.
|
||||
window.onload = function () {
|
||||
test(false);
|
||||
};
|
||||
</script>
|
||||
</window>
|
||||
@@ -5,6 +5,7 @@ support-files =
|
||||
ccd.sjs
|
||||
ccd-standards.html
|
||||
css_properties.js
|
||||
chrome/bug418986-2.js
|
||||
descriptor_database.js
|
||||
empty.html
|
||||
media_queries_dynamic_xbl_binding.xml
|
||||
@@ -74,6 +75,7 @@ skip-if = true # Bug 701060
|
||||
[test_bug412901.html]
|
||||
skip-if = android_version == '18' # bug 1147986
|
||||
[test_bug413958.html]
|
||||
[test_bug418986-2.html]
|
||||
[test_bug437915.html]
|
||||
[test_bug450191.html]
|
||||
[test_bug453896_deck.html]
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
<!DOCTYPE HTML>
|
||||
<html>
|
||||
<!--
|
||||
https://bugzilla.mozilla.org/show_bug.cgi?id=418986
|
||||
-->
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Test 2/3 for Bug #418986: Resist fingerprinting by preventing exposure of screen and system info</title>
|
||||
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
|
||||
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
|
||||
<style id="test-css"></style>
|
||||
<script type="text/javascript;version=1.7" src="chrome/bug418986-2.js"></script>
|
||||
<script type="text/javascript;version=1.7">
|
||||
// Run all tests now.
|
||||
window.onload = function () {
|
||||
test(true);
|
||||
};
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=418986">Bug 418986</a>
|
||||
<p id="display">TEST</p>
|
||||
<div id="content" style="display: none">
|
||||
|
||||
</div>
|
||||
<pre id="test">
|
||||
</pre>
|
||||
</body>
|
||||
</html>
|
||||
@@ -67,6 +67,9 @@ nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor)
|
||||
intSource);
|
||||
}
|
||||
break;
|
||||
case eSpeculativeLoadPreconnect:
|
||||
aExecutor->Preconnect(mUrl);
|
||||
break;
|
||||
default:
|
||||
NS_NOTREACHED("Bogus speculative load.");
|
||||
break;
|
||||
|
||||
@@ -24,7 +24,8 @@ enum eHtml5SpeculativeLoad {
|
||||
eSpeculativeLoadScriptFromHead,
|
||||
eSpeculativeLoadStyle,
|
||||
eSpeculativeLoadManifest,
|
||||
eSpeculativeLoadSetDocumentCharset
|
||||
eSpeculativeLoadSetDocumentCharset,
|
||||
eSpeculativeLoadPreconnect
|
||||
};
|
||||
|
||||
class nsHtml5SpeculativeLoad {
|
||||
@@ -163,6 +164,14 @@ class nsHtml5SpeculativeLoad {
|
||||
mTypeOrCharsetSource.Assign((char16_t)aCharsetSource);
|
||||
}
|
||||
|
||||
inline void InitPreconnect(const nsAString& aUrl)
|
||||
{
|
||||
NS_PRECONDITION(mOpCode == eSpeculativeLoadUninitialized,
|
||||
"Trying to reinitialize a speculative load!");
|
||||
mOpCode = eSpeculativeLoadPreconnect;
|
||||
mUrl.Assign(aUrl);
|
||||
}
|
||||
|
||||
void Perform(nsHtml5TreeOpExecutor* aExecutor);
|
||||
|
||||
private:
|
||||
|
||||
@@ -176,16 +176,24 @@ nsHtml5TreeBuilder::createElement(int32_t aNamespace, nsIAtom* aName,
|
||||
nsString* rel = aAttributes->getValue(nsHtml5AttributeName::ATTR_REL);
|
||||
// Not splitting on space here is bogus but the old parser didn't even
|
||||
// do a case-insensitive check.
|
||||
if (rel && rel->LowerCaseEqualsASCII("stylesheet")) {
|
||||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
|
||||
if (url) {
|
||||
nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
|
||||
nsString* crossOrigin =
|
||||
aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
|
||||
mSpeculativeLoadQueue.AppendElement()->
|
||||
InitStyle(*url,
|
||||
(charset) ? *charset : EmptyString(),
|
||||
(crossOrigin) ? *crossOrigin : NullString());
|
||||
if (rel) {
|
||||
if (rel->LowerCaseEqualsASCII("stylesheet")) {
|
||||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
|
||||
if (url) {
|
||||
nsString* charset = aAttributes->getValue(nsHtml5AttributeName::ATTR_CHARSET);
|
||||
nsString* crossOrigin =
|
||||
aAttributes->getValue(nsHtml5AttributeName::ATTR_CROSSORIGIN);
|
||||
mSpeculativeLoadQueue.AppendElement()->
|
||||
InitStyle(*url,
|
||||
(charset) ? *charset : EmptyString(),
|
||||
(crossOrigin) ? *crossOrigin : NullString());
|
||||
}
|
||||
} else if (rel->LowerCaseEqualsASCII("preconnect")) {
|
||||
nsString* url = aAttributes->getValue(nsHtml5AttributeName::ATTR_HREF);
|
||||
if (url) {
|
||||
mSpeculativeLoadQueue.AppendElement()->
|
||||
InitPreconnect(*url);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (nsHtml5Atoms::video == aName) {
|
||||
|
||||
@@ -61,15 +61,21 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=1067345
|
||||
if (testDPIs.length) {
|
||||
currentDPI = testDPIs.pop();
|
||||
info("Starting test for DPI: " + currentDPI);
|
||||
SpecialPowers.pushPrefEnv({'set': [ [ "layout.css.devPixelsPerPx", currentDPI ]] },
|
||||
function() {
|
||||
// Clear image cache for next run (we don't try to validate cached items
|
||||
// in preload).
|
||||
SpecialPowers.Cc["@mozilla.org/image/tools;1"]
|
||||
.getService(SpecialPowers.Ci.imgITools)
|
||||
.getImgCacheForDocument(iframe.contentDocument)
|
||||
.clearCache(false);
|
||||
iframe.src = "./file_img_picture_preload.html?" + currentDPI;
|
||||
// To avoid spurious image loads being reported when the resolution changes,
|
||||
// load an intermediate iframe.
|
||||
iframe.src = "about:blank";
|
||||
iframe.addEventListener('load', function on_iframe_load() {
|
||||
iframe.removeEventListener('load', on_iframe_load);
|
||||
SpecialPowers.pushPrefEnv({'set': [ [ "layout.css.devPixelsPerPx", currentDPI ]] },
|
||||
function() {
|
||||
// Clear image cache for next run (we don't try to validate cached items
|
||||
// in preload).
|
||||
SpecialPowers.Cc["@mozilla.org/image/tools;1"]
|
||||
.getService(SpecialPowers.Ci.imgITools)
|
||||
.getImgCacheForDocument(iframe.contentDocument)
|
||||
.clearCache(false);
|
||||
iframe.src = "./file_img_picture_preload.html?" + currentDPI;
|
||||
});
|
||||
});
|
||||
} else {
|
||||
SimpleTest.finish();
|
||||
|
||||
@@ -171,9 +171,9 @@ ScreenProxy::InvalidateCacheOnNextTick()
|
||||
|
||||
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
|
||||
if (appShell) {
|
||||
appShell->RunInStableState(
|
||||
NS_NewRunnableMethod(this, &ScreenProxy::InvalidateCache)
|
||||
);
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethod(this, &ScreenProxy::InvalidateCache);
|
||||
appShell->RunInStableState(r.forget());
|
||||
} else {
|
||||
// It's pretty bad news if we can't get the appshell. In that case,
|
||||
// let's just invalidate the cache right away.
|
||||
|
||||
@@ -378,7 +378,8 @@ nsBaseAppShell::RunSyncSectionsInternal(bool aStable,
|
||||
}
|
||||
|
||||
void
|
||||
nsBaseAppShell::ScheduleSyncSection(nsIRunnable* aRunnable, bool aStable)
|
||||
nsBaseAppShell::ScheduleSyncSection(already_AddRefed<nsIRunnable> aRunnable,
|
||||
bool aStable)
|
||||
{
|
||||
NS_ASSERTION(NS_IsMainThread(), "Should be on main thread.");
|
||||
|
||||
@@ -435,16 +436,16 @@ nsBaseAppShell::Observe(nsISupports *subject, const char *topic,
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsBaseAppShell::RunInStableState(nsIRunnable* aRunnable)
|
||||
void
|
||||
nsBaseAppShell::RunInStableState(already_AddRefed<nsIRunnable> aRunnable)
|
||||
{
|
||||
ScheduleSyncSection(aRunnable, true);
|
||||
return NS_OK;
|
||||
ScheduleSyncSection(mozilla::Move(aRunnable), true);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
nsBaseAppShell::RunBeforeNextEvent(nsIRunnable* aRunnable)
|
||||
{
|
||||
ScheduleSyncSection(aRunnable, false);
|
||||
nsCOMPtr<nsIRunnable> runnable = aRunnable;
|
||||
ScheduleSyncSection(runnable.forget(), false);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
@@ -25,6 +25,8 @@ class nsBaseAppShell : public nsIAppShell, public nsIThreadObserver,
|
||||
public:
|
||||
NS_DECL_THREADSAFE_ISUPPORTS
|
||||
NS_DECL_NSIAPPSHELL
|
||||
void RunInStableState(already_AddRefed<nsIRunnable> runnable) override;
|
||||
|
||||
NS_DECL_NSITHREADOBSERVER
|
||||
NS_DECL_NSIOBSERVER
|
||||
|
||||
@@ -94,7 +96,7 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void ScheduleSyncSection(nsIRunnable* runnable, bool stable);
|
||||
void ScheduleSyncSection(already_AddRefed<nsIRunnable> runnable, bool stable);
|
||||
|
||||
struct SyncSection {
|
||||
SyncSection()
|
||||
|
||||
+12
-5
@@ -7,12 +7,15 @@
|
||||
#include "nsISupports.idl"
|
||||
|
||||
interface nsIRunnable;
|
||||
%{ C++
|
||||
template <class T> struct already_AddRefed;
|
||||
%}
|
||||
|
||||
/**
|
||||
* Interface for the native event system layer. This interface is designed
|
||||
* to be used on the main application thread only.
|
||||
*/
|
||||
[uuid(2d10ca53-f143-439a-bb2e-c1fbc71f6a05)]
|
||||
[uuid(3d09973e-3975-4fd4-b103-276300cc8437)]
|
||||
interface nsIAppShell : nsISupports
|
||||
{
|
||||
/**
|
||||
@@ -71,16 +74,20 @@ interface nsIAppShell : nsISupports
|
||||
*/
|
||||
readonly attribute unsigned long eventloopNestingLevel;
|
||||
|
||||
%{ C++
|
||||
/**
|
||||
* Allows running of a "synchronous section", in the form of an nsIRunnable
|
||||
* once the event loop has reached a "stable state". We've reached a stable
|
||||
* state when the currently executing task/event has finished, see:
|
||||
* Add a "synchronous section", in the form of an nsIRunnable run once the
|
||||
* event loop has reached a "stable state". |runnable| must not cause any
|
||||
* queued events to be processed (i.e. must not spin the event loop). We've
|
||||
* reached a stable state when the currently executing task/event has
|
||||
* finished, see:
|
||||
* http://www.whatwg.org/specs/web-apps/current-work/multipage/webappapis.html#synchronous-section
|
||||
* In practice this runs aRunnable once the currently executing event
|
||||
* finishes. If called multiple times per task/event, all the runnables will
|
||||
* be executed, in the order in which runInStableState() was called.
|
||||
*/
|
||||
void runInStableState(in nsIRunnable runnable);
|
||||
virtual void RunInStableState(already_AddRefed<nsIRunnable> runnable) = 0;
|
||||
%}
|
||||
|
||||
/**
|
||||
* Run the given runnable before the next iteration of the event loop (this
|
||||
|
||||
@@ -200,9 +200,9 @@ nsScreenManagerProxy::InvalidateCacheOnNextTick()
|
||||
|
||||
nsCOMPtr<nsIAppShell> appShell = do_GetService(kAppShellCID);
|
||||
if (appShell) {
|
||||
appShell->RunInStableState(
|
||||
NS_NewRunnableMethod(this, &nsScreenManagerProxy::InvalidateCache)
|
||||
);
|
||||
nsCOMPtr<nsIRunnable> r =
|
||||
NS_NewRunnableMethod(this, &nsScreenManagerProxy::InvalidateCache);
|
||||
appShell->RunInStableState(r.forget());
|
||||
} else {
|
||||
// It's pretty bad news if we can't get the appshell. In that case,
|
||||
// let's just invalidate the cache right away.
|
||||
|
||||
@@ -60,7 +60,6 @@ EXPORTS += [
|
||||
'nsTPriorityQueue.h',
|
||||
'nsTWeakRef.h',
|
||||
'nsVersionComparator.h',
|
||||
'nsVoidArray.h',
|
||||
'nsWeakReference.h',
|
||||
'nsXPTCUtils.h',
|
||||
'pldhash.h',
|
||||
|
||||
@@ -216,10 +216,9 @@ ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
|
||||
// * modified/removed. Be careful not to NS_RELEASE(foo)!
|
||||
// T* foo = array[i];
|
||||
//
|
||||
// This array will accept null as an argument for any object, and will
|
||||
// store null in the array, just like nsVoidArray. But that also means
|
||||
// that methods like ObjectAt() may return null when referring to an
|
||||
// existing, but null entry in the array.
|
||||
// This array will accept null as an argument for any object, and will store
|
||||
// null in the array. But that also means that methods like ObjectAt() may
|
||||
// return null when referring to an existing, but null entry in the array.
|
||||
template<class T>
|
||||
class nsCOMArray : public nsCOMArray_base
|
||||
{
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,265 +0,0 @@
|
||||
/* -*- 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 nsVoidArray_h___
|
||||
#define nsVoidArray_h___
|
||||
|
||||
//#define DEBUG_VOIDARRAY 1
|
||||
|
||||
#include "nsDebug.h"
|
||||
|
||||
#include "mozilla/MemoryReporting.h"
|
||||
#include <stdint.h>
|
||||
|
||||
// Comparator callback function for sorting array values.
|
||||
typedef int (*nsVoidArrayComparatorFunc)(const void* aElement1,
|
||||
const void* aElement2, void* aData);
|
||||
|
||||
// Enumerator callback function. Return false to stop
|
||||
typedef bool (*nsVoidArrayEnumFunc)(void* aElement, void* aData);
|
||||
typedef bool (*nsVoidArrayEnumFuncConst)(const void* aElement, void* aData);
|
||||
|
||||
// SizeOfExcludingThis callback function.
|
||||
typedef size_t (*nsVoidArraySizeOfElementIncludingThisFunc)(
|
||||
const void* aElement, mozilla::MallocSizeOf aMallocSizeOf, void* aData);
|
||||
|
||||
/// A basic zero-based array of void*'s that manages its own memory
|
||||
class nsVoidArray
|
||||
{
|
||||
public:
|
||||
nsVoidArray();
|
||||
explicit nsVoidArray(int32_t aCount); // initial count of aCount elements set to nullptr
|
||||
~nsVoidArray();
|
||||
|
||||
nsVoidArray& operator=(const nsVoidArray& aOther);
|
||||
|
||||
inline int32_t Count() const { return mImpl ? mImpl->mCount : 0; }
|
||||
// If the array grows, the newly created entries will all be null
|
||||
bool SetCount(int32_t aNewCount);
|
||||
// returns the max number that can be held without allocating
|
||||
inline int32_t GetArraySize() const { return mImpl ? mImpl->mSize : 0; }
|
||||
|
||||
void* FastElementAt(int32_t aIndex) const
|
||||
{
|
||||
NS_ASSERTION(aIndex >= 0 && aIndex < Count(),
|
||||
"nsVoidArray::FastElementAt: index out of range");
|
||||
return mImpl->mArray[aIndex];
|
||||
}
|
||||
|
||||
// This both asserts and bounds-checks, because (1) we don't want
|
||||
// people to write bad code, but (2) we don't want to change it to
|
||||
// crashing for backwards compatibility. See bug 96108.
|
||||
void* ElementAt(int32_t aIndex) const
|
||||
{
|
||||
NS_ASSERTION(aIndex >= 0 && aIndex < Count(),
|
||||
"nsVoidArray::ElementAt: index out of range");
|
||||
return SafeElementAt(aIndex);
|
||||
}
|
||||
|
||||
// bounds-checked version
|
||||
void* SafeElementAt(int32_t aIndex) const
|
||||
{
|
||||
if (uint32_t(aIndex) >= uint32_t(Count())) { // handles aIndex < 0 too
|
||||
return nullptr;
|
||||
}
|
||||
// The bounds check ensures mImpl is non-null.
|
||||
return mImpl->mArray[aIndex];
|
||||
}
|
||||
|
||||
void* operator[](int32_t aIndex) const { return ElementAt(aIndex); }
|
||||
|
||||
int32_t IndexOf(void* aPossibleElement) const;
|
||||
|
||||
bool InsertElementAt(void* aElement, int32_t aIndex);
|
||||
bool InsertElementsAt(const nsVoidArray& aOther, int32_t aIndex);
|
||||
|
||||
bool ReplaceElementAt(void* aElement, int32_t aIndex);
|
||||
|
||||
// useful for doing LRU arrays, sorting, etc
|
||||
bool MoveElement(int32_t aFrom, int32_t aTo);
|
||||
|
||||
bool AppendElement(void* aElement)
|
||||
{
|
||||
return InsertElementAt(aElement, Count());
|
||||
}
|
||||
|
||||
bool AppendElements(nsVoidArray& aElements)
|
||||
{
|
||||
return InsertElementsAt(aElements, Count());
|
||||
}
|
||||
|
||||
bool RemoveElement(void* aElement);
|
||||
void RemoveElementsAt(int32_t aIndex, int32_t aCount);
|
||||
void RemoveElementAt(int32_t aIndex)
|
||||
{
|
||||
return RemoveElementsAt(aIndex, 1);
|
||||
}
|
||||
|
||||
void Clear();
|
||||
|
||||
bool SizeTo(int32_t aMin);
|
||||
// Subtly different - Compact() tries to be smart about whether we
|
||||
// should reallocate the array; SizeTo() always reallocates.
|
||||
void Compact();
|
||||
|
||||
void Sort(nsVoidArrayComparatorFunc aFunc, void* aData);
|
||||
|
||||
bool EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData);
|
||||
bool EnumerateForwards(nsVoidArrayEnumFuncConst aFunc, void* aData) const;
|
||||
bool EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData);
|
||||
|
||||
// Measures the size of the array's element storage, and if
|
||||
// |aSizeOfElementIncludingThis| is non-nullptr, measures the size of things
|
||||
// pointed to by elements.
|
||||
size_t SizeOfExcludingThis(
|
||||
nsVoidArraySizeOfElementIncludingThisFunc aSizeOfElementIncludingThis,
|
||||
mozilla::MallocSizeOf aMallocSizeOf, void* aData = nullptr) const;
|
||||
|
||||
protected:
|
||||
bool GrowArrayBy(int32_t aGrowBy);
|
||||
|
||||
struct Impl
|
||||
{
|
||||
/**
|
||||
* The actual array size.
|
||||
*/
|
||||
int32_t mSize;
|
||||
|
||||
/**
|
||||
* The number of elements in the array
|
||||
*/
|
||||
int32_t mCount;
|
||||
|
||||
/**
|
||||
* Array data, padded out to the actual size of the array.
|
||||
*/
|
||||
void* mArray[1];
|
||||
};
|
||||
|
||||
Impl* mImpl;
|
||||
#if DEBUG_VOIDARRAY
|
||||
int32_t mMaxCount;
|
||||
int32_t mMaxSize;
|
||||
bool mIsAuto;
|
||||
#endif
|
||||
|
||||
// bit twiddlers
|
||||
void SetArray(Impl* aNewImpl, int32_t aSize, int32_t aCount);
|
||||
|
||||
private:
|
||||
/// Copy constructors are not allowed
|
||||
nsVoidArray(const nsVoidArray& aOther);
|
||||
};
|
||||
|
||||
//===================================================================
|
||||
// nsSmallVoidArray is not a general-purpose replacement for
|
||||
// ns(Auto)VoidArray because there is (some) extra CPU overhead for arrays
|
||||
// larger than 1 element, though not a lot. It is appropriate for
|
||||
// space-sensitive uses where sizes of 0 or 1 are moderately common or
|
||||
// more, and where we're NOT storing arbitrary integers or arbitrary
|
||||
// pointers.
|
||||
|
||||
// NOTE: nsSmallVoidArray can ONLY be used for holding items that always
|
||||
// have the low bit as a 0 - i.e. element & 1 == 0. This happens to be
|
||||
// true for allocated and object pointers for all the architectures we run
|
||||
// on, but conceivably there might be some architectures/compilers for
|
||||
// which it is NOT true. We know this works for all existing architectures
|
||||
// because if it didn't then nsCheapVoidArray would have failed. Also note
|
||||
// that we will ASSERT if this assumption is violated in DEBUG builds.
|
||||
|
||||
// XXX we're really re-implementing the whole nsVoidArray interface here -
|
||||
// some form of abstract class would be useful
|
||||
|
||||
// I disagree on the abstraction here. If the point of this class is to be
|
||||
// as small as possible, and no one will ever derive from it, as I found
|
||||
// today, there should not be any virtualness to it to avoid the vtable
|
||||
// ptr overhead.
|
||||
|
||||
class nsSmallVoidArray : private nsVoidArray
|
||||
{
|
||||
public:
|
||||
~nsSmallVoidArray();
|
||||
|
||||
nsSmallVoidArray& operator=(nsSmallVoidArray& aOther);
|
||||
void* operator[](int32_t aIndex) const { return ElementAt(aIndex); }
|
||||
|
||||
int32_t GetArraySize() const;
|
||||
|
||||
int32_t Count() const;
|
||||
void* FastElementAt(int32_t aIndex) const;
|
||||
// This both asserts and bounds-checks, because (1) we don't want
|
||||
// people to write bad code, but (2) we don't want to change it to
|
||||
// crashing for backwards compatibility. See bug 96108.
|
||||
void* ElementAt(int32_t aIndex) const
|
||||
{
|
||||
NS_ASSERTION(aIndex >= 0 && aIndex < Count(),
|
||||
"nsSmallVoidArray::ElementAt: index out of range");
|
||||
return SafeElementAt(aIndex);
|
||||
}
|
||||
void* SafeElementAt(int32_t aIndex) const
|
||||
{
|
||||
// let compiler inline; it may be able to remove these checks
|
||||
if (uint32_t(aIndex) >= uint32_t(Count())) { // handles aIndex < 0 too
|
||||
return nullptr;
|
||||
}
|
||||
return FastElementAt(aIndex);
|
||||
}
|
||||
int32_t IndexOf(void* aPossibleElement) const;
|
||||
bool InsertElementAt(void* aElement, int32_t aIndex);
|
||||
bool InsertElementsAt(const nsVoidArray& aOther, int32_t aIndex);
|
||||
bool ReplaceElementAt(void* aElement, int32_t aIndex);
|
||||
bool MoveElement(int32_t aFrom, int32_t aTo);
|
||||
bool AppendElement(void* aElement);
|
||||
bool AppendElements(nsVoidArray& aElements)
|
||||
{
|
||||
return InsertElementsAt(aElements, Count());
|
||||
}
|
||||
bool RemoveElement(void* aElement);
|
||||
void RemoveElementsAt(int32_t aIndex, int32_t aCount);
|
||||
void RemoveElementAt(int32_t aIndex);
|
||||
|
||||
void Clear();
|
||||
bool SizeTo(int32_t aMin);
|
||||
void Compact();
|
||||
void Sort(nsVoidArrayComparatorFunc aFunc, void* aData);
|
||||
|
||||
bool EnumerateForwards(nsVoidArrayEnumFunc aFunc, void* aData);
|
||||
bool EnumerateBackwards(nsVoidArrayEnumFunc aFunc, void* aData);
|
||||
|
||||
private:
|
||||
|
||||
bool HasSingle() const
|
||||
{
|
||||
return !!(reinterpret_cast<intptr_t>(mImpl) & 0x1);
|
||||
}
|
||||
void* GetSingle() const
|
||||
{
|
||||
NS_ASSERTION(HasSingle(), "wrong type");
|
||||
return reinterpret_cast<void*>(reinterpret_cast<intptr_t>(mImpl) & ~0x1);
|
||||
}
|
||||
void SetSingle(void* aChild)
|
||||
{
|
||||
NS_ASSERTION(HasSingle() || !mImpl, "overwriting array");
|
||||
mImpl = reinterpret_cast<Impl*>(reinterpret_cast<intptr_t>(aChild) | 0x1);
|
||||
}
|
||||
bool IsEmpty() const
|
||||
{
|
||||
// Note that this isn't the same as Count()==0
|
||||
return !mImpl;
|
||||
}
|
||||
const nsVoidArray* AsArray() const
|
||||
{
|
||||
NS_ASSERTION(!HasSingle(), "This is a single");
|
||||
return this;
|
||||
}
|
||||
nsVoidArray* AsArray()
|
||||
{
|
||||
NS_ASSERTION(!HasSingle(), "This is a single");
|
||||
return this;
|
||||
}
|
||||
bool EnsureArray();
|
||||
};
|
||||
|
||||
#endif /* nsVoidArray_h___ */
|
||||
@@ -29,7 +29,6 @@ xpcom_glue_src_lcppsrcs = [
|
||||
'nsThreadUtils.cpp',
|
||||
'nsTObserverArray.cpp',
|
||||
'nsVersionComparator.cpp',
|
||||
'nsVoidArray.cpp',
|
||||
'nsWeakReference.cpp',
|
||||
'pldhash.cpp',
|
||||
]
|
||||
|
||||
Reference in New Issue
Block a user