author | Bogdan Tara <btara@mozilla.com> |
Thu, 14 May 2020 12:37:13 +0300 | |
changeset 593573 | 8af03d77567d3104c00efc5e6161e4910d835504 |
parent 593525 | 045d696faa87fc06c8040b75dec7cd6358c34067 (current diff) |
parent 593537 | 96df7fe7497f00497167c036cc05c347abdcf25c (diff) |
child 593575 | 175bb2f682a37f23fbe8e82485b5c4f5a2825e5b |
push id | 13186 |
push user | ffxbld-merge |
push date | Mon, 01 Jun 2020 09:52:46 +0000 |
treeherder | mozilla-beta@3e7c70a1e4a1 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 78.0a1 |
first release with | nightly linux32
8af03d77567d
/
78.0a1
/
20200514094044
/
files
nightly linux64
8af03d77567d
/
78.0a1
/
20200514094044
/
files
nightly mac
8af03d77567d
/
78.0a1
/
20200514094044
/
files
nightly win32
8af03d77567d
/
78.0a1
/
20200514094044
/
files
nightly win64
8af03d77567d
/
78.0a1
/
20200514094044
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
78.0a1
/
20200514094044
/
pushlog to previous
nightly linux64
78.0a1
/
20200514094044
/
pushlog to previous
nightly mac
78.0a1
/
20200514094044
/
pushlog to previous
nightly win32
78.0a1
/
20200514094044
/
pushlog to previous
nightly win64
78.0a1
/
20200514094044
/
pushlog to previous
|
--- a/accessible/base/RoleMap.h +++ b/accessible/base/RoleMap.h @@ -222,17 +222,17 @@ ROLE(TOOLBAR, ROLE_SYSTEM_TOOLBAR, ROLE_SYSTEM_TOOLBAR, java::SessionAccessibility::CLASSNAME_VIEW, eNoNameRule) ROLE(STATUSBAR, "statusbar", ATK_ROLE_STATUSBAR, - NSAccessibilityUnknownRole, //Doesn't exist on OS X (a status bar is its parts; a progressbar, a label, etc.) + NSAccessibilityGroupRole, ROLE_SYSTEM_STATUSBAR, ROLE_SYSTEM_STATUSBAR, java::SessionAccessibility::CLASSNAME_VIEW, eNoNameRule) ROLE(TABLE, "table", ATK_ROLE_TABLE, @@ -1060,17 +1060,17 @@ ROLE(COMBOBOX_OPTION, ROLE_SYSTEM_LISTITEM, ROLE_SYSTEM_LISTITEM, java::SessionAccessibility::CLASSNAME_MENUITEM, eNameFromSubtreeRule) ROLE(IMAGE_MAP, "image map", ATK_ROLE_IMAGE, - NSAccessibilityUnknownRole, + @"AXImageMap", ROLE_SYSTEM_GRAPHIC, ROLE_SYSTEM_GRAPHIC, java::SessionAccessibility::CLASSNAME_IMAGE, eNoNameRule) ROLE(OPTION, "listbox option", ATK_ROLE_LIST_ITEM,
--- a/accessible/ipc/DocAccessibleParent.cpp +++ b/accessible/ipc/DocAccessibleParent.cpp @@ -599,17 +599,17 @@ ipc::IPCResult DocAccessibleParent::AddC aChildDoc->GetCOMInterface((void**)getter_AddRefs(docAcc)); RefPtr<IDispatch> docWrapped( mscom::PassthruProxy::Wrap<IDispatch>(WrapNotNull(docAcc))); IDispatchHolder::COMPtrType docPtr( mscom::ToProxyUniquePtr(std::move(docWrapped))); IDispatchHolder docHolder(std::move(docPtr)); if (bridge->SendSetEmbeddedDocAccessibleCOMProxy(docHolder)) { # if defined(MOZ_SANDBOX) - mDocProxyStream = docHolder.GetPreservedStream(); + aChildDoc->mDocProxyStream = docHolder.GetPreservedStream(); # endif // defined(MOZ_SANDBOX) } // Send a COM proxy for the embedder OuterDocAccessible to the embedded // document process. This will be returned as the parent of the // embedded document. aChildDoc->SendParentCOMProxy(WrapperFor(outerDoc)); if (nsWinUtils::IsWindowEmulationStarted()) { // The embedded document should use the same emulated window handle as
--- a/accessible/tests/browser/mac/browser_roles_elements.js +++ b/accessible/tests/browser/mac/browser_roles_elements.js @@ -123,17 +123,17 @@ addAccessibleTask( testRoleAndSubRole(accDoc, "code", "AXGroup", "AXCodeStyleGroup"); testRoleAndSubRole(accDoc, "dialog", null, "AXApplicationDialog", "dialog"); testRoleAndSubRole(accDoc, "ariaDocument", null, "AXDocument"); testRoleAndSubRole(accDoc, "log", null, "AXApplicationLog"); testRoleAndSubRole(accDoc, "marquee", null, "AXApplicationMarquee"); testRoleAndSubRole(accDoc, "ariaMath", null, "AXDocumentMath"); testRoleAndSubRole(accDoc, "note", null, "AXDocumentNote"); testRoleAndSubRole(accDoc, "ariaRegion", null, "AXLandmarkRegion"); - testRoleAndSubRole(accDoc, "ariaStatus", null, "AXApplicationStatus"); + testRoleAndSubRole(accDoc, "ariaStatus", "AXGroup", "AXApplicationStatus"); testRoleAndSubRole(accDoc, "switch", "AXCheckBox", "AXSwitch"); testRoleAndSubRole(accDoc, "timer", null, "AXApplicationTimer"); testRoleAndSubRole(accDoc, "tooltip", null, "AXUserInterfaceTooltip"); // True HTML5 search field testRoleAndSubRole(accDoc, "htmlSearch", "AXTextField", "AXSearchField"); // A button morphed into a toggle by ARIA
--- a/browser/base/content/browser.js +++ b/browser/base/content/browser.js @@ -1781,18 +1781,19 @@ var gBrowserInit = { window.browserDOMWindow = new nsBrowserAccess(); gBrowser = window._gBrowser; delete window._gBrowser; gBrowser.init(); BrowserWindowTracker.track(window); - gNavToolbox.palette = document.getElementById("BrowserToolbarPalette"); - gNavToolbox.palette.remove(); + gNavToolbox.palette = document.getElementById( + "BrowserToolbarPalette" + ).content; let areas = CustomizableUI.areas; areas.splice(areas.indexOf(CustomizableUI.AREA_FIXED_OVERFLOW_PANEL), 1); for (let area of areas) { let node = document.getElementById(area); CustomizableUI.registerToolbarNode(node); } BrowserSearch.initPlaceHolder();
--- a/browser/base/content/browser.xhtml +++ b/browser/base/content/browser.xhtml @@ -1219,17 +1219,17 @@ tooltip="bhTooltip" popupsinherittooltip="true" context="placesContext"/> </toolbarbutton> </hbox> </hbox> </toolbaritem> </toolbar> - <toolbarpalette id="BrowserToolbarPalette"> + <html:template id="BrowserToolbarPalette"> <toolbarbutton id="print-button" class="toolbarbutton-1 chromeclass-toolbar-additional" #ifdef XP_MACOSX command="cmd_print" tooltip="dynamic-shortcut-tooltip" #else command="cmd_printPreview" tooltiptext="&printButton.tooltip;" @@ -1351,17 +1351,17 @@ title="&searchItem.title;" align="center" flex="175" persist="width"> <toolbartabstop/> <searchbar id="searchbar" flex="1"/> <toolbartabstop/> </toolbaritem> - </toolbarpalette> + </html:template> </toolbox> </box> <hbox id="fullscr-toggler" hidden="true"/> <hbox flex="1" id="browser"> <vbox id="browser-border-start" hidden="true" layer="true"/> <vbox id="sidebar-box" hidden="true" class="chromeclass-extrachrome">
--- a/browser/components/customizableui/CustomizableUI.jsm +++ b/browser/components/customizableui/CustomizableUI.jsm @@ -1459,17 +1459,20 @@ var CustomizableUIInternal = { } let container = this.getCustomizationTarget(aAreaNode); let placements = gPlacements.get(areaId); let nodeIndex = placements.indexOf(aNode.id); while (++nodeIndex < placements.length) { let nextNodeId = placements[nodeIndex]; - let nextNode = aNode.ownerDocument.getElementById(nextNodeId); + // We use aAreaNode here, because if aNode is in a template, its + // `ownerDocument` is *not* going to be the browser.xhtml document, + // so we cannot rely on it. + let nextNode = aAreaNode.ownerDocument.getElementById(nextNodeId); // If the next placed widget exists, and is a direct child of the // container, or wrapped in a customize mode wrapper (toolbarpaletteitem) // inside the container, insert beside it. // We have to check the parent to avoid errors when the placement ids // are for nodes that are no longer customizable. if ( nextNode && (nextNode.parentNode == container || @@ -1856,17 +1859,19 @@ var CustomizableUIInternal = { }, addShortcut(aShortcutNode, aTargetNode = aShortcutNode) { // Detect if we've already been here before. if (aTargetNode.hasAttribute("shortcut")) { return; } - let document = aShortcutNode.ownerDocument; + // Use ownerGlobal.document to ensure we get the right doc even for + // elements in template tags. + let { document } = aShortcutNode.ownerGlobal; let shortcutId = aShortcutNode.getAttribute("key"); let shortcut; if (shortcutId) { shortcut = document.getElementById(shortcutId); } else { let commandId = aShortcutNode.getAttribute("command"); if (commandId) { shortcut = ShortcutUtils.findShortcut( @@ -4349,17 +4354,19 @@ var CustomizableUI = { "hidden", "class", "origin", "image", "checked", "style", ]; - let doc = aSubview.ownerDocument; + // Use ownerGlobal.document to ensure we get the right doc even for + // elements in template tags. + let doc = aSubview.ownerGlobal.document; let fragment = doc.createDocumentFragment(); for (let menuChild of aMenuItems) { if (menuChild.hidden) { continue; } let subviewItem; if (menuChild.localName == "menuseparator") { @@ -4657,17 +4664,17 @@ function XULWidgetSingleWrapper(aWidgetI this.__defineGetter__("node", function() { // If we've set this to null (further down), we're sure there's nothing to // be gotten here, so bail out early: if (!weakDoc) { return null; } if (aNode) { // Return the last known node if it's still in the DOM... - if (aNode.ownerDocument.contains(aNode)) { + if (aNode.isConnected) { return aNode; } // ... or the toolbox let toolbox = aNode.ownerGlobal.gNavToolbox; if (toolbox && toolbox.palette && aNode.parentNode == toolbox.palette) { return aNode; } // If it isn't, clear the cached value and fall through to the "slow" case: @@ -5288,17 +5295,19 @@ OverflowableToolbar.prototype = { // before the desired location for the new node). Once we pass // the desired location of the widget, we look for placement ids // that actually have DOM equivalents to insert before. If all // else fails, we insert at the end of either the overflow list // or the toolbar target. while (++loopIndex < placements.length) { let nextNodeId = placements[loopIndex]; if (loopIndex > nodeIndex) { - let nextNode = aNode.ownerDocument.getElementById(nextNodeId); + // Note that if aNode is in a template, its `ownerDocument` is *not* + // going to be the browser.xhtml document, so we cannot rely on it. + let nextNode = this._toolbar.ownerDocument.getElementById(nextNodeId); // If the node we're inserting can overflow, and the next node // in the toolbar is overflown, we should insert this node // in the overflow panel before it. if ( newNodeCanOverflow && this._collapsed.has(nextNodeId) && nextNode && nextNode.parentNode == this._list
--- a/browser/components/customizableui/CustomizeMode.jsm +++ b/browser/components/customizableui/CustomizeMode.jsm @@ -642,17 +642,17 @@ CustomizeMode.prototype = { animationNode.removeEventListener( "animationend", cleanupWidgetAnimationEnd ); animationNode.removeEventListener( "customizationending", cleanupCustomizationExit ); - resolve(); + resolve(animationNode); } // Wait until the next frame before setting the class to ensure // we do start the animation. this.window.requestAnimationFrame(() => { this.window.requestAnimationFrame(() => { animationNode.classList.add("animate-out"); animationNode.ownerGlobal.gNavToolbox.addEventListener( @@ -669,18 +669,19 @@ CustomizeMode.prototype = { }, async addToToolbar(aNode) { aNode = this._getCustomizableChildForNode(aNode); if (aNode.localName == "toolbarpaletteitem" && aNode.firstElementChild) { aNode = aNode.firstElementChild; } let widgetAnimationPromise = this._promiseWidgetAnimationOut(aNode); + let animationNode; if (widgetAnimationPromise) { - await widgetAnimationPromise; + animationNode = await widgetAnimationPromise; } let widgetToAdd = aNode.id; if ( CustomizableUI.isSpecialWidget(widgetToAdd) && aNode.closest("#customization-palette") ) { widgetToAdd = widgetToAdd.match( @@ -696,55 +697,48 @@ CustomizeMode.prototype = { // If the user explicitly moves this item, turn off autohide. if (aNode.id == "downloads-button") { Services.prefs.setBoolPref(kDownloadAutoHidePref, false); if (this._customizing) { this._showDownloadsAutoHidePanel(); } } - if (widgetAnimationPromise) { - if (aNode.parentNode && aNode.parentNode.id.startsWith("wrapper-")) { - aNode.parentNode.classList.remove("animate-out"); - } else { - aNode.classList.remove("animate-out"); - } + if (animationNode) { + animationNode.classList.remove("animate-out"); } }, async addToPanel(aNode) { aNode = this._getCustomizableChildForNode(aNode); if (aNode.localName == "toolbarpaletteitem" && aNode.firstElementChild) { aNode = aNode.firstElementChild; } let widgetAnimationPromise = this._promiseWidgetAnimationOut(aNode); + let animationNode; if (widgetAnimationPromise) { - await widgetAnimationPromise; + animationNode = await widgetAnimationPromise; } let panel = CustomizableUI.AREA_FIXED_OVERFLOW_PANEL; CustomizableUI.addWidgetToArea(aNode.id, panel); if (!this._customizing) { CustomizableUI.dispatchToolboxEvent("customizationchange"); } // If the user explicitly moves this item, turn off autohide. if (aNode.id == "downloads-button") { Services.prefs.setBoolPref(kDownloadAutoHidePref, false); if (this._customizing) { this._showDownloadsAutoHidePanel(); } } - if (widgetAnimationPromise) { - if (aNode.parentNode && aNode.parentNode.id.startsWith("wrapper-")) { - aNode.parentNode.classList.remove("animate-out"); - } else { - aNode.classList.remove("animate-out"); - } + if (animationNode) { + animationNode.classList.remove("animate-out"); } if (!this.window.gReduceMotion) { let overflowButton = this.$("nav-bar-overflow-button"); BrowserUtils.setToolbarButtonHeightProperty(overflowButton).then(() => { overflowButton.setAttribute("animate", "true"); overflowButton.addEventListener("animationend", function onAnimationEnd( event ) { @@ -761,38 +755,35 @@ CustomizeMode.prototype = { }, async removeFromArea(aNode) { aNode = this._getCustomizableChildForNode(aNode); if (aNode.localName == "toolbarpaletteitem" && aNode.firstElementChild) { aNode = aNode.firstElementChild; } let widgetAnimationPromise = this._promiseWidgetAnimationOut(aNode); + let animationNode; if (widgetAnimationPromise) { - await widgetAnimationPromise; + animationNode = await widgetAnimationPromise; } CustomizableUI.removeWidgetFromArea(aNode.id); if (!this._customizing) { CustomizableUI.dispatchToolboxEvent("customizationchange"); } // If the user explicitly removes this item, turn off autohide. if (aNode.id == "downloads-button") { Services.prefs.setBoolPref(kDownloadAutoHidePref, false); if (this._customizing) { this._showDownloadsAutoHidePanel(); } } - if (widgetAnimationPromise) { - if (aNode.parentNode && aNode.parentNode.id.startsWith("wrapper-")) { - aNode.parentNode.classList.remove("animate-out"); - } else { - aNode.classList.remove("animate-out"); - } + if (animationNode) { + animationNode.classList.remove("animate-out"); } }, populatePalette() { let fragment = this.document.createDocumentFragment(); let toolboxPalette = this.window.gNavToolbox.palette; try {
--- a/browser/components/customizableui/SearchWidgetTracker.jsm +++ b/browser/components/customizableui/SearchWidgetTracker.jsm @@ -107,18 +107,18 @@ const SearchWidgetTracker = { win.document .getElementById("nav-bar") .querySelectorAll("toolbarspring") .forEach(n => n.removeAttribute("width")); win.PanelUI.overflowPanel .querySelectorAll("toolbarspring") .forEach(n => n.removeAttribute("width")); let searchbar = - win.document.getElementById(this.WIDGET_ID) || - win.gNavToolbox.palette.querySelector("#" + this.WIDGET_ID); + win.document.getElementById(WIDGET_ID) || + win.gNavToolbox.palette.querySelector("#" + WIDGET_ID); searchbar.removeAttribute("width"); } }, get widgetIsInNavBar() { let placement = CustomizableUI.getPlacementOfWidget(WIDGET_ID); return placement ? placement.area == CustomizableUI.AREA_NAVBAR : false; },
--- a/dom/base/Document.cpp +++ b/dom/base/Document.cpp @@ -1246,20 +1246,16 @@ Document::Document(const char* aContentT mDocURISchemeIsChrome(false), mInChromeDocShell(false), mIsDevToolsDocument(false), mIsSyntheticDocument(false), mHasLinksToUpdateRunnable(false), mFlushingPendingLinkUpdates(false), mMayHaveDOMMutationObservers(false), mMayHaveAnimationObservers(false), - mHasMixedActiveContentLoaded(false), - mHasMixedActiveContentBlocked(false), - mHasMixedDisplayContentLoaded(false), - mHasMixedDisplayContentBlocked(false), mHasCSP(false), mHasUnsafeEvalCSP(false), mHasUnsafeInlineCSP(false), mHasCSPDeliveredThroughHeader(false), mBFCacheDisallowed(false), mHasHadDefaultView(false), mStyleSheetChangeEventsEnabled(false), mIsSrcdocDocument(false),
--- a/dom/base/Document.h +++ b/dom/base/Document.h @@ -1043,65 +1043,101 @@ class Document : public nsINode, * change to actually change anything immediately. * @see nsBidiUtils.h */ void SetBidiOptions(uint32_t aBidiOptions) { mBidiOptions = aBidiOptions; } /** * Get the has mixed active content loaded flag for this document. */ - bool GetHasMixedActiveContentLoaded() { return mHasMixedActiveContentLoaded; } + bool GetHasMixedActiveContentLoaded() { + return mMixedContentFlags & + nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT; + } /** * Set the has mixed active content loaded flag for this document. */ void SetHasMixedActiveContentLoaded(bool aHasMixedActiveContentLoaded) { - mHasMixedActiveContentLoaded = aHasMixedActiveContentLoaded; + if (aHasMixedActiveContentLoaded) { + mMixedContentFlags |= + nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT; + } else { + mMixedContentFlags &= + ~nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT; + } } /** * Get mixed active content blocked flag for this document. */ bool GetHasMixedActiveContentBlocked() { - return mHasMixedActiveContentBlocked; + return mMixedContentFlags & + nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT; } /** * Set the mixed active content blocked flag for this document. */ void SetHasMixedActiveContentBlocked(bool aHasMixedActiveContentBlocked) { - mHasMixedActiveContentBlocked = aHasMixedActiveContentBlocked; + if (aHasMixedActiveContentBlocked) { + mMixedContentFlags |= + nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT; + } else { + mMixedContentFlags &= + ~nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT; + } } /** * Get the has mixed display content loaded flag for this document. */ bool GetHasMixedDisplayContentLoaded() { - return mHasMixedDisplayContentLoaded; + return mMixedContentFlags & + nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT; } /** * Set the has mixed display content loaded flag for this document. */ void SetHasMixedDisplayContentLoaded(bool aHasMixedDisplayContentLoaded) { - mHasMixedDisplayContentLoaded = aHasMixedDisplayContentLoaded; + if (aHasMixedDisplayContentLoaded) { + mMixedContentFlags |= + nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT; + } else { + mMixedContentFlags &= + ~nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT; + } } /** * Get mixed display content blocked flag for this document. */ bool GetHasMixedDisplayContentBlocked() { - return mHasMixedDisplayContentBlocked; + return mMixedContentFlags & + nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT; } /** * Set the mixed display content blocked flag for this document. */ void SetHasMixedDisplayContentBlocked(bool aHasMixedDisplayContentBlocked) { - mHasMixedDisplayContentBlocked = aHasMixedDisplayContentBlocked; + if (aHasMixedDisplayContentBlocked) { + mMixedContentFlags |= + nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT; + } else { + mMixedContentFlags &= + ~nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT; + } + } + + uint32_t GetMixedContentFlags() const { return mMixedContentFlags; } + + void AddMixedContentFlags(uint32_t aMixedContentFlags) { + mMixedContentFlags |= aMixedContentFlags; } /** * Set CSP flag for this document. */ void SetHasCSP(bool aHasCSP) { mHasCSP = aHasCSP; } /** @@ -4339,16 +4375,18 @@ class Document : public nsINode, RefPtr<mozilla::dom::FeaturePolicy> mFeaturePolicy; UniquePtr<ResizeObserverController> mResizeObserverController; // Permission Delegate Handler, lazily-initialized in // GetPermissionDelegateHandler RefPtr<PermissionDelegateHandler> mPermissionDelegateHandler; + uint32_t mMixedContentFlags = 0; + // True if BIDI is enabled. bool mBidiEnabled : 1; // True if we may need to recompute the language prefs for this document. bool mMayNeedFontPrefsUpdate : 1; // True if a MathML element has ever been owned by this document. bool mMathMLEnabled : 1; // True if this document is the initial document for a window. This should @@ -4432,32 +4470,16 @@ class Document : public nsINode, // True if a DOMMutationObserver is perhaps attached to a node in the // document. bool mMayHaveDOMMutationObservers : 1; // True if an nsIAnimationObserver is perhaps attached to a node in the // document. bool mMayHaveAnimationObservers : 1; - // True if a document has loaded Mixed Active Script (see - // nsMixedContentBlocker.cpp) - bool mHasMixedActiveContentLoaded : 1; - - // True if a document has blocked Mixed Active Script (see - // nsMixedContentBlocker.cpp) - bool mHasMixedActiveContentBlocked : 1; - - // True if a document has loaded Mixed Display/Passive Content (see - // nsMixedContentBlocker.cpp) - bool mHasMixedDisplayContentLoaded : 1; - - // True if a document has blocked Mixed Display/Passive Content (see - // nsMixedContentBlocker.cpp) - bool mHasMixedDisplayContentBlocked : 1; - // True if a document load has a CSP attached. bool mHasCSP : 1; // True if a document load has a CSP with unsafe-eval attached. bool mHasUnsafeEvalCSP : 1; // True if a document load has a CSP with unsafe-inline attached. bool mHasUnsafeInlineCSP : 1;
--- a/dom/indexedDB/IDBFileHandle.cpp +++ b/dom/indexedDB/IDBFileHandle.cpp @@ -35,17 +35,18 @@ RefPtr<IDBFileRequest> GenerateFileReque aFileHandle->AssertIsOnOwningThread(); return IDBFileRequest::Create(aFileHandle, /* aWrapAsDOMRequest */ false); } } // namespace IDBFileHandle::IDBFileHandle(IDBMutableFile* aMutableFile, FileMode aMode) - : mMutableFile(aMutableFile), + : DOMEventTargetHelper(aMutableFile), + mMutableFile(aMutableFile), mBackgroundActor(nullptr), mLocation(0), mPendingRequestCount(0), mReadyState(INITIAL), mMode(aMode), mAborted(false), mCreating(false) #ifdef DEBUG @@ -78,18 +79,16 @@ IDBFileHandle::~IDBFileHandle() { RefPtr<IDBFileHandle> IDBFileHandle::Create(IDBMutableFile* aMutableFile, FileMode aMode) { MOZ_ASSERT(aMutableFile); aMutableFile->AssertIsOnOwningThread(); MOZ_ASSERT(aMode == FileMode::Readonly || aMode == FileMode::Readwrite); RefPtr<IDBFileHandle> fileHandle = new IDBFileHandle(aMutableFile, aMode); - fileHandle->BindToOwner(aMutableFile); - // XXX Fix! MOZ_ASSERT(NS_IsMainThread(), "This won't work on non-main threads!"); nsCOMPtr<nsIRunnable> runnable = do_QueryObject(fileHandle); nsContentUtils::AddPendingIDBTransaction(runnable.forget()); fileHandle->mCreating = true;
--- a/dom/media/webaudio/AudioWorkletNode.cpp +++ b/dom/media/webaudio/AudioWorkletNode.cpp @@ -202,18 +202,19 @@ void WorkletNodeEngine::SendProcessorErr ProcessorErrorDetails details; details.mMessage.Assign(u"Unknown processor error"); SendErrorToMainThread(aTrack, details); return; } JS::ExceptionStack exnStack(aCx); if (JS::StealPendingExceptionStack(aCx, &exnStack)) { - js::ErrorReport jsReport(aCx); - if (!jsReport.init(aCx, exnStack, js::ErrorReport::WithSideEffects)) { + JS::ErrorReportBuilder jsReport(aCx); + if (!jsReport.init(aCx, exnStack, + JS::ErrorReportBuilder::WithSideEffects)) { ProcessorErrorDetails details; details.mMessage.Assign(u"Unknown processor error"); SendErrorToMainThread(aTrack, details); // Set the exception and stack back to have it in the console with a stack // trace. JS::SetPendingExceptionStack(aCx, exnStack); return; }
--- a/dom/promise/Promise.cpp +++ b/dom/promise/Promise.cpp @@ -26,16 +26,17 @@ #include "mozilla/dom/UserActivation.h" #include "mozilla/dom/WorkerPrivate.h" #include "mozilla/dom/WorkerRunnable.h" #include "mozilla/dom/WorkerRef.h" #include "mozilla/dom/WorkletImpl.h" #include "mozilla/dom/WorkletGlobalScope.h" #include "jsfriendapi.h" +#include "js/Exception.h" // JS::ExceptionStack #include "js/StructuredClone.h" #include "nsContentUtils.h" #include "nsGlobalWindow.h" #include "nsIScriptObjectPrincipal.h" #include "nsJSEnvironment.h" #include "nsJSPrincipals.h" #include "nsJSUtils.h" #include "nsPIDOMWindow.h" @@ -544,28 +545,31 @@ void Promise::ReportRejectedPromise(JSCo { Maybe<JSAutoRealm> ar; JS::Rooted<JS::Value> unwrapped(aCx, result); if (unwrapped.isObject()) { unwrapped.setObject(*js::UncheckedUnwrap(&unwrapped.toObject())); ar.emplace(aCx, &unwrapped.toObject()); } - js::ErrorReport report(aCx); + JS::ErrorReportBuilder report(aCx); RefPtr<Exception> exn; if (unwrapped.isObject() && (NS_SUCCEEDED(UNWRAP_OBJECT(DOMException, &unwrapped, exn)) || NS_SUCCEEDED(UNWRAP_OBJECT(Exception, &unwrapped, exn)))) { xpcReport->Init(aCx, exn, isChrome, innerWindowID); - } else if (report.init(aCx, unwrapped, js::ErrorReport::NoSideEffects)) { + } else { + JS::ExceptionStack exnStack(aCx, unwrapped, nullptr); + if (!report.init(aCx, exnStack, JS::ErrorReportBuilder::NoSideEffects)) { + JS_ClearPendingException(aCx); + return; + } + xpcReport->Init(report.report(), report.toStringResult().c_str(), isChrome, innerWindowID); - } else { - JS_ClearPendingException(aCx); - return; } } // Now post an event to do the real reporting async RefPtr<AsyncErrorReporter> event = new AsyncErrorReporter(xpcReport); if (win) { if (!win->IsDying()) { // Exceptions from a dying window will cause the window to leak.
--- a/dom/script/ScriptSettings.cpp +++ b/dom/script/ScriptSettings.cpp @@ -497,19 +497,19 @@ void AutoJSAPI::ReportException() { return; } } } } MOZ_ASSERT(JS_IsGlobalObject(errorGlobal)); JSAutoRealm ar(cx(), errorGlobal); JS::ExceptionStack exnStack(cx()); - js::ErrorReport jsReport(cx()); + JS::ErrorReportBuilder jsReport(cx()); if (StealExceptionAndStack(&exnStack) && - jsReport.init(cx(), exnStack, js::ErrorReport::WithSideEffects)) { + jsReport.init(cx(), exnStack, JS::ErrorReportBuilder::WithSideEffects)) { if (mIsMainThread) { RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport(); RefPtr<nsGlobalWindowInner> inner = xpc::WindowOrNull(errorGlobal); bool isChrome = nsContentUtils::ObjectPrincipal(errorGlobal)->IsSystemPrincipal(); xpcReport->Init(jsReport.report(), jsReport.toStringResult().c_str(), isChrome, inner ? inner->WindowID() : 0);
--- a/dom/security/nsMixedContentBlocker.cpp +++ b/dom/security/nsMixedContentBlocker.cpp @@ -57,161 +57,16 @@ bool nsMixedContentBlocker::sSecureconte enum MixedContentHSTSState { MCB_HSTS_PASSIVE_NO_HSTS = 0, MCB_HSTS_PASSIVE_WITH_HSTS = 1, MCB_HSTS_ACTIVE_NO_HSTS = 2, MCB_HSTS_ACTIVE_WITH_HSTS = 3 }; -// Fired at the document that attempted to load mixed content. The UI could -// handle this event, for example, by displaying an info bar that offers the -// choice to reload the page with mixed content permitted. -class nsMixedContentEvent : public Runnable { - public: - nsMixedContentEvent(nsISupports* aContext, MixedContentTypes aType, - bool aRootHasSecureConnection) - : mozilla::Runnable("nsMixedContentEvent"), - mContext(aContext), - mType(aType), - mRootHasSecureConnection(aRootHasSecureConnection) {} - - NS_IMETHOD Run() override { - NS_ASSERTION(mContext, - "You can't call this runnable without a requesting context"); - - // To update the security UI in the tab with the blocked mixed content, call - // nsDocLoader::OnSecurityChange. - - // Mixed content was allowed and is about to load; get the document and - // set the approriate flag to true if we are about to load Mixed Active - // Content. - nsCOMPtr<nsIDocShell> docShell = NS_CP_GetDocShellFromContext(mContext); - if (!docShell) { - return NS_OK; - } - - nsCOMPtr<nsIDocShell> rootShell = - docShell->GetBrowsingContext()->Top()->GetDocShell(); - if (!rootShell) { - return NS_OK; - } - - // now get the document from sameTypeRoot - nsCOMPtr<Document> rootDoc = rootShell->GetDocument(); - NS_ASSERTION(rootDoc, - "No root document from document shell root tree item."); - - nsDocShell* nativeDocShell = nsDocShell::Cast(docShell); - uint32_t state = nsIWebProgressListener::STATE_IS_BROKEN; - nsCOMPtr<nsISecureBrowserUI> securityUI; - rootShell->GetSecurityUI(getter_AddRefs(securityUI)); - // If there is no securityUI, document doesn't have a security state to - // update. But we still want to set the document flags, so we don't return - // early. - nsresult stateRV = NS_ERROR_FAILURE; - if (securityUI) { - stateRV = securityUI->GetState(&state); - } - - if (mType == eMixedScript) { - // See if the pref will change here. If it will, only then do we need to - // call OnSecurityChange() to update the UI. - if (rootDoc->GetHasMixedActiveContentLoaded()) { - return NS_OK; - } - rootDoc->SetHasMixedActiveContentLoaded(true); - - // Update the security UI in the tab with the allowed mixed active content - if (securityUI) { - // Bug 1182551 - before changing the security state to broken, check - // that the root is actually secure. - if (mRootHasSecureConnection) { - // reset state security flag - state = state >> 4 << 4; - // set state security flag to broken, since there is mixed content - state |= nsIWebProgressListener::STATE_IS_BROKEN; - - // If mixed display content is loaded, make sure to include that in - // the state. - if (rootDoc->GetHasMixedDisplayContentLoaded()) { - state |= nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT; - } - - nativeDocShell->nsDocLoader::OnSecurityChange( - mContext, - (state | - nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); - } else { - // root not secure, mixed active content loaded in an https subframe - if (NS_SUCCEEDED(stateRV)) { - nativeDocShell->nsDocLoader::OnSecurityChange( - mContext, - (state | - nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); - } - } - } - - } else if (mType == eMixedDisplay) { - // See if the pref will change here. If it will, only then do we need to - // call OnSecurityChange() to update the UI. - if (rootDoc->GetHasMixedDisplayContentLoaded()) { - return NS_OK; - } - rootDoc->SetHasMixedDisplayContentLoaded(true); - - // Update the security UI in the tab with the allowed mixed display - // content. - if (securityUI) { - // Bug 1182551 - before changing the security state to broken, check - // that the root is actually secure. - if (mRootHasSecureConnection) { - // reset state security flag - state = state >> 4 << 4; - // set state security flag to broken, since there is mixed content - state |= nsIWebProgressListener::STATE_IS_BROKEN; - - // If mixed active content is loaded, make sure to include that in the - // state. - if (rootDoc->GetHasMixedActiveContentLoaded()) { - state |= nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT; - } - - nativeDocShell->nsDocLoader::OnSecurityChange( - mContext, - (state | - nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); - } else { - // root not secure, mixed display content loaded in an https subframe - if (NS_SUCCEEDED(stateRV)) { - nativeDocShell->nsDocLoader::OnSecurityChange( - mContext, - (state | - nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); - } - } - } - } - - return NS_OK; - } - - private: - // The requesting context for the content load. Generally, a DOM node from - // the document that caused the load. - nsCOMPtr<nsISupports> mContext; - - // The type of mixed content detected, e.g. active or display - const MixedContentTypes mType; - - // Indicates whether the top level load is https or not. - bool mRootHasSecureConnection; -}; - nsMixedContentBlocker::~nsMixedContentBlocker() = default; NS_IMPL_ISUPPORTS(nsMixedContentBlocker, nsIContentPolicy, nsIChannelEventSink) static void LogMixedContentMessage( MixedContentTypes aClassification, nsIURI* aContentLocation, uint64_t aInnerWindowID, nsMixedContentBlockerMessageType aMessageType, nsIURI* aRequestingLocation, @@ -889,30 +744,24 @@ nsresult nsMixedContentBlocker::ShouldLo *aDecision = nsIContentPolicy::ACCEPT; return NS_OK; } *aDecision = nsIContentPolicy::REJECT_REQUEST; return NS_OK; } } - uint64_t topInnerWindowID = - docShell->GetBrowsingContext()->GetTopWindowContext()->Id(); - nsDocShell* nativeDocShell = nsDocShell::Cast(docShell); - - uint32_t state = nsIWebProgressListener::STATE_IS_BROKEN; nsCOMPtr<nsISecureBrowserUI> securityUI; rootShell->GetSecurityUI(getter_AddRefs(securityUI)); // If there is no securityUI, document doesn't have a security state. // Allow load and return early. if (!securityUI) { *aDecision = nsIContentPolicy::ACCEPT; return NS_OK; } - nsresult stateRV = securityUI->GetState(&state); OriginAttributes originAttributes; if (loadingPrincipal) { originAttributes = loadingPrincipal->OriginAttributesRef(); } else if (triggeringPrincipal) { originAttributes = triggeringPrincipal->OriginAttributesRef(); } @@ -944,160 +793,95 @@ nsresult nsMixedContentBlocker::ShouldLo } } // set hasMixedContentObjectSubrequest on this object if necessary if (contentType == TYPE_OBJECT_SUBREQUEST) { if (!StaticPrefs::security_mixed_content_block_object_subrequest()) { nsAutoCString messageLookUpKey( "LoadingMixedDisplayObjectSubrequestDeprecation"); - LogMixedContentMessage(classification, aContentLocation, topInnerWindowID, + LogMixedContentMessage(classification, aContentLocation, topWC->Id(), eUserOverride, requestingLocation, messageLookUpKey); } } + uint32_t newState = 0; // If the content is display content, and the pref says display content should // be blocked, block it. - if (StaticPrefs::security_mixed_content_block_display_content() && - classification == eMixedDisplay) { - if (allowMixedContent) { - LogMixedContentMessage(classification, aContentLocation, topInnerWindowID, - eUserOverride, requestingLocation); + if (classification == eMixedDisplay) { + if (!StaticPrefs::security_mixed_content_block_display_content() || + allowMixedContent) { *aDecision = nsIContentPolicy::ACCEPT; - // See if mixed display content has already loaded on the page or if the - // state needs to be updated here. If mixed display hasn't loaded - // previously, then we need to call OnSecurityChange() to update the UI. - if (rootDoc->GetHasMixedDisplayContentLoaded()) { - return NS_OK; - } - rootDoc->SetHasMixedDisplayContentLoaded(true); - - if (rootHasSecureConnection) { - // reset state security flag - state = state >> 4 << 4; - // set state security flag to broken, since there is mixed content - state |= nsIWebProgressListener::STATE_IS_BROKEN; - - // If mixed active content is loaded, make sure to include that in the - // state. - if (rootDoc->GetHasMixedActiveContentLoaded()) { - state |= nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT; - } - - nativeDocShell->nsDocLoader::OnSecurityChange( - requestingContext, - (state | - nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); - } else { - // User has overriden the pref and the root is not https; - // mixed display content was allowed on an https subframe. - if (NS_SUCCEEDED(stateRV)) { - nativeDocShell->nsDocLoader::OnSecurityChange( - requestingContext, - (state | - nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT)); - } - } + // User has overriden the pref and the root is not https; + // mixed display content was allowed on an https subframe. + newState |= nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT; } else { *aDecision = nsIContentPolicy::REJECT_REQUEST; - LogMixedContentMessage(classification, aContentLocation, topInnerWindowID, - eBlocked, requestingLocation); - if (!rootDoc->GetHasMixedDisplayContentBlocked() && - NS_SUCCEEDED(stateRV)) { - rootDoc->SetHasMixedDisplayContentBlocked(true); - nativeDocShell->nsDocLoader::OnSecurityChange( - requestingContext, - (state | - nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT)); - } + newState |= nsIWebProgressListener::STATE_BLOCKED_MIXED_DISPLAY_CONTENT; } - return NS_OK; - - } else if (StaticPrefs::security_mixed_content_block_active_content() && - classification == eMixedScript) { + } else { + MOZ_ASSERT(classification == eMixedScript); // If the content is active content, and the pref says active content should // be blocked, block it unless the user has choosen to override the pref - if (allowMixedContent) { - LogMixedContentMessage(classification, aContentLocation, topInnerWindowID, - eUserOverride, requestingLocation); + if (!StaticPrefs::security_mixed_content_block_active_content() || + allowMixedContent) { *aDecision = nsIContentPolicy::ACCEPT; - // See if the state will change here. If it will, only then do we need to - // call OnSecurityChange() to update the UI. - if (rootDoc->GetHasMixedActiveContentLoaded()) { - return NS_OK; - } - rootDoc->SetHasMixedActiveContentLoaded(true); - - if (rootHasSecureConnection) { - // reset state security flag - state = state >> 4 << 4; - // set state security flag to broken, since there is mixed content - state |= nsIWebProgressListener::STATE_IS_BROKEN; - - // If mixed display content is loaded, make sure to include that in the - // state. - if (rootDoc->GetHasMixedDisplayContentLoaded()) { - state |= nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT; - } - - nativeDocShell->nsDocLoader::OnSecurityChange( - requestingContext, - (state | - nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); - - return NS_OK; - } else { - // User has already overriden the pref and the root is not https; - // mixed active content was allowed on an https subframe. - if (NS_SUCCEEDED(stateRV)) { - nativeDocShell->nsDocLoader::OnSecurityChange( - requestingContext, - (state | - nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT)); - } - return NS_OK; - } + // User has already overriden the pref and the root is not https; + // mixed active content was allowed on an https subframe. + newState |= nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT; } else { // User has not overriden the pref by Disabling protection. Reject the // request and update the security state. *aDecision = nsIContentPolicy::REJECT_REQUEST; - LogMixedContentMessage(classification, aContentLocation, topInnerWindowID, - eBlocked, requestingLocation); - // See if the pref will change here. If it will, only then do we need to - // call OnSecurityChange() to update the UI. - if (rootDoc->GetHasMixedActiveContentBlocked()) { - return NS_OK; - } - rootDoc->SetHasMixedActiveContentBlocked(true); - // The user has not overriden the pref, so make sure they still have an // option by calling nativeDocShell which will invoke the doorhanger - if (NS_SUCCEEDED(stateRV)) { - nativeDocShell->nsDocLoader::OnSecurityChange( - requestingContext, - (state | - nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT)); - } - return NS_OK; + newState |= nsIWebProgressListener::STATE_BLOCKED_MIXED_ACTIVE_CONTENT; } - } else { - // The content is not blocked by the mixed content prefs. + } - // Log a message that we are loading mixed content. - LogMixedContentMessage(classification, aContentLocation, topInnerWindowID, - eUserOverride, requestingLocation); + LogMixedContentMessage(classification, aContentLocation, topWC->Id(), + (*aDecision == nsIContentPolicy::REJECT_REQUEST) + ? eBlocked + : eUserOverride, + requestingLocation); - // Fire the event from a script runner as it is unsafe to run script - // from within ShouldLoad - nsContentUtils::AddScriptRunner(new nsMixedContentEvent( - requestingContext, classification, rootHasSecureConnection)); - *aDecision = ACCEPT; + if (rootDoc->GetMixedContentFlags() == newState) { return NS_OK; } + + // Copy the new state onto the Document flags. + rootDoc->AddMixedContentFlags(newState); + + uint32_t state = nsIWebProgressListener::STATE_IS_BROKEN; + MOZ_ALWAYS_SUCCEEDS(securityUI->GetState(&state)); + + if (*aDecision == nsIContentPolicy::ACCEPT && rootHasSecureConnection) { + // reset state security flag + state = state >> 4 << 4; + // set state security flag to broken, since there is mixed content + state |= nsIWebProgressListener::STATE_IS_BROKEN; + + // If mixed display content is loaded, make sure to include that in the + // state. + if (rootDoc->GetHasMixedDisplayContentLoaded()) { + state |= nsIWebProgressListener::STATE_LOADED_MIXED_DISPLAY_CONTENT; + } + + // If mixed active content is loaded, make sure to include that in the + // state. + if (rootDoc->GetHasMixedActiveContentLoaded()) { + state |= nsIWebProgressListener::STATE_LOADED_MIXED_ACTIVE_CONTENT; + } + } + + state |= newState; + nsDocShell* nativeDocShell = nsDocShell::Cast(docShell); + nativeDocShell->nsDocLoader::OnSecurityChange(requestingContext, state); + return NS_OK; } bool nsMixedContentBlocker::URISafeToBeLoadedInSecureContext(nsIURI* aURI) { /* Returns a bool if the URI can be loaded as a sub resource safely. * * Check Protocol Flags to determine if scheme is safe to load: * URI_DOES_NOT_RETURN_DATA - e.g. * "mailto"
--- a/dom/serviceworkers/ServiceWorkerEvents.cpp +++ b/dom/serviceworkers/ServiceWorkerEvents.cpp @@ -6,16 +6,17 @@ #include "ServiceWorkerEvents.h" #include <utility> #include "ServiceWorker.h" #include "ServiceWorkerManager.h" #include "js/Conversions.h" +#include "js/Exception.h" // JS::ExceptionStack, JS::StealPendingExceptionStack #include "js/TypeDecls.h" #include "mozilla/Encoding.h" #include "mozilla/ErrorResult.h" #include "mozilla/LoadInfo.h" #include "mozilla/Preferences.h" #include "mozilla/dom/BodyUtil.h" #include "mozilla/dom/Client.h" #include "mozilla/dom/EventBinding.h" @@ -495,26 +496,24 @@ class MOZ_STACK_CLASS AutoCancel { // Storing the error as exception in the JSContext. if (!aRv.MaybeSetPendingException(aCx)) { return; } MOZ_ASSERT(!aRv.Failed()); // Let's take the pending exception. - JS::Rooted<JS::Value> exn(aCx); - if (!JS_GetPendingException(aCx, &exn)) { + JS::ExceptionStack exnStack(aCx); + if (!JS::StealPendingExceptionStack(aCx, &exnStack)) { return; } - JS_ClearPendingException(aCx); - - // Converting the exception in a js::ErrorReport. - js::ErrorReport report(aCx); - if (!report.init(aCx, exn, js::ErrorReport::WithSideEffects)) { + // Converting the exception in a JS::ErrorReportBuilder. + JS::ErrorReportBuilder report(aCx); + if (!report.init(aCx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) { JS_ClearPendingException(aCx); return; } MOZ_ASSERT(mOwner); MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL")); MOZ_ASSERT(mParams.Length() == 1);
--- a/dom/serviceworkers/ServiceWorkerOp.cpp +++ b/dom/serviceworkers/ServiceWorkerOp.cpp @@ -3,16 +3,17 @@ /* 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 "ServiceWorkerOp.h" #include <utility> +#include "js/Exception.h" // JS::ExceptionStack, JS::StealPendingExceptionStack #include "jsapi.h" #include "nsCOMPtr.h" #include "nsContentUtils.h" #include "nsDebug.h" #include "nsError.h" #include "nsINamed.h" #include "nsIPushErrorReporter.h" @@ -1046,26 +1047,24 @@ class MOZ_STACK_CLASS FetchEventOp::Auto // Storing the error as exception in the JSContext. if (!aRv.MaybeSetPendingException(aCx)) { return; } MOZ_ASSERT(!aRv.Failed()); // Let's take the pending exception. - JS::Rooted<JS::Value> exn(aCx); - if (!JS_GetPendingException(aCx, &exn)) { + JS::ExceptionStack exnStack(aCx); + if (!JS::StealPendingExceptionStack(aCx, &exnStack)) { return; } - JS_ClearPendingException(aCx); - - // Converting the exception in a js::ErrorReport. - js::ErrorReport report(aCx); - if (!report.init(aCx, exn, js::ErrorReport::WithSideEffects)) { + // Converting the exception in a JS::ErrorReportBuilder. + JS::ErrorReportBuilder report(aCx); + if (!report.init(aCx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) { JS_ClearPendingException(aCx); return; } MOZ_ASSERT(mOwner); MOZ_ASSERT(mMessageName.EqualsLiteral("InterceptionFailedWithURL")); MOZ_ASSERT(mParams.Length() == 1);
--- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -26,16 +26,17 @@ #include "nsIStreamListenerTee.h" #include "nsIThreadRetargetableRequest.h" #include "nsIURI.h" #include "nsIXPConnect.h" #include "jsapi.h" #include "jsfriendapi.h" #include "js/CompilationAndEvaluation.h" +#include "js/Exception.h" #include "js/SourceText.h" #include "nsError.h" #include "nsContentPolicyUtils.h" #include "nsContentUtils.h" #include "nsDocShellCID.h" #include "nsNetUtil.h" #include "nsIPipe.h" #include "nsIOutputStream.h" @@ -2245,18 +2246,19 @@ void ScriptExecutorRunnable::LogExceptio if (!ToJSValue(aCx, std::move(mScriptLoader.mRv), &exn)) { return; } // Now the exception state should all be in exn. MOZ_ASSERT(!JS_IsExceptionPending(aCx)); MOZ_ASSERT(!mScriptLoader.mRv.Failed()); - js::ErrorReport report(aCx); - if (!report.init(aCx, exn, js::ErrorReport::WithSideEffects)) { + JS::ExceptionStack exnStack(aCx, exn, nullptr); + JS::ErrorReportBuilder report(aCx); + if (!report.init(aCx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) { JS_ClearPendingException(aCx); return; } RefPtr<xpc::ErrorReport> xpcReport = new xpc::ErrorReport(); xpcReport->Init(report.report(), report.toStringResult().c_str(), aWorkerPrivate->IsChromeWorker(), aWorkerPrivate->WindowID());
--- a/dom/xhr/XMLHttpRequest.cpp +++ b/dom/xhr/XMLHttpRequest.cpp @@ -37,18 +37,18 @@ already_AddRefed<XMLHttpRequest> XMLHttp } cookieJarSettings = document->CookieJarSettings(); } else { // We are here because this is a sandbox. cookieJarSettings = net::CookieJarSettings::Create(); } - RefPtr<XMLHttpRequestMainThread> req = new XMLHttpRequestMainThread(); - req->Construct(principal->GetPrincipal(), global, cookieJarSettings, false); + RefPtr<XMLHttpRequestMainThread> req = new XMLHttpRequestMainThread(global); + req->Construct(principal->GetPrincipal(), cookieJarSettings, false); req->InitParameters(aParams.mMozAnon, aParams.mMozSystem); return req.forget(); } return XMLHttpRequestWorker::Construct(aGlobal, aParams, aRv); } } // namespace dom
--- a/dom/xhr/XMLHttpRequest.h +++ b/dom/xhr/XMLHttpRequest.h @@ -122,14 +122,18 @@ class XMLHttpRequest : public XMLHttpReq virtual bool MozAnon() const = 0; virtual bool MozSystem() const = 0; virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override { return mozilla::dom::XMLHttpRequest_Binding::Wrap(aCx, this, aGivenProto); } + + protected: + explicit XMLHttpRequest(nsIGlobalObject* aGlobalObject) + : XMLHttpRequestEventTarget(aGlobalObject) {} }; } // namespace dom } // namespace mozilla #endif // mozilla_dom_XMLHttpRequest_h
--- a/dom/xhr/XMLHttpRequestEventTarget.h +++ b/dom/xhr/XMLHttpRequestEventTarget.h @@ -12,17 +12,18 @@ namespace mozilla { namespace dom { class XMLHttpRequestEventTarget : public DOMEventTargetHelper { protected: explicit XMLHttpRequestEventTarget(DOMEventTargetHelper* aOwner) : DOMEventTargetHelper(aOwner) {} - XMLHttpRequestEventTarget() = default; + explicit XMLHttpRequestEventTarget(nsIGlobalObject* aGlobalObject) + : DOMEventTargetHelper(aGlobalObject) {} virtual ~XMLHttpRequestEventTarget() = default; public: NS_DECL_ISUPPORTS_INHERITED NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(XMLHttpRequestEventTarget, DOMEventTargetHelper)
--- a/dom/xhr/XMLHttpRequestMainThread.cpp +++ b/dom/xhr/XMLHttpRequestMainThread.cpp @@ -181,18 +181,20 @@ static void AddLoadFlags(nsIRequest* req ///////////////////////////////////////////// // // ///////////////////////////////////////////// bool XMLHttpRequestMainThread::sDontWarnAboutSyncXHR = false; -XMLHttpRequestMainThread::XMLHttpRequestMainThread() - : mResponseBodyDecodedPos(0), +XMLHttpRequestMainThread::XMLHttpRequestMainThread( + nsIGlobalObject* aGlobalObject) + : XMLHttpRequest(aGlobalObject), + mResponseBodyDecodedPos(0), mResponseType(XMLHttpRequestResponseType::_empty), mRequestObserver(nullptr), mState(XMLHttpRequest_Binding::UNSENT), mFlagSynchronous(false), mFlagAborted(false), mFlagParseBody(false), mFlagSyncLooping(false), mFlagBackgroundRequest(false),
--- a/dom/xhr/XMLHttpRequestMainThread.h +++ b/dom/xhr/XMLHttpRequestMainThread.h @@ -205,26 +205,25 @@ class XMLHttpRequestMainThread final : p eRequest, eUnreachable, eChannelOpen, eRedirect, eTerminated, ENUM_MAX }; - XMLHttpRequestMainThread(); + explicit XMLHttpRequestMainThread(nsIGlobalObject* aGlobalObject); - void Construct(nsIPrincipal* aPrincipal, nsIGlobalObject* aGlobalObject, + void Construct(nsIPrincipal* aPrincipal, nsICookieJarSettings* aCookieJarSettings, bool aForWorker, nsIURI* aBaseURI = nullptr, nsILoadGroup* aLoadGroup = nullptr, PerformanceStorage* aPerformanceStorage = nullptr, nsICSPEventListener* aCSPEventListener = nullptr) { MOZ_ASSERT(aPrincipal); mPrincipal = aPrincipal; - BindToOwner(aGlobalObject); mBaseURI = aBaseURI; mLoadGroup = aLoadGroup; mCookieJarSettings = aCookieJarSettings; mForWorker = aForWorker; mPerformanceStorage = aPerformanceStorage; mCSPEventListener = aCSPEventListener; }
--- a/dom/xhr/XMLHttpRequestWorker.cpp +++ b/dom/xhr/XMLHttpRequestWorker.cpp @@ -760,19 +760,19 @@ bool Proxy::Init() { } nsPIDOMWindowInner* ownerWindow = mWorkerPrivate->GetWindow(); if (ownerWindow && !ownerWindow->IsCurrentInnerWindow()) { NS_WARNING("Window has navigated, cannot create XHR here."); return false; } - mXHR = new XMLHttpRequestMainThread(); + mXHR = new XMLHttpRequestMainThread(ownerWindow ? ownerWindow->AsGlobal() + : nullptr); mXHR->Construct(mWorkerPrivate->GetPrincipal(), - ownerWindow ? ownerWindow->AsGlobal() : nullptr, mWorkerPrivate->CookieJarSettings(), true, mWorkerPrivate->GetBaseURI(), mWorkerPrivate->GetLoadGroup(), mWorkerPrivate->GetPerformanceStorage(), mWorkerPrivate->CSPEventListener()); mXHR->SetParameters(mMozAnon, mMozSystem); mXHR->SetClientInfoAndController(mClientInfo, mController); @@ -1350,18 +1350,20 @@ void SendRunnable::RunOnMainThread(Error if (!mProxy->mUploadEventListenersAttached && !mProxy->AddRemoveEventListeners(true, true)) { MOZ_ASSERT(false, "This should never fail!"); } } } } -XMLHttpRequestWorker::XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate) - : mWorkerPrivate(aWorkerPrivate), +XMLHttpRequestWorker::XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate, + nsIGlobalObject* aGlobalObject) + : XMLHttpRequest(aGlobalObject), + mWorkerPrivate(aWorkerPrivate), mResponseType(XMLHttpRequestResponseType::_empty), mStateData(new StateData()), mResponseData(new ResponseData()), mResponseArrayBufferValue(nullptr), mResponseJSONValue(JS::UndefinedValue()), mTimeout(0), mBackgroundRequest(false), mWithCredentials(false), @@ -1423,18 +1425,18 @@ already_AddRefed<XMLHttpRequest> XMLHttp MOZ_ASSERT(workerPrivate); nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(aGlobal.GetAsSupports()); if (NS_WARN_IF(!global)) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } - RefPtr<XMLHttpRequestWorker> xhr = new XMLHttpRequestWorker(workerPrivate); - xhr->BindToOwner(global); + RefPtr<XMLHttpRequestWorker> xhr = + new XMLHttpRequestWorker(workerPrivate, global); if (workerPrivate->XHRParamsAllowed()) { if (aParams.mMozSystem) xhr->mMozAnon = true; else xhr->mMozAnon = aParams.mMozAnon; xhr->mMozSystem = aParams.mMozSystem; }
--- a/dom/xhr/XMLHttpRequestWorker.h +++ b/dom/xhr/XMLHttpRequestWorker.h @@ -218,17 +218,18 @@ class XMLHttpRequestWorker final : publi virtual bool MozAnon() const override { return mMozAnon; } virtual bool MozSystem() const override { return mMozSystem; } bool SendInProgress() const { return !!mWorkerRef; } private: - explicit XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate); + XMLHttpRequestWorker(WorkerPrivate* aWorkerPrivate, + nsIGlobalObject* aGlobalObject); ~XMLHttpRequestWorker(); enum ReleaseType { Default, XHRIsGoingAway, WorkerIsGoingAway }; void ReleaseProxy(ReleaseType aType = Default); void MaybePin(ErrorResult& aRv);
--- a/gfx/layers/AnimationHelper.cpp +++ b/gfx/layers/AnimationHelper.cpp @@ -309,20 +309,21 @@ AnimationHelper::SampleResult AnimationH MOZ_ASSERT(aAnimationValues.IsEmpty(), "Should be called with empty aAnimationValues"); nsTArray<RefPtr<RawServoAnimationValue>> nonAnimatingValues; for (PropertyAnimationGroup& group : aPropertyAnimationGroups) { // Initialize animation value with base style. RefPtr<RawServoAnimationValue> currValue = group.mBaseStyle; - CanSkipCompose canSkipCompose = aPropertyAnimationGroups.Length() == 1 && - group.mAnimations.Length() == 1 - ? CanSkipCompose::IfPossible - : CanSkipCompose::No; + CanSkipCompose canSkipCompose = + aPreviousValue && aPropertyAnimationGroups.Length() == 1 && + group.mAnimations.Length() == 1 + ? CanSkipCompose::IfPossible + : CanSkipCompose::No; MOZ_ASSERT( !group.mAnimations.IsEmpty() || nsCSSPropertyIDSet::TransformLikeProperties().HasProperty( group.mProperty), "Only transform-like properties can have empty PropertyAnimation list"); // For properties which are not animating (i.e. their values are always the
--- a/js/public/ErrorReport.h +++ b/js/public/ErrorReport.h @@ -23,16 +23,18 @@ #include <stddef.h> // size_t #include <stdint.h> // int16_t, uint16_t #include <string.h> // strlen #include "jstypes.h" // JS_PUBLIC_API #include "js/AllocPolicy.h" // js::SystemAllocPolicy #include "js/CharacterEncoding.h" // JS::ConstUTF8CharsZ +#include "js/Exception.h" // JS::ExceptionStack +#include "js/RootingAPI.h" // JS::HandleObject, JS::RootedObject #include "js/UniquePtr.h" // js::UniquePtr #include "js/Vector.h" // js::Vector struct JS_PUBLIC_API JSContext; class JS_PUBLIC_API JSString; /** * Possible exception types. These types are part of a JSErrorFormatString @@ -260,9 +262,99 @@ class JSErrorReport : public JSErrorBase size_t tokenOffsetArg); bool isWarning() const { return isWarning_; } private: void freeLinebuf(); }; +namespace JS { + +struct MOZ_STACK_CLASS JS_PUBLIC_API ErrorReportBuilder { + explicit ErrorReportBuilder(JSContext* cx); + ~ErrorReportBuilder(); + + enum SniffingBehavior { WithSideEffects, NoSideEffects }; + + /** + * Generate a JSErrorReport from the provided thrown value. + * + * If the value is a (possibly wrapped) Error object, the JSErrorReport will + * be exactly initialized from the Error object's information, without + * observable side effects. (The Error object's JSErrorReport is reused, if + * it has one.) + * + * Otherwise various attempts are made to derive JSErrorReport information + * from |exnStack| and from the current execution state. This process is + * *definitely* inconsistent with any standard, and particulars of the + * behavior implemented here generally shouldn't be relied upon. + * + * If the value of |sniffingBehavior| is |WithSideEffects|, some of these + * attempts *may* invoke user-configurable behavior when the exception is an + * object: converting it to a string, detecting and getting its properties, + * accessing its prototype chain, and others are possible. Users *must* + * tolerate |ErrorReportBuilder::init| potentially having arbitrary effects. + * Any exceptions thrown by these operations will be caught and silently + * ignored, and "default" values will be substituted into the JSErrorReport. + * + * But if the value of |sniffingBehavior| is |NoSideEffects|, these attempts + * *will not* invoke any observable side effects. The JSErrorReport will + * simply contain fewer, less precise details. + * + * Unlike some functions involved in error handling, this function adheres + * to the usual JSAPI return value error behavior. + */ + bool init(JSContext* cx, const JS::ExceptionStack& exnStack, + SniffingBehavior sniffingBehavior); + + JSErrorReport* report() const { return reportp; } + + const JS::ConstUTF8CharsZ toStringResult() const { return toStringResult_; } + + private: + // More or less an equivalent of JS_ReportErrorNumber/js::ReportErrorNumberVA + // but fills in an ErrorReport instead of reporting it. Uses varargs to + // make it simpler to call js::ExpandErrorArgumentsVA. + // + // Returns false if we fail to actually populate the ErrorReport + // for some reason (probably out of memory). + bool populateUncaughtExceptionReportUTF8(JSContext* cx, + JS::HandleObject stack, ...); + bool populateUncaughtExceptionReportUTF8VA(JSContext* cx, + JS::HandleObject stack, + va_list ap); + + // Reports exceptions from add-on scopes to telemetry. + void ReportAddonExceptionToTelemetry(JSContext* cx); + + // We may have a provided JSErrorReport, so need a way to represent that. + JSErrorReport* reportp; + + // Or we may need to synthesize a JSErrorReport one of our own. + JSErrorReport ownedReport; + + // Root our exception value to keep a possibly borrowed |reportp| alive. + JS::RootedObject exnObject; + + // And for our filename. + JS::UniqueChars filename; + + // We may have a result of error.toString(). + // FIXME: We should not call error.toString(), since it could have side + // effect (see bug 633623). + JS::ConstUTF8CharsZ toStringResult_; + JS::UniqueChars toStringResultBytesStorage; +}; + +// Writes a full report to a file descriptor. Does nothing for JSErrorReports +// which are warnings, unless reportWarnings is set. +extern JS_PUBLIC_API void PrintError(JSContext* cx, FILE* file, + JSErrorReport* report, + bool reportWarnings); + +extern JS_PUBLIC_API void PrintError(JSContext* cx, FILE* file, + const JS::ErrorReportBuilder& builder, + bool reportWarnings); + +} // namespace JS + #endif /* js_ErrorReport_h */
--- a/js/src/jsapi-tests/moz.build +++ b/js/src/jsapi-tests/moz.build @@ -158,16 +158,22 @@ if CONFIG['JS_BUILD_BINAST'] and CONFIG[ # Otherwise, in the current state of the build system, # we can't have data files in js/src tests. # Also, fuzzing builds modify the const matching in the # token reader and hence affect the correctness of the tests. UNIFIED_SOURCES += [ 'testBinASTReader.cpp', ] +if CONFIG['OS_ARCH'] not in ('WINNT', 'Darwin') and CONFIG['OS_TARGET'] != 'Android': + # open_memstream() not available on Windows, macOS, or Android + UNIFIED_SOURCES += [ + 'testPrintError.cpp', + ] + DEFINES['EXPORT_JS_API'] = True LOCAL_INCLUDES += [ '!..', '..', ]
--- a/js/src/jsapi-tests/testArrayBuffer.cpp +++ b/js/src/jsapi-tests/testArrayBuffer.cpp @@ -272,18 +272,18 @@ BEGIN_TEST(testArrayBuffer_serializeExte // serialize(externalBuffer, [externalBuffer]) should throw for an unhandled // BufferContents kind. CHECK(!JS::Call(cx, JS::UndefinedHandleValue, serializeValue, JS::HandleValueArray(args), &v)); JS::ExceptionStack exnStack(cx); CHECK(JS::StealPendingExceptionStack(cx, &exnStack)); - js::ErrorReport report(cx); - CHECK(report.init(cx, exnStack, js::ErrorReport::NoSideEffects)); + JS::ErrorReportBuilder report(cx); + CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::NoSideEffects)); CHECK_EQUAL(report.report()->errorNumber, static_cast<unsigned int>(JSMSG_SC_NOT_TRANSFERABLE)); // Data should have been left alone. CHECK(!data.wasFreed()); v.setNull();
--- a/js/src/jsapi-tests/testBinASTReader.cpp +++ b/js/src/jsapi-tests/testBinASTReader.cpp @@ -389,36 +389,34 @@ void runTestFromPath(JSContext* cx, cons MOZ_CRASH("Couldn't clear binExn"); } } // The binary parser should accept the file iff the text parser has. if (binParsed.isOk() && !txtParsed) { fprintf(stderr, "Text file parsing failed: "); - js::ErrorReport report(cx); - if (!report.init(cx, txtExn, js::ErrorReport::WithSideEffects)) { + JS::ErrorReportBuilder report(cx); + if (!report.init(cx, txtExn, JS::ErrorReportBuilder::WithSideEffects)) { MOZ_CRASH("Couldn't report txtExn"); } - PrintError(cx, stderr, report.toStringResult(), report.report(), - /* reportWarnings */ true); + PrintError(cx, stderr, report, /* reportWarnings */ true); MOZ_CRASH("Binary parser accepted a file that text parser rejected"); } if (binParsed.isErr() && txtParsed) { fprintf(stderr, "Binary file parsing failed: "); - js::ErrorReport report(cx); - if (!report.init(cx, binExn, js::ErrorReport::WithSideEffects)) { + JS::ErrorReportBuilder report(cx); + if (!report.init(cx, binExn, JS::ErrorReportBuilder::WithSideEffects)) { MOZ_CRASH("Couldn't report binExn"); } - PrintError(cx, stderr, report.toStringResult(), report.report(), - /* reportWarnings */ true); + PrintError(cx, stderr, report, /* reportWarnings */ true); MOZ_CRASH("Binary parser rejected a file that text parser accepted"); } if (binParsed.isErr()) { fprintf(stderr, "Binary parser and text parser agree that %s is invalid\n", txtPath.get()); continue;
--- a/js/src/jsapi-tests/testCompileUtf8.cpp +++ b/js/src/jsapi-tests/testCompileUtf8.cpp @@ -193,18 +193,18 @@ bool testBadUtf8(const char (&chars)[N], script = JS::Compile(cx, options, srcBuf); CHECK(!script); } JS::ExceptionStack exnStack(cx); CHECK(JS::StealPendingExceptionStack(cx, &exnStack)); - js::ErrorReport report(cx); - CHECK(report.init(cx, exnStack, js::ErrorReport::WithSideEffects)); + JS::ErrorReportBuilder report(cx); + CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)); const auto* errorReport = report.report(); CHECK(errorReport->errorNumber == errorNumber); CHECK(testMessage(errorReport->message())); { @@ -276,18 +276,18 @@ bool testContext(const char (&chars)[N], script = JS::Compile(cx, options, srcBuf); CHECK(!script); } JS::ExceptionStack exnStack(cx); CHECK(JS::StealPendingExceptionStack(cx, &exnStack)); - js::ErrorReport report(cx); - CHECK(report.init(cx, exnStack, js::ErrorReport::WithSideEffects)); + JS::ErrorReportBuilder report(cx); + CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)); const auto* errorReport = report.report(); CHECK(errorReport->errorNumber == JSMSG_ILLEGAL_CHARACTER); const char16_t* lineOfContext = errorReport->linebuf(); size_t lineOfContextLength = errorReport->linebufLength();
--- a/js/src/jsapi-tests/testEmptyWindowIsOmitted.cpp +++ b/js/src/jsapi-tests/testEmptyWindowIsOmitted.cpp @@ -108,18 +108,18 @@ template <typename CharT, size_t N> bool testOmittedWindow(const CharT (&chars)[N], unsigned expectedErrorNumber, const char* badCodeUnits = nullptr) { JS::Rooted<JSScript*> script(cx, compile(chars, N - 1)); CHECK(!script); JS::ExceptionStack exnStack(cx); CHECK(JS::StealPendingExceptionStack(cx, &exnStack)); - js::ErrorReport report(cx); - CHECK(report.init(cx, exnStack, js::ErrorReport::WithSideEffects)); + JS::ErrorReportBuilder report(cx); + CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)); const auto* errorReport = report.report(); CHECK(errorReport->errorNumber == expectedErrorNumber); if (const auto& notes = errorReport->notes) { CHECK(sizeof(CharT) == 1); CHECK(badCodeUnits != nullptr);
--- a/js/src/jsapi-tests/testErrorCopying.cpp +++ b/js/src/jsapi-tests/testErrorCopying.cpp @@ -17,16 +17,16 @@ BEGIN_TEST(testErrorCopying_columnCopied EXEC("function check() { Object; foo; }"); JS::RootedValue rval(cx); CHECK(!JS_CallFunctionName(cx, global, "check", JS::HandleValueArray::empty(), &rval)); JS::ExceptionStack exnStack(cx); CHECK(JS::StealPendingExceptionStack(cx, &exnStack)); - js::ErrorReport report(cx); - CHECK(report.init(cx, exnStack, js::ErrorReport::WithSideEffects)); + JS::ErrorReportBuilder report(cx); + CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)); CHECK_EQUAL(report.report()->column, 28u); return true; } END_TEST(testErrorCopying_columnCopied)
--- a/js/src/jsapi-tests/testErrorLineOfContext.cpp +++ b/js/src/jsapi-tests/testErrorLineOfContext.cpp @@ -53,18 +53,18 @@ template <size_t N> bool testLineOfContextHasNoLineTerminator(const char16_t (&chars)[N], char16_t expectedLast) { JS::RootedValue rval(cx); CHECK(!eval(chars, N - 1, &rval)); JS::ExceptionStack exnStack(cx); CHECK(JS::StealPendingExceptionStack(cx, &exnStack)); - js::ErrorReport report(cx); - CHECK(report.init(cx, exnStack, js::ErrorReport::WithSideEffects)); + JS::ErrorReportBuilder report(cx); + CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)); const auto* errorReport = report.report(); const char16_t* lineOfContext = errorReport->linebuf(); size_t lineOfContextLength = errorReport->linebufLength(); CHECK(lineOfContext[lineOfContextLength] == '\0'); char16_t last = lineOfContext[lineOfContextLength - 1];
--- a/js/src/jsapi-tests/testMutedErrors.cpp +++ b/js/src/jsapi-tests/testMutedErrors.cpp @@ -85,14 +85,14 @@ bool testInner(const char* asciiChars, b bool testError(const char* asciiChars) { JS::RootedValue rval(cx); CHECK(!eval(asciiChars, true, &rval)); JS::ExceptionStack exnStack(cx); CHECK(JS::StealPendingExceptionStack(cx, &exnStack)); - js::ErrorReport report(cx); - CHECK(report.init(cx, exnStack, js::ErrorReport::WithSideEffects)); + JS::ErrorReportBuilder report(cx); + CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)); CHECK(report.report()->isMuted == true); return true; } END_TEST(testMutedErrors)
--- a/js/src/jsapi-tests/testParseJSON.cpp +++ b/js/src/jsapi-tests/testParseJSON.cpp @@ -296,18 +296,18 @@ inline bool Error(JSContext* cx, const c str = input; bool ok = JS_ParseJSON(cx, str.chars(), str.length(), &dummy); CHECK(!ok); JS::ExceptionStack exnStack(cx); CHECK(StealPendingExceptionStack(cx, &exnStack)); - js::ErrorReport report(cx); - CHECK(report.init(cx, exnStack, js::ErrorReport::WithSideEffects)); + JS::ErrorReportBuilder report(cx); + CHECK(report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)); CHECK(report.report()->errorNumber == JSMSG_JSON_BAD_PARSE); UniqueChars lineAndColumnASCII = JS_smprintf("line %d column %d", expectedLine, expectedColumn); CHECK(strstr(report.toStringResult().c_str(), lineAndColumnASCII.get()) != nullptr); /* We do not execute JS, so there should be no exception thrown. */
new file mode 100644 --- /dev/null +++ b/js/src/jsapi-tests/testPrintError.cpp @@ -0,0 +1,126 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- + * vim: set ts=8 sts=2 et sw=2 tw=80: + */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include <cstdio> // fclose, fflush, open_memstream + +#include "jsapi.h" // JS_{Clear,Get}PendingException +#include "jsfriendapi.h" // js::ErrorReport +#include "js/ErrorReport.h" // JS::PrintError +#include "js/Warnings.h" // JS::SetWarningReporter, JS::WarnUTF8 + +#include "jsapi-tests/tests.h" + +class AutoStreamBuffer { + char* buffer; + size_t size; + FILE* fp; + + public: + AutoStreamBuffer() { fp = open_memstream(&buffer, &size); } + + ~AutoStreamBuffer() { + fclose(fp); + free(buffer); + } + + FILE* stream() { return fp; } + + bool contains(const char* str) { + if (fflush(fp) != 0) { + fprintf(stderr, "Error flushing stream\n"); + return false; + } + if (strcmp(buffer, str) != 0) { + fprintf(stderr, "Expected |%s|, got |%s|\n", str, buffer); + return false; + } + return true; + } +}; + +BEGIN_TEST(testPrintError_Works) { + AutoStreamBuffer buf; + + CHECK(!execDontReport("throw null;", "testPrintError_Works.js", 3)); + + JS::ExceptionStack exnStack(cx); + CHECK(JS::StealPendingExceptionStack(cx, &exnStack)); + + JS::ErrorReportBuilder builder(cx); + CHECK(builder.init(cx, exnStack, JS::ErrorReportBuilder::NoSideEffects)); + JS::PrintError(cx, buf.stream(), builder, false); + + CHECK(buf.contains("testPrintError_Works.js:3:1 uncaught exception: null\n")); + + return true; +} +END_TEST(testPrintError_Works) + +BEGIN_TEST(testPrintError_SkipWarning) { + JS::SetWarningReporter(cx, warningReporter); + CHECK(JS::WarnUTF8(cx, "warning message")); + CHECK(warningSuccess); + return true; +} + +static bool warningSuccess; + +static void warningReporter(JSContext* cx, JSErrorReport* report) { + AutoStreamBuffer buf; + JS::PrintError(cx, buf.stream(), report, false); + warningSuccess = buf.contains(""); +} +END_TEST(testPrintError_SkipWarning) + +bool cls_testPrintError_SkipWarning::warningSuccess = false; + +BEGIN_TEST(testPrintError_PrintWarning) { + JS::SetWarningReporter(cx, warningReporter); + CHECK(JS::WarnUTF8(cx, "warning message")); + CHECK(warningSuccess); + return true; +} + +static bool warningSuccess; + +static void warningReporter(JSContext* cx, JSErrorReport* report) { + AutoStreamBuffer buf; + JS::PrintError(cx, buf.stream(), report, true); + warningSuccess = buf.contains("warning: warning message\n"); +} +END_TEST(testPrintError_PrintWarning) + +bool cls_testPrintError_PrintWarning::warningSuccess = false; + +#define BURRITO "\xF0\x9F\x8C\xAF" + +BEGIN_TEST(testPrintError_UTF16CodePoints) { + AutoStreamBuffer buf; + + static const char utf8code[] = + "function f() {\n var x = `\n" BURRITO "`; " BURRITO "; } f();"; + + CHECK(!execDontReport(utf8code, "testPrintError_UTF16CodePoints.js", 1)); + + JS::ExceptionStack exnStack(cx); + CHECK(JS::StealPendingExceptionStack(cx, &exnStack)); + + JS::ErrorReportBuilder builder(cx); + CHECK(builder.init(cx, exnStack, JS::ErrorReportBuilder::NoSideEffects)); + JS::PrintError(cx, buf.stream(), builder, false); + + CHECK(buf.contains( + "testPrintError_UTF16CodePoints.js:3:4 SyntaxError: illegal character:\n" + "testPrintError_UTF16CodePoints.js:3:4 " BURRITO "`; " BURRITO + "; } f();\n" + "testPrintError_UTF16CodePoints.js:3:4 .....^\n")); + + return true; +} +END_TEST(testPrintError_UTF16CodePoints) + +#undef BURRITO
--- a/js/src/jsapi-tests/testUncaughtSymbol.cpp +++ b/js/src/jsapi-tests/testUncaughtSymbol.cpp @@ -30,19 +30,19 @@ BEGIN_TEST(testUncaughtSymbol) { return true; } static SymbolExceptionType GetSymbolExceptionType(JSContext* cx) { JS::ExceptionStack exnStack(cx); MOZ_RELEASE_ASSERT(JS::StealPendingExceptionStack(cx, &exnStack)); MOZ_RELEASE_ASSERT(exnStack.exception().isSymbol()); - js::ErrorReport report(cx); + JS::ErrorReportBuilder report(cx); MOZ_RELEASE_ASSERT( - report.init(cx, exnStack, js::ErrorReport::WithSideEffects)); + report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)); if (strcmp(report.toStringResult().c_str(), "uncaught exception: Symbol(Symbol.iterator)") == 0) { return SYMBOL_ITERATOR; } if (strcmp(report.toStringResult().c_str(), "uncaught exception: Symbol(foo)") == 0) { return SYMBOL_FOO;
--- a/js/src/jsexn.cpp +++ b/js/src/jsexn.cpp @@ -23,17 +23,18 @@ #include "jsapi.h" #include "jsfriendapi.h" #include "jstypes.h" #include "gc/Rooting.h" #include "js/CharacterEncoding.h" #include "js/Class.h" #include "js/Conversions.h" -#include "js/Exception.h" // JS::ExceptionStack +#include "js/ErrorReport.h" // JS::PrintError +#include "js/Exception.h" // JS::ExceptionStack #include "js/SavedFrameAPI.h" #include "js/UniquePtr.h" #include "js/Value.h" #include "js/Warnings.h" // JS::{,Set}WarningReporter #include "js/Wrapper.h" #include "util/Memory.h" #include "util/StringBuffer.h" #include "vm/Compartment.h" @@ -282,17 +283,17 @@ JS_FRIEND_API JSLinearString* js::GetErr void js::ErrorToException(JSContext* cx, JSErrorReport* reportp, JSErrorCallback callback, void* userRef) { MOZ_ASSERT(!reportp->isWarning()); // We cannot throw a proper object inside the self-hosting realm, as we // cannot construct the Error constructor without self-hosted code. Just // print the error to stderr to help debugging. if (cx->realm()->isSelfHostingRealm()) { - PrintError(cx, stderr, JS::ConstUTF8CharsZ(), reportp, true); + JS::PrintError(cx, stderr, reportp, true); return; } // Find the exception index associated with this error. JSErrNum errorNumber = static_cast<JSErrNum>(reportp->errorNumber); if (!callback) { callback = GetErrorMessage; } @@ -345,17 +346,17 @@ void js::ErrorToException(JSContext* cx, RootedValue errValue(cx, ObjectValue(*errObject)); RootedSavedFrame nstack(cx); if (stack) { nstack = &stack->as<SavedFrame>(); } cx->setPendingException(errValue, nstack); } -using SniffingBehavior = js::ErrorReport::SniffingBehavior; +using SniffingBehavior = JS::ErrorReportBuilder::SniffingBehavior; static bool IsDuckTypedErrorObject(JSContext* cx, HandleObject exnObject, const char** filename_strp) { /* * This function is called from ErrorReport::init and so should not generate * any new exceptions. */ AutoClearPendingException acpe(cx); @@ -459,55 +460,52 @@ static JSString* ErrorReportToString(JSC if (!message) { return nullptr; } } return FormatErrorMessage(cx, name, message); } -ErrorReport::ErrorReport(JSContext* cx) : reportp(nullptr), exnObject(cx) {} - -ErrorReport::~ErrorReport() = default; +JS::ErrorReportBuilder::ErrorReportBuilder(JSContext* cx) + : reportp(nullptr), exnObject(cx) {} -bool ErrorReport::init(JSContext* cx, const JS::ExceptionStack& exnStack, - SniffingBehavior sniffingBehavior) { - return init(cx, exnStack.exception(), sniffingBehavior, exnStack.stack()); -} +JS::ErrorReportBuilder::~ErrorReportBuilder() = default; -bool ErrorReport::init(JSContext* cx, HandleValue exn, - SniffingBehavior sniffingBehavior, - HandleObject fallbackStack) { +bool JS::ErrorReportBuilder::init(JSContext* cx, + const JS::ExceptionStack& exnStack, + SniffingBehavior sniffingBehavior) { MOZ_ASSERT(!cx->isExceptionPending()); MOZ_ASSERT(!reportp); - if (exn.isObject()) { + if (exnStack.exception().isObject()) { // Because ToString below could error and an exception object could become // unrooted, we must root our exception object, if any. - exnObject = &exn.toObject(); + exnObject = &exnStack.exception().toObject(); reportp = ErrorFromException(cx, exnObject); } // Be careful not to invoke ToString if we've already successfully extracted // an error report, since the exception might be wrapped in a security // wrapper, and ToString-ing it might throw. RootedString str(cx); if (reportp) { str = ErrorReportToString(cx, exnObject, reportp, sniffingBehavior); - } else if (exn.isSymbol()) { + } else if (exnStack.exception().isSymbol()) { RootedValue strVal(cx); - if (js::SymbolDescriptiveString(cx, exn.toSymbol(), &strVal)) { + if (js::SymbolDescriptiveString(cx, exnStack.exception().toSymbol(), + &strVal)) { str = strVal.toString(); } else { str = nullptr; } } else if (exnObject && sniffingBehavior == NoSideEffects) { str = cx->names().Object; } else { - str = ToString<CanGC>(cx, exn); + str = js::ToString<CanGC>(cx, exnStack.exception()); } if (!str) { cx->clearPendingException(); } // If ErrorFromException didn't get us a JSErrorReport, then the object // was not an ErrorObject, security-wrapped or otherwise. However, it might @@ -540,17 +538,17 @@ bool ErrorReport::init(JSContext* cx, Ha // If we have the right fields, override the ToString we performed on // the exception object above with something built out of its quacks // (i.e. as much of |NameQuack: MessageQuack| as we can make). str = FormatErrorMessage(cx, name, msg); { AutoClearPendingException acpe(cx); if (JS_GetProperty(cx, exnObject, filename_str, &val)) { - RootedString tmp(cx, ToString<CanGC>(cx, val)); + RootedString tmp(cx, js::ToString<CanGC>(cx, val)); if (tmp) { filename = JS_EncodeStringToUTF8(cx, tmp); } } } if (!filename) { filename = DuplicateString(""); if (!filename) { @@ -609,46 +607,47 @@ bool ErrorReport::init(JSContext* cx, Ha if (!reportp) { // This is basically an inlined version of // // JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, // JSMSG_UNCAUGHT_EXCEPTION, utf8Message); // // but without the reporting bits. Instead it just puts all // the stuff we care about in our ownedReport and message_. - if (!populateUncaughtExceptionReportUTF8(cx, fallbackStack, utf8Message)) { + if (!populateUncaughtExceptionReportUTF8(cx, exnStack.stack(), + utf8Message)) { // Just give up. We're out of memory or something; not much we can // do here. return false; } } else { toStringResult_ = JS::ConstUTF8CharsZ(utf8Message, strlen(utf8Message)); } return true; } -bool ErrorReport::populateUncaughtExceptionReportUTF8( - JSContext* cx, HandleObject fallbackStack, ...) { +bool JS::ErrorReportBuilder::populateUncaughtExceptionReportUTF8( + JSContext* cx, HandleObject stack, ...) { va_list ap; - va_start(ap, fallbackStack); - bool ok = populateUncaughtExceptionReportUTF8VA(cx, fallbackStack, ap); + va_start(ap, stack); + bool ok = populateUncaughtExceptionReportUTF8VA(cx, stack, ap); va_end(ap); return ok; } -bool ErrorReport::populateUncaughtExceptionReportUTF8VA( - JSContext* cx, HandleObject fallbackStack, va_list ap) { +bool JS::ErrorReportBuilder::populateUncaughtExceptionReportUTF8VA( + JSContext* cx, HandleObject stack, va_list ap) { new (&ownedReport) JSErrorReport(); ownedReport.isWarning_ = false; ownedReport.errorNumber = JSMSG_UNCAUGHT_EXCEPTION; bool skippedAsync; RootedSavedFrame frame( - cx, UnwrapSavedFrame(cx, cx->realm()->principals(), fallbackStack, + cx, UnwrapSavedFrame(cx, cx->realm()->principals(), stack, SavedFrameSelfHosted::Exclude, skippedAsync)); if (frame) { filename = StringToNewUTF8CharsZ(cx, *frame->getSource()); if (!filename) { return false; } // |ownedReport.filename| inherits the lifetime of |ErrorReport::filename|.
--- a/js/src/jsfriendapi.h +++ b/js/src/jsfriendapi.h @@ -1284,100 +1284,16 @@ typedef enum JSErrNum { namespace js { /* Implemented in vm/JSContext.cpp. */ extern JS_FRIEND_API const JSErrorFormatString* GetErrorMessage( void* userRef, const unsigned errorNumber); -struct MOZ_STACK_CLASS JS_FRIEND_API ErrorReport { - explicit ErrorReport(JSContext* cx); - ~ErrorReport(); - - enum SniffingBehavior { WithSideEffects, NoSideEffects }; - - /** - * Generate a JSErrorReport from the provided thrown value. - * - * If the value is a (possibly wrapped) Error object, the JSErrorReport will - * be exactly initialized from the Error object's information, without - * observable side effects. (The Error object's JSErrorReport is reused, if - * it has one.) - * - * Otherwise various attempts are made to derive JSErrorReport information - * from |exn| and from the current execution state. This process is - * *definitely* inconsistent with any standard, and particulars of the - * behavior implemented here generally shouldn't be relied upon. - * - * To fill in information such as file-name or line number the (optional) - * stack for the pending exception can be used. For this first SavedFrame - * is used. - * - * If the value of |sniffingBehavior| is |WithSideEffects|, some of these - * attempts *may* invoke user-configurable behavior when |exn| is an object: - * converting |exn| to a string, detecting and getting properties on |exn|, - * accessing |exn|'s prototype chain, and others are possible. Users *must* - * tolerate |ErrorReport::init| potentially having arbitrary effects. Any - * exceptions thrown by these operations will be caught and silently - * ignored, and "default" values will be substituted into the JSErrorReport. - * - * But if the value of |sniffingBehavior| is |NoSideEffects|, these attempts - * *will not* invoke any observable side effects. The JSErrorReport will - * simply contain fewer, less precise details. - * - * Unlike some functions involved in error handling, this function adheres - * to the usual JSAPI return value error behavior. - */ - bool init(JSContext* cx, JS::HandleValue exn, - SniffingBehavior sniffingBehavior, - JS::HandleObject fallbackStack = nullptr); - - bool init(JSContext* cx, const JS::ExceptionStack& exnStack, - SniffingBehavior sniffingBehavior); - - JSErrorReport* report() { return reportp; } - - const JS::ConstUTF8CharsZ toStringResult() { return toStringResult_; } - - private: - // More or less an equivalent of JS_ReportErrorNumber/js::ReportErrorNumberVA - // but fills in an ErrorReport instead of reporting it. Uses varargs to - // make it simpler to call js::ExpandErrorArgumentsVA. - // - // Returns false if we fail to actually populate the ErrorReport - // for some reason (probably out of memory). - bool populateUncaughtExceptionReportUTF8(JSContext* cx, - JS::HandleObject fallbackStack, ...); - bool populateUncaughtExceptionReportUTF8VA(JSContext* cx, - JS::HandleObject fallbackStack, - va_list ap); - - // Reports exceptions from add-on scopes to telemetry. - void ReportAddonExceptionToTelemetry(JSContext* cx); - - // We may have a provided JSErrorReport, so need a way to represent that. - JSErrorReport* reportp; - - // Or we may need to synthesize a JSErrorReport one of our own. - JSErrorReport ownedReport; - - // Root our exception value to keep a possibly borrowed |reportp| alive. - JS::RootedObject exnObject; - - // And for our filename. - JS::UniqueChars filename; - - // We may have a result of error.toString(). - // FIXME: We should not call error.toString(), since it could have side - // effect (see bug 633623). - JS::ConstUTF8CharsZ toStringResult_; - JS::UniqueChars toStringResultBytesStorage; -}; - /* Implemented in vm/StructuredClone.cpp. */ extern JS_FRIEND_API uint64_t GetSCOffset(JSStructuredCloneWriter* writer); namespace Scalar { // Scalar types that can appear in typed arrays and typed objects. // The enum values must be kept in sync with: // * the JS_SCALARTYPEREPR constants
--- a/js/src/shell/js.cpp +++ b/js/src/shell/js.cpp @@ -101,16 +101,17 @@ #include "js/ArrayBuffer.h" // JS::{CreateMappedArrayBufferContents,NewMappedArrayBufferWithContents,IsArrayBufferObject,GetArrayBufferLengthAndData} #include "js/BuildId.h" // JS::BuildIdCharVector, JS::SetProcessBuildIdOp #include "js/CharacterEncoding.h" #include "js/CompilationAndEvaluation.h" #include "js/CompileOptions.h" #include "js/ContextOptions.h" // JS::ContextOptions{,Ref} #include "js/Debug.h" #include "js/Equality.h" // JS::SameValue +#include "js/ErrorReport.h" // JS::PrintError #include "js/Exception.h" // JS::StealPendingExceptionStack #include "js/experimental/SourceHook.h" // js::{Set,Forget,}SourceHook #include "js/GCVector.h" #include "js/Initialization.h" #include "js/JSON.h" #include "js/MemoryFunctions.h" #include "js/Modules.h" // JS::GetModulePrivate, JS::SetModule{DynamicImport,Metadata,Resolve}Hook, JS::SetModulePrivate #include "js/Printf.h" @@ -3837,18 +3838,18 @@ static bool CopyErrorReportToObject(JSCo return DefineDataProperty(cx, obj, cx->names().notes, notesArrayVal); } static bool CreateErrorReport(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); // We don't have a stack here, so just initialize with null. JS::ExceptionStack exnStack(cx, args.get(0), nullptr); - js::ErrorReport report(cx); - if (!report.init(cx, exnStack, js::ErrorReport::WithSideEffects)) { + JS::ErrorReportBuilder report(cx); + if (!report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) { return false; } MOZ_ASSERT(!report.report()->isWarning()); RootedObject obj(cx, JS_NewPlainObject(cx)); if (!obj) { return false; @@ -8807,17 +8808,17 @@ static const JSFunctionSpecWithHelp shel " Stop accounting time to mutator vs GC and dump the results."), JS_FN_HELP("throwError", ThrowError, 0, 0, "throwError()", " Throw an error from JS_ReportError."), JS_FN_HELP("createErrorReport", CreateErrorReport, 1, 0, "createErrorReport(value)", -" Create an js::ErrorReport object from the given value and serialize\n" +" Create an JS::ErrorReportBuilder object from the given value and serialize\n" " to an object."), #if defined(DEBUG) || defined(JS_JITSPEW) JS_FN_HELP("disassemble", DisassembleToString, 1, 0, "disassemble([fun/code])", " Return the disassembly for the given function or code.\n" " All disassembly functions take these options as leading string arguments:\n" " \"-r\" (disassemble recursively)\n" @@ -9725,28 +9726,28 @@ js::shell::AutoReportException::~AutoRep if (!JS::StealPendingExceptionStack(cx, &exnStack)) { fprintf(stderr, "out of memory while stealing exception\n"); fflush(stderr); JS_ClearPendingException(cx); return; } ShellContext* sc = GetShellContext(cx); - js::ErrorReport report(cx); - if (!report.init(cx, exnStack, js::ErrorReport::WithSideEffects)) { - fprintf(stderr, "out of memory initializing ErrorReport\n"); + JS::ErrorReportBuilder report(cx); + if (!report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) { + fprintf(stderr, "out of memory initializing JS::ErrorReportBuilder\n"); fflush(stderr); JS_ClearPendingException(cx); return; } MOZ_ASSERT(!report.report()->isWarning()); FILE* fp = ErrorFilePointer(); - PrintError(cx, fp, report.toStringResult(), report.report(), reportWarnings); + JS::PrintError(cx, fp, report, reportWarnings); JS_ClearPendingException(cx); if (!PrintStackTrace(cx, exnStack.stack())) { fputs("(Unable to print stack trace)\n", fp); JS_ClearPendingException(cx); } #if defined(DEBUG) || defined(JS_OOM_BREAKPOINT) @@ -9776,17 +9777,17 @@ void js::shell::WarningReporter(JSContex fputs("Unhandled error happened while creating last warning object.\n", fp); fflush(fp); } savedExc.restore(); } // Print the warning. - PrintError(cx, fp, JS::ConstUTF8CharsZ(), report, reportWarnings); + JS::PrintError(cx, fp, report, reportWarnings); } static bool global_enumerate(JSContext* cx, JS::HandleObject obj, JS::MutableHandleIdVector properties, bool enumerableOnly) { #ifdef LAZY_STANDARD_CLASSES return JS_NewEnumerateStandardClasses(cx, obj, properties, enumerableOnly); #else
--- a/js/src/shell/jsrtfuzzing/jsrtfuzzing.cpp +++ b/js/src/shell/jsrtfuzzing/jsrtfuzzing.cpp @@ -13,43 +13,42 @@ #include <stdio.h> // fflush, fprintf, fputs #include "FuzzerDefs.h" #include "FuzzingInterface.h" #include "jsapi.h" // JS_ClearPendingException, JS_IsExceptionPending, JS_SetProperty #include "js/CompilationAndEvaluation.h" // JS::Evaluate #include "js/CompileOptions.h" // JS::CompileOptions +#include "js/ErrorReport.h" // JS::PrintError #include "js/Exception.h" // JS::StealPendingExceptionStack #include "js/RootingAPI.h" // JS::Rooted #include "js/SourceText.h" // JS::Source{Ownership,Text} #include "js/Value.h" // JS::Value #include "shell/jsshell.h" // js::shell::{reportWarnings,PrintStackTrace,sArg{c,v}} #include "vm/Interpreter.h" -#include "vm/JSContext.h" // js::PrintError #include "vm/TypedArrayObject.h" #include "vm/ArrayBufferObject-inl.h" #include "vm/JSContext-inl.h" static JSContext* gCx = nullptr; static std::string gFuzzModuleName; static void CrashOnPendingException() { if (JS_IsExceptionPending(gCx)) { JS::ExceptionStack exnStack(gCx); (void)JS::StealPendingExceptionStack(gCx, &exnStack); - js::ErrorReport report(gCx); - if (!report.init(gCx, exnStack, js::ErrorReport::WithSideEffects)) { - fprintf(stderr, "out of memory initializing ErrorReport\n"); + JS::ErrorReportBuilder report(gCx); + if (!report.init(gCx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) { + fprintf(stderr, "out of memory initializing JS::ErrorReportBuilder\n"); fflush(stderr); } else { - js::PrintError(gCx, stderr, report.toStringResult(), report.report(), - js::shell::reportWarnings); + JS::PrintError(gCx, stderr, report, js::shell::reportWarnings); if (!js::shell::PrintStackTrace(gCx, exnStack.stack())) { fputs("(Unable to print stack trace)\n", stderr); } } MOZ_CRASH("Unhandled exception from JS runtime!"); } }
--- a/js/src/vm/JSContext.cpp +++ b/js/src/vm/JSContext.cpp @@ -6,21 +6,23 @@ /* * JS execution context. */ #include "vm/JSContext-inl.h" #include "mozilla/ArrayUtils.h" +#include "mozilla/CheckedInt.h" #include "mozilla/DebugOnly.h" #include "mozilla/MemoryReporting.h" #include "mozilla/Sprintf.h" #include "mozilla/TextUtils.h" #include "mozilla/Unused.h" +#include "mozilla/Utf8.h" // mozilla::ConvertUtf16ToUtf8 #include <stdarg.h> #include <string.h> #ifdef ANDROID # include <android/log.h> # include <fstream> # include <string> #endif // ANDROID @@ -349,39 +351,65 @@ void js::ReportUsageErrorASCII(JSContext } } enum class PrintErrorKind { Error, Warning, Note }; static void PrintErrorLine(FILE* file, const char* prefix, JSErrorReport* report) { if (const char16_t* linebuf = report->linebuf()) { - size_t n = report->linebufLength(); + UniqueChars line; + size_t n; + { + size_t linebufLen = report->linebufLength(); + + // This function is only used for shell command-line sorts of stuff where + // performance doesn't really matter, so just encode into max-sized + // memory. + mozilla::CheckedInt<size_t> utf8Len(linebufLen); + utf8Len *= 3; + if (utf8Len.isValid()) { + line = UniqueChars(js_pod_malloc<char>(utf8Len.value())); + if (line) { + n = mozilla::ConvertUtf16toUtf8({linebuf, linebufLen}, + {line.get(), utf8Len.value()}); + } + } + } + + const char* utf8buf; + if (line) { + utf8buf = line.get(); + } else { + static const char unavailableStr[] = "<context unavailable>"; + utf8buf = unavailableStr; + n = mozilla::ArrayLength(unavailableStr) - 1; + } fputs(":\n", file); if (prefix) { fputs(prefix, file); } for (size_t i = 0; i < n; i++) { - fputc(static_cast<char>(linebuf[i]), file); + fputc(utf8buf[i], file); } - // linebuf usually ends with a newline. If not, add one here. - if (n == 0 || linebuf[n - 1] != '\n') { + // linebuf/utf8buf usually ends with a newline. If not, add one here. + if (n == 0 || utf8buf[n - 1] != '\n') { fputc('\n', file); } if (prefix) { fputs(prefix, file); } n = report->tokenOffset(); for (size_t i = 0, j = 0; i < n; i++) { - if (linebuf[i] == '\t') { + if (utf8buf[i] == '\t') { for (size_t k = (j + 8) & ~7; j < k; j++) { fputc('.', file); } continue; } fputc('.', file); j++; } @@ -443,19 +471,19 @@ static void PrintSingleError(JSContext* fputs(message, file); PrintErrorLine(file, prefix.get(), report); fputc('\n', file); fflush(file); } -void js::PrintError(JSContext* cx, FILE* file, - JS::ConstUTF8CharsZ toStringResult, JSErrorReport* report, - bool reportWarnings) { +static void PrintErrorImpl(JSContext* cx, FILE* file, + JS::ConstUTF8CharsZ toStringResult, + JSErrorReport* report, bool reportWarnings) { MOZ_ASSERT(report); /* Conditionally ignore reported warnings. */ if (report->isWarning() && !reportWarnings) { return; } PrintErrorKind kind = PrintErrorKind::Error; @@ -467,16 +495,28 @@ void js::PrintError(JSContext* cx, FILE* if (report->notes) { for (auto&& note : *report->notes) { PrintSingleError(cx, file, JS::ConstUTF8CharsZ(), note.get(), PrintErrorKind::Note); } } } +JS_PUBLIC_API void JS::PrintError(JSContext* cx, FILE* file, + JSErrorReport* report, bool reportWarnings) { + PrintErrorImpl(cx, file, JS::ConstUTF8CharsZ(), report, reportWarnings); +} + +JS_PUBLIC_API void JS::PrintError(JSContext* cx, FILE* file, + const JS::ErrorReportBuilder& builder, + bool reportWarnings) { + PrintErrorImpl(cx, file, builder.toStringResult(), builder.report(), + reportWarnings); +} + void js::ReportIsNotDefined(JSContext* cx, HandleId id) { if (UniqueChars printable = IdToPrintableUTF8(cx, id, IdToPrintableBehavior::IdIsIdentifier)) { JS_ReportErrorNumberUTF8(cx, GetErrorMessage, nullptr, JSMSG_NOT_DEFINED, printable.get()); } }
--- a/js/src/vm/JSContext.h +++ b/js/src/vm/JSContext.h @@ -1031,23 +1031,16 @@ struct MOZ_RAII AutoResolving { extern JSContext* NewContext(uint32_t maxBytes, JSRuntime* parentRuntime); extern void DestroyContext(JSContext* cx); /* |callee| requires a usage string provided by JS_DefineFunctionsWithHelp. */ extern void ReportUsageErrorASCII(JSContext* cx, HandleObject callee, const char* msg); -// Writes a full report to a file descriptor. -// Does nothing for JSErrorReport which are warnings, unless -// reportWarnings is set. -extern void PrintError(JSContext* cx, FILE* file, - JS::ConstUTF8CharsZ toStringResult, - JSErrorReport* report, bool reportWarnings); - extern void ReportIsNotDefined(JSContext* cx, HandlePropertyName name); extern void ReportIsNotDefined(JSContext* cx, HandleId id); /* * Report an attempt to access the property of a null or undefined value (v). */ extern void ReportIsNullOrUndefinedForPropertyAccess(JSContext* cx,
--- a/js/src/vm/SelfHosting.cpp +++ b/js/src/vm/SelfHosting.cpp @@ -44,16 +44,17 @@ #include "frontend/BytecodeCompiler.h" // js::frontend::CreateScriptSourceObject #include "gc/Marking.h" #include "gc/Policy.h" #include "jit/AtomicOperations.h" #include "jit/InlinableNatives.h" #include "js/CharacterEncoding.h" #include "js/CompilationAndEvaluation.h" #include "js/Date.h" +#include "js/ErrorReport.h" // JS::PrintError #include "js/Exception.h" #include "js/Modules.h" // JS::GetModulePrivate #include "js/PropertySpec.h" #include "js/SourceText.h" // JS::SourceText #include "js/StableStringChars.h" #include "js/Warnings.h" // JS::{,Set}WarningReporter #include "js/Wrapper.h" #include "util/StringBuffer.h" @@ -103,17 +104,17 @@ using namespace js::selfhosted; using JS::AutoCheckCannotGC; using JS::AutoStableStringChars; using JS::CompileOptions; using mozilla::Maybe; static void selfHosting_WarningReporter(JSContext* cx, JSErrorReport* report) { MOZ_ASSERT(report->isWarning()); - PrintError(cx, stderr, JS::ConstUTF8CharsZ(), report, true); + JS::PrintError(cx, stderr, report, true); } static bool intrinsic_ToObject(JSContext* cx, unsigned argc, Value* vp) { CallArgs args = CallArgsFromVp(argc, vp); JSObject* obj = ToObject(cx, args[0]); if (!obj) { return false; } @@ -2589,24 +2590,24 @@ static void MaybePrintAndClearPendingExc AutoClearPendingException acpe(cx); JS::ExceptionStack exnStack(cx); if (!JS::StealPendingExceptionStack(cx, &exnStack)) { fprintf(file, "error getting pending exception\n"); return; } - ErrorReport report(cx); - if (!report.init(cx, exnStack, js::ErrorReport::WithSideEffects)) { - fprintf(file, "out of memory initializing ErrorReport\n"); + JS::ErrorReportBuilder report(cx); + if (!report.init(cx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) { + fprintf(file, "out of memory initializing JS::ErrorReportBuilder\n"); return; } MOZ_ASSERT(!report.report()->isWarning()); - PrintError(cx, file, report.toStringResult(), report.report(), true); + JS::PrintError(cx, file, report, true); } class MOZ_STACK_CLASS AutoSelfHostingErrorReporter { JSContext* cx_; JS::WarningReporter oldReporter_; public: explicit AutoSelfHostingErrorReporter(JSContext* cx) : cx_(cx) {
--- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -3335,17 +3335,16 @@ void nsIFrame::BuildDisplayListForStacki allowAsyncAnimation = true; visibleRect = dirtyRect; break; case nsDisplayTransform::PrerenderDecision::No: { // If we didn't prerender an animated frame in a preserve-3d context, // then we want disable async animations for the rest of the preserve-3d // (especially ancestors). if ((extend3DContext || combines3DTransformWithAncestors) && - decision.mDecision == nsDisplayTransform::PrerenderDecision::No && decision.mHasAnimations) { aBuilder->SavePreserves3DAllowAsyncAnimation(false); } const nsRect overflow = GetVisualOverflowRectRelativeToSelf(); if (overflow.IsEmpty() && !extend3DContext) { return; }
--- a/layout/painting/nsDisplayList.h +++ b/layout/painting/nsDisplayList.h @@ -7086,25 +7086,25 @@ class nsDisplayTransform : public nsDisp * and child 3D rendering context. * \see nsIFrame::BuildDisplayListForStackingContext(). */ bool IsTransformSeparator() const { return mIsTransformSeparator; } /** * This item is the boundary between parent and child 3D rendering * context. */ - bool IsLeafOf3DContext() { + bool IsLeafOf3DContext() const { return (IsTransformSeparator() || (!mFrame->Extend3DContext() && Combines3DTransformWithAncestors())); } /** * The backing frame of this item participates a 3D rendering * context. */ - bool IsParticipating3DContext() { + bool IsParticipating3DContext() const { return mFrame->Extend3DContext() || Combines3DTransformWithAncestors(); } void AddSizeOfExcludingThis(nsWindowSizes&) const override; private: void ComputeBounds(nsDisplayListBuilder* aBuilder); nsRect TransformUntransformedBounds(nsDisplayListBuilder* aBuilder,
--- a/netwerk/base/ProxyAutoConfig.cpp +++ b/netwerk/base/ProxyAutoConfig.cpp @@ -392,24 +392,23 @@ class MOZ_STACK_CLASS AutoPACErrorReport JSContext* mCx; public: explicit AutoPACErrorReporter(JSContext* aCx) : mCx(aCx) {} ~AutoPACErrorReporter() { if (!JS_IsExceptionPending(mCx)) { return; } - JS::RootedValue exn(mCx); - if (!JS_GetPendingException(mCx, &exn)) { + JS::ExceptionStack exnStack(mCx); + if (!JS::StealPendingExceptionStack(mCx, &exnStack)) { return; } - JS_ClearPendingException(mCx); - js::ErrorReport report(mCx); - if (!report.init(mCx, exn, js::ErrorReport::WithSideEffects)) { + JS::ErrorReportBuilder report(mCx); + if (!report.init(mCx, exnStack, JS::ErrorReportBuilder::WithSideEffects)) { JS_ClearPendingException(mCx); return; } PACLogErrorOrWarning(NS_LITERAL_STRING("Error"), report.report()); } };
--- a/services/sync/golden_gate/src/task.rs +++ b/services/sync/golden_gate/src/task.rs @@ -1,143 +1,140 @@ /* 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/. */ use std::{ fmt::Write, - mem, + mem, result, sync::{Arc, Weak}, }; use atomic_refcell::AtomicRefCell; use moz_task::{DispatchOptions, Task, TaskRunnable, ThreadPtrHandle, ThreadPtrHolder}; use nserror::nsresult; use nsstring::{nsACString, nsCString}; use sync15_traits::{ApplyResults, BridgedEngine, Guid}; use thin_vec::ThinVec; use xpcom::{ interfaces::{ mozIBridgedSyncEngineApplyCallback, mozIBridgedSyncEngineCallback, nsIEventTarget, }, RefPtr, }; -use crate::error::{self, BridgedError, Error}; +use crate::error::{BridgedError, Error, Result}; use crate::ferry::{Ferry, FerryResult}; /// A ferry task sends (or ferries) an operation to a bridged engine on a /// background thread or task queue, and ferries back an optional result to /// a callback. pub struct FerryTask<N: ?Sized + BridgedEngine> { /// A ferry task holds a weak reference to the bridged engine, and upgrades /// it to a strong reference when run on a background thread. This avoids /// scheduled ferries blocking finalization: if the main thread holds the /// only strong reference to the engine, it can be unwrapped (using /// `Arc::try_unwrap`) and dropped, either on the main thread, or as part of /// a teardown task. engine: Weak<N>, ferry: Ferry, callback: ThreadPtrHandle<mozIBridgedSyncEngineCallback>, - result: AtomicRefCell<Result<FerryResult, N::Error>>, + result: AtomicRefCell<result::Result<FerryResult, N::Error>>, } impl<N> FerryTask<N> where N: ?Sized + BridgedEngine + Send + Sync + 'static, N::Error: BridgedError, { /// Creates a task to fetch the engine's last sync time, in milliseconds. #[inline] pub fn for_last_sync( engine: &Arc<N>, callback: &mozIBridgedSyncEngineCallback, - ) -> error::Result<FerryTask<N>> { + ) -> Result<FerryTask<N>> { Self::with_ferry(engine, Ferry::LastSync, callback) } /// Creates a task to set the engine's last sync time, in milliseconds. #[inline] pub fn for_set_last_sync( engine: &Arc<N>, last_sync_millis: i64, callback: &mozIBridgedSyncEngineCallback, - ) -> error::Result<FerryTask<N>> { + ) -> Result<FerryTask<N>> { Self::with_ferry(engine, Ferry::SetLastSync(last_sync_millis), callback) } /// Creates a task to fetch the engine's sync ID. #[inline] pub fn for_sync_id( engine: &Arc<N>, callback: &mozIBridgedSyncEngineCallback, - ) -> error::Result<FerryTask<N>> { + ) -> Result<FerryTask<N>> { Self::with_ferry(engine, Ferry::SyncId, callback) } /// Creates a task to reset the engine's sync ID and all its local Sync /// metadata. #[inline] pub fn for_reset_sync_id( engine: &Arc<N>, callback: &mozIBridgedSyncEngineCallback, - ) -> error::Result<FerryTask<N>> { + ) -> Result<FerryTask<N>> { Self::with_ferry(engine, Ferry::ResetSyncId, callback) } /// Creates a task to compare the bridged engine's local sync ID with /// the `new_sync_id` from `meta/global`, and ferry back the final sync ID /// to use. #[inline] pub fn for_ensure_current_sync_id( engine: &Arc<N>, new_sync_id: &nsACString, callback: &mozIBridgedSyncEngineCallback, - ) -> error::Result<FerryTask<N>> { + ) -> Result<FerryTask<N>> { Self::with_ferry( engine, Ferry::EnsureCurrentSyncId(std::str::from_utf8(new_sync_id)?.into()), callback, ) } /// Creates a task to signal that the engine is about to sync. #[inline] pub fn for_sync_started( engine: &Arc<N>, callback: &mozIBridgedSyncEngineCallback, - ) -> error::Result<FerryTask<N>> { + ) -> Result<FerryTask<N>> { Self::with_ferry(engine, Ferry::SyncStarted, callback) } /// Creates a task to store incoming records. pub fn for_store_incoming( engine: &Arc<N>, incoming_envelopes_json: &[nsCString], callback: &mozIBridgedSyncEngineCallback, - ) -> error::Result<FerryTask<N>> { - let incoming_envelopes = incoming_envelopes_json.iter().try_fold( - Vec::with_capacity(incoming_envelopes_json.len()), - |mut envelopes, envelope| -> error::Result<_> { - envelopes.push(serde_json::from_slice(&*envelope)?); - Ok(envelopes) - }, - )?; + ) -> Result<FerryTask<N>> { + let incoming_envelopes = incoming_envelopes_json + .iter() + .map(|envelope| Ok(serde_json::from_slice(&*envelope)?)) + .collect::<Result<_>>()?; Self::with_ferry(engine, Ferry::StoreIncoming(incoming_envelopes), callback) } /// Creates a task to mark a subset of outgoing records as uploaded. This /// may be called multiple times per sync, or not at all if there are no /// records to upload. pub fn for_set_uploaded( engine: &Arc<N>, server_modified_millis: i64, uploaded_ids: &[nsCString], callback: &mozIBridgedSyncEngineCallback, - ) -> error::Result<FerryTask<N>> { + ) -> Result<FerryTask<N>> { let uploaded_ids = uploaded_ids .iter() .map(|id| Guid::from_slice(&*id)) .collect(); Self::with_ferry( engine, Ferry::SetUploaded(server_modified_millis, uploaded_ids), callback, @@ -146,77 +143,77 @@ where /// Creates a task to signal that all records have been uploaded, and /// the engine has been synced. This is called even if there were no /// records uploaded. #[inline] pub fn for_sync_finished( engine: &Arc<N>, callback: &mozIBridgedSyncEngineCallback, - ) -> error::Result<FerryTask<N>> { + ) -> Result<FerryTask<N>> { Self::with_ferry(engine, Ferry::SyncFinished, callback) } /// Creates a task to reset all local Sync state for the engine, without /// erasing user data. #[inline] pub fn for_reset( engine: &Arc<N>, callback: &mozIBridgedSyncEngineCallback, - ) -> error::Result<FerryTask<N>> { + ) -> Result<FerryTask<N>> { Self::with_ferry(engine, Ferry::Reset, callback) } /// Creates a task to erase all local user data for the engine. #[inline] pub fn for_wipe( engine: &Arc<N>, callback: &mozIBridgedSyncEngineCallback, - ) -> error::Result<FerryTask<N>> { + ) -> Result<FerryTask<N>> { Self::with_ferry(engine, Ferry::Wipe, callback) } /// Creates a task for a ferry. The `callback` is bound to the current /// thread, and will be called once, after the ferry returns from the /// background thread. fn with_ferry( engine: &Arc<N>, ferry: Ferry, callback: &mozIBridgedSyncEngineCallback, - ) -> error::Result<FerryTask<N>> { + ) -> Result<FerryTask<N>> { let name = ferry.name(); Ok(FerryTask { engine: Arc::downgrade(engine), ferry, callback: ThreadPtrHolder::new( cstr!("mozIBridgedSyncEngineCallback"), RefPtr::new(callback), )?, result: AtomicRefCell::new(Err(Error::DidNotRun(name).into())), }) } /// Dispatches the task to the given thread `target`. - pub fn dispatch(self, target: &nsIEventTarget) -> Result<(), Error> { + pub fn dispatch(self, target: &nsIEventTarget) -> Result<()> { let runnable = TaskRunnable::new(self.ferry.name(), Box::new(self))?; // `may_block` schedules the task on the I/O thread pool, since we // expect most operations to wait on I/O. runnable.dispatch_with_options(target, DispatchOptions::default().may_block(true))?; Ok(()) } } impl<N> FerryTask<N> where N: ?Sized + BridgedEngine, N::Error: BridgedError, { /// Runs the task on the background thread. This is split out into its own /// method to make error handling easier. - fn inner_run(&self) -> Result<FerryResult, N::Error> { + fn inner_run(&self) -> result::Result<FerryResult, N::Error> { let engine = match self.engine.upgrade() { Some(outer) => outer, None => return Err(Error::DidNotRun(self.ferry.name()).into()), }; Ok(match &self.ferry { Ferry::LastSync => FerryResult::LastSync(engine.last_sync()?), Ferry::SetLastSync(last_sync_millis) => { engine.set_last_sync(*last_sync_millis)?; @@ -259,17 +256,17 @@ impl<N> Task for FerryTask<N> where N: ?Sized + BridgedEngine, N::Error: BridgedError, { fn run(&self) { *self.result.borrow_mut() = self.inner_run(); } - fn done(&self) -> Result<(), nsresult> { + fn done(&self) -> result::Result<(), nsresult> { let callback = self.callback.get().unwrap(); match mem::replace( &mut *self.result.borrow_mut(), Err(Error::DidNotRun(self.ferry.name()).into()), ) { Ok(result) => unsafe { callback.HandleSuccess(result.into_variant().coerce()) }, Err(err) => { let mut message = nsCString::new(); @@ -282,89 +279,86 @@ where } /// An apply task ferries incoming records to an engine on a background /// thread, and ferries back records to upload. It's separate from /// `FerryTask` because its callback type is different. pub struct ApplyTask<N: ?Sized + BridgedEngine> { engine: Weak<N>, callback: ThreadPtrHandle<mozIBridgedSyncEngineApplyCallback>, - result: AtomicRefCell<Result<Vec<String>, N::Error>>, + result: AtomicRefCell<result::Result<Vec<String>, N::Error>>, } impl<N> ApplyTask<N> where N: ?Sized + BridgedEngine, N::Error: BridgedError, { /// Returns the task name for debugging. pub fn name() -> &'static str { concat!(module_path!(), "apply") } /// Runs the task on the background thread. - fn inner_run(&self) -> Result<Vec<String>, N::Error> { + fn inner_run(&self) -> result::Result<Vec<String>, N::Error> { let engine = match self.engine.upgrade() { Some(outer) => outer, None => return Err(Error::DidNotRun(Self::name()).into()), }; let ApplyResults { envelopes: outgoing_envelopes, .. } = engine.apply()?; - let outgoing_envelopes_json = outgoing_envelopes.iter().try_fold( - Vec::with_capacity(outgoing_envelopes.len()), - |mut envelopes, envelope| { - envelopes.push(serde_json::to_string(envelope)?); - Ok(envelopes) - }, - )?; + let outgoing_envelopes_json = outgoing_envelopes + .iter() + .map(|envelope| Ok(serde_json::to_string(envelope)?)) + .collect::<Result<_>>()?; Ok(outgoing_envelopes_json) } } impl<N> ApplyTask<N> where N: ?Sized + BridgedEngine + Send + Sync + 'static, N::Error: BridgedError, { /// Creates a task. The `callback` is bound to the current thread, and will /// be called once, after the records are applied on the background thread. pub fn new( engine: &Arc<N>, callback: &mozIBridgedSyncEngineApplyCallback, - ) -> error::Result<ApplyTask<N>> { + ) -> Result<ApplyTask<N>> { Ok(ApplyTask { engine: Arc::downgrade(engine), callback: ThreadPtrHolder::new( cstr!("mozIBridgedSyncEngineApplyCallback"), RefPtr::new(callback), )?, result: AtomicRefCell::new(Err(Error::DidNotRun(Self::name()).into())), }) } /// Dispatches the task to the given thread `target`. - pub fn dispatch(self, target: &nsIEventTarget) -> Result<(), Error> { + pub fn dispatch(self, target: &nsIEventTarget) -> Result<()> { let runnable = TaskRunnable::new(Self::name(), Box::new(self))?; runnable.dispatch_with_options(target, DispatchOptions::default().may_block(true))?; Ok(()) } } impl<N> Task for ApplyTask<N> where N: ?Sized + BridgedEngine, N::Error: BridgedError, { fn run(&self) { *self.result.borrow_mut() = self.inner_run(); } - fn done(&self) -> Result<(), nsresult> { + fn done(&self) -> result::Result<(), nsresult> { let callback = self.callback.get().unwrap(); match mem::replace( &mut *self.result.borrow_mut(), Err(Error::DidNotRun(Self::name()).into()), ) { Ok(envelopes) => { let result = envelopes .into_iter()
--- a/taskcluster/taskgraph/util/hash.py +++ b/taskcluster/taskgraph/util/hash.py @@ -35,13 +35,15 @@ def hash_paths(base_path, patterns): files = {} for pattern in patterns: found = list(finder.find(pattern)) if found: files.update(found) else: raise Exception('%s did not match anything' % pattern) for path in sorted(files.keys()): + if path.endswith(('.pyc', '.pyd', '.pyo')): + continue h.update(six.ensure_binary('{} {}\n'.format( hash_path(mozpath.abspath(mozpath.join(base_path, path))), mozpath.normsep(path) ))) return h.hexdigest()
--- a/toolkit/components/extensions/test/xpcshell/test_ext_background_telemetry.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_background_telemetry.js @@ -1,16 +1,21 @@ /* -*- Mode: indent-tabs-mode: nil; js-indent-level: 2 -*- */ /* vim: set sts=2 sw=2 et tw=80: */ "use strict"; const HISTOGRAM = "WEBEXT_BACKGROUND_PAGE_LOAD_MS"; const HISTOGRAM_KEYED = "WEBEXT_BACKGROUND_PAGE_LOAD_MS_BY_ADDONID"; add_task(async function test_telemetry() { + Services.prefs.setBoolPref( + "toolkit.telemetry.testing.overrideProductsCheck", + true + ); + let extension1 = ExtensionTestUtils.loadExtension({ background() { browser.test.sendMessage("loaded"); }, }); let extension2 = ExtensionTestUtils.loadExtension({ background() {
--- a/toolkit/components/extensions/test/xpcshell/test_ext_experiments.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_experiments.js @@ -163,17 +163,17 @@ add_task(async function test_bundled_exp { isPrivileged: false, temporarilyInstalled: true, shouldHaveExperiments: AddonSettings.EXPERIMENTS_ENABLED, }, { isPrivileged: false, temporarilyInstalled: false, - shouldHaveExperiments: false, + shouldHaveExperiments: AppConstants.MOZ_APP_NAME == "thunderbird", }, ]; async function background(shouldHaveExperiments) { if (shouldHaveExperiments) { await testFooExperiment(); } else { await testFooFailExperiment();
--- a/toolkit/components/extensions/test/xpcshell/test_ext_extension_content_telemetry.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_extension_content_telemetry.js @@ -10,16 +10,21 @@ server.registerDirectory("/data/", do_ge const BASE_URL = `http://localhost:${server.identity.primaryPort}/data`; add_task(async function test_telemetry() { function contentScript() { browser.test.sendMessage("content-script-run"); } + Services.prefs.setBoolPref( + "toolkit.telemetry.testing.overrideProductsCheck", + true + ); + let extension1 = ExtensionTestUtils.loadExtension({ manifest: { content_scripts: [ { matches: ["http://*/*/file_sample.html"], js: ["content_script.js"], run_at: "document_end", },
--- a/toolkit/components/extensions/test/xpcshell/test_ext_extension_startup_telemetry.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_extension_startup_telemetry.js @@ -13,16 +13,21 @@ function processKeyedSnapshot(snapshot) let res = {}; for (let key of Object.keys(snapshot)) { res[key] = snapshot[key].sum > 0; } return res; } add_task(async function test_telemetry() { + Services.prefs.setBoolPref( + "toolkit.telemetry.testing.overrideProductsCheck", + true + ); + let extension1 = ExtensionTestUtils.loadExtension({}); let extension2 = ExtensionTestUtils.loadExtension({}); clearHistograms(); assertHistogramEmpty(HISTOGRAM); assertKeyedHistogramEmpty(HISTOGRAM_KEYED);
--- a/toolkit/components/extensions/test/xpcshell/test_ext_startup_perf.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_startup_perf.js @@ -19,16 +19,20 @@ const STARTUP_MODULES = [ if (!Services.prefs.getBoolPref("extensions.webextensions.remote")) { STARTUP_MODULES.push( "resource://gre/modules/ExtensionChild.jsm", "resource://gre/modules/ExtensionPageChild.jsm" ); } +if (AppConstants.MOZ_APP_NAME == "thunderbird") { + STARTUP_MODULES.push("resource://gre/modules/ExtensionContent.jsm"); +} + AddonTestUtils.init(this); // Tests that only the minimal set of API scripts and modules are loaded at // startup for a simple extension. add_task(async function test_loaded_scripts() { await ExtensionTestUtils.startAddonManager(); let extension = ExtensionTestUtils.loadExtension({
--- a/toolkit/components/extensions/test/xpcshell/test_ext_storage_telemetry.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_storage_telemetry.js @@ -258,16 +258,21 @@ async function test_telemetry_background `No data recorded for histogram: ${id}.` ); } await contentPage.close(); } add_task(async function setup() { + Services.prefs.setBoolPref( + "toolkit.telemetry.testing.overrideProductsCheck", + true + ); + // Telemetry test setup needed to ensure that the builtin events are defined // and they can be collected and verified. await TelemetryController.testSetup(); // This is actually only needed on Android, because it does not properly support unified telemetry // and so, if not enabled explicitly here, it would make these tests to fail when running on a // non-Nightly build. const oldCanRecordBase = Services.telemetry.canRecordBase;
--- a/toolkit/components/extensions/test/xpcshell/test_ext_userScripts_telemetry.js +++ b/toolkit/components/extensions/test/xpcshell/test_ext_userScripts_telemetry.js @@ -34,16 +34,21 @@ add_task(async function test_userScripts scriptMetadata: { name: "test-user-script-telemetry", }, }); browser.test.sendMessage("userScript-registered"); } + Services.prefs.setBoolPref( + "toolkit.telemetry.testing.overrideProductsCheck", + true + ); + let testExtensionDef = { manifest: { permissions: ["http://*/*/file_sample.html"], user_scripts: { api_script: "api-script.js", }, }, background,
--- a/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell-common.ini @@ -20,24 +20,26 @@ skip-if = os == "android" # Android does [test_ext_background_telemetry.js] [test_ext_background_window_properties.js] skip-if = os == "android" [test_ext_browserSettings.js] [test_ext_browserSettings_homepage.js] skip-if = appname == "thunderbird" || os == "android" [test_ext_captivePortal.js] # As with test_captive_portal_service.js, we use the same limits here. -skip-if = os == "android" || (os == "mac" && debug) # CP service is disabled on Android, macosx1014/debug due to 1564534 +skip-if = appname == "thunderbird" || os == "android" || (os == "mac" && debug) # CP service is disabled on Android, macosx1014/debug due to 1564534 run-sequentially = node server exceptions dont replay well [test_ext_captivePortal_url.js] # As with test_captive_portal_service.js, we use the same limits here. -skip-if = os == "android" || (os == "mac" && debug) # CP service is disabled on Android, macosx1014/debug due to 1564534 +skip-if = appname == "thunderbird" || os == "android" || (os == "mac" && debug) # CP service is disabled on Android, macosx1014/debug due to 1564534 run-sequentially = node server exceptions dont replay well [test_ext_cookieBehaviors.js] +skip-if = appname == "thunderbird" [test_ext_cookies_firstParty.js] +skip-if = appname == "thunderbird" [test_ext_cookies_samesite.js] [test_ext_content_security_policy.js] skip-if = (os == "win" && debug) #Bug 1485567 [test_ext_contentscript_api_injection.js] [test_ext_contentscript_async_loading.js] skip-if = os == 'android' && debug # The generated script takes too long to load on Android debug [test_ext_contentscript_context.js] [test_ext_contentscript_context_isolation.js] @@ -64,38 +66,39 @@ skip-if = appname == "thunderbird" || os skip-if = appname == "thunderbird" || os == "android" [test_ext_downloads_search.js] skip-if = appname == "thunderbird" || os == "android" || tsan # tsan: bug 1612707 [test_ext_downloads_urlencoded.js] skip-if = appname == "thunderbird" || os == "android" [test_ext_error_location.js] [test_ext_eventpage_warning.js] [test_ext_experiments.js] -fail-if = appname == "thunderbird" [test_ext_extension.js] [test_ext_extensionPreferencesManager.js] [test_ext_extensionSettingsStore.js] [test_ext_extension_content_telemetry.js] skip-if = os == "android" # checking for telemetry needs to be updated: 1384923 [test_ext_extension_startup_telemetry.js] [test_ext_file_access.js] [test_ext_geckoProfiler_control.js] skip-if = os == "android" || tsan # Not shipped on Android. tsan: bug 1612707 [test_ext_geturl.js] [test_ext_idle.js] [test_ext_incognito.js] +skip-if = appname == "thunderbird" [test_ext_l10n.js] [test_ext_localStorage.js] [test_ext_management.js] skip-if = (os == "win" && !debug) #Bug 1419183 disable on Windows [test_ext_management_uninstall_self.js] [test_ext_messaging_startup.js] skip-if = appname == "thunderbird" || (os == "android" && debug) [test_ext_networkStatus.js] [test_ext_notifications_incognito.js] +skip-if = appname == "thunderbird" [test_ext_notifications_unsupported.js] [test_ext_onmessage_removelistener.js] skip-if = true # This test no longer tests what it is meant to test. [test_ext_permission_xhr.js] [test_ext_persistent_events.js] [test_ext_privacy.js] skip-if = appname == "thunderbird" || (os == "android" && debug) [test_ext_privacy_disable.js] @@ -135,26 +138,27 @@ skip-if = ccov && os == 'linux' # bug 16 [test_ext_startupData.js] [test_ext_startup_cache.js] skip-if = os == "android" [test_ext_startup_perf.js] [test_ext_startup_request_handler.js] [test_ext_storage.js] skip-if = os == "android" && debug [test_ext_storage_idb_data_migration.js] -skip-if = os == "android" && debug +skip-if = appname == "thunderbird" || (os == "android" && debug) [test_ext_storage_content.js] skip-if = os == "android" && debug [test_ext_storage_quota_exceeded_errors.js] skip-if = os == "android" # Bug 1564871 [test_ext_storage_managed.js] skip-if = os == "android" [test_ext_storage_managed_policy.js] -skip-if = os == "android" +skip-if = appname == "thunderbird" || os == "android" [test_ext_storage_sanitizer.js] +skip-if = appname == "thunderbird" [test_ext_storage_sync.js] head = head.js head_sync.js skip-if = appname == "thunderbird" || os == "android" [test_ext_storage_sync_crypto.js] skip-if = appname == "thunderbird" || os == "android" [test_ext_storage_tab.js] [test_ext_storage_telemetry.js] skip-if = os == "android" # checking for telemetry needs to be updated: 1384923 @@ -202,16 +206,17 @@ skip-if = os == "android" skip-if = appname == "thunderbird" || os == "android" # Bug 1350559 [test_ext_permissions_api.js] skip-if = appname == "thunderbird" || os == "android" # Bug 1350559 [test_ext_permissions_migrate.js] skip-if = appname == "thunderbird" || os == "android" # Bug 1350559 [test_ext_permissions_uninstall.js] skip-if = appname == "thunderbird" || os == "android" # Bug 1350559 [test_proxy_listener.js] +skip-if = appname == "thunderbird" [test_proxy_incognito.js] skip-if = os == "android" # incognito not supported on android [test_proxy_info_results.js] [test_proxy_userContextId.js] [test_webRequest_ancestors.js] [test_webRequest_cookies.js] [test_webRequest_filtering.js] [test_ext_brokenlinks.js]
--- a/toolkit/components/extensions/test/xpcshell/xpcshell.ini +++ b/toolkit/components/extensions/test/xpcshell/xpcshell.ini @@ -78,11 +78,11 @@ skip-if = os == 'android' && processor = skip-if = os == 'android' && processor == 'x86_64' [test_ext_ipcBlob.js] skip-if = os == 'android' && processor == 'x86_64' [test_ext_runtime_sendMessage_args.js] skip-if = os == 'android' && processor == 'x86_64' [include:xpcshell-common.ini] -skip-if = os != 'android' || (os == 'android' && processor == 'x86_64') # only android is left without e10s +run-if = appname == "thunderbird" || (os == 'android' && processor != 'x86_64') # Thunderbird and Android have no e10s [include:xpcshell-content.ini] -skip-if = os != 'android' || (os == 'android' && processor == 'x86_64') # only android is left without e10s +run-if = appname == "thunderbird" || (os == 'android' && processor != 'x86_64') # Thunderbird and Android have no e10s