author | Sebastian Hengst <archaeopteryx@coole-files.de> |
Sat, 02 Sep 2017 10:54:44 +0200 | |
changeset 378438 | a46a5879b8781ae9ea99f37b5d34a891f0f75047 |
parent 378437 | b01a7e57425b5fe791ab091f5c33e069890753fb (current diff) |
parent 378352 | 59ea29d58ab0b297fd57c3ac1595d770d1f389d6 (diff) |
child 378439 | 5cc81a8efe158d3ea9504251cb3563d1a474e4b6 |
child 378442 | 66d3a14132a12d5a871ecffbab5a9f3775b02989 |
child 378456 | 1fdc9b1dbb9e5b4f4ef03934dfa1827f3581d725 |
push id | 94474 |
push user | archaeopteryx@coole-files.de |
push date | Sat, 02 Sep 2017 09:00:26 +0000 |
treeherder | mozilla-inbound@a46a5879b878 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge, merge |
milestone | 57.0a1 |
first release with | nightly linux32
a46a5879b878
/
57.0a1
/
20170902100317
/
files
nightly linux64
a46a5879b878
/
57.0a1
/
20170902100317
/
files
nightly mac
a46a5879b878
/
57.0a1
/
20170902100317
/
files
nightly win32
a46a5879b878
/
57.0a1
/
20170902100317
/
files
nightly win64
a46a5879b878
/
57.0a1
/
20170902100317
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
57.0a1
/
20170902100317
/
pushlog to previous
nightly linux64
57.0a1
/
20170902100317
/
pushlog to previous
nightly mac
57.0a1
/
20170902100317
/
pushlog to previous
nightly win32
57.0a1
/
20170902100317
/
pushlog to previous
nightly win64
57.0a1
/
20170902100317
/
pushlog to previous
|
--- a/accessible/windows/msaa/Compatibility.cpp +++ b/accessible/windows/msaa/Compatibility.cpp @@ -19,16 +19,35 @@ #include "mozilla/Preferences.h" #include <shlobj.h> using namespace mozilla; using namespace mozilla::a11y; /** + * String versions of consumer flags. See GetHumanReadableConsumersStr. + */ +static const wchar_t* ConsumerStringMap[CONSUMERS_ENUM_LEN+1] = { + L"NVDA", + L"JAWS", + L"OLDJAWS", + L"WE", + L"DOLPHIN", + L"SEROTEK", + L"COBRA", + L"ZOOMTEXT", + L"KAZAGURU", + L"YOUDAO", + L"UNKNOWN", + L"UIAUTOMATION", + L"\0" +}; + +/** * Return true if module version is lesser than the given version. */ bool IsModuleVersionLessThan(HMODULE aModuleHandle, DWORD aMajor, DWORD aMinor) { wchar_t fileName[MAX_PATH]; ::GetModuleFileNameW(aModuleHandle, fileName, MAX_PATH); @@ -393,8 +412,27 @@ Compatibility::GetActCtxResourceId() UseIAccessibleProxyStub()) { return 64; } return 32; #endif // defined(HAVE_64BIT_BUILD) } +// static +void +Compatibility::GetHumanReadableConsumersStr(nsAString &aResult) +{ + bool appened = false; + uint32_t index = 0; + for (uint32_t consumers = sConsumers; consumers; consumers = consumers >> 1) { + if (consumers & 0x1) { + if (appened) { + aResult.AppendLiteral(","); + } + aResult.Append(ConsumerStringMap[index]); + appened = true; + } + if (++index > CONSUMERS_ENUM_LEN) { + break; + } + } +}
--- a/accessible/windows/msaa/Compatibility.h +++ b/accessible/windows/msaa/Compatibility.h @@ -2,16 +2,17 @@ /* vim: set ts=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 COMPATIBILITY_MANAGER_H #define COMPATIBILITY_MANAGER_H +#include "nsString.h" #include <stdint.h> namespace mozilla { namespace a11y { /** * Used to get compatibility modes. Note, modes are computed at accessibility * start up time and aren't changed during lifetime. @@ -40,16 +41,22 @@ public: static bool IsDolphin() { return !!(sConsumers & DOLPHIN); } /** * @return ID of a11y manifest resource to be passed to * mscom::ActivationContext */ static uint16_t GetActCtxResourceId(); + /** + * Return a string describing sConsumers suitable for about:support. + * Exposed through nsIXULRuntime.accessibilityInstantiator. + */ + static void GetHumanReadableConsumersStr(nsAString &aResult); + private: Compatibility(); Compatibility(const Compatibility&); Compatibility& operator = (const Compatibility&); /** * Initialize compatibility mode. Called by platform (see Platform.h) during * accessibility initialization. @@ -69,16 +76,17 @@ private: SEROTEK = 1 << 5, COBRA = 1 << 6, ZOOMTEXT = 1 << 7, KAZAGURU = 1 << 8, YOUDAO = 1 << 9, UNKNOWN = 1 << 10, UIAUTOMATION = 1 << 11 }; + #define CONSUMERS_ENUM_LEN 12 private: static uint32_t sConsumers; }; } // a11y namespace } // mozilla namespace
--- a/browser/base/content/tabbrowser.xml +++ b/browser/base/content/tabbrowser.xml @@ -2322,16 +2322,17 @@ }); } ]]> </body> </method> <method name="_insertBrowser"> <parameter name="aTab"/> + <parameter name="aInsertedOnTabCreation"/> <body> <![CDATA[ "use strict"; // If browser is already inserted or window is closed don't do anything. if (aTab.linkedPanel || window.closed) { return; } @@ -2383,17 +2384,18 @@ // set the "nodefaultsrc" attribute that prevents a frameLoader // from being created as soon as the linked <browser> is inserted // into the DOM. We thus have to register the new outerWindowID // for non-remote browsers after we have called browser.loadURI(). if (remoteType == E10SUtils.NOT_REMOTE) { this._outerWindowIDBrowserMap.set(browser.outerWindowID, browser); } - var evt = new CustomEvent("TabBrowserInserted", { bubbles: true, detail: {} }); + var evt = new CustomEvent("TabBrowserInserted", + { bubbles: true, detail: { insertedOnTabCreation: aInsertedOnTabCreation } }); aTab.dispatchEvent(evt); ]]> </body> </method> <method name="addTab"> <parameter name="aURI"/> <parameter name="aReferrerURI"/> @@ -2634,17 +2636,17 @@ if (lazyBrowserURI) { // Lazy browser must be explicitly registered so tab will appear as // a switch-to-tab candidate in autocomplete. this._unifiedComplete.registerOpenPage(lazyBrowserURI, aUserContextId); b.registeredOpenURI = lazyBrowserURI; } } else { - this._insertBrowser(t); + this._insertBrowser(t, true); } // Dispatch a new tab notification. We do this once we're // entirely done, so that things are in a consistent state // even if the event listener opens or closes tabs. var detail = aEventDetail || {}; var evt = new CustomEvent("TabOpen", { bubbles: true, detail }); t.dispatchEvent(evt);
--- a/browser/components/extensions/ext-browser.js +++ b/browser/components/extensions/ext-browser.js @@ -564,16 +564,20 @@ class Tab extends TabBase { get audible() { return this.nativeTab.soundPlaying; } get browser() { return this.nativeTab.linkedBrowser; } + get discarded() { + return !this.nativeTab.linkedPanel; + } + get frameLoader() { // If we don't have a frameLoader yet, just return a dummy with no width and // height. return super.frameLoader || {lazyWidth: 0, lazyHeight: 0}; } get cookieStoreId() { return getCookieStoreIdForTab(this, this.nativeTab);
--- a/browser/components/extensions/ext-tabs.js +++ b/browser/components/extensions/ext-tabs.js @@ -257,16 +257,19 @@ this.tabs = class extends ExtensionAPI { } if (changed.includes("label")) { needed.push("title"); } } else if (event.type == "TabPinned") { needed.push("pinned"); } else if (event.type == "TabUnpinned") { needed.push("pinned"); + } else if (event.type == "TabBrowserInserted" && + !event.detail.insertedOnTabCreation) { + needed.push("discarded"); } let tab = tabManager.getWrapper(event.originalTarget); let changeInfo = {}; for (let prop of needed) { changeInfo[prop] = tab[prop]; } @@ -285,22 +288,24 @@ this.tabs = class extends ExtensionAPI { fireForTab(tabManager.wrapTab(tabElem), changed); } }; windowTracker.addListener("status", statusListener); windowTracker.addListener("TabAttrModified", listener); windowTracker.addListener("TabPinned", listener); windowTracker.addListener("TabUnpinned", listener); + windowTracker.addListener("TabBrowserInserted", listener); return () => { windowTracker.removeListener("status", statusListener); windowTracker.removeListener("TabAttrModified", listener); windowTracker.removeListener("TabPinned", listener); windowTracker.removeListener("TabUnpinned", listener); + windowTracker.removeListener("TabBrowserInserted", listener); }; }).api(), create(createProperties) { return new Promise((resolve, reject) => { let window = createProperties.windowId !== null ? windowTracker.getWindow(createProperties.windowId, context) : windowTracker.topWindow;
--- a/browser/components/extensions/schemas/tabs.json +++ b/browser/components/extensions/schemas/tabs.json @@ -66,16 +66,17 @@ "pinned": {"type": "boolean", "description": "Whether the tab is pinned."}, "lastAccessed": {"type": "integer", "optional": true, "description": "The last time the tab was accessed as the number of milliseconds since epoch."}, "audible": {"type": "boolean", "optional": true, "description": "Whether the tab has produced sound over the past couple of seconds (but it might not be heard if also muted). Equivalent to whether the speaker audio indicator is showing."}, "mutedInfo": {"$ref": "MutedInfo", "optional": true, "description": "Current tab muted state and the reason for the last state change."}, "url": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The URL the tab is displaying. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."}, "title": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The title of the tab. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."}, "favIconUrl": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The URL of the tab's favicon. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission. It may also be an empty string if the tab is loading."}, "status": {"type": "string", "optional": true, "description": "Either <em>loading</em> or <em>complete</em>."}, + "discarded": {"type": "boolean", "optional": true, "description": "True while the tab is not loaded with content."}, "incognito": {"type": "boolean", "description": "Whether the tab is in an incognito window."}, "width": {"type": "integer", "optional": true, "description": "The width of the tab in pixels."}, "height": {"type": "integer", "optional": true, "description": "The height of the tab in pixels."}, "sessionId": {"type": "string", "optional": true, "description": "The session ID used to uniquely identify a Tab obtained from the $(ref:sessions) API."}, "cookieStoreId": {"type": "string", "optional": true, "description": "The CookieStoreId used for the tab."} } }, { @@ -584,16 +585,21 @@ "optional": true, "description": "Whether the tabs are in the last focused window." }, "status": { "$ref": "TabStatus", "optional": true, "description": "Whether the tabs have completed loading." }, + "discarded": { + "type": "boolean", + "optional": true, + "description": "True while the tabs are not loaded with content." + }, "title": { "type": "string", "optional": true, "description": "Match page titles against a pattern." }, "url": { "choices": [ {"type": "string"}, @@ -1190,16 +1196,21 @@ "name": "changeInfo", "description": "Lists the changes to the state of the tab that was updated.", "properties": { "status": { "type": "string", "optional": true, "description": "The status of the tab. Can be either <em>loading</em> or <em>complete</em>." }, + "discarded": { + "type": "boolean", + "optional": true, + "description": "True while the tab is not loaded with content." + }, "url": { "type": "string", "optional": true, "description": "The tab's URL if it has changed." }, "pinned": { "type": "boolean", "optional": true,
--- a/browser/components/extensions/test/browser/browser-common.ini +++ b/browser/components/extensions/test/browser/browser-common.ini @@ -122,16 +122,17 @@ skip-if = debug || asan # Bug 1354681 [browser_ext_sidebarAction_windows.js] [browser_ext_simple.js] [browser_ext_tab_runtimeConnect.js] [browser_ext_tabs_audio.js] [browser_ext_tabs_captureVisibleTab.js] [browser_ext_tabs_create.js] [browser_ext_tabs_create_invalid_url.js] [browser_ext_tabs_detectLanguage.js] +[browser_ext_tabs_discarded.js] [browser_ext_tabs_duplicate.js] [browser_ext_tabs_events.js] [browser_ext_tabs_executeScript.js] [browser_ext_tabs_executeScript_good.js] [browser_ext_tabs_executeScript_bad.js] [browser_ext_tabs_executeScript_multiple.js] [browser_ext_tabs_executeScript_no_create.js] [browser_ext_tabs_executeScript_runAt.js]
new file mode 100644 --- /dev/null +++ b/browser/components/extensions/test/browser/browser_ext_tabs_discarded.js @@ -0,0 +1,66 @@ +/* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ +/* vim: set sts=2 sw=2 et tw=80: */ +/* global gBrowser SessionStore */ +"use strict"; + +let lazyTabState = {entries: [{url: "http://example.com/", title: "Example Domain"}]}; + +add_task(async function test_discarded() { + let extension = ExtensionTestUtils.loadExtension({ + manifest: { + "permissions": ["tabs"], + }, + + background: async function() { + let onCreatedTabData = []; + let discardedEventData = []; + + async function finishTest() { + browser.test.assertEq(0, discardedEventData.length, "number of discarded events fired"); + + onCreatedTabData.sort((data1, data2) => data1.index - data2.index); + browser.test.assertEq(false, onCreatedTabData[0].discarded, "non-lazy tab onCreated discard property"); + browser.test.assertEq(true, onCreatedTabData[1].discarded, "lazy tab onCreated discard property"); + + let tabs = await browser.tabs.query({currentWindow: true}); + tabs.sort((tab1, tab2) => tab1.index - tab2.index); + + browser.test.assertEq(false, tabs[1].discarded, "non-lazy tab query discard property"); + browser.test.assertEq(true, tabs[2].discarded, "lazy tab query discard property"); + + let updatedTab = await browser.tabs.update(tabs[2].id, {active: true}); + browser.test.assertEq(false, updatedTab.discarded, "lazy to non-lazy update discard property"); + browser.test.assertEq(false, discardedEventData[0], "lazy to non-lazy onUpdated discard property"); + + browser.test.notifyPass("test-finished"); + } + + browser.tabs.onUpdated.addListener(function(tabId, updatedInfo) { + if ("discarded" in updatedInfo) { + discardedEventData.push(updatedInfo.discarded); + } + }); + + browser.tabs.onCreated.addListener(function(tab) { + onCreatedTabData.push({discarded: tab.discarded, index: tab.index}); + if (onCreatedTabData.length == 2) { + finishTest(); + } + }); + }, + }); + + await extension.startup(); + + let tab1 = await BrowserTestUtils.openNewForegroundTab(gBrowser, "http://example.com"); + + let tab2 = BrowserTestUtils.addTab(gBrowser, "about:blank", {createLazyBrowser: true}); + SessionStore.setTabState(tab2, JSON.stringify(lazyTabState)); + + await extension.awaitFinish("test-finished"); + await extension.unload(); + + await BrowserTestUtils.removeTab(tab1); + await BrowserTestUtils.removeTab(tab2); +}); +
--- a/dom/base/Attr.cpp +++ b/dom/base/Attr.cpp @@ -314,23 +314,16 @@ Attr::GetChildCount() const } nsIContent * Attr::GetChildAt(uint32_t aIndex) const { return nullptr; } -nsIContent * const * -Attr::GetChildArray(uint32_t* aChildCount) const -{ - *aChildCount = 0; - return nullptr; -} - int32_t Attr::IndexOf(const nsINode* aPossibleChild) const { return -1; } nsresult Attr::InsertChildAt(nsIContent* aKid, uint32_t aIndex,
--- a/dom/base/Attr.h +++ b/dom/base/Attr.h @@ -60,17 +60,16 @@ public: void SetMap(nsDOMAttributeMap *aMap) override; Element* GetElement() const; nsresult SetOwnerDocument(nsIDocument* aDocument) override; // nsINode interface virtual bool IsNodeOfType(uint32_t aFlags) const override; virtual uint32_t GetChildCount() const override; virtual nsIContent *GetChildAt(uint32_t aIndex) const override; - virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const override; virtual int32_t IndexOf(const nsINode* aPossibleChild) const override; virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) override; virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult, bool aPreallocateChildren) const override; virtual already_AddRefed<nsIURI> GetBaseURI(bool aTryUseXHRDocBaseURI = false) const override;
--- a/dom/base/Element.cpp +++ b/dom/base/Element.cpp @@ -3039,19 +3039,25 @@ Element::List(FILE* out, int32_t aIndent fprintf(out, "@%p", (void *)this); ListAttributes(out); fprintf(out, " state=[%llx]", static_cast<unsigned long long>(State().GetInternalValue())); fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags())); if (IsCommonAncestorForRangeInSelection()) { - const nsTHashtable<nsPtrHashKey<nsRange>>* ranges = - GetExistingCommonAncestorRanges(); - fprintf(out, " ranges:%d", ranges ? ranges->Count() : 0); + const LinkedList<nsRange>* ranges = GetExistingCommonAncestorRanges(); + int32_t count = 0; + if (ranges) { + // Can't use range-based iteration on a const LinkedList, unfortunately. + for (const nsRange* r = ranges->getFirst(); r; r = r->getNext()) { + ++count; + } + } + fprintf(out, " ranges:%d", count); } fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame())); fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get()); nsIContent* child = GetFirstChild(); if (child) { fputs("\n", out);
--- a/dom/base/FragmentOrElement.cpp +++ b/dom/base/FragmentOrElement.cpp @@ -2322,22 +2322,16 @@ FragmentOrElement::GetChildCount() const } nsIContent * FragmentOrElement::GetChildAt(uint32_t aIndex) const { return mAttrsAndChildren.GetSafeChildAt(aIndex); } -nsIContent * const * -FragmentOrElement::GetChildArray(uint32_t* aChildCount) const -{ - return mAttrsAndChildren.GetChildArray(aChildCount); -} - int32_t FragmentOrElement::IndexOf(const nsINode* aPossibleChild) const { return mAttrsAndChildren.IndexOfChild(aPossibleChild); } static inline bool IsVoidTag(nsIAtom* aTag)
--- a/dom/base/FragmentOrElement.h +++ b/dom/base/FragmentOrElement.h @@ -115,17 +115,16 @@ public: NS_DECL_CYCLE_COLLECTING_ISUPPORTS NS_DECL_ADDSIZEOFEXCLUDINGTHIS // nsINode interface methods virtual uint32_t GetChildCount() const override; virtual nsIContent *GetChildAt(uint32_t aIndex) const override; - virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const override; virtual int32_t IndexOf(const nsINode* aPossibleChild) const override; virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) override; virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override; virtual void GetTextContentInternal(nsAString& aTextContent, mozilla::OOMReporter& aError) override; virtual void SetTextContentInternal(const nsAString& aTextContent, mozilla::ErrorResult& aError) override;
--- a/dom/base/nsDocument.cpp +++ b/dom/base/nsDocument.cpp @@ -4349,23 +4349,16 @@ nsDocument::IndexOf(const nsINode* aPoss } uint32_t nsDocument::GetChildCount() const { return mChildren.ChildCount(); } -nsIContent * const * -nsDocument::GetChildArray(uint32_t* aChildCount) const -{ - return mChildren.GetChildArray(aChildCount); -} - - nsresult nsDocument::InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) { if (aKid->IsElement() && GetRootElement()) { NS_WARNING("Inserting root element when we already have one"); return NS_ERROR_DOM_HIERARCHY_REQUEST_ERR; }
--- a/dom/base/nsDocument.h +++ b/dom/base/nsDocument.h @@ -593,17 +593,16 @@ public: virtual void OnPageHide(bool aPersisted, mozilla::dom::EventTarget* aDispatchStartTarget) override; virtual void WillDispatchMutationEvent(nsINode* aTarget) override; virtual void MutationEventDispatched(nsINode* aTarget) override; // nsINode virtual bool IsNodeOfType(uint32_t aFlags) const override; virtual nsIContent *GetChildAt(uint32_t aIndex) const override; - virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const override; virtual int32_t IndexOf(const nsINode* aPossibleChild) const override; virtual uint32_t GetChildCount() const override; virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) override; virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override; virtual nsresult Clone(mozilla::dom::NodeInfo *aNodeInfo, nsINode **aResult, bool aPreallocateChildren) const override {
--- a/dom/base/nsGenericDOMDataNode.cpp +++ b/dom/base/nsGenericDOMDataNode.cpp @@ -676,23 +676,16 @@ nsGenericDOMDataNode::GetChildCount() co } nsIContent * nsGenericDOMDataNode::GetChildAt(uint32_t aIndex) const { return nullptr; } -nsIContent * const * -nsGenericDOMDataNode::GetChildArray(uint32_t* aChildCount) const -{ - *aChildCount = 0; - return nullptr; -} - int32_t nsGenericDOMDataNode::IndexOf(const nsINode* aPossibleChild) const { return -1; } nsresult nsGenericDOMDataNode::InsertChildAt(nsIContent* aKid, uint32_t aIndex,
--- a/dom/base/nsGenericDOMDataNode.h +++ b/dom/base/nsGenericDOMDataNode.h @@ -95,17 +95,16 @@ public: nsresult InsertData(uint32_t aOffset, const nsAString& aArg); nsresult DeleteData(uint32_t aOffset, uint32_t aCount); nsresult ReplaceData(uint32_t aOffset, uint32_t aCount, const nsAString& aArg); // nsINode methods virtual uint32_t GetChildCount() const override; virtual nsIContent *GetChildAt(uint32_t aIndex) const override; - virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const override; virtual int32_t IndexOf(const nsINode* aPossibleChild) const override; virtual nsresult InsertChildAt(nsIContent* aKid, uint32_t aIndex, bool aNotify) override; virtual void RemoveChildAt(uint32_t aIndex, bool aNotify) override; virtual void GetTextContentInternal(nsAString& aTextContent, mozilla::OOMReporter& aError) override { GetNodeValue(aTextContent);
--- a/dom/base/nsINode.h +++ b/dom/base/nsINode.h @@ -14,16 +14,17 @@ #include "nsIDOMNode.h" #include "mozilla/dom/NodeInfo.h" // member (in nsCOMPtr) #include "nsIVariant.h" // for use in GetUserData() #include "nsNodeInfoManager.h" // for use in NodePrincipal() #include "nsPropertyTable.h" // for typedefs #include "nsTObserverArray.h" // for member #include "nsWindowSizes.h" // for nsStyleSizes #include "mozilla/ErrorResult.h" +#include "mozilla/LinkedList.h" #include "mozilla/MemoryReporting.h" #include "mozilla/dom/EventTarget.h" // for base class #include "js/TypeDecls.h" // for Handle, Value, JSObject, JSContext #include "mozilla/dom/DOMString.h" #include "mozilla/dom/BindingDeclarations.h" #include "nsTHashtable.h" #include <iosfwd> @@ -496,27 +497,16 @@ public: /** * Get a child by index * @param aIndex the index of the child to get * @return the child, or null if index out of bounds */ virtual nsIContent* GetChildAt(uint32_t aIndex) const = 0; /** - * Get a raw pointer to the child array. This should only be used if you - * plan to walk a bunch of the kids, promise to make sure that nothing ever - * mutates (no attribute changes, not DOM tree changes, no script execution, - * NOTHING), and will never ever peform an out-of-bounds access here. This - * method may return null if there are no children, or it may return a - * garbage pointer. In all cases the out param will be set to the number of - * children. - */ - virtual nsIContent * const * GetChildArray(uint32_t* aChildCount) const = 0; - - /** * Get the index of a child within this content * @param aPossibleChild the child to get the index of. * @return the index of the child, or -1 if not a child * * If the return value is not -1, then calling GetChildAt() with that value * will return aPossibleChild. */ virtual int32_t IndexOf(const nsINode* aPossibleChild) const = 0; @@ -1127,20 +1117,22 @@ public: /** * Weak reference to this node. This is cleared by the destructor of * nsNodeWeakReference. */ nsNodeWeakReference* MOZ_NON_OWNING_REF mWeakReference; /** - * A set of ranges in the common ancestor for the selection to which - * this node belongs to. + * A set of ranges which are in the selection and which have this node as + * their endpoints' common ancestor. This is a UniquePtr instead of just a + * LinkedList, because that prevents us from pushing DOMSlots up to the next + * allocation bucket size, at the cost of some complexity. */ - mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>> mCommonAncestorRanges; + mozilla::UniquePtr<mozilla::LinkedList<nsRange>> mCommonAncestorRanges; /** * Number of descendant nodes in the uncomposed document that have been * explicitly set as editable. */ uint32_t mEditableDescendantCount; }; @@ -1283,20 +1275,19 @@ public: * not in same subtree, this returns the root content of the closeset subtree. */ nsIContent* GetSelectionRootContent(nsIPresShell* aPresShell); virtual nsINodeList* ChildNodes(); nsIContent* GetFirstChild() const { return mFirstChild; } nsIContent* GetLastChild() const { - uint32_t count; - nsIContent* const* children = GetChildArray(&count); + uint32_t count = GetChildCount(); - return count > 0 ? children[count - 1] : nullptr; + return count > 0 ? GetChildAt(count - 1) : nullptr; } /** * Implementation is in nsIDocument.h, because it needs to cast from * nsIDocument* to nsINode*. */ nsIDocument* GetOwnerDocument() const; @@ -1938,33 +1929,33 @@ public: CallerType aCallerType, ErrorResult& aRv); already_AddRefed<DOMPoint> ConvertPointFromNode(const DOMPointInit& aPoint, const TextOrElementOrDocument& aFrom, const ConvertCoordinateOptions& aOptions, CallerType aCallerType, ErrorResult& aRv); - const nsTHashtable<nsPtrHashKey<nsRange>>* GetExistingCommonAncestorRanges() const + const mozilla::LinkedList<nsRange>* GetExistingCommonAncestorRanges() const { if (!HasSlots()) { return nullptr; } - mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>>& ranges = - GetExistingSlots()->mCommonAncestorRanges; - return ranges.get(); + return GetExistingSlots()->mCommonAncestorRanges.get(); } - nsTHashtable<nsPtrHashKey<nsRange>>* GetExistingCommonAncestorRanges() + mozilla::LinkedList<nsRange>* GetExistingCommonAncestorRanges() { - nsINode::nsSlots* slots = GetExistingSlots(); - return slots ? slots->mCommonAncestorRanges.get() : nullptr; + if (!HasSlots()) { + return nullptr; + } + return GetExistingSlots()->mCommonAncestorRanges.get(); } - mozilla::UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>>& GetCommonAncestorRangesPtr() + mozilla::UniquePtr<mozilla::LinkedList<nsRange>>& GetCommonAncestorRangesPtr() { return Slots()->mCommonAncestorRanges; } protected: // Override this function to create a custom slots class. // Must not return null.
--- a/dom/base/nsRange.cpp +++ b/dom/base/nsRange.cpp @@ -200,24 +200,24 @@ nsRange::IsNodeSelected(nsINode* aNode, NS_ASSERTION(n || !aNode->IsSelectionDescendant(), "orphan selection descendant"); // Collect the potential ranges and their selection objects. RangeHashTable ancestorSelectionRanges; nsTHashtable<nsPtrHashKey<Selection>> ancestorSelections; uint32_t maxRangeCount = 0; for (; n; n = GetNextRangeCommonAncestor(n->GetParentNode())) { - nsTHashtable<nsPtrHashKey<nsRange>>* ranges = - n->GetExistingCommonAncestorRanges(); + LinkedList<nsRange>* ranges = n->GetExistingCommonAncestorRanges(); if (!ranges) { continue; } - for (auto iter = ranges->ConstIter(); !iter.Done(); iter.Next()) { - nsRange* range = iter.Get()->GetKey(); - if (range->IsInSelection() && !range->Collapsed()) { + for (nsRange* range : *ranges) { + MOZ_ASSERT(range->IsInSelection(), + "Why is this range registeed with a node?"); + if (!range->Collapsed()) { ancestorSelectionRanges.PutEntry(range); Selection* selection = range->mSelection; ancestorSelections.PutEntry(selection); maxRangeCount = std::max(maxRangeCount, selection->RangeCount()); } } } @@ -332,16 +332,24 @@ NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION( NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMRange) NS_INTERFACE_MAP_END NS_IMPL_CYCLE_COLLECTION_CLASS(nsRange) NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsRange) NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER NS_IMPL_CYCLE_COLLECTION_UNLINK(mOwner); + + // We _could_ just rely on Reset() to UnregisterCommonAncestor(), + // but it wouldn't know we're calling it from Unlink and so would do + // more work than it really needs to. + if (tmp->mRegisteredCommonAncestor) { + tmp->UnregisterCommonAncestor(tmp->mRegisteredCommonAncestor, true); + } + tmp->Reset(); // This needs to be unlinked after Reset() is called, as it controls // the result of IsInSelection() which is used by tmp->Reset(). NS_IMPL_CYCLE_COLLECTION_UNLINK(mSelection); NS_IMPL_CYCLE_COLLECTION_UNLINK_END NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsRange) @@ -405,56 +413,55 @@ nsRange::RegisterCommonAncestor(nsINode* { NS_PRECONDITION(aNode, "bad arg"); NS_ASSERTION(IsInSelection(), "registering range not in selection"); mRegisteredCommonAncestor = aNode; MarkDescendants(aNode); - UniquePtr<nsTHashtable<nsPtrHashKey<nsRange>>>& ranges = - aNode->GetCommonAncestorRangesPtr(); + UniquePtr<LinkedList<nsRange>>& ranges = aNode->GetCommonAncestorRangesPtr(); if (!ranges) { - ranges = MakeUnique<nsRange::RangeHashTable>(); + ranges = MakeUnique<LinkedList<nsRange>>(); } - ranges->PutEntry(this); + ranges->insertBack(this); aNode->SetCommonAncestorForRangeInSelection(); } void -nsRange::UnregisterCommonAncestor(nsINode* aNode) +nsRange::UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking) { NS_PRECONDITION(aNode, "bad arg"); NS_ASSERTION(aNode->IsCommonAncestorForRangeInSelection(), "wrong node"); MOZ_DIAGNOSTIC_ASSERT(aNode == mRegisteredCommonAncestor, "wrong node"); - nsTHashtable<nsPtrHashKey<nsRange>>* ranges = - aNode->GetExistingCommonAncestorRanges(); + LinkedList<nsRange>* ranges = aNode->GetExistingCommonAncestorRanges(); MOZ_ASSERT(ranges); - NS_ASSERTION(ranges->GetEntry(this), "unknown range"); mRegisteredCommonAncestor = nullptr; - bool isNativeAnon = aNode->IsInNativeAnonymousSubtree(); - bool removed = false; - - if (ranges->Count() == 1) { +#ifdef DEBUG + bool found = false; + for (nsRange* range : *ranges) { + if (range == this) { + found = true; + break; + } + } + MOZ_ASSERT(found, + "We should be in the list on our registered common ancestor"); +#endif // DEBUG + + remove(); + + // We don't want to waste time unmarking flags on nodes that are + // being unlinked anyway. + if (!aIsUnlinking && ranges->isEmpty()) { aNode->ClearCommonAncestorForRangeInSelection(); - if (!isNativeAnon) { - // For nodes which are in native anonymous subtrees, we optimize away the - // cost of deallocating the hashtable here because we may need to create - // it again shortly afterward. We don't do this for all nodes in order - // to avoid the additional memory usage unconditionally. - aNode->GetCommonAncestorRangesPtr().reset(); - removed = true; - } UnmarkDescendants(aNode); } - if (!removed) { - ranges->RemoveEntry(this); - } } /****************************************************** * nsIMutationObserver implementation ******************************************************/ void nsRange::CharacterDataChanged(nsIDocument* aDocument, nsIContent* aContent, @@ -507,17 +514,17 @@ nsRange::CharacterDataChanged(nsIDocumen newStart.Set(aInfo->mDetails->mNextSibling, newStartOffset); if (MOZ_UNLIKELY(aContent == mRoot)) { newRoot = IsValidBoundary(newStart.Container()); } bool isCommonAncestor = IsInSelection() && mStart.Container() == mEnd.Container(); if (isCommonAncestor) { - UnregisterCommonAncestor(mStart.Container()); + UnregisterCommonAncestor(mStart.Container(), false); RegisterCommonAncestor(newStart.Container()); } if (mStart.Container()->IsDescendantOfCommonAncestorForRangeInSelection()) { newStart.Container()->SetDescendantOfCommonAncestorForRangeInSelection(); } } else { // If boundary is inside changed text, position it before change // else adjust start offset for the change in length. @@ -541,17 +548,17 @@ nsRange::CharacterDataChanged(nsIDocumen NS_ASSERTION(mEnd.Offset() <= aInfo->mChangeEnd + 1, "mEnd.Offset() is beyond the end of this node"); newEnd.Set(aInfo->mDetails->mNextSibling, mEnd.Offset() - aInfo->mChangeStart); bool isCommonAncestor = IsInSelection() && mStart.Container() == mEnd.Container(); if (isCommonAncestor && !newStart.Container()) { // The split occurs inside the range. - UnregisterCommonAncestor(mStart.Container()); + UnregisterCommonAncestor(mStart.Container(), false); RegisterCommonAncestor(mStart.Container()->GetParentNode()); newEnd.Container()->SetDescendantOfCommonAncestorForRangeInSelection(); } else if (mEnd.Container()-> IsDescendantOfCommonAncestorForRangeInSelection()) { newEnd.Container()->SetDescendantOfCommonAncestorForRangeInSelection(); } } else { int32_t newEndOffset = mEnd.Offset() <= aInfo->mChangeEnd ? @@ -969,25 +976,29 @@ nsRange::DoSetRange(const RawRangeBounda } if (aRoot) { aRoot->AddMutationObserver(this); } } bool checkCommonAncestor = (mStart.Container() != aStart.Container() || mEnd.Container() != aEnd.Container()) && IsInSelection() && !aNotInsertedYet; - nsINode* oldCommonAncestor = checkCommonAncestor ? GetCommonAncestor() : nullptr; + + // GetCommonAncestor is unreliable while we're unlinking (could + // return null if our start/end have already been unlinked), so make + // sure to not use it here to determine our "old" current ancestor. mStart = aStart; mEnd = aEnd; mIsPositioned = !!mStart.Container(); if (checkCommonAncestor) { + nsINode* oldCommonAncestor = mRegisteredCommonAncestor; nsINode* newCommonAncestor = GetCommonAncestor(); if (newCommonAncestor != oldCommonAncestor) { if (oldCommonAncestor) { - UnregisterCommonAncestor(oldCommonAncestor); + UnregisterCommonAncestor(oldCommonAncestor, false); } if (newCommonAncestor) { RegisterCommonAncestor(newCommonAncestor); } else { NS_ASSERTION(!mIsPositioned, "unexpected disconnected nodes"); mSelection = nullptr; } } @@ -1029,22 +1040,22 @@ nsRange::SetSelection(mozilla::dom::Sele } // At least one of aSelection and mSelection must be null // aSelection will be null when we are removing from a selection // and a range can't be in more than one selection at a time, // thus mSelection must be null too. MOZ_ASSERT(!aSelection || !mSelection); mSelection = aSelection; - nsINode* commonAncestor = GetCommonAncestor(); - NS_ASSERTION(commonAncestor, "unexpected disconnected nodes"); if (mSelection) { + nsINode* commonAncestor = GetCommonAncestor(); + NS_ASSERTION(commonAncestor, "unexpected disconnected nodes"); RegisterCommonAncestor(commonAncestor); } else { - UnregisterCommonAncestor(commonAncestor); + UnregisterCommonAncestor(mRegisteredCommonAncestor, false); } } nsINode* nsRange::GetCommonAncestor() const { return mIsPositioned ? nsContentUtils::GetCommonAncestor(mStart.Container(), mEnd.Container()) :
--- a/dom/base/nsRange.h +++ b/dom/base/nsRange.h @@ -17,31 +17,34 @@ #include "nsIDocument.h" #include "nsIDOMNode.h" #include "nsLayoutUtils.h" #include "prmon.h" #include "nsStubMutationObserver.h" #include "nsWrapperCache.h" #include "mozilla/Attributes.h" #include "mozilla/GuardObjects.h" +#include "mozilla/LinkedList.h" namespace mozilla { class ErrorResult; namespace dom { struct ClientRectsAndTexts; class DocumentFragment; class DOMRect; class DOMRectList; class Selection; } // namespace dom } // namespace mozilla class nsRange final : public nsIDOMRange, public nsStubMutationObserver, - public nsWrapperCache + public nsWrapperCache, + // For linking together selection-associated ranges. + public mozilla::LinkedListElement<nsRange> { typedef mozilla::ErrorResult ErrorResult; typedef mozilla::dom::DOMRect DOMRect; typedef mozilla::dom::DOMRectList DOMRectList; virtual ~nsRange(); public: @@ -585,17 +588,17 @@ protected: mutable mozilla::Maybe<uint32_t> mOffset; }; typedef RangeBoundaryBase<nsCOMPtr<nsINode>, nsCOMPtr<nsIContent>> RangeBoundary; typedef RangeBoundaryBase<nsINode*, nsIContent*> RawRangeBoundary; void RegisterCommonAncestor(nsINode* aNode); - void UnregisterCommonAncestor(nsINode* aNode); + void UnregisterCommonAncestor(nsINode* aNode, bool aIsUnlinking); nsINode* IsValidBoundary(nsINode* aNode) const { return ComputeRootNode(aNode, mMaySpanAnonymousSubtrees); } /** * XXX nsRange should accept 0 - UINT32_MAX as offset. However, users of * nsRange treat offset as int32_t. Additionally, some other internal
--- a/dom/base/nsTextNode.cpp +++ b/dom/base/nsTextNode.cpp @@ -160,19 +160,25 @@ void nsTextNode::List(FILE* out, int32_t aIndent) const { int32_t index; for (index = aIndent; --index >= 0; ) fputs(" ", out); fprintf(out, "Text@%p", static_cast<const void*>(this)); fprintf(out, " flags=[%08x]", static_cast<unsigned int>(GetFlags())); if (IsCommonAncestorForRangeInSelection()) { - const nsTHashtable<nsPtrHashKey<nsRange>>* ranges = - GetExistingCommonAncestorRanges(); - fprintf(out, " ranges:%d", ranges ? ranges->Count() : 0); + const LinkedList<nsRange>* ranges = GetExistingCommonAncestorRanges(); + int32_t count = 0; + if (ranges) { + // Can't use range-based iteration on a const LinkedList, unfortunately. + for (const nsRange* r = ranges->getFirst(); r; r = r->getNext()) { + ++count; + } + } + fprintf(out, " ranges:%d", count); } fprintf(out, " primaryframe=%p", static_cast<void*>(GetPrimaryFrame())); fprintf(out, " refcount=%" PRIuPTR "<", mRefCnt.get()); nsAutoString tmp; ToCString(tmp, 0, mText.GetLength()); fputs(NS_LossyConvertUTF16toASCII(tmp).get(), out);
--- a/dom/ipc/ContentParent.h +++ b/dom/ipc/ContentParent.h @@ -31,18 +31,16 @@ #include "nsIDOMGeoPositionCallback.h" #include "nsIDOMGeoPositionErrorCallback.h" #include "nsRefPtrHashtable.h" #include "PermissionMessageUtils.h" #include "DriverCrashGuard.h" #define CHILD_PROCESS_SHUTDOWN_MESSAGE NS_LITERAL_STRING("child-process-shutdown") -#define NO_REMOTE_TYPE "" - // These must match the similar ones in E10SUtils.jsm. // Process names as reported by about:memory are defined in // ContentChild:RecvRemoteType. Add your value there too or it will be called // "Web Content". #define DEFAULT_REMOTE_TYPE "web" #define FILE_REMOTE_TYPE "file" #define EXTENSION_REMOTE_TYPE "extension" @@ -164,17 +162,17 @@ public: /** * Get or create a content process for: * 1. browser iframe * 2. remote xul <browser> * 3. normal iframe */ static already_AddRefed<ContentParent> - GetNewOrUsedBrowserProcess(const nsAString& aRemoteType = NS_LITERAL_STRING(NO_REMOTE_TYPE), + GetNewOrUsedBrowserProcess(const nsAString& aRemoteType, hal::ProcessPriority aPriority = hal::ProcessPriority::PROCESS_PRIORITY_FOREGROUND, ContentParent* aOpener = nullptr, bool aPreferUsed = false); /** * Get or create a content process for a JS plugin. aPluginID is the id of the JS plugin * (@see nsFakePlugin::mId). There is a maximum of one process per JS plugin.
--- a/dom/plugins/ipc/PluginModuleChild.cpp +++ b/dom/plugins/ipc/PluginModuleChild.cpp @@ -107,16 +107,19 @@ static WindowsDllInterceptor sComDlg32In typedef BOOL (WINAPI *GetOpenFileNameWPtr)(LPOPENFILENAMEW lpofn); static GetOpenFileNameWPtr sGetOpenFileNameWPtrStub = nullptr; typedef BOOL (WINAPI *GetSaveFileNameWPtr)(LPOPENFILENAMEW lpofn); static GetSaveFileNameWPtr sGetSaveFileNameWPtrStub = nullptr; typedef BOOL (WINAPI *SetCursorPosPtr)(int x, int y); static SetCursorPosPtr sSetCursorPosPtrStub = nullptr; +typedef BOOL (WINAPI *PrintDlgWPtr)(LPPRINTDLGW aDlg); +static PrintDlgWPtr sPrintDlgWPtrStub = nullptr; + #endif /* static */ bool PluginModuleChild::CreateForContentProcess(Endpoint<PPluginModuleChild>&& aEndpoint) { auto* child = new PluginModuleChild(false); return child->InitForContent(Move(aEndpoint)); @@ -2182,16 +2185,29 @@ PMCGetSaveFileNameW(LPOPENFILENAMEW aLpo } // static BOOL WINAPI PMCGetOpenFileNameW(LPOPENFILENAMEW aLpofn) { return PMCGetFileNameW(OPEN_FUNC, aLpofn); } +//static +BOOL WINAPI +PMCPrintDlgW(LPPRINTDLGW aDlg) +{ + // Zero out the HWND supplied by the plugin. We are sacrificing window + // parentage for the ability to run in the NPAPI sandbox. + HWND hwnd = aDlg->hwndOwner; + aDlg->hwndOwner = 0; + BOOL ret = sPrintDlgWPtrStub(aDlg); + aDlg->hwndOwner = hwnd; + return ret; +} + BOOL WINAPI PMCSetCursorPos(int x, int y); class SetCursorPosTaskData : public PluginThreadTaskData { public: SetCursorPosTaskData(int x, int y) : mX(x), mY(y) {} bool RunTask() { return PMCSetCursorPos(mX, mY); } private: @@ -2260,16 +2276,22 @@ PluginModuleChild::AllocPPluginInstanceC sComDlg32Intercept.AddHook("GetSaveFileNameW", reinterpret_cast<intptr_t>(PMCGetSaveFileNameW), (void**) &sGetSaveFileNameWPtrStub); } if (!sGetOpenFileNameWPtrStub) { sComDlg32Intercept.AddHook("GetOpenFileNameW", reinterpret_cast<intptr_t>(PMCGetOpenFileNameW), (void**) &sGetOpenFileNameWPtrStub); } + + if ((mQuirks & QUIRK_FLASH_HOOK_PRINTDLGW) && + !sPrintDlgWPtrStub) { + sComDlg32Intercept.AddHook("PrintDlgW", reinterpret_cast<intptr_t>(PMCPrintDlgW), + (void**) &sPrintDlgWPtrStub); + } #endif return new PluginInstanceChild(&mFunctions, aMimeType, aNames, aValues); } void PluginModuleChild::InitQuirksModes(const nsCString& aMimeType)
--- a/dom/plugins/ipc/PluginQuirks.cpp +++ b/dom/plugins/ipc/PluginQuirks.cpp @@ -24,16 +24,17 @@ int GetQuirksFromMimeTypeAndFilename(con quirks |= QUIRK_WINLESS_TRACKPOPUP_HOOK; quirks |= QUIRK_FLASH_THROTTLE_WMUSER_EVENTS; quirks |= QUIRK_FLASH_HOOK_SETLONGPTR; quirks |= QUIRK_FLASH_HOOK_GETWINDOWINFO; quirks |= QUIRK_FLASH_FIXUP_MOUSE_CAPTURE; quirks |= QUIRK_WINLESS_HOOK_IME; #if defined(_M_X64) || defined(__x86_64__) quirks |= QUIRK_FLASH_HOOK_GETKEYSTATE; + quirks |= QUIRK_FLASH_HOOK_PRINTDLGW; #endif #endif } #ifdef XP_MACOSX // Whitelist Flash to support offline renderer. if (specialType == nsPluginHost::eSpecialType_Flash) { quirks |= QUIRK_ALLOW_OFFLINE_RENDERER;
--- a/dom/plugins/ipc/PluginQuirks.h +++ b/dom/plugins/ipc/PluginQuirks.h @@ -40,16 +40,18 @@ enum PluginQuirks { // Work around a Flash bug where it fails to check the error code of a // NPN_GetValue(NPNVdocumentOrigin) call before trying to dereference // its char* output. QUIRK_FLASH_RETURN_EMPTY_DOCUMENT_ORIGIN = 1 << 10, // Win: Hook IMM32 API to handle IME event on windowless plugin QUIRK_WINLESS_HOOK_IME = 1 << 12, // Win: Hook GetKeyState to get keyboard state on sandbox process QUIRK_FLASH_HOOK_GETKEYSTATE = 1 << 13, + // Win: Hook PrintDlgW to show print settings dialog on sandbox process + QUIRK_FLASH_HOOK_PRINTDLGW = 1 << 14, }; int GetQuirksFromMimeTypeAndFilename(const nsCString& aMimeType, const nsCString& aPluginFilename); } /* namespace plugins */ } /* namespace mozilla */
--- a/dom/push/PushNotifier.cpp +++ b/dom/push/PushNotifier.cpp @@ -103,16 +103,23 @@ PushNotifier::Dispatch(PushDispatcher& a Unused << NS_WARN_IF(NS_FAILED(aDispatcher.NotifyObservers())); nsTArray<ContentParent*> contentActors; ContentParent::GetAll(contentActors); if (!contentActors.IsEmpty()) { // At least one content process is active, so e10s must be enabled. // Broadcast a message to notify observers and service workers. for (uint32_t i = 0; i < contentActors.Length(); ++i) { + // We need to filter based on process type, only "web" AKA the default + // remote type is acceptable. + if (!contentActors[i]->GetRemoteType().EqualsLiteral( + DEFAULT_REMOTE_TYPE)) { + continue; + } + // Ensure that the content actor has the permissions avaliable for the // principal the push is being sent for before sending the push message // down. Unused << contentActors[i]-> TransmitPermissionsForPrincipal(aDispatcher.GetPrincipal()); if (aDispatcher.SendToChild(contentActors[i])) { // Only send the push message to the first content process to avoid // multiple SWs showing the same notification. See bug 1300112.
--- a/dom/storage/StorageDBThread.cpp +++ b/dom/storage/StorageDBThread.cpp @@ -1086,18 +1086,20 @@ StorageDBThread::DBOperation::Perform(St nsAutoString value; rv = stmt->GetString(1, value); NS_ENSURE_SUCCESS(rv, rv); if (!mCache->LoadItem(key, value)) { break; } } - - mCache->LoadDone(NS_OK); + // The loop condition's call to ExecuteStep() may have terminated because + // !NS_SUCCEEDED(), we need an early return to cover that case. This also + // covers success cases as well, but that's inductively safe. + NS_ENSURE_SUCCESS(rv, rv); break; } case opGetUsage: { nsCOMPtr<mozIStorageStatement> stmt = aThread->mWorkerStatements.GetCachedStatement( "SELECT SUM(LENGTH(key) + LENGTH(value)) FROM webappsstore2 " "WHERE (originAttributes || ':' || originKey) LIKE :usageOrigin"
--- a/dom/storage/StorageIPC.cpp +++ b/dom/storage/StorageIPC.cpp @@ -664,32 +664,37 @@ public: } virtual const nsCString& OriginNoSuffix() const { return mOrigin; } virtual const nsCString& OriginSuffix() const { return mSuffix; } virtual bool Loaded() { return mLoaded; } virtual uint32_t LoadedCount() { return mLoadedCount; } virtual bool LoadItem(const nsAString& aKey, const nsString& aValue) { // Called on the aCache background thread + MOZ_ASSERT(!mLoaded); if (mLoaded) { return false; } ++mLoadedCount; mKeys->AppendElement(aKey); mValues->AppendElement(aValue); return true; } virtual void LoadDone(nsresult aRv) { // Called on the aCache background thread MonitorAutoLock monitor(mMonitor); + MOZ_ASSERT(!mLoaded && mRv); mLoaded = true; - *mRv = aRv; + if (mRv) { + *mRv = aRv; + mRv = nullptr; + } monitor.Notify(); } virtual void LoadWait() { // Called on the main thread, exits after LoadDone() call MonitorAutoLock monitor(mMonitor); while (!mLoaded) {
--- a/gfx/gl/GLContext.cpp +++ b/gfx/gl/GLContext.cpp @@ -548,21 +548,18 @@ GLContext::InitWithPrefixImpl(const char uint32_t majorVer, minorVer; if (!ParseVersion(versionStr, &majorVer, &minorVer)) { MOZ_ASSERT(false, "Failed to parse GL_VERSION"); return false; } MOZ_ASSERT(majorVer < 10); MOZ_ASSERT(minorVer < 10); mVersion = majorVer*100 + minorVer*10; - if (mVersion < 200) { - // Mac OSX 10.6/10.7 machines with Intel GPUs claim only OpenGL 1.4 but - // have all the GL2+ extensions that we need. - mVersion = 200; - } + if (mVersion < 200) + return false; //// const auto glslVersionStr = (const char*)fGetString(LOCAL_GL_SHADING_LANGUAGE_VERSION); if (!glslVersionStr) { // This happens on the Android emulators. We'll just return 100 mShadingLanguageVersion = 100; } else if (ParseVersion(glslVersionStr, &majorVer, &minorVer)) {
--- a/intl/strres/nsStringBundle.cpp +++ b/intl/strres/nsStringBundle.cpp @@ -20,16 +20,19 @@ #include "nsIInputStream.h" #include "nsIURI.h" #include "nsIObserverService.h" #include "nsCOMArray.h" #include "nsTextFormatter.h" #include "nsIErrorService.h" #include "nsICategoryManager.h" #include "nsContentUtils.h" +#include "nsStringStream.h" +#include "mozilla/ResultExtensions.h" +#include "mozilla/URLPreloader.h" // for async loading #ifdef ASYNC_LOADING #include "nsIBinaryInputStream.h" #include "nsIStringStream.h" #endif using namespace mozilla; @@ -88,31 +91,37 @@ nsStringBundle::LoadProperties() nsCString scheme; uri->GetScheme(scheme); if (!scheme.EqualsLiteral("chrome") && !scheme.EqualsLiteral("jar") && !scheme.EqualsLiteral("resource") && !scheme.EqualsLiteral("file") && !scheme.EqualsLiteral("data")) { return NS_ERROR_ABORT; } - nsCOMPtr<nsIChannel> channel; - rv = NS_NewChannel(getter_AddRefs(channel), - uri, - nsContentUtils::GetSystemPrincipal(), - nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, - nsIContentPolicy::TYPE_OTHER); + nsCOMPtr<nsIInputStream> in; - if (NS_FAILED(rv)) return rv; + auto result = URLPreloader::ReadURI(uri); + if (result.isOk()) { + MOZ_TRY(NS_NewCStringInputStream(getter_AddRefs(in), result.unwrap())); + } else { + nsCOMPtr<nsIChannel> channel; + rv = NS_NewChannel(getter_AddRefs(channel), + uri, + nsContentUtils::GetSystemPrincipal(), + nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL, + nsIContentPolicy::TYPE_OTHER); - // It's a string bundle. We expect a text/plain type, so set that as hint - channel->SetContentType(NS_LITERAL_CSTRING("text/plain")); + if (NS_FAILED(rv)) return rv; - nsCOMPtr<nsIInputStream> in; - rv = channel->Open2(getter_AddRefs(in)); - if (NS_FAILED(rv)) return rv; + // It's a string bundle. We expect a text/plain type, so set that as hint + channel->SetContentType(NS_LITERAL_CSTRING("text/plain")); + + rv = channel->Open2(getter_AddRefs(in)); + if (NS_FAILED(rv)) return rv; + } NS_ASSERTION(NS_SUCCEEDED(rv) && in, "Error in OpenBlockingStream"); NS_ENSURE_TRUE(NS_SUCCEEDED(rv) && in, NS_ERROR_FAILURE); static NS_DEFINE_CID(kPersistentPropertiesCID, NS_IPERSISTENTPROPERTIES_CID); mProps = do_CreateInstance(kPersistentPropertiesCID, &rv); NS_ENSURE_SUCCESS(rv, rv);
--- a/ipc/mscom/Interceptor.cpp +++ b/ipc/mscom/Interceptor.cpp @@ -300,16 +300,17 @@ Interceptor::DisconnectObject(DWORD dwRe { return mStdMarshal->DisconnectObject(dwReserved); } Interceptor::MapEntry* Interceptor::Lookup(REFIID aIid) { mMutex.AssertCurrentThreadOwns(); + for (uint32_t index = 0, len = mInterceptorMap.Length(); index < len; ++index) { if (mInterceptorMap[index].mIID == aIid) { return &mInterceptorMap[index]; } } return nullptr; } @@ -362,25 +363,67 @@ Interceptor::CreateInterceptor(REFIID aI // If this assert fires then the interceptor doesn't like something about // the format of the typelib. One thing in particular that it doesn't like // is complex types that contain unions. MOZ_ASSERT(SUCCEEDED(hr)); return hr; } HRESULT -Interceptor::GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLock, +Interceptor::PublishTarget(detail::LiveSetAutoLock& aLiveSetLock, + RefPtr<IUnknown> aInterceptor, + REFIID aTargetIid, + STAUniquePtr<IUnknown> aTarget) +{ + RefPtr<IWeakReference> weakRef; + HRESULT hr = GetWeakReference(getter_AddRefs(weakRef)); + if (FAILED(hr)) { + return hr; + } + + // mTarget is a weak reference to aTarget. This is safe because we transfer + // ownership of aTarget into mInterceptorMap which remains live for the + // lifetime of this Interceptor. + mTarget = ToInterceptorTargetPtr(aTarget); + GetLiveSet().Put(mTarget.get(), weakRef.forget()); + + // Now we transfer aTarget's ownership into mInterceptorMap. + mInterceptorMap.AppendElement(MapEntry(aTargetIid, + aInterceptor, + aTarget.release())); + + // Release the live set lock because subsequent operations may post work to + // the main thread, creating potential for deadlocks. + aLiveSetLock.Unlock(); + return S_OK; +} + +HRESULT +Interceptor::GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLiveSetLock, REFIID aTargetIid, STAUniquePtr<IUnknown> aTarget, void** aOutInterceptor) { MOZ_ASSERT(aOutInterceptor); - MOZ_ASSERT(aTargetIid != IID_IUnknown && aTargetIid != IID_IMarshal); + MOZ_ASSERT(aTargetIid != IID_IMarshal); MOZ_ASSERT(!IsProxy(aTarget.get())); + if (aTargetIid == IID_IUnknown) { + // We must lock ourselves so that nothing can race with us once we have been + // published to the live set. + AutoLock lock(*this); + + HRESULT hr = PublishTarget(aLiveSetLock, nullptr, aTargetIid, Move(aTarget)); + if (FAILED(hr)) { + return hr; + } + + return QueryInterface(aTargetIid, aOutInterceptor); + } + // Raise the refcount for stabilization purposes during aggregation RefPtr<IUnknown> kungFuDeathGrip(static_cast<IUnknown*>( static_cast<WeakReferenceSupport*>(this))); RefPtr<IUnknown> unkInterceptor; HRESULT hr = CreateInterceptor(aTargetIid, kungFuDeathGrip, getter_AddRefs(unkInterceptor)); if (FAILED(hr)) { @@ -394,37 +437,25 @@ Interceptor::GetInitialInterceptorForIID return hr; } hr = interceptor->RegisterSink(mEventSink); if (FAILED(hr)) { return hr; } - RefPtr<IWeakReference> weakRef; - hr = GetWeakReference(getter_AddRefs(weakRef)); + // We must lock ourselves so that nothing can race with us once we have been + // published to the live set. + AutoLock lock(*this); + + hr = PublishTarget(aLiveSetLock, unkInterceptor, aTargetIid, Move(aTarget)); if (FAILED(hr)) { return hr; } - // mTarget is a weak reference to aTarget. This is safe because we transfer - // ownership of aTarget into mInterceptorMap which remains live for the - // lifetime of this Interceptor. - mTarget = ToInterceptorTargetPtr(aTarget); - GetLiveSet().Put(mTarget.get(), weakRef.forget()); - - // Release the live set lock because GetInterceptorForIID will post work to - // the main thread, creating potential for deadlocks. - aLock.Unlock(); - - // Now we transfer aTarget's ownership into mInterceptorMap. - mInterceptorMap.AppendElement(MapEntry(aTargetIid, - unkInterceptor, - aTarget.release())); - if (mEventSink->MarshalAs(aTargetIid) == aTargetIid) { return unkInterceptor->QueryInterface(aTargetIid, aOutInterceptor); } return GetInterceptorForIID(aTargetIid, aOutInterceptor); } /**
--- a/ipc/mscom/Interceptor.h +++ b/ipc/mscom/Interceptor.h @@ -115,25 +115,29 @@ private: IID mIID; RefPtr<IUnknown> mInterceptor; IUnknown* mTargetInterface; }; private: explicit Interceptor(IInterceptorSink* aSink); ~Interceptor(); - HRESULT GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLock, + HRESULT GetInitialInterceptorForIID(detail::LiveSetAutoLock& aLiveSetLock, REFIID aTargetIid, STAUniquePtr<IUnknown> aTarget, void** aOutInterface); MapEntry* Lookup(REFIID aIid); HRESULT QueryInterfaceTarget(REFIID aIid, void** aOutput); HRESULT ThreadSafeQueryInterface(REFIID aIid, IUnknown** aOutInterface) override; HRESULT CreateInterceptor(REFIID aIid, IUnknown* aOuter, IUnknown** aOutput); + HRESULT PublishTarget(detail::LiveSetAutoLock& aLiveSetLock, + RefPtr<IUnknown> aInterceptor, + REFIID aTargetIid, + STAUniquePtr<IUnknown> aTarget); private: InterceptorTargetPtr<IUnknown> mTarget; RefPtr<IInterceptorSink> mEventSink; mozilla::Mutex mMutex; // Guards mInterceptorMap // Using a nsTArray since the # of interfaces is not going to be very high nsTArray<MapEntry> mInterceptorMap; RefPtr<IUnknown> mStdMarshalUnk;
--- a/ipc/mscom/WeakRef.cpp +++ b/ipc/mscom/WeakRef.cpp @@ -104,16 +104,28 @@ WeakReferenceSupport::WeakReferenceSuppo ::InitializeCS(mCSForQI); } WeakReferenceSupport::~WeakReferenceSupport() { ::DeleteCriticalSection(&mCSForQI); } +void +WeakReferenceSupport::Lock() +{ + ::EnterCriticalSection(&mCSForQI); +} + +void +WeakReferenceSupport::Unlock() +{ + ::LeaveCriticalSection(&mCSForQI); +} + HRESULT WeakReferenceSupport::QueryInterface(REFIID riid, void** ppv) { RefPtr<IUnknown> punk; if (!ppv) { return E_INVALIDARG; } *ppv = nullptr;
--- a/ipc/mscom/WeakRef.h +++ b/ipc/mscom/WeakRef.h @@ -7,16 +7,17 @@ #ifndef mozilla_mscom_WeakRef_h #define mozilla_mscom_WeakRef_h #include <guiddef.h> #include <unknwn.h> #include "mozilla/Assertions.h" #include "mozilla/Atomics.h" +#include "mozilla/Mutex.h" #include "mozilla/RefPtr.h" #include "nsISupportsImpl.h" /** * Thread-safe weak references for COM that works pre-Windows 8 and do not * require WinRT. */ @@ -96,16 +97,22 @@ public: protected: explicit WeakReferenceSupport(Flags aFlags); virtual ~WeakReferenceSupport(); virtual HRESULT ThreadSafeQueryInterface(REFIID aIid, IUnknown** aOutInterface) = 0; + void Lock(); + void Unlock(); + + typedef BaseAutoLock<WeakReferenceSupport> AutoLock; + friend class BaseAutoLock<WeakReferenceSupport>; + private: RefPtr<detail::SharedRef> mSharedRef; ULONG mRefCnt; Flags mFlags; CRITICAL_SECTION mCSForQI; }; class WeakRef final : public IWeakReference
--- a/js/src/build/Makefile.in +++ b/js/src/build/Makefile.in @@ -76,19 +76,26 @@ install:: # END SpiderMonkey header installation ############################################# # Install versioned script, for parallel installability in Linux distributions install:: js-config cp $^ js$(MOZJS_MAJOR_VERSION)-config $(SYSINSTALL) js$(MOZJS_MAJOR_VERSION)-config $(DESTDIR)$(bindir) +# Use install_name_tool to set the install_name properly for standalone +# installed libraries on macOS install:: $(REAL_LIBRARY) $(SHARED_LIBRARY) $(IMPORT_LIBRARY) ifneq (,$(REAL_LIBRARY)) $(SYSINSTALL) $(REAL_LIBRARY) $(DESTDIR)$(libdir) mv -f $(DESTDIR)$(libdir)/$(REAL_LIBRARY) $(subst $(STATIC_LIBRARY_NAME),$(LIBRARY_NAME),$(DESTDIR)$(libdir)/$(REAL_LIBRARY)) endif ifneq (,$(SHARED_LIBRARY)) $(SYSINSTALL) $(SHARED_LIBRARY) $(DESTDIR)$(libdir) +ifeq ($(OS_ARCH),Darwin) + install_name_tool -id $(abspath $(libdir)/$(SHARED_LIBRARY)) $(DESTDIR)$(libdir)/$(SHARED_LIBRARY) +endif endif ifneq (,$(IMPORT_LIBRARY)) +ifneq ($(IMPORT_LIBRARY),$(SHARED_LIBRARY)) $(SYSINSTALL) $(IMPORT_LIBRARY) $(DESTDIR)$(libdir) endif +endif
--- a/js/xpconnect/idl/xpccomponents.idl +++ b/js/xpconnect/idl/xpccomponents.idl @@ -9,16 +9,17 @@ #include "jspubtd.h" %} interface xpcIJSWeakReference; interface nsIAddonInterposition; interface nsIClassInfo; interface nsIComponentManager; interface nsICycleCollectorListener; +interface nsIFile; interface nsIJSCID; interface nsIJSIID; interface nsIPrincipal; interface nsIStackFrame; /** * interface of Components.interfacesByID * (interesting stuff only reflected into JavaScript) @@ -688,16 +689,23 @@ interface nsIXPCComponents_Utils : nsISu [implicit_jscontext] void allowCPOWsInAddon(in ACString addonId, in bool allow); /* * Return a fractional number of milliseconds from process * startup, measured with a monotonic clock. */ double now(); + + /* + * Reads the given file and returns its contents. If called during early + * startup, the file will be pre-read on a background thread during profile + * startup so its contents will be available the next time they're read. + */ + ACString readFile(in nsIFile file); }; /** * Interface for the 'Components' object. * * The first interface contains things that are available to non-chrome XBL code * that runs in a scope with an ExpandedPrincipal. The second interface * includes members that are only exposed to chrome.
--- a/js/xpconnect/loader/ScriptPreloader-inl.h +++ b/js/xpconnect/loader/ScriptPreloader-inl.h @@ -20,16 +20,25 @@ #include <prio.h> namespace mozilla { namespace loader { using mozilla::dom::AutoJSAPI; +static inline Result<Ok, nsresult> +Write(PRFileDesc* fd, const void* data, int32_t len) +{ + if (PR_Write(fd, data, len) != len) { + return Err(NS_ERROR_FAILURE); + } + return Ok(); +} + struct MOZ_RAII AutoSafeJSAPI : public AutoJSAPI { AutoSafeJSAPI() { Init(); } }; class OutputBuffer { @@ -254,18 +263,25 @@ public: ElemType get() { if (done_) { return nullptr; } return iter().Data(); } + const ElemType get() const + { + return const_cast<Elem*>(this)->get(); + } + ElemType operator->() { return get(); } + const ElemType operator->() const { return get(); } + operator ElemType() { return get(); } void Remove() { iter().Remove(); } Elem& operator++() { MOZ_ASSERT(!done_);
--- a/js/xpconnect/loader/ScriptPreloader.cpp +++ b/js/xpconnect/loader/ScriptPreloader.cpp @@ -3,16 +3,18 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ScriptPreloader-inl.h" #include "mozilla/ScriptPreloader.h" #include "mozilla/loader/ScriptCacheActors.h" +#include "mozilla/URLPreloader.h" + #include "mozilla/ArrayUtils.h" #include "mozilla/ClearOnShutdown.h" #include "mozilla/FileUtils.h" #include "mozilla/Logging.h" #include "mozilla/ScopeExit.h" #include "mozilla/Services.h" #include "mozilla/Unused.h" #include "mozilla/dom/ContentChild.h" @@ -408,16 +410,20 @@ ScriptPreloader::InitCache(const nsAStri mBaseName = basePath; RegisterWeakMemoryReporter(this); if (!XRE_IsParentProcess()) { return Ok(); } + // Note: Code on the main thread *must not access Omnijar in any way* until + // this AutoBeginReading guard is destroyed. + URLPreloader::AutoBeginReading abr; + MOZ_TRY(OpenCache()); return InitCacheInternal(); } Result<Ok, nsresult> ScriptPreloader::InitCache(const Maybe<ipc::FileDescriptor>& cacheFile, ScriptCacheChild* cacheChild) { @@ -512,25 +518,16 @@ ScriptPreloader::InitCacheInternal() mPendingScripts = Move(scripts); cleanup.release(); } DecodeNextBatch(OFF_THREAD_FIRST_CHUNK_SIZE); return Ok(); } -static inline Result<Ok, nsresult> -Write(PRFileDesc* fd, const void* data, int32_t len) -{ - if (PR_Write(fd, data, len) != len) { - return Err(NS_ERROR_FAILURE); - } - return Ok(); -} - void ScriptPreloader::PrepareCacheWriteInternal() { MOZ_ASSERT(NS_IsMainThread()); mMonitor.AssertCurrentThreadOwns(); auto cleanup = MakeScopeExit([&] () { @@ -598,16 +595,18 @@ ScriptPreloader::PrepareCacheWrite() // // - A block of XDR data for the encoded scripts, with each script's data at // an offset from the start of the block, as specified above. Result<Ok, nsresult> ScriptPreloader::WriteCache() { MOZ_ASSERT(!NS_IsMainThread()); + Unused << URLPreloader::GetSingleton().WriteCache(); + if (!mDataPrepared && !mSaveComplete) { MOZ_ASSERT(!mBlockedOnSyncDispatch); mBlockedOnSyncDispatch = true; MonitorAutoUnlock mau(mSaveMonitor); NS_DispatchToMainThread( NewRunnableMethod("ScriptPreloader::PrepareCacheWrite",
new file mode 100644 --- /dev/null +++ b/js/xpconnect/loader/URLPreloader.cpp @@ -0,0 +1,691 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* vim: set ts=8 sts=4 et sw=4 tw=99: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ScriptPreloader-inl.h" +#include "mozilla/URLPreloader.h" +#include "mozilla/loader/AutoMemMap.h" + +#include "mozilla/ArrayUtils.h" +#include "mozilla/ClearOnShutdown.h" +#include "mozilla/FileUtils.h" +#include "mozilla/Logging.h" +#include "mozilla/ScopeExit.h" +#include "mozilla/Services.h" +#include "mozilla/Unused.h" +#include "mozilla/Vector.h" + +#include "MainThreadUtils.h" +#include "nsPrintfCString.h" +#include "nsDebug.h" +#include "nsIFile.h" +#include "nsIFileURL.h" +#include "nsIObserverService.h" +#include "nsNetUtil.h" +#include "nsPromiseFlatString.h" +#include "nsProxyRelease.h" +#include "nsThreadUtils.h" +#include "nsXULAppAPI.h" +#include "nsZipArchive.h" +#include "xpcpublic.h" + +#include "mozilla/dom/ContentChild.h" + +#undef DELAYED_STARTUP_TOPIC +#define DELAYED_STARTUP_TOPIC "sessionstore-windows-restored" + +namespace mozilla { +namespace { +static LazyLogModule gURLLog("URLPreloader"); + +#define LOG(level, ...) MOZ_LOG(gURLLog, LogLevel::level, (__VA_ARGS__)) + +template<typename T> +bool +StartsWith(const T& haystack, const T& needle) +{ + return StringHead(haystack, needle.Length()) == needle; +} +} // anonymous namespace + +using namespace mozilla::loader; + +nsresult +URLPreloader::CollectReports(nsIHandleReportCallback* aHandleReport, + nsISupports* aData, bool aAnonymize) +{ + MOZ_COLLECT_REPORT( + "explicit/url-preloader/other", KIND_HEAP, UNITS_BYTES, + ShallowSizeOfIncludingThis(MallocSizeOf), + "Memory used by the URL preloader service itself."); + + for (const auto& elem : IterHash(mCachedURLs)) { + nsAutoCString pathName; + pathName.Append(elem->mPath); + // The backslashes will automatically be replaced with slashes in + // about:memory, without splitting each path component into a separate + // branch in the memory report tree. + pathName.ReplaceChar('/', '\\'); + + nsPrintfCString path("explicit/url-preloader/cached-urls/%s/[%s]", + elem->TypeString(), pathName.get()); + + aHandleReport->Callback( + EmptyCString(), path, KIND_HEAP, UNITS_BYTES, + elem->SizeOfIncludingThis(MallocSizeOf), + NS_LITERAL_CSTRING("Memory used to hold cache data for files which " + "have been read or pre-loaded during this session."), + aData); + } + + return NS_OK; +} + + +URLPreloader& +URLPreloader::GetSingleton() +{ + static RefPtr<URLPreloader> singleton; + + if (!singleton) { + singleton = new URLPreloader(); + ClearOnShutdown(&singleton); + } + + return *singleton; +} + + +bool URLPreloader::sInitialized = false; + +URLPreloader::URLPreloader() +{ + if (InitInternal().isOk()) { + sInitialized = true; + RegisterWeakMemoryReporter(this); + } +} + +URLPreloader::~URLPreloader() +{ + if (sInitialized) { + UnregisterWeakMemoryReporter(this); + } +} + +Result<Ok, nsresult> +URLPreloader::InitInternal() +{ + MOZ_RELEASE_ASSERT(NS_IsMainThread()); + + if (Omnijar::HasOmnijar(Omnijar::GRE)) { + MOZ_TRY(Omnijar::GetURIString(Omnijar::GRE, mGREPrefix)); + } + if (Omnijar::HasOmnijar(Omnijar::APP)) { + MOZ_TRY(Omnijar::GetURIString(Omnijar::APP, mAppPrefix)); + } + + nsresult rv; + nsCOMPtr<nsIIOService> ios = do_GetIOService(&rv); + MOZ_TRY(rv); + + nsCOMPtr<nsIProtocolHandler> ph; + MOZ_TRY(ios->GetProtocolHandler("resource", getter_AddRefs(ph))); + + mResProto = do_QueryInterface(ph, &rv); + MOZ_TRY(rv); + + mChromeReg = services::GetChromeRegistryService(); + if (!mChromeReg) { + return Err(NS_ERROR_UNEXPECTED); + } + + if (XRE_IsParentProcess()) { + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); + + obs->AddObserver(this, DELAYED_STARTUP_TOPIC, false); + + MOZ_TRY(NS_GetSpecialDirectory("ProfLDS", getter_AddRefs(mProfD))); + } else { + mStartupFinished = true; + mReaderInitialized = true; + } + + return Ok(); +} + +Result<nsCOMPtr<nsIFile>, nsresult> +URLPreloader::GetCacheFile(const nsAString& suffix) +{ + if (!mProfD) { + return Err(NS_ERROR_NOT_INITIALIZED); + } + + nsCOMPtr<nsIFile> cacheFile; + MOZ_TRY(mProfD->Clone(getter_AddRefs(cacheFile))); + + MOZ_TRY(cacheFile->AppendNative(NS_LITERAL_CSTRING("startupCache"))); + Unused << cacheFile->Create(nsIFile::DIRECTORY_TYPE, 0777); + + MOZ_TRY(cacheFile->Append(NS_LITERAL_STRING("urlCache") + suffix)); + + return Move(cacheFile); +} + +static const uint8_t URL_MAGIC[] = "mozURLcachev001"; + +Result<nsCOMPtr<nsIFile>, nsresult> +URLPreloader::FindCacheFile() +{ + nsCOMPtr<nsIFile> cacheFile; + MOZ_TRY_VAR(cacheFile, GetCacheFile(NS_LITERAL_STRING(".bin"))); + + bool exists; + MOZ_TRY(cacheFile->Exists(&exists)); + if (exists) { + MOZ_TRY(cacheFile->MoveTo(nullptr, NS_LITERAL_STRING("urlCache-current.bin"))); + } else { + MOZ_TRY(cacheFile->SetLeafName(NS_LITERAL_STRING("urlCache-current.bin"))); + MOZ_TRY(cacheFile->Exists(&exists)); + if (!exists) { + return Err(NS_ERROR_FILE_NOT_FOUND); + } + } + + return Move(cacheFile); +} + +Result<Ok, nsresult> +URLPreloader::WriteCache() +{ + MOZ_ASSERT(!NS_IsMainThread()); + + nsCOMPtr<nsIFile> cacheFile; + MOZ_TRY_VAR(cacheFile, GetCacheFile(NS_LITERAL_STRING("-new.bin"))); + + bool exists; + MOZ_TRY(cacheFile->Exists(&exists)); + if (exists) { + MOZ_TRY(cacheFile->Remove(false)); + } + + { + AutoFDClose fd; + MOZ_TRY(cacheFile->OpenNSPRFileDesc(PR_WRONLY | PR_CREATE_FILE, 0644, &fd.rwget())); + + nsTArray<URLEntry*> entries; + for (auto& entry : IterHash(mCachedURLs)) { + if (entry->mReadTime) { + entries.AppendElement(entry); + } + } + + entries.Sort(URLEntry::Comparator()); + + OutputBuffer buf; + for (auto entry : entries) { + entry->Code(buf); + } + + uint8_t headerSize[4]; + LittleEndian::writeUint32(headerSize, buf.cursor()); + + MOZ_TRY(Write(fd, URL_MAGIC, sizeof(URL_MAGIC))); + MOZ_TRY(Write(fd, headerSize, sizeof(headerSize))); + MOZ_TRY(Write(fd, buf.Get(), buf.cursor())); + } + + MOZ_TRY(cacheFile->MoveTo(nullptr, NS_LITERAL_STRING("urlCache.bin"))); + + NS_DispatchToMainThread( + NewRunnableMethod("URLPreloader::Cleanup", + this, + &URLPreloader::Cleanup)); + + return Ok(); +} + +void +URLPreloader::Cleanup() +{ + mCachedURLs.Clear(); +} + +Result<Ok, nsresult> +URLPreloader::ReadCache(LinkedList<URLEntry>& pendingURLs) +{ + nsCOMPtr<nsIFile> cacheFile; + MOZ_TRY_VAR(cacheFile, FindCacheFile()); + + AutoMemMap cache; + MOZ_TRY(cache.init(cacheFile)); + + auto size = cache.size(); + + uint32_t headerSize; + if (size < sizeof(URL_MAGIC) + sizeof(headerSize)) { + return Err(NS_ERROR_UNEXPECTED); + } + + auto data = cache.get<uint8_t>(); + auto end = data + size; + + if (memcmp(URL_MAGIC, data.get(), sizeof(URL_MAGIC))) { + return Err(NS_ERROR_UNEXPECTED); + } + data += sizeof(URL_MAGIC); + + headerSize = LittleEndian::readUint32(data.get()); + data += sizeof(headerSize); + + if (data + headerSize > end) { + return Err(NS_ERROR_UNEXPECTED); + } + + { + mMonitor.AssertCurrentThreadOwns(); + + auto cleanup = MakeScopeExit([&] () { + while (auto* elem = pendingURLs.getFirst()) { + elem->remove(); + } + mCachedURLs.Clear(); + }); + + Range<uint8_t> header(data, data + headerSize); + data += headerSize; + + InputBuffer buf(header); + while (!buf.finished()) { + CacheKey key(buf); + + auto entry = mCachedURLs.LookupOrAdd(key, key); + entry->mResultCode = NS_ERROR_NOT_INITIALIZED; + + pendingURLs.insertBack(entry); + } + + if (buf.error()) { + return Err(NS_ERROR_UNEXPECTED); + } + + cleanup.release(); + } + + return Ok(); +} + +void +URLPreloader::BackgroundReadFiles() +{ + Vector<nsZipCursor> cursors; + LinkedList<URLEntry> pendingURLs; + + { + MonitorAutoLock mal(mMonitor); + + if (ReadCache(pendingURLs).isErr()) { + mReaderInitialized = true; + mal.NotifyAll(); + return; + } + + int numZipEntries = 0; + for (auto entry : pendingURLs) { + if (entry->mType != entry->TypeFile) { + numZipEntries++; + } + } + MOZ_RELEASE_ASSERT(cursors.reserve(numZipEntries)); + + // Initialize the zip cursors for all files in Omnijar while the monitor + // is locked. Omnijar is not threadsafe, so the caller of + // AutoBeginReading guard must ensure that no code accesses Omnijar + // until this segment is done. Once the cursors have been initialized, + // the actual reading and decompression can safely be done off-thread, + // as is the case for thread-retargeted jar: channels. + for (auto entry : pendingURLs) { + if (entry->mType == entry->TypeFile) { + continue; + } + + RefPtr<nsZipArchive> zip = entry->Archive(); + + auto item = zip->GetItem(entry->mPath.get()); + if (!item) { + entry->mResultCode = NS_ERROR_FILE_NOT_FOUND; + continue; + } + + size_t size = item->RealSize(); + + entry->mData.SetLength(size); + auto data = entry->mData.BeginWriting(); + + cursors.infallibleEmplaceBack(item, zip, reinterpret_cast<uint8_t*>(data), + size, true); + } + + mReaderInitialized = true; + mal.NotifyAll(); + } + + // Loop over the entries, read the file's contents, store them in the + // entry's mData pointer, and notify any waiting threads to check for + // completion. + uint32_t i = 0; + for (auto entry : pendingURLs) { + // If there is any other error code, the entry has already failed at + // this point, so don't bother trying to read it again. + if (entry->mResultCode != NS_ERROR_NOT_INITIALIZED) { + continue; + } + + nsresult rv = NS_OK; + + if (entry->mType == entry->TypeFile) { + auto result = entry->Read(); + if (result.isErr()) { + rv = result.unwrapErr(); + } + } else { + auto& cursor = cursors[i++]; + + uint32_t len; + cursor.Copy(&len); + if (len != entry->mData.Length()) { + entry->mData.Truncate(); + rv = NS_ERROR_FAILURE; + } + } + + entry->mResultCode = rv; + mMonitor.NotifyAll(); + } + + // We're done reading pending entries, so clear the list. + pendingURLs.clear(); + + NS_DispatchToMainThread( + NewRunnableMethod("nsIThread::Shutdown", + mReaderThread, &nsIThread::Shutdown)); + mReaderThread = nullptr; +} + +void +URLPreloader::BeginBackgroundRead() +{ + if (!mReaderThread && !mReaderInitialized && sInitialized) { + nsCOMPtr<nsIRunnable> runnable = + NewRunnableMethod("URLPreloader::BackgroundReadFiles", + this, + &URLPreloader::BackgroundReadFiles); + + Unused << NS_NewNamedThread( + "BGReadURLs", getter_AddRefs(mReaderThread), runnable); + } +} + + +Result<const nsCString, nsresult> +URLPreloader::ReadInternal(const CacheKey& key, ReadType readType) +{ + if (mStartupFinished) { + URLEntry entry(key); + + return entry.Read(); + } + + auto entry = mCachedURLs.LookupOrAdd(key, key); + + entry->UpdateUsedTime(); + + return entry->ReadOrWait(readType); +} + +Result<const nsCString, nsresult> +URLPreloader::ReadURIInternal(nsIURI* uri, ReadType readType) +{ + CacheKey key; + MOZ_TRY_VAR(key, ResolveURI(uri)); + + return ReadInternal(key, readType); +} + +/* static */ Result<const nsCString, nsresult> +URLPreloader::Read(const CacheKey& key, ReadType readType) +{ + // If we're being called before the preloader has been initialized (i.e., + // before the profile has been initialized), just fall back to a synchronous + // read. This happens when we're reading .ini and preference files that are + // needed to locate and initialize the profile. + if (!sInitialized) { + return URLEntry(key).Read(); + } + + return GetSingleton().ReadInternal(key, readType); +} + +/* static */ Result<const nsCString, nsresult> +URLPreloader::ReadURI(nsIURI* uri, ReadType readType) +{ + if (!sInitialized) { + return Err(NS_ERROR_NOT_INITIALIZED); + } + + return GetSingleton().ReadURIInternal(uri, readType); +} + +/* static */ Result<const nsCString, nsresult> +URLPreloader::ReadFile(nsIFile* file, ReadType readType) +{ + return Read(CacheKey(file), readType); +} + +/* static */ Result<const nsCString, nsresult> +URLPreloader::ReadFile(const nsACString& path, ReadType readType) +{ + CacheKey key(CacheKey::TypeFile, path); + return Read(key, readType); +} + +/* static */ Result<const nsCString, nsresult> +URLPreloader::Read(FileLocation& location, ReadType readType) +{ + if (location.IsZip()) { + if (location.GetBaseZip()) { + nsCString path; + location.GetPath(path); + return ReadZip(location.GetBaseZip(), path); + } + return URLEntry::ReadLocation(location); + } + + nsCOMPtr<nsIFile> file = location.GetBaseFile(); + return ReadFile(file, readType); +} + +/* static */ Result<const nsCString, nsresult> +URLPreloader::ReadZip(nsZipArchive* zip, const nsACString& path, ReadType readType) +{ + // If the zip archive belongs to an Omnijar location, map it to a cache + // entry, and cache it as normal. Otherwise, simply read the entry + // synchronously, since other JAR archives are currently unsupported by the + // cache. + RefPtr<nsZipArchive> reader = Omnijar::GetReader(Omnijar::GRE); + if (zip == reader) { + CacheKey key(CacheKey::TypeGREJar, path); + return Read(key, readType); + } + + reader = Omnijar::GetReader(Omnijar::APP); + if (zip == reader) { + CacheKey key(CacheKey::TypeAppJar, path); + return Read(key, readType); + } + + // Not an Omnijar archive, so just read it directly. + FileLocation location(zip, PromiseFlatCString(path).BeginReading()); + return URLEntry::ReadLocation(location); +} + +Result<URLPreloader::CacheKey, nsresult> +URLPreloader::ResolveURI(nsIURI* uri) +{ + nsCString spec; + nsCString scheme; + MOZ_TRY(uri->GetSpec(spec)); + MOZ_TRY(uri->GetScheme(scheme)); + + nsCOMPtr<nsIURI> resolved; + + // If the URI is a resource: or chrome: URI, first resolve it to the + // underlying URI that it wraps. + if (scheme.EqualsLiteral("resource")) { + MOZ_TRY(mResProto->ResolveURI(uri, spec)); + MOZ_TRY(NS_NewURI(getter_AddRefs(resolved), spec)); + } else if (scheme.EqualsLiteral("chrome")) { + MOZ_TRY(mChromeReg->ConvertChromeURL(uri, getter_AddRefs(resolved))); + MOZ_TRY(resolved->GetSpec(spec)); + } else { + resolved = uri; + } + MOZ_TRY(resolved->GetScheme(scheme)); + + // Try the GRE and App Omnijar prefixes. + if (mGREPrefix.Length() && StartsWith(spec, mGREPrefix)) { + return CacheKey(CacheKey::TypeGREJar, + Substring(spec, mGREPrefix.Length())); + } + + if (mAppPrefix.Length() && StartsWith(spec, mAppPrefix)) { + return CacheKey(CacheKey::TypeAppJar, + Substring(spec, mAppPrefix.Length())); + } + + // Try for a file URI. + if (scheme.EqualsLiteral("file")) { + nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(resolved); + MOZ_ASSERT(fileURL); + + nsCOMPtr<nsIFile> file; + MOZ_TRY(fileURL->GetFile(getter_AddRefs(file))); + + nsCString path; + MOZ_TRY(file->GetNativePath(path)); + + return CacheKey(CacheKey::TypeFile, path); + } + + // Not a file or Omnijar URI, so currently unsupported. + return Err(NS_ERROR_INVALID_ARG); +} + +size_t +URLPreloader::ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) +{ + return (mallocSizeOf(this) + + mAppPrefix.SizeOfExcludingThisEvenIfShared(mallocSizeOf) + + mGREPrefix.SizeOfExcludingThisEvenIfShared(mallocSizeOf) + + mCachedURLs.ShallowSizeOfExcludingThis(mallocSizeOf)); +} + +Result<FileLocation, nsresult> +URLPreloader::CacheKey::ToFileLocation() +{ + if (mType == TypeFile) { + nsCOMPtr<nsIFile> file; + MOZ_TRY(NS_NewNativeLocalFile(mPath, false, getter_AddRefs(file))); + return Move(FileLocation(file)); + } + + RefPtr<nsZipArchive> zip = Archive(); + return Move(FileLocation(zip, mPath.get())); +} + +Result<const nsCString, nsresult> +URLPreloader::URLEntry::Read() +{ + FileLocation location; + MOZ_TRY_VAR(location, ToFileLocation()); + + MOZ_TRY_VAR(mData, ReadLocation(location)); + return mData; +} + +/* static */ Result<const nsCString, nsresult> +URLPreloader::URLEntry::ReadLocation(FileLocation& location) +{ + FileLocation::Data data; + MOZ_TRY(location.GetData(data)); + + uint32_t size; + MOZ_TRY(data.GetSize(&size)); + + nsCString result; + result.SetLength(size); + MOZ_TRY(data.Copy(result.BeginWriting(), size)); + + return Move(result); +} + +Result<const nsCString, nsresult> +URLPreloader::URLEntry::ReadOrWait(ReadType readType) +{ + auto now = TimeStamp::Now(); + LOG(Info, "Reading %s\n", mPath.get()); + auto cleanup = MakeScopeExit([&] () { + LOG(Info, "Read in %fms\n", (TimeStamp::Now() - now).ToMilliseconds()); + }); + + if (mResultCode == NS_ERROR_NOT_INITIALIZED) { + MonitorAutoLock mal(GetSingleton().mMonitor); + + while (mResultCode == NS_ERROR_NOT_INITIALIZED) { + mal.Wait(); + } + } + + if (mResultCode == NS_OK && mData.IsVoid()) { + LOG(Info, "Reading synchronously...\n"); + return Read(); + } + + if (NS_FAILED(mResultCode)) { + return Err(mResultCode); + } + + nsCString res = mData; + + if (readType == Forget) { + mData.SetIsVoid(true); + } + return res; +} + +inline +URLPreloader::CacheKey::CacheKey(InputBuffer& buffer) +{ + Code(buffer); +} + +nsresult +URLPreloader::Observe(nsISupports* subject, const char* topic, const char16_t* data) +{ + nsCOMPtr<nsIObserverService> obs = services::GetObserverService(); + if (!strcmp(topic, DELAYED_STARTUP_TOPIC)) { + obs->RemoveObserver(this, DELAYED_STARTUP_TOPIC); + mStartupFinished = true; + } + + return NS_OK; +} + + +NS_IMPL_ISUPPORTS(URLPreloader, nsIObserver, nsIMemoryReporter) + +#undef LOG + +} // namespace mozilla
new file mode 100644 --- /dev/null +++ b/js/xpconnect/loader/URLPreloader.h @@ -0,0 +1,325 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; -*- */ +/* 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 URLPreloader_h +#define URLPreloader_h + +#include "mozilla/FileLocation.h" +#include "mozilla/HashFunctions.h" +#include "mozilla/LinkedList.h" +#include "mozilla/MemoryReporting.h" +#include "mozilla/Monitor.h" +#include "mozilla/Omnijar.h" +#include "mozilla/Range.h" +#include "mozilla/Vector.h" +#include "mozilla/Result.h" +#include "nsClassHashtable.h" +#include "nsHashKeys.h" +#include "nsIChromeRegistry.h" +#include "nsIFile.h" +#include "nsIURI.h" +#include "nsIMemoryReporter.h" +#include "nsIObserver.h" +#include "nsIResProtocolHandler.h" +#include "nsIThread.h" +#include "nsReadableUtils.h" + +class nsZipArchive; + +namespace mozilla { +namespace loader { + class InputBuffer; +} + +using namespace mozilla::loader; + +class ScriptPreloader; + +/** + * A singleton class to manage loading local URLs during startup, recording + * them, and pre-loading them during early startup in the next session. URLs + * that are not already loaded (or already being pre-loaded) when required are + * read synchronously from disk, and (if startup is not already complete) + * added to the pre-load list for the next session. + */ +class URLPreloader final : public nsIObserver + , public nsIMemoryReporter +{ + MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf) + + URLPreloader(); + +public: + NS_DECL_THREADSAFE_ISUPPORTS + NS_DECL_NSIOBSERVER + NS_DECL_NSIMEMORYREPORTER + + static URLPreloader& GetSingleton(); + + // The type of read operation to perform. + enum ReadType + { + // Read the file and then immediately forget its data. + Forget, + // Read the file and retain its data for the next caller. + Retain, + }; + + // Helpers to read the contents of files or JAR archive entries with various + // representations. If the preloader has not yet been initialized, or the + // given location is not supported by the cache, the entries will be read + // synchronously, and not stored in the cache. + static Result<const nsCString, nsresult> Read(FileLocation& location, ReadType readType = Forget); + + static Result<const nsCString, nsresult> ReadURI(nsIURI* uri, ReadType readType = Forget); + + static Result<const nsCString, nsresult> ReadFile(nsIFile* file, ReadType readType = Forget); + + static Result<const nsCString, nsresult> ReadFile(const nsACString& path, ReadType readType = Forget); + + static Result<const nsCString, nsresult> ReadZip(nsZipArchive* archive, + const nsACString& path, + ReadType readType = Forget); + +private: + struct CacheKey; + + Result<const nsCString, nsresult> ReadInternal(const CacheKey& key, ReadType readType); + + Result<const nsCString, nsresult> ReadURIInternal(nsIURI* uri, ReadType readType); + + Result<const nsCString, nsresult> ReadFileInternal(nsIFile* file, ReadType readType); + + static Result<const nsCString, nsresult> Read(const CacheKey& key, ReadType readType); + + static bool sInitialized; + +protected: + friend class ScriptPreloader; + + virtual ~URLPreloader(); + + Result<Ok, nsresult> WriteCache(); + + // Clear leftover entries after the cache has been written. + void Cleanup(); + + // Begins reading files off-thread, and ensures that initialization has + // completed before leaving the current scope. The caller *must* ensure that + // no code on the main thread access Omnijar, either directly or indirectly, + // for the lifetime of this guard object. + struct MOZ_RAII AutoBeginReading final + { + AutoBeginReading() + { + GetSingleton().BeginBackgroundRead(); + } + + ~AutoBeginReading() + { + auto& reader = GetSingleton(); + + MonitorAutoLock mal(reader.mMonitor); + + while (!reader.mReaderInitialized && reader.sInitialized) { + mal.Wait(); + } + } + }; + +private: + // Represents a key for an entry in the URI cache, based on its file or JAR + // location. + struct CacheKey + { + // The type of the entry. TypeAppJar and TypeGREJar entries are in the + // app-specific or toolkit Omnijar files, and are handled specially. + // TypeFile entries are plain files in the filesystem. + enum EntryType : uint8_t + { + TypeAppJar, + TypeGREJar, + TypeFile, + }; + + CacheKey() = default; + CacheKey(const CacheKey& other) = default; + + CacheKey(EntryType type, const nsACString& path) + : mType(type), mPath(path) + {} + + explicit CacheKey(nsIFile* file) + : mType(TypeFile) + { + MOZ_ALWAYS_SUCCEEDS(file->GetNativePath(mPath)); + } + + explicit inline CacheKey(InputBuffer& buffer); + + // Encodes or decodes the cache key for storage in a session cache file. + template <typename Buffer> + void Code(Buffer& buffer) + { + buffer.codeUint8(*reinterpret_cast<uint8_t*>(&mType)); + buffer.codeString(mPath); + } + + uint32_t Hash() const + { + return HashGeneric(mType, HashString(mPath)); + } + + bool operator==(const CacheKey& other) const + { + return mType == other.mType && mPath == other.mPath; + } + + // Returns the Omnijar type for this entry. This may *only* be called + // for Omnijar entries. + Omnijar::Type OmnijarType() + { + switch (mType) { + case TypeAppJar: + return Omnijar::APP; + case TypeGREJar: + return Omnijar::GRE; + default: + MOZ_CRASH("Unexpected entry type"); + return Omnijar::GRE; + } + } + + const char* TypeString() + { + switch (mType) { + case TypeAppJar: return "AppJar"; + case TypeGREJar: return "GREJar"; + case TypeFile: return "File"; + } + MOZ_ASSERT_UNREACHABLE("no such type"); + return ""; + } + + already_AddRefed<nsZipArchive> Archive() + { + return Omnijar::GetReader(OmnijarType()); + } + + Result<FileLocation, nsresult> ToFileLocation(); + + EntryType mType = TypeFile; + + // The path of the entry. For Type*Jar entries, this is the path within + // the Omnijar archive. For TypeFile entries, this is the full path to + // the file. + nsCString mPath{}; + }; + + // Represents an entry in the URI cache. + struct URLEntry final : public CacheKey + , public LinkedListElement<URLEntry> + { + MOZ_IMPLICIT URLEntry(const CacheKey& key) + : CacheKey(key) + , mData(NullCString()) + {} + + explicit URLEntry(nsIFile* file) + : CacheKey(file) + {} + + // For use with nsTArray::Sort. + // + // Sorts entries by the time they were initially read during this + // session. + struct Comparator final + { + bool Equals(const URLEntry* a, const URLEntry* b) const + { + return a->mReadTime == b->mReadTime; + } + + bool LessThan(const URLEntry* a, const URLEntry* b) const + { + return a->mReadTime < b->mReadTime; + } + }; + + // Sets the first-used time of this file to the earlier of its current + // first-use time or the given timestamp. + void UpdateUsedTime(const TimeStamp& time = TimeStamp::Now()) + { + if (!mReadTime || time < mReadTime) { + mReadTime = time; + } + } + + Result<const nsCString, nsresult> Read(); + static Result<const nsCString, nsresult> ReadLocation(FileLocation& location); + + size_t SizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf) const + { + return (mallocSizeOf(this) + + mPath.SizeOfExcludingThisEvenIfShared(mallocSizeOf) + + mData.SizeOfExcludingThisEvenIfShared(mallocSizeOf)); + } + + // Reads the contents of the file referenced by this entry, or wait for + // an off-thread read operation to finish if it is currently pending, + // and return the file's contents. + Result<const nsCString, nsresult> ReadOrWait(ReadType readType); + + nsCString mData; + + TimeStamp mReadTime{}; + + nsresult mResultCode = NS_OK; + }; + + // Resolves the given URI to a CacheKey, if the URI is cacheable. + Result<CacheKey, nsresult> ResolveURI(nsIURI* uri); + + Result<Ok, nsresult> InitInternal(); + + // Returns a file pointer to the (possibly nonexistent) cache file with the + // given suffix. + Result<nsCOMPtr<nsIFile>, nsresult> GetCacheFile(const nsAString& suffix); + // Finds the correct cache file to use for this session. + Result<nsCOMPtr<nsIFile>, nsresult> FindCacheFile(); + + Result<Ok, nsresult> ReadCache(LinkedList<URLEntry>& pendingURLs); + + void BackgroundReadFiles(); + void BeginBackgroundRead(); + + using HashType = nsClassHashtable<nsGenericHashKey<CacheKey>, URLEntry>; + + size_t ShallowSizeOfIncludingThis(mozilla::MallocSizeOf mallocSizeOf); + + + bool mStartupFinished = false; + bool mReaderInitialized = false; + + // The prefix URLs for files in the GRE and App omni jar archives. + nsCString mGREPrefix; + nsCString mAppPrefix; + + nsCOMPtr<nsIResProtocolHandler> mResProto; + nsCOMPtr<nsIChromeRegistry> mChromeReg; + nsCOMPtr<nsIFile> mProfD; + + nsCOMPtr<nsIThread> mReaderThread; + + // A map of URL entries which have were either read this session, or read + // from the last session's cache file. + HashType mCachedURLs; + + Monitor mMonitor{"[URLPreloader::mMutex]"}; +}; + +} // namespace mozilla + +#endif // URLPreloader_h
--- a/js/xpconnect/loader/moz.build +++ b/js/xpconnect/loader/moz.build @@ -6,30 +6,32 @@ UNIFIED_SOURCES += [ 'AutoMemMap.cpp', 'ChromeScriptLoader.cpp', 'mozJSLoaderUtils.cpp', 'mozJSSubScriptLoader.cpp', 'ScriptCacheActors.cpp', 'ScriptPreloader.cpp', + 'URLPreloader.cpp', ] # mozJSComponentLoader.cpp cannot be built in unified mode because it uses # windows.h SOURCES += [ 'mozJSComponentLoader.cpp' ] IPDL_SOURCES += [ 'PScriptCache.ipdl', ] EXPORTS.mozilla += [ 'ScriptPreloader.h', + 'URLPreloader.h', ] EXPORTS.mozilla.dom += [ 'PrecompiledScript.h', ] EXPORTS.mozilla.loader += [ 'AutoMemMap.h',
--- a/js/xpconnect/src/XPCComponents.cpp +++ b/js/xpconnect/src/XPCComponents.cpp @@ -16,16 +16,18 @@ #include "nsCycleCollector.h" #include "jsfriendapi.h" #include "js/StructuredClone.h" #include "mozilla/Attributes.h" #include "mozilla/jsipc/CrossProcessObjectWrappers.h" #include "mozilla/Preferences.h" #include "nsJSEnvironment.h" #include "mozilla/TimeStamp.h" +#include "mozilla/ResultExtensions.h" +#include "mozilla/URLPreloader.h" #include "mozilla/XPTInterfaceInfoManager.h" #include "mozilla/dom/DOMException.h" #include "mozilla/dom/DOMExceptionBinding.h" #include "mozilla/dom/BindingUtils.h" #include "mozilla/dom/StructuredCloneTags.h" #include "mozilla/dom/WindowBinding.h" #include "nsZipArchive.h" #include "nsIDOMFileList.h" @@ -3375,16 +3377,25 @@ nsXPCComponents_Utils::AllowCPOWsInAddon return NS_ERROR_FAILURE; if (!XPCWrappedNativeScope::AllowCPOWsInAddon(cx, addonId, allow)) return NS_ERROR_FAILURE; return NS_OK; } NS_IMETHODIMP +nsXPCComponents_Utils::ReadFile(nsIFile* aFile, nsACString& aResult) +{ + NS_ENSURE_TRUE(aFile, NS_ERROR_INVALID_ARG); + + MOZ_TRY_VAR(aResult, URLPreloader::ReadFile(aFile)); + return NS_OK; +} + +NS_IMETHODIMP nsXPCComponents_Utils::Now(double* aRetval) { TimeStamp start = TimeStamp::ProcessCreation(); *aRetval = (TimeStamp::Now() - start).ToMilliseconds(); return NS_OK; } /***************************************************************************/
--- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -560,19 +560,21 @@ WrapperFactory::Rewrap(JSContext* cx, Ha wrapper = SelectWrapper(securityWrapper, xrayType, waiveXrays, obj); // If we want to apply add-on interposition in the target compartment, // then we try to "upgrade" the wrapper to an interposing one. if (targetCompartmentPrivate->scope->HasInterposition()) wrapper = SelectAddonWrapper(cx, obj, wrapper); } - if (!targetSubsumesOrigin) { + if (!targetSubsumesOrigin && + !originCompartmentPrivate->forcePermissiveCOWs) { // Do a belt-and-suspenders check against exposing eval()/Function() to - // non-subsuming content. + // non-subsuming content. But don't worry about doing it in the + // SpecialPowers case. if (JSFunction* fun = JS_GetObjectFunction(obj)) { if (JS_IsBuiltinEvalFunction(fun) || JS_IsBuiltinFunctionConstructor(fun)) { NS_WARNING("Trying to expose eval or Function to non-subsuming content!"); wrapper = &FilteringWrapper<CrossCompartmentSecurityWrapper, Opaque>::singleton; } } }
--- a/layout/base/nsCSSFrameConstructor.cpp +++ b/layout/base/nsCSSFrameConstructor.cpp @@ -4288,21 +4288,19 @@ SetFlagsOnSubtree(nsIContent *aNode, uin "The node should not have any XBL children"); } #endif // Set the flag on the node itself aNode->SetFlags(aFlagsToSet); // Set the flag on all of its children recursively - uint32_t count; - nsIContent * const *children = aNode->GetChildArray(&count); - - for (uint32_t index = 0; index < count; ++index) { - SetFlagsOnSubtree(children[index], aFlagsToSet); + for (nsIContent* child = aNode->GetFirstChild(); child; + child = child->GetNextSibling()) { + SetFlagsOnSubtree(child, aFlagsToSet); } } /** * This function takes a tree of nsIAnonymousContentCreator::ContentInfo * objects where the nsIContent nodes have just been created, and appends the * nsIContent children in the tree to their parent. The leaf nsIContent objects * are appended first to minimize the number of notifications that are sent
--- a/layout/generic/nsPlaceholderFrame.cpp +++ b/layout/generic/nsPlaceholderFrame.cpp @@ -204,35 +204,20 @@ nsPlaceholderFrame::GetParentStyleContex } return GetLayoutParentStyleForOutOfFlow(aProviderFrame); } nsStyleContext* nsPlaceholderFrame::GetLayoutParentStyleForOutOfFlow(nsIFrame** aProviderFrame) const { - nsIFrame* parentFrame = GetParent(); - // Placeholder of backdrop frame is a child of the corresponding top - // layer frame, and its style context inherits from that frame. In - // case of table, the top layer frame is the table wrapper frame. - // However, it will be skipped in CorrectStyleParentFrame below, so - // we need to handle it specially here. - if ((GetStateBits() & PLACEHOLDER_FOR_TOPLAYER) && - parentFrame->IsTableWrapperFrame()) { - MOZ_ASSERT(mOutOfFlowFrame->IsBackdropFrame(), - "Only placeholder of backdrop frame can be put inside " - "a table wrapper frame"); - *aProviderFrame = parentFrame; - return parentFrame->StyleContext(); - } - // Lie about our pseudo so we can step out of all anon boxes and // pseudo-elements. The other option would be to reimplement the // {ib} split gunk here. - *aProviderFrame = CorrectStyleParentFrame(parentFrame, + *aProviderFrame = CorrectStyleParentFrame(GetParent(), nsGkAtoms::placeholderFrame); return *aProviderFrame ? (*aProviderFrame)->StyleContext() : nullptr; } #ifdef DEBUG static void PaintDebugPlaceholder(nsIFrame* aFrame, DrawTarget* aDrawTarget,
--- a/layout/style/Loader.cpp +++ b/layout/style/Loader.cpp @@ -11,16 +11,18 @@ #include "mozilla/ArrayUtils.h" #include "mozilla/dom/DocGroup.h" #include "mozilla/IntegerPrintfMacros.h" #include "mozilla/LoadInfo.h" #include "mozilla/MemoryReporting.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/SystemGroup.h" +#include "mozilla/ResultExtensions.h" +#include "mozilla/URLPreloader.h" #include "nsIRunnable.h" #include "nsIUnicharStreamLoader.h" #include "nsSyncLoadService.h" #include "nsCOMPtr.h" #include "nsString.h" #include "nsIContent.h" #include "nsIDocument.h" #include "nsIDOMNode.h" @@ -39,16 +41,17 @@ #include "nsIStyleSheetLinkingElement.h" #include "nsICSSLoaderObserver.h" #include "nsCSSParser.h" #include "mozilla/css/ImportRule.h" #include "nsThreadUtils.h" #include "nsGkAtoms.h" #include "nsIThreadInternal.h" #include "nsINetworkPredictor.h" +#include "nsStringStream.h" #include "mozilla/dom/MediaList.h" #include "mozilla/dom/ShadowRoot.h" #include "mozilla/dom/URL.h" #include "mozilla/AsyncEventDispatcher.h" #include "mozilla/ServoBindings.h" #include "mozilla/StyleSheet.h" #include "mozilla/StyleSheetInlines.h" #include "mozilla/ConsoleReportCollector.h" @@ -1351,21 +1354,34 @@ Loader::LoadSheet(SheetLoadData* aLoadDa securityFlags, contentPolicyType); } else { // either we are loading something inside a document, in which case // we should always have a requestingNode, or we are loading something // outside a document, in which case the loadingPrincipal and the // triggeringPrincipal should always be the systemPrincipal. - rv = NS_NewChannel(getter_AddRefs(channel), - aLoadData->mURI, - nsContentUtils::GetSystemPrincipal(), - securityFlags, - contentPolicyType); + auto result = URLPreloader::ReadURI(aLoadData->mURI); + if (result.isOk()) { + nsCOMPtr<nsIInputStream> stream; + MOZ_TRY(NS_NewCStringInputStream(getter_AddRefs(stream), result.unwrap())); + + rv = NS_NewInputStreamChannel(getter_AddRefs(channel), + aLoadData->mURI, + stream, + nsContentUtils::GetSystemPrincipal(), + securityFlags, + contentPolicyType); + } else { + rv = NS_NewChannel(getter_AddRefs(channel), + aLoadData->mURI, + nsContentUtils::GetSystemPrincipal(), + securityFlags, + contentPolicyType); + } } if (NS_FAILED(rv)) { LOG_ERROR((" Failed to create channel")); SheetComplete(aLoadData, rv); return rv; } nsCOMPtr<nsIInputStream> stream;
--- a/layout/tables/nsTableColGroupFrame.cpp +++ b/layout/tables/nsTableColGroupFrame.cpp @@ -12,34 +12,27 @@ #include "nsGkAtoms.h" #include "nsCOMPtr.h" #include "nsCSSRendering.h" #include "nsIPresShell.h" #include "mozilla/GeckoStyleContext.h" using namespace mozilla; -#define COL_GROUP_TYPE_BITS (NS_FRAME_STATE_BIT(30) | \ - NS_FRAME_STATE_BIT(31)) -#define COL_GROUP_TYPE_OFFSET 30 +#define COLGROUP_SYNTHETIC_BIT NS_FRAME_STATE_BIT(30) -nsTableColGroupType -nsTableColGroupFrame::GetColType() const +bool +nsTableColGroupFrame::IsSynthetic() const { - return (nsTableColGroupType)((mState & COL_GROUP_TYPE_BITS) >> COL_GROUP_TYPE_OFFSET); + return HasAnyStateBits(COLGROUP_SYNTHETIC_BIT); } -void nsTableColGroupFrame::SetColType(nsTableColGroupType aType) +void nsTableColGroupFrame::SetIsSynthetic() { - NS_ASSERTION(GetColType() == eColGroupContent, - "should only call nsTableColGroupFrame::SetColType with aType " - "!= eColGroupContent once"); - uint32_t type = aType - eColGroupContent; - RemoveStateBits(COL_GROUP_TYPE_BITS); - AddStateBits(nsFrameState(type << COL_GROUP_TYPE_OFFSET)); + AddStateBits(COLGROUP_SYNTHETIC_BIT); } void nsTableColGroupFrame::ResetColIndices(nsIFrame* aFirstColGroup, int32_t aFirstColIndex, nsIFrame* aStartColFrame) { nsTableColGroupFrame* colGroupFrame = (nsTableColGroupFrame*)aFirstColGroup; int32_t colIndex = aFirstColIndex; @@ -109,33 +102,26 @@ nsTableColGroupFrame::AddColsToTable(int } nsTableColGroupFrame* nsTableColGroupFrame::GetLastRealColGroup(nsTableFrame* aTableFrame) { nsFrameList colGroups = aTableFrame->GetColGroups(); - nsIFrame* nextToLastColGroup = nullptr; - nsFrameList::FrameLinkEnumerator link(colGroups); - for ( ; !link.AtEnd(); link.Next()) { - nextToLastColGroup = link.PrevFrame(); + auto lastColGroup = static_cast<nsTableColGroupFrame*>(colGroups.LastChild()); + if (!lastColGroup) { + return nullptr; } - if (!link.PrevFrame()) { - return nullptr; // there are no col group frames + if (!lastColGroup->IsSynthetic()) { + return lastColGroup; } - nsTableColGroupType lastColGroupType = - static_cast<nsTableColGroupFrame*>(link.PrevFrame())->GetColType(); - if (eColGroupAnonymousCell == lastColGroupType) { - return static_cast<nsTableColGroupFrame*>(nextToLastColGroup); - } - - return static_cast<nsTableColGroupFrame*>(link.PrevFrame()); + return static_cast<nsTableColGroupFrame*>(lastColGroup->GetPrevSibling()); } // don't set mColCount here, it is done in AddColsToTable void nsTableColGroupFrame::SetInitialChildList(ChildListID aListID, nsFrameList& aChildList) { MOZ_ASSERT(mFrames.IsEmpty(), @@ -185,19 +171,18 @@ nsTableColGroupFrame::AppendFrames(Child // has content columns in it. nextCol = col->GetNextCol(); RemoveFrame(kPrincipalList, col); col = nextCol; } // Our next colframe should be an eColContent. We've removed all the // eColAnonymousColGroup colframes, eColAnonymousCol colframes always follow - // eColContent ones, and eColAnonymousCell colframes only appear in an - // eColGroupAnonymousCell colgroup, which never gets AppendFrames() called on - // it. + // eColContent ones, and eColAnonymousCell colframes only appear in a + // synthetic colgroup, which never gets AppendFrames() called on it. MOZ_ASSERT(!col || col->GetColType() == eColContent, "What's going on with our columns?"); const nsFrameList::Slice& newFrames = mFrames.AppendFrames(this, aFrameList); InsertColsReflow(GetStartColumnIndex() + mColCount, newFrames); } @@ -226,19 +211,18 @@ nsTableColGroupFrame::InsertFrames(Child aPrevFrame = nullptr; } RemoveFrame(kPrincipalList, col); col = nextCol; } // Our next colframe should be an eColContent. We've removed all the // eColAnonymousColGroup colframes, eColAnonymousCol colframes always follow - // eColContent ones, and eColAnonymousCell colframes only appear in an - // eColGroupAnonymousCell colgroup, which never gets InsertFrames() called on - // it. + // eColContent ones, and eColAnonymousCell colframes only appear in a + // synthetic colgroup, which never gets InsertFrames() called on it. MOZ_ASSERT(!col || col->GetColType() == eColContent, "What's going on with our columns?"); NS_ASSERTION(!aPrevFrame || aPrevFrame == aPrevFrame->LastContinuation(), "Prev frame should be last in continuation chain"); NS_ASSERTION(!aPrevFrame || !GetNextColumn(aPrevFrame) || GetNextColumn(aPrevFrame)->GetColType() != eColAnonymousCol, "Shouldn't be inserting before a spanned colframe"); @@ -333,18 +317,17 @@ nsTableColGroupFrame::RemoveFrame(ChildL } int32_t colIndex = colFrame->GetColIndex(); // The RemoveChild call handles calling FrameNeedsReflow on us. RemoveChild(*colFrame, true); nsTableFrame* tableFrame = GetTableFrame(); tableFrame->RemoveCol(this, colIndex, true, true); - if (mFrames.IsEmpty() && contentRemoval && - GetColType() == eColGroupContent) { + if (mFrames.IsEmpty() && contentRemoval && !IsSynthetic()) { tableFrame->AppendAnonymousColFrames(this, GetSpan(), eColAnonymousColGroup, true); } } else { mFrames.DestroyFrame(aOldFrame); } } @@ -510,30 +493,20 @@ void nsTableColGroupFrame::Dump(int32_t { char* indent = new char[aIndent + 1]; if (!indent) return; for (int32_t i = 0; i < aIndent + 1; i++) { indent[i] = ' '; } indent[aIndent] = 0; - printf("%s**START COLGROUP DUMP**\n%s startcolIndex=%d colcount=%d span=%d coltype=", - indent, indent, GetStartColumnIndex(), GetColCount(), GetSpan()); - nsTableColGroupType colType = GetColType(); - switch (colType) { - case eColGroupContent: - printf(" content "); - break; - case eColGroupAnonymousCol: - printf(" anonymous-column "); - break; - case eColGroupAnonymousCell: - printf(" anonymous-cell "); - break; - } + printf("%s**START COLGROUP DUMP**\n%s startcolIndex=%d colcount=%d span=%d isSynthetic=%s", + indent, indent, GetStartColumnIndex(), GetColCount(), GetSpan(), + IsSynthetic() ? "true" : "false"); + // verify the colindices int32_t j = GetStartColumnIndex(); nsTableColFrame* col = GetFirstColumn(); while (col) { NS_ASSERTION(j == col->GetColIndex(), "wrong colindex on col frame"); col = col->GetNextCol(); j++; }
--- a/layout/tables/nsTableColGroupFrame.h +++ b/layout/tables/nsTableColGroupFrame.h @@ -49,29 +49,30 @@ public: "Col group should always be in a first-in-flow table frame"); return static_cast<nsTableFrame*>(parent); } virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder, const nsDisplayListSet& aLists) override; /** A colgroup can be caused by three things: - * 1) An element with table-column-group display - * 2) An element with a table-column display without a - * table-column-group parent - * 3) Cells that are not in a column (and hence get an anonymous - * column and colgroup). - * @return colgroup type + * 1) An element with table-column-group display + * 2) An element with a table-column display without a + * table-column-group parent + * 3) Cells that are not in a column (and hence get an anonymous + * column and colgroup). + * + * In practice, we don't need to differentiate between cases (1) and (2), + * because they both correspond to table-column-group boxes in the spec and + * hence have observably identical behavior. Case three is flagged as a + * synthetic colgroup, because it may need to have different behavior in some + * cases. */ - nsTableColGroupType GetColType() const; - - /** Set the colgroup type based on the creation cause - * @param aType - the reason why this colgroup is needed - */ - void SetColType(nsTableColGroupType aType); + bool IsSynthetic() const; + void SetIsSynthetic(); /** Real in this context are colgroups that come from an element * with table-column-group display or wrap around columns that * come from an element with table-column display. Colgroups * that are the result of wrapping cells in an anonymous * column and colgroup are not considered real here. * @param aTableFrame - the table parent of the colgroups * @return the last real colgroup @@ -219,17 +220,16 @@ protected: BCPixelSize mBEndContBorderWidth; }; inline nsTableColGroupFrame::nsTableColGroupFrame(nsStyleContext* aContext) : nsContainerFrame(aContext, kClassID) , mColCount(0) , mStartColIndex(0) { - SetColType(eColGroupContent); } inline int32_t nsTableColGroupFrame::GetStartColumnIndex() { return mStartColIndex; } inline void nsTableColGroupFrame::SetStartColumnIndex (int32_t aIndex)
--- a/layout/tables/nsTableFrame.cpp +++ b/layout/tables/nsTableFrame.cpp @@ -599,19 +599,20 @@ nsTableFrame::InsertCol(nsTableColFrame& bool removedFromCache = false; if (eColAnonymousCell != insertedColType) { nsTableColFrame* lastCol = mColFrames.ElementAt(numCacheCols - 1); if (lastCol) { nsTableColType lastColType = lastCol->GetColType(); if (eColAnonymousCell == lastColType) { // remove the col from the cache mColFrames.RemoveElementAt(numCacheCols - 1); - // remove the col from the eColGroupAnonymousCell col group + // remove the col from the synthetic col group nsTableColGroupFrame* lastColGroup = (nsTableColGroupFrame *)mColGroups.LastChild(); if (lastColGroup) { + MOZ_ASSERT(lastColGroup->IsSynthetic()); lastColGroup->RemoveChild(*lastCol, false); // remove the col group if it is empty if (lastColGroup->GetColCount() <= 0) { mColGroups.DestroyFrame((nsIFrame*)lastColGroup); } } removedFromCache = true; @@ -673,48 +674,47 @@ nsTableFrame::RemoveCol(nsTableColGroupF * Only the first-in-flow has a legit cell map. */ nsTableCellMap* nsTableFrame::GetCellMap() const { return static_cast<nsTableFrame*>(FirstInFlow())->mCellMap; } -// XXX this needs to be moved to nsCSSFrameConstructor nsTableColGroupFrame* -nsTableFrame::CreateAnonymousColGroupFrame(nsTableColGroupType aColGroupType) +nsTableFrame::CreateSyntheticColGroupFrame() { nsIContent* colGroupContent = GetContent(); nsPresContext* presContext = PresContext(); nsIPresShell *shell = presContext->PresShell(); RefPtr<nsStyleContext> colGroupStyle; colGroupStyle = shell->StyleSet()-> ResolveNonInheritingAnonymousBoxStyle(nsCSSAnonBoxes::tableColGroup); // Create a col group frame - nsIFrame* newFrame = NS_NewTableColGroupFrame(shell, colGroupStyle); - ((nsTableColGroupFrame *)newFrame)->SetColType(aColGroupType); + nsTableColGroupFrame* newFrame = + NS_NewTableColGroupFrame(shell, colGroupStyle); + newFrame->SetIsSynthetic(); newFrame->Init(colGroupContent, this, nullptr); - return (nsTableColGroupFrame *)newFrame; + return newFrame; } void nsTableFrame::AppendAnonymousColFrames(int32_t aNumColsToAdd) { MOZ_ASSERT(aNumColsToAdd > 0, "We should be adding _something_."); // get the last col group frame nsTableColGroupFrame* colGroupFrame = static_cast<nsTableColGroupFrame*>(mColGroups.LastChild()); - if (!colGroupFrame || - (colGroupFrame->GetColType() != eColGroupAnonymousCell)) { + if (!colGroupFrame || !colGroupFrame->IsSynthetic()) { int32_t colIndex = (colGroupFrame) ? colGroupFrame->GetStartColumnIndex() + colGroupFrame->GetColCount() : 0; - colGroupFrame = CreateAnonymousColGroupFrame(eColGroupAnonymousCell); + colGroupFrame = CreateSyntheticColGroupFrame(); if (!colGroupFrame) { return; } // add the new frame to the child list mColGroups.AppendFrame(this, colGroupFrame); colGroupFrame->SetStartColumnIndex(colIndex); } AppendAnonymousColFrames(colGroupFrame, aNumColsToAdd, eColAnonymousCell,
--- a/layout/tables/nsTableFrame.h +++ b/layout/tables/nsTableFrame.h @@ -100,22 +100,16 @@ private: nsDisplayTableItem* mOldCurrentItem; #ifdef DEBUG nsDisplayTableItem* mPushedItem; #endif }; /* ============================================================================ */ -enum nsTableColGroupType { - eColGroupContent = 0, // there is real col group content associated - eColGroupAnonymousCol = 1, // the result of a col - eColGroupAnonymousCell = 2 // the result of a cell alone -}; - enum nsTableColType { eColContent = 0, // there is real col content associated eColAnonymousCol = 1, // the result of a span on a col eColAnonymousColGroup = 2, // the result of a span on a col group eColAnonymousCell = 3 // the result of a cell alone }; /** @@ -499,23 +493,22 @@ public: /** Insert a col frame reference into the colframe cache and adapt the cellmap * @param aColFrame - the column frame * @param aColIndex - index where the column should be inserted into the * colframe cache */ void InsertCol(nsTableColFrame& aColFrame, int32_t aColIndex); - nsTableColGroupFrame* CreateAnonymousColGroupFrame(nsTableColGroupType aType); + nsTableColGroupFrame* CreateSyntheticColGroupFrame(); int32_t DestroyAnonymousColFrames(int32_t aNumFrames); // Append aNumColsToAdd anonymous col frames of type eColAnonymousCell to our - // last eColGroupAnonymousCell colgroup. If we have no such colgroup, then - // create one. + // last synthetic colgroup. If we have no such colgroup, then create one. void AppendAnonymousColFrames(int32_t aNumColsToAdd); // Append aNumColsToAdd anonymous col frames of type aColType to // aColGroupFrame. If aAddToTable is true, also call AddColsToTable on the // new cols. void AppendAnonymousColFrames(nsTableColGroupFrame* aColGroupFrame, int32_t aNumColsToAdd, nsTableColType aColType,
--- a/mobile/android/components/extensions/ext-utils.js +++ b/mobile/android/components/extensions/ext-utils.js @@ -477,16 +477,20 @@ class Tab extends TabBase { get audible() { return this.nativeTab.playingAudio; } get browser() { return this.nativeTab.browser; } + get discarded() { + return this.browser.getAttribute("pending") === "true"; + } + get cookieStoreId() { return getCookieStoreIdForTab(this, this.nativeTab); } get height() { return this.browser.clientHeight; }
--- a/mobile/android/components/extensions/schemas/tabs.json +++ b/mobile/android/components/extensions/schemas/tabs.json @@ -66,16 +66,17 @@ "pinned": {"type": "boolean", "description": "Whether the tab is pinned."}, "lastAccessed": {"type": "integer", "optional": true, "description": "The last time the tab was accessed as the number of milliseconds since epoch."}, "audible": {"type": "boolean", "optional": true, "description": "Whether the tab has produced sound over the past couple of seconds (but it might not be heard if also muted). Equivalent to whether the speaker audio indicator is showing."}, "mutedInfo": {"$ref": "MutedInfo", "optional": true, "description": "Current tab muted state and the reason for the last state change."}, "url": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The URL the tab is displaying. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."}, "title": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The title of the tab. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission."}, "favIconUrl": {"type": "string", "optional": true, "permissions": ["tabs"], "description": "The URL of the tab's favicon. This property is only present if the extension's manifest includes the <code>\"tabs\"</code> permission. It may also be an empty string if the tab is loading."}, "status": {"type": "string", "optional": true, "description": "Either <em>loading</em> or <em>complete</em>."}, + "discarded": {"type": "boolean", "optional": true, "description": "True while the tab is not loaded with content."}, "incognito": {"type": "boolean", "description": "Whether the tab is in an incognito window."}, "width": {"type": "integer", "optional": true, "description": "The width of the tab in pixels."}, "height": {"type": "integer", "optional": true, "description": "The height of the tab in pixels."}, "sessionId": {"type": "string", "optional": true, "description": "The session ID used to uniquely identify a Tab obtained from the $(ref:sessions) API."}, "cookieStoreId": {"type": "string", "optional": true, "description": "The CookieStoreId used for the tab."} } }, { @@ -489,16 +490,21 @@ "optional": true, "description": "Whether the tabs are in the last focused window." }, "status": { "$ref": "TabStatus", "optional": true, "description": "Whether the tabs have completed loading." }, + "discarded": { + "type": "boolean", + "optional": true, + "description": "True while the tabs are not loaded with content." + }, "title": { "type": "string", "optional": true, "description": "Match page titles against a pattern." }, "url": { "choices": [ {"type": "string"}, @@ -1051,16 +1057,21 @@ "name": "changeInfo", "description": "Lists the changes to the state of the tab that was updated.", "properties": { "status": { "type": "string", "optional": true, "description": "The status of the tab. Can be either <em>loading</em> or <em>complete</em>." }, + "discarded": { + "type": "boolean", + "optional": true, + "description": "True while the tab is not loaded with content." + }, "url": { "type": "string", "optional": true, "description": "The tab's URL if it has changed." }, "pinned": { "type": "boolean", "optional": true,
--- a/modules/libpref/Preferences.cpp +++ b/modules/libpref/Preferences.cpp @@ -5,19 +5,22 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "mozilla/MemoryReporting.h" #include "mozilla/dom/PContent.h" #include "mozilla/ArrayUtils.h" #include "mozilla/Attributes.h" #include "mozilla/HashFunctions.h" +#include "mozilla/ResultExtensions.h" +#include "mozilla/ScopeExit.h" #include "mozilla/ServoStyleSet.h" #include "mozilla/SyncRunnable.h" #include "mozilla/Telemetry.h" +#include "mozilla/URLPreloader.h" #include "mozilla/UniquePtrExtensions.h" #include "nsXULAppAPI.h" #include "mozilla/Preferences.h" #include "nsAppDirectoryServiceDefs.h" #include "nsDataHashtable.h" #include "nsDirectoryServiceDefs.h" @@ -1260,56 +1263,30 @@ Preferences::WritePrefFile(nsIFile* aFil // as AllowOffMainThreadSave() returns a consistent value for the // lifetime of the parent process. PrefSaveData prefsData = pref_savePrefs(gHashTable); return PreferencesWriter::Write(aFile, prefsData); } static nsresult openPrefFile(nsIFile* aFile) { - nsCOMPtr<nsIInputStream> inStr; - - nsresult rv = NS_NewLocalFileInputStream(getter_AddRefs(inStr), aFile); - if (NS_FAILED(rv)) - return rv; - - int64_t fileSize64; - rv = aFile->GetFileSize(&fileSize64); - if (NS_FAILED(rv)) - return rv; - NS_ENSURE_TRUE(fileSize64 <= UINT32_MAX, NS_ERROR_FILE_TOO_BIG); - - uint32_t fileSize = (uint32_t)fileSize64; - auto fileBuffer = MakeUniqueFallible<char[]>(fileSize); - if (fileBuffer == nullptr) - return NS_ERROR_OUT_OF_MEMORY; - PrefParseState ps; PREF_InitParseState(&ps, PREF_ReaderCallback, ReportToConsole, nullptr); + auto cleanup = MakeScopeExit([&] () { + PREF_FinalizeParseState(&ps); + }); - // Read is not guaranteed to return a buf the size of fileSize, - // but usually will. - nsresult rv2 = NS_OK; - uint32_t offset = 0; - for (;;) { - uint32_t amtRead = 0; - rv = inStr->Read(fileBuffer.get(), fileSize, &amtRead); - if (NS_FAILED(rv) || amtRead == 0) - break; - if (!PREF_ParseBuf(&ps, fileBuffer.get(), amtRead)) - rv2 = NS_ERROR_FILE_CORRUPTED; - offset += amtRead; - if (offset == fileSize) { - break; - } + nsCString data; + MOZ_TRY_VAR(data, URLPreloader::ReadFile(aFile)); + if (!PREF_ParseBuf(&ps, data.get(), data.Length())) { + return NS_ERROR_FILE_CORRUPTED; } - PREF_FinalizeParseState(&ps); + return NS_OK; - return NS_FAILED(rv) ? rv : rv2; } /* * some stuff that gets called from Pref_Init() */ static int pref_CompareFileNames(nsIFile* aFile1, nsIFile* aFile2, void* /*unused*/) @@ -1456,22 +1433,22 @@ static nsresult pref_LoadPrefsInDirList( else pref_LoadPrefsInDir(path, nullptr, 0); } return NS_OK; } static nsresult pref_ReadPrefFromJar(nsZipArchive* jarReader, const char *name) { - nsZipItemPtr<char> manifest(jarReader, name, true); - NS_ENSURE_TRUE(manifest.Buffer(), NS_ERROR_NOT_AVAILABLE); + nsCString manifest; + MOZ_TRY_VAR(manifest, URLPreloader::ReadZip(jarReader, nsDependentCString(name))); PrefParseState ps; PREF_InitParseState(&ps, PREF_ReaderCallback, ReportToConsole, nullptr); - PREF_ParseBuf(&ps, manifest, manifest.Length()); + PREF_ParseBuf(&ps, manifest.get(), manifest.Length()); PREF_FinalizeParseState(&ps); return NS_OK; } //---------------------------------------------------------------------------------------- // Initialize default preference JavaScript buffers from // appropriate TEXT resources
--- a/netwerk/protocol/http/HttpChannelChild.cpp +++ b/netwerk/protocol/http/HttpChannelChild.cpp @@ -2299,16 +2299,28 @@ HttpChannelChild::AsyncOpen(nsIStreamLis mLoadInfo->GetSecurityMode() == 0 || mLoadInfo->GetInitialSecurityCheckDone() || (mLoadInfo->GetSecurityMode() == nsILoadInfo::SEC_ALLOW_CROSS_ORIGIN_DATA_IS_NULL && nsContentUtils::IsSystemPrincipal(mLoadInfo->LoadingPrincipal())), "security flags in loadInfo but asyncOpen2() not called"); LOG(("HttpChannelChild::AsyncOpen [this=%p uri=%s]\n", this, mSpec.get())); + if (LOG4_ENABLED()) { + JSContext* cx = nsContentUtils::GetCurrentJSContext(); + if (cx) { + nsAutoCString fileNameString; + uint32_t line = 0, col = 0; + if (nsJSUtils::GetCallingLocation(cx, fileNameString, &line, &col)) { + LOG(("HttpChannelChild %p source script=%s:%u:%u", + this, fileNameString.get(), line, col)); + } + } + } + #ifdef DEBUG AssertPrivateBrowsingId(); #endif if (mCanceled) return mStatus; NS_ENSURE_TRUE(gNeckoChild != nullptr, NS_ERROR_FAILURE);
--- a/toolkit/components/extensions/ext-tabs-base.js +++ b/toolkit/components/extensions/ext-tabs-base.js @@ -486,16 +486,17 @@ class TabBase { let result = { id: this.id, index: this.index, windowId: this.windowId, highlighted: this.selected, active: this.selected, pinned: this.pinned, status: this.status, + discarded: this.discarded, incognito: this.incognito, width: this.width, height: this.height, lastAccessed: this.lastAccessed, audible: this.audible, mutedInfo: this.mutedInfo, };
--- a/toolkit/components/passwordmgr/LoginManagerContent.jsm +++ b/toolkit/components/passwordmgr/LoginManagerContent.jsm @@ -32,16 +32,20 @@ XPCOMUtils.defineLazyServiceGetter(this, "@mozilla.org/network/util;1", "nsINetUtil"); XPCOMUtils.defineLazyGetter(this, "log", () => { let logger = LoginHelper.createLogger("LoginManagerContent"); return logger.log.bind(logger); }); +Services.cpmm.addMessageListener("clearRecipeCache", () => { + LoginRecipesContent._clearRecipeCache(); +}); + // These mirror signon.* prefs. var gEnabled, gAutofillForms, gStoreWhenAutocompleteOff; var gLastRightClickTimeStamp = Number.NEGATIVE_INFINITY; var observer = { QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, Ci.nsIFormSubmitObserver, Ci.nsIWebProgressListener, @@ -555,16 +559,19 @@ var LoginManagerContent = { userTriggered: true, }); }, loginsFound({ form, loginsFound, recipes }) { let doc = form.ownerDocument; let autofillForm = gAutofillForms && !PrivateBrowsingUtils.isContentWindowPrivate(doc.defaultView); + let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI); + LoginRecipesContent.cacheRecipes(formOrigin, doc.defaultView, recipes); + this._fillForm(form, loginsFound, recipes, {autofillForm}); }, /** * Focus event handler for username fields to decide whether to show autocomplete. * @param {FocusEvent} event */ _onUsernameFocus(event) { @@ -626,20 +633,18 @@ var LoginManagerContent = { // fillForm() to try filling in a login without a username // to filter on (bug 471906). if (!acInputField.value) return; log("onUsernameInput from", event.type); let doc = acForm.ownerDocument; - let messageManager = messageManagerFromWindow(doc.defaultView); - let recipes = messageManager.sendSyncMessage("RemoteLogins:findRecipes", { - formOrigin: LoginUtils._getPasswordOrigin(doc.documentURI), - })[0]; + let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI); + let recipes = LoginRecipesContent.getRecipes(formOrigin, doc.defaultView); // Make sure the username field fillForm will use is the // same field as the autocomplete was activated on. var [usernameField, passwordField, ignored] = this._getFormFields(acForm, false, recipes); if (usernameField == acInputField && passwordField) { this._getLoginDataFromParent(acForm, { showMasterPassword: false }) .then(({ form, loginsFound, recipes }) => { @@ -923,19 +928,17 @@ var LoginManagerContent = { if (!hostname) { log("(form submission ignored -- invalid hostname)"); return; } let formSubmitURL = LoginUtils._getActionOrigin(form); let messageManager = messageManagerFromWindow(win); - let recipes = messageManager.sendSyncMessage("RemoteLogins:findRecipes", { - formOrigin: hostname, - })[0]; + let recipes = LoginRecipesContent.getRecipes(hostname, win); // Get the appropriate fields from the form. var [usernameField, newPasswordField, oldPasswordField] = this._getFormFields(form, true, recipes); // Need at least 1 valid password field to do anything. if (newPasswordField == null) return; @@ -1340,20 +1343,18 @@ var LoginManagerContent = { if (!(aField instanceof Ci.nsIDOMHTMLInputElement) || (aField.type != "password" && !LoginHelper.isUsernameFieldType(aField)) || !aField.ownerDocument) { return null; } let form = LoginFormFactory.createFromField(aField); let doc = aField.ownerDocument; - let messageManager = messageManagerFromWindow(doc.defaultView); - let recipes = messageManager.sendSyncMessage("RemoteLogins:findRecipes", { - formOrigin: LoginUtils._getPasswordOrigin(doc.documentURI), - })[0]; + let formOrigin = LoginUtils._getPasswordOrigin(doc.documentURI); + let recipes = LoginRecipesContent.getRecipes(formOrigin, doc.defaultView); let [usernameField, newPasswordField] = this._getFormFields(form, false, recipes); // If we are not verifying a password field, we want // to use aField as the username field. if (aField.type != "password") { usernameField = aField;
--- a/toolkit/components/passwordmgr/LoginRecipes.jsm +++ b/toolkit/components/passwordmgr/LoginRecipes.jsm @@ -108,16 +108,17 @@ LoginRecipesParent.prototype = { if (!Components.isSuccessCode(result)) { throw new Error("Error fetching recipe file:" + result); } let count = stream.available(); let data = NetUtil.readInputStreamToString(stream, count, { charset: "UTF-8" }); resolve(JSON.parse(data)); }); }).then(recipes => { + Services.ppmm.broadcastAsyncMessage("clearRecipeCache"); return this.load(recipes); }).then(resolve => { return this; }); } catch (e) { throw new Error("Error reading recipe file:" + e); } } else { @@ -184,16 +185,72 @@ LoginRecipesParent.prototype = { } return hostRecipes; }, }; var LoginRecipesContent = { + _recipeCache: new WeakMap(), + + _clearRecipeCache() { + this._recipeCache = new WeakMap(); + }, + + /** + * Locally caches recipes for a given host. + * + * @param {String} aHost (e.g. example.com:8080 [non-default port] or sub.example.com) + * @param {Object} win - the window of the host + * @param {Set} recipes - recipes that apply to the host + */ + cacheRecipes(aHost, win, recipes) { + let recipeMap = this._recipeCache.get(win); + + if (!recipeMap) { + recipeMap = new Map(); + this._recipeCache.set(win, recipeMap); + } + + recipeMap.set(aHost, recipes); + }, + + /** + * Tries to fetch recipes for a given host, using a local cache if possible. + * Otherwise, the recipes are cached for later use. + * + * @param {String} aHost (e.g. example.com:8080 [non-default port] or sub.example.com) + * @param {Object} win - the window of the host + * @return {Set} of recipes that apply to the host + */ + getRecipes(aHost, win) { + let recipes; + let recipeMap = this._recipeCache.get(win); + + if (recipeMap) { + recipes = recipeMap.get(aHost); + + if (recipes) { + return recipes; + } + } + + let mm = win.QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIWebNavigation) + .QueryInterface(Ci.nsIDocShell) + .QueryInterface(Ci.nsIInterfaceRequestor) + .getInterface(Ci.nsIContentFrameMessageManager); + + recipes = mm.sendSyncMessage("RemoteLogins:findRecipes", { formOrigin: aHost })[0]; + this.cacheRecipes(aHost, win, recipes); + + return recipes; + }, + /** * @param {Set} aRecipes - Possible recipes that could apply to the form * @param {FormLike} aForm - We use a form instead of just a URL so we can later apply * tests to the page contents. * @return {Set} a subset of recipes that apply to the form with the order preserved */ _filterRecipesForForm(aRecipes, aForm) { let formDocURL = aForm.ownerDocument.location;
--- a/toolkit/components/passwordmgr/test/mochitest/mochitest.ini +++ b/toolkit/components/passwordmgr/test/mochitest/mochitest.ini @@ -47,16 +47,17 @@ skip-if = toolkit == 'android' # autocom [test_form_action_javascript.html] [test_formless_autofill.html] [test_formless_submit.html] [test_formless_submit_navigation.html] [test_formless_submit_navigation_negative.html] [test_input_events.html] [test_input_events_for_identical_values.html] [test_maxlength.html] +[test_onsubmit_value_change.html] [test_passwords_in_type_password.html] [test_prompt.html] skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts [test_prompt_http.html] skip-if = os == "linux" || toolkit == 'android' # Tests desktop prompts [test_prompt_noWindow.html] skip-if = e10s || toolkit == 'android' # Tests desktop prompts. e10s: bug 1217876 [test_prompt_promptAuth.html]
--- a/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html +++ b/toolkit/components/passwordmgr/test/mochitest/test_basic_form_autocomplete.html @@ -157,29 +157,29 @@ var setupScript = runInParent(function s <!-- test autocomplete dropdown --> <form id="form9" action="http://autocomplete5" onsubmit="return false;"> <input type="text" name="uname"> <input type="password" name="pword"> <button type="submit">Submit</button> </form> + <!-- tests <form>-less autocomplete --> + <div id="form11"> + <input type="text" name="uname" id="uname"> + <input type="password" name="pword" id="pword"> + <button type="submit">Submit</button> + </div> + <!-- test for onUsernameInput recipe testing --> - <form id="form11" action="http://autocomplete7" onsubmit="return false;"> + <form id="form12" action="http://autocomplete7" onsubmit="return false;"> <input type="text" name="1"> <input type="text" name="2"> <button type="submit">Submit</button> </form> - - <!-- tests <form>-less autocomplete --> - <div id="form12"> - <input type="text" name="uname" id="uname"> - <input type="password" name="pword" id="pword"> - <button type="submit">Submit</button> - </div> </div> <pre id="test"> <script class="testbody" type="text/javascript"> /** Test for Login Manager: multiple login autocomplete. **/ var uname = $_(1, "uname"); @@ -766,26 +766,72 @@ add_task(async function test_form9_autoc // check that empty results are cached - bug 496466 promise0 = notifyMenuChanged(0); sendChar("z"); await promise0; popupState = await getPopupState(); is(popupState.open, false, "Check popup stays closed due to cached empty result"); }); -add_task(async function test_form11_recipes() { +add_task(async function test_form11_formless() { + // Test form-less autocomplete + uname = $_(11, "uname"); + pword = $_(11, "pword"); + restoreForm(); + checkACForm("", ""); + let shownPromise = promiseACShown(); + doKey("down"); // open + await shownPromise; + + // Trigger autocomplete + doKey("down"); + checkACForm("", ""); // value shouldn't update + let processedPromise = promiseFormsProcessed(); + doKey("return"); // not "enter"! + await processedPromise; + checkACForm("testuser", "testpass"); +}); + +add_task(async function test_form11_open_on_trusted_focus() { + uname = $_(11, "uname"); + pword = $_(11, "pword"); + uname.value = ""; + pword.value = ""; + + // Move focus to the password field so we can test the first click on the + // username field. + pword.focus(); + checkACForm("", ""); + const firePrivEventPromise = new Promise((resolve) => { + uname.addEventListener("click", (e) => { + ok(e.isTrusted, "Ensure event is trusted"); + resolve(); + }); + }); + const shownPromise = promiseACShown(); + synthesizeMouseAtCenter(uname, {}); + await firePrivEventPromise; + await shownPromise; + doKey("down"); + const processedPromise = promiseFormsProcessed(); + doKey("return"); // not "enter"! + await processedPromise; + checkACForm("testuser", "testpass"); +}); + +add_task(async function test_form12_recipes() { await loadRecipes({ siteRecipes: [{ "hosts": ["mochi.test:8888"], "usernameSelector": "input[name='1']", "passwordSelector": "input[name='2']" }], }); - uname = $_(11, "1"); - pword = $_(11, "2"); + uname = $_(12, "1"); + pword = $_(12, "2"); // First test DOMAutocomplete // Switch the password field to type=password so _fillForm marks the username // field for autocomplete. pword.type = "password"; await promiseFormsProcessed(); restoreForm(); checkACForm("", ""); @@ -804,58 +850,12 @@ add_task(async function test_form11_reci checkACForm("", ""); uname.value = "testuser10"; checkACForm("testuser10", ""); doKey("tab"); await promiseFormsProcessed(); checkACForm("testuser10", "testpass10"); await resetRecipes(); }); - -add_task(async function test_form12_formless() { - // Test form-less autocomplete - uname = $_(12, "uname"); - pword = $_(12, "pword"); - restoreForm(); - checkACForm("", ""); - let shownPromise = promiseACShown(); - doKey("down"); // open - await shownPromise; - - // Trigger autocomplete - doKey("down"); - checkACForm("", ""); // value shouldn't update - let processedPromise = promiseFormsProcessed(); - doKey("return"); // not "enter"! - await processedPromise; - checkACForm("testuser", "testpass"); -}); - -add_task(async function test_form12_open_on_trusted_focus() { - uname = $_(12, "uname"); - pword = $_(12, "pword"); - uname.value = ""; - pword.value = ""; - - // Move focus to the password field so we can test the first click on the - // username field. - pword.focus(); - checkACForm("", ""); - const firePrivEventPromise = new Promise((resolve) => { - uname.addEventListener("click", (e) => { - ok(e.isTrusted, "Ensure event is trusted"); - resolve(); - }); - }); - const shownPromise = promiseACShown(); - synthesizeMouseAtCenter(uname, {}); - await firePrivEventPromise; - await shownPromise; - doKey("down"); - const processedPromise = promiseFormsProcessed(); - doKey("return"); // not "enter"! - await processedPromise; - checkACForm("testuser", "testpass"); -}); </script> </pre> </body> </html>
new file mode 100644 --- /dev/null +++ b/toolkit/components/passwordmgr/test/mochitest/test_onsubmit_value_change.html @@ -0,0 +1,67 @@ +<!DOCTYPE HTML> +<html> +<head> + <meta charset="utf-8"> + <title>Test input value change right after onsubmit event</title> + <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> + <script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script> + <script type="text/javascript" src="pwmgr_common.js"></script> + <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> +</head> +<body> +Login Manager test: input value change right after onsubmit event + +<script> + let chromeScript = runChecksAfterCommonInit(); + + function getSubmitMessage() { + info("getSubmitMessage"); + return new Promise((resolve, reject) => { + chromeScript.addMessageListener("formSubmissionProcessed", function processed(...args) { + info("got formSubmissionProcessed"); + chromeScript.removeMessageListener("formSubmissionProcessed", processed); + resolve(...args); + }); + }); + } +</script> +<p id="display"></p> + +<div id="content" style="display: none"> + + <form id="form1" action="formTest.js" onsubmit="return false;"> + <input type="text" name="uname" id="ufield"> + <input type="password" name="pword" id="pfield"> + <button type="submit" id="submitBtn">Submit</button> + </form> + +</div> + +<pre id="test"></pre> +<script> + /** Test for Login Manager: input value change right after onsubmit event **/ + add_task(async function checkFormValues() { + document.getElementById("ufield").value = "testuser"; + document.getElementById("pfield").value = "testpass"; + is($_(1, "uname").value, "testuser", "Checking for filled username"); + is($_(1, "pword").value, "testpass", "Checking for filled password"); + + document.getElementById("form1").addEventListener("submit", () => { + document.getElementById("ufield").value = "newuser"; + document.getElementById("pfield").value = "newpass"; + }, true); + + document.getElementById("form1").addEventListener("submit", (e) => e.preventDefault()); + + let processedPromise = getSubmitMessage(); + + let button = document.getElementById("submitBtn"); + button.click(); + + let submittedResult = await processedPromise; + is(submittedResult.usernameField.value, "testuser", "Should have registered \"testuser\" for username"); + is(submittedResult.newPasswordField.value, "testpass", "Should have registered \"testpass\" for username"); + }); +</script> +</body> +</html>
--- a/toolkit/components/telemetry/Histograms.json +++ b/toolkit/components/telemetry/Histograms.json @@ -8045,20 +8045,23 @@ "SUBJECT_PRINCIPAL_ACCESSED_WITHOUT_SCRIPT_ON_STACK": { "record_in_processes": ["main", "content"], "expires_in_version": "46", "alert_emails": ["bholley@mozilla.com"], "kind": "flag", "description": "Whether the subject principal was accessed without script on the stack during the current session" }, "TOUCH_ENABLED_DEVICE": { - "record_in_processes": ["main", "content"], - "expires_in_version": "never", - "kind": "boolean", - "description": "The device supports touch input", + "record_in_processes": ["main"], + "expires_in_version": "never", + "kind": "boolean", + "releaseChannelCollection": "opt-out", + "bug_numbers": [795307, 1390269], + "alert_emails": ["jimm@mozilla.com"], + "description": "Boolean indicating if a touch input device is detected.", "cpp_guard": "XP_WIN" }, "COMPONENTS_SHIM_ACCESSED_BY_CONTENT": { "record_in_processes": ["main", "content"], "expires_in_version": "never", "kind": "flag", "description": "Whether content ever accesed the Components shim in this session" },
--- a/toolkit/components/telemetry/histogram-whitelists.json +++ b/toolkit/components/telemetry/histogram-whitelists.json @@ -544,17 +544,16 @@ "SYSTEM_FONT_FALLBACK_SCRIPT", "TAP_TO_LOAD_ENABLED", "TAP_TO_LOAD_IMAGE_SIZE", "THUNDERBIRD_CONVERSATIONS_TIME_TO_2ND_GLODA_QUERY_MS", "THUNDERBIRD_GLODA_SIZE_MB", "THUNDERBIRD_INDEXING_RATE_MSG_PER_S", "TLS_ERROR_REPORT_UI", "TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED", - "TOUCH_ENABLED_DEVICE", "TRANSLATED_CHARACTERS", "TRANSLATED_PAGES", "TRANSLATED_PAGES_BY_LANGUAGE", "TRANSLATION_OPPORTUNITIES", "TRANSLATION_OPPORTUNITIES_BY_LANGUAGE", "VIDEO_CANPLAYTYPE_H264_CONSTRAINT_SET_FLAG", "VIDEO_CANPLAYTYPE_H264_LEVEL", "VIDEO_CANPLAYTYPE_H264_PROFILE", @@ -1347,17 +1346,16 @@ "THUNDERBIRD_CONVERSATIONS_TIME_TO_2ND_GLODA_QUERY_MS", "THUNDERBIRD_GLODA_SIZE_MB", "THUNDERBIRD_INDEXING_RATE_MSG_PER_S", "TLS_ERROR_REPORT_UI", "TOP_LEVEL_CONTENT_DOCUMENTS_DESTROYED", "TOTAL_CONTENT_PAGE_LOAD_TIME", "TOTAL_COUNT_HIGH_ERRORS", "TOTAL_COUNT_LOW_ERRORS", - "TOUCH_ENABLED_DEVICE", "TRANSACTION_WAIT_TIME_HTTP", "TRANSACTION_WAIT_TIME_SPDY", "TRANSLATED_CHARACTERS", "TRANSLATED_PAGES", "TRANSLATED_PAGES_BY_LANGUAGE", "TRANSLATION_OPPORTUNITIES", "TRANSLATION_OPPORTUNITIES_BY_LANGUAGE", "UPDATE_CANNOT_STAGE_EXTERNAL",
--- a/toolkit/components/xulstore/XULStore.js +++ b/toolkit/components/xulstore/XULStore.js @@ -80,31 +80,22 @@ XULStore.prototype = { log(message) { if (!debugMode) return; dump("XULStore: " + message + "\n"); Services.console.logStringMessage("XULStore: " + message); }, readFile() { - const MODE_RDONLY = 0x01; - const FILE_PERMS = 0o600; - - let stream = Cc["@mozilla.org/network/file-input-stream;1"]. - createInstance(Ci.nsIFileInputStream); - let json = Cc["@mozilla.org/dom/json;1"].createInstance(Ci.nsIJSON); try { - stream.init(this._storeFile, MODE_RDONLY, FILE_PERMS, 0); - this._data = json.decodeFromStream(stream, stream.available()); + this._data = JSON.parse(Cu.readFile(this._storeFile)); } catch (e) { this.log("Error reading JSON: " + e); // This exception could mean that the file didn't exist. // We'll just ignore the error and start with a blank slate. - } finally { - stream.close(); } }, async writeFile() { if (!this._needsSaving) return; this._needsSaving = false;
--- a/toolkit/mozapps/extensions/AddonManagerStartup.cpp +++ b/toolkit/mozapps/extensions/AddonManagerStartup.cpp @@ -14,16 +14,17 @@ #include "mozilla/ClearOnShutdown.h" #include "mozilla/EndianUtils.h" #include "mozilla/Compression.h" #include "mozilla/LinkedList.h" #include "mozilla/Preferences.h" #include "mozilla/ResultExtensions.h" #include "mozilla/ScopeExit.h" #include "mozilla/Services.h" +#include "mozilla/URLPreloader.h" #include "mozilla/Unused.h" #include "mozilla/ErrorResult.h" #include "mozilla/dom/ipc/StructuredCloneData.h" #include "nsAppDirectoryServiceDefs.h" #include "nsAppRunner.h" #include "nsContentUtils.h" #include "nsChromeRegistry.h" @@ -99,38 +100,16 @@ CloneAndAppend(nsIFile* aFile, const cha static bool IsNormalFile(nsIFile* file) { bool result; return NS_SUCCEEDED(file->IsFile(&result)) && result; } -static Result<nsCString, nsresult> -ReadFile(nsIFile* file) -{ - nsCString result; - - AutoFDClose fd; - MOZ_TRY(file->OpenNSPRFileDesc(PR_RDONLY, 0, &fd.rwget())); - - auto size = PR_Seek64(fd, 0, PR_SEEK_END); - PR_Seek64(fd, 0, PR_SEEK_SET); - - result.SetLength(size); - - auto len = PR_Read(fd, result.BeginWriting(), size); - - if (size != len) { - return Err(NS_ERROR_FAILURE); - } - - return result; -} - static const char STRUCTURED_CLONE_MAGIC[] = "mozJSSCLz40v001"; template <typename T> static Result<nsCString, nsresult> DecodeLZ4(const nsACString& lz4, const T& magicNumber) { constexpr auto HEADER_SIZE = sizeof(magicNumber) + 4; @@ -184,29 +163,24 @@ EncodeLZ4(const nsACString& data, const } static_assert(sizeof STRUCTURED_CLONE_MAGIC % 8 == 0, "Magic number should be an array of uint64_t"); /** * Reads the contents of a LZ4-compressed file, as stored by the OS.File * module, and returns the decompressed contents on success. - * - * A nonexistent or empty file is treated as success. A corrupt or non-LZ4 - * file is treated as failure. */ static Result<nsCString, nsresult> ReadFileLZ4(nsIFile* file) { static const char MAGIC_NUMBER[] = "mozLz40"; - nsCString result; - nsCString lz4; - MOZ_TRY_VAR(lz4, ReadFile(file)); + MOZ_TRY_VAR(lz4, URLPreloader::ReadFile(file)); if (lz4.IsEmpty()) { return lz4; } return DecodeLZ4(lz4, MAGIC_NUMBER); } @@ -581,17 +555,22 @@ AddonManagerStartup::AddInstallLocation( nsresult AddonManagerStartup::ReadStartupData(JSContext* cx, JS::MutableHandleValue locations) { locations.set(JS::UndefinedValue()); nsCOMPtr<nsIFile> file = CloneAndAppend(ProfileDir(), "addonStartup.json.lz4"); nsCString data; - MOZ_TRY_VAR(data, ReadFileLZ4(file)); + auto res = ReadFileLZ4(file); + if (res.isOk()) { + data = res.unwrap(); + } else if (res.unwrapErr() != NS_ERROR_FILE_NOT_FOUND) { + return res.unwrapErr(); + } if (data.IsEmpty() || !ParseJSON(cx, data, locations)) { return NS_OK; } if (!locations.isObject()) { return NS_ERROR_UNEXPECTED; }
--- a/toolkit/xre/nsAppRunner.cpp +++ b/toolkit/xre/nsAppRunner.cpp @@ -976,19 +976,26 @@ nsXULAppInfo::GetAccessibleHandlerUsed(b #endif return NS_OK; } NS_IMETHODIMP nsXULAppInfo::GetAccessibilityInstantiator(nsAString &aInstantiator) { #if defined(ACCESSIBILITY) && defined(XP_WIN) - if (!a11y::GetInstantiator(aInstantiator)) { + if (!GetAccService()) { aInstantiator = NS_LITERAL_STRING(""); - } + return NS_OK; + } + nsAutoString oopClientInfo, ipClientInfo; + a11y::Compatibility::GetHumanReadableConsumersStr(ipClientInfo); + aInstantiator.Append(ipClientInfo); + aInstantiator.AppendLiteral("|"); + a11y::GetInstantiator(oopClientInfo); + aInstantiator.Append(oopClientInfo); #else aInstantiator = NS_LITERAL_STRING(""); #endif return NS_OK; } NS_IMETHODIMP nsXULAppInfo::GetIs64Bit(bool* aResult) @@ -1002,17 +1009,18 @@ nsXULAppInfo::GetIs64Bit(bool* aResult) } NS_IMETHODIMP nsXULAppInfo::EnsureContentProcess() { if (!XRE_IsParentProcess()) return NS_ERROR_NOT_AVAILABLE; - RefPtr<ContentParent> unused = ContentParent::GetNewOrUsedBrowserProcess(); + RefPtr<ContentParent> unused = ContentParent::GetNewOrUsedBrowserProcess( + NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)); return NS_OK; } NS_IMETHODIMP nsXULAppInfo::InvalidateCachesOnRestart() { nsCOMPtr<nsIFile> file; nsresult rv = NS_GetSpecialDirectory(NS_APP_PROFILE_DIR_STARTUP,
--- a/toolkit/xre/nsEmbedFunctions.cpp +++ b/toolkit/xre/nsEmbedFunctions.cpp @@ -896,17 +896,23 @@ XRE_ShutdownChildProcess() #endif // XP_MACOSX } namespace { ContentParent* gContentParent; //long-lived, manually refcounted TestShellParent* GetOrCreateTestShellParent() { if (!gContentParent) { - RefPtr<ContentParent> parent = ContentParent::GetNewOrUsedBrowserProcess(); + // Use a "web" child process by default. File a bug if you don't like + // this and you're sure you wouldn't be better off writing a "browser" + // chrome mochitest where you can have multiple types of content + // processes. + RefPtr<ContentParent> parent = + ContentParent::GetNewOrUsedBrowserProcess( + NS_LITERAL_STRING(DEFAULT_REMOTE_TYPE)); parent.forget(&gContentParent); } else if (!gContentParent->IsAlive()) { return nullptr; } TestShellParent* tsp = gContentParent->GetTestShellSingleton(); if (!tsp) { tsp = gContentParent->CreateTestShell(); }
--- a/toolkit/xre/test/win/TestDllInterceptor.cpp +++ b/toolkit/xre/test/win/TestDllInterceptor.cpp @@ -427,16 +427,24 @@ bool TestTlsAlloc(void* aFunc) bool TestTlsFree(void* aFunc) { auto patchedTlsFree = reinterpret_cast<decltype(&TlsFree)>(aFunc); return sTlsIndex != 0 && patchedTlsFree(sTlsIndex); } +bool TestPrintDlgW(void* aFunc) +{ + auto patchedPrintDlgW = + reinterpret_cast<decltype(&PrintDlgW)>(aFunc); + patchedPrintDlgW(0); + return true; +} + int main() { payload initial = { 0x12345678, 0xfc4e9d31, 0x87654321 }; payload p0, p1; ZeroMemory(&p0, sizeof(p0)); ZeroMemory(&p1, sizeof(p1)); p0 = rotatePayload(initial); @@ -526,16 +534,17 @@ int main() TestHook(TestImmNotifyIME, "imm32.dll", "ImmNotifyIME") && TestHook(TestGetSaveFileNameW, "comdlg32.dll", "GetSaveFileNameW") && TestHook(TestGetOpenFileNameW, "comdlg32.dll", "GetOpenFileNameW") && #ifdef _M_X64 TestHook(TestGetKeyState, "user32.dll", "GetKeyState") && // see Bug 1316415 TestHook(TestLdrUnloadDll, "ntdll.dll", "LdrUnloadDll") && MaybeTestHook(IsWin8OrLater(), TestLdrResolveDelayLoadedAPI, "ntdll.dll", "LdrResolveDelayLoadedAPI") && MaybeTestHook(!IsWin8OrLater(), TestRtlInstallFunctionTableCallback, "kernel32.dll", "RtlInstallFunctionTableCallback") && + TestHook(TestPrintDlgW, "comdlg32.dll", "PrintDlgW") && #endif MaybeTestHook(ShouldTestTipTsf(), TestProcessCaretEvents, "tiptsf.dll", "ProcessCaretEvents") && #ifdef _M_IX86 TestHook(TestSendMessageTimeoutW, "user32.dll", "SendMessageTimeoutW") && #endif TestHook(TestSetCursorPos, "user32.dll", "SetCursorPos") && TestHook(TestTlsAlloc, "kernel32.dll", "TlsAlloc") && TestHook(TestTlsFree, "kernel32.dll", "TlsFree") &&
--- a/xpcom/base/nsINIParser.cpp +++ b/xpcom/base/nsINIParser.cpp @@ -5,30 +5,26 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ // Moz headers (alphabetical) #include "nsCRTGlue.h" #include "nsError.h" #include "nsIFile.h" #include "nsINIParser.h" #include "mozilla/FileUtils.h" // AutoFILE +#include "mozilla/ResultExtensions.h" +#include "mozilla/URLPreloader.h" // System headers (alphabetical) #include <stdio.h> #include <stdlib.h> #ifdef XP_WIN #include <windows.h> #endif -#if defined(XP_WIN) -#define READ_BINARYMODE L"rb" -#else -#define READ_BINARYMODE "r" -#endif - using namespace mozilla; #ifdef XP_WIN inline FILE* TS_tfopen(const char* aPath, const wchar_t* aMode) { wchar_t wPath[MAX_PATH]; MultiByteToWideChar(CP_UTF8, 0, aPath, -1, wPath, MAX_PATH); @@ -60,134 +56,60 @@ public: void operator=(FILE* aFp) { fp_ = aFp; } private: FILE* fp_; }; nsresult nsINIParser::Init(nsIFile* aFile) { - /* open the file. Don't use OpenANSIFileDesc, because you mustn't - pass FILE* across shared library boundaries, which may be using - different CRTs */ - - AutoFILE fd; - -#ifdef XP_WIN - nsAutoString path; - nsresult rv = aFile->GetPath(path); - if (NS_WARN_IF(NS_FAILED(rv))) { - return rv; - } + nsCString result; + MOZ_TRY_VAR(result, URLPreloader::ReadFile(aFile)); - fd = _wfopen(path.get(), READ_BINARYMODE); -#else - nsAutoCString path; - aFile->GetNativePath(path); - - fd = fopen(path.get(), READ_BINARYMODE); -#endif - - if (!fd) { - return NS_ERROR_FAILURE; - } - - return InitFromFILE(fd); + return InitFromString(result); } nsresult nsINIParser::Init(const char* aPath) { - /* open the file */ - AutoFILE fd(TS_tfopen(aPath, READ_BINARYMODE)); - if (!fd) { - return NS_ERROR_FAILURE; - } + nsCString result; + MOZ_TRY_VAR(result, URLPreloader::ReadFile(nsDependentCString(aPath))); - return InitFromFILE(fd); + return InitFromString(result); } static const char kNL[] = "\r\n"; static const char kEquals[] = "="; static const char kWhitespace[] = " \t"; static const char kRBracket[] = "]"; nsresult -nsINIParser::InitFromFILE(FILE* aFd) +nsINIParser::InitFromString(const nsCString& aStr) { - /* get file size */ - if (fseek(aFd, 0, SEEK_END) != 0) { - return NS_ERROR_FAILURE; - } - - long flen = ftell(aFd); - /* zero-sized file, or an error */ - if (flen <= 0) { - return NS_ERROR_FAILURE; - } - - /* malloc an internal buf the size of the file */ - mFileContents = MakeUnique<char[]>(flen + 2); - if (!mFileContents) { - return NS_ERROR_OUT_OF_MEMORY; - } + char* buffer; - /* read the file in one swoop */ - if (fseek(aFd, 0, SEEK_SET) != 0) { - return NS_BASE_STREAM_OSERROR; - } - - int rd = fread(mFileContents.get(), sizeof(char), flen, aFd); - if (rd != flen) { - return NS_BASE_STREAM_OSERROR; - } - - // We write a UTF16 null so that the file is easier to convert to UTF8 - mFileContents[flen] = mFileContents[flen + 1] = '\0'; - - char* buffer = &mFileContents[0]; - - if (flen >= 3 && - mFileContents[0] == '\xEF' && - mFileContents[1] == '\xBB' && - mFileContents[2] == '\xBF') { + if (StringHead(aStr, 3) == "\xEF\xBB\xBF") { // Someone set us up the Utf-8 BOM // This case is easy, since we assume that BOM-less // files are Utf-8 anyway. Just skip the BOM and process as usual. - buffer = &mFileContents[3]; - } + mFileContents.Append(aStr); + buffer = mFileContents.BeginWriting() + 3; + } else { + if (StringHead(aStr, 2) == "\xFF\xFE") { + // Someone set us up the Utf-16LE BOM + nsDependentSubstring str(reinterpret_cast<const char16_t*>(aStr.get()), + aStr.Length() / 2); -#ifdef XP_WIN - if (flen >= 2 && - mFileContents[0] == '\xFF' && - mFileContents[1] == '\xFE') { - // Someone set us up the Utf-16LE BOM - buffer = &mFileContents[2]; - // Get the size required for our Utf8 buffer - flen = WideCharToMultiByte(CP_UTF8, - 0, - reinterpret_cast<LPWSTR>(buffer), - -1, - nullptr, - 0, - nullptr, - nullptr); - if (flen == 0) { - return NS_ERROR_FAILURE; + AppendUTF16toUTF8(Substring(str, 1), mFileContents); + } else { + mFileContents.Append(aStr); } - UniquePtr<char[]> utf8Buffer(new char[flen]); - if (WideCharToMultiByte(CP_UTF8, 0, reinterpret_cast<LPWSTR>(buffer), -1, - utf8Buffer.get(), flen, nullptr, nullptr) == 0) { - return NS_ERROR_FAILURE; - } - mFileContents = Move(utf8Buffer); - buffer = mFileContents.get(); + buffer = mFileContents.BeginWriting(); } -#endif char* currSection = nullptr; // outer loop tokenizes into lines while (char* token = NS_strtok(kNL, &buffer)) { if (token[0] == '#' || token[0] == ';') { // it's a comment continue; }
--- a/xpcom/base/nsINIParser.h +++ b/xpcom/base/nsINIParser.h @@ -105,14 +105,14 @@ private: } const char* key; const char* value; mozilla::UniquePtr<INIValue> next; }; nsClassHashtable<nsDepCharHashKey, INIValue> mSections; - mozilla::UniquePtr<char[]> mFileContents; + nsCString mFileContents; - nsresult InitFromFILE(FILE* aFd); + nsresult InitFromString(const nsCString& aStr); }; #endif /* nsINIParser_h__ */
--- a/xpcom/base/nsMemoryInfoDumper.cpp +++ b/xpcom/base/nsMemoryInfoDumper.cpp @@ -33,17 +33,19 @@ #else #include <unistd.h> #endif #ifdef XP_UNIX #define MOZ_SUPPORTS_FIFO 1 #endif -#if defined(XP_LINUX) || defined(__FreeBSD__) +// Some Android devices seem to send RT signals to Firefox so we want to avoid +// consuming those as they're not user triggered. +#if !defined(ANDROID) && (defined(XP_LINUX) || defined(__FreeBSD__)) #define MOZ_SUPPORTS_RT_SIGNALS 1 #endif #if defined(MOZ_SUPPORTS_RT_SIGNALS) #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #endif
--- a/xpcom/build/FileLocation.cpp +++ b/xpcom/build/FileLocation.cpp @@ -19,16 +19,36 @@ FileLocation::FileLocation(nsIFile* aFil Init(aFile); } FileLocation::FileLocation(nsIFile* aFile, const char* aPath) { Init(aFile, aPath); } +FileLocation::FileLocation(nsZipArchive* aZip, const char* aPath) +{ + Init(aZip, aPath); +} + +FileLocation::FileLocation(const FileLocation& aOther) + : mBaseFile(aOther.mBaseFile) + , mBaseZip(aOther.mBaseZip) + , mPath(aOther.mPath) +{ +} + +FileLocation::FileLocation(FileLocation&& aOther) + : mBaseFile(Move(aOther.mBaseFile)) + , mBaseZip(Move(aOther.mBaseZip)) + , mPath(Move(aOther.mPath)) +{ + aOther.mPath.Truncate(); +} + FileLocation::FileLocation(const FileLocation& aFile, const char* aPath) { if (aFile.IsZip()) { if (aFile.mBaseFile) { Init(aFile.mBaseFile, aFile.mPath.get()); } else { Init(aFile.mBaseZip, aFile.mPath.get());
--- a/xpcom/build/FileLocation.h +++ b/xpcom/build/FileLocation.h @@ -30,33 +30,38 @@ public: * As such, it stores a path within an archive, as well as the archive * path itself, or the complete file path alone when on a filesystem. * When the archive is in an archive, an nsZipArchive is stored instead * of a file path. */ FileLocation(); ~FileLocation(); + FileLocation(const FileLocation& aOther); + FileLocation(FileLocation&& aOther); + + FileLocation& operator=(const FileLocation&) = default; + /** * Constructor for plain files */ explicit FileLocation(nsIFile* aFile); /** * Constructors for path within an archive. The archive can be given either * as nsIFile or nsZipArchive. */ FileLocation(nsIFile* aZip, const char* aPath); FileLocation(nsZipArchive* aZip, const char* aPath); /** * Creates a new file location relative to another one. */ - FileLocation(const FileLocation& aFile, const char* aPath = nullptr); + FileLocation(const FileLocation& aFile, const char* aPath); /** * Initialization functions corresponding to constructors */ void Init(nsIFile* aFile); void Init(nsIFile* aZip, const char* aPath); @@ -70,16 +75,18 @@ public: /** * Returns the base file of the location, where base file is defined as: * - The file itself when the location is on a filesystem * - The archive file when the location is in an archive * - The outer archive file when the location is in an archive in an archive */ already_AddRefed<nsIFile> GetBaseFile(); + nsZipArchive* GetBaseZip() { return mBaseZip; } + /** * Returns whether the "base file" (see GetBaseFile) is an archive */ bool IsZip() const { return !mPath.IsEmpty(); } /** * Returns the path within the archive, when within an archive */
--- a/xpcom/components/nsComponentManager.cpp +++ b/xpcom/components/nsComponentManager.cpp @@ -44,16 +44,17 @@ #include "mozilla/GenericFactory.h" #include "nsSupportsPrimitives.h" #include "nsArray.h" #include "nsIMutableArray.h" #include "nsArrayEnumerator.h" #include "nsStringEnumerator.h" #include "mozilla/FileUtils.h" +#include "mozilla/URLPreloader.h" #include "mozilla/UniquePtr.h" #include "nsDataHashtable.h" #include <new> // for placement new #include "mozilla/Omnijar.h" #include "mozilla/Logging.h" @@ -533,30 +534,22 @@ CutExtension(nsCString& aPath) static void DoRegisterManifest(NSLocationType aType, FileLocation& aFile, bool aChromeOnly, bool aXPTOnly) { MOZ_ASSERT(!aXPTOnly || !nsComponentManagerImpl::gComponentManager); - uint32_t len; - FileLocation::Data data; - UniquePtr<char[]> buf; - nsresult rv = aFile.GetData(data); - if (NS_SUCCEEDED(rv)) { - rv = data.GetSize(&len); - } - if (NS_SUCCEEDED(rv)) { - buf = MakeUnique<char[]>(len + 1); - rv = data.Copy(buf.get(), len); - } - if (NS_SUCCEEDED(rv)) { - buf[len] = '\0'; - ParseManifest(aType, aFile, buf.get(), aChromeOnly, aXPTOnly); + + auto result = URLPreloader::Read(aFile); + if (result.isOk()) { + nsCString buf(result.unwrap()); + + ParseManifest(aType, aFile, buf.BeginWriting(), aChromeOnly, aXPTOnly); } else if (NS_BOOTSTRAPPED_LOCATION != aType) { nsCString uri; aFile.GetURIString(uri); LogMessage("Could not read chrome manifest '%s'.", uri.get()); } } void