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:
2021-01-21 14:16:50 +08:00
parent d8dded16af
commit 3dc3bde9f2
70 changed files with 1622 additions and 1634 deletions
@@ -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();
}
+1 -1
View File
@@ -437,7 +437,7 @@ nsCCUncollectableMarker::Observe(nsISupports* aSubject, const char* aTopic,
switch(sFSState) {
case eUnmarkJSEventListeners: {
nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments(sGeneration);
nsContentUtils::UnmarkGrayJSListenersInCCGenerationDocuments();
break;
}
case eUnmarkMessageManagers: {
+3 -9
View File
@@ -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);
}
}
+5 -2
View File
@@ -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
View File
@@ -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()
{
+36 -1
View File
@@ -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;
+4 -6
View File
@@ -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
View File
@@ -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
View File
@@ -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;
+25
View File
@@ -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();
}
+2
View File
@@ -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
View File
@@ -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;
+3 -4
View File
@@ -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
View File
@@ -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;
}
+3
View File
@@ -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;
};
+71
View File
@@ -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();
}
+2
View File
@@ -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]
+26
View File
@@ -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>
+2
View File
@@ -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]
+24
View File
@@ -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>
+7
View File
@@ -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;
}
+13
View File
@@ -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);
}
}
}
+69
View File
@@ -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
+25 -1
View File
@@ -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
+11 -1
View File
@@ -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);
};
+69
View File
@@ -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
View File
@@ -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]
+4
View File
@@ -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]
+25
View File
@@ -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>
+27
View File
@@ -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>
+106
View File
@@ -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>
+60 -30
View File
@@ -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
+4
View File
@@ -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);
+5 -5
View File
@@ -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);
}
}
}
+1 -6
View File
@@ -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()
+1
View File
@@ -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]
+44
View File
@@ -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>
+5 -3
View File
@@ -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
+2 -6
View File
@@ -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();
+2 -6
View File
@@ -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;
+1 -10
View File
@@ -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
+8 -10
View File
@@ -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
View File
@@ -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))
+1 -1
View File
@@ -75,7 +75,7 @@ public:
bool RemoveElement(mozilla::dom::Element* aElement);
private:
nsSmallVoidArray mRefContentList;
nsTArray<mozilla::dom::Element*> mRefContentList;
};
/**
-1
View File
@@ -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);
});
-5
View File
@@ -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");
+2
View File
@@ -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
+4
View File
@@ -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));
+49 -15
View File
@@ -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(
+274
View File
@@ -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);
});
};
+2
View File
@@ -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>
+2
View File
@@ -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]
+29
View File
@@ -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>
+3
View File
@@ -67,6 +67,9 @@ nsHtml5SpeculativeLoad::Perform(nsHtml5TreeOpExecutor* aExecutor)
intSource);
}
break;
case eSpeculativeLoadPreconnect:
aExecutor->Preconnect(mUrl);
break;
default:
NS_NOTREACHED("Bogus speculative load.");
break;
+10 -1
View File
@@ -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:
+18 -10
View File
@@ -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();
+3 -3
View File
@@ -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.
+7 -6
View File
@@ -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;
}
+3 -1
View File
@@ -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
View File
@@ -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
+3 -3
View File
@@ -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.
-1
View File
@@ -60,7 +60,6 @@ EXPORTS += [
'nsTPriorityQueue.h',
'nsTWeakRef.h',
'nsVersionComparator.h',
'nsVoidArray.h',
'nsWeakReference.h',
'nsXPTCUtils.h',
'pldhash.h',
+3 -4
View File
@@ -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
-265
View File
@@ -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___ */
-1
View File
@@ -29,7 +29,6 @@ xpcom_glue_src_lcppsrcs = [
'nsThreadUtils.cpp',
'nsTObserverArray.cpp',
'nsVersionComparator.cpp',
'nsVoidArray.cpp',
'nsWeakReference.cpp',
'pldhash.cpp',
]