author | Ryan VanderMeulen <ryanvm@gmail.com> |
Thu, 16 Jul 2015 12:45:32 -0400 | |
changeset 253159 | 61101cfa98d2cdcee1f6582023cdd041adb4d02b |
parent 253157 | 63ec4e50cb90205c4b1a74e29154e23b92e2e957 (diff) |
parent 253158 | d8023b434e25beed6cb5d38b57d6bb27aaf888df (current diff) |
child 253160 | 19f222fd79fe6651e313b2361c19561027c091af |
child 253262 | a0f4a688433d5d8ef7468f9b41227b5a93cd2308 |
push id | 62369 |
push user | ryanvm@gmail.com |
push date | Thu, 16 Jul 2015 16:45:58 +0000 |
treeherder | mozilla-inbound@61101cfa98d2 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 42.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/accessible/ipc/DocAccessibleParent.cpp +++ b/accessible/ipc/DocAccessibleParent.cpp @@ -62,16 +62,21 @@ DocAccessibleParent::AddSubtree(ProxyAcc } const AccessibleData& newChild = aNewTree[aIdx]; if (newChild.Role() > roles::LAST_ROLE) { NS_ERROR("invalid role"); return 0; } + if (mAccessibles.Contains(newChild.ID())) { + NS_ERROR("ID already in use"); + return 0; + } + auto role = static_cast<a11y::role>(newChild.Role()); ProxyAccessible* newProxy = new ProxyAccessible(newChild.ID(), aParent, this, role); aParent->AddChildAt(aIdxInParent, newProxy); mAccessibles.PutEntry(newChild.ID())->mProxy = newProxy; ProxyCreated(newProxy, newChild.Interfaces()); uint32_t accessibles = 1; @@ -90,16 +95,18 @@ DocAccessibleParent::AddSubtree(ProxyAcc } bool DocAccessibleParent::RecvHideEvent(const uint64_t& aRootID) { if (mShutdown) return true; + CheckDocTree(); + ProxyEntry* rootEntry = mAccessibles.GetEntry(aRootID); if (!rootEntry) { NS_ERROR("invalid root being removed!"); return true; } ProxyAccessible* root = rootEntry->mProxy; if (!root) { @@ -231,10 +238,22 @@ DocAccessibleParent::Destroy() mAccessibles.EnumerateEntries(ShutdownAccessibles, nullptr); ProxyDestroyed(this); if (mParentDoc) mParentDoc->RemoveChildDoc(this); else if (IsTopLevel()) GetAccService()->RemoteDocShutdown(this); } +void +DocAccessibleParent::CheckDocTree() const +{ + size_t childDocs = mChildDocs.Length(); + for (size_t i = 0; i < childDocs; i++) { + if (!mChildDocs[i] || mChildDocs[i]->mParentDoc != this) + MOZ_CRASH("document tree is broken!"); + + mChildDocs[i]->CheckDocTree(); + } +} + } // a11y } // mozilla
--- a/accessible/ipc/DocAccessibleParent.h +++ b/accessible/ipc/DocAccessibleParent.h @@ -145,16 +145,18 @@ private: enum { ALLOW_MEMMOVE = true }; ProxyAccessible* mProxy; }; uint32_t AddSubtree(ProxyAccessible* aParent, const nsTArray<AccessibleData>& aNewTree, uint32_t aIdx, uint32_t aIdxInParent); + void CheckDocTree() const; + static PLDHashOperator ShutdownAccessibles(ProxyEntry* entry, void* unused); nsTArray<DocAccessibleParent*> mChildDocs; DocAccessibleParent* mParentDoc; /* * Conceptually this is a map from IDs to proxies, but we store the ID in the * proxy object so we can't use a real map.
--- a/browser/app/profile/firefox.js +++ b/browser/app/profile/firefox.js @@ -1451,17 +1451,16 @@ pref("devtools.debugger.chrome-debugging pref("devtools.debugger.remote-host", "localhost"); pref("devtools.debugger.remote-timeout", 20000); pref("devtools.debugger.pause-on-exceptions", false); pref("devtools.debugger.ignore-caught-exceptions", true); pref("devtools.debugger.source-maps-enabled", true); pref("devtools.debugger.pretty-print-enabled", true); pref("devtools.debugger.auto-pretty-print", false); pref("devtools.debugger.auto-black-box", true); -pref("devtools.debugger.tracer", false); pref("devtools.debugger.workers", false); pref("devtools.debugger.promise", false); // The default Debugger UI settings pref("devtools.debugger.ui.panes-workers-and-sources-width", 200); pref("devtools.debugger.ui.panes-instruments-width", 300); pref("devtools.debugger.ui.panes-visible-on-startup", false); pref("devtools.debugger.ui.variables-sorting-enabled", true);
--- a/browser/modules/PanelFrame.jsm +++ b/browser/modules/PanelFrame.jsm @@ -178,19 +178,17 @@ let PanelFrame = { // first time load, wait for load and dispatch after load notificationFrame.addEventListener("load", function panelBrowserOnload(e) { notificationFrame.removeEventListener("load", panelBrowserOnload, true); initFrameShow(); }, true); } }); - // in overflow, the anchor is a normal toolbarbutton, in toolbar it is a badge button - let anchor = aWindow.document.getAnonymousElementByAttribute(anchorBtn, "class", "toolbarbutton-badge-container") || - aWindow.document.getAnonymousElementByAttribute(anchorBtn, "class", "toolbarbutton-icon"); + let anchor = aWindow.document.getAnonymousElementByAttribute(anchorBtn, "class", "toolbarbutton-icon"); // Bug 849216 - open the popup asynchronously so we avoid the auto-rollup // handling from preventing it being opened in some cases. Services.tm.mainThread.dispatch(function() { panel.openPopup(anchor, "bottomcenter topright", 0, 0, false, false); }, Ci.nsIThread.DISPATCH_NORMAL); if (aCallback) aCallback(notificationFrame);
--- a/browser/themes/linux/browser.css +++ b/browser/themes/linux/browser.css @@ -579,35 +579,35 @@ menuitem:not([type]):not(.menuitem-toolt :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1[open="true"], :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:hover:active { padding: 3px; } .findbar-button > .toolbarbutton-text, :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon, :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon, -:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-badge-container, +:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-badge-stack, :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-icon { -moz-margin-end: 0; padding: 2px 6px; border: 1px solid transparent; border-radius: 2px; transition-property: background-color, border-color; transition-duration: 150ms; } :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon, -:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-badge-container, +:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-badge-stack, :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-menubutton-button > .toolbarbutton-icon { padding: 3px 7px; } /* Help SDK icons fit: */ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon, -toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-container > .toolbarbutton-icon { +toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-stack > .toolbarbutton-icon { width: 16px; } :-moz-any(#TabsToolbar, #nav-bar) toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { /* XXXgijs box models strike again: this is 16px + 2 * 7px padding + 2 * 1px border (from the rules above) */ width: 32px; } @@ -615,17 +615,17 @@ toolbarbutton[constrain-size="true"][cui -moz-padding-start: 7px; -moz-padding-end: 5px; } .findbar-button:not(:-moz-any([checked="true"],[disabled="true"])):hover > .toolbarbutton-text, :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1[open]:not([disabled=true]) > .toolbarbutton-menubutton-button > .toolbarbutton-icon, :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):not([open]):hover > .toolbarbutton-menubutton-button > .toolbarbutton-icon, :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):not([open]):hover > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon, -:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-badge-container, +:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-badge-stack, :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):hover > .toolbarbutton-icon { background: var(--toolbarbutton-hover-background); border-width: 1px; border-style: solid; border-color: var(--toolbarbutton-hover-bordercolor); box-shadow: var(--toolbarbutton-hover-boxshadow); } @@ -633,17 +633,17 @@ toolbarbutton[constrain-size="true"][cui :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1[open] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon { margin-top: 4px; margin-bottom: 4px; } .findbar-button:not([disabled=true]):-moz-any([checked="true"],:hover:active) > .toolbarbutton-text, :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):-moz-any(:hover:active, [open="true"]) > .toolbarbutton-icon, :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1[open="true"] > .toolbarbutton-menubutton-dropmarker:not([disabled=true]) > .dropmarker-icon, -:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-container, +:-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-stack, :-moz-any(#TabsToolbar, #nav-bar) .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon { background: var(--toolbarbutton-active-background); box-shadow: var(--toolbarbutton-active-boxshadow); border-width: 1px; border-style: solid; border-color: var(--toolbarbutton-active-bordercolor); transition-duration: 10ms; }
--- a/browser/themes/osx/browser.css +++ b/browser/themes/osx/browser.css @@ -1062,17 +1062,17 @@ toolbar .toolbarbutton-1 > .toolbarbutto -moz-image-region: rect(36px, 1404px, 72px, 1368px); } #web-apps-button@toolbarButtonPressed@ { -moz-image-region: rect(36px, 1440px, 72px, 1404px); } :-moz-any(@primaryToolbarButtons@) > .toolbarbutton-icon, - :-moz-any(@primaryToolbarButtons@) > .toolbarbutton-badge-container > .toolbarbutton-icon, + :-moz-any(@primaryToolbarButtons@) > .toolbarbutton-badge-stack > .toolbarbutton-icon, :-moz-any(@primaryToolbarButtons@) > .toolbarbutton-menubutton-button > .toolbarbutton-icon { width: 18px; } #add-share-provider { list-style-image: url(chrome://browser/skin/menuPanel-small@2x.png); -moz-image-region: rect(0px, 192px, 32px, 160px); } @@ -1085,43 +1085,43 @@ toolbar .toolbarbutton-1 > .toolbarbutto /* Help 16px icons fit: */ toolbar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon { margin: 2px; } /* Help SDK icons fit: */ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon, -toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-container > .toolbarbutton-icon { +toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-stack > .toolbarbutton-icon { width: 16px; } #main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-icon, -#main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon, +#main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-badge-stack > .toolbarbutton-icon, #main-window:not([customizing]) .toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon { opacity: .4; } @media (-moz-mac-lion-theme) { #main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-icon, - #main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon, + #main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-badge-stack > .toolbarbutton-icon, #main-window:not([customizing]) .toolbarbutton-1 > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon, #main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-menu-dropmarker, #main-window:not([customizing]) .toolbarbutton-1[disabled="true"] > .toolbarbutton-menubutton-dropmarker, .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-icon, .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-text, - .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-badge-container > .toolbarbutton-icon, + .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-badge-stack > .toolbarbutton-icon, .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-menu-dropmarker, .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon, .toolbarbutton-1:not(:hover):-moz-window-inactive > .toolbarbutton-menubutton-button > .toolbarbutton-icon { opacity: .5; } #main-window:not([customizing]) .toolbarbutton-1:-moz-window-inactive[disabled="true"] > .toolbarbutton-icon, - #main-window:not([customizing]) .toolbarbutton-1:-moz-window-inactive[disabled="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon, + #main-window:not([customizing]) .toolbarbutton-1:-moz-window-inactive[disabled="true"] > .toolbarbutton-badge-stack > .toolbarbutton-icon, #main-window:not([customizing]) .toolbarbutton-1:-moz-window-inactive > .toolbarbutton-menubutton-button[disabled="true"] > .toolbarbutton-icon { opacity: .25; } } .toolbarbutton-1 > .toolbarbutton-menu-dropmarker, .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker { list-style-image: url(chrome://browser/skin/toolbarbutton-dropmarker.png);
--- a/browser/themes/shared/customizableui/customizeMode.inc.css +++ b/browser/themes/shared/customizableui/customizeMode.inc.css @@ -228,24 +228,24 @@ toolbarpaletteitem[place="panel"] { toolbarpaletteitem[notransition].panel-customization-placeholder, toolbarpaletteitem[notransition][place="toolbar"], toolbarpaletteitem[notransition][place="palette"], toolbarpaletteitem[notransition][place="panel"] { transition: none; } toolbarpaletteitem > toolbarbutton > .toolbarbutton-icon, -toolbarpaletteitem > toolbarbutton > .toolbarbutton-badge-container > .toolbarbutton-icon, +toolbarpaletteitem > toolbarbutton > .toolbarbutton-badge-stack > .toolbarbutton-icon, toolbarpaletteitem > toolbaritem.panel-wide-item, toolbarpaletteitem > toolbarbutton[type="menu-button"] { transition: transform .3s cubic-bezier(.6, 2, .75, 1.5) !important; } toolbarpaletteitem[mousedown] > toolbarbutton > .toolbarbutton-icon, -toolbarpaletteitem[mousedown] > toolbarbutton > .toolbarbutton-badge-container > .toolbarbutton-icon { +toolbarpaletteitem[mousedown] > toolbarbutton > .toolbarbutton-badge-stack > .toolbarbutton-icon { transform: scale(1.3); } toolbarpaletteitem[mousedown] > toolbaritem.panel-wide-item, toolbarpaletteitem[mousedown] > toolbarbutton[type="menu-button"] { transform: scale(1.1); }
--- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css +++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css @@ -307,19 +307,19 @@ toolbaritem[cui-areatype="menu-panel"][s * should have a min-width set so they abide by the width set above (which they do outside of * customize mode because they're in a flexed container) */ toolbarpaletteitem[place="panel"]:not([haswideitem=true]) > .toolbarbutton-1 { min-width: 0.01px; } /* Help SDK buttons fit in. */ toolbarpaletteitem[place="palette"] > toolbarbutton[constrain-size="true"] > .toolbarbutton-icon, -toolbarpaletteitem[place="palette"] > toolbarbutton[constrain-size="true"] > .toolbarbutton-badge-container > .toolbarbutton-icon, +toolbarpaletteitem[place="palette"] > toolbarbutton[constrain-size="true"] > .toolbarbutton-badge-stack > .toolbarbutton-icon, toolbarbutton[constrain-size="true"][cui-areatype="menu-panel"] > .toolbarbutton-icon, -toolbarbutton[constrain-size="true"][cui-areatype="menu-panel"] > .toolbarbutton-badge-container > .toolbarbutton-icon { +toolbarbutton[constrain-size="true"][cui-areatype="menu-panel"] > .toolbarbutton-badge-stack > .toolbarbutton-icon { height: 32px; width: 32px; } toolbarpaletteitem:-moz-any([place="palette"], [place="panel"]) > toolbaritem[sdkstylewidget="true"] > .toolbarbutton-1 > .toolbarbutton-icon { width: 32px; height: 32px; } @@ -427,20 +427,20 @@ toolbaritem[cui-areatype="menu-panel"][s } toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-item) > .toolbarbutton-text { text-align: center; } .panelUI-grid .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon, .panelUI-grid .toolbarbutton-1 > .toolbarbutton-icon, -.panelUI-grid .toolbarbutton-1 > .toolbarbutton-badge-container, +.panelUI-grid .toolbarbutton-1 > .toolbarbutton-badge-stack, .customization-palette .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon, .customization-palette .toolbarbutton-1 > .toolbarbutton-icon, -.customization-palette .toolbarbutton-1 > .toolbarbutton-badge-container, +.customization-palette .toolbarbutton-1 > .toolbarbutton-badge-stack, .panelUI-grid #bookmarks-toolbar-placeholder > .toolbarbutton-icon, .customization-palette #bookmarks-toolbar-placeholder > .toolbarbutton-icon, .panel-customization-placeholder-child > .toolbarbutton-icon { width: 32px; height: 32px; min-width: 32px; min-height: 32px; /* Explanation for the below formula (A / B - C) @@ -456,18 +456,18 @@ toolbaritem[cui-areatype="menu-panel"][s which means each horizontal margin should be the half the button's width - (46/2) px. */ margin: 4px calc(@menuPanelButtonWidth@ / 2 - 23px); } /* above we treat the container as the icon for the margins, that is so the /* badge itself is positioned correctly. Here we make sure that the icon itself /* has the minimum size we want, but no padding/margin. */ -.panelUI-grid .toolbarbutton-1 > .toolbarbutton-badge-container > .toolbarbutton-icon, -.customization-palette .toolbarbutton-1 > .toolbarbutton-badge-container > .toolbarbutton-icon { +.panelUI-grid .toolbarbutton-1 > .toolbarbutton-badge-stack > .toolbarbutton-icon, +.customization-palette .toolbarbutton-1 > .toolbarbutton-badge-stack > .toolbarbutton-icon { width: 32px; height: 32px; min-width: 32px; min-height: 32px; margin: 0; padding: 0; }
--- a/browser/themes/shared/menupanel.inc.css +++ b/browser/themes/shared/menupanel.inc.css @@ -186,49 +186,49 @@ #pocket-button[cui-areatype="menu-panel"][panel-multiview-anchor=true] { -moz-image-region: rect(32px, 992px, 64px, 960px); } toolbaritem[sdkstylewidget="true"] > toolbarbutton { -moz-image-region: rect(0, 832px, 32px, 800px); } - #loop-button[cui-areatype="menu-panel"] > .toolbarbutton-badge-container, - toolbarpaletteitem[place="palette"] > #loop-button > .toolbarbutton-badge-container { + #loop-button[cui-areatype="menu-panel"], + toolbarpaletteitem[place="palette"] > #loop-button { list-style-image: url(chrome://browser/skin/loop/menuPanel.png); -moz-image-region: rect(0, 32px, 32px, 0); } /* Make sure that the state icons are not shown in the customization palette. */ - toolbarpaletteitem[place="palette"] > #loop-button > .toolbarbutton-badge-container { + toolbarpaletteitem[place="palette"] > #loop-button { -moz-image-region: rect(0, 32px, 32px, 0) !important; } - #loop-button[cui-areatype="menu-panel"][state="disabled"] > .toolbarbutton-badge-container, - #loop-button[cui-areatype="menu-panel"][disabled="true"] > .toolbarbutton-badge-container { + #loop-button[cui-areatype="menu-panel"][state="disabled"], + #loop-button[cui-areatype="menu-panel"][disabled="true"] { -moz-image-region: rect(0, 64px, 32px, 32px); } - #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="error"] > .toolbarbutton-badge-container { + #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="error"] { -moz-image-region: rect(0, 96px, 32px, 64px); } - #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"] > .toolbarbutton-badge-container { + #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"] { -moz-image-region: rect(0, 128px, 32px, 96px); } - #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container { + #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) { -moz-image-region: rect(0, 160px, 32px, 128px); } - #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"] > .toolbarbutton-badge-container { + #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"] { -moz-image-region: rect(0, 192px, 32px, 160px); } - #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container { + #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) { -moz-image-region: rect(0, 224px, 32px, 192px); } /* Wide panel control icons */ #edit-controls@inAnyPanel@ > toolbarbutton, #zoom-controls@inAnyPanel@ > toolbarbutton, toolbarpaletteitem[place="palette"] > #edit-controls > toolbarbutton, @@ -370,49 +370,49 @@ toolbarpaletteitem[place="palette"] > #pocket-button { -moz-image-region: rect(0px, 1984px, 64px, 1920px); } #pocket-button[cui-areatype="menu-panel"][panel-multiview-anchor=true] { -moz-image-region: rect(64px, 1984px, 128px, 1920px); } - #loop-button[cui-areatype="menu-panel"] > .toolbarbutton-badge-container, - toolbarpaletteitem[place="palette"] > #loop-button > .toolbarbutton-badge-container { + #loop-button[cui-areatype="menu-panel"], + toolbarpaletteitem[place="palette"] > #loop-button { list-style-image: url(chrome://browser/skin/loop/menuPanel@2x.png); -moz-image-region: rect(0, 64px, 64px, 0); } /* Make sure that the state icons are not shown in the customization palette. */ - toolbarpaletteitem[place="palette"] > #loop-button > .toolbarbutton-badge-container { + toolbarpaletteitem[place="palette"] > #loop-button { -moz-image-region: rect(0, 64px, 64px, 0) !important; } - #loop-button[cui-areatype="menu-panel"][state="disabled"] > .toolbarbutton-badge-container, - #loop-button[cui-areatype="menu-panel"][disabled="true"] > .toolbarbutton-badge-container { + #loop-button[cui-areatype="menu-panel"][state="disabled"], + #loop-button[cui-areatype="menu-panel"][disabled="true"] { -moz-image-region: rect(0, 128px, 64px, 64px); } - #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="error"] > .toolbarbutton-badge-container { + #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="error"] { -moz-image-region: rect(0, 192px, 64px, 128px); } - #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"] > .toolbarbutton-badge-container { + #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"] { -moz-image-region: rect(0, 256px, 64px, 192px); } - #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container { + #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) { -moz-image-region: rect(0, 320px, 64px, 256px); } - #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"] > .toolbarbutton-badge-container { + #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"] { -moz-image-region: rect(0, 384px, 64px, 320px); } - #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container { + #loop-button[cui-areatype="menu-panel"]:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) { -moz-image-region: rect(0, 448px, 64px, 384px); } #new-tab-button[cui-areatype="menu-panel"], toolbarpaletteitem[place="palette"] > #new-tab-button { -moz-image-region: rect(0px, 1088px, 64px, 1024px); }
--- a/browser/themes/shared/toolbarbuttons.inc.css +++ b/browser/themes/shared/toolbarbuttons.inc.css @@ -179,47 +179,47 @@ toolbar[brighttext] #sync-button[status= #panic-button:-moz-locale-dir(rtl) > .toolbarbutton-icon { transform: scaleX(-1); } #web-apps-button[cui-areatype="toolbar"] { -moz-image-region: rect(0, 720px, 18px, 702px); } -#loop-button > .toolbarbutton-badge-container { +#loop-button { list-style-image: url(chrome://browser/skin/loop/toolbar.png); -moz-image-region: rect(0, 18px, 18px, 0); } -toolbar[brighttext] #loop-button > .toolbarbutton-badge-container { +toolbar[brighttext] #loop-button { list-style-image: url(chrome://browser/skin/loop/toolbar-inverted.png); } -#loop-button[state="disabled"] > .toolbarbutton-badge-container, -#loop-button[disabled="true"] > .toolbarbutton-badge-container { +#loop-button[state="disabled"], +#loop-button[disabled="true"] { -moz-image-region: rect(0, 36px, 18px, 18px); } -#loop-button:not([disabled="true"])[state="error"] > .toolbarbutton-badge-container { +#loop-button:not([disabled="true"])[state="error"] { -moz-image-region: rect(0, 54px, 18px, 36px); } -#loop-button:not([disabled="true"])[state="action"] > .toolbarbutton-badge-container { +#loop-button:not([disabled="true"])[state="action"] { -moz-image-region: rect(0, 72px, 18px, 54px); } -#loop-button:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container { +#loop-button:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) { -moz-image-region: rect(0, 90px, 18px, 72px); } -#loop-button:not([disabled="true"])[state="active"] > .toolbarbutton-badge-container { +#loop-button:not([disabled="true"])[state="active"] { -moz-image-region: rect(0, 108px, 18px, 90px); } -#loop-button:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container { +#loop-button:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) { -moz-image-region: rect(0, 126px, 18px, 108px); } #webide-button[cui-areatype="toolbar"] { -moz-image-region: rect(0, 738px, 18px, 720px); } #pocket-button[cui-areatype="toolbar"] { @@ -425,43 +425,43 @@ toolbar[brighttext] #loop-button > .tool #pocket-button[cui-areatype="toolbar"][open] { %ifdef XP_MACOSX -moz-image-region: rect(72px, 1548px, 108px, 1512px); %else -moz-image-region: rect(36px, 1548px, 72px, 1512px); %endif } - #loop-button > .toolbarbutton-badge-container { + #loop-button { list-style-image: url("chrome://browser/skin/loop/toolbar@2x.png"); -moz-image-region: rect(0, 36px, 36px, 0); } - toolbar[brighttext] #loop-button > .toolbarbutton-badge-container { + toolbar[brighttext] #loop-button { list-style-image: url("chrome://browser/skin/loop/toolbar-inverted@2x.png"); } - #loop-button[state="disabled"] > .toolbarbutton-badge-container, - #loop-button[disabled="true"] > .toolbarbutton-badge-container { + #loop-button[state="disabled"], + #loop-button[disabled="true"] { -moz-image-region: rect(0, 72px, 36px, 36px); } - #loop-button:not([disabled="true"])[state="error"] > .toolbarbutton-badge-container { + #loop-button:not([disabled="true"])[state="error"] { -moz-image-region: rect(0, 108px, 36px, 72px); } - #loop-button:not([disabled="true"])[state="action"] > .toolbarbutton-badge-container { + #loop-button:not([disabled="true"])[state="action"] { -moz-image-region: rect(0, 144px, 36px, 108px); } - #loop-button:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container { + #loop-button:not([disabled="true"])[state="action"]:-moz-any(:hover,:hover:active,[open]) { -moz-image-region: rect(0, 180px, 36px, 144px); } - #loop-button:not([disabled="true"])[state="active"] > .toolbarbutton-badge-container { + #loop-button:not([disabled="true"])[state="active"] { -moz-image-region: rect(0, 216px, 36px, 180px); } - #loop-button:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) > .toolbarbutton-badge-container { + #loop-button:not([disabled="true"])[state="active"]:-moz-any(:hover,:hover:active,[open]) { -moz-image-region: rect(0, 252px, 36px, 216px); } } %endif
--- a/browser/themes/windows/browser.css +++ b/browser/themes/windows/browser.css @@ -588,23 +588,23 @@ menuitem.bookmark-item { %include ../shared/menupanel.inc.css @media (-moz-windows-theme: luna-silver) and (max-resolution: 1dppx) { :-moz-any(@primaryToolbarButtons@), #bookmarks-menu-button.toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon { list-style-image: url("chrome://browser/skin/Toolbar-lunaSilver.png"); } - #loop-button > .toolbarbutton-badge-container { + #loop-button { list-style-image: url(chrome://browser/skin/loop/toolbar-lunaSilver.png) } } @media (-moz-windows-theme: luna-silver) and (min-resolution: 1.1dppx) { - #loop-button > .toolbarbutton-badge-container { + #loop-button { list-style-image: url(chrome://browser/skin/loop/toolbar-lunaSilver@2x.png) } } #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-icon, #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menu-dropmarker, #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-dropmarker, #main-window:not([customizing]) .toolbarbutton-1[disabled=true] > .toolbarbutton-menubutton-button > .toolbarbutton-icon, @@ -669,30 +669,30 @@ toolbar[brighttext] .toolbarbutton-1 > . #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker { -moz-padding-start: 0; -moz-box-align: center; } .findbar-button > .toolbarbutton-text, #nav-bar .toolbarbutton-1 > .toolbarbutton-icon, #nav-bar .toolbarbutton-1 > .toolbarbutton-text, -#nav-bar .toolbarbutton-1 > .toolbarbutton-badge-container, +#nav-bar .toolbarbutton-1 > .toolbarbutton-badge-stack, #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon, #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon, @conditionalForwardWithUrlbar@ > .toolbarbutton-1:-moz-any([disabled],:not([open]):not([disabled]):not(:active)) > .toolbarbutton-icon { padding: var(--toolbarbutton-vertical-inner-padding) 6px; border: 1px solid; border-color: transparent; transition-property: background-color, border-color; transition-duration: 150ms; } toolbarbutton[cui-areatype="toolbar"] > :-moz-any(@nestedButtons@) > .toolbarbutton-icon, :-moz-any(@primaryToolbarButtons@):-moz-any([cui-areatype="toolbar"],:not([cui-areatype])):not(:-moz-any(@nestedButtons@)) > .toolbarbutton-icon, -:-moz-any(@primaryToolbarButtons@):-moz-any([cui-areatype="toolbar"],:not([cui-areatype])) > .toolbarbutton-badge-container > .toolbarbutton-icon, +:-moz-any(@primaryToolbarButtons@):-moz-any([cui-areatype="toolbar"],:not([cui-areatype])) > .toolbarbutton-badge-stack > .toolbarbutton-icon, :-moz-any(@primaryToolbarButtons@):-moz-any([cui-areatype="toolbar"],:not([cui-areatype])) > .toolbarbutton-menubutton-button > .toolbarbutton-icon, #bookmarks-menu-button[cui-areatype="toolbar"] > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon { width: 18px; } #nav-bar toolbarbutton[cui-areatype="toolbar"] > :-moz-any(@nestedButtons@) > .toolbarbutton-icon, #nav-bar :-moz-any(@primaryToolbarButtons@):-moz-any([cui-areatype="toolbar"],:not([cui-areatype])) > .toolbarbutton-icon, #nav-bar :-moz-any(@primaryToolbarButtons@):-moz-any([cui-areatype="toolbar"],:not([cui-areatype])) > .toolbarbutton-menubutton-button > .toolbarbutton-icon, @@ -723,40 +723,40 @@ toolbarbutton[cui-areatype="toolbar"] > --toolbarbutton-checkedhover-backgroundcolor: rgba(90%,90%,90%,.4); --toolbarbutton-combined-backgroundimage: linear-gradient(hsla(210,54%,20%,.2) 0, hsla(210,54%,20%,.2) 18px); --toolbarbutton-combined-boxshadow: 0 0 0 1px hsla(0,0%,100%,.2); } .findbar-button > .toolbarbutton-text, #nav-bar .toolbarbutton-1 > .toolbarbutton-icon, #nav-bar .toolbarbutton-1 > .toolbarbutton-text, - #nav-bar .toolbarbutton-1 > .toolbarbutton-badge-container, + #nav-bar .toolbarbutton-1 > .toolbarbutton-badge-stack, #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button > .toolbarbutton-icon, #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon { background-color: hsla(210,32%,93%,0); background-origin: padding-box; border-radius: 2px; border-color: hsla(210,54%,20%,0) hsla(210,54%,20%,0) hsla(210,54%,20%,0); box-shadow: 0 1px hsla(0,0%,100%,0) inset, 0 1px hsla(210,54%,20%,0), 0 0 2px hsla(210,54%,20%,0); transition-property: background-color, border-color, box-shadow; transition-duration: 150ms; } } #nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-icon, -#nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-badge-container, +#nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-badge-stack, #nav-bar .toolbarbutton-1:not(:-moz-any(@primaryToolbarButtons@)) > .toolbarbutton-menubutton-button > .toolbarbutton-icon { padding: calc(var(--toolbarbutton-vertical-inner-padding) + 1px) 7px; } /* Help SDK icons fit: */ toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon, -toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-container > .toolbarbutton-icon { +toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-badge-stack > .toolbarbutton-icon { width: 16px; } #nav-bar toolbarbutton[constrain-size="true"][cui-areatype="toolbar"] > .toolbarbutton-icon { /* XXXgijs box models strike again: this is 16px + 2 * 7px padding + 2 * 1px border (from the rules above) */ width: 32px; } @@ -816,17 +816,17 @@ toolbarbutton[constrain-size="true"][cui } .findbar-button:not(:-moz-any([checked="true"],[disabled="true"])):hover > .toolbarbutton-text, #nav-bar .toolbarbutton-1:not([disabled=true]) > .toolbarbutton-menubutton-button[open] + .toolbarbutton-menubutton-dropmarker > .dropmarker-icon, #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-button > .toolbarbutton-icon, #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any(:hover,[open]) > .toolbarbutton-menubutton-dropmarker > .dropmarker-icon, #nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-icon, #nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-text, -#nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-badge-container, +#nav-bar .toolbarbutton-1:not([disabled=true]):not([checked]):not([open]):not(:active):hover > .toolbarbutton-badge-stack, @conditionalForwardWithUrlbar@ > #forward-button:not([open]):not(:active):not([disabled]):hover > .toolbarbutton-icon, #nav-bar .toolbarbutton-1:not([buttonover]):not([open]):not(:active):hover > .toolbarbutton-menubutton-dropmarker:not([disabled]) > .dropmarker-icon { background: var(--toolbarbutton-hover-background); border-color: var(--toolbarbutton-hover-bordercolor); box-shadow: var(--toolbarbutton-hover-boxshadow); } @media (-moz-os-version: windows-xp), @@ -861,33 +861,33 @@ toolbarbutton[constrain-size="true"][cui } } .findbar-button:not([disabled=true]):-moz-any([checked="true"],:hover:active) > .toolbarbutton-text, #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):-moz-any(:hover:active, [open]) > .toolbarbutton-icon, #nav-bar .toolbarbutton-1[open] > .toolbarbutton-menubutton-dropmarker:not([disabled=true]) > .dropmarker-icon, #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon, #nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-text, -#nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-container { +#nav-bar .toolbarbutton-1:not([disabled=true]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-stack { background: var(--toolbarbutton-active-background); border-color: var(--toolbarbutton-active-bordercolor); box-shadow: var(--toolbarbutton-active-boxshadow); transition-duration: 10ms; } @media (-moz-os-version: windows-xp), (-moz-os-version: windows-vista), (-moz-os-version: windows-win7) { /* < Win8 */ .findbar-button:not([disabled=true]):-moz-any([checked="true"],:hover:active) > .toolbarbutton-text, #nav-bar .toolbarbutton-1 > .toolbarbutton-menubutton-button:not([disabled=true]):-moz-any(:hover:active, [open]) > .toolbarbutton-icon, #nav-bar .toolbarbutton-1[open] > .toolbarbutton-menubutton-dropmarker:not([disabled]) > .dropmarker-icon, #nav-bar .toolbarbutton-1:not([disabled]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-icon, #nav-bar .toolbarbutton-1:not([disabled]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-text, - #nav-bar .toolbarbutton-1:not([disabled]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-container { + #nav-bar .toolbarbutton-1:not([disabled]):-moz-any([open],[checked],:hover:active) > .toolbarbutton-badge-stack { text-shadow: none; transition: none; } #nav-bar .toolbarbutton-1:-moz-any(:hover,[open]) > .toolbarbutton-menubutton-dropmarker:not([disabled]) > .dropmarker-icon { -moz-border-start-color: hsla(210,54%,20%,.35); }
--- a/caps/nsIScriptSecurityManager.idl +++ b/caps/nsIScriptSecurityManager.idl @@ -21,17 +21,17 @@ class DomainPolicyClone; } } %} [ptr] native JSContextPtr(JSContext); [ptr] native JSObjectPtr(JSObject); [ptr] native DomainPolicyClonePtr(mozilla::dom::DomainPolicyClone); -[scriptable, uuid(f4c578b8-5bac-4ba1-9582-f1140e09a3b4)] +[scriptable, uuid(50418f5c-b0d8-42c3-ba5d-efffb6927e1c)] interface nsIScriptSecurityManager : nsISupports { /** * For each of these hooks returning NS_OK means 'let the action continue'. * Returning an error code means 'veto the action'. XPConnect will return * false to the js engine if the action is vetoed. The implementor of this * interface is responsible for setting a JS exception into the JSContext * if that is appropriate. @@ -197,16 +197,30 @@ interface nsIScriptSecurityManager : nsI * Returns a unique nonce principal with |originAttributes|. * See nsIPrincipal.h for a description of origin attributes, and * SystemDictionaries.webidl for a list of origin attributes and their defaults. */ [implicit_jscontext] nsIPrincipal createNullPrincipal(in jsval originAttributes); /** + * Creates an expanded principal whose capabilities are the union of the + * given principals. An expanded principal has an asymmetric privilege + * relationship with its sub-principals (that is to say, it subsumes the + * sub-principals, but the sub-principals do not subsume it), even if + * there's only one. This presents a legitimate use-case for making an + * expanded principal around a single sub-principal, which we do frequently. + * + * Expanded principals cannot have origin attributes themselves, but rather + * have them through their sub-principals - so we don't accept them here. + */ + nsIPrincipal createExpandedPrincipal([array, size_is(aLength)] in nsIPrincipal aPrincipalArray, + [optional] in unsigned long aLength); + + /** * Returns OK if aSourceURI and target have the same "origin" * (scheme, host, and port). * ReportError flag suppresses error reports for functions that * don't need reporting. */ void checkSameOriginURI(in nsIURI aSourceURI, in nsIURI aTargetURI, in boolean reportError);
--- a/caps/nsScriptSecurityManager.cpp +++ b/caps/nsScriptSecurityManager.cpp @@ -1,10 +1,10 @@ /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ -/* vim: set ts=4 et sw=4 tw=80: */ +/* 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 "nsScriptSecurityManager.h" #include "mozilla/ArrayUtils.h" @@ -1023,16 +1023,31 @@ nsScriptSecurityManager::CreateNullPrinc } nsCOMPtr<nsIPrincipal> prin = nsNullPrincipal::Create(attrs); NS_ENSURE_TRUE(prin, NS_ERROR_FAILURE); prin.forget(aPrincipal); return NS_OK; } NS_IMETHODIMP +nsScriptSecurityManager::CreateExpandedPrincipal(nsIPrincipal** aPrincipalArray, uint32_t aLength, + nsIPrincipal** aResult) +{ + nsTArray<nsCOMPtr<nsIPrincipal>> principals; + principals.SetCapacity(aLength); + for (uint32_t i = 0; i < aLength; ++i) { + principals.AppendElement(aPrincipalArray[i]); + } + + nsCOMPtr<nsIPrincipal> p = new nsExpandedPrincipal(principals); + p.forget(aResult); + return NS_OK; +} + +NS_IMETHODIMP nsScriptSecurityManager::GetAppCodebasePrincipal(nsIURI* aURI, uint32_t aAppId, bool aInMozBrowser, nsIPrincipal** aPrincipal) { NS_ENSURE_TRUE(aAppId != nsIScriptSecurityManager::UNKNOWN_APP_ID, NS_ERROR_INVALID_ARG);
--- a/caps/tests/unit/test_origin.js +++ b/caps/tests/unit/test_origin.js @@ -42,17 +42,17 @@ function run_test() { do_check_eq(exampleOrg.origin, 'http://example.org'); checkOriginAttributes(exampleOrg); var exampleCom = ssm.createCodebasePrincipal(makeURI('https://www.example.com:123'), {}); do_check_eq(exampleCom.origin, 'https://www.example.com:123'); checkOriginAttributes(exampleCom); var nullPrin = Cu.getObjectPrincipal(new Cu.Sandbox(null)); do_check_true(/^moz-nullprincipal:\{([0-9]|[a-z]|\-){36}\}$/.test(nullPrin.origin)); checkOriginAttributes(nullPrin); - var ep = Cu.getObjectPrincipal(new Cu.Sandbox([exampleCom, nullPrin, exampleOrg])); + var ep = ssm.createExpandedPrincipal([exampleCom, nullPrin, exampleOrg]); checkOriginAttributes(ep); checkCrossOrigin(exampleCom, exampleOrg); checkCrossOrigin(exampleOrg, nullPrin); // nsEP origins should be in lexical order. do_check_eq(ep.origin, `[Expanded Principal [${exampleOrg.origin}, ${exampleCom.origin}, ${nullPrin.origin}]]`); // Make sure createCodebasePrincipal does what the rest of gecko does.
--- a/configure.in +++ b/configure.in @@ -471,26 +471,46 @@ esac AC_SUBST(MACOSX_DEPLOYMENT_TARGET) dnl ======================================================== dnl Special MacOS X checks dnl ======================================================== if test -n "$MACOSX_DEPLOYMENT_TARGET" -a -n "$MOZ_RUST"; then - AC_MSG_CHECKING([MacOS X compatibility with rust]) - # rustc doesn't support MacOS X 10.6 or earlier. + AC_MSG_CHECKING([rustc compatibility with MacOS X]) + # Stock rustc doesn't support MacOS X 10.6 or earlier. # https://github.com/rust-lang/rust/issues/25342 _MACOSX_TARGET_MINOR=`echo "$MACOSX_DEPLOYMENT_TARGET" | cut -d. -f2` if test "$_MACOSX_TARGET_MINOR" -lt 7; then + dnl Test C linkage against rust code to see if the rust + dnl toolchain output is compatible. + cat > conftest.rs <<EOF + [#[no_mangle]] + pub extern fn rusty_answer() -> u8 { 42 } +EOF + ac_try="$RUSTC --crate-type staticlib -o conftest.a conftest.rs >/dev/null" + AC_TRY_EVAL(ac_try) + save_LDFLAGS=$LDFLAGS + LDFLAGS="$LDFLAGS conftest.a -lpthread -lm" + AC_TRY_LINK_FUNC([rusty_answer], [ + AC_MSG_RESULT([$MACOSX_DEPLOYMENT_TARGET is ok with this rustc]) + ], [ + AC_MSG_RESULT([cannot link on $MACOSX_DEPLOYMENT_TARGET]) + MOZ_RUST= + ]) + LDFLAGS=$save_LDFLAGS + rm -rf conftest* + else + AC_MSG_RESULT([$MACOSX_DEPLOYMENT_TARGET is ok]) + fi + if test -z "$MOZ_RUST"; then AC_MSG_ERROR([rustc does not support MacOS X $MACOSX_DEPLOYMENT_TARGET Add 'ac_add_options --enable-macos-target=10.7' (or later) - to mozconfig, or disable rust support.]) - else - AC_MSG_RESULT([$MACOSX_DEPLOYMENT_TARGET is ok]) + to mozconfig, disable rust support, or use an alternate toolchain.]) fi fi dnl ======================================================== dnl Special win32 checks dnl ======================================================== # Target the Windows 8.1 SDK by default
--- a/docshell/shistory/nsSHistory.cpp +++ b/docshell/shistory/nsSHistory.cpp @@ -1310,17 +1310,19 @@ nsSHistory::RemoveDuplicate(int32_t aInd int32_t compareIndex = aKeepNext ? aIndex + 1 : aIndex - 1; nsCOMPtr<nsISHEntry> root1, root2; GetEntryAtIndex(aIndex, false, getter_AddRefs(root1)); GetEntryAtIndex(compareIndex, false, getter_AddRefs(root2)); if (IsSameTree(root1, root2)) { nsCOMPtr<nsISHTransaction> txToRemove, txToKeep, txNext, txPrev; GetTransactionAtIndex(aIndex, getter_AddRefs(txToRemove)); GetTransactionAtIndex(compareIndex, getter_AddRefs(txToKeep)); - NS_ENSURE_TRUE(txToRemove, false); + if (!txToRemove) { + return false; + } NS_ENSURE_TRUE(txToKeep, false); txToRemove->GetNext(getter_AddRefs(txNext)); txToRemove->GetPrev(getter_AddRefs(txPrev)); txToRemove->SetNext(nullptr); txToRemove->SetPrev(nullptr); if (aKeepNext) { if (txPrev) { txPrev->SetNext(txToKeep);
--- a/dom/audiochannel/AudioChannelService.cpp +++ b/dom/audiochannel/AudioChannelService.cpp @@ -202,18 +202,17 @@ NS_INTERFACE_MAP_BEGIN(AudioChannelServi NS_INTERFACE_MAP_ENTRY(nsIAudioChannelService) NS_INTERFACE_MAP_ENTRY(nsIObserver) NS_INTERFACE_MAP_END NS_IMPL_ADDREF(AudioChannelService) NS_IMPL_RELEASE(AudioChannelService) AudioChannelService::AudioChannelService() - : mDisabled(false) - , mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN) + : mDefChannelChildID(CONTENT_PROCESS_ID_UNKNOWN) , mTelephonyChannel(false) , mContentOrNormalChannel(false) , mAnyChannel(false) { if (IsParentProcess()) { nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); if (obs) { obs->AddObserver(this, "ipc:content-shutdown", false); @@ -233,20 +232,16 @@ AudioChannelService::AudioChannelService AudioChannelService::~AudioChannelService() { } void AudioChannelService::RegisterAudioChannelAgent(AudioChannelAgent* aAgent, AudioChannel aChannel) { - if (mDisabled) { - return; - } - uint64_t windowID = aAgent->WindowID(); AudioChannelWindow* winData = GetWindowData(windowID); if (!winData) { winData = new AudioChannelWindow(windowID); mWindows.AppendElement(winData); } MOZ_ASSERT(!winData->mAgents.Contains(aAgent)); @@ -267,20 +262,16 @@ AudioChannelService::RegisterAudioChanne } MaybeSendStatusUpdate(); } void AudioChannelService::UnregisterAudioChannelAgent(AudioChannelAgent* aAgent) { - if (mDisabled) { - return; - } - AudioChannelWindow* winData = GetWindowData(aAgent->WindowID()); if (!winData) { return; } if (winData->mAgents.Contains(aAgent)) { int32_t channel = aAgent->AudioChannelType(); uint64_t windowID = aAgent->WindowID(); @@ -461,18 +452,18 @@ AudioChannelService::AnyAudioChannelIsAc return false; } NS_IMETHODIMP AudioChannelService::Observe(nsISupports* aSubject, const char* aTopic, const char16_t* aData) { if (!strcmp(aTopic, "xpcom-shutdown")) { - mDisabled = true; mWindows.Clear(); + Shutdown(); } #ifdef MOZ_WIDGET_GONK // To process the volume control on each audio channel according to // change of settings else if (!strcmp(aTopic, "mozsettings-changed")) { RootedDictionary<SettingChangeNotification> setting(nsContentUtils::RootingCxForThread()); if (!WrappedJSToDictionary(aSubject, setting)) {
--- a/dom/audiochannel/AudioChannelService.h +++ b/dom/audiochannel/AudioChannelService.h @@ -38,21 +38,16 @@ public: /** * Returns the AudioChannelServce singleton. * If AudioChannelServce is not exist, create and return new one. * Only to be called from main thread. */ static already_AddRefed<AudioChannelService> GetOrCreate(); - /** - * Shutdown the singleton. - */ - static void Shutdown(); - static bool IsAudioChannelMutedByDefault(); /** * Any audio channel agent that starts playing should register itself to * this service, sharing the AudioChannel. */ void RegisterAudioChannelAgent(AudioChannelAgent* aAgent, AudioChannel aChannel); @@ -131,16 +126,21 @@ public: void ChildStatusReceived(uint64_t aChildID, bool aTelephonyChannel, bool aContentOrNormalChannel, bool aAnyChannel); private: AudioChannelService(); ~AudioChannelService(); + /** + * Shutdown the singleton. + */ + static void Shutdown(); + void MaybeSendStatusUpdate(); bool ContentOrNormalChannelIsActive(); /* Send the default-volume-channel-changed notification */ void SetDefaultVolumeControlChannelInternal(int32_t aChannel, bool aVisible, uint64_t aChildID); @@ -199,18 +199,16 @@ private: nsTObserverArray<nsAutoPtr<AudioChannelWindow>> mWindows; nsTObserverArray<nsAutoPtr<AudioChannelChildStatus>> mPlayingChildren; #ifdef MOZ_WIDGET_GONK nsTArray<SpeakerManagerService*> mSpeakerManager; #endif - bool mDisabled; - nsCOMPtr<nsIRunnable> mRunnable; uint64_t mDefChannelChildID; // These boolean are used to know if we have to send an status update to the // service running in the main process. bool mTelephonyChannel; bool mContentOrNormalChannel;
--- a/dom/base/nsFocusManager.cpp +++ b/dom/base/nsFocusManager.cpp @@ -712,26 +712,16 @@ nsFocusManager::WindowRaised(nsIDOMWindo nsCOMPtr<nsPIDOMWindow> currentWindow; nsCOMPtr<nsIContent> currentFocus = GetFocusedDescendant(window, true, getter_AddRefs(currentWindow)); NS_ASSERTION(currentWindow, "window raised with no window current"); if (!currentWindow) return NS_OK; - nsCOMPtr<nsIDocShell> currentDocShell = currentWindow->GetDocShell(); - - nsCOMPtr<nsIPresShell> presShell = currentDocShell->GetPresShell(); - if (presShell) { - // disable selection mousedown state on activation - // XXXndeakin P3 not sure if this is necessary, but it doesn't hurt - nsRefPtr<nsFrameSelection> frameSelection = presShell->FrameSelection(); - frameSelection->SetDragState(false); - } - // If there is no nsIXULWindow, then this is an embedded or child process window. // Pass false for aWindowRaised so that commands get updated. nsCOMPtr<nsIXULWindow> xulWin(do_GetInterface(baseWindow)); Focus(currentWindow, currentFocus, 0, true, false, xulWin != nullptr, true); return NS_OK; } @@ -759,16 +749,29 @@ nsFocusManager::WindowLowered(nsIDOMWind } if (mActiveWindow != window) return NS_OK; // clear the mouse capture as the active window has changed nsIPresShell::SetCapturingContent(nullptr, 0); + // In addition, reset the drag state to ensure that we are no longer in + // drag-select mode. + if (mFocusedWindow) { + nsCOMPtr<nsIDocShell> docShell = mFocusedWindow->GetDocShell(); + if (docShell) { + nsCOMPtr<nsIPresShell> presShell = docShell->GetPresShell(); + if (presShell) { + nsRefPtr<nsFrameSelection> frameSelection = presShell->FrameSelection(); + frameSelection->SetDragState(false); + } + } + } + // If this is a parent or single process window, send the deactivate event. // Events for child process windows will be sent when ParentActivated // is called. if (XRE_IsParentProcess()) { ActivateOrDeactivate(window, false); } // keep track of the window being lowered, so that attempts to raise the
--- a/dom/base/test/chrome/cpows_child.js +++ b/dom/base/test/chrome/cpows_child.js @@ -12,17 +12,19 @@ const Cu = Components.utils; xray_test, symbol_test, compartment_test, regexp_test, postmessage_test, sync_test, async_test, rpc_test, - lifetime_test + lifetime_test, + cancel_test, + cancel_test2, ]; function go() { if (tests.length == 0) { sendRpcMessage("cpows:done", {}); return; } @@ -261,8 +263,77 @@ function lifetime_test(finish) Components.utils.schedulePreciseGC(function() { addMessageListener("cpows:lifetime_test_3", (msg) => { ok(obj.wont_die.f == 2, "reverse CPOW still works"); finish(); }); sendRpcMessage("cpows:lifetime_test_2"); }); } + +function cancel_test(finish) +{ + if (!is_remote) { + // No point in doing this in single-process mode. + finish(); + return; + } + + let fin1 = false, fin2 = false; + + // CPOW from the parent runs f. When it sends a sync message, the + // CPOW is canceled. The parent starts running again immediately + // after the CPOW is canceled; f also continues running. + function f() { + let res = sendSyncMessage("cpows:cancel_sync_message"); + ok(res[0] == 12, "cancel_sync_message result correct"); + fin1 = true; + if (fin1 && fin2) finish(); + } + + sendAsyncMessage("cpows:cancel_test", null, {f: f}); + addMessageListener("cpows:cancel_test_done", msg => { + fin2 = true; + if (fin1 && fin2) finish(); + }); +} + +function cancel_test2(finish) +{ + if (!is_remote) { + // No point in doing this in single-process mode. + finish(); + return; + } + + let fin1 = false, fin2 = false; + + // CPOW from the parent runs f. When it does a sync XHR, the + // CPOW is canceled. The parent starts running again immediately + // after the CPOW is canceled; f also continues running. + function f() { + let req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]. + createInstance(Components.interfaces.nsIXMLHttpRequest); + let fin = false; + let reqListener = () => { + if (req.readyState != req.DONE) { + return; + } + ok(req.status == 200, "XHR succeeded"); + fin = true; + }; + + req.onload = reqListener; + req.open("get", "http://example.com", false); + req.send(null); + + ok(fin == true, "XHR happened"); + + fin1 = true; + if (fin1 && fin2) finish(); + } + + sendAsyncMessage("cpows:cancel_test2", null, {f: f}); + addMessageListener("cpows:cancel_test2_done", msg => { + fin2 = true; + if (fin1 && fin2) finish(); + }); +}
--- a/dom/base/test/chrome/cpows_parent.xul +++ b/dom/base/test/chrome/cpows_parent.xul @@ -359,16 +359,42 @@ } ok(threw, "limited-lifetime CPOW stopped working"); wontDie = null; Components.utils.schedulePreciseGC(function() { savedMM.sendAsyncMessage("cpows:lifetime_test_3"); }); } + function recvCancelTest(msg) { + let failed = false; + try { + msg.objects.f(); + } catch (e if /cross-process JS call failed/.test(String(e))) { + failed = true; + } + ok(failed, "CPOW should fail due to cancelation"); + msg.target.messageManager.sendAsyncMessage("cpows:cancel_test_done"); + } + + function recvCancelSyncMessage() { + return 12; + } + + function recvCancelTest2(msg) { + let failed = false; + try { + msg.objects.f(); + } catch (e if /cross-process JS call failed/.test(String(e))) { + failed = true; + } + ok(failed, "CPOW should fail due to cancelation"); + msg.target.messageManager.sendAsyncMessage("cpows:cancel_test2_done"); + } + function run_tests(type) { info("Running tests: " + type); var node = document.getElementById('cpowbrowser_' + type); test_state = type; test_node = node; function recvIsRemote(message) { @@ -395,16 +421,19 @@ if (typeof Symbol === "function") { mm.addMessageListener("cpows:symbol_test", recvSymbolTest); } mm.addMessageListener("cpows:compartment_test", recvCompartmentTest); mm.addMessageListener("cpows:regexp_test", recvRegExpTest); mm.addMessageListener("cpows:postmessage_test", recvPostMessageTest); mm.addMessageListener("cpows:lifetime_test_1", recvLifetimeTest1); mm.addMessageListener("cpows:lifetime_test_2", recvLifetimeTest2); + mm.addMessageListener("cpows:cancel_test", recvCancelTest); + mm.addMessageListener("cpows:cancel_sync_message", recvCancelSyncMessage); + mm.addMessageListener("cpows:cancel_test2", recvCancelTest2); mm.loadFrameScript("chrome://mochitests/content/chrome/dom/base/test/chrome/cpows_child.js", true); } function start() { run_tests('remote'); } function finish() {
new file mode 100644 --- /dev/null +++ b/dom/canvas/crashtests/1183363.html @@ -0,0 +1,23 @@ +<!DOCTYPE html> +<html> +<head> +<script> + +function boom() +{ + var ctx = canvas.getContext('2d'); + ctx.rect(2, 6, 9, 8); + ctx.setTransform(1, 2, 3, 0, 4, 1); + setTimeout(function() { + ctx.moveTo(0, 1); + ctx.isPointInPath(0, 0, 'evenodd'); + }, 0); +} + +</script> +</head> + +<body onload="boom();"> +<canvas id="canvas"></canvas> +</body> +</html>
--- a/dom/canvas/crashtests/crashtests.list +++ b/dom/canvas/crashtests/crashtests.list @@ -17,8 +17,9 @@ skip-if(Android||B2G) load 780392-1.html skip-if(Android||B2G) skip-if(gtkWidget&&isDebugBuild) load 789933-1.html # bug 833371 for B2G, bug 1155252 for linux load 794463-1.html load 802926-1.html load 896047-1.html load 896047-2.html load 916128-1.html load 934939-1.html load 1099143-1.html +load 1183363.html
--- a/dom/events/IMEStateManager.cpp +++ b/dom/events/IMEStateManager.cpp @@ -940,17 +940,17 @@ IMEStateManager::SetInputContextForChild GetIMEStateSetOpenName(aInputContext.mIMEState.mOpen), NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputType).get(), NS_ConvertUTF16toUTF8(aInputContext.mHTMLInputInputmode).get(), NS_ConvertUTF16toUTF8(aInputContext.mActionHint).get(), GetActionCauseName(aAction.mCause), GetActionFocusChangeName(aAction.mFocusChange), sPresContext, sActiveTabParent.get())); - if (NS_WARN_IF(aTabParent != sActiveTabParent)) { + if (aTabParent != sActiveTabParent) { MOZ_LOG(sISMLog, LogLevel::Error, ("ISM: IMEStateManager::SetInputContextForChildProcess(), FAILED, " "because non-focused tab parent tries to set input context")); return; } if (NS_WARN_IF(!sPresContext)) { MOZ_LOG(sISMLog, LogLevel::Error,
--- a/dom/interfaces/base/nsIServiceWorkerManager.idl +++ b/dom/interfaces/base/nsIServiceWorkerManager.idl @@ -4,16 +4,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "domstubs.idl" interface nsIArray; interface nsIDocument; interface nsIInterceptedChannel; interface nsIPrincipal; +interface nsIRunnable; interface nsIURI; [scriptable, uuid(52ee2c9d-ee87-4caf-9588-23ae77ff8798)] interface nsIServiceWorkerUnregisterCallback : nsISupports { // aState is true if the unregistration succeded. // It's false if this ServiceWorkerRegistration doesn't exist. void unregisterSucceeded(in bool aState); @@ -28,17 +29,17 @@ interface nsIServiceWorkerInfo : nsISupp readonly attribute DOMString scope; readonly attribute DOMString scriptSpec; readonly attribute DOMString currentWorkerURL; readonly attribute DOMString activeCacheName; readonly attribute DOMString waitingCacheName; }; -[scriptable, builtinclass, uuid(ed1cbbf2-0400-4caa-8eb2-b09d21a94e20)] +[scriptable, builtinclass, uuid(8d80dd18-597b-4378-b41e-768bfe48dd4f)] interface nsIServiceWorkerManager : nsISupports { /** * Registers a ServiceWorker with script loaded from `aScriptURI` to act as * the ServiceWorker for aScope. Requires a valid entry settings object on * the stack. This means you must call this from content code 'within' * a window. * @@ -88,18 +89,21 @@ interface nsIServiceWorkerManager : nsIS * These are only meant to be called from ServiceWorkerRegistration instances. */ [noscript] nsISupports GetInstalling(in nsIDOMWindow aWindow, in DOMString aScope); [noscript] nsISupports GetWaiting(in nsIDOMWindow aWindow, in DOMString aScope); [noscript] nsISupports GetActive(in nsIDOMWindow aWindow, in DOMString aScope); /* * Returns a ServiceWorker. + * - aLoadFailedRunnable is an optional callback that will fire on main thread if + * a ServiceWorker object is returned, but later fails to load for some reason. */ - [noscript] nsISupports GetDocumentController(in nsIDOMWindow aWindow); + [noscript] nsISupports GetDocumentController(in nsIDOMWindow aWindow, + in nsIRunnable aLoadFailedRunnable); /* * Clears ServiceWorker registrations from memory and disk for the specified * host. * - All ServiceWorker instances change their state to redundant. * - Existing ServiceWorker instances handling fetches will keep running. * - All documents will immediately stop being controlled. * - Unregister jobs will be queued for all registrations.
--- a/dom/media/AudioSink.cpp +++ b/dom/media/AudioSink.cpp @@ -11,49 +11,16 @@ namespace mozilla { extern PRLogModuleInfo* gMediaDecoderLog; #define SINK_LOG(msg, ...) \ MOZ_LOG(gMediaDecoderLog, LogLevel::Debug, ("AudioSink=%p " msg, this, ##__VA_ARGS__)) #define SINK_LOG_V(msg, ...) \ MOZ_LOG(gMediaDecoderLog, LogLevel::Verbose, ("AudioSink=%p " msg, this, ##__VA_ARGS__)) -AudioSink::OnAudioEndTimeUpdateTask::OnAudioEndTimeUpdateTask( - MediaDecoderStateMachine* aStateMachine) - : mMutex("OnAudioEndTimeUpdateTask") - , mEndTime(0) - , mStateMachine(aStateMachine) -{ -} - -NS_IMETHODIMP -AudioSink::OnAudioEndTimeUpdateTask::Run() { - MutexAutoLock lock(mMutex); - if (mStateMachine) { - mStateMachine->OnAudioEndTimeUpdate(mEndTime); - } - return NS_OK; -} - -void -AudioSink::OnAudioEndTimeUpdateTask::Dispatch(int64_t aEndTime) { - MutexAutoLock lock(mMutex); - if (mStateMachine) { - mEndTime = aEndTime; - nsRefPtr<AudioSink::OnAudioEndTimeUpdateTask> runnable(this); - mStateMachine->TaskQueue()->Dispatch(runnable.forget()); - } -} - -void -AudioSink::OnAudioEndTimeUpdateTask::Cancel() { - MutexAutoLock lock(mMutex); - mStateMachine = nullptr; -} - // The amount of audio frames that is used to fuzz rounding errors. static const int64_t AUDIO_FUZZ_FRAMES = 1; AudioSink::AudioSink(MediaDecoderStateMachine* aStateMachine, int64_t aStartTime, AudioInfo aInfo, dom::AudioChannel aChannel) : mStateMachine(aStateMachine) , mStartTime(aStartTime) , mWritten(0) @@ -63,17 +30,16 @@ AudioSink::AudioSink(MediaDecoderStateMa , mVolume(1.0) , mPlaybackRate(1.0) , mPreservesPitch(false) , mStopAudioThread(false) , mSetVolume(false) , mSetPlaybackRate(false) , mSetPreservesPitch(false) , mPlaying(true) - , mOnAudioEndTimeUpdateTask(new OnAudioEndTimeUpdateTask(aStateMachine)) { } nsresult AudioSink::Init() { nsresult rv = NS_NewNamedThread("Media Audio", getter_AddRefs(mThread), @@ -127,17 +93,16 @@ AudioSink::PrepareToShutdown() mAudioStream->Cancel(); } GetReentrantMonitor().NotifyAll(); } void AudioSink::Shutdown() { - mOnAudioEndTimeUpdateTask->Cancel(); mThread->Shutdown(); mThread = nullptr; MOZ_ASSERT(!mAudioStream); } void AudioSink::SetVolume(double aVolume) { @@ -213,20 +178,16 @@ AudioSink::AudioLoop() // we pushed to the audio hardware. We must push silence into the audio // hardware so that the next audio chunk begins playback at the correct // time. missingFrames = std::min<int64_t>(UINT32_MAX, missingFrames.value()); mWritten += PlaySilence(static_cast<uint32_t>(missingFrames.value())); } else { mWritten += PlayFromAudioQueue(); } - int64_t endTime = GetEndTime(); - if (endTime != -1) { - mOnAudioEndTimeUpdateTask->Dispatch(endTime); - } } ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); MOZ_ASSERT(mStopAudioThread || AudioQueue().AtEndOfStream()); if (!mStopAudioThread && mPlaying) { Drain(); } SINK_LOG("AudioLoop complete"); Cleanup(); @@ -422,17 +383,17 @@ AudioSink::WriteSilence(uint32_t aFrames buf.SetLength(numSamples); memset(buf.Elements(), 0, numSamples * sizeof(AudioDataValue)); mAudioStream->Write(buf.Elements(), aFrames); StartAudioStreamPlaybackIfNeeded(); } int64_t -AudioSink::GetEndTime() +AudioSink::GetEndTime() const { CheckedInt64 playedUsecs = FramesToUsecs(mWritten, mInfo.mRate) + mStartTime; if (!playedUsecs.isValid()) { NS_WARNING("Int overflow calculating audio end time"); return -1; } return playedUsecs.value(); }
--- a/dom/media/AudioSink.h +++ b/dom/media/AudioSink.h @@ -22,16 +22,19 @@ public: AudioSink(MediaDecoderStateMachine* aStateMachine, int64_t aStartTime, AudioInfo aInfo, dom::AudioChannel aChannel); nsresult Init(); int64_t GetPosition(); + // Thread-safe. Can be called on any thread. + int64_t GetEndTime() const; + // Check whether we've pushed more frames to the audio hardware than it has // played. bool HasUnplayedFrames(); // Tell the AudioSink to stop processing and initiate shutdown. Must be // called with the decoder monitor held. void PrepareToShutdown(); @@ -85,18 +88,16 @@ private: void UpdateStreamSettings(); // If we have already written enough frames to the AudioStream, start the // playback. void StartAudioStreamPlaybackIfNeeded(); void WriteSilence(uint32_t aFrames); - int64_t GetEndTime(); - MediaQueue<AudioData>& AudioQueue(); ReentrantMonitor& GetReentrantMonitor(); void AssertCurrentThreadInMonitor(); void AssertOnAudioThread(); nsRefPtr<MediaDecoderStateMachine> mStateMachine; @@ -119,45 +120,28 @@ private: // PCM frames written to the stream so far. Atomic<int64_t> mWritten; // Keep the last good position returned from the audio stream. Used to ensure // position returned by GetPosition() is mono-increasing in spite of audio // stream error. int64_t mLastGoodPosition; - AudioInfo mInfo; + const AudioInfo mInfo; dom::AudioChannel mChannel; double mVolume; double mPlaybackRate; bool mPreservesPitch; bool mStopAudioThread; bool mSetVolume; bool mSetPlaybackRate; bool mSetPreservesPitch; bool mPlaying; - - class OnAudioEndTimeUpdateTask : public nsRunnable { - public: - explicit OnAudioEndTimeUpdateTask(MediaDecoderStateMachine* aStateMachine); - - NS_IMETHOD Run() override; - - void Dispatch(int64_t aEndTime); - void Cancel(); - - private: - Mutex mMutex; - int64_t mEndTime; - nsRefPtr<MediaDecoderStateMachine> mStateMachine; - }; - - nsRefPtr<OnAudioEndTimeUpdateTask> mOnAudioEndTimeUpdateTask; }; } // namespace mozilla #endif
--- a/dom/media/Intervals.h +++ b/dom/media/Intervals.h @@ -152,16 +152,21 @@ public: } bool Intersects(const SelfType& aOther) const { return (mStart - mFuzz < aOther.mEnd + aOther.mFuzz) && (aOther.mStart - aOther.mFuzz < mEnd + mFuzz); } + bool IntersectsStrict(const SelfType& aOther) const + { + return mStart < aOther.mEnd && aOther.mStart < mEnd; + } + // Same as Intersects, but including the boundaries. bool Touches(const SelfType& aOther) const { return (mStart - mFuzz <= aOther.mEnd + aOther.mFuzz) && (aOther.mStart - aOther.mFuzz <= mEnd + mFuzz); } // Returns true if aOther is strictly to the right of this and contiguous. @@ -303,24 +308,28 @@ public: this->~IntervalSet(); new(this) IntervalSet(Move(aOther)); return *this; } SelfType& operator= (const ElemType& aInterval) { mIntervals.Clear(); - mIntervals.AppendElement(aInterval); + if (!aInterval.IsEmpty()) { + mIntervals.AppendElement(aInterval); + } return *this; } SelfType& operator= (ElemType&& aInterval) { mIntervals.Clear(); - mIntervals.AppendElement(Move(aInterval)); + if (!aInterval.IsEmpty()) { + mIntervals.AppendElement(Move(aInterval)); + } return *this; } SelfType& Add(const SelfType& aIntervals) { mIntervals.AppendElements(aIntervals.mIntervals); Normalize(); return *this; @@ -458,17 +467,17 @@ public: // Mutate this TimeRange to be the intersection of this and aOther. SelfType& Intersection(const SelfType& aOther) { ContainerType intersection; const ContainerType& other = aOther.mIntervals; IndexType i = 0, j = 0; for (; i < mIntervals.Length() && j < other.Length();) { - if (mIntervals[i].Intersects(other[j])) { + if (mIntervals[i].IntersectsStrict(other[j])) { intersection.AppendElement(mIntervals[i].Intersection(other[j])); } if (mIntervals[i].mEnd < other[j].mEnd) { i++; } else { j++; } }
--- a/dom/media/MediaDecoderStateMachine.cpp +++ b/dom/media/MediaDecoderStateMachine.cpp @@ -353,18 +353,18 @@ bool MediaDecoderStateMachine::HaveNextF (!HasVideo() || VideoQueue().GetSize() > 1); } int64_t MediaDecoderStateMachine::GetDecodedAudioDuration() { MOZ_ASSERT(OnTaskQueue()); AssertCurrentThreadInMonitor(); int64_t audioDecoded = AudioQueue().Duration(); - if (mAudioEndTime != -1) { - audioDecoded += mAudioEndTime - GetMediaTime(); + if (AudioEndTime() != -1) { + audioDecoded += AudioEndTime() - GetMediaTime(); } return audioDecoded; } void MediaDecoderStateMachine::SendStreamData() { MOZ_ASSERT(OnTaskQueue()); AssertCurrentThreadInMonitor(); @@ -1771,17 +1771,17 @@ MediaDecoderStateMachine::StartAudioThre int64_t MediaDecoderStateMachine::AudioDecodedUsecs() { MOZ_ASSERT(OnTaskQueue()); NS_ASSERTION(HasAudio(), "Should only call AudioDecodedUsecs() when we have audio"); // The amount of audio we have decoded is the amount of audio data we've // already decoded and pushed to the hardware, plus the amount of audio // data waiting to be pushed to the hardware. - int64_t pushed = (mAudioEndTime != -1) ? (mAudioEndTime - GetMediaTime()) : 0; + int64_t pushed = (AudioEndTime() != -1) ? (AudioEndTime() - GetMediaTime()) : 0; // Currently for real time streams, AudioQueue().Duration() produce // wrong values (Bug 1114434), so we use frame counts to calculate duration. if (IsRealTime()) { return pushed + FramesToUsecs(AudioQueue().FrameCount(), mInfo.mAudio.mRate).value(); } return pushed + AudioQueue().Duration(); } @@ -2417,32 +2417,35 @@ nsresult MediaDecoderStateMachine::RunSt if (mState != DECODER_STATE_COMPLETED) { // While we're presenting a frame we can change state. Whatever changed // our state should have scheduled another state machine run. NS_ASSERTION(IsStateMachineScheduled(), "Must have timer scheduled"); return NS_OK; } - StopAudioThread(); - mDecodedStream->StopPlayback(); - if (mPlayState == MediaDecoder::PLAY_STATE_PLAYING && !mSentPlaybackEndedEvent) { - int64_t clockTime = std::max(mAudioEndTime, mVideoFrameEndTime); + int64_t clockTime = std::max(AudioEndTime(), mVideoFrameEndTime); clockTime = std::max(int64_t(0), std::max(clockTime, Duration().ToMicroseconds())); UpdatePlaybackPosition(clockTime); nsCOMPtr<nsIRunnable> event = NS_NewRunnableMethod(mDecoder, &MediaDecoder::PlaybackEnded); AbstractThread::MainThread()->Dispatch(event.forget()); mSentPlaybackEndedEvent = true; } + + // Stop audio sink after call to AudioEndTime() above, otherwise it will + // return an incorrect value due to a null mAudioSink. + StopAudioThread(); + mDecodedStream->StopPlayback(); + return NS_OK; } } return NS_OK; } void @@ -2728,19 +2731,19 @@ void MediaDecoderStateMachine::UpdateRen ScheduleStateMachineIn(USECS_PER_S); return; } } // Cap the current time to the larger of the audio and video end time. // This ensures that if we're running off the system clock, we don't // advance the clock to after the media end time. - if (mVideoFrameEndTime != -1 || mAudioEndTime != -1) { + if (mVideoFrameEndTime != -1 || AudioEndTime() != -1) { // These will be non -1 if we've displayed a video frame, or played an audio frame. - int64_t t = std::min(clockTime, std::max(mVideoFrameEndTime, mAudioEndTime)); + int64_t t = std::min(clockTime, std::max(mVideoFrameEndTime, AudioEndTime())); // FIXME: Bug 1091422 - chained ogg files hit this assertion. //MOZ_ASSERT(t >= GetMediaTime()); if (t > GetMediaTime()) { UpdatePlaybackPosition(t); } } // Note we have to update playback position before releasing the monitor. // Otherwise, MediaDecoder::AddOutputStream could kick in when we are outside @@ -3065,21 +3068,35 @@ void MediaDecoderStateMachine::QueueMeta AssertCurrentThreadInMonitor(); TimedMetadata* metadata = new TimedMetadata; metadata->mPublishTime = aPublishTime; metadata->mInfo = aInfo.forget(); metadata->mTags = aTags.forget(); mMetadataManager.QueueMetadata(metadata); } +int64_t +MediaDecoderStateMachine::AudioEndTime() const +{ + MOZ_ASSERT(OnTaskQueue()); + AssertCurrentThreadInMonitor(); + if (mAudioSink) { + return mAudioSink->GetEndTime(); + } + // Don't call this after mAudioSink becomes null since we can't distinguish + // "before StartAudioThread" and "after StopAudioThread". + MOZ_ASSERT(mAudioCaptured || !mAudioCompleted); + return mAudioEndTime; +} + void MediaDecoderStateMachine::OnAudioEndTimeUpdate(int64_t aAudioEndTime) { MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); - MOZ_ASSERT(aAudioEndTime >= mAudioEndTime); + MOZ_ASSERT(aAudioEndTime >= AudioEndTime()); mAudioEndTime = aAudioEndTime; } void MediaDecoderStateMachine::OnPlaybackOffsetUpdate(int64_t aPlaybackOffset) { MOZ_ASSERT(OnTaskQueue()); ReentrantMonitorAutoEnter mon(mDecoder->GetReentrantMonitor()); mDecoder->UpdatePlaybackOffset(aPlaybackOffset);
--- a/dom/media/MediaDecoderStateMachine.h +++ b/dom/media/MediaDecoderStateMachine.h @@ -1065,16 +1065,17 @@ protected: Canonical<int64_t> mCurrentPosition; public: AbstractCanonical<int64_t>* CanonicalCurrentPosition() { return &mCurrentPosition; } protected: // The end time of the last audio frame that's been pushed onto the audio // hardware in microseconds. This will approximately be the end time of the // audio stream, unless another frame is pushed to the hardware. int64_t mAudioEndTime; + int64_t AudioEndTime() const; // The end time of the last decoded audio frame. This signifies the end of // decoded audio data. Used to check if we are low in decoded data. int64_t mDecodedAudioEndTime; // The presentation end time of the last video frame which has been displayed // in microseconds. Accessed from the state machine thread. int64_t mVideoFrameEndTime;
--- a/dom/media/MediaFormatReader.cpp +++ b/dom/media/MediaFormatReader.cpp @@ -76,34 +76,25 @@ MediaFormatReader::MediaFormatReader(Abs { MOZ_ASSERT(aDemuxer); MOZ_COUNT_CTOR(MediaFormatReader); } MediaFormatReader::~MediaFormatReader() { MOZ_COUNT_DTOR(MediaFormatReader); - // shutdown main thread demuxer and track demuxers. - if (mAudioTrackDemuxer) { - mAudioTrackDemuxer->BreakCycles(); - mAudioTrackDemuxer = nullptr; - } - if (mVideoTrackDemuxer) { - mVideoTrackDemuxer->BreakCycles(); - mVideoTrackDemuxer = nullptr; - } - mMainThreadDemuxer = nullptr; } nsRefPtr<ShutdownPromise> MediaFormatReader::Shutdown() { MOZ_ASSERT(OnTaskQueue()); mDemuxerInitRequest.DisconnectIfExists(); + mMetadataPromise.RejectIfExists(ReadMetadataFailureReason::METADATA_ERROR, __func__); mSeekPromise.RejectIfExists(NS_ERROR_FAILURE, __func__); mSkipRequest.DisconnectIfExists(); if (mAudio.mDecoder) { Flush(TrackInfo::kAudioTrack); if (mAudio.HasPromise()) { mAudio.RejectPromise(CANCELED, __func__); } @@ -139,16 +130,27 @@ MediaFormatReader::Shutdown() mVideo.mTaskQueue->BeginShutdown(); mVideo.mTaskQueue->AwaitShutdownAndIdle(); mVideo.mTaskQueue = nullptr; } MOZ_ASSERT(mVideo.mPromise.IsEmpty()); mDemuxer = nullptr; + // shutdown main thread demuxer and track demuxers. + if (mAudioTrackDemuxer) { + mAudioTrackDemuxer->BreakCycles(); + mAudioTrackDemuxer = nullptr; + } + if (mVideoTrackDemuxer) { + mVideoTrackDemuxer->BreakCycles(); + mVideoTrackDemuxer = nullptr; + } + mMainThreadDemuxer = nullptr; + mPlatform = nullptr; return MediaDecoderReader::Shutdown(); } void MediaFormatReader::InitLayersBackendType() { @@ -351,16 +353,21 @@ MediaFormatReader::OnDemuxerInitDone(nsr } else { mMainThreadDemuxer = mDemuxer->Clone(); } if (!mMainThreadDemuxer) { mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__); NS_WARNING("Unable to clone current MediaDataDemuxer"); return; } + + if (!videoActive && !audioActive) { + mMetadataPromise.Reject(ReadMetadataFailureReason::METADATA_ERROR, __func__); + return; + } if (videoActive) { mVideoTrackDemuxer = mMainThreadDemuxer->GetTrackDemuxer(TrackInfo::kVideoTrack, 0); MOZ_ASSERT(mVideoTrackDemuxer); } if (audioActive) { mAudioTrackDemuxer = mMainThreadDemuxer->GetTrackDemuxer(TrackInfo::kAudioTrack, 0); @@ -520,17 +527,18 @@ MediaFormatReader::ShouldSkip(bool aSkip nsRefPtr<MediaDecoderReader::VideoDataPromise> MediaFormatReader::RequestVideoData(bool aSkipToNextKeyframe, int64_t aTimeThreshold, bool aForceDecodeAhead) { MOZ_ASSERT(OnTaskQueue()); MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty(), "No sample requests allowed while seeking"); MOZ_DIAGNOSTIC_ASSERT(!mVideo.HasPromise(), "No duplicate sample requests"); - MOZ_DIAGNOSTIC_ASSERT(!mVideo.mSeekRequest.Exists()); + MOZ_DIAGNOSTIC_ASSERT(!mVideo.mSeekRequest.Exists() || + mVideo.mTimeThreshold.isSome()); MOZ_DIAGNOSTIC_ASSERT(!mSkipRequest.Exists(), "called mid-skipping"); MOZ_DIAGNOSTIC_ASSERT(!IsSeeking(), "called mid-seek"); LOGV("RequestVideoData(%d, %lld)", aSkipToNextKeyframe, aTimeThreshold); if (!HasVideo()) { LOG("called with no video track"); return VideoDataPromise::CreateAndReject(DECODE_ERROR, __func__); } @@ -617,17 +625,18 @@ MediaFormatReader::OnVideoDemuxCompleted ScheduleUpdate(TrackInfo::kVideoTrack); } nsRefPtr<MediaDecoderReader::AudioDataPromise> MediaFormatReader::RequestAudioData() { MOZ_ASSERT(OnTaskQueue()); MOZ_DIAGNOSTIC_ASSERT(mSeekPromise.IsEmpty(), "No sample requests allowed while seeking"); - MOZ_DIAGNOSTIC_ASSERT(!mAudio.mSeekRequest.Exists()); + MOZ_DIAGNOSTIC_ASSERT(!mAudio.mSeekRequest.Exists() || + mAudio.mTimeThreshold.isSome()); MOZ_DIAGNOSTIC_ASSERT(!mAudio.HasPromise(), "No duplicate sample requests"); MOZ_DIAGNOSTIC_ASSERT(!IsSeeking(), "called mid-seek"); LOGV(""); if (!HasAudio()) { LOG("called with no audio track"); return AudioDataPromise::CreateAndReject(DECODE_ERROR, __func__); } @@ -714,47 +723,51 @@ MediaFormatReader::NotifyDrainComplete(T void MediaFormatReader::NotifyError(TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); LOGV("%s Decoding error", TrackTypeToStr(aTrack)); auto& decoder = GetDecoderData(aTrack); decoder.mError = true; + decoder.mNeedDraining = true; ScheduleUpdate(aTrack); } void MediaFormatReader::NotifyWaitingForData(TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); auto& decoder = GetDecoderData(aTrack); decoder.mWaitingForData = true; + decoder.mNeedDraining = true; ScheduleUpdate(aTrack); } void MediaFormatReader::NotifyEndOfStream(TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); auto& decoder = GetDecoderData(aTrack); decoder.mDemuxEOS = true; + decoder.mNeedDraining = true; ScheduleUpdate(aTrack); } bool MediaFormatReader::NeedInput(DecoderData& aDecoder) { MOZ_ASSERT(OnTaskQueue()); // We try to keep a few more compressed samples input than decoded samples // have been output, provided the state machine has requested we send it a // decoded sample. To account for H.264 streams which may require a longer // run of input than we input, decoders fire an "input exhausted" callback, // which overrides our "few more samples" threshold. return + !aDecoder.mDraining && !aDecoder.mError && (aDecoder.HasPromise() || aDecoder.mForceDecodeAhead) && !aDecoder.mDemuxRequest.Exists() && aDecoder.mOutput.IsEmpty() && (aDecoder.mInputExhausted || !aDecoder.mQueuedSamples.IsEmpty() || aDecoder.mTimeThreshold.isSome() || aDecoder.mForceDecodeAhead || aDecoder.mNumSamplesInput - aDecoder.mNumSamplesOutput < aDecoder.mDecodeAhead); @@ -792,17 +805,16 @@ MediaFormatReader::UpdateReceivedNewData bool hasLastEnd; media::TimeUnit lastEnd = decoder.mTimeRanges.GetEnd(&hasLastEnd); // Update our cached TimeRange. decoder.mTimeRanges = decoder.mTrackDemuxer->GetBuffered(); if (decoder.mTimeRanges.Length() && (!hasLastEnd || decoder.mTimeRanges.GetEnd() > lastEnd)) { // New data was added after our previous end, we can clear the EOS flag. decoder.mDemuxEOS = false; - decoder.mDemuxEOSServiced = false; } if (decoder.mError) { return false; } if (decoder.HasWaitingPromise()) { MOZ_ASSERT(!decoder.HasPromise()); LOG("We have new data. Resolving WaitingPromise"); @@ -867,16 +879,27 @@ MediaFormatReader::DecodeDemuxedSamples( nsRefPtr<SharedTrackInfo> info = sample->mTrackInfo; if (info && decoder.mLastStreamSourceID != info->GetID()) { if (samplesPending) { // Let existing samples complete their decoding. We'll resume later. return; } + if (decoder.mNextStreamSourceID.isNothing() || + decoder.mNextStreamSourceID.ref() != info->GetID()) { + LOG("%s stream id has changed from:%d to:%d, draining decoder.", + TrackTypeToStr(aTrack), decoder.mLastStreamSourceID, + info->GetID()); + decoder.mNeedDraining = true; + decoder.mNextStreamSourceID = Some(info->GetID()); + DrainDecoder(aTrack); + return; + } + LOG("%s stream id has changed from:%d to:%d, recreating decoder.", TrackTypeToStr(aTrack), decoder.mLastStreamSourceID, info->GetID()); decoder.mInfo = info; decoder.mLastStreamSourceID = info->GetID(); // Flush will clear our array of queued samples. So make a copy now. nsTArray<nsRefPtr<MediaRawData>> samples{decoder.mQueuedSamples}; Flush(aTrack); @@ -940,16 +963,35 @@ MediaFormatReader::DecodeDemuxedSamples( samplesPending = true; } // We have serviced the decoder's request for more data. decoder.mInputExhausted = false; } void +MediaFormatReader::DrainDecoder(TrackType aTrack) +{ + MOZ_ASSERT(OnTaskQueue()); + + auto& decoder = GetDecoderData(aTrack); + if (!decoder.mNeedDraining || decoder.mDraining) { + return; + } + decoder.mNeedDraining = false; + if (!decoder.mDecoder) { + return; + } + decoder.mOutputRequested = true; + decoder.mDecoder->Drain(); + decoder.mDraining = true; + LOG("Requesting %s decoder to drain", TrackTypeToStr(aTrack)); +} + +void MediaFormatReader::Update(TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); if (mShutdown) { return; } @@ -960,43 +1002,27 @@ MediaFormatReader::Update(TrackType aTra auto& decoder = GetDecoderData(aTrack); decoder.mUpdateScheduled = false; if (UpdateReceivedNewData(aTrack)) { LOGV("Nothing more to do"); return; } - if (decoder.HasPromise()) { - // Handle pending requests from the MediaDecoderStateMachine. - if (decoder.mError) { - LOG("Decoding Error"); - decoder.RejectPromise(DECODE_ERROR, __func__); - return; - } - if (decoder.mWaitingForData) { - LOG("Waiting For Data"); - decoder.RejectPromise(WAITING_FOR_DATA, __func__); - } - } else if (decoder.mWaitingForData) { - // Nothing more we can do at present. - LOGV("Still waiting for data."); - return; - } - // Record number of frames decoded and parsed. Automatically update the // stats counters using the AutoNotifyDecoded stack-based class. AbstractMediaDecoder::AutoNotifyDecoded a(mDecoder); if (aTrack == TrackInfo::kVideoTrack) { uint64_t delta = decoder.mNumSamplesOutput - mLastReportedNumDecodedFrames; a.mDecoded = static_cast<uint32_t>(delta); mLastReportedNumDecodedFrames = decoder.mNumSamplesOutput; } + if (decoder.HasPromise()) { needOutput = true; if (!decoder.mOutput.IsEmpty()) { // We have a decoded sample ready to be returned. nsRefPtr<MediaData> output = decoder.mOutput[0]; decoder.mOutput.RemoveElementAt(0); decoder.mSizeOfQueue -= 1; if (decoder.mTimeThreshold.isNothing() || @@ -1005,26 +1031,36 @@ MediaFormatReader::Update(TrackType aTra decoder.mTimeThreshold.reset(); } else { LOGV("Internal Seeking: Dropping frame time:%f wanted:%f (kf:%d)", media::TimeUnit::FromMicroseconds(output->mTime).ToSeconds(), decoder.mTimeThreshold.ref().ToSeconds(), output->mKeyframe); } } else if (decoder.mDrainComplete) { - decoder.RejectPromise(END_OF_STREAM, __func__); decoder.mDrainComplete = false; + decoder.mDraining = false; + if (decoder.mError) { + LOG("Decoding Error"); + decoder.RejectPromise(DECODE_ERROR, __func__); + return; + } else if (decoder.mDemuxEOS) { + decoder.RejectPromise(END_OF_STREAM, __func__); + } else if (decoder.mWaitingForData) { + LOG("Waiting For Data"); + decoder.RejectPromise(WAITING_FOR_DATA, __func__); + } + } else if (decoder.mError && !decoder.mDecoder) { + decoder.RejectPromise(DECODE_ERROR, __func__); + return; } } - if (decoder.mDemuxEOS && !decoder.mDemuxEOSServiced) { - decoder.mOutputRequested = true; - decoder.mDecoder->Drain(); - decoder.mDemuxEOSServiced = true; - LOGV("Requesting decoder to drain"); + if (decoder.mError || decoder.mDemuxEOS || decoder.mWaitingForData) { + DrainDecoder(aTrack); return; } if (!NeedInput(decoder)) { LOGV("No need for additional input"); return; } @@ -1495,36 +1531,38 @@ MediaFormatReader::NotifyDemuxer(uint32_ } void MediaFormatReader::NotifyDataArrivedInternal(uint32_t aLength, int64_t aOffset) { MOZ_ASSERT(OnTaskQueue()); MOZ_ASSERT(aLength); - if (!mInitDone) { + if (!mInitDone || mShutdown) { return; } + MOZ_ASSERT(mMainThreadDemuxer); + // Queue a task to notify our main thread demuxer. nsCOMPtr<nsIRunnable> task = NS_NewRunnableMethodWithArgs<uint32_t, int64_t>( mMainThreadDemuxer, &MediaDataDemuxer::NotifyDataArrived, aLength, aOffset); AbstractThread::MainThread()->Dispatch(task.forget()); NotifyDemuxer(aLength, aOffset); } void MediaFormatReader::NotifyDataRemoved() { MOZ_ASSERT(OnTaskQueue()); - if (!mInitDone) { + if (!mInitDone || mShutdown) { return; } MOZ_ASSERT(mMainThreadDemuxer); // Queue a task to notify our main thread demuxer. nsCOMPtr<nsIRunnable> task = NS_NewRunnableMethod(
--- a/dom/media/MediaFormatReader.h +++ b/dom/media/MediaFormatReader.h @@ -113,16 +113,18 @@ private: // Handle actions should more data be received. // Returns true if no more action is required. bool UpdateReceivedNewData(TrackType aTrack); // Called when new samples need to be demuxed. void RequestDemuxSamples(TrackType aTrack); // Decode any pending already demuxed samples. void DecodeDemuxedSamples(TrackType aTrack, AbstractMediaDecoder::AutoNotifyDecoded& aA); + // Drain the current decoder. + void DrainDecoder(TrackType aTrack); void NotifyNewOutput(TrackType aTrack, MediaData* aSample); void NotifyInputExhausted(TrackType aTrack); void NotifyDrainComplete(TrackType aTrack); void NotifyError(TrackType aTrack); void NotifyWaitingForData(TrackType aTrack); void NotifyEndOfStream(TrackType aTrack); void ExtractCryptoInitData(nsTArray<uint8_t>& aInitData); @@ -183,23 +185,24 @@ private: MediaData::Type aType, uint32_t aDecodeAhead) : mOwner(aOwner) , mType(aType) , mDecodeAhead(aDecodeAhead) , mForceDecodeAhead(false) , mUpdateScheduled(false) , mDemuxEOS(false) - , mDemuxEOSServiced(false) , mWaitingForData(false) , mReceivedNewData(false) , mDiscontinuity(true) , mOutputRequested(false) , mInputExhausted(false) , mError(false) + , mNeedDraining(false) + , mDraining(false) , mDrainComplete(false) , mNumSamplesInput(0) , mNumSamplesOutput(0) , mSizeOfQueue(0) , mLastStreamSourceID(UINT32_MAX) {} MediaFormatReader* mOwner; @@ -214,17 +217,16 @@ private: // Callback that receives output and error notifications from the decoder. nsAutoPtr<DecoderCallback> mCallback; // Only accessed from reader's task queue. uint32_t mDecodeAhead; bool mForceDecodeAhead; bool mUpdateScheduled; bool mDemuxEOS; - bool mDemuxEOSServiced; bool mWaitingForData; bool mReceivedNewData; bool mDiscontinuity; // Pending seek. MediaPromiseRequestHolder<MediaTrackDemuxer::SeekPromise> mSeekRequest; // Queued demux samples waiting to be decoded. @@ -236,16 +238,18 @@ private: MOZ_ASSERT(mOwner->OnTaskQueue()); return !mWaitingPromise.IsEmpty(); } // MediaDataDecoder handler's variables. bool mOutputRequested; bool mInputExhausted; bool mError; + bool mNeedDraining; + bool mDraining; bool mDrainComplete; // If set, all decoded samples prior mTimeThreshold will be dropped. // Used for internal seeking when a change of stream is detected. Maybe<media::TimeUnit> mTimeThreshold; // Decoded samples returned my mDecoder awaiting being returned to // state machine upon request. nsTArray<nsRefPtr<MediaData>> mOutput; @@ -265,35 +269,38 @@ private: mTrackDemuxer->Reset(); } void ResetState() { MOZ_ASSERT(mOwner->OnTaskQueue()); mForceDecodeAhead = false; mDemuxEOS = false; - mDemuxEOSServiced = false; mWaitingForData = false; mReceivedNewData = false; mDiscontinuity = true; mQueuedSamples.Clear(); mOutputRequested = false; mInputExhausted = false; + mNeedDraining = false; + mDraining = false; mDrainComplete = false; mTimeThreshold.reset(); mOutput.Clear(); mNumSamplesInput = 0; mNumSamplesOutput = 0; mSizeOfQueue = 0; + mNextStreamSourceID.reset(); } // Used by the MDSM for logging purposes. Atomic<size_t> mSizeOfQueue; // Sample format monitoring. uint32_t mLastStreamSourceID; + Maybe<uint32_t> mNextStreamSourceID; media::TimeIntervals mTimeRanges; nsRefPtr<SharedTrackInfo> mInfo; }; template<typename PromiseType> struct DecoderDataWithPromise : public DecoderData { DecoderDataWithPromise(MediaFormatReader* aOwner, MediaData::Type aType,
--- a/dom/media/fmp4/MP4Demuxer.cpp +++ b/dom/media/fmp4/MP4Demuxer.cpp @@ -10,18 +10,59 @@ #include "MP4Demuxer.h" #include "mp4_demuxer/Index.h" #include "mp4_demuxer/MoofParser.h" #include "mp4_demuxer/MP4Metadata.h" #include "mp4_demuxer/ResourceStream.h" #include "mp4_demuxer/BufferStream.h" +// Used for telemetry +#include "mozilla/Telemetry.h" +#include "mp4_demuxer/AnnexB.h" +#include "mp4_demuxer/H264.h" + namespace mozilla { +// Returns true if no SPS was found and search for it should continue. +bool +AccumulateSPSTelemetry(const MediaByteBuffer* aExtradata) +{ + mp4_demuxer::SPSData spsdata; + if (mp4_demuxer::H264::DecodeSPSFromExtraData(aExtradata, spsdata)) { + uint8_t constraints = (spsdata.constraint_set0_flag ? (1 << 0) : 0) | + (spsdata.constraint_set1_flag ? (1 << 1) : 0) | + (spsdata.constraint_set2_flag ? (1 << 2) : 0) | + (spsdata.constraint_set3_flag ? (1 << 3) : 0) | + (spsdata.constraint_set4_flag ? (1 << 4) : 0) | + (spsdata.constraint_set5_flag ? (1 << 5) : 0); + Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_CONSTRAINT_SET_FLAG, + constraints); + + // Collect profile_idc values up to 244, otherwise 0 for unknown. + Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_PROFILE, + spsdata.profile_idc <= 244 ? spsdata.profile_idc : 0); + + // Make sure level_idc represents a value between levels 1 and 5.2, + // otherwise collect 0 for unknown level. + Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_LEVEL, + (spsdata.level_idc >= 10 && spsdata.level_idc <= 52) ? + spsdata.level_idc : 0); + + // max_num_ref_frames should be between 0 and 16, anything larger will + // be treated as invalid. + Telemetry::Accumulate(Telemetry::VIDEO_H264_SPS_MAX_NUM_REF_FRAMES, + std::min(spsdata.max_num_ref_frames, 17u)); + + return false; + } + + return true; +} + MP4Demuxer::MP4Demuxer(MediaResource* aResource) : mResource(aResource) , mStream(new mp4_demuxer::ResourceStream(aResource)) , mInitData(new MediaByteBuffer) { } nsRefPtr<MP4Demuxer::InitPromise> @@ -164,16 +205,27 @@ MP4TrackDemuxer::MP4TrackDemuxer(MP4Demu } mIndex = new mp4_demuxer::Index(indices, mStream, mInfo->mTrackId, mInfo->IsAudio(), &mMonitor); mIterator = MakeUnique<mp4_demuxer::SampleIterator>(mIndex); EnsureUpToDateIndex(); // Force update of index + + // Collect telemetry from h264 AVCC SPS. + if (mInfo->GetAsVideoInfo() && + (mInfo->mMimeType.EqualsLiteral("video/mp4") || + mInfo->mMimeType.EqualsLiteral("video/avc"))) { + mNeedSPSForTelemetry = + AccumulateSPSTelemetry(mInfo->GetAsVideoInfo()->mExtraData); + } else { + // No SPS to be found. + mNeedSPSForTelemetry = false; + } } UniquePtr<TrackInfo> MP4TrackDemuxer::GetInfo() const { return mInfo->Clone(); } @@ -263,16 +315,22 @@ MP4TrackDemuxer::Reset() SetNextKeyFrameTime(); } void MP4TrackDemuxer::UpdateSamples(nsTArray<nsRefPtr<MediaRawData>>& aSamples) { for (size_t i = 0; i < aSamples.Length(); i++) { MediaRawData* sample = aSamples[i]; + // Collect telemetry from h264 Annex B SPS. + if (mNeedSPSForTelemetry && mp4_demuxer::AnnexB::HasSPS(sample)) { + nsRefPtr<MediaByteBuffer> extradata = + mp4_demuxer::AnnexB::ExtractExtraData(sample); + mNeedSPSForTelemetry = AccumulateSPSTelemetry(extradata); + } if (sample->mCrypto.mValid) { nsAutoPtr<MediaRawDataWriter> writer(sample->CreateWriter()); writer->mCrypto.mMode = mInfo->mCrypto.mMode; writer->mCrypto.mIVSize = mInfo->mCrypto.mIVSize; writer->mCrypto.mKeyId.AppendElements(mInfo->mCrypto.mKeyId); } if (mInfo->GetAsVideoInfo()) { sample->mExtraData = mInfo->GetAsVideoInfo()->mExtraData;
--- a/dom/media/fmp4/MP4Demuxer.h +++ b/dom/media/fmp4/MP4Demuxer.h @@ -91,16 +91,17 @@ private: nsRefPtr<mp4_demuxer::Index> mIndex; UniquePtr<mp4_demuxer::SampleIterator> mIterator; UniquePtr<TrackInfo> mInfo; nsRefPtr<mp4_demuxer::ResourceStream> mStream; Maybe<media::TimeUnit> mNextKeyframeTime; // Queued samples extracted by the demuxer, but not yet returned. nsRefPtr<MediaRawData> mQueuedSample; bool mNeedReIndex; + bool mNeedSPSForTelemetry; // We do not actually need a monitor, however MoofParser will assert // if a monitor isn't held. Monitor mMonitor; }; } // namespace mozilla
--- a/dom/media/fmp4/MP4Reader.cpp +++ b/dom/media/fmp4/MP4Reader.cpp @@ -61,50 +61,17 @@ TrackTypeToStr(TrackInfo::TrackType aTra return "Audio"; case TrackInfo::kVideoTrack: return "Video"; default: return "Unknown"; } } -bool -AccumulateSPSTelemetry(const MediaByteBuffer* aExtradata) -{ - SPSData spsdata; - if (H264::DecodeSPSFromExtraData(aExtradata, spsdata)) { - uint8_t constraints = (spsdata.constraint_set0_flag ? (1 << 0) : 0) | - (spsdata.constraint_set1_flag ? (1 << 1) : 0) | - (spsdata.constraint_set2_flag ? (1 << 2) : 0) | - (spsdata.constraint_set3_flag ? (1 << 3) : 0) | - (spsdata.constraint_set4_flag ? (1 << 4) : 0) | - (spsdata.constraint_set5_flag ? (1 << 5) : 0); - Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_CONSTRAINT_SET_FLAG, - constraints); - - // Collect profile_idc values up to 244, otherwise 0 for unknown. - Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_PROFILE, - spsdata.profile_idc <= 244 ? spsdata.profile_idc : 0); - - // Make sure level_idc represents a value between levels 1 and 5.2, - // otherwise collect 0 for unknown level. - Telemetry::Accumulate(Telemetry::VIDEO_DECODED_H264_SPS_LEVEL, - (spsdata.level_idc >= 10 && spsdata.level_idc <= 52) ? - spsdata.level_idc : 0); - - // max_num_ref_frames should be between 0 and 16, anything larger will - // be treated as invalid. - Telemetry::Accumulate(Telemetry::VIDEO_H264_SPS_MAX_NUM_REF_FRAMES, - std::min(spsdata.max_num_ref_frames, 17u)); - - return true; - } - - return false; -} +extern bool AccumulateSPSTelemetry(const MediaByteBuffer* aExtradata); // MP4Demuxer wants to do various blocking reads, which cause deadlocks while // mDemuxerMonitor is held. This stuff should really be redesigned, but we don't // have time for that right now. So in order to get proper synchronization while // keeping behavior as similar as possible, we do the following nasty hack: // // The demuxer has a Stream object with APIs to do both blocking and non-blocking // reads. When it does a blocking read, MP4Stream actually redirects it to a non-
--- a/dom/media/gmp/GMPSharedMemManager.cpp +++ b/dom/media/gmp/GMPSharedMemManager.cpp @@ -35,19 +35,19 @@ GMPSharedMemManager::MgrAllocShmem(GMPSh return true; } } // Didn't find a buffer free with enough space; allocate one size_t pagesize = ipc::SharedMemory::SystemPageSize(); aSize = (aSize + (pagesize-1)) & ~(pagesize-1); // round up to page size bool retval = Alloc(aSize, aType, aMem); - // The allocator (or NeedsShmem call) should never return less than we ask for... - MOZ_ASSERT(aMem->Size<uint8_t>() >= aSize); if (retval) { + // The allocator (or NeedsShmem call) should never return less than we ask for... + MOZ_ASSERT(aMem->Size<uint8_t>() >= aSize); mData->mGmpAllocated[aClass]++; } return retval; } bool GMPSharedMemManager::MgrDeallocShmem(GMPSharedMem::GMPMemoryClasses aClass, ipc::Shmem& aMem) {
--- a/dom/media/mediasource/MediaSourceDecoder.cpp +++ b/dom/media/mediasource/MediaSourceDecoder.cpp @@ -141,16 +141,17 @@ void MediaSourceDecoder::Shutdown() { MSE_DEBUG("Shutdown"); // Detach first so that TrackBuffers are unused on the main thread when // shut down on the decode task queue. if (mMediaSource) { mMediaSource->Detach(); } + mDemuxer = nullptr; MediaDecoder::Shutdown(); // Kick WaitForData out of its slumber. ReentrantMonitorAutoEnter mon(GetReentrantMonitor()); mon.NotifyAll(); } /*static*/ @@ -287,16 +288,17 @@ MediaSourceDecoder::PrepareReaderInitial MOZ_ASSERT(mReader); GetReader()->PrepareInitialization(); } void MediaSourceDecoder::GetMozDebugReaderData(nsAString& aString) { if (mIsUsingFormatReader) { + mDemuxer->GetMozDebugReaderData(aString); return; } GetReader()->GetMozDebugReaderData(aString); } #ifdef MOZ_EME nsresult MediaSourceDecoder::SetCDMProxy(CDMProxy* aProxy)
--- a/dom/media/mediasource/MediaSourceDemuxer.cpp +++ b/dom/media/mediasource/MediaSourceDemuxer.cpp @@ -5,16 +5,17 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include <algorithm> #include <limits> #include <stdint.h> #include "MediaSourceDemuxer.h" #include "SourceBufferList.h" +#include "nsPrintfCString.h" namespace mozilla { typedef TrackInfo::TrackType TrackType; using media::TimeUnit; using media::TimeIntervals; // Gap allowed between frames. Due to inaccuracies in determining buffer end @@ -219,16 +220,51 @@ MediaSourceDemuxer::GetManager(TrackType } MediaSourceDemuxer::~MediaSourceDemuxer() { mTaskQueue->BeginShutdown(); mTaskQueue = nullptr; } +void +MediaSourceDemuxer::GetMozDebugReaderData(nsAString& aString) +{ + MonitorAutoLock mon(mMonitor); + nsAutoCString result; + result += nsPrintfCString("Dumping data for demuxer %p:\n", this); + if (mAudioTrack) { + result += nsPrintfCString("\tDumping Audio Track Buffer(%s): - mLastAudioTime: %f\n" + "\t\tNumSamples:%u Size:%u NextGetSampleIndex:%u NextInsertionIndex:%d\n", + mAudioTrack->mAudioTracks.mInfo->mMimeType.get(), + mAudioTrack->mAudioTracks.mNextSampleTime.ToSeconds(), + mAudioTrack->mAudioTracks.mBuffers[0].Length(), + mAudioTrack->mAudioTracks.mSizeBuffer, + mAudioTrack->mAudioTracks.mNextGetSampleIndex.valueOr(-1), + mAudioTrack->mAudioTracks.mNextInsertionIndex.valueOr(-1)); + + result += nsPrintfCString("\t\tBuffered: ranges=%s\n", + DumpTimeRanges(mAudioTrack->SafeBuffered(TrackInfo::kAudioTrack)).get()); + } + if (mVideoTrack) { + result += nsPrintfCString("\tDumping Video Track Buffer(%s) - mLastVideoTime: %f\n" + "\t\tNumSamples:%u Size:%u NextGetSampleIndex:%u NextInsertionIndex:%d\n", + mVideoTrack->mVideoTracks.mInfo->mMimeType.get(), + mVideoTrack->mVideoTracks.mNextSampleTime.ToSeconds(), + mVideoTrack->mVideoTracks.mBuffers[0].Length(), + mVideoTrack->mVideoTracks.mSizeBuffer, + mVideoTrack->mVideoTracks.mNextGetSampleIndex.valueOr(-1), + mVideoTrack->mVideoTracks.mNextInsertionIndex.valueOr(-1)); + + result += nsPrintfCString("\t\tBuffered: ranges=%s\n", + DumpTimeRanges(mVideoTrack->SafeBuffered(TrackInfo::kVideoTrack)).get()); + } + aString += NS_ConvertUTF8toUTF16(result); +} + MediaSourceTrackDemuxer::MediaSourceTrackDemuxer(MediaSourceDemuxer* aParent, TrackInfo::TrackType aType, TrackBuffersManager* aManager) : mParent(aParent) , mManager(aManager) , mType(aType) , mMonitor("MediaSourceTrackDemuxer") { @@ -309,17 +345,20 @@ MediaSourceTrackDemuxer::GetBuffered() return mManager->Buffered(); } void MediaSourceTrackDemuxer::BreakCycles() { nsRefPtr<MediaSourceTrackDemuxer> self = this; nsCOMPtr<nsIRunnable> task = - NS_NewRunnableFunction([self]() { self->mParent = nullptr; } ); + NS_NewRunnableFunction([self]() { + self->mParent = nullptr; + self->mManager = nullptr; + } ); mParent->GetTaskQueue()->Dispatch(task.forget()); } nsRefPtr<MediaSourceTrackDemuxer::SeekPromise> MediaSourceTrackDemuxer::DoSeek(media::TimeUnit aTime) { if (aTime.ToMicroseconds() && !mBufferedRanges.Contains(aTime)) { // We don't have the data to seek to.
--- a/dom/media/mediasource/MediaSourceDemuxer.h +++ b/dom/media/mediasource/MediaSourceDemuxer.h @@ -50,16 +50,20 @@ public: bool ShouldComputeStartTime() const override { return false; } /* interface for TrackBuffersManager */ void AttachSourceBuffer(TrackBuffersManager* aSourceBuffer); void DetachSourceBuffer(TrackBuffersManager* aSourceBuffer); MediaTaskQueue* GetTaskQueue() { return mTaskQueue; } void NotifyTimeRangesChanged(); + // Returns a string describing the state of the MediaSource internal + // buffered data. Used for debugging purposes. + void GetMozDebugReaderData(nsAString& aString); + private: ~MediaSourceDemuxer(); friend class MediaSourceTrackDemuxer; // Scan source buffers and update information. bool ScanSourceBuffersForContent(); nsRefPtr<InitPromise> AttemptInit(); TrackBuffersManager* GetManager(TrackInfo::TrackType aType); TrackInfo* GetTrackInfo(TrackInfo::TrackType);
--- a/dom/media/mediasource/TrackBuffersManager.cpp +++ b/dom/media/mediasource/TrackBuffersManager.cpp @@ -1708,16 +1708,25 @@ TrackBuffersManager::GetMetadata() const TimeIntervals& TrackBuffersManager::Buffered(TrackInfo::TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); return GetTracksData(aTrack).mBufferedRanges; } +TimeIntervals +TrackBuffersManager::SafeBuffered(TrackInfo::TrackType aTrack) const +{ + MonitorAutoLock mon(mMonitor); + return aTrack == TrackInfo::kVideoTrack + ? mVideoBufferedRanges + : mAudioBufferedRanges; +} + const TrackBuffersManager::TrackBuffer& TrackBuffersManager::GetTrackBuffer(TrackInfo::TrackType aTrack) { MOZ_ASSERT(OnTaskQueue()); return GetTracksData(aTrack).mBuffers.LastElement(); } TimeUnit
--- a/dom/media/mediasource/TrackBuffersManager.h +++ b/dom/media/mediasource/TrackBuffersManager.h @@ -72,16 +72,17 @@ public: void SetGroupStartTimestamp(const TimeUnit& aGroupStartTimestamp) override; void RestartGroupStartTimestamp() override; TimeUnit GroupEndTimestamp() override; // Interface for MediaSourceDemuxer MediaInfo GetMetadata(); const TrackBuffer& GetTrackBuffer(TrackInfo::TrackType aTrack); const TimeIntervals& Buffered(TrackInfo::TrackType); + TimeIntervals SafeBuffered(TrackInfo::TrackType) const; bool IsEnded() const { return mEnded; } TimeUnit Seek(TrackInfo::TrackType aTrack, const TimeUnit& aTime); uint32_t SkipToNextRandomAccessPoint(TrackInfo::TrackType aTrack, const TimeUnit& aTimeThreadshold, bool& aFound); @@ -90,16 +91,18 @@ public: bool& aError); TimeUnit GetNextRandomAccessPoint(TrackInfo::TrackType aTrack); #if defined(DEBUG) void Dump(const char* aPath) override; #endif private: + // for MediaSourceDemuxer::GetMozDebugReaderData + friend class MediaSourceDemuxer; virtual ~TrackBuffersManager(); // All following functions run on the taskqueue. nsRefPtr<AppendPromise> InitSegmentParserLoop(); void ScheduleSegmentParserLoop(); void SegmentParserLoop(); void AppendIncomingBuffers(); void InitializationSegmentReceived(); void ShutdownDemuxers();
--- a/dom/media/test/test_bug495145.html +++ b/dom/media/test/test_bug495145.html @@ -10,16 +10,20 @@ https://bugzilla.mozilla.org/show_bug.cg <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> <script type="text/javascript" src="manifest.js"></script> </head> <body> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=495145">Mozilla Bug 495145</a> <pre id="test"> <script class="testbody" type="text/javascript"> +//longer timeout for slow platforms +if (isSlowPlatform()) { + SimpleTest.requestLongerTimeout(1.5); +} var manager = new MediaTestManager; function start(e) { e.target.play(); } function ended1(e) {
--- a/dom/media/test/test_bug879717.html +++ b/dom/media/test/test_bug879717.html @@ -4,16 +4,21 @@ <title>Test for bug 879717, check that a video element can be drawn into a canvas at various states of playback</title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> <script type="text/javascript" src="manifest.js"></script> </head> <body> <pre id="test"> <script class="testbody" type="text/javascript"> +//longer timeout for slow platforms +if (isSlowPlatform()) { + SimpleTest.requestLongerTimeout(1.5); +} + var manager = new MediaTestManager; var canvas = document.createElement('canvas'); document.body.appendChild(canvas); var checkDrawImage = function(eventName, videoElement) { var exception = null; var exceptionName = "nothing";
--- a/dom/media/test/test_loop.html +++ b/dom/media/test/test_loop.html @@ -4,16 +4,21 @@ <title>Test looping support</title> <script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" /> <script type="text/javascript" src="manifest.js"></script> </head> <body> <pre id="test"> <script class="testbody" type="text/javascript"> +//longer timeout for slow platforms +if (isSlowPlatform()) { + SimpleTest.requestLongerTimeout(1.5); +} + var manager = new MediaTestManager; function startTest(test, token) { manager.started(token); var v = document.createElement('video'); v.token = token; v.src = test.name; v.name = test.name;
--- a/dom/media/tests/mochitest/mochitest.ini +++ b/dom/media/tests/mochitest/mochitest.ini @@ -89,18 +89,18 @@ skip-if = toolkit == 'gonk' # B2G emulat skip-if = buildapp == 'b2g' || buildapp == 'mulet' || os == 'android' # bug 1043403 # Bug 1141029 Mulet parity with B2G Desktop for TC [test_peerConnection_capturedVideo.html] tags=capturestream skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) [test_peerConnection_captureStream_canvas_2d.html] skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) [test_peerConnection_captureStream_canvas_webgl.html] skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) -[test_peerConnection_certificates.html] -skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867) +# [test_peerConnection_certificates.html] # bug 1180968 +# skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g (Bug 1059867) [test_peerConnection_close.html] [test_peerConnection_closeDuringIce.html] [test_peerConnection_errorCallbacks.html] [test_peerConnection_iceFailure.html] skip-if = toolkit == 'gonk' || buildapp == 'mulet' # Disabling because of test failures on B2G emulator [test_peerConnection_forwarding_basicAudioVideoCombined.html] skip-if = toolkit == 'gonk' || buildapp == 'mulet' # b2g(Bug 960442, video support for WebRTC is disabled on b2g) [test_peerConnection_noTrickleAnswer.html]
--- a/dom/media/webaudio/AlignedTArray.h +++ b/dom/media/webaudio/AlignedTArray.h @@ -9,77 +9,55 @@ #include "mozilla/Alignment.h" #include "nsTArray.h" /** * E: element type, must be a POD type. * N: N bytes alignment for the first element, defaults to 32 */ -template <typename E, int N, typename Alloc> -class AlignedTArray_Impl : public nsTArray_Impl<E, Alloc> +template <typename E, int N = 32> +class AlignedTArray : public nsTArray_Impl<E, nsTArrayInfallibleAllocator> { static_assert((N & (N-1)) == 0, "N must be power of 2"); - typedef nsTArray_Impl<E, Alloc> base_type; + typedef nsTArray_Impl<E, nsTArrayInfallibleAllocator> base_type; public: typedef E elem_type; typedef typename base_type::size_type size_type; typedef typename base_type::index_type index_type; - AlignedTArray_Impl() {} - explicit AlignedTArray_Impl(size_type capacity) : base_type(capacity+sExtra) {} + AlignedTArray() {} + explicit AlignedTArray(size_type capacity) : base_type(capacity + sExtra) {} elem_type* Elements() { return getAligned(base_type::Elements()); } const elem_type* Elements() const { return getAligned(base_type::Elements()); } elem_type& operator[](index_type i) { return Elements()[i];} const elem_type& operator[](index_type i) const { return Elements()[i]; } - typename Alloc::ResultType SetLength(size_type newLen) { - return base_type::SetLength(newLen + sExtra); + void SetLength(size_type newLen) + { + base_type::SetLength(newLen + sExtra); } + + MOZ_WARN_UNUSED_RESULT + bool SetLength(size_type newLen, const mozilla::fallible_t&) + { + return base_type::SetLength(newLen + sExtra, mozilla::fallible); + } + size_type Length() const { return base_type::Length() <= sExtra ? 0 : base_type::Length() - sExtra; } private: - AlignedTArray_Impl(const AlignedTArray_Impl& other) = delete; - void operator=(const AlignedTArray_Impl& other) = delete; + AlignedTArray(const AlignedTArray& other) = delete; + void operator=(const AlignedTArray& other) = delete; static const size_type sPadding = N <= MOZ_ALIGNOF(E) ? 0 : N - MOZ_ALIGNOF(E); static const size_type sExtra = (sPadding + sizeof(E) - 1) / sizeof(E); template <typename U> static U* getAligned(U* p) { return reinterpret_cast<U*>(((uintptr_t)p + N - 1) & ~(N-1)); } }; -template <typename E, int N=32> -class AlignedTArray : public AlignedTArray_Impl<E, N, nsTArrayInfallibleAllocator> -{ -public: - typedef AlignedTArray_Impl<E, N, nsTArrayInfallibleAllocator> base_type; - typedef AlignedTArray<E, N> self_type; - typedef typename base_type::size_type size_type; - - AlignedTArray() {} - explicit AlignedTArray(size_type capacity) : base_type(capacity) {} -private: - AlignedTArray(const AlignedTArray& other) = delete; - void operator=(const AlignedTArray& other) = delete; -}; - -template <typename E, int N=32> -class AlignedFallibleTArray : public AlignedTArray_Impl<E, N, nsTArrayFallibleAllocator> -{ -public: - typedef AlignedTArray_Impl<E, N, nsTArrayFallibleAllocator> base_type; - typedef AlignedFallibleTArray<E, N> self_type; - typedef typename base_type::size_type size_type; - - AlignedFallibleTArray() {} - explicit AlignedFallibleTArray(size_type capacity) : base_type(capacity) {} -private: - AlignedFallibleTArray(const AlignedFallibleTArray& other) = delete; - void operator=(const AlignedFallibleTArray& other) = delete; -}; - #endif // AlignedTArray_h__
--- a/dom/media/webaudio/AnalyserNode.cpp +++ b/dom/media/webaudio/AnalyserNode.cpp @@ -246,21 +246,21 @@ AnalyserNode::GetByteTimeDomainData(cons buffer[i] = static_cast<unsigned char>(scaled); } } bool AnalyserNode::FFTAnalysis() { float* inputBuffer; - AlignedFallibleTArray<float> tmpBuffer; + AlignedTArray<float> tmpBuffer; if (mWriteIndex == 0) { inputBuffer = mBuffer.Elements(); } else { - if (!tmpBuffer.SetLength(FftSize())) { + if (!tmpBuffer.SetLength(FftSize(), fallible)) { return false; } inputBuffer = tmpBuffer.Elements(); memcpy(inputBuffer, mBuffer.Elements() + mWriteIndex, sizeof(float) * (FftSize() - mWriteIndex)); memcpy(inputBuffer + FftSize() - mWriteIndex, mBuffer.Elements(), sizeof(float) * mWriteIndex); } ApplyBlackmanWindow(inputBuffer, FftSize()); @@ -296,23 +296,23 @@ AnalyserNode::ApplyBlackmanWindow(float* } } bool AnalyserNode::AllocateBuffer() { bool result = true; if (mBuffer.Length() != FftSize()) { - if (!mBuffer.SetLength(FftSize())) { + if (!mBuffer.SetLength(FftSize(), fallible)) { return false; } memset(mBuffer.Elements(), 0, sizeof(float) * FftSize()); mWriteIndex = 0; - if (!mOutputBuffer.SetLength(FrequencyBinCount())) { + if (!mOutputBuffer.SetLength(FrequencyBinCount(), fallible)) { return false; } memset(mOutputBuffer.Elements(), 0, sizeof(float) * FrequencyBinCount()); } return result; } void
--- a/dom/media/webaudio/AnalyserNode.h +++ b/dom/media/webaudio/AnalyserNode.h @@ -73,17 +73,17 @@ private: void ApplyBlackmanWindow(float* aBuffer, uint32_t aSize); private: FFTBlock mAnalysisBlock; double mMinDecibels; double mMaxDecibels; double mSmoothingTimeConstant; uint32_t mWriteIndex; - AlignedFallibleTArray<float> mBuffer; - AlignedFallibleTArray<float> mOutputBuffer; + AlignedTArray<float> mBuffer; + AlignedTArray<float> mOutputBuffer; }; } // namespace dom } // namespace mozilla #endif
--- a/dom/plugins/ipc/PluginModuleParent.cpp +++ b/dom/plugins/ipc/PluginModuleParent.cpp @@ -130,17 +130,17 @@ mozilla::plugins::SetupBridge(uint32_t a return true; } *rv = PPluginModule::Bridge(aContentParent, chromeParent); if (NS_FAILED(*rv)) { #if defined(MOZ_CRASHREPORTER) // We are going to abort due to the failure, lets note the cause // in the report for diagnosing. nsAutoCString error; - error.AppendPrintf("%X", *rv); + error.AppendPrintf("%X %d", *rv, chromeParent->GetIPCChannel()->GetChannelState__TotallyRacy()); CrashReporter::AnnotateCrashReport(NS_LITERAL_CSTRING("BridgePluginError"), error); #endif return false; } return true; } #ifdef MOZ_CRASHREPORTER_INJECTOR
--- a/dom/speakermanager/SpeakerManagerService.cpp +++ b/dom/speakermanager/SpeakerManagerService.cpp @@ -174,35 +174,47 @@ SpeakerManagerService::Observe(nsISuppor } if (mOrgSpeakerStatus) { TurnOnSpeaker(!mOrgSpeakerStatus); mOrgSpeakerStatus = false; } } else { NS_WARNING("ipc:content-shutdown message without childID property"); } + } else if (!strcmp(aTopic, "xpcom-will-shutdown")) { + // Note that we need to do this before xpcom-shutdown, since the + // AudioChannelService cannot be used past that point. + nsRefPtr<AudioChannelService> audioChannelService = + AudioChannelService::GetOrCreate(); + audioChannelService->UnregisterSpeakerManager(this); + + nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); + if (obs) { + obs->RemoveObserver(this, "ipc:content-shutdown"); + obs->RemoveObserver(this, "xpcom-will-shutdown"); + } + + Shutdown(); } return NS_OK; } SpeakerManagerService::SpeakerManagerService() : mOrgSpeakerStatus(false), mVisible(false) { MOZ_COUNT_CTOR(SpeakerManagerService); if (XRE_IsParentProcess()) { nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService(); if (obs) { obs->AddObserver(this, "ipc:content-shutdown", false); + obs->AddObserver(this, "xpcom-will-shutdown", false); } } nsRefPtr<AudioChannelService> audioChannelService = AudioChannelService::GetOrCreate(); audioChannelService->RegisterSpeakerManager(this); } SpeakerManagerService::~SpeakerManagerService() { MOZ_COUNT_DTOR(SpeakerManagerService); - nsRefPtr<AudioChannelService> audioChannelService = - AudioChannelService::GetOrCreate(); - audioChannelService->UnregisterSpeakerManager(this); }
--- a/dom/speakermanager/SpeakerManagerService.h +++ b/dom/speakermanager/SpeakerManagerService.h @@ -41,30 +41,31 @@ public: void RegisterSpeakerManager(SpeakerManager* aSpeakerManager) { mRegisteredSpeakerManagers.AppendElement(aSpeakerManager); } void UnRegisterSpeakerManager(SpeakerManager* aSpeakerManager) { mRegisteredSpeakerManagers.RemoveElement(aSpeakerManager); } - /** - * Shutdown the singleton. - */ - static void Shutdown(); protected: SpeakerManagerService(); virtual ~SpeakerManagerService(); // Notify to UA if device speaker status changed virtual void Notify(); void TurnOnSpeaker(bool aEnable); + /** + * Shutdown the singleton. + */ + static void Shutdown(); + nsTArray<nsRefPtr<SpeakerManager> > mRegisteredSpeakerManagers; // Set for remember all the child speaker status nsCheapSet<nsUint64HashKey> mSpeakerStatusSet; // The Speaker status assign by UA bool mOrgSpeakerStatus; bool mVisible; // This is needed for IPC communication between
--- a/dom/workers/ScriptLoader.cpp +++ b/dom/workers/ScriptLoader.cpp @@ -1724,16 +1724,17 @@ ScriptExecutorRunnable::WorkerRun(JSCont NS_ASSERTION(!loadInfo.mChannel, "Should no longer have a channel!"); NS_ASSERTION(loadInfo.mExecutionScheduled, "Should be scheduled!"); NS_ASSERTION(!loadInfo.mExecutionResult, "Should not have executed yet!"); if (NS_FAILED(loadInfo.mLoadResult)) { scriptloader::ReportLoadError(aCx, loadInfo.mURL, loadInfo.mLoadResult, false); + aWorkerPrivate->MaybeDispatchLoadFailedRunnable(); return true; } NS_ConvertUTF16toUTF8 filename(loadInfo.mURL); JS::CompileOptions options(aCx); options.setFileAndLine(filename.get(), 1) .setNoScriptRval(true);
--- a/dom/workers/ServiceWorkerContainer.cpp +++ b/dom/workers/ServiceWorkerContainer.cpp @@ -159,18 +159,23 @@ ServiceWorkerContainer::GetController() { if (!mControllerWorker) { nsresult rv; nsCOMPtr<nsIServiceWorkerManager> swm = mozilla::services::GetServiceWorkerManager(); if (!swm) { return nullptr; } + // TODO: What should we do here if the ServiceWorker script fails to load? + // In theory the DOM ServiceWorker object can exist without the worker + // thread running, but it seems our design does not expect that. nsCOMPtr<nsISupports> serviceWorker; - rv = swm->GetDocumentController(GetOwner(), getter_AddRefs(serviceWorker)); + rv = swm->GetDocumentController(GetOwner(), + nullptr, // aLoadFailedRunnable + getter_AddRefs(serviceWorker)); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } mControllerWorker = static_cast<workers::ServiceWorker*>(serviceWorker.get()); }
--- a/dom/workers/ServiceWorkerManager.cpp +++ b/dom/workers/ServiceWorkerManager.cpp @@ -1057,47 +1057,63 @@ public: } nsAutoString cacheName; // We have to create a ServiceWorker here simply to ensure there are no // errors. Ideally we should just pass this worker on to ContinueInstall. MOZ_ASSERT(!data->mSetOfScopesBeingUpdated.Contains(mRegistration->mScope)); data->mSetOfScopesBeingUpdated.Put(mRegistration->mScope, true); + // Call FailScopeUpdate on main thread if the SW script load fails below. + nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs + <StorensRefPtrPassByPtr<ServiceWorkerManager>, nsCString> + (this, &ServiceWorkerRegisterJob::FailScopeUpdate, swm, scopeKey); + MOZ_ASSERT(!mUpdateAndInstallInfo); mUpdateAndInstallInfo = new ServiceWorkerInfo(mRegistration, mRegistration->mScriptSpec, aNewCacheName); nsRefPtr<ServiceWorker> serviceWorker; rv = swm->CreateServiceWorker(mRegistration->mPrincipal, mUpdateAndInstallInfo, + failRunnable, getter_AddRefs(serviceWorker)); if (NS_WARN_IF(NS_FAILED(rv))) { - data->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope); - return Fail(NS_ERROR_DOM_ABORT_ERR); + return FailScopeUpdate(swm, scopeKey); } nsRefPtr<ServiceWorkerJob> upcasted = this; nsMainThreadPtrHandle<nsISupports> handle( new nsMainThreadPtrHolder<nsISupports>(upcasted)); nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle( new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker)); nsRefPtr<CheckWorkerEvaluationAndContinueUpdateWorkerRunnable> r = new CheckWorkerEvaluationAndContinueUpdateWorkerRunnable(serviceWorkerHandle, handle); AutoJSAPI jsapi; jsapi.Init(); bool ok = r->Dispatch(jsapi.cx()); if (NS_WARN_IF(!ok)) { - data->mSetOfScopesBeingUpdated.Remove(mRegistration->mScope); - return Fail(NS_ERROR_DOM_ABORT_ERR); + return FailScopeUpdate(swm, scopeKey); } } + void + FailScopeUpdate(ServiceWorkerManager* aSwm, const nsACString& aScopeKey) + { + MOZ_ASSERT(NS_IsMainThread()); + MOZ_ASSERT(aSwm); + ServiceWorkerManager::RegistrationDataPerPrincipal* data; + if (aSwm->mRegistrationInfos.Get(aScopeKey, &data)) { + data->mSetOfScopesBeingUpdated.Remove(aScopeKey); + } + Fail(NS_ERROR_DOM_ABORT_ERR); + } + // Public so our error handling code can use it. // Callers MUST hold a strong ref before calling this! void Fail(const ErrorEventInit& aError) { MOZ_ASSERT(mCallback); nsRefPtr<ServiceWorkerUpdateFinishCallback> callback = mCallback.forget(); // With cancellation support, we may only be running with one reference @@ -1163,19 +1179,25 @@ public: nsCOMPtr<nsIRunnable> upr = NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>( swm, &ServiceWorkerManager::FireUpdateFoundOnServiceWorkerRegistrations, mRegistration); NS_DispatchToMainThread(upr); + // Call ContinueAfterInstallEvent(false, false) on main thread if the SW + // script fails to load. + nsCOMPtr<nsIRunnable> failRunnable = NS_NewRunnableMethodWithArgs<bool, bool> + (this, &ServiceWorkerRegisterJob::ContinueAfterInstallEvent, false, false); + nsRefPtr<ServiceWorker> serviceWorker; rv = swm->CreateServiceWorker(mRegistration->mPrincipal, mRegistration->mInstallingWorker, + failRunnable, getter_AddRefs(serviceWorker)); if (NS_WARN_IF(NS_FAILED(rv))) { ContinueAfterInstallEvent(false /* aSuccess */, false /* aActivateImmediately */); return; } nsMainThreadPtrHandle<ContinueLifecycleTask> handle( @@ -1829,28 +1851,30 @@ ServiceWorkerRegistrationInfo::Activate( // "Queue a task to fire a simple event named controllerchange..." nsCOMPtr<nsIRunnable> controllerChangeRunnable = NS_NewRunnableMethodWithArg<ServiceWorkerRegistrationInfo*>(swm, &ServiceWorkerManager::FireControllerChange, this); NS_DispatchToMainThread(controllerChangeRunnable); + nsCOMPtr<nsIRunnable> failRunnable = + NS_NewRunnableMethodWithArg<bool>(this, + &ServiceWorkerRegistrationInfo::FinishActivate, + false /* success */); + MOZ_ASSERT(mActiveWorker); nsRefPtr<ServiceWorker> serviceWorker; nsresult rv = swm->CreateServiceWorker(mPrincipal, mActiveWorker, + failRunnable, getter_AddRefs(serviceWorker)); if (NS_WARN_IF(NS_FAILED(rv))) { - nsCOMPtr<nsIRunnable> r = - NS_NewRunnableMethodWithArg<bool>(this, - &ServiceWorkerRegistrationInfo::FinishActivate, - false /* success */); - MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(r))); + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(failRunnable))); return; } nsMainThreadPtrHandle<ContinueLifecycleTask> handle( new nsMainThreadPtrHolder<ContinueLifecycleTask>(new ContinueActivateTask(this))); nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle( new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker)); @@ -2222,17 +2246,17 @@ ServiceWorkerManager::SendPushEvent(cons return NS_ERROR_NOT_AVAILABLE; #else OriginAttributes attrs; if (!attrs.PopulateFromSuffix(aOriginAttributes)) { return NS_ERROR_INVALID_ARG; } nsRefPtr<ServiceWorker> serviceWorker = - CreateServiceWorkerForScope(attrs, aScope); + CreateServiceWorkerForScope(attrs, aScope, nullptr /* failure runnable */); if (!serviceWorker) { return NS_ERROR_FAILURE; } nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle( new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker)); nsRefPtr<SendPushEventRunnable> r = @@ -2257,17 +2281,17 @@ ServiceWorkerManager::SendPushSubscripti return NS_ERROR_NOT_AVAILABLE; #else OriginAttributes attrs; if (!attrs.PopulateFromSuffix(aOriginAttributes)) { return NS_ERROR_INVALID_ARG; } nsRefPtr<ServiceWorker> serviceWorker = - CreateServiceWorkerForScope(attrs, aScope); + CreateServiceWorkerForScope(attrs, aScope, nullptr /* fail runnable */); if (!serviceWorker) { return NS_ERROR_FAILURE; } nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle( new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker)); nsRefPtr<SendPushSubscriptionChangeEventRunnable> r = new SendPushSubscriptionChangeEventRunnable( @@ -2382,17 +2406,18 @@ ServiceWorkerManager::SendNotificationCl const nsAString& aData, const nsAString& aBehavior) { OriginAttributes attrs; if (!attrs.PopulateFromSuffix(aOriginSuffix)) { return NS_ERROR_INVALID_ARG; } - nsRefPtr<ServiceWorker> serviceWorker = CreateServiceWorkerForScope(attrs, aScope); + nsRefPtr<ServiceWorker> serviceWorker = + CreateServiceWorkerForScope(attrs, aScope, nullptr); if (!serviceWorker) { return NS_ERROR_FAILURE; } nsMainThreadPtrHandle<ServiceWorker> serviceWorkerHandle( new nsMainThreadPtrHolder<ServiceWorker>(serviceWorker)); nsRefPtr<SendNotificationClickEventRunnable> r = new SendNotificationClickEventRunnable(serviceWorker->GetWorkerPrivate(), @@ -2520,17 +2545,18 @@ ServiceWorkerManager::CheckReadyPromise( return true; } return false; } already_AddRefed<ServiceWorker> ServiceWorkerManager::CreateServiceWorkerForScope(const OriginAttributes& aOriginAttributes, - const nsACString& aScope) + const nsACString& aScope, + nsIRunnable* aLoadFailedRunnable) { AssertIsOnMainThread(); nsCOMPtr<nsIURI> scopeURI; nsresult rv = NS_NewURI(getter_AddRefs(scopeURI), aScope, nullptr, nullptr); if (NS_FAILED(rv)) { return nullptr; } @@ -2542,16 +2568,17 @@ ServiceWorkerManager::CreateServiceWorke if (!registration->mActiveWorker) { return nullptr; } nsRefPtr<ServiceWorker> sw; rv = CreateServiceWorker(registration->mPrincipal, registration->mActiveWorker, + aLoadFailedRunnable, getter_AddRefs(sw)); if (NS_WARN_IF(NS_FAILED(rv))) { return nullptr; } return sw.forget(); } @@ -2822,16 +2849,17 @@ ServiceWorkerRegistrationInfo::FinishAct mActiveWorker->UpdateState(ServiceWorkerState::Redundant); mActiveWorker = nullptr; } } NS_IMETHODIMP ServiceWorkerManager::CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow, ServiceWorkerInfo* aInfo, + nsIRunnable* aLoadFailedRunnable, ServiceWorker** aServiceWorker) { AssertIsOnMainThread(); MOZ_ASSERT(aWindow); MOZ_ASSERT(aInfo); AutoJSAPI jsapi; jsapi.Init(aWindow); @@ -2846,16 +2874,17 @@ ServiceWorkerManager::CreateServiceWorke &loadInfo); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } MOZ_ASSERT(!aInfo->CacheName().IsEmpty()); loadInfo.mServiceWorkerCacheName = aInfo->CacheName(); loadInfo.mServiceWorkerID = aInfo->ID(); + loadInfo.mLoadFailedAsyncRunnable = aLoadFailedRunnable; RuntimeService* rs = RuntimeService::GetOrCreateService(); if (!rs) { return NS_ERROR_FAILURE; } nsRefPtr<SharedWorker> sharedWorker; rv = rs->CreateSharedWorkerForServiceWorkerFromLoadInfo(cx, &loadInfo, @@ -3413,19 +3442,23 @@ ServiceWorkerManager::GetServiceWorkerFo } else { MOZ_CRASH("Invalid worker type"); } if (NS_WARN_IF(!info)) { return NS_ERROR_DOM_NOT_FOUND_ERR; } + // TODO: How should we handle async failure of SW load for getters? In + // theory getting a handle to the DOM ServiceWorker object should not + // require the worker thread to actually be running. nsRefPtr<ServiceWorker> serviceWorker; rv = CreateServiceWorkerForWindow(window, info, + nullptr, // load failed runnable getter_AddRefs(serviceWorker)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } serviceWorker->SetState(info->State()); serviceWorker.forget(aServiceWorker); return NS_OK; @@ -3685,21 +3718,27 @@ ServiceWorkerManager::DispatchFetchEvent nsCOMPtr<nsISupports> serviceWorker; bool isNavigation = false; aRv = aChannel->GetIsNavigation(&isNavigation); if (NS_WARN_IF(aRv.Failed())) { return; } + // if the ServiceWorker script fails to load for some reason, just resume + // the original channel. + nsCOMPtr<nsIRunnable> failRunnable = + NS_NewRunnableMethod(aChannel, &nsIInterceptedChannel::ResetInterception); + nsAutoPtr<ServiceWorkerClientInfo> clientInfo; if (!isNavigation) { MOZ_ASSERT(aDoc); - aRv = GetDocumentController(aDoc->GetInnerWindow(), getter_AddRefs(serviceWorker)); + aRv = GetDocumentController(aDoc->GetInnerWindow(), failRunnable, + getter_AddRefs(serviceWorker)); clientInfo = new ServiceWorkerClientInfo(aDoc, aDoc->GetWindow()); } else { nsCOMPtr<nsIChannel> internalChannel; aRv = aChannel->GetChannel(getter_AddRefs(internalChannel)); if (NS_WARN_IF(aRv.Failed())) { return; } @@ -3718,16 +3757,17 @@ ServiceWorkerManager::DispatchFetchEvent } // This should only happen if IsAvailable() returned true. MOZ_ASSERT(registration->mActiveWorker); nsRefPtr<ServiceWorker> sw; aRv = CreateServiceWorker(registration->mPrincipal, registration->mActiveWorker, + failRunnable, getter_AddRefs(sw)); serviceWorker = sw.forget(); } if (NS_WARN_IF(aRv.Failed())) { return; } @@ -3801,17 +3841,19 @@ ServiceWorkerManager::GetDocumentRegistr return NS_OK; } /* * The .controller is for the registration associated with the document when * the document was loaded. */ NS_IMETHODIMP -ServiceWorkerManager::GetDocumentController(nsIDOMWindow* aWindow, nsISupports** aServiceWorker) +ServiceWorkerManager::GetDocumentController(nsIDOMWindow* aWindow, + nsIRunnable* aLoadFailedRunnable, + nsISupports** aServiceWorker) { nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(aWindow); MOZ_ASSERT(window); if (!window || !window->GetExtantDoc()) { return NS_ERROR_FAILURE; } nsCOMPtr<nsIDocument> doc = window->GetExtantDoc(); @@ -3820,16 +3862,17 @@ ServiceWorkerManager::GetDocumentControl nsresult rv = GetDocumentRegistration(doc, getter_AddRefs(registration)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } nsRefPtr<ServiceWorker> serviceWorker; rv = CreateServiceWorkerForWindow(window, registration->mActiveWorker, + aLoadFailedRunnable, getter_AddRefs(serviceWorker)); if (NS_WARN_IF(NS_FAILED(rv))) { return rv; } serviceWorker.forget(aServiceWorker); return NS_OK; } @@ -3862,16 +3905,17 @@ ServiceWorkerManager::GetActive(nsIDOMWi return GetServiceWorkerForScope(aWindow, aScope, WhichServiceWorker::ACTIVE_WORKER, aServiceWorker); } NS_IMETHODIMP ServiceWorkerManager::CreateServiceWorker(nsIPrincipal* aPrincipal, ServiceWorkerInfo* aInfo, + nsIRunnable* aLoadFailedRunnable, ServiceWorker** aServiceWorker) { AssertIsOnMainThread(); MOZ_ASSERT(aPrincipal); WorkerLoadInfo info; nsresult rv = NS_NewURI(getter_AddRefs(info.mBaseURI), aInfo->ScriptSpec(), nullptr, nullptr); @@ -3916,16 +3960,18 @@ ServiceWorkerManager::CreateServiceWorke // NOTE: this defaults the SW load context to: // - private browsing = false // - content = true // - use remote tabs = false // Alternatively we could persist the original load group values and use // them here. WorkerPrivate::OverrideLoadInfoLoadGroup(info); + info.mLoadFailedAsyncRunnable = aLoadFailedRunnable; + RuntimeService* rs = RuntimeService::GetOrCreateService(); if (!rs) { return NS_ERROR_FAILURE; } AutoJSAPI jsapi; jsapi.Init(); nsRefPtr<SharedWorker> sharedWorker;
--- a/dom/workers/ServiceWorkerManager.h +++ b/dom/workers/ServiceWorkerManager.h @@ -415,32 +415,35 @@ private: Update(ServiceWorkerRegistrationInfo* aRegistration); nsresult GetDocumentRegistration(nsIDocument* aDoc, ServiceWorkerRegistrationInfo** aRegistrationInfo); NS_IMETHOD CreateServiceWorkerForWindow(nsPIDOMWindow* aWindow, ServiceWorkerInfo* aInfo, + nsIRunnable* aLoadFailedRunnable, ServiceWorker** aServiceWorker); NS_IMETHOD CreateServiceWorker(nsIPrincipal* aPrincipal, ServiceWorkerInfo* aInfo, + nsIRunnable* aLoadFailedRunnable, ServiceWorker** aServiceWorker); NS_IMETHODIMP GetServiceWorkerForScope(nsIDOMWindow* aWindow, const nsAString& aScope, WhichServiceWorker aWhichWorker, nsISupports** aServiceWorker); already_AddRefed<ServiceWorker> CreateServiceWorkerForScope(const OriginAttributes& aOriginAttributes, - const nsACString& aScope); + const nsACString& aScope, + nsIRunnable* aLoadFailedRunnable); void InvalidateServiceWorkerRegistrationWorker(ServiceWorkerRegistrationInfo* aRegistration, WhichServiceWorker aWhichOnes); void StartControllingADocument(ServiceWorkerRegistrationInfo* aRegistration, nsIDocument* aDoc);
--- a/dom/workers/WorkerPrivate.cpp +++ b/dom/workers/WorkerPrivate.cpp @@ -2379,16 +2379,19 @@ WorkerLoadInfo::StealFrom(WorkerLoadInfo aOther.mCSP.swap(mCSP); MOZ_ASSERT(!mChannel); aOther.mChannel.swap(mChannel); MOZ_ASSERT(!mLoadGroup); aOther.mLoadGroup.swap(mLoadGroup); + MOZ_ASSERT(!mLoadFailedAsyncRunnable); + aOther.mLoadFailedAsyncRunnable.swap(mLoadFailedAsyncRunnable); + MOZ_ASSERT(!mInterfaceRequestor); aOther.mInterfaceRequestor.swap(mInterfaceRequestor); MOZ_ASSERT(!mPrincipalInfo); mPrincipalInfo = aOther.mPrincipalInfo.forget(); mDomain = aOther.mDomain; mServiceWorkerCacheName = aOther.mServiceWorkerCacheName; @@ -3413,28 +3416,29 @@ WorkerPrivateParent<Derived>::ForgetOver template <class Derived> void WorkerPrivateParent<Derived>::ForgetMainThreadObjects( nsTArray<nsCOMPtr<nsISupports> >& aDoomed) { AssertIsOnParentThread(); MOZ_ASSERT(!mMainThreadObjectsForgotten); - static const uint32_t kDoomedCount = 9; + static const uint32_t kDoomedCount = 10; aDoomed.SetCapacity(kDoomedCount); SwapToISupportsArray(mLoadInfo.mWindow, aDoomed); SwapToISupportsArray(mLoadInfo.mScriptContext, aDoomed); SwapToISupportsArray(mLoadInfo.mBaseURI, aDoomed); SwapToISupportsArray(mLoadInfo.mResolvedScriptURI, aDoomed); SwapToISupportsArray(mLoadInfo.mPrincipal, aDoomed); SwapToISupportsArray(mLoadInfo.mChannel, aDoomed); SwapToISupportsArray(mLoadInfo.mCSP, aDoomed); SwapToISupportsArray(mLoadInfo.mLoadGroup, aDoomed); + SwapToISupportsArray(mLoadInfo.mLoadFailedAsyncRunnable, aDoomed); SwapToISupportsArray(mLoadInfo.mInterfaceRequestor, aDoomed); // Before adding anything here update kDoomedCount above! MOZ_ASSERT(aDoomed.Length() == kDoomedCount); mMainThreadObjectsForgotten = true; } @@ -5462,16 +5466,29 @@ WorkerPrivate::RunBeforeNextEvent(nsIRun return false; } } return true; } void +WorkerPrivate::MaybeDispatchLoadFailedRunnable() +{ + AssertIsOnWorkerThread(); + + nsCOMPtr<nsIRunnable> runnable = StealLoadFailedAsyncRunnable(); + if (!runnable) { + return; + } + + MOZ_ALWAYS_TRUE(NS_SUCCEEDED(NS_DispatchToMainThread(runnable.forget()))); +} + +void WorkerPrivate::InitializeGCTimers() { AssertIsOnWorkerThread(); // We need a timer for GC. The basic plan is to run a non-shrinking GC // periodically (PERIODIC_GC_TIMER_DELAY_SEC) while the worker is running. // Once the worker goes idle we set a short (IDLE_GC_TIMER_DELAY_SEC) timer to // run a shrinking GC. If the worker receives more messages then the short
--- a/dom/workers/WorkerPrivate.h +++ b/dom/workers/WorkerPrivate.h @@ -793,16 +793,22 @@ public: GetAllSharedWorkers(nsTArray<nsRefPtr<SharedWorker>>& aSharedWorkers); void CloseSharedWorkersForWindow(nsPIDOMWindow* aWindow); void UpdateOverridenLoadGroup(nsILoadGroup* aBaseLoadGroup); + already_AddRefed<nsIRunnable> + StealLoadFailedAsyncRunnable() + { + return mLoadInfo.mLoadFailedAsyncRunnable.forget(); + } + IMPL_EVENT_HANDLER(message) IMPL_EVENT_HANDLER(error) #ifdef DEBUG void AssertIsOnParentThread() const; void @@ -951,16 +957,19 @@ class WorkerPrivate : public WorkerPriva nsCOMPtr<nsITimer> mGCTimer; nsCOMPtr<nsIEventTarget> mPeriodicGCTimerTarget; nsCOMPtr<nsIEventTarget> mIdleGCTimerTarget; nsRefPtr<MemoryReporter> mMemoryReporter; nsRefPtrHashtable<nsUint64HashKey, MessagePort> mWorkerPorts; + // fired on the main thread if the worker script fails to load + nsCOMPtr<nsIRunnable> mLoadFailedRunnable; + TimeStamp mKillTime; uint32_t mErrorHandlerRecursionCount; uint32_t mNextTimeoutId; Status mStatus; bool mFrozen; bool mTimerRunning; bool mRunningExpiredTimeouts; bool mCloseHandlerStarted; @@ -1362,16 +1371,19 @@ public: return mWorkerScriptExecutedSuccessfully; } // Just like nsIAppShell::RunBeforeNextEvent. May only be called on the worker // thread. bool RunBeforeNextEvent(nsIRunnable* aRunnable); + void + MaybeDispatchLoadFailedRunnable(); + private: WorkerPrivate(JSContext* aCx, WorkerPrivate* aParent, const nsAString& aScriptURL, bool aIsChromeWorker, WorkerType aWorkerType, const nsACString& aSharedWorkerName, WorkerLoadInfo& aLoadInfo); bool MayContinueRunning()
--- a/dom/workers/Workers.h +++ b/dom/workers/Workers.h @@ -34,16 +34,17 @@ class nsIContentSecurityPolicy; class nsIScriptContext; class nsIGlobalObject; class nsPIDOMWindow; class nsIPrincipal; class nsILoadGroup; class nsITabChild; class nsIChannel; +class nsIRunnable; class nsIURI; namespace mozilla { namespace ipc { class PrincipalInfo; } // namespace ipc namespace dom { @@ -216,16 +217,22 @@ struct WorkerLoadInfo nsCOMPtr<nsIURI> mResolvedScriptURI; nsCOMPtr<nsIPrincipal> mPrincipal; nsCOMPtr<nsIScriptContext> mScriptContext; nsCOMPtr<nsPIDOMWindow> mWindow; nsCOMPtr<nsIContentSecurityPolicy> mCSP; nsCOMPtr<nsIChannel> mChannel; nsCOMPtr<nsILoadGroup> mLoadGroup; + // mLoadFailedAsyncRunnable will execute on main thread if script loading + // fails during script loading. If script loading is never started due to + // a synchronous error, then the runnable is never executed. The runnable + // is guaranteed to be released on the main thread. + nsCOMPtr<nsIRunnable> mLoadFailedAsyncRunnable; + class InterfaceRequestor final : public nsIInterfaceRequestor { NS_DECL_ISUPPORTS public: InterfaceRequestor(nsIPrincipal* aPrincipal, nsILoadGroup* aLoadGroup); void MaybeAddTabChild(nsILoadGroup* aLoadGroup); NS_IMETHOD GetInterface(const nsIID& aIID, void** aSink) override;
--- a/editor/composer/nsEditingSession.cpp +++ b/editor/composer/nsEditingSession.cpp @@ -201,17 +201,19 @@ nsEditingSession::DisableJSAndPlugins(ns mDisabledJSAndPlugins = true; return NS_OK; } NS_IMETHODIMP nsEditingSession::RestoreJSAndPlugins(nsIDOMWindow *aWindow) { - NS_ENSURE_TRUE(mDisabledJSAndPlugins, NS_OK); + if (!mDisabledJSAndPlugins) { + return NS_OK; + } mDisabledJSAndPlugins = false; nsIDocShell *docShell = GetDocShellFromWindow(aWindow); NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE); nsresult rv = docShell->SetAllowJavascript(mScriptsEnabled); NS_ENSURE_SUCCESS(rv, rv);
--- a/extensions/cookie/nsPermissionManager.cpp +++ b/extensions/cookie/nsPermissionManager.cpp @@ -1450,67 +1450,41 @@ nsPermissionManager::GetPermissionHashKe return GetPermissionHashKey(domain, aAppId, aIsInBrowserElement, aType, aExactHostMatch); } } // No entry, really... return nullptr; } -// helper struct for passing arguments into hash enumeration callback. -struct nsGetEnumeratorData -{ - nsGetEnumeratorData(nsCOMArray<nsIPermission> *aArray, - const nsTArray<nsCString> *aTypes, - int64_t aSince = 0) - : array(aArray) - , types(aTypes) - , since(aSince) {} - - nsCOMArray<nsIPermission> *array; - const nsTArray<nsCString> *types; - int64_t since; -}; - -static PLDHashOperator -AddPermissionsToList(nsPermissionManager::PermissionHashKey* entry, void *arg) -{ - nsGetEnumeratorData *data = static_cast<nsGetEnumeratorData *>(arg); - - for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { - nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; - - // given how "default" permissions work and the possibility of them being - // overridden with UNKNOWN_ACTION, we might see this value here - but we - // do *not* want to return them via the enumerator. - if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) { - continue; - } - - nsPermission *perm = new nsPermission(entry->GetKey()->mHost, - entry->GetKey()->mAppId, - entry->GetKey()->mIsInBrowserElement, - data->types->ElementAt(permEntry.mType), - permEntry.mPermission, - permEntry.mExpireType, - permEntry.mExpireTime); - - data->array->AppendObject(perm); - } - - return PL_DHASH_NEXT; -} - NS_IMETHODIMP nsPermissionManager::GetEnumerator(nsISimpleEnumerator **aEnum) { // roll an nsCOMArray of all our permissions, then hand out an enumerator nsCOMArray<nsIPermission> array; - nsGetEnumeratorData data(&array, &mTypeArray); + + for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) { + PermissionHashKey* entry = iter.Get(); + for (const auto& permEntry : entry->GetPermissions()) { + // Given how "default" permissions work and the possibility of them being + // overridden with UNKNOWN_ACTION, we might see this value here - but we + // do *not* want to return them via the enumerator. + if (permEntry.mPermission == nsIPermissionManager::UNKNOWN_ACTION) { + continue; + } - mPermissionTable.EnumerateEntries(AddPermissionsToList, &data); + array.AppendObject( + new nsPermission(entry->GetKey()->mHost, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, + mTypeArray.ElementAt(permEntry.mType), + permEntry.mPermission, + permEntry.mExpireType, + permEntry.mExpireTime)); + } + } return NS_NewArrayEnumerator(aEnum, array); } NS_IMETHODIMP nsPermissionManager::Observe(nsISupports *aSubject, const char *aTopic, const char16_t *someData) { ENSURE_NOT_CHILD_PROCESS; @@ -1524,52 +1498,39 @@ NS_IMETHODIMP nsPermissionManager::Obser else if (!nsCRT::strcmp(aTopic, "profile-do-change")) { // the profile has already changed; init the db from the new location InitDB(false); } return NS_OK; } -static PLDHashOperator -AddPermissionsModifiedSinceToList( - nsPermissionManager::PermissionHashKey* entry, void* arg) -{ - nsGetEnumeratorData* data = static_cast<nsGetEnumeratorData *>(arg); - - for (size_t i = 0; i < entry->GetPermissions().Length(); ++i) { - const nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; - - if (data->since > permEntry.mModificationTime) { - continue; - } - - nsPermission* perm = new nsPermission(entry->GetKey()->mHost, - entry->GetKey()->mAppId, - entry->GetKey()->mIsInBrowserElement, - data->types->ElementAt(permEntry.mType), - permEntry.mPermission, - permEntry.mExpireType, - permEntry.mExpireTime); - - data->array->AppendObject(perm); - } - return PL_DHASH_NEXT; -} - nsresult nsPermissionManager::RemoveAllModifiedSince(int64_t aModificationTime) { ENSURE_NOT_CHILD_PROCESS; - // roll an nsCOMArray of all our permissions, then hand out an enumerator nsCOMArray<nsIPermission> array; - nsGetEnumeratorData data(&array, &mTypeArray, aModificationTime); + for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) { + PermissionHashKey* entry = iter.Get(); + for (const auto& permEntry : entry->GetPermissions()) { + if (aModificationTime > permEntry.mModificationTime) { + continue; + } - mPermissionTable.EnumerateEntries(AddPermissionsModifiedSinceToList, &data); + array.AppendObject( + new nsPermission(entry->GetKey()->mHost, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, + mTypeArray.ElementAt(permEntry.mType), + permEntry.mPermission, + permEntry.mExpireType, + permEntry.mExpireTime)); + } + } for (int32_t i = 0; i<array.Count(); ++i) { nsAutoCString host; bool isInBrowserElement = false; nsAutoCString type; uint32_t appId = 0; array[i]->GetHost(host); @@ -1594,54 +1555,29 @@ nsPermissionManager::RemoveAllModifiedSi nsPermissionManager::eWriteToDB); } // now re-import any defaults as they may now be required if we just deleted // an override. ImportDefaults(); return NS_OK; } -PLDHashOperator -nsPermissionManager::GetPermissionsForApp(nsPermissionManager::PermissionHashKey* entry, void* arg) -{ - GetPermissionsForAppStruct* data = static_cast<GetPermissionsForAppStruct*>(arg); - - for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { - nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; - - if (entry->GetKey()->mAppId != data->appId || - (data->browserOnly && !entry->GetKey()->mIsInBrowserElement)) { - continue; - } - - data->permissions.AppendObject(new nsPermission(entry->GetKey()->mHost, - entry->GetKey()->mAppId, - entry->GetKey()->mIsInBrowserElement, - gPermissionManager->mTypeArray.ElementAt(permEntry.mType), - permEntry.mPermission, - permEntry.mExpireType, - permEntry.mExpireTime)); - } - - return PL_DHASH_NEXT; -} - NS_IMETHODIMP nsPermissionManager::RemovePermissionsForApp(uint32_t aAppId, bool aBrowserOnly) { ENSURE_NOT_CHILD_PROCESS; NS_ENSURE_ARG(aAppId != nsIScriptSecurityManager::NO_APP_ID); // We begin by removing all the permissions from the DB. // After clearing the DB, we call AddInternal() to make sure that all // processes are aware of this change and the representation of the DB in // memory is updated. - // We have to get all permissions associated with an application and then - // remove those because doing so in EnumerateEntries() would fail because - // we might happen to actually delete entries from the list. + // We have to get all permissions associated with an application first + // because removing entries from the permissions table while iterating over + // it is dangerous. nsAutoCString sql; sql.AppendLiteral("DELETE FROM moz_hosts WHERE appId="); sql.AppendInt(aAppId); if (aBrowserOnly) { sql.AppendLiteral(" AND isInBrowserElement=1"); } @@ -1649,27 +1585,44 @@ nsPermissionManager::RemovePermissionsFo nsCOMPtr<mozIStorageAsyncStatement> removeStmt; nsresult rv = mDBConn->CreateAsyncStatement(sql, getter_AddRefs(removeStmt)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<mozIStoragePendingStatement> pending; rv = removeStmt->ExecuteAsync(nullptr, getter_AddRefs(pending)); NS_ENSURE_SUCCESS(rv, rv); - GetPermissionsForAppStruct data(aAppId, aBrowserOnly); - mPermissionTable.EnumerateEntries(GetPermissionsForApp, &data); + nsCOMArray<nsIPermission> permissions; + for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) { + PermissionHashKey* entry = iter.Get(); + if (entry->GetKey()->mAppId != aAppId || + (aBrowserOnly && !entry->GetKey()->mIsInBrowserElement)) { + continue; + } - for (int32_t i=0; i<data.permissions.Count(); ++i) { + for (const auto& permEntry : entry->GetPermissions()) { + permissions.AppendObject( + new nsPermission(entry->GetKey()->mHost, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, + mTypeArray.ElementAt(permEntry.mType), + permEntry.mPermission, + permEntry.mExpireType, + permEntry.mExpireTime)); + } + } + + for (int32_t i = 0; i < permissions.Count(); ++i) { nsAutoCString host; bool isInBrowserElement; nsAutoCString type; - data.permissions[i]->GetHost(host); - data.permissions[i]->GetIsInBrowserElement(&isInBrowserElement); - data.permissions[i]->GetType(type); + permissions[i]->GetHost(host); + permissions[i]->GetIsInBrowserElement(&isInBrowserElement); + permissions[i]->GetType(type); nsCOMPtr<nsIPrincipal> principal; if (NS_FAILED(GetPrincipal(host, aAppId, isInBrowserElement, getter_AddRefs(principal)))) { NS_ERROR("GetPrincipal() failed!"); continue; } @@ -1682,73 +1635,69 @@ nsPermissionManager::RemovePermissionsFo 0, nsPermissionManager::eNotify, nsPermissionManager::eNoDBOperation); } return NS_OK; } -PLDHashOperator -nsPermissionManager::RemoveExpiredPermissionsForAppEnumerator( - nsPermissionManager::PermissionHashKey* entry, void* arg) -{ - uint32_t* appId = static_cast<uint32_t*>(arg); - - for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { - if (entry->GetKey()->mAppId != *appId) { - continue; - } - - nsPermissionManager::PermissionEntry& permEntry = entry->GetPermissions()[i]; - if (permEntry.mExpireType != nsIPermissionManager::EXPIRE_SESSION) { - continue; - } - - if (permEntry.mNonSessionExpireType == nsIPermissionManager::EXPIRE_SESSION) { - PermissionEntry oldPermissionEntry = entry->GetPermissions()[i]; - - entry->GetPermissions().RemoveElementAt(i); - - gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost, - entry->GetKey()->mAppId, - entry->GetKey()->mIsInBrowserElement, - gPermissionManager->mTypeArray.ElementAt(oldPermissionEntry.mType), - oldPermissionEntry.mPermission, - oldPermissionEntry.mExpireType, - oldPermissionEntry.mExpireTime, - MOZ_UTF16("deleted")); - --i; - continue; - } - - permEntry.mPermission = permEntry.mNonSessionPermission; - permEntry.mExpireType = permEntry.mNonSessionExpireType; - permEntry.mExpireTime = permEntry.mNonSessionExpireTime; - - gPermissionManager->NotifyObserversWithPermission(entry->GetKey()->mHost, - entry->GetKey()->mAppId, - entry->GetKey()->mIsInBrowserElement, - gPermissionManager->mTypeArray.ElementAt(permEntry.mType), - permEntry.mPermission, - permEntry.mExpireType, - permEntry.mExpireTime, - MOZ_UTF16("changed")); - } - - return PL_DHASH_NEXT; -} - nsresult nsPermissionManager::RemoveExpiredPermissionsForApp(uint32_t aAppId) { ENSURE_NOT_CHILD_PROCESS; - if (aAppId != nsIScriptSecurityManager::NO_APP_ID) { - mPermissionTable.EnumerateEntries(RemoveExpiredPermissionsForAppEnumerator, &aAppId); + if (aAppId == nsIScriptSecurityManager::NO_APP_ID) { + return NS_OK; + } + + for (auto iter = mPermissionTable.Iter(); !iter.Done(); iter.Next()) { + PermissionHashKey* entry = iter.Get(); + if (entry->GetKey()->mAppId != aAppId) { + continue; + } + + for (uint32_t i = 0; i < entry->GetPermissions().Length(); ++i) { + PermissionEntry& permEntry = entry->GetPermissions()[i]; + if (permEntry.mExpireType != nsIPermissionManager::EXPIRE_SESSION) { + continue; + } + + if (permEntry.mNonSessionExpireType == + nsIPermissionManager::EXPIRE_SESSION) { + PermissionEntry oldPermEntry = entry->GetPermissions()[i]; + + entry->GetPermissions().RemoveElementAt(i); + + NotifyObserversWithPermission(entry->GetKey()->mHost, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, + mTypeArray.ElementAt(oldPermEntry.mType), + oldPermEntry.mPermission, + oldPermEntry.mExpireType, + oldPermEntry.mExpireTime, + MOZ_UTF16("deleted")); + + --i; + continue; + } + + permEntry.mPermission = permEntry.mNonSessionPermission; + permEntry.mExpireType = permEntry.mNonSessionExpireType; + permEntry.mExpireTime = permEntry.mNonSessionExpireTime; + + NotifyObserversWithPermission(entry->GetKey()->mHost, + entry->GetKey()->mAppId, + entry->GetKey()->mIsInBrowserElement, + mTypeArray.ElementAt(permEntry.mType), + permEntry.mPermission, + permEntry.mExpireType, + permEntry.mExpireTime, + MOZ_UTF16("changed")); + } } return NS_OK; } //***************************************************************************** //*** nsPermissionManager private methods //*****************************************************************************
--- a/extensions/cookie/nsPermissionManager.h +++ b/extensions/cookie/nsPermissionManager.h @@ -268,51 +268,16 @@ private: int64_t aExpireTime, int64_t aModificationTime, uint32_t aAppId, bool aIsInBrowserElement); nsresult RemoveExpiredPermissionsForApp(uint32_t aAppId); /** - * This struct has to be passed as an argument to GetPermissionsForApp. - * |appId| and |browserOnly| have to be defined. - * |permissions| will be filed with permissions that are related to the app. - * If |browserOnly| is true, only permissions related to a browserElement will - * be in |permissions|. - */ - struct GetPermissionsForAppStruct { - uint32_t appId; - bool browserOnly; - nsCOMArray<nsIPermission> permissions; - - GetPermissionsForAppStruct() = delete; - GetPermissionsForAppStruct(uint32_t aAppId, bool aBrowserOnly) - : appId(aAppId) - , browserOnly(aBrowserOnly) - {} - }; - - /** - * This method will return the list of all permissions that are related to a - * specific app. - * @param arg has to be an instance of GetPermissionsForAppStruct. - */ - static PLDHashOperator - GetPermissionsForApp(PermissionHashKey* entry, void* arg); - - /** - * This method restores an app's permissions when its session ends. - */ - static PLDHashOperator - RemoveExpiredPermissionsForAppEnumerator(PermissionHashKey* entry, - void* nonused); - - - /** * This method removes all permissions modified after the specified time. */ nsresult RemoveAllModifiedSince(int64_t aModificationTime); /** * Retrieve permissions from chrome process. */
--- a/extensions/spellcheck/src/mozPersonalDictionary.cpp +++ b/extensions/spellcheck/src/mozPersonalDictionary.cpp @@ -237,26 +237,16 @@ void mozPersonalDictionary::SyncLoadInte if( (NS_OK != convStream->Read(&c, 1, &nRead)) || (nRead != 1)) done = true; } mDictionaryTable.PutEntry(word.get()); } } while(!done); mDirty = false; } -// A little helper function to add the key to the list. -// This is not threadsafe, and only safe if the consumer does not -// modify the list. -static PLDHashOperator -AddHostToStringArray(nsUnicharPtrHashKey *aEntry, void *aArg) -{ - static_cast<nsTArray<nsString>*>(aArg)->AppendElement(nsDependentString(aEntry->GetKey())); - return PL_DHASH_NEXT; -} - /* void Save (); */ NS_IMETHODIMP mozPersonalDictionary::Save() { nsCOMPtr<nsIFile> theFile; nsresult res; WaitForLoad(); if(!mDirty) return NS_OK; @@ -272,17 +262,19 @@ NS_IMETHODIMP mozPersonalDictionary::Sav NS_NewSafeLocalFileOutputStream(getter_AddRefs(outStream), theFile, PR_CREATE_FILE | PR_WRONLY | PR_TRUNCATE ,0664); // get a buffered output stream 4096 bytes big, to optimize writes nsCOMPtr<nsIOutputStream> bufferedOutputStream; res = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream), outStream, 4096); if (NS_FAILED(res)) return res; nsTArray<nsString> array(mDictionaryTable.Count()); - mDictionaryTable.EnumerateEntries(AddHostToStringArray, &array); + for (auto iter = mDictionaryTable.Iter(); !iter.Done(); iter.Next()) { + array.AppendElement(nsDependentString(iter.Get()->GetKey())); + } uint32_t bytesWritten; nsAutoCString utf8Key; for (uint32_t i = 0; i < array.Length(); ++i ) { CopyUTF16toUTF8(array[i], utf8Key); bufferedOutputStream->Write(utf8Key.get(), utf8Key.Length(), &bytesWritten); bufferedOutputStream->Write("\n", 1, &bytesWritten); @@ -302,20 +294,19 @@ NS_IMETHODIMP mozPersonalDictionary::Sav NS_IMETHODIMP mozPersonalDictionary::GetWordList(nsIStringEnumerator **aWords) { NS_ENSURE_ARG_POINTER(aWords); *aWords = nullptr; WaitForLoad(); nsTArray<nsString> *array = new nsTArray<nsString>(mDictionaryTable.Count()); - if (!array) - return NS_ERROR_OUT_OF_MEMORY; - - mDictionaryTable.EnumerateEntries(AddHostToStringArray, array); + for (auto iter = mDictionaryTable.Iter(); !iter.Done(); iter.Next()) { + array->AppendElement(nsDependentString(iter.Get()->GetKey())); + } array->Sort(); return NS_NewAdoptingStringEnumerator(aWords, array); } /* boolean Check (in wstring word, in wstring language); */ NS_IMETHODIMP mozPersonalDictionary::Check(const char16_t *aWord, const char16_t *aLanguage, bool *aResult)
--- a/gfx/layers/client/TiledContentClient.cpp +++ b/gfx/layers/client/TiledContentClient.cpp @@ -861,17 +861,17 @@ ClientTiledLayerBuffer::GetSurfaceDescri tileDesc = PlaceholderTileDescriptor(); } else { tileDesc = tile.GetTileDescriptor(); } tiles.AppendElement(tileDesc); // Reset the update rect tile.mUpdateRect = IntRect(); } - return SurfaceDescriptorTiles(mValidRegion, mPaintedRegion, + return SurfaceDescriptorTiles(mValidRegion, tiles, mTiles.mFirst.x, mTiles.mFirst.y, mTiles.mSize.width, mTiles.mSize.height, mResolution, mFrameResolution.xScale, mFrameResolution.yScale); } void
--- a/gfx/layers/composite/TiledContentHost.cpp +++ b/gfx/layers/composite/TiledContentHost.cpp @@ -104,38 +104,23 @@ TiledContentHost::UseTiledLayerBuffer(IS } return true; } void UseTileTexture(CompositableTextureHostRef& aTexture, CompositableTextureSourceRef& aTextureSource, const IntRect& aUpdateRect, - TextureHost* aNewTexture, Compositor* aCompositor) { - MOZ_ASSERT(aNewTexture); - if (!aNewTexture) { + MOZ_ASSERT(aTexture); + if (!aTexture) { return; } - if (aTexture) { - aTexture->SetCompositor(aCompositor); - aNewTexture->SetCompositor(aCompositor); - - if (aTexture->GetFormat() != aNewTexture->GetFormat()) { - // Only reuse textures if their format match the new texture's. - aTextureSource = nullptr; - aTexture = nullptr; - } - } - - aTexture = aNewTexture; - - if (aCompositor) { aTexture->SetCompositor(aCompositor); } if (!aUpdateRect.IsEmpty()) { #ifdef MOZ_GFX_OPTIMIZE_MOBILE aTexture->Updated(nullptr); #else @@ -170,16 +155,93 @@ GetCopyOnWriteLock(const TileLock& ipcLo // The corresponding AddRef is in TiledClient::GetTileDescriptor sharedLock.get()->Release(); } } aTile.mSharedLock = sharedLock; return true; } +void +TiledLayerBufferComposite::MarkTilesForUnlock() +{ + // Tiles without an internal buffer will have internal locks + // held by the gpu driver until the previous draw operation has finished. + // We don't know when that will be exactly, so wait until we start the + // next composite before unlocking. + for (TileHost& tile : mRetainedTiles) { + // Tile with an internal buffer get unlocked as soon as we've uploaded, + // so won't have a lock at this point. + if (tile.mTextureHost && tile.mSharedLock) { + mDelayedUnlocks.AppendElement(tile.mSharedLock); + tile.mSharedLock = nullptr; + } + } +} + +class TextureSourceRecycler +{ +public: + explicit TextureSourceRecycler(nsTArray<TileHost>&& aTileSet) + : mTiles(Move(aTileSet)) + , mFirstPossibility(0) + {} + + // Attempts to recycle a texture source that is already bound to the + // texture host for aTile. + void RecycleTextureSourceForTile(TileHost& aTile) { + for (size_t i = mFirstPossibility; i < mTiles.Length(); i++) { + // Skip over existing tiles without a retained texture source + // and make sure we don't iterate them in the future. + if (!mTiles[i].mTextureSource) { + if (i == mFirstPossibility) { + mFirstPossibility++; + } + continue; + } + + // If this tile matches, then copy across the retained texture source (if + // any). + if (aTile.mTextureHost == mTiles[i].mTextureHost) { + aTile.mTextureSource = Move(mTiles[i].mTextureSource); + if (aTile.mTextureHostOnWhite) { + aTile.mTextureSourceOnWhite = Move(mTiles[i].mTextureSourceOnWhite); + } + break; + } + } + } + + // Attempts to recycle any texture source to avoid needing to allocate + // a new one. + void RecycleTextureSource(TileHost& aTile) { + for (size_t i = mFirstPossibility; i < mTiles.Length(); i++) { + if (!mTiles[i].mTextureSource) { + if (i == mFirstPossibility) { + mFirstPossibility++; + } + continue; + } + + if (mTiles[i].mTextureSource && + mTiles[i].mTextureHost->GetFormat() == aTile.mTextureHost->GetFormat()) { + aTile.mTextureSource = Move(mTiles[i].mTextureSource); + if (aTile.mTextureHostOnWhite) { + aTile.mTextureSourceOnWhite = Move(mTiles[i].mTextureSourceOnWhite); + } + break; + } + } + } + +protected: + nsTArray<TileHost> mTiles; + size_t mFirstPossibility; +}; + bool TiledLayerBufferComposite::UseTiles(const SurfaceDescriptorTiles& aTiles, Compositor* aCompositor, ISurfaceAllocator* aAllocator) { if (mResolution != aTiles.resolution()) { Clear(); } @@ -190,179 +252,138 @@ TiledLayerBufferComposite::UseTiles(cons } if (aTiles.resolution() == 0 || IsNaN(aTiles.resolution())) { // There are divisions by mResolution so this protects the compositor process // against malicious content processes and fuzzing. return false; } - TilesPlacement oldTiles = mTiles; TilesPlacement newTiles(aTiles.firstTileX(), aTiles.firstTileY(), aTiles.retainedWidth(), aTiles.retainedHeight()); const InfallibleTArray<TileDescriptor>& tileDescriptors = aTiles.tiles(); - nsTArray<TileHost> oldRetainedTiles; - mRetainedTiles.SwapElements(oldRetainedTiles); + // Step 1, unlock all the old tiles that haven't been unlocked yet. Any tiles that + // exist in both the old and new sets will have been locked again by content, so this + // doesn't result in the surface being writeable again. + MarkTilesForUnlock(); + + TextureSourceRecycler oldRetainedTiles(Move(mRetainedTiles)); mRetainedTiles.SetLength(tileDescriptors.Length()); - // Step 1, we need to unlock tiles that don't have an internal buffer after the - // next frame where they are replaced. - // Since we are about to replace the tiles' textures, we need to keep their locks - // somewhere (in mPreviousSharedLock) until we composite the layer. - for (size_t i = 0; i < oldRetainedTiles.Length(); ++i) { - TileHost& tile = oldRetainedTiles[i]; - // It can happen that we still have a previous lock at this point, - // if we changed a tile's front buffer (causing mSharedLock to - // go into mPreviousSharedLock, and then did not composite that tile until - // the next transaction, either because the tile is offscreen or because the - // two transactions happened with no composition in between (over-production). - tile.ReadUnlockPrevious(); - - if (tile.mTextureHost && !tile.mTextureHost->HasInternalBuffer()) { - MOZ_ASSERT(tile.mSharedLock); - const TileIntPoint tilePosition = oldTiles.TilePosition(i); - if (newTiles.HasTile(tilePosition)) { - // This tile still exist in the new buffer - tile.mPreviousSharedLock = tile.mSharedLock; - tile.mSharedLock = nullptr; - } else { - // This tile does not exist anymore in the new buffer because the size - // changed. - tile.ReadUnlock(); - } - } - - // By now we should not have anything in mSharedLock. - MOZ_ASSERT(!tile.mSharedLock); - } - - // Step 2, move the tiles in mRetainedTiles at places that correspond to where - // they should be with the new retained with and height rather than the - // old one. + // Step 2, deserialize the incoming set of tiles into mRetainedTiles, and attempt + // to recycle the TextureSource for any repeated tiles. + // + // Since we don't have any retained 'tile' object, we have to search for instances + // of the same TextureHost in the old tile set. The cost of binding a TextureHost + // to a TextureSource for gralloc (binding EGLImage to GL texture) can be really + // high, so we avoid this whenever possible. for (size_t i = 0; i < tileDescriptors.Length(); i++) { - const TileIntPoint tilePosition = newTiles.TilePosition(i); - // First, get the already existing tiles to the right place in the array, - // and use placeholders where there was no tiles. - if (!oldTiles.HasTile(tilePosition)) { - mRetainedTiles[i] = GetPlaceholderTile(); - } else { - mRetainedTiles[i] = oldRetainedTiles[oldTiles.TileIndex(tilePosition)]; - // If we hit this assertion it means we probably mixed something up in the - // logic that tries to reuse tiles on the compositor side. It is most likely - // benign, but we are missing some fast paths so let's try to make it not happen. - MOZ_ASSERT(tilePosition.x == mRetainedTiles[i].x && - tilePosition.y == mRetainedTiles[i].y); - } - } - - // It is important to remove the duplicated reference to tiles before calling - // TextureHost::PrepareTextureSource, etc. because depending on the textures - // ref counts we may or may not get some of the fast paths. - oldRetainedTiles.Clear(); - - // Step 3, handle the texture updates and release the copy-on-write locks. - for (size_t i = 0; i < mRetainedTiles.Length(); i++) { const TileDescriptor& tileDesc = tileDescriptors[i]; TileHost& tile = mRetainedTiles[i]; - switch (tileDesc.type()) { - case TileDescriptor::TTexturedTileDescriptor: { - const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor(); + if (tileDesc.type() != TileDescriptor::TTexturedTileDescriptor) { + NS_WARN_IF_FALSE(tileDesc.type() == TileDescriptor::TPlaceholderTileDescriptor, + "Unrecognised tile descriptor type"); + continue; + } - const TileLock& ipcLock = texturedDesc.sharedLock(); - if (!GetCopyOnWriteLock(ipcLock, tile, aAllocator)) { - return false; - } + const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor(); - RefPtr<TextureHost> textureHost = TextureHost::AsTextureHost( - texturedDesc.textureParent() - ); + const TileLock& ipcLock = texturedDesc.sharedLock(); + if (!GetCopyOnWriteLock(ipcLock, tile, aAllocator)) { + return false; + } - RefPtr<TextureHost> textureOnWhite = nullptr; - if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) { - textureOnWhite = TextureHost::AsTextureHost( - texturedDesc.textureOnWhite().get_PTextureParent() - ); - } + tile.mTextureHost = TextureHost::AsTextureHost(texturedDesc.textureParent()); + + if (texturedDesc.textureOnWhite().type() == MaybeTexture::TPTextureParent) { + tile.mTextureHostOnWhite = + TextureHost::AsTextureHost(texturedDesc.textureOnWhite().get_PTextureParent()); + } - UseTileTexture(tile.mTextureHost, - tile.mTextureSource, - texturedDesc.updateRect(), - textureHost, - aCompositor); + tile.mTilePosition = newTiles.TilePosition(i); + + // If this same tile texture existed in the old tile set then this will move the texture + // source into our new tile. + oldRetainedTiles.RecycleTextureSourceForTile(tile); + } - if (textureOnWhite) { - UseTileTexture(tile.mTextureHostOnWhite, - tile.mTextureSourceOnWhite, - texturedDesc.updateRect(), - textureOnWhite, - aCompositor); - } else { - // We could still have component alpha textures from a previous frame. - tile.mTextureSourceOnWhite = nullptr; - tile.mTextureHostOnWhite = nullptr; - } + // Step 3, attempt to recycle unused texture sources from the old tile set into new tiles. + // + // For gralloc, binding a new TextureHost to the existing TextureSource is the fastest way + // to ensure that any implicit locking on the old gralloc image is released. + for (TileHost& tile : mRetainedTiles) { + if (!tile.mTextureHost || tile.mTextureSource) { + continue; + } + oldRetainedTiles.RecycleTextureSource(tile); + } - if (textureHost->HasInternalBuffer()) { - // Now that we did the texture upload (in UseTileTexture), we can release - // the lock. - tile.ReadUnlock(); - } + // Step 4, handle the texture uploads, texture source binding and release the + // copy-on-write locks for textures with an internal buffer. + for (size_t i = 0; i < mRetainedTiles.Length(); i++) { + TileHost& tile = mRetainedTiles[i]; + if (!tile.mTextureHost) { + continue; + } - break; - } - default: - NS_WARNING("Unrecognised tile descriptor type"); - case TileDescriptor::TPlaceholderTileDescriptor: { + const TileDescriptor& tileDesc = tileDescriptors[i]; + const TexturedTileDescriptor& texturedDesc = tileDesc.get_TexturedTileDescriptor(); + + UseTileTexture(tile.mTextureHost, + tile.mTextureSource, + texturedDesc.updateRect(), + aCompositor); - if (tile.mTextureHost) { - tile.mTextureHost->UnbindTextureSource(); - tile.mTextureSource = nullptr; - } - if (tile.mTextureHostOnWhite) { - tile.mTextureHostOnWhite->UnbindTextureSource(); - tile.mTextureSourceOnWhite = nullptr; - } - // we may have a previous lock, and are about to loose our reference to it. - // It is okay to unlock it because we just destroyed the texture source. - tile.ReadUnlockPrevious(); - tile = GetPlaceholderTile(); + if (tile.mTextureHostOnWhite) { + UseTileTexture(tile.mTextureHostOnWhite, + tile.mTextureSourceOnWhite, + texturedDesc.updateRect(), + aCompositor); + } - break; - } + if (tile.mTextureHost->HasInternalBuffer()) { + // Now that we did the texture upload (in UseTileTexture), we can release + // the lock. + tile.ReadUnlock(); } - TileIntPoint tilePosition = newTiles.TilePosition(i); - tile.x = tilePosition.x; - tile.y = tilePosition.y; } mTiles = newTiles; mValidRegion = aTiles.validRegion(); mResolution = aTiles.resolution(); mFrameResolution = CSSToParentLayerScale2D(aTiles.frameXResolution(), aTiles.frameYResolution()); return true; } void +TiledLayerBufferComposite::ProcessDelayedUnlocks() +{ + for (gfxSharedReadLock* lock : mDelayedUnlocks) { + lock->ReadUnlock(); + } + mDelayedUnlocks.Clear(); +} + +void TiledLayerBufferComposite::Clear() { for (TileHost& tile : mRetainedTiles) { tile.ReadUnlock(); - tile.ReadUnlockPrevious(); } mRetainedTiles.Clear(); + ProcessDelayedUnlocks(); mTiles.mFirst = TileIntPoint(); mTiles.mSize = TileIntSize(); mValidRegion = nsIntRegion(); - mPaintedRegion = nsIntRegion(); mResolution = 1.0; } void TiledContentHost::Composite(LayerComposite* aLayer, EffectChain& aEffectChain, float aOpacity, const gfx::Matrix4x4& aTransform, @@ -411,16 +432,18 @@ TiledContentHost::Composite(LayerComposi // Render the low and high precision buffers. RenderLayerBuffer(mLowPrecisionTiledBuffer, lowPrecisionOpacityReduction < 1.0f ? &backgroundColor : nullptr, aEffectChain, lowPrecisionOpacityReduction * aOpacity, aFilter, aClipRect, *renderRegion, aTransform); RenderLayerBuffer(mTiledBuffer, nullptr, aEffectChain, aOpacity, aFilter, aClipRect, *renderRegion, aTransform); + mLowPrecisionTiledBuffer.ProcessDelayedUnlocks(); + mTiledBuffer.ProcessDelayedUnlocks(); } void TiledContentHost::RenderTile(TileHost& aTile, EffectChain& aEffectChain, float aOpacity, const gfx::Matrix4x4& aTransform, @@ -474,17 +497,16 @@ TiledContentHost::RenderTile(TileHost& a mCompositor->DrawQuad(graphicsRect, aClipRect, aEffectChain, aOpacity, aTransform, aVisibleRect); } DiagnosticFlags flags = DiagnosticFlags::CONTENT | DiagnosticFlags::TILE; if (aTile.mTextureHostOnWhite) { flags |= DiagnosticFlags::COMPONENT_ALPHA; } mCompositor->DrawDiagnostics(flags, aScreenRegion, aClipRect, aTransform, mFlashCounter); - aTile.ReadUnlockPrevious(); } void TiledContentHost::RenderLayerBuffer(TiledLayerBufferComposite& aLayerBuffer, const gfxRGBA* aBackgroundColor, EffectChain& aEffectChain, float aOpacity, const gfx::Filter& aFilter, @@ -553,17 +575,17 @@ TiledContentHost::RenderLayerBuffer(Tile for (size_t i = 0; i < aLayerBuffer.GetTileCount(); ++i) { TileHost& tile = aLayerBuffer.GetTile(i); if (tile.IsPlaceholderTile()) { continue; } TileIntPoint tilePosition = aLayerBuffer.GetPlacement().TilePosition(i); // A sanity check that catches a lot of mistakes. - MOZ_ASSERT(tilePosition.x == tile.x && tilePosition.y == tile.y); + MOZ_ASSERT(tilePosition.x == tile.mTilePosition.x && tilePosition.y == tile.mTilePosition.y); IntPoint tileOffset = aLayerBuffer.GetTileOffset(tilePosition); nsIntRegion tileDrawRegion = IntRect(tileOffset, aLayerBuffer.GetScaledTileSize()); tileDrawRegion.AndWith(compositeRegion); if (tileDrawRegion.IsEmpty()) { continue; }
--- a/gfx/layers/composite/TiledContentHost.h +++ b/gfx/layers/composite/TiledContentHost.h @@ -47,57 +47,49 @@ struct EffectChain; class TileHost { public: // Constructs a placeholder TileHost. See the comments above // TiledLayerBuffer for more information on what this is used for; // essentially, this is a sentinel used to represent an invalid or blank // tile. TileHost() - : x(-1) - , y(-1) {} // Constructs a TileHost from a gfxSharedReadLock and TextureHost. TileHost(gfxSharedReadLock* aSharedLock, TextureHost* aTextureHost, TextureHost* aTextureHostOnWhite, TextureSource* aSource, TextureSource* aSourceOnWhite) : mSharedLock(aSharedLock) , mTextureHost(aTextureHost) , mTextureHostOnWhite(aTextureHostOnWhite) , mTextureSource(aSource) , mTextureSourceOnWhite(aSourceOnWhite) - , x(-1) - , y(-1) {} TileHost(const TileHost& o) { mTextureHost = o.mTextureHost; mTextureHostOnWhite = o.mTextureHostOnWhite; mTextureSource = o.mTextureSource; mTextureSourceOnWhite = o.mTextureSourceOnWhite; mSharedLock = o.mSharedLock; - mPreviousSharedLock = o.mPreviousSharedLock; - x = o.x; - y = o.y; + mTilePosition = o.mTilePosition; } TileHost& operator=(const TileHost& o) { if (this == &o) { return *this; } mTextureHost = o.mTextureHost; mTextureHostOnWhite = o.mTextureHostOnWhite; mTextureSource = o.mTextureSource; mTextureSourceOnWhite = o.mTextureSourceOnWhite; mSharedLock = o.mSharedLock; - mPreviousSharedLock = o.mPreviousSharedLock; - x = o.x; - y = o.y; + mTilePosition = o.mTilePosition; return *this; } bool operator== (const TileHost& o) const { return mTextureHost == o.mTextureHost; } bool operator!= (const TileHost& o) const { return mTextureHost != o.mTextureHost; @@ -107,41 +99,32 @@ public: void ReadUnlock() { if (mSharedLock) { mSharedLock->ReadUnlock(); mSharedLock = nullptr; } } - void ReadUnlockPrevious() { - if (mPreviousSharedLock) { - mPreviousSharedLock->ReadUnlock(); - mPreviousSharedLock = nullptr; - } - } - void Dump(std::stringstream& aStream) { aStream << "TileHost(...)"; // fill in as needed } void DumpTexture(std::stringstream& aStream) { // TODO We should combine the OnWhite/OnBlack here an just output a single image. CompositableHost::DumpTextureHost(aStream, mTextureHost); } RefPtr<gfxSharedReadLock> mSharedLock; - RefPtr<gfxSharedReadLock> mPreviousSharedLock; CompositableTextureHostRef mTextureHost; CompositableTextureHostRef mTextureHostOnWhite; mutable CompositableTextureSourceRef mTextureSource; mutable CompositableTextureSourceRef mTextureSourceOnWhite; // This is not strictly necessary but makes debugging whole lot easier. - int x; - int y; + TileIntPoint mTilePosition; }; class TiledLayerBufferComposite : public TiledLayerBuffer<TiledLayerBufferComposite, TileHost> { friend class TiledLayerBuffer<TiledLayerBufferComposite, TileHost>; public: @@ -149,30 +132,35 @@ public: ~TiledLayerBufferComposite(); bool UseTiles(const SurfaceDescriptorTiles& aTileDescriptors, Compositor* aCompositor, ISurfaceAllocator* aAllocator); void Clear(); + void MarkTilesForUnlock(); + void ProcessDelayedUnlocks(); + TileHost GetPlaceholderTile() const { return TileHost(); } // Stores the absolute resolution of the containing frame, calculated // by the sum of the resolutions of all parent layers' FrameMetrics. const CSSToParentLayerScale2D& GetFrameResolution() { return mFrameResolution; } void SetCompositor(Compositor* aCompositor); // Recycle callback for TextureHost. // Used when TiledContentClient is present in client side. static void RecycleCallback(TextureHost* textureHost, void* aClosure); protected: + CSSToParentLayerScale2D mFrameResolution; + nsTArray<RefPtr<gfxSharedReadLock>> mDelayedUnlocks; }; /** * ContentHost for tiled PaintedLayers. Since tiled layers are special snow * flakes, we have a unique update process. All the textures that back the * tiles are added in the usual way, but Updated is called on the host side * in response to a message that describes the transaction for every tile. * Composition happens in the normal way.
--- a/gfx/layers/ipc/LayersMessages.ipdlh +++ b/gfx/layers/ipc/LayersMessages.ipdlh @@ -327,17 +327,16 @@ struct PlaceholderTileDescriptor { union TileDescriptor { TexturedTileDescriptor; PlaceholderTileDescriptor; }; struct SurfaceDescriptorTiles { nsIntRegion validRegion; - nsIntRegion paintedRegion; TileDescriptor[] tiles; int firstTileX; int firstTileY; int retainedWidth; int retainedHeight; float resolution; float frameXResolution; float frameYResolution;
--- a/gfx/thebes/gfxPrefs.h +++ b/gfx/thebes/gfxPrefs.h @@ -225,16 +225,17 @@ private: DECL_GFX_PREF(Once, "gfx.direct2d.disabled", Direct2DDisabled, bool, false); DECL_GFX_PREF(Once, "gfx.direct2d.force-enabled", Direct2DForceEnabled, bool, false); DECL_GFX_PREF(Live, "gfx.direct2d.use1_1", Direct2DUse1_1, bool, false); DECL_GFX_PREF(Live, "gfx.draw-color-bars", CompositorDrawColorBars, bool, false); // This should be set to values in the DriverInitStatus enumeration found in // DriverInitCrashDetection.h. DECL_GFX_PREF(Live, "gfx.driver-init.status", DriverInitStatus, int32_t, 0); + DECL_GFX_PREF(Once, "gfx.font_rendering.directwrite.enabled", DirectWriteFontRenderingEnabled, bool, false); DECL_GFX_PREF(Live, "gfx.gralloc.fence-with-readpixels", GrallocFenceWithReadPixels, bool, false); DECL_GFX_PREF(Live, "gfx.layerscope.enabled", LayerScopeEnabled, bool, false); DECL_GFX_PREF(Live, "gfx.layerscope.port", LayerScopePort, int32_t, 23456); // Note that "gfx.logging.level" is defined in Logging.h DECL_GFX_PREF(Once, "gfx.logging.crash.length", GfxLoggingCrashLength, uint32_t, 6); DECL_GFX_PREF(Live, "gfx.perf-warnings.enabled", PerfWarnings, bool, false); DECL_GFX_PREF(Once, "gfx.touch.resample", TouchResampling, bool, false);
old mode 100644 new mode 100755 --- a/gfx/thebes/gfxWindowsPlatform.cpp +++ b/gfx/thebes/gfxWindowsPlatform.cpp @@ -432,16 +432,105 @@ gfxWindowsPlatform::CanUseHardwareVideoD { if (!gfxPrefs::LayersPreferD3D9() && !mDoesD3D11TextureSharingWork) { return false; } return !IsWARP() && gfxPlatform::CanUseHardwareVideoDecoding(); } void +gfxWindowsPlatform::InitD2DSupport() +{ +#ifdef CAIRO_HAS_D2D_SURFACE + bool d2dBlocked = false; + nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo(); + if (gfxInfo) { + int32_t status; + if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, &status))) { + if (status != nsIGfxInfo::FEATURE_STATUS_OK) { + d2dBlocked = true; + } + } + if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) { + if (status != nsIGfxInfo::FEATURE_STATUS_OK) { + d2dBlocked = true; + } + } + } + + // If D2D is blocked or D3D9 is prefered, and D2D is not force-enabled, then + // we don't attempt to use D2D. + if ((d2dBlocked || gfxPrefs::LayersPreferD3D9()) && !gfxPrefs::Direct2DForceEnabled()) { + return; + } + + // Do not ever try to use D2D if it's explicitly disabled or if we're not + // using DWrite fonts. + if (gfxPrefs::Direct2DDisabled() || mUsingGDIFonts) { + return; + } + + ID3D11Device* device = GetD3D11Device(); + if (IsVistaOrLater() && + !InSafeMode() && + device && + mDoesD3D11TextureSharingWork) + { + VerifyD2DDevice(gfxPrefs::Direct2DForceEnabled()); + if (mD3D10Device && GetD3D11Device()) { + mRenderMode = RENDER_DIRECT2D; + mUseDirectWrite = true; + } + } else { + mD3D10Device = nullptr; + } +#endif +} + +void +gfxWindowsPlatform::InitDWriteSupport() +{ +#ifdef CAIRO_HAS_DWRITE_FONT + // Enable when it's preffed on -and- we're using Vista or higher. Or when + // we're going to use D2D. + if (mDWriteFactory || (!mUseDirectWrite || !IsVistaOrLater())) { + return; + } + + mozilla::ScopedGfxFeatureReporter reporter("DWrite"); + decltype(DWriteCreateFactory)* createDWriteFactory = (decltype(DWriteCreateFactory)*) + GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory"); + + if (!createDWriteFactory) { + return; + } + + // I need a direct pointer to be able to cast to IUnknown**, I also need to + // remember to release this because the nsRefPtr will AddRef it. + IDWriteFactory *factory; + HRESULT hr = createDWriteFactory( + DWRITE_FACTORY_TYPE_SHARED, + __uuidof(IDWriteFactory), + reinterpret_cast<IUnknown**>(&factory)); + + if (SUCCEEDED(hr) && factory) { + mDWriteFactory = factory; + factory->Release(); + hr = mDWriteFactory->CreateTextAnalyzer(getter_AddRefs(mDWriteAnalyzer)); + } + + SetupClearTypeParams(); + + if (hr == S_OK) { + reporter.SetSuccessful(); + } +#endif +} + +void gfxWindowsPlatform::UpdateRenderMode() { /* Pick the default render mode for * desktop. */ bool didReset = false; DeviceResetReason resetReason = DeviceResetReason::OK; if (DidRenderingDeviceReset(&resetReason)) { @@ -456,102 +545,20 @@ gfxWindowsPlatform::UpdateRenderMode() imgLoader::Singleton()->ClearCache(true); imgLoader::Singleton()->ClearCache(false); Factory::SetDirect3D11Device(nullptr); didReset = true; } mRenderMode = RENDER_GDI; - - bool isVistaOrHigher = IsVistaOrLater(); - - mUseDirectWrite = Preferences::GetBool("gfx.font_rendering.directwrite.enabled", false); - -#ifdef CAIRO_HAS_D2D_SURFACE - bool d2dDisabled = false; - bool d2dForceEnabled = false; - bool d2dBlocked = false; - - nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo(); - if (gfxInfo) { - int32_t status; - if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT2D, &status))) { - if (status != nsIGfxInfo::FEATURE_STATUS_OK) { - d2dBlocked = true; - } - } - if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) { - if (status != nsIGfxInfo::FEATURE_STATUS_OK) { - d2dBlocked = true; - } - } - } - - // These will only be evaluated once, and any subsequent changes to - // the preferences will be ignored until restart. - d2dDisabled = gfxPrefs::Direct2DDisabled(); - d2dForceEnabled = gfxPrefs::Direct2DForceEnabled(); - - bool tryD2D = d2dForceEnabled || (!d2dBlocked && !gfxPrefs::LayersPreferD3D9()); - - // Do not ever try if d2d is explicitly disabled, - // or if we're not using DWrite fonts. - if (d2dDisabled || mUsingGDIFonts) { - tryD2D = false; - } - - ID3D11Device *device = GetD3D11Device(); - if (isVistaOrHigher && !InSafeMode() && tryD2D && device && - mDoesD3D11TextureSharingWork) { + mUseDirectWrite = gfxPrefs::DirectWriteFontRenderingEnabled(); - VerifyD2DDevice(d2dForceEnabled); - if (mD3D10Device && GetD3D11Device()) { - mRenderMode = RENDER_DIRECT2D; - mUseDirectWrite = true; - } - } else { - mD3D10Device = nullptr; - } -#endif - -#ifdef CAIRO_HAS_DWRITE_FONT - // Enable when it's preffed on -and- we're using Vista or higher. Or when - // we're going to use D2D. - if (!mDWriteFactory && (mUseDirectWrite && isVistaOrHigher)) { - mozilla::ScopedGfxFeatureReporter reporter("DWrite"); - decltype(DWriteCreateFactory)* createDWriteFactory = (decltype(DWriteCreateFactory)*) - GetProcAddress(LoadLibraryW(L"dwrite.dll"), "DWriteCreateFactory"); - - if (createDWriteFactory) { - /** - * I need a direct pointer to be able to cast to IUnknown**, I also - * need to remember to release this because the nsRefPtr will - * AddRef it. - */ - IDWriteFactory *factory; - HRESULT hr = createDWriteFactory( - DWRITE_FACTORY_TYPE_SHARED, - __uuidof(IDWriteFactory), - reinterpret_cast<IUnknown**>(&factory)); - - if (SUCCEEDED(hr) && factory) { - mDWriteFactory = factory; - factory->Release(); - hr = mDWriteFactory->CreateTextAnalyzer( - getter_AddRefs(mDWriteAnalyzer)); - } - - SetupClearTypeParams(); - - if (hr == S_OK) - reporter.SetSuccessful(); - } - } -#endif + InitD2DSupport(); + InitDWriteSupport(); uint32_t canvasMask = BackendTypeBit(BackendType::CAIRO); uint32_t contentMask = BackendTypeBit(BackendType::CAIRO); BackendType defaultBackend = BackendType::CAIRO; if (mRenderMode == RENDER_DIRECT2D) { canvasMask |= BackendTypeBit(BackendType::DIRECT2D); contentMask |= BackendTypeBit(BackendType::DIRECT2D); if (gfxPrefs::Direct2DUse1_1() && Factory::SupportsD2D1() && @@ -1885,222 +1892,254 @@ bool DoesD3D11TextureSharingWork(ID3D11D return result; } bool DoesD3D11AlphaTextureSharingWork(ID3D11Device *device) { return DoesD3D11TextureSharingWorkInternal(device, DXGI_FORMAT_R8_UNORM, D3D11_BIND_SHADER_RESOURCE); } +auto +gfxWindowsPlatform::CheckD3D11Support() -> D3D11Status +{ + if (gfxPrefs::LayersD3D11ForceWARP()) { + return D3D11Status::ForceWARP; + } + + if (nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo()) { + int32_t status; + if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) { + if (status != nsIGfxInfo::FEATURE_STATUS_OK) { + // See if we can use WARP instead. + // + // It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703 for more. + if (gfxPrefs::LayersD3D11DisableWARP() || GetModuleHandleA("nvdxgiwrap.dll")) { + return D3D11Status::Blocked; + } + + if (!IsWin8OrLater()) { + // We do not use WARP on Windows 7 or earlier. + return D3D11Status::Blocked; + } + + return D3D11Status::TryWARP; + } + } + } + + // Either nsIGfxInfo was bugged or we're not blacklisted. + if (!GetDXGIAdapter()) { + return D3D11Status::TryWARP; + } + return D3D11Status::Ok; +} + +// We don't have access to the D3D11CreateDevice type in gfxWindowsPlatform.h, +// since it doesn't include d3d11.h, so we use a static here. It should only +// be used within InitD3D11Devices. +decltype(D3D11CreateDevice)* sD3D11CreateDeviceFn = nullptr; + +bool +gfxWindowsPlatform::AttemptD3D11DeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels) +{ + RefPtr<IDXGIAdapter1> adapter = GetDXGIAdapter(); + MOZ_ASSERT(adapter); + + HRESULT hr = E_INVALIDARG; + MOZ_SEH_TRY { + hr = + sD3D11CreateDeviceFn(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, + // Use + // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS + // to prevent bug 1092260. IE 11 also uses this flag. + D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, + aFeatureLevels.Elements(), aFeatureLevels.Length(), + D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr); + } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { + gfxCriticalError() << "Crash during D3D11 device creation"; + return false; + } + + if (FAILED(hr) || !DoesD3D11DeviceWork(mD3D11Device)) { + gfxCriticalError() << "D3D11 device creation failed" << hexa(hr); + return false; + } + if (!mD3D11Device) { + return false; + } + + CheckIfRenderTargetViewNeedsRecreating(mD3D11Device); + + // Only test this when not using WARP since it can fail and cause + // GetDeviceRemovedReason to return weird values. + mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device); + mIsWARP = false; + return true; +} + +bool +gfxWindowsPlatform::AttemptWARPDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels) +{ + if (gfxPrefs::LayersD3D11DisableWARP()) { + return false; + } + + MOZ_ASSERT(!mD3D11Device); + + ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP()); + + MOZ_SEH_TRY { + HRESULT hr = + sD3D11CreateDeviceFn(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, + // Use + // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS + // to prevent bug 1092260. IE 11 also uses this flag. + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + aFeatureLevels.Elements(), aFeatureLevels.Length(), + D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr); + + if (FAILED(hr)) { + // This should always succeed... in theory. + gfxCriticalError() << "Failed to initialize WARP D3D11 device! " << hexa(hr); + return false; + } + + reporterWARP.SetSuccessful(); + } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { + gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!"; + return false; + } + + mIsWARP = true; + return true; +} + +bool +gfxWindowsPlatform::AttemptD3D11ContentDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels) +{ + HRESULT hr = E_INVALIDARG; + MOZ_SEH_TRY { + hr = + sD3D11CreateDeviceFn(mIsWARP ? nullptr : GetDXGIAdapter(), + mIsWARP ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_UNKNOWN, + nullptr, + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + aFeatureLevels.Elements(), aFeatureLevels.Length(), + D3D11_SDK_VERSION, byRef(mD3D11ContentDevice), nullptr, nullptr); + } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { + return false; + } + + return SUCCEEDED(hr); +} + +bool +gfxWindowsPlatform::AttemptD3D11ImageBridgeDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels) +{ + HRESULT hr = E_INVALIDARG; + MOZ_SEH_TRY{ + hr = + sD3D11CreateDeviceFn(GetDXGIAdapter(), D3D_DRIVER_TYPE_UNKNOWN, nullptr, + D3D11_CREATE_DEVICE_BGRA_SUPPORT, + aFeatureLevels.Elements(), aFeatureLevels.Length(), + D3D11_SDK_VERSION, byRef(mD3D11ImageBridgeDevice), nullptr, nullptr); + } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { + return false; + } + + if (FAILED(hr)) { + return false; + } + + mD3D11ImageBridgeDevice->SetExceptionMode(0); + + return DoesD3D11AlphaTextureSharingWork(mD3D11ImageBridgeDevice); +} + void gfxWindowsPlatform::InitD3D11Devices() { // This function attempts to initialize our D3D11 devices. If the hardware // is not blacklisted for D3D11 layers. This will first attempt to create a // hardware accelerated device. If this creation fails or the hardware is // blacklisted, then this function will abort if WARP is disabled, causing us // to fallback to D3D9 or Basic layers. If WARP is not disabled it will use // a WARP device which should always be available on Windows 7 and higher. - mD3D11DeviceInitialized = true; mDoesD3D11TextureSharingWork = false; MOZ_ASSERT(!mD3D11Device); DriverInitCrashDetection detectCrashes; if (InSafeMode() || detectCrashes.DisableAcceleration()) { return; } - bool useWARP = false; - - nsCOMPtr<nsIGfxInfo> gfxInfo = services::GetGfxInfo(); - if (gfxInfo) { - int32_t status; - if (NS_SUCCEEDED(gfxInfo->GetFeatureStatus(nsIGfxInfo::FEATURE_DIRECT3D_11_LAYERS, &status))) { - if (status != nsIGfxInfo::FEATURE_STATUS_OK) { - - // It seems like nvdxgiwrap makes a mess of WARP. See bug 1154703 for more. - if (gfxPrefs::LayersD3D11DisableWARP() || GetModuleHandleA("nvdxgiwrap.dll")) { - return; - } - - if (!IsWin8OrLater()) { - /* On Windows 7 WARP runs very badly on the builtin vga driver */ - nsString driver; - gfxInfo->GetAdapterDriver(driver); - // driver can start with vga or svga so only look for "framebuf..." - if (driver.Find("framebuf vga256 vga64k") != kNotFound) { - gfxCriticalError(CriticalLog::DefaultOptions(false)) << "Disabling WARP on builtin vga driver"; - return; - } - } - - useWARP = true; - } - } - } - - if (gfxPrefs::LayersD3D11ForceWARP()) { - useWARP = true; + D3D11Status status = CheckD3D11Support(); + if (status == D3D11Status::Blocked) { + return; } nsModuleHandle d3d11Module(LoadLibrarySystem32(L"d3d11.dll")); - decltype(D3D11CreateDevice)* d3d11CreateDevice = (decltype(D3D11CreateDevice)*) - GetProcAddress(d3d11Module, "D3D11CreateDevice"); + sD3D11CreateDeviceFn = + (decltype(D3D11CreateDevice)*)GetProcAddress(d3d11Module, "D3D11CreateDevice"); - if (!d3d11CreateDevice) { + if (!sD3D11CreateDeviceFn) { // We should just be on Windows Vista or XP in this case. return; } nsTArray<D3D_FEATURE_LEVEL> featureLevels; if (IsWin8OrLater()) { featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_1); } featureLevels.AppendElement(D3D_FEATURE_LEVEL_11_0); featureLevels.AppendElement(D3D_FEATURE_LEVEL_10_1); featureLevels.AppendElement(D3D_FEATURE_LEVEL_10_0); featureLevels.AppendElement(D3D_FEATURE_LEVEL_9_3); - RefPtr<IDXGIAdapter1> adapter; - - if (!useWARP) { - adapter = GetDXGIAdapter(); - - if (!adapter) { - if (!gfxPrefs::LayersD3D11DisableWARP()) { - return; - } - useWARP = true; + if (status == D3D11Status::Ok) { + if (!AttemptD3D11DeviceCreation(featureLevels)) { + status = D3D11Status::TryWARP; } } - HRESULT hr = E_INVALIDARG; - - if (!useWARP) { - MOZ_SEH_TRY { - hr = d3d11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, - // Use - // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS - // to prevent bug 1092260. IE 11 also uses this flag. - D3D11_CREATE_DEVICE_BGRA_SUPPORT | D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS, - featureLevels.Elements(), featureLevels.Length(), - D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr); - } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { - gfxCriticalError() << "Crash during D3D11 device creation"; - - if (gfxPrefs::LayersD3D11DisableWARP()) { - return; - } - - useWARP = true; - adapter = nullptr; - } - - if (FAILED(hr) || !DoesD3D11DeviceWork(mD3D11Device)) { - gfxCriticalError() << "D3D11 device creation failed" << hexa(hr); - if (gfxPrefs::LayersD3D11DisableWARP()) { - return; - } - - useWARP = true; - adapter = nullptr; - } - - if (mD3D11Device) { - CheckIfRenderTargetViewNeedsRecreating(mD3D11Device); - // Only test this when not using WARP since it can fail and cause GetDeviceRemovedReason to return - // weird values. - mDoesD3D11TextureSharingWork = ::DoesD3D11TextureSharingWork(mD3D11Device); + if (status == D3D11Status::TryWARP || status == D3D11Status::ForceWARP) { + if (!AttemptWARPDeviceCreation(featureLevels)) { + // Nothing more we can do. + return; } } - if (useWARP) { - MOZ_ASSERT(!gfxPrefs::LayersD3D11DisableWARP()); - MOZ_ASSERT(!mD3D11Device); - MOZ_ASSERT(!adapter); - - ScopedGfxFeatureReporter reporterWARP("D3D11-WARP", gfxPrefs::LayersD3D11ForceWARP()); - - MOZ_SEH_TRY { - hr = d3d11CreateDevice(nullptr, D3D_DRIVER_TYPE_WARP, nullptr, - // Use - // D3D11_CREATE_DEVICE_PREVENT_INTERNAL_THREADING_OPTIMIZATIONS - // to prevent bug 1092260. IE 11 also uses this flag. - D3D11_CREATE_DEVICE_BGRA_SUPPORT, - featureLevels.Elements(), featureLevels.Length(), - D3D11_SDK_VERSION, byRef(mD3D11Device), nullptr, nullptr); - - if (FAILED(hr)) { - // This should always succeed... in theory. - gfxCriticalError() << "Failed to initialize WARP D3D11 device! " << hexa(hr); - return; - } - - mIsWARP = true; - reporterWARP.SetSuccessful(); - } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { - gfxCriticalError() << "Exception occurred initializing WARP D3D11 device!"; - return; - } + if (!mD3D11Device) { + return; } mD3D11Device->SetExceptionMode(0); // We create our device for D2D content drawing here. Normally we don't use // D2D content drawing when using WARP. However when WARP is forced by // default we will let Direct2D use WARP as well. - if (Factory::SupportsD2D1() && (!useWARP || gfxPrefs::LayersD3D11ForceWARP())) { - MOZ_ASSERT((useWARP && !adapter) || !useWARP); - - hr = E_INVALIDARG; - MOZ_SEH_TRY { - hr = d3d11CreateDevice(adapter, useWARP ? D3D_DRIVER_TYPE_WARP : D3D_DRIVER_TYPE_UNKNOWN, nullptr, - D3D11_CREATE_DEVICE_BGRA_SUPPORT, - featureLevels.Elements(), featureLevels.Length(), - D3D11_SDK_VERSION, byRef(mD3D11ContentDevice), nullptr, nullptr); - } MOZ_SEH_EXCEPT (EXCEPTION_EXECUTE_HANDLER) { + if (Factory::SupportsD2D1() && (!mIsWARP || (status == D3D11Status::ForceWARP))) { + if (!AttemptD3D11ContentDeviceCreation(featureLevels)) { mD3D11ContentDevice = nullptr; - } - - if (FAILED(hr)) { d3d11Module.disown(); return; } mD3D11ContentDevice->SetExceptionMode(0); - nsRefPtr<ID3D10Multithread> multi; mD3D11ContentDevice->QueryInterface(__uuidof(ID3D10Multithread), getter_AddRefs(multi)); multi->SetMultithreadProtected(TRUE); Factory::SetDirect3D11Device(mD3D11ContentDevice); } - if (!useWARP) { - hr = E_INVALIDARG; - - MOZ_SEH_TRY{ - hr = d3d11CreateDevice(adapter, D3D_DRIVER_TYPE_UNKNOWN, nullptr, - D3D11_CREATE_DEVICE_BGRA_SUPPORT, - featureLevels.Elements(), featureLevels.Length(), - D3D11_SDK_VERSION, byRef(mD3D11ImageBridgeDevice), nullptr, nullptr); - } MOZ_SEH_EXCEPT(EXCEPTION_EXECUTE_HANDLER) { - mD3D11ImageBridgeDevice = nullptr; - } - - if (FAILED(hr)) { - d3d11Module.disown(); - return; - } - - mD3D11ImageBridgeDevice->SetExceptionMode(0); - - if (!DoesD3D11AlphaTextureSharingWork(mD3D11ImageBridgeDevice)) { + if (!mIsWARP) { + if (!AttemptD3D11ImageBridgeDeviceCreation(featureLevels)) { mD3D11ImageBridgeDevice = nullptr; } } // We leak these everywhere and we need them our entire runtime anyway, let's // leak it here as well. d3d11Module.disown(); }
--- a/gfx/thebes/gfxWindowsPlatform.h +++ b/gfx/thebes/gfxWindowsPlatform.h @@ -270,17 +270,36 @@ public: protected: RenderMode mRenderMode; int8_t mUseClearTypeForDownloadableFonts; int8_t mUseClearTypeAlways; private: void Init(); + void InitD3D11Devices(); + + // Used by InitD3D11Devices(). + enum class D3D11Status { + Ok, + TryWARP, + ForceWARP, + Blocked + }; + D3D11Status CheckD3D11Support(); + bool AttemptD3D11DeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels); + bool AttemptWARPDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels); + bool AttemptD3D11ImageBridgeDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels); + bool AttemptD3D11ContentDeviceCreation(const nsTArray<D3D_FEATURE_LEVEL>& aFeatureLevels); + + // Used by UpdateRenderMode(). + void InitD2DSupport(); + void InitDWriteSupport(); + IDXGIAdapter1 *GetDXGIAdapter(); bool IsDeviceReset(HRESULT hr, DeviceResetReason* aReason); bool mUseDirectWrite; bool mUsingGDIFonts; #ifdef CAIRO_HAS_DWRITE_FONT nsRefPtr<IDWriteFactory> mDWriteFactory;
--- a/ipc/glue/MessageChannel.cpp +++ b/ipc/glue/MessageChannel.cpp @@ -862,42 +862,44 @@ MessageChannel::Send(Message* aMsg, Mess if (mTimedOutMessageSeqno) { // Don't bother sending another sync message if a previous one timed out // and we haven't received a reply for it. Once the original timed-out // message receives a reply, we'll be able to send more sync messages // again. return false; } - if (DispatchingSyncMessagePriority() == IPC::Message::PRIORITY_NORMAL && + if (mCurrentTransaction && + DispatchingSyncMessagePriority() == IPC::Message::PRIORITY_NORMAL && msg->priority() > IPC::Message::PRIORITY_NORMAL) { // Don't allow sending CPOWs while we're dispatching a sync message. // If you want to do that, use sendRpcMessage instead. return false; } if (mCurrentTransaction && (msg->priority() < DispatchingSyncMessagePriority() || - mAwaitingSyncReplyPriority > msg->priority() || - DispatchingSyncMessagePriority() == IPC::Message::PRIORITY_URGENT || - DispatchingAsyncMessagePriority() == IPC::Message::PRIORITY_URGENT)) + mAwaitingSyncReplyPriority > msg->priority())) { CancelCurrentTransactionInternal(); mLink->SendMessage(new CancelMessage()); } IPC_ASSERT(msg->is_sync(), "can only Send() sync messages here"); - IPC_ASSERT(msg->priority() >= DispatchingSyncMessagePriority(), - "can't send sync message of a lesser priority than what's being dispatched"); - IPC_ASSERT(AwaitingSyncReplyPriority() <= msg->priority(), - "nested sync message sends must be of increasing priority"); - IPC_ASSERT(DispatchingSyncMessagePriority() != IPC::Message::PRIORITY_URGENT, - "not allowed to send messages while dispatching urgent messages"); + if (mCurrentTransaction) { + IPC_ASSERT(msg->priority() >= DispatchingSyncMessagePriority(), + "can't send sync message of a lesser priority than what's being dispatched"); + IPC_ASSERT(AwaitingSyncReplyPriority() <= msg->priority(), + "nested sync message sends must be of increasing priority"); + IPC_ASSERT(DispatchingSyncMessagePriority() != IPC::Message::PRIORITY_URGENT, + "not allowed to send messages while dispatching urgent messages"); + } + IPC_ASSERT(DispatchingAsyncMessagePriority() != IPC::Message::PRIORITY_URGENT, "not allowed to send messages while dispatching urgent messages"); if (!Connected()) { ReportConnectionError("MessageChannel::SendAndWait", msg); return false; } @@ -1311,21 +1313,16 @@ MessageChannel::DispatchSyncMessage(cons // We don't want to run any code that might run a nested event loop here, so // we avoid running event handlers. Once we've sent the response to the // urgent message, it's okay to run event handlers again since the parent is // no longer blocked. MOZ_ASSERT_IF(prio > IPC::Message::PRIORITY_NORMAL, NS_IsMainThread()); MaybeScriptBlocker scriptBlocker(this, prio > IPC::Message::PRIORITY_NORMAL); - IPC_ASSERT(prio >= DispatchingSyncMessagePriority(), - "priority inversion while dispatching sync message"); - IPC_ASSERT(prio >= mAwaitingSyncReplyPriority, - "dispatching a message of lower priority while waiting for a response"); - MessageChannel* dummy; MessageChannel*& blockingVar = ShouldBlockScripts() ? gParentProcessBlocker : dummy; Result rv; if (mTimedOutMessageSeqno && mTimedOutMessagePriority >= prio) { // If the other side sends a message in response to one of our messages // that we've timed out, then we reply with an error. // @@ -2017,26 +2014,36 @@ MessageChannel::CancelCurrentTransaction // canceled. Consequently, we have to update the state variables below. // // We also need to ensure that when any IPC functions on the stack return, // they don't reset these values using an RAII class like AutoSetValue. To // avoid that, these RAII classes check if the variable they set has been // tampered with (by us). If so, they don't reset the variable to the old // value. - MOZ_ASSERT(!mCurrentTransaction); + MOZ_ASSERT(mCurrentTransaction); mCurrentTransaction = 0; + + mAwaitingSyncReply = false; + mAwaitingSyncReplyPriority = 0; + + // We could also zero out mDispatchingSyncMessage here. However, that would + // cause a race because mDispatchingSyncMessage is a worker-thread-only + // field and we can be called on the I/O thread. Luckily, we can check to + // see if mCurrentTransaction is 0 before examining DispatchSyncMessage. } void MessageChannel::CancelCurrentTransaction() { MonitorAutoLock lock(*mMonitor); - CancelCurrentTransactionInternal(); - mLink->SendMessage(new CancelMessage()); + if (mCurrentTransaction) { + CancelCurrentTransactionInternal(); + mLink->SendMessage(new CancelMessage()); + } } void CancelCPOWs() { if (gParentProcessBlocker) { mozilla::Telemetry::Accumulate(mozilla::Telemetry::IPC_TRANSACTION_CANCEL, true); gParentProcessBlocker->CancelCurrentTransaction();
--- a/ipc/glue/MessageChannel.h +++ b/ipc/glue/MessageChannel.h @@ -124,16 +124,21 @@ class MessageChannel : HasResultCodes // Make an Interrupt call to the other side of the channel bool Call(Message* aMsg, Message* aReply); // Wait until a message is received bool WaitForIncomingMessage(); bool CanSend() const; + // Currently only for debugging purposes, doesn't aquire mMonitor. + ChannelState GetChannelState__TotallyRacy() const { + return mChannelState; + } + void SetReplyTimeoutMs(int32_t aTimeoutMs); bool IsOnCxxStack() const { return !mCxxStackFrames.empty(); } void CancelCurrentTransaction(); @@ -549,17 +554,17 @@ class MessageChannel : HasResultCodes // The current transaction ID. int32_t mCurrentTransaction; class AutoEnterTransaction { public: explicit AutoEnterTransaction(MessageChannel *aChan, int32_t aMsgSeqno) : mChan(aChan), - mNewTransaction(0), + mNewTransaction(INT32_MAX), mOldTransaction(mChan->mCurrentTransaction) { mChan->mMonitor->AssertCurrentThreadOwns(); if (mChan->mCurrentTransaction == 0) { mNewTransaction = aMsgSeqno; mChan->mCurrentTransaction = aMsgSeqno; } }
--- a/ipc/ipdl/test/cxx/TestBridgeMain.cpp +++ b/ipc/ipdl/test/cxx/TestBridgeMain.cpp @@ -128,17 +128,17 @@ TestBridgeSubParent::Main() { if (!SendPing()) fail("sending Ping"); } bool TestBridgeSubParent::RecvBridgeEm() { - if (!PTestBridgeMainSub::Bridge(gBridgeMainChild, this)) + if (NS_FAILED(PTestBridgeMainSub::Bridge(gBridgeMainChild, this))) fail("bridging Main and Sub"); return true; } void TestBridgeSubParent::ActorDestroy(ActorDestroyReason why) { if (NormalShutdown != why)
--- a/js/src/frontend/BytecodeEmitter.cpp +++ b/js/src/frontend/BytecodeEmitter.cpp @@ -4130,38 +4130,58 @@ BytecodeEmitter::emitDestructuringOps(Pa return emitDestructuringOpsHelper(pattern, emitOption); } bool BytecodeEmitter::emitTemplateString(ParseNode* pn) { MOZ_ASSERT(pn->isArity(PN_LIST)); + bool pushedString = false; + for (ParseNode* pn2 = pn->pn_head; pn2 != NULL; pn2 = pn2->pn_next) { - if (pn2->getKind() != PNK_STRING && pn2->getKind() != PNK_TEMPLATE_STRING) { + bool isString = (pn2->getKind() == PNK_STRING || pn2->getKind() == PNK_TEMPLATE_STRING); + + // Skip empty strings. These are very common: a template string like + // `${a}${b}` has three empty strings and without this optimization + // we'd emit four JSOP_ADD operations instead of just one. + if (isString && pn2->pn_atom->empty()) + continue; + + if (!isString) { // We update source notes before emitting the expression if (!updateSourceCoordNotes(pn2->pn_pos.begin)) return false; } + if (!emitTree(pn2)) return false; - if (pn2->getKind() != PNK_STRING && pn2->getKind() != PNK_TEMPLATE_STRING) { + if (!isString) { // We need to convert the expression to a string if (!emit1(JSOP_TOSTRING)) return false; } - if (pn2 != pn->pn_head) { + if (pushedString) { // We've pushed two strings onto the stack. Add them together, leaving just one. if (!emit1(JSOP_ADD)) return false; - } - - } + } else { + pushedString = true; + } + } + + if (!pushedString) { + // All strings were empty, this can happen for something like `${""}`. + // Just push an empty string. + if (!emitAtomOp(cx->names().empty, JSOP_STRING)) + return false; + } + return true; } bool BytecodeEmitter::emitVariables(ParseNode* pn, VarEmitOption emitOption, bool isLetExpr) { MOZ_ASSERT(pn->isArity(PN_LIST)); MOZ_ASSERT(isLetExpr == (emitOption == PushInitialValues));
--- a/js/src/jit/RegisterSets.h +++ b/js/src/jit/RegisterSets.h @@ -1254,19 +1254,21 @@ SavedNonVolatileRegisters(AllocatableGen LiveGeneralRegisterSet result; for (GeneralRegisterIterator iter(GeneralRegisterSet::NonVolatile()); iter.more(); iter++) { Register reg = *iter; if (!unused.has(reg)) result.add(reg); } - // ARM and MIPS require an additional register to be saved, if calls can be made. + // Some platforms require the link register to be saved, if calls can be made. #if defined(JS_CODEGEN_ARM) result.add(Register::FromCode(Registers::lr)); +#elif defined(JS_CODEGEN_ARM64) + result.add(Register::FromCode(Registers::lr)); #elif defined(JS_CODEGEN_MIPS) result.add(Register::FromCode(Registers::ra)); #endif return result; } } // namespace jit
--- a/js/src/jit/ScalarReplacement.cpp +++ b/js/src/jit/ScalarReplacement.cpp @@ -215,17 +215,17 @@ IsObjectEscaped(MInstruction* ins, JSObj } #endif break; } case MDefinition::Op_GuardShape: { MGuardShape* guard = def->toGuardShape(); MOZ_ASSERT(!ins->isGuardShape()); - if (obj->as<NativeObject>().lastProperty() != guard->shape()) { + if (obj->maybeShape() != guard->shape()) { JitSpewDef(JitSpew_Escape, "has a non-matching guard shape\n", guard); return true; } if (IsObjectEscaped(def->toInstruction(), obj)) { JitSpewDef(JitSpew_Escape, "is indirectly escaped by\n", def); return true; } break;
--- a/js/xpconnect/src/Sandbox.cpp +++ b/js/xpconnect/src/Sandbox.cpp @@ -1024,29 +1024,30 @@ xpc::CreateSandboxObject(JSContext* cx, ? &SandboxWriteToProtoClass : &SandboxClass; RootedObject sandbox(cx, xpc::CreateGlobalObject(cx, js::Jsvalify(clasp), principal, compartmentOptions)); if (!sandbox) return NS_ERROR_FAILURE; - CompartmentPrivate::Get(sandbox)->writeToGlobalPrototype = - options.writeToGlobalPrototype; + CompartmentPrivate* priv = CompartmentPrivate::Get(sandbox); + priv->allowWaivers = options.allowWaivers; + priv->writeToGlobalPrototype = options.writeToGlobalPrototype; // Set up the wantXrays flag, which indicates whether xrays are desired even // for same-origin access. // // This flag has historically been ignored for chrome sandboxes due to // quirks in the wrapping implementation that have now been removed. Indeed, // same-origin Xrays for chrome->chrome access seems a bit superfluous. // Arguably we should just flip the default for chrome and still honor the // flag, but such a change would break code in subtle ways for minimal // benefit. So we just switch it off here. - CompartmentPrivate::Get(sandbox)->wantXrays = + priv->wantXrays = AccessCheck::isChrome(sandbox) ? false : options.wantXrays; { JSAutoCompartment ac(cx, sandbox); if (options.proto) { bool ok = JS_WrapObject(cx, &options.proto); if (!ok) @@ -1475,16 +1476,17 @@ SandboxOptions::ParseGlobalProperties() /* * Helper that parsing the sandbox options object (from) and sets the fields of the incoming options struct (options). */ bool SandboxOptions::Parse() { bool ok = ParseObject("sandboxPrototype", &proto) && ParseBoolean("wantXrays", &wantXrays) && + ParseBoolean("allowWaivers", &allowWaivers) && ParseBoolean("wantComponents", &wantComponents) && ParseBoolean("wantExportHelpers", &wantExportHelpers) && ParseString("sandboxName", sandboxName) && ParseObject("sameZoneAs", &sameZoneAs) && ParseBoolean("freshZone", &freshZone) && ParseBoolean("invisibleToDebugger", &invisibleToDebugger) && ParseBoolean("discardSource", &discardSource) && ParseJSString("addonId", &addonId) &&
--- a/js/xpconnect/src/XPCWrappedNative.cpp +++ b/js/xpconnect/src/XPCWrappedNative.cpp @@ -1292,17 +1292,17 @@ class MOZ_STACK_CLASS CallMethodHelper nsAutoTArray<nsXPTCVariant, 8> mDispatchParams; uint8_t mJSContextIndex; // TODO make const uint8_t mOptArgcIndex; // TODO make const jsval* const mArgv; const uint32_t mArgc; MOZ_ALWAYS_INLINE bool - GetArraySizeFromParam(uint8_t paramIndex, uint32_t* result) const; + GetArraySizeFromParam(uint8_t paramIndex, HandleValue maybeArray, uint32_t* result); MOZ_ALWAYS_INLINE bool GetInterfaceTypeFromParam(uint8_t paramIndex, const nsXPTType& datum_type, nsID* result) const; MOZ_ALWAYS_INLINE bool GetOutParamSource(uint8_t paramIndex, MutableHandleValue srcp) const; @@ -1441,17 +1441,17 @@ CallMethodHelper::~CallMethodHelper() if (!p) continue; // Clean up the array contents if necessary. if (dp->DoesValNeedCleanup()) { // We need some basic information to properly destroy the array. uint32_t array_count = 0; nsXPTType datum_type; - if (!GetArraySizeFromParam(i, &array_count) || + if (!GetArraySizeFromParam(i, UndefinedHandleValue, &array_count) || !NS_SUCCEEDED(mIFaceInfo->GetTypeForParam(mVTableIndex, ¶mInfo, 1, &datum_type))) { // XXXbholley - I'm not convinced that the above calls will // ever fail. NS_ERROR("failed to get array information, we'll leak here"); continue; } @@ -1475,27 +1475,44 @@ CallMethodHelper::~CallMethodHelper() } } mCallContext.GetXPCContext()->SetLastResult(mInvokeResult); } bool CallMethodHelper::GetArraySizeFromParam(uint8_t paramIndex, - uint32_t* result) const + HandleValue maybeArray, + uint32_t* result) { nsresult rv; const nsXPTParamInfo& paramInfo = mMethodInfo->GetParam(paramIndex); // TODO fixup the various exceptions that are thrown rv = mIFaceInfo->GetSizeIsArgNumberForParam(mVTableIndex, ¶mInfo, 0, ¶mIndex); if (NS_FAILED(rv)) return Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext); + // If the array length wasn't passed, it might have been listed as optional. + // When converting arguments from JS to C++, we pass the array as |maybeArray|, + // and give ourselves the chance to infer the length. Once we have it, we stick + // it in the right slot so that we can find it again when cleaning up the params. + // from the array. + if (paramIndex >= mArgc && maybeArray.isObject()) { + MOZ_ASSERT(mMethodInfo->GetParam(paramIndex).IsOptional()); + RootedObject arrayOrNull(mCallContext, maybeArray.isObject() ? &maybeArray.toObject() + : nullptr); + if (!JS_IsArrayObject(mCallContext, maybeArray) || + !JS_GetArrayLength(mCallContext, arrayOrNull, &GetDispatchParam(paramIndex)->val.u32)) + { + return Throw(NS_ERROR_XPC_CANT_CONVERT_OBJECT_TO_ARRAY, mCallContext); + } + } + *result = GetDispatchParam(paramIndex)->val.u32; return true; } bool CallMethodHelper::GetInterfaceTypeFromParam(uint8_t paramIndex, const nsXPTType& datum_type, @@ -1581,17 +1598,17 @@ CallMethodHelper::GatherAndConvertResult &datum_type))) { Throw(NS_ERROR_XPC_CANT_GET_ARRAY_INFO, mCallContext); return false; } } else datum_type = type; if (isArray || isSizedString) { - if (!GetArraySizeFromParam(i, &array_count)) + if (!GetArraySizeFromParam(i, UndefinedHandleValue, &array_count)) return false; } nsID param_iid; if (datum_type.IsInterfacePointer() && !GetInterfaceTypeFromParam(i, datum_type, ¶m_iid)) return false; @@ -1963,17 +1980,17 @@ CallMethodHelper::ConvertDependentParam( nsID param_iid; if (datum_type.IsInterfacePointer() && !GetInterfaceTypeFromParam(i, datum_type, ¶m_iid)) return false; nsresult err; if (isArray || isSizedString) { - if (!GetArraySizeFromParam(i, &array_count)) + if (!GetArraySizeFromParam(i, src, &array_count)) return false; if (isArray) { if (array_count && !XPCConvert::JSArray2Native((void**)&dp->val, src, array_count, datum_type, ¶m_iid, &err)) { // XXX need exception scheme for arrays to indicate bad element
--- a/js/xpconnect/src/XPCWrapper.cpp +++ b/js/xpconnect/src/XPCWrapper.cpp @@ -27,26 +27,24 @@ static bool UnwrapNW(JSContext* cx, unsigned argc, jsval* vp) { JS::CallArgs args = JS::CallArgsFromVp(argc, vp); if (args.length() != 1) { return ThrowException(NS_ERROR_XPC_NOT_ENOUGH_ARGS, cx); } JS::RootedValue v(cx, args[0]); - if (!v.isObject() || !js::IsWrapper(&v.toObject())) { + if (!v.isObject() || !js::IsCrossCompartmentWrapper(&v.toObject()) || + !WrapperFactory::AllowWaiver(&v.toObject())) { args.rval().set(v); return true; } - if (AccessCheck::wrapperSubsumes(&v.toObject())) { - bool ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v); - NS_ENSURE_TRUE(ok, false); - } - + bool ok = xpc::WrapperFactory::WaiveXrayAndWrap(cx, &v); + NS_ENSURE_TRUE(ok, false); args.rval().set(v); return true; } static bool XrayWrapperConstructor(JSContext* cx, unsigned argc, jsval* vp) { JS::CallArgs args = CallArgsFromVp(argc, vp);
--- a/js/xpconnect/src/xpcprivate.h +++ b/js/xpconnect/src/xpcprivate.h @@ -3038,17 +3038,17 @@ public: NS_IMETHOD Init(const nsAString& message, const nsAString& sourceName, const nsAString& sourceLine, uint32_t lineNumber, uint32_t columnNumber, uint32_t flags, const char* category) override; - NS_IMETHOD GetStack(JS::MutableHandleValue); + NS_IMETHOD GetStack(JS::MutableHandleValue) override; private: virtual ~nsScriptErrorWithStack(); // Complete stackframe where the error happened. // Must be SavedFrame object. JS::Heap<JSObject*> mStack; }; @@ -3467,31 +3467,33 @@ protected: }; class MOZ_STACK_CLASS SandboxOptions : public OptionsBase { public: explicit SandboxOptions(JSContext* cx = xpc_GetSafeJSContext(), JSObject* options = nullptr) : OptionsBase(cx, options) , wantXrays(true) + , allowWaivers(true) , wantComponents(true) , wantExportHelpers(false) , proto(cx) , addonId(cx) , writeToGlobalPrototype(false) , sameZoneAs(cx) , freshZone(false) , invisibleToDebugger(false) , discardSource(false) , metadata(cx) { } virtual bool Parse(); bool wantXrays; + bool allowWaivers; bool wantComponents; bool wantExportHelpers; JS::RootedObject proto; nsCString sandboxName; JS::RootedString addonId; bool writeToGlobalPrototype; JS::RootedObject sameZoneAs; bool freshZone; @@ -3678,16 +3680,17 @@ class CompartmentPrivate public: enum LocationHint { LocationHintRegular, LocationHintAddon }; explicit CompartmentPrivate(JSCompartment* c) : wantXrays(false) + , allowWaivers(true) , writeToGlobalPrototype(false) , skipWriteToGlobalPrototype(false) , universalXPConnectEnabled(false) , forcePermissiveCOWs(false) , skipCOWCallableChecks(false) , scriptability(c) , scope(nullptr) { @@ -3705,19 +3708,27 @@ public: } static CompartmentPrivate* Get(JSObject* object) { JSCompartment* compartment = js::GetObjectCompartment(object); return Get(compartment); } - + // Controls whether this compartment gets Xrays to same-origin. This behavior + // is deprecated, but is still the default for sandboxes for compatibity + // reasons. bool wantXrays; + // Controls whether this compartment is allowed to waive Xrays to content + // that it subsumes. This should generally be true, except in cases where we + // want to prevent code from depending on Xray Waivers (which might make it + // more portable to other browser architectures). + bool allowWaivers; + // This flag is intended for a very specific use, internal to Gecko. It may // go away or change behavior at any time. It should not be added to any // documentation and it should not be used without consulting the XPConnect // module owner. bool writeToGlobalPrototype; // When writeToGlobalPrototype is true, we use this flag to temporarily // disable the writeToGlobalPrototype behavior (when resolving standard
--- a/js/xpconnect/tests/components/js/xpctest_params.js +++ b/js/xpconnect/tests/components/js/xpctest_params.js @@ -68,12 +68,18 @@ TestParams.prototype = { testDoubleArray: f_is, testStringArray: f_is, testWstringArray: f_is, testInterfaceArray: f_is, testSizedString: f_is, testSizedWstring: f_is, testInterfaceIs: f_is, testInterfaceIsArray: f_size_and_iid, - testOutAString: function(o) { o.value = "out"; } + testOutAString: function(o) { o.value = "out"; }, + testStringArrayOptionalSize: function(arr, size) { + if (arr.length != size) { throw "bad size passed to test method"; } + var rv = ""; + arr.forEach((x) => rv += x); + return rv; + } }; this.NSGetFactory = XPCOMUtils.generateNSGetFactory([TestParams]);
--- a/js/xpconnect/tests/components/native/xpctest_params.cpp +++ b/js/xpconnect/tests/components/native/xpctest_params.cpp @@ -341,8 +341,21 @@ NS_IMETHODIMP nsXPCTestParams::TestInter } /* void testOutAString (out AString o); */ NS_IMETHODIMP nsXPCTestParams::TestOutAString(nsAString & o) { o.AssignLiteral("out"); return NS_OK; } + +/* + * ACString testStringArrayOptionalSize([array, size_is(aLength)] in string a, [optional] in unsigned long aLength); + */ +NS_IMETHODIMP nsXPCTestParams::TestStringArrayOptionalSize(const char * *a, uint32_t length, nsACString& out) +{ + out.Truncate(); + for (uint32_t i = 0; i < length; ++i) { + out.Append(a[i]); + } + + return NS_OK; +}
--- a/js/xpconnect/tests/idl/xpctest_params.idl +++ b/js/xpconnect/tests/idl/xpctest_params.idl @@ -10,17 +10,17 @@ * covered by the intersection of return values and inout). */ #include "nsISupports.idl" interface nsIXPCTestInterfaceA; interface nsIXPCTestInterfaceB; -[scriptable, uuid(fe2b7433-ac3b-49ef-9344-b67228bfdd46)] +[scriptable, uuid(812145c7-9fcc-425e-a878-36ad1b7730b7)] interface nsIXPCTestParams : nsISupports { // These types correspond to the ones in typelib.py boolean testBoolean(in boolean a, inout boolean b); octet testOctet(in octet a, inout octet b); short testShort(in short a, inout short b); long testLong(in long a, inout long b); long long testLongLong(in long long a, inout long long b); @@ -79,9 +79,12 @@ interface nsIXPCTestParams : nsISupports [array, size_is(aLength), iid_is(aIID)] in nsQIResult a, inout unsigned long bLength, inout nsIIDPtr bIID, [array, size_is(bLength), iid_is(bIID)] inout nsQIResult b, out unsigned long rvLength, out nsIIDPtr rvIID, [retval, array, size_is(rvLength), iid_is(rvIID)] out nsQIResult rv); // Test for out dipper parameters void testOutAString(out AString o); + + // Test for optional array size_is. + ACString testStringArrayOptionalSize([array, size_is(aLength)] in string a, [optional] in unsigned long aLength); };
new file mode 100644 --- /dev/null +++ b/js/xpconnect/tests/unit/test_allowWaivers.js @@ -0,0 +1,30 @@ +const Cu = Components.utils; +function checkWaivers(from, allowed) { + var sb = new Cu.Sandbox('http://example.com'); + from.test = sb.eval('var o = {prop: 2, f: function() {return 42;}}; o'); + + // Make sure that |from| has Xrays to sb. + do_check_eq(from.eval('test.prop'), 2); + do_check_eq(from.eval('test.f'), undefined); + + // Make sure that waivability works as expected. + do_check_eq(from.eval('!!test.wrappedJSObject'), allowed); + do_check_eq(from.eval('XPCNativeWrapper.unwrap(test) !== test'), allowed); + + // Make a sandbox with the same principal as |from|, but without any waiver + // restrictions, and make sure that the waiver does not transfer. + var friend = new Cu.Sandbox(Cu.getObjectPrincipal(from)); + friend.test = from.test; + friend.eval('var waived = test.wrappedJSObject;'); + do_check_true(friend.eval('waived.f()'), 42); + friend.from = from; + friend.eval('from.waived = waived'); + do_check_eq(from.eval('!!waived.f'), allowed); +} + +function run_test() { + checkWaivers(new Cu.Sandbox('http://example.com'), true); + checkWaivers(new Cu.Sandbox('http://example.com', {allowWaivers: false}), false); + checkWaivers(new Cu.Sandbox(['http://example.com']), true); + checkWaivers(new Cu.Sandbox(['http://example.com'], {allowWaivers: false}), false); +}
--- a/js/xpconnect/tests/unit/test_params.js +++ b/js/xpconnect/tests/unit/test_params.js @@ -183,16 +183,19 @@ function test_component(contractid) { doIsTest("testInterfaceIs", makeA(), Ci['nsIXPCTestInterfaceA'], makeB(), Ci['nsIXPCTestInterfaceB'], interfaceComparator, dotEqualsComparator); // Test arrays of iids. doIs2Test("testInterfaceIsArray", [makeA(), makeA(), makeA(), makeA(), makeA()], 5, Ci['nsIXPCTestInterfaceA'], [makeB(), makeB(), makeB()], 3, Ci['nsIXPCTestInterfaceB']); + // Test optional array size. + do_check_eq(o.testStringArrayOptionalSize(["some", "string", "array"]), "somestringarray"); + // Test incorrect (too big) array size parameter; this should throw NOT_ENOUGH_ELEMENTS. doTypedArrayMismatchTest("testShortArray", new Int16Array([-3, 7, 4]), 4, new Int16Array([1, -32, 6]), 3); // Test type mismatch (int16 <-> uint16); this should throw BAD_CONVERT_JS. doTypedArrayMismatchTest("testShortArray", new Uint16Array([0, 7, 4, 3]), 4, new Uint16Array([1, 5, 6]), 3); }
--- a/js/xpconnect/tests/unit/xpcshell.ini +++ b/js/xpconnect/tests/unit/xpcshell.ini @@ -13,16 +13,17 @@ support-files = component-file.manifest component_import.js component_import.manifest importer.jsm recursive_importA.jsm recursive_importB.jsm syntax_error.jsm +[test_allowWaivers.js] [test_bogus_files.js] [test_bug408412.js] [test_bug451678.js] [test_bug604362.js] [test_bug641378.js] [test_bug677864.js] [test_bug711404.js] [test_bug742444.js]
--- a/js/xpconnect/wrappers/WrapperFactory.cpp +++ b/js/xpconnect/wrappers/WrapperFactory.cpp @@ -101,16 +101,30 @@ WrapperFactory::WaiveXray(JSContext* cx, MOZ_ASSERT(!js::IsInnerObject(obj)); JSObject* waiver = GetXrayWaiver(obj); if (waiver) return waiver; return CreateXrayWaiver(cx, obj); } +/* static */ bool +WrapperFactory::AllowWaiver(JSCompartment* target, JSCompartment* origin) +{ + return CompartmentPrivate::Get(target)->allowWaivers && + AccessCheck::subsumes(target, origin); +} + +/* static */ bool +WrapperFactory::AllowWaiver(JSObject* wrapper) { + MOZ_ASSERT(js::IsCrossCompartmentWrapper(wrapper)); + return AllowWaiver(js::GetObjectCompartment(wrapper), + js::GetObjectCompartment(js::UncheckedUnwrap(wrapper))); +} + inline bool ShouldWaiveXray(JSContext* cx, JSObject* originalObj) { unsigned flags; (void) js::UncheckedUnwrap(originalObj, /* stopAtOuter = */ true, &flags); // If the original object did not point through an Xray waiver, we're done. if (!(flags & WrapperFactory::WAIVE_XRAY_WRAPPER_FLAG)) @@ -463,18 +477,20 @@ WrapperFactory::Rewrap(JSContext* cx, Ha // // Xrays are a bidirectional protection, since it affords clarity to the // caller and privacy to the callee. bool sameOriginXrays = CompartmentPrivate::Get(origin)->wantXrays || CompartmentPrivate::Get(target)->wantXrays; bool wantXrays = !sameOrigin || sameOriginXrays; // If Xrays are warranted, the caller may waive them for non-security - // wrappers. - bool waiveXrays = wantXrays && !securityWrapper && HasWaiveXrayFlag(obj); + // wrappers (unless explicitly forbidden from doing so). + bool waiveXrays = wantXrays && !securityWrapper && + CompartmentPrivate::Get(target)->allowWaivers && + HasWaiveXrayFlag(obj); // We have slightly different behavior for the case when the object // being wrapped is in an XBL scope. bool originIsContentXBLScope = IsContentXBLScope(origin); wrapper = SelectWrapper(securityWrapper, wantXrays, xrayType, waiveXrays, originIsContentXBLScope, obj); @@ -537,17 +553,17 @@ WrapperFactory::WaiveXrayAndWrap(JSConte // handing out waivers to callers that can't use them. The transitive waiving // machinery unconditionally calls WaiveXrayAndWrap on return values from // waived functions, even though the return value might be not be same-origin // with the function. So if we find ourselves trying to create a waiver for // |cx|, we should check whether the caller has any business with waivers // to things in |obj|'s compartment. JSCompartment* target = js::GetContextCompartment(cx); JSCompartment* origin = js::GetObjectCompartment(obj); - obj = AccessCheck::subsumes(target, origin) ? WaiveXray(cx, obj) : obj; + obj = AllowWaiver(target, origin) ? WaiveXray(cx, obj) : obj; if (!obj) return false; if (!JS_WrapObject(cx, &obj)) return false; argObj.set(obj); return true; }
--- a/js/xpconnect/wrappers/WrapperFactory.h +++ b/js/xpconnect/wrappers/WrapperFactory.h @@ -32,16 +32,23 @@ class WrapperFactory { } static bool IsCOW(JSObject* wrapper); static JSObject* GetXrayWaiver(JS::HandleObject obj); static JSObject* CreateXrayWaiver(JSContext* cx, JS::HandleObject obj); static JSObject* WaiveXray(JSContext* cx, JSObject* obj); + // Computes whether we should allow the creation of an Xray waiver from + // |target| to |origin|. + static bool AllowWaiver(JSCompartment* target, JSCompartment* origin); + + // Convenience method for the above, operating on a wrapper. + static bool AllowWaiver(JSObject* wrapper); + // Prepare a given object for wrapping in a new compartment. static JSObject* PrepareForWrapping(JSContext* cx, JS::HandleObject scope, JS::HandleObject obj, JS::HandleObject objectPassedToWrap); // Rewrap an object that is about to cross compartment boundaries. static JSObject* Rewrap(JSContext* cx,
--- a/js/xpconnect/wrappers/XrayWrapper.cpp +++ b/js/xpconnect/wrappers/XrayWrapper.cpp @@ -1310,17 +1310,17 @@ wrappedJSObject_getter(JSContext* cx, un { CallArgs args = CallArgsFromVp(argc, vp); if (!args.thisv().isObject()) { JS_ReportError(cx, "This value not an object"); return false; } RootedObject wrapper(cx, &args.thisv().toObject()); if (!IsWrapper(wrapper) || !WrapperFactory::IsXrayWrapper(wrapper) || - !AccessCheck::wrapperSubsumes(wrapper)) { + !WrapperFactory::AllowWaiver(wrapper)) { JS_ReportError(cx, "Unexpected object"); return false; } args.rval().setObject(*wrapper); return WrapperFactory::WaiveXrayAndWrap(cx, args.rval()); } @@ -1373,17 +1373,17 @@ XrayTraits::resolveOwnProperty(JSContext // Pretend the property lives on the wrapper. desc.object().set(wrapper); return true; } // Handle .wrappedJSObject for subsuming callers. This should move once we // sort out own-ness for the holder. if (id == GetRTIdByIndex(cx, XPCJSRuntime::IDX_WRAPPED_JSOBJECT) && - AccessCheck::wrapperSubsumes(wrapper)) + WrapperFactory::AllowWaiver(wrapper)) { if (!JS_AlreadyHasOwnPropertyById(cx, holder, id, &found)) return false; if (!found && !JS_DefinePropertyById(cx, holder, id, UndefinedHandleValue, JSPROP_ENUMERATE | JSPROP_SHARED, wrappedJSObject_getter)) { return false; }
--- a/layout/base/nsBidiPresUtils.cpp +++ b/layout/base/nsBidiPresUtils.cpp @@ -1461,20 +1461,21 @@ nsBidiPresUtils::RepositionRubyContentFr } nscoord residualISize = aFrame->ISize(aFrameWM) - isize; if (residualISize <= 0) { return; } // When ruby-align is not "start", if the content does not fill this // frame, we need to center the children. + const nsSize dummyContainerSize; for (nsIFrame* child : childList) { - LogicalRect rect = child->GetLogicalRect(aFrameWM, 0); + LogicalRect rect = child->GetLogicalRect(aFrameWM, dummyContainerSize); rect.IStart(aFrameWM) += residualISize / 2; - child->SetRect(aFrameWM, rect, 0); + child->SetRect(aFrameWM, rect, dummyContainerSize); } } /* static */ nscoord nsBidiPresUtils::RepositionRubyFrame( nsIFrame* aFrame, const nsContinuationStates* aContinuationStates, const WritingMode aContainerWM, @@ -1614,38 +1615,33 @@ nsBidiPresUtils::RepositionFrame(nsIFram } else if (RubyUtils::IsRubyBox(aFrame->GetType())) { icoord += RepositionRubyFrame(aFrame, aContinuationStates, aContainerWM, borderPadding); } else { icoord += frameWM.IsOrthogonalTo(aContainerWM) ? aFrame->BSize() : frameISize; } - // LogicalRect doesn't correctly calculate the vertical position - // in vertical writing modes with right-to-left direction (Bug 1131451). - // This does the correct calculation ad hoc pending the fix for that. - nsRect rect = aFrame->GetRect(); - - LogicalMargin margin = frameMargin.ConvertTo(aContainerWM, frameWM); // In the following variables, if aContainerReverseDir is true, i.e. // the container is positioning its children in reverse of its logical // direction, the "StartOrEnd" refers to the distance from the frame // to the inline end edge of the container, elsewise, it refers to the // distance to the inline start edge. - nscoord marginStartOrEnd = aContainerReverseDir ? - margin.IEnd(aContainerWM) : margin.IStart(aContainerWM); + const LogicalMargin margin = frameMargin.ConvertTo(aContainerWM, frameWM); + nscoord marginStartOrEnd = + aContainerReverseDir ? margin.IEnd(aContainerWM) + : margin.IStart(aContainerWM); nscoord frameStartOrEnd = aStartOrEnd + marginStartOrEnd; - // Whether we are placing frames from right to left. - // e.g. If the frames are placed reversely in LTR mode, they are - // actually placed from right to left. - bool orderingRTL = aContainerReverseDir == aContainerWM.IsBidiLTR(); - (aContainerWM.IsVertical() ? rect.y : rect.x) = orderingRTL ? - lineSize - (frameStartOrEnd + icoord) : frameStartOrEnd; - (aContainerWM.IsVertical() ? rect.height : rect.width) = icoord; - aFrame->SetRect(rect); + + LogicalRect rect = aFrame->GetLogicalRect(aContainerWM, aContainerSize); + rect.ISize(aContainerWM) = icoord; + rect.IStart(aContainerWM) = + aContainerReverseDir ? lineSize - frameStartOrEnd - icoord + : frameStartOrEnd; + aFrame->SetRect(aContainerWM, rect, aContainerSize); return icoord + margin.IStartEnd(aContainerWM); } void nsBidiPresUtils::InitContinuationStates(nsIFrame* aFrame, nsContinuationStates* aContinuationStates) {
--- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -819,17 +819,17 @@ nsLayoutUtils::UsesAsyncScrolling(nsIFra return AsyncPanZoomEnabled(aFrame); } bool nsLayoutUtils::AsyncPanZoomEnabled(nsIFrame* aFrame) { // We use this as a shortcut, since if the compositor will never use APZ, // no widget will either. - if (!gfxPrefs::AsyncPanZoomEnabledDoNotUseDirectly()) { + if (!gfxPlatform::AsyncPanZoomEnabled()) { return false; } nsIFrame *frame = nsLayoutUtils::GetDisplayRootFrame(aFrame); nsIWidget* widget = frame->GetNearestWidget(); if (!widget) { return false; } @@ -5614,17 +5614,17 @@ nsLayoutUtils::GetFirstLinePosition(Writ } if (fType == nsGkAtoms::fieldSetFrame) { LinePosition kidPosition; nsIFrame* kid = aFrame->GetFirstPrincipalChild(); // kid might be a legend frame here, but that's ok. if (GetFirstLinePosition(aWM, kid, &kidPosition)) { *aResult = kidPosition + - kid->GetLogicalNormalPosition(aWM, aFrame->GetSize().width).B(aWM); + kid->GetLogicalNormalPosition(aWM, aFrame->GetSize()).B(aWM); return true; } return false; } // No baseline. return false; } @@ -5634,19 +5634,19 @@ nsLayoutUtils::GetFirstLinePosition(Writ line != line_end; ++line) { if (line->IsBlock()) { nsIFrame *kid = line->mFirstChild; LinePosition kidPosition; if (GetFirstLinePosition(aWM, kid, &kidPosition)) { //XXX Not sure if this is the correct value to use for container // width here. It will only be used in vertical-rl layout, // which we don't have full support and testing for yet. - nscoord containerWidth = line->mContainerWidth; + const nsSize& containerSize = line->mContainerSize; *aResult = kidPosition + - kid->GetLogicalNormalPosition(aWM, containerWidth).B(aWM); + kid->GetLogicalNormalPosition(aWM, containerSize).B(aWM); return true; } } else { // XXX Is this the right test? We have some bogus empty lines // floating around, but IsEmpty is perhaps too weak. if (line->BSize() != 0 || !line->IsEmpty()) { nscoord bStart = line->BStart(); aResult->mBStart = bStart; @@ -5669,26 +5669,26 @@ nsLayoutUtils::GetLastLineBaseline(Writi return false; for (nsBlockFrame::const_reverse_line_iterator line = block->rbegin_lines(), line_end = block->rend_lines(); line != line_end; ++line) { if (line->IsBlock()) { nsIFrame *kid = line->mFirstChild; nscoord kidBaseline; - nscoord containerWidth = line->mContainerWidth; + const nsSize& containerSize = line->mContainerSize; if (GetLastLineBaseline(aWM, kid, &kidBaseline)) { // Ignore relative positioning for baseline calculations *aResult = kidBaseline + - kid->GetLogicalNormalPosition(aWM, containerWidth).B(aWM); + kid->GetLogicalNormalPosition(aWM, containerSize).B(aWM); return true; } else if (kid->GetType() == nsGkAtoms::scrollFrame) { // Use the bottom of the scroll frame. // XXX CSS2.1 really doesn't say what to do here. - *aResult = kid->GetLogicalNormalPosition(aWM, containerWidth).B(aWM) + + *aResult = kid->GetLogicalNormalPosition(aWM, containerSize).B(aWM) + kid->BSize(aWM); return true; } } else { // XXX Is this the right test? We have some bogus empty lines // floating around, but IsEmpty is perhaps too weak. if (line->BSize() != 0 || !line->IsEmpty()) { *aResult = line->BStart() + line->GetLogicalAscent(); @@ -5706,19 +5706,19 @@ CalculateBlockContentBEnd(WritingMode aW nscoord contentBEnd = 0; for (nsBlockFrame::line_iterator line = aFrame->begin_lines(), line_end = aFrame->end_lines(); line != line_end; ++line) { if (line->IsBlock()) { nsIFrame* child = line->mFirstChild; - nscoord containerWidth = line->mContainerWidth; + const nsSize& containerSize = line->mContainerSize; nscoord offset = - child->GetLogicalNormalPosition(aWM, containerWidth).B(aWM); + child->GetLogicalNormalPosition(aWM, containerSize).B(aWM); contentBEnd = std::max(contentBEnd, nsLayoutUtils::CalculateContentBEnd(aWM, child) + offset); } else { contentBEnd = std::max(contentBEnd, line->BEnd()); } } @@ -5748,17 +5748,17 @@ nsLayoutUtils::CalculateContentBEnd(Writ nsIFrame::ChildListIterator lists(aFrame); for (; !lists.IsDone(); lists.Next()) { if (!skip.Contains(lists.CurrentID())) { nsFrameList::Enumerator childFrames(lists.CurrentList()); for (; !childFrames.AtEnd(); childFrames.Next()) { nsIFrame* child = childFrames.get(); nscoord offset = child->GetLogicalNormalPosition(aWM, - aFrame->GetSize().width).B(aWM); + aFrame->GetSize()).B(aWM); contentBEnd = std::max(contentBEnd, CalculateContentBEnd(aWM, child) + offset); } } } } return contentBEnd; }
--- a/layout/build/nsLayoutStatics.cpp +++ b/layout/build/nsLayoutStatics.cpp @@ -104,17 +104,16 @@ #endif #include "CubebUtils.h" #include "Latency.h" #include "WebAudioUtils.h" #ifdef MOZ_WIDGET_GONK #include "nsVolumeService.h" -#include "SpeakerManagerService.h" using namespace mozilla::system; #endif #include "nsError.h" #include "nsJSEnvironment.h" #include "nsContentSink.h" #include "nsFrameMessageManager.h" @@ -400,17 +399,16 @@ nsLayoutStatics::Shutdown() #endif CubebUtils::ShutdownLibrary(); AsyncLatencyLogger::ShutdownLogger(); WebAudioUtils::Shutdown(); #ifdef MOZ_WIDGET_GONK nsVolumeService::Shutdown(); - SpeakerManagerService::Shutdown(); #endif #ifdef MOZ_WEBSPEECH nsSynthVoiceRegistry::Shutdown(); #endif nsCORSListenerProxy::Shutdown(); @@ -430,18 +428,16 @@ nsLayoutStatics::Shutdown() HTMLInputElement::DestroyUploadLastDir(); nsLayoutUtils::Shutdown(); nsHyphenationManager::Shutdown(); nsDOMMutationObserver::Shutdown(); - AudioChannelService::Shutdown(); - DataStoreService::Shutdown(); ContentParent::ShutDown(); nsRefreshDriver::Shutdown(); DisplayItemClip::Shutdown();
--- a/layout/forms/nsComboboxControlFrame.cpp +++ b/layout/forms/nsComboboxControlFrame.cpp @@ -458,29 +458,30 @@ nsComboboxControlFrame::ReflowDropdown(n // dropped down int32_t flags = mDroppedDown ? 0 : NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_VISIBILITY | NS_FRAME_NO_SIZE_VIEW; //XXX Can this be different from the dropdown's writing mode? // That would be odd! - // Note that we don't need to pass the true frame position or container width + // Note that we don't need to pass the true frame position or container size // to ReflowChild or FinishReflowChild here; it will be positioned as needed // by AbsolutelyPositionDropDown(). WritingMode outerWM = GetWritingMode(); + const nsSize dummyContainerSize; nsHTMLReflowMetrics desiredSize(aReflowState); nsReflowStatus ignoredStatus; ReflowChild(mDropdownFrame, aPresContext, desiredSize, - kidReflowState, outerWM, LogicalPoint(outerWM), 0, - flags, ignoredStatus); + kidReflowState, outerWM, LogicalPoint(outerWM), + dummyContainerSize, flags, ignoredStatus); // Set the child's width and height to its desired size FinishReflowChild(mDropdownFrame, aPresContext, desiredSize, &kidReflowState, - outerWM, LogicalPoint(outerWM), 0, flags); + outerWM, LogicalPoint(outerWM), dummyContainerSize, flags); } nsPoint nsComboboxControlFrame::GetCSSTransformTranslation() { nsIFrame* frame = this; bool is3DTransform = false; Matrix transform; @@ -570,39 +571,42 @@ nsComboboxControlFrame::GetAvailableDrop // location can change based on whether the dropdown is placed after // or before the display frame. The approach taken here is to get the // absolute position of the display frame and use its location to // determine if the dropdown will go offscreen. // Normal frame geometry (eg GetOffsetTo, mRect) doesn't include transforms. // In the special case that our transform is only a 2D translation we // introduce this hack so that the dropdown will show up in the right place. - *aTranslation = LogicalPoint(aWM, GetCSSTransformTranslation(), 0); + // Use null container size when converting a vector from logical to physical. + const nsSize nullContainerSize; + *aTranslation = LogicalPoint(aWM, GetCSSTransformTranslation(), + nullContainerSize); *aBefore = 0; *aAfter = 0; nsRect screen = nsFormControlFrame::GetUsableScreenRect(PresContext()); - nscoord containerWidth = screen.width; - LogicalRect logicalScreen(aWM, screen, containerWidth); + nsSize containerSize = screen.Size(); + LogicalRect logicalScreen(aWM, screen, containerSize); if (mLastDropDownAfterScreenBCoord == nscoord_MIN) { LogicalRect thisScreenRect(aWM, GetScreenRectInAppUnits(), - containerWidth); + containerSize); mLastDropDownAfterScreenBCoord = thisScreenRect.BEnd(aWM) + aTranslation->B(aWM); mLastDropDownBeforeScreenBCoord = thisScreenRect.BEnd(aWM) + aTranslation->B(aWM); } nscoord minBCoord; nsPresContext* pc = PresContext()->GetToplevelContentDocumentPresContext(); nsIFrame* root = pc ? pc->PresShell()->GetRootFrame() : nullptr; if (root) { minBCoord = LogicalRect(aWM, root->GetScreenRectInAppUnits(), - containerWidth).BStart(aWM); + containerSize).BStart(aWM); if (mLastDropDownAfterScreenBCoord < minBCoord) { // Don't allow the drop-down to be placed before the content area. return; } } else { minBCoord = logicalScreen.BStart(aWM); } @@ -664,22 +668,22 @@ nsComboboxControlFrame::AbsolutelyPositi // Position the drop-down after if there is room, otherwise place it before // if there is room. If there is no room for it on either side then place // it after (to avoid overlapping UI like the URL bar). bool b = dropdownSize.BSize(wm)<= after || dropdownSize.BSize(wm) > before; LogicalPoint dropdownPosition(wm, 0, b ? BSize(wm) : -dropdownSize.BSize(wm)); // Don't position the view unless the position changed since it might cause // a call to NotifyGeometryChange() and an infinite loop here. - nscoord containerWidth = GetRect().width; + nsSize containerSize = GetSize(); const LogicalPoint currentPos = - mDropdownFrame->GetLogicalPosition(containerWidth); + mDropdownFrame->GetLogicalPosition(containerSize); const LogicalPoint newPos = dropdownPosition + translation; if (currentPos != newPos) { - mDropdownFrame->SetPosition(wm, newPos, containerWidth); + mDropdownFrame->SetPosition(wm, newPos, containerSize); nsContainerFrame::PositionFrameView(mDropdownFrame); } return eDropDownPositionFinal; } void nsComboboxControlFrame::NotifyGeometryChange() { @@ -861,32 +865,31 @@ nsComboboxControlFrame::Reflow(nsPresCon } mDisplayISize = aReflowState.ComputedISize() - buttonISize; nsBlockFrame::Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); // The button should occupy the same space as a scrollbar WritingMode wm = aReflowState.GetWritingMode(); - nscoord containerWidth = aReflowState.ComputedWidth() + - aReflowState.ComputedPhysicalBorderPadding().LeftRight(); - LogicalRect buttonRect = mButtonFrame->GetLogicalRect(containerWidth); + nsSize containerSize = aReflowState.ComputedSizeAsContainerIfConstrained(); + LogicalRect buttonRect = mButtonFrame->GetLogicalRect(containerSize); buttonRect.IStart(wm) = aReflowState.ComputedLogicalBorderPadding().IStartEnd(wm) + mDisplayISize - (aReflowState.ComputedLogicalBorderPadding().IEnd(wm) - aReflowState.ComputedLogicalPadding().IEnd(wm)); buttonRect.ISize(wm) = buttonISize; buttonRect.BStart(wm) = this->GetLogicalUsedBorder(wm).BStart(wm); buttonRect.BSize(wm) = mDisplayFrame->BSize(wm) + this->GetLogicalUsedPadding(wm).BStartEnd(wm); - mButtonFrame->SetRect(buttonRect, containerWidth); + mButtonFrame->SetRect(buttonRect, containerSize); if (!NS_INLINE_IS_BREAK_BEFORE(aStatus) && !NS_FRAME_IS_FULLY_COMPLETE(aStatus)) { // This frame didn't fit inside a fragmentation container. Splitting // a nsComboboxControlFrame makes no sense, so we override the status here. aStatus = NS_FRAME_COMPLETE; } }
--- a/layout/forms/nsFieldSetFrame.cpp +++ b/layout/forms/nsFieldSetFrame.cpp @@ -50,23 +50,23 @@ nsFieldSetFrame::GetType() const nsRect nsFieldSetFrame::VisualBorderRectRelativeToSelf() const { WritingMode wm = GetWritingMode(); css::Side legendSide = wm.PhysicalSide(eLogicalSideBStart); nscoord legendBorder = StyleBorder()->GetComputedBorderWidth(legendSide); LogicalRect r(wm, LogicalPoint(wm, 0, 0), GetLogicalSize(wm)); - nscoord containerWidth = r.Width(wm); + nsSize containerSize = r.Size(wm).GetPhysicalSize(wm); if (legendBorder < mLegendRect.BSize(wm)) { nscoord off = (mLegendRect.BSize(wm) - legendBorder) / 2; r.BStart(wm) += off; r.BSize(wm) -= off; } - return r.GetPhysicalRect(wm, containerWidth); + return r.GetPhysicalRect(wm, containerSize); } nsIFrame* nsFieldSetFrame::GetInner() const { nsIFrame* last = mFrames.LastChild(); if (last && last->StyleContext()->GetPseudo() == nsCSSAnonBoxes::fieldsetContent) { @@ -232,57 +232,56 @@ nsFieldSetFrame::PaintBorderBackground(n if (nsIFrame* legend = GetLegend()) { css::Side legendSide = wm.PhysicalSide(eLogicalSideBStart); nscoord legendBorderWidth = StyleBorder()->GetComputedBorderWidth(legendSide); // Use the rect of the legend frame, not mLegendRect, so we draw our // border under the legend's inline-start and -end margins. - LogicalRect legendRect(wm, legend->GetRect() + aPt, rect.width); + LogicalRect legendRect(wm, legend->GetRect() + aPt, rect.Size()); // Compute clipRect using logical coordinates, so that the legend space // will be clipped out of the appropriate physical side depending on mode. - LogicalRect clipRect = LogicalRect(wm, rect, rect.width); + LogicalRect clipRect = LogicalRect(wm, rect, rect.Size()); DrawTarget* drawTarget = aRenderingContext.GetDrawTarget(); gfxContext* gfx = aRenderingContext.ThebesContext(); int32_t appUnitsPerDevPixel = presContext->AppUnitsPerDevPixel(); // draw inline-start portion of the block-start side of the border clipRect.ISize(wm) = legendRect.IStart(wm) - clipRect.IStart(wm); clipRect.BSize(wm) = legendBorderWidth; gfx->Save(); - gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.width), + gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()), appUnitsPerDevPixel, *drawTarget)); nsCSSRendering::PaintBorder(presContext, aRenderingContext, this, aDirtyRect, rect, mStyleContext); gfx->Restore(); // draw inline-end portion of the block-start side of the border - clipRect = LogicalRect(wm, rect, rect.width); + clipRect = LogicalRect(wm, rect, rect.Size()); clipRect.ISize(wm) = clipRect.IEnd(wm) - legendRect.IEnd(wm); clipRect.IStart(wm) = legendRect.IEnd(wm); clipRect.BSize(wm) = legendBorderWidth; gfx->Save(); - gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.width), + gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()), appUnitsPerDevPixel, *drawTarget)); nsCSSRendering::PaintBorder(presContext, aRenderingContext, this, aDirtyRect, rect, mStyleContext); gfx->Restore(); // draw remainder of the border (omitting the block-start side) - clipRect = LogicalRect(wm, rect, rect.width); + clipRect = LogicalRect(wm, rect, rect.Size()); clipRect.BStart(wm) += legendBorderWidth; - clipRect.BSize(wm) = - GetLogicalRect(rect.width).BSize(wm) - (off + legendBorderWidth); + clipRect.BSize(wm) = BSize(wm) - (off + legendBorderWidth); gfx->Save(); - gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.width), + gfx->Clip(NSRectToSnappedRect(clipRect.GetPhysicalRect(wm, rect.Size()), appUnitsPerDevPixel, *drawTarget)); nsCSSRendering::PaintBorder(presContext, aRenderingContext, this, aDirtyRect, rect, mStyleContext); gfx->Restore(); } else { nsCSSRendering::PaintBorder(presContext, aRenderingContext, this, aDirtyRect, nsRect(aPt, mRect.Size()), @@ -444,18 +443,22 @@ nsFieldSetFrame::Reflow(nsPresContext* Maybe<nsHTMLReflowState> legendReflowState; if (legend) { legendReflowState.emplace(aPresContext, aReflowState, legend, legendAvailSize); } if (reflowLegend) { nsHTMLReflowMetrics legendDesiredSize(aReflowState); + // We'll move the legend to its proper place later, so the position + // and containerSize passed here are unimportant. + const nsSize dummyContainerSize; ReflowChild(legend, aPresContext, legendDesiredSize, *legendReflowState, - wm, LogicalPoint(wm), 0, NS_FRAME_NO_MOVE_FRAME, aStatus); + wm, LogicalPoint(wm), dummyContainerSize, + NS_FRAME_NO_MOVE_FRAME, aStatus); #ifdef NOISY_REFLOW printf(" returned (%d, %d)\n", legendDesiredSize.Width(), legendDesiredSize.Height()); #endif // figure out the legend's rectangle legendMargin = legend->GetLogicalUsedMargin(wm); mLegendRect = LogicalRect(wm, 0, 0, @@ -472,31 +475,32 @@ nsFieldSetFrame::Reflow(nsPresContext* } // if the legend space changes then we need to reflow the // content area as well. if (mLegendSpace != oldSpace && inner) { reflowInner = true; } - // We'll move the legend to its proper place later. FinishReflowChild(legend, aPresContext, legendDesiredSize, - legendReflowState.ptr(), wm, LogicalPoint(wm), 0, - NS_FRAME_NO_MOVE_FRAME); + legendReflowState.ptr(), wm, LogicalPoint(wm), + dummyContainerSize, NS_FRAME_NO_MOVE_FRAME); } else if (!legend) { mLegendRect.SetEmpty(); mLegendSpace = 0; } else { // mLegendSpace and mLegendRect haven't changed, but we need // the used margin when placing the legend. legendMargin = legend->GetLogicalUsedMargin(wm); } - nscoord containerWidth = (wm.IsVertical() ? mLegendSpace : 0) + - border.LeftRight(wm); + // This containerSize is incomplete as yet: it does not include the size + // of the |inner| frame itself. + nsSize containerSize = (LogicalSize(wm, 0, mLegendSpace) + + border.Size(wm)).GetPhysicalSize(wm); // reflow the content frame only if needed if (reflowInner) { nsHTMLReflowState kidReflowState(aPresContext, aReflowState, inner, innerAvailSize, nullptr, nsHTMLReflowState::CALLER_WILL_INIT); // Override computed padding, in case it's percentage padding kidReflowState.Init(aPresContext, nullptr, nullptr, &aReflowState.ComputedPhysicalPadding()); @@ -520,36 +524,41 @@ nsFieldSetFrame::Reflow(nsPresContext* nsHTMLReflowMetrics kidDesiredSize(kidReflowState, aDesiredSize.mFlags); // Reflow the frame NS_ASSERTION(kidReflowState.ComputedPhysicalMargin() == nsMargin(0,0,0,0), "Margins on anonymous fieldset child not supported!"); LogicalPoint pt(wm, border.IStart(wm), border.BStart(wm) + mLegendSpace); + // We don't know the correct containerSize until we have reflowed |inner|, + // so we use a dummy value for now; FinishReflowChild will fix the position + // if necessary. + const nsSize dummyContainerSize; ReflowChild(inner, aPresContext, kidDesiredSize, kidReflowState, - wm, pt, containerWidth, 0, aStatus); + wm, pt, dummyContainerSize, 0, aStatus); - // update the container width after reflowing the inner frame + // Update containerSize to account for size of the inner frame, so that + // FinishReflowChild can position it correctly. + containerSize += kidDesiredSize.PhysicalSize(); FinishReflowChild(inner, aPresContext, kidDesiredSize, - &kidReflowState, wm, pt, - containerWidth + kidDesiredSize.Width(), 0); + &kidReflowState, wm, pt, containerSize, 0); NS_FRAME_TRACE_REFLOW_OUT("FieldSet::Reflow", aStatus); - } - - if (inner) { - containerWidth += inner->GetSize().width; + } else if (inner) { + // |inner| didn't need to be reflowed but we do need to include its size + // in containerSize. + containerSize += inner->GetSize(); } LogicalRect contentRect(wm); if (inner) { // We don't support margins on inner, so our content rect is just the - // inner's border-box. We don't care about container-width at this point, - // as we'll figure out the actual positioning later. - contentRect = inner->GetLogicalRect(wm, containerWidth); + // inner's border-box. (We don't really care about container size at this + // point, as we'll figure out the actual positioning later.) + contentRect = inner->GetLogicalRect(wm, containerSize); } // Our content rect must fill up the available width LogicalSize availSize = aReflowState.ComputedSizeWithPadding(wm); if (availSize.ISize(wm) > contentRect.ISize(wm)) { contentRect.ISize(wm) = innerAvailSize.ISize(wm); } @@ -602,19 +611,19 @@ nsFieldSetFrame::Reflow(nsPresContext* // Note that legend's writing mode may be different from the fieldset's, // so we need to convert offsets before applying them to it (bug 1134534). LogicalMargin offsets = legendReflowState->ComputedLogicalOffsets(). ConvertTo(wm, legendReflowState->GetWritingMode()); nsHTMLReflowState::ApplyRelativePositioning(legend, wm, offsets, &actualLegendPos, - containerWidth); + containerSize); - legend->SetPosition(wm, actualLegendPos, containerWidth); + legend->SetPosition(wm, actualLegendPos, containerSize); nsContainerFrame::PositionFrameView(legend); nsContainerFrame::PositionChildViews(legend); } // Return our size and our result. LogicalSize finalSize(wm, contentRect.ISize(wm) + border.IStartEnd(wm), mLegendSpace + border.BStartEnd(wm) + (inner ? inner->BSize(wm) : 0)); @@ -677,11 +686,11 @@ nsFieldSetFrame::AccessibleType() return a11y::eHTMLGroupboxType; } #endif nscoord nsFieldSetFrame::GetLogicalBaseline(WritingMode aWritingMode) const { nsIFrame* inner = GetInner(); - return inner->BStart(aWritingMode, GetParent()->GetSize().width) + + return inner->BStart(aWritingMode, GetParent()->GetSize()) + inner->GetLogicalBaseline(aWritingMode); }
--- a/layout/forms/nsHTMLButtonControlFrame.cpp +++ b/layout/forms/nsHTMLButtonControlFrame.cpp @@ -315,21 +315,22 @@ nsHTMLButtonControlFrame::ReflowButtonCo adjustedButtonReflowState, aFirstKid, availSize); nsReflowStatus contentsReflowStatus; nsHTMLReflowMetrics contentsDesiredSize(aButtonReflowState); childPos.B(wm) = 0; // This will be set properly later, after reflowing the // child to determine its size. - // We just pass 0 for containerWidth here, as the child will be repositioned - // later by FinishReflowChild. + // We just pass a dummy containerSize here, as the child will be + // repositioned later by FinishReflowChild. + nsSize dummyContainerSize; ReflowChild(aFirstKid, aPresContext, contentsDesiredSize, contentsReflowState, - wm, childPos, 0, 0, contentsReflowStatus); + wm, childPos, dummyContainerSize, 0, contentsReflowStatus); MOZ_ASSERT(NS_FRAME_IS_COMPLETE(contentsReflowStatus), "We gave button-contents frame unconstrained available height, " "so it should be complete"); // Compute the button's content-box size: LogicalSize buttonContentBox(wm); if (aButtonReflowState.ComputedBSize() != NS_INTRINSICSIZE) { // Button has a fixed block-size -- that's its content-box bSize. @@ -368,22 +369,23 @@ nsHTMLButtonControlFrame::ReflowButtonCo contentsDesiredSize.BSize(wm); childPos.B(wm) = std::max(0, extraSpace / 2); // Adjust childPos.B() to be in terms of the button's frame-rect, instead of // its focus-padding rect: childPos.B(wm) += focusPadding.BStart(wm) + clbp.BStart(wm); - nscoord containerWidth = buttonContentBox.Width(wm) + clbp.LeftRight(wm); + nsSize containerSize = + (buttonContentBox + clbp.Size(wm)).GetPhysicalSize(wm); // Place the child FinishReflowChild(aFirstKid, aPresContext, contentsDesiredSize, &contentsReflowState, - wm, childPos, containerWidth, 0); + wm, childPos, containerSize, 0); // Make sure we have a useful 'ascent' value for the child if (contentsDesiredSize.BlockStartAscent() == nsHTMLReflowMetrics::ASK_FOR_BASELINE) { WritingMode wm = aButtonReflowState.GetWritingMode(); contentsDesiredSize.SetBlockStartAscent(aFirstKid->GetLogicalBaseline(wm)); }
--- a/layout/forms/nsNumberControlFrame.cpp +++ b/layout/forms/nsNumberControlFrame.cpp @@ -170,18 +170,22 @@ nsNumberControlFrame::Reflow(nsPresConte LogicalPoint wrapperOffset(myWM, aReflowState.ComputedLogicalBorderPadding().IStart(myWM) + wrapperMargin.IStart(myWM), aReflowState.ComputedLogicalBorderPadding().BStart(myWM) + wrapperMargin.BStart(myWM)); nsReflowStatus childStatus; + // We initially reflow the child with a dummy containerSize; positioning + // will be fixed later. + const nsSize dummyContainerSize; ReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize, - wrapperReflowState, myWM, wrapperOffset, 0, 0, childStatus); + wrapperReflowState, myWM, wrapperOffset, dummyContainerSize, 0, + childStatus); MOZ_ASSERT(NS_FRAME_IS_FULLY_COMPLETE(childStatus), "We gave our child unconstrained available block-size, " "so it should be complete"); nscoord wrappersMarginBoxBSize = wrappersDesiredSize.BSize(myWM) + wrapperMargin.BStartEnd(myWM); if (contentBoxBSize == NS_INTRINSICSIZE) { @@ -203,28 +207,31 @@ nsNumberControlFrame::Reflow(nsPresConte aReflowState.ComputedLogicalBorderPadding().BStartEnd(myWM); } // Center child in block axis nscoord extraSpace = contentBoxBSize - wrappersMarginBoxBSize; wrapperOffset.B(myWM) += std::max(0, extraSpace / 2); // Needed in FinishReflowChild, for logical-to-physical conversion: - nscoord borderBoxWidth = myWM.IsVertical() ? - borderBoxBSize : borderBoxISize; + nsSize borderBoxSize = LogicalSize(myWM, borderBoxISize, borderBoxBSize). + GetPhysicalSize(myWM); // Place the child FinishReflowChild(outerWrapperFrame, aPresContext, wrappersDesiredSize, &wrapperReflowState, myWM, wrapperOffset, - borderBoxWidth, 0); + borderBoxSize, 0); + nsSize contentBoxSize = + LogicalSize(myWM, contentBoxISize, contentBoxBSize). + GetPhysicalSize(myWM); aDesiredSize.SetBlockStartAscent( wrappersDesiredSize.BlockStartAscent() + outerWrapperFrame->BStart(aReflowState.GetWritingMode(), - contentBoxISize)); + contentBoxSize)); } LogicalSize logicalDesiredSize(myWM, borderBoxISize, borderBoxBSize); aDesiredSize.SetSize(myWM, logicalDesiredSize); aDesiredSize.SetOverflowAreasToDesiredBounds(); if (outerWrapperFrame) {
--- a/layout/generic/TextOverflow.cpp +++ b/layout/generic/TextOverflow.cpp @@ -258,21 +258,21 @@ nsDisplayTextOverflowMarker::PaintTextTo mStyle->mString.Length(), pt); } } TextOverflow::TextOverflow(nsDisplayListBuilder* aBuilder, nsIFrame* aBlockFrame) : mContentArea(aBlockFrame->GetWritingMode(), aBlockFrame->GetContentRectRelativeToSelf(), - aBlockFrame->GetRect().width) + aBlockFrame->GetSize()) , mBuilder(aBuilder) , mBlock(aBlockFrame) , mScrollableFrame(nsLayoutUtils::GetScrollableFrameFor(aBlockFrame)) - , mBlockWidth(aBlockFrame->GetRect().width) + , mBlockSize(aBlockFrame->GetSize()) , mBlockWM(aBlockFrame->GetWritingMode()) , mAdjustForPixelSnapping(false) { #ifdef MOZ_XUL if (!mScrollableFrame) { nsIAtom* pseudoType = aBlockFrame->StyleContext()->GetPseudo(); if (pseudoType == nsCSSAnonBoxes::mozXULAnonymousBlock) { mScrollableFrame = @@ -291,19 +291,22 @@ TextOverflow::TextOverflow(nsDisplayList mScrollableFrame->GetScrollbarStyles().mVertical : mScrollableFrame->GetScrollbarStyles().mHorizontal; mCanHaveInlineAxisScrollbar = scrollbarStyle != NS_STYLE_OVERFLOW_HIDDEN; if (!mAdjustForPixelSnapping) { // Scrolling to the end position can leave some text still overflowing due // to pixel snapping behaviour in our scrolling code. mAdjustForPixelSnapping = mCanHaveInlineAxisScrollbar; } + // Use a null containerSize to convert a vector from logical to physical. + const nsSize nullContainerSize; mContentArea.MoveBy(mBlockWM, LogicalPoint(mBlockWM, - mScrollableFrame->GetScrollPosition(), 0)); + mScrollableFrame->GetScrollPosition(), + nullContainerSize)); nsIFrame* scrollFrame = do_QueryFrame(mScrollableFrame); scrollFrame->AddStateBits(NS_SCROLLFRAME_INVALIDATE_CONTENTS_ON_SCROLL); } uint8_t direction = aBlockFrame->StyleVisibility()->mDirection; const nsStyleTextReset* style = aBlockFrame->StyleTextReset(); if (mBlockWM.IsBidiLTR()) { mIStart.Init(style->mTextOverflow.GetLeft(direction)); mIEnd.Init(style->mTextOverflow.GetRight(direction)); @@ -389,17 +392,17 @@ TextOverflow::AnalyzeMarkerEdges(nsIFram FrameHashtable* aFramesToHide, AlignmentEdges* aAlignmentEdges, bool* aFoundVisibleTextOrAtomic, InnerClipEdges* aClippedMarkerEdges) { LogicalRect borderRect(mBlockWM, nsRect(aFrame->GetOffsetTo(mBlock), aFrame->GetSize()), - mBlockWidth); + mBlockSize); nscoord istartOverlap = std::max( aInsideMarkersArea.IStart(mBlockWM) - borderRect.IStart(mBlockWM), 0); nscoord iendOverlap = std::max( borderRect.IEnd(mBlockWM) - aInsideMarkersArea.IEnd(mBlockWM), 0); bool insideIStartEdge = aInsideMarkersArea.IStart(mBlockWM) <= borderRect.IEnd(mBlockWM); bool insideIEndEdge = borderRect.IStart(mBlockWM) <= aInsideMarkersArea.IEnd(mBlockWM); @@ -463,36 +466,36 @@ TextOverflow::ExamineLineFrames(nsLineBo FrameHashtable* aFramesToHide, AlignmentEdges* aAlignmentEdges) { // No ellipsing for 'clip' style. bool suppressIStart = mIStart.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP; bool suppressIEnd = mIEnd.mStyle->mType == NS_STYLE_TEXT_OVERFLOW_CLIP; if (mCanHaveInlineAxisScrollbar) { LogicalPoint pos(mBlockWM, mScrollableFrame->GetScrollPosition(), - mBlockWidth); + mBlockSize); LogicalRect scrollRange(mBlockWM, mScrollableFrame->GetScrollRange(), - mBlockWidth); + mBlockSize); // No ellipsing when nothing to scroll to on that side (this includes // overflow:auto that doesn't trigger a horizontal scrollbar). if (pos.I(mBlockWM) <= scrollRange.IStart(mBlockWM)) { suppressIStart = true; } if (pos.I(mBlockWM) >= scrollRange.IEnd(mBlockWM)) { suppressIEnd = true; } } LogicalRect contentArea = mContentArea; const nscoord scrollAdjust = mAdjustForPixelSnapping ? mBlock->PresContext()->AppUnitsPerDevPixel() : 0; InflateIStart(mBlockWM, &contentArea, scrollAdjust); InflateIEnd(mBlockWM, &contentArea, scrollAdjust); LogicalRect lineRect(mBlockWM, aLine->GetScrollableOverflowArea(), - mBlockWidth); + mBlockSize); const bool istartOverflow = !suppressIStart && lineRect.IStart(mBlockWM) < contentArea.IStart(mBlockWM); const bool iendOverflow = !suppressIEnd && lineRect.IEnd(mBlockWM) > contentArea.IEnd(mBlockWM); if (!istartOverflow && !iendOverflow) { // The line does not overflow on a side we should ellipsize. return; } @@ -755,35 +758,35 @@ TextOverflow::CreateMarkers(const nsLine if (aCreateIStart) { DisplayListClipState::AutoSaveRestore clipState(mBuilder); LogicalRect markerLogicalRect( mBlockWM, aInsideMarkersArea.IStart(mBlockWM) - mIStart.mIntrinsicISize, aLine->BStart(), mIStart.mIntrinsicISize, aLine->BSize()); nsPoint offset = mBuilder->ToReferenceFrame(mBlock); nsRect markerRect = - markerLogicalRect.GetPhysicalRect(mBlockWM, mBlockWidth) + offset; - ClipMarker(mContentArea.GetPhysicalRect(mBlockWM, mBlockWidth) + offset, + markerLogicalRect.GetPhysicalRect(mBlockWM, mBlockSize) + offset; + ClipMarker(mContentArea.GetPhysicalRect(mBlockWM, mBlockSize) + offset, markerRect, clipState); nsDisplayItem* marker = new (mBuilder) nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect, aLine->GetLogicalAscent(), mIStart.mStyle, 0); mMarkerList.AppendNewToTop(marker); } if (aCreateIEnd) { DisplayListClipState::AutoSaveRestore clipState(mBuilder); LogicalRect markerLogicalRect( mBlockWM, aInsideMarkersArea.IEnd(mBlockWM), aLine->BStart(), mIEnd.mIntrinsicISize, aLine->BSize()); nsPoint offset = mBuilder->ToReferenceFrame(mBlock); nsRect markerRect = - markerLogicalRect.GetPhysicalRect(mBlockWM, mBlockWidth) + offset; - ClipMarker(mContentArea.GetPhysicalRect(mBlockWM, mBlockWidth) + offset, + markerLogicalRect.GetPhysicalRect(mBlockWM, mBlockSize) + offset; + ClipMarker(mContentArea.GetPhysicalRect(mBlockWM, mBlockSize) + offset, markerRect, clipState); nsDisplayItem* marker = new (mBuilder) nsDisplayTextOverflowMarker(mBuilder, mBlock, markerRect, aLine->GetLogicalAscent(), mIEnd.mStyle, 1); mMarkerList.AppendNewToTop(marker); } }
--- a/layout/generic/TextOverflow.h +++ b/layout/generic/TextOverflow.h @@ -112,17 +112,17 @@ class TextOverflow { }; LogicalRect GetLogicalScrollableOverflowRectRelativeToBlock(nsIFrame* aFrame) const { return LogicalRect(mBlockWM, aFrame->GetScrollableOverflowRect() + aFrame->GetOffsetTo(mBlock), - mBlockWidth); + mBlockSize); } /** * Examines frames on the line to determine whether we should draw a left * and/or right marker, and if so, which frames should be completely hidden * and the bounds of what will be displayed between the markers. * @param aLine the line we're processing * @param aFramesToHide frames that should have their display items removed @@ -205,17 +205,17 @@ class TextOverflow { bool aCreateIStart, bool aCreateIEnd, const LogicalRect& aInsideMarkersArea); LogicalRect mContentArea; nsDisplayListBuilder* mBuilder; nsIFrame* mBlock; nsIScrollableFrame* mScrollableFrame; nsDisplayList mMarkerList; - nscoord mBlockWidth; + nsSize mBlockSize; WritingMode mBlockWM; bool mCanHaveInlineAxisScrollbar; bool mAdjustForPixelSnapping; class Marker { public: void Init(const nsStyleTextOverflowSide& aStyle) { mInitialized = false;
--- a/layout/generic/WritingModes.h +++ b/layout/generic/WritingModes.h @@ -636,64 +636,51 @@ public: : #ifdef DEBUG mWritingMode(aWritingMode), #endif mPoint(aI, aB) { } // Construct from a writing mode and a physical point, within a given - // containing rectangle's width (defining the conversion between LTR - // and RTL coordinates). + // containing rectangle's size (defining the conversion between LTR + // and RTL coordinates, and between TTB and BTT coordinates). LogicalPoint(WritingMode aWritingMode, const nsPoint& aPoint, - nscoord aContainerWidth) + const nsSize& aContainerSize) #ifdef DEBUG : mWritingMode(aWritingMode) #endif { if (aWritingMode.IsVertical()) { - I() = aPoint.y; - B() = aWritingMode.IsVerticalLR() ? aPoint.x : aContainerWidth - aPoint.x; + I() = aWritingMode.IsBidiLTR() ? aPoint.y + : aContainerSize.height - aPoint.y; + B() = aWritingMode.IsVerticalLR() ? aPoint.x + : aContainerSize.width - aPoint.x; } else { - I() = aWritingMode.IsBidiLTR() ? aPoint.x : aContainerWidth - aPoint.x; + I() = aWritingMode.IsBidiLTR() ? aPoint.x + : aContainerSize.width - aPoint.x; B() = aPoint.y; } } /** - * Read-only (const) access to the coordinates, in both logical - * and physical terms. + * Read-only (const) access to the logical coordinates. */ nscoord I(WritingMode aWritingMode) const // inline-axis { CHECK_WRITING_MODE(aWritingMode); return mPoint.x; } nscoord B(WritingMode aWritingMode) const // block-axis { CHECK_WRITING_MODE(aWritingMode); return mPoint.y; } - nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const - { - CHECK_WRITING_MODE(aWritingMode); - if (aWritingMode.IsVertical()) { - return aWritingMode.IsVerticalLR() ? B() : aContainerWidth - B(); - } else { - return aWritingMode.IsBidiLTR() ? I() : aContainerWidth - I(); - } - } - nscoord Y(WritingMode aWritingMode) const - { - CHECK_WRITING_MODE(aWritingMode); - return aWritingMode.IsVertical() ? I() : B(); - } - /** * These non-const accessors return a reference (lvalue) that can be * assigned to by callers. */ nscoord& I(WritingMode aWritingMode) // inline-axis { CHECK_WRITING_MODE(aWritingMode); return mPoint.x; @@ -704,39 +691,42 @@ public: return mPoint.y; } /** * Return a physical point corresponding to our logical coordinates, * converted according to our writing mode. */ nsPoint GetPhysicalPoint(WritingMode aWritingMode, - nscoord aContainerWidth) const + const nsSize& aContainerSize) const { CHECK_WRITING_MODE(aWritingMode); if (aWritingMode.IsVertical()) { - return nsPoint(aWritingMode.IsVerticalLR() ? B() : aContainerWidth - B(), - I()); + return nsPoint(aWritingMode.IsVerticalLR() + ? B() : aContainerSize.width - B(), + aWritingMode.IsBidiLTR() + ? I() : aContainerSize.height - I()); } else { - return nsPoint(aWritingMode.IsBidiLTR() ? I() : aContainerWidth - I(), + return nsPoint(aWritingMode.IsBidiLTR() + ? I() : aContainerSize.width - I(), B()); } } /** * Return the equivalent point in a different writing mode. */ LogicalPoint ConvertTo(WritingMode aToMode, WritingMode aFromMode, - nscoord aContainerWidth) const + const nsSize& aContainerSize) const { CHECK_WRITING_MODE(aFromMode); return aToMode == aFromMode ? *this : LogicalPoint(aToMode, - GetPhysicalPoint(aFromMode, aContainerWidth), - aContainerWidth); + GetPhysicalPoint(aFromMode, aContainerSize), + aContainerSize); } bool operator==(const LogicalPoint& aOther) const { CHECK_WRITING_MODE(aOther.GetWritingMode()); return mPoint == aOther.mPoint; } @@ -940,17 +930,17 @@ public: // expected writing mode CHECK_WRITING_MODE(aFromMode); return aToMode == aFromMode ? *this : LogicalSize(aToMode, GetPhysicalSize(aFromMode)); #else // optimization for non-DEBUG builds where LogicalSize doesn't store // the writing mode return (aToMode == aFromMode || !aToMode.IsOrthogonalTo(aFromMode)) - ? *this : LogicalSize(aToMode, BSize(), ISize()); + ? *this : LogicalSize(aToMode, BSize(), ISize()); #endif } /** * Test if a size is (0, 0). */ bool IsAllZero() const { @@ -1210,26 +1200,26 @@ public: /** * Return an nsMargin containing our physical coordinates */ nsMargin GetPhysicalMargin(WritingMode aWritingMode) const { CHECK_WRITING_MODE(aWritingMode); return aWritingMode.IsVertical() - ? (aWritingMode.IsVerticalLR() - ? (aWritingMode.IsBidiLTR() - ? nsMargin(IStart(), BEnd(), IEnd(), BStart()) - : nsMargin(IEnd(), BEnd(), IStart(), BStart())) - : (aWritingMode.IsBidiLTR() - ? nsMargin(IStart(), BStart(), IEnd(), BEnd()) - : nsMargin(IEnd(), BStart(), IStart(), BEnd()))) - : (aWritingMode.IsBidiLTR() - ? nsMargin(BStart(), IEnd(), BEnd(), IStart()) - : nsMargin(BStart(), IStart(), BEnd(), IEnd())); + ? (aWritingMode.IsVerticalLR() + ? (aWritingMode.IsBidiLTR() + ? nsMargin(IStart(), BEnd(), IEnd(), BStart()) + : nsMargin(IEnd(), BEnd(), IStart(), BStart())) + : (aWritingMode.IsBidiLTR() + ? nsMargin(IStart(), BStart(), IEnd(), BEnd()) + : nsMargin(IEnd(), BStart(), IStart(), BEnd()))) + : (aWritingMode.IsBidiLTR() + ? nsMargin(BStart(), IEnd(), BEnd(), IStart()) + : nsMargin(BStart(), IStart(), BEnd(), IEnd())); } /** * Return a LogicalMargin representing this margin in a different * writing mode */ LogicalMargin ConvertTo(WritingMode aToMode, WritingMode aFromMode) const { @@ -1378,38 +1368,33 @@ public: mRect(aOrigin.mPoint, aSize.mSize) { CHECK_WRITING_MODE(aOrigin.GetWritingMode()); CHECK_WRITING_MODE(aSize.GetWritingMode()); } LogicalRect(WritingMode aWritingMode, const nsRect& aRect, - nscoord aContainerWidth) + const nsSize& aContainerSize) #ifdef DEBUG : mWritingMode(aWritingMode) #endif { if (aWritingMode.IsVertical()) { - if (aWritingMode.IsVerticalLR()) { - mRect.y = aRect.x; - } else { - mRect.y = aContainerWidth - aRect.XMost(); - } + mRect.y = aWritingMode.IsVerticalLR() + ? aRect.x : aContainerSize.width - aRect.XMost(); + mRect.x = aWritingMode.IsBidiLTR() + ? aRect.y : aContainerSize.height - aRect.YMost(); mRect.height = aRect.width; - mRect.x = aRect.y; mRect.width = aRect.height; } else { - if (aWritingMode.IsBidiLTR()) { - mRect.x = aRect.x; - } else { - mRect.x = aContainerWidth - aRect.XMost(); - } + mRect.x = aWritingMode.IsBidiLTR() + ? aRect.x : aContainerSize.width - aRect.XMost(); + mRect.y = aRect.y; mRect.width = aRect.width; - mRect.y = aRect.y; mRect.height = aRect.height; } } /** * Inline- and block-dimension geometry. */ nscoord IStart(WritingMode aWritingMode) const // inline-start edge @@ -1467,35 +1452,37 @@ public: { CHECK_WRITING_MODE(aWritingMode); return mRect.height; } /** * Accessors for line-relative coordinates */ - nscoord LineLeft(WritingMode aWritingMode, nscoord aContainerWidth) const + nscoord LineLeft(WritingMode aWritingMode, + const nsSize& aContainerSize) const { CHECK_WRITING_MODE(aWritingMode); - if (aWritingMode.IsVertical()) { - return IStart(); // sideways-left will require aContainerHeight - } else { - return aWritingMode.IsBidiLTR() ? IStart() - : aContainerWidth - IEnd(); + if (aWritingMode.IsBidiLTR()) { + return IStart(); } + nscoord containerISize = + aWritingMode.IsVertical() ? aContainerSize.height : aContainerSize.width; + return containerISize - IEnd(); } - nscoord LineRight(WritingMode aWritingMode, nscoord aContainerWidth) const + nscoord LineRight(WritingMode aWritingMode, + const nsSize& aContainerSize) const { CHECK_WRITING_MODE(aWritingMode); - if (aWritingMode.IsVertical()) { - return IEnd(); // sideways-left will require aContainerHeight - } else { - return aWritingMode.IsBidiLTR() ? IEnd() - : aContainerWidth - IStart(); + if (aWritingMode.IsBidiLTR()) { + return IEnd(); } + nscoord containerISize = + aWritingMode.IsVertical() ? aContainerSize.height : aContainerSize.width; + return containerISize - IStart(); } /** * Physical coordinates of the rect. */ nscoord X(WritingMode aWritingMode, nscoord aContainerWidth) const { CHECK_WRITING_MODE(aWritingMode); @@ -1503,20 +1490,25 @@ public: return aWritingMode.IsVerticalLR() ? mRect.Y() : aContainerWidth - mRect.YMost(); } else { return aWritingMode.IsBidiLTR() ? mRect.X() : aContainerWidth - mRect.XMost(); } } - nscoord Y(WritingMode aWritingMode) const + nscoord Y(WritingMode aWritingMode, nscoord aContainerHeight) const { CHECK_WRITING_MODE(aWritingMode); - return aWritingMode.IsVertical() ? mRect.X() : mRect.Y(); + if (aWritingMode.IsVertical()) { + return aWritingMode.IsBidiLTR() ? mRect.X() + : aContainerHeight - mRect.XMost(); + } else { + return mRect.Y(); + } } nscoord Width(WritingMode aWritingMode) const { CHECK_WRITING_MODE(aWritingMode); return aWritingMode.IsVertical() ? mRect.Height() : mRect.Width(); } @@ -1533,20 +1525,25 @@ public: return aWritingMode.IsVerticalLR() ? mRect.YMost() : aContainerWidth - mRect.Y(); } else { return aWritingMode.IsBidiLTR() ? mRect.XMost() : aContainerWidth - mRect.X(); } } - nscoord YMost(WritingMode aWritingMode) const + nscoord YMost(WritingMode aWritingMode, nscoord aContainerHeight) const { CHECK_WRITING_MODE(aWritingMode); - return aWritingMode.IsVertical() ? mRect.XMost() : mRect.YMost(); + if (aWritingMode.IsVertical()) { + return aWritingMode.IsBidiLTR() ? mRect.XMost() + : aContainerHeight - mRect.x; + } else { + return mRect.YMost(); + } } bool IsEmpty() const { return mRect.IsEmpty(); } bool IsAllZero() const @@ -1638,43 +1635,45 @@ public: { CHECK_WRITING_MODE(aWritingMode); CHECK_WRITING_MODE(aMargin.GetWritingMode()); mRect.Deflate(aMargin.mMargin); } /** * Return an nsRect containing our physical coordinates within the given - * container width + * container size. */ nsRect GetPhysicalRect(WritingMode aWritingMode, - nscoord aContainerWidth) const + const nsSize& aContainerSize) const { CHECK_WRITING_MODE(aWritingMode); if (aWritingMode.IsVertical()) { - return nsRect(aWritingMode.IsVerticalLR() ? - BStart() : aContainerWidth - BEnd(), - IStart(), BSize(), ISize()); + return nsRect(aWritingMode.IsVerticalLR() + ? BStart() : aContainerSize.width - BEnd(), + aWritingMode.IsBidiLTR() + ? IStart() : aContainerSize.height - IEnd(), + BSize(), ISize()); } else { - return nsRect(aWritingMode.IsBidiLTR() ? - IStart() : aContainerWidth - IEnd(), + return nsRect(aWritingMode.IsBidiLTR() + ? IStart() : aContainerSize.width - IEnd(), BStart(), ISize(), BSize()); } } /** * Return a LogicalRect representing this rect in a different writing mode */ LogicalRect ConvertTo(WritingMode aToMode, WritingMode aFromMode, - nscoord aContainerWidth) const + const nsSize& aContainerSize) const { CHECK_WRITING_MODE(aFromMode); return aToMode == aFromMode ? - *this : LogicalRect(aToMode, GetPhysicalRect(aFromMode, aContainerWidth), - aContainerWidth); + *this : LogicalRect(aToMode, GetPhysicalRect(aFromMode, aContainerSize), + aContainerSize); } /** * Set *this to be the rectangle containing the intersection of aRect1 * and aRect2, return whether the intersection is non-empty. */ bool IntersectRect(const LogicalRect& aRect1, const LogicalRect& aRect2) {
--- a/layout/generic/nsAbsoluteContainingBlock.cpp +++ b/layout/generic/nsAbsoluteContainingBlock.cpp @@ -403,17 +403,17 @@ nsAbsoluteContainingBlock::ReflowAbsolut aReflowState.mStyleBorder->GetComputedBorder()); const LogicalMargin margin = kidReflowState.ComputedLogicalMargin().ConvertTo(outerWM, wm); bool constrainBSize = (aReflowState.AvailableBSize() != NS_UNCONSTRAINEDSIZE) && aConstrainBSize // Don't split if told not to (e.g. for fixed frames) && (aDelegatingFrame->GetType() != nsGkAtoms::inlineFrame) //XXX we don't handle splitting frames for inline absolute containing blocks yet - && (aKidFrame->GetLogicalRect(aContainingBlock.width).BStart(wm) <= + && (aKidFrame->GetLogicalRect(aContainingBlock.Size()).BStart(wm) <= aReflowState.AvailableBSize()); // Don't split things below the fold. (Ideally we shouldn't *have* // anything totally below the fold, but we can't position frames // across next-in-flow breaks yet. if (constrainBSize) { kidReflowState.AvailableBSize() = aReflowState.AvailableBSize() - border.ConvertTo(wm, outerWM).BStart(wm) - kidReflowState.ComputedLogicalMargin().BStart(wm); @@ -463,22 +463,18 @@ nsAbsoluteContainingBlock::ReflowAbsolut // Position the child relative to our padding edge LogicalRect rect(outerWM, border.IStart(outerWM) + offsets.IStart(outerWM) + margin.IStart(outerWM), border.BStart(outerWM) + offsets.BStart(outerWM) + margin.BStart(outerWM), kidSize.ISize(outerWM), kidSize.BSize(outerWM)); nsRect r = - rect.GetPhysicalRect(outerWM, logicalCBSize.Width(wm) + - border.LeftRight(outerWM)); - // XXX hack to correct for lack of bidi support in vertical mode - if (outerWM.IsVertical() && !outerWM.IsBidiLTR()) { - r.y = logicalCBSize.Height(wm) + border.TopBottom(outerWM) - r.YMost(); - } + rect.GetPhysicalRect(outerWM, logicalCBSize.GetPhysicalSize(wm) + + border.Size(outerWM).GetPhysicalSize(outerWM)); // Offset the frame rect by the given origin of the absolute containing block. // If the frame is auto-positioned on both sides of an axis, it will be // positioned based on its containing block and we don't need to offset. if (aContainingBlock.TopLeft() != nsPoint(0, 0)) { const nsStyleSides& offsets = kidReflowState.mStylePosition->mOffset; if (!(offsets.GetLeftUnit() == eStyleUnit_Auto && offsets.GetRightUnit() == eStyleUnit_Auto)) {
--- a/layout/generic/nsBlockFrame.cpp +++ b/layout/generic/nsBlockFrame.cpp @@ -1239,50 +1239,49 @@ nsBlockFrame::Reflow(nsPresContext* if (havePosition && !BulletIsEmpty()) { // We have some lines to align the bullet with. // Doing the alignment using the baseline will also cater for // bullets that are placed next to a child block (bug 92896) // Tall bullets won't look particularly nice here... - LogicalRect bbox = bullet->GetLogicalRect(wm, metrics.Width()); + LogicalRect bbox = bullet->GetLogicalRect(wm, metrics.PhysicalSize()); bbox.BStart(wm) = position.mBaseline - metrics.BlockStartAscent(); - bullet->SetRect(wm, bbox, metrics.Width()); + bullet->SetRect(wm, bbox, metrics.PhysicalSize()); } // Otherwise just leave the bullet where it is, up against our // block-start padding. } CheckFloats(state); // Compute our final size nscoord blockEndEdgeOfChildren; ComputeFinalSize(*reflowState, state, aMetrics, &blockEndEdgeOfChildren); // If the block direction is right-to-left, we need to update the bounds of - // lines that were placed relative to mContainerWidth during reflow, as - // we typically do not know the true container width (block-dir size of the - // finished paragraph/block) until we've reflowed all its children. So we - // use a "fake" mContainerWidth during reflow (see nsBlockReflowState's - // constructor) and then fix up the positions of the lines here, once the - // final block size is known. + // lines that were placed relative to mContainerSize during reflow, as + // we typically do not know the true container size until we've reflowed all + // its children. So we use a dummy mContainerSize during reflow (see + // nsBlockReflowState's constructor) and then fix up the positions of the + // lines here, once the final block size is known. // // Note that writing-mode:vertical-rl is the only case where the block // logical direction progresses in a negative physical direction, and // therefore block-dir coordinate conversion depends on knowing the width // of the coordinate space in order to translate between the logical and // physical origins. if (wm.IsVerticalRL()) { - nscoord containerWidth = aMetrics.Width(); - nscoord deltaX = containerWidth - state.ContainerWidth(); - if (deltaX) { + nsSize containerSize = aMetrics.PhysicalSize(); + nscoord deltaX = containerSize.width - state.ContainerSize().width; + if (deltaX != 0) { for (line_iterator line = begin_lines(), end = end_lines(); line != end; line++) { - UpdateLineContainerWidth(line, containerWidth); + UpdateLineContainerSize(line, containerSize); } for (nsIFrame* f : mFloats) { nsPoint physicalDelta(deltaX, 0); f->MovePositionBy(physicalDelta); } nsFrameList* bulletList = GetOutsideBulletList(); if (bulletList) { nsPoint physicalDelta(deltaX, 0); @@ -1928,19 +1927,19 @@ nsBlockFrame::PropagateFloatDamage(nsBlo if (floatManager->HasFloatDamage()) { // Need to check mBounds *and* mCombinedArea to find intersections // with aLine's floats nscoord lineBCoordBefore = aLine->BStart() + aDeltaBCoord; nscoord lineBCoordAfter = lineBCoordBefore + aLine->BSize(); // Scrollable overflow should be sufficient for things that affect // layout. WritingMode wm = aState.mReflowState.GetWritingMode(); - nscoord containerWidth = aState.ContainerWidth(); + nsSize containerSize = aState.ContainerSize(); LogicalRect overflow = aLine->GetOverflowArea(eScrollableOverflow, wm, - containerWidth); + containerSize); nscoord lineBCoordCombinedBefore = overflow.BStart(wm) + aDeltaBCoord; nscoord lineBCoordCombinedAfter = lineBCoordCombinedBefore + overflow.BSize(wm); bool isDirty = floatManager->IntersectsDamage(wm, lineBCoordBefore, lineBCoordAfter) || floatManager->IntersectsDamage(wm, lineBCoordCombinedBefore, lineBCoordCombinedAfter); @@ -2171,21 +2170,21 @@ nsBlockFrame::ReflowDirtyLines(nsBlockRe } if (!line->IsDirty()) { // See if there's any reflow damage that requires that we mark the // line dirty. PropagateFloatDamage(aState, line, deltaBCoord); } - // If the container width has changed reset the container width. If the + // If the container size has changed, reset mContainerSize. If the // line's writing mode is not ltr, or if the line is not left-aligned, also // mark the line dirty. - if (aState.ContainerWidth() != line->mContainerWidth) { - line->mContainerWidth = aState.ContainerWidth(); + if (aState.ContainerSize() != line->mContainerSize) { + line->mContainerSize = aState.ContainerSize(); bool isLastLine = line == mLines.back() && !GetNextInFlow() && NS_STYLE_TEXT_ALIGN_AUTO == StyleText()->mTextAlignLast; uint8_t align = isLastLine ? StyleText()->mTextAlign : StyleText()->mTextAlignLast; if (line->mWritingMode.IsVertical() || @@ -2812,36 +2811,36 @@ nsBlockFrame::PullFrameFrom(nsLineBox* void nsBlockFrame::SlideLine(nsBlockReflowState& aState, nsLineBox* aLine, nscoord aDeltaBCoord) { NS_PRECONDITION(aDeltaBCoord != 0, "why slide a line nowhere?"); // Adjust line state - aLine->SlideBy(aDeltaBCoord, aState.ContainerWidth()); + aLine->SlideBy(aDeltaBCoord, aState.ContainerSize()); // Adjust the frames in the line MoveChildFramesOfLine(aLine, aDeltaBCoord); } void -nsBlockFrame::UpdateLineContainerWidth(nsLineBox* aLine, - nscoord aNewContainerWidth) -{ - if (aNewContainerWidth == aLine->mContainerWidth) { +nsBlockFrame::UpdateLineContainerSize(nsLineBox* aLine, + const nsSize& aNewContainerSize) +{ + if (aNewContainerSize == aLine->mContainerSize) { return; } // Adjust line state - nscoord widthDelta = aLine->UpdateContainerWidth(aNewContainerWidth); + nsSize sizeDelta = aLine->UpdateContainerSize(aNewContainerSize); // Changing container width only matters if writing mode is vertical-rl if (GetWritingMode().IsVerticalRL()) { - MoveChildFramesOfLine(aLine, widthDelta); + MoveChildFramesOfLine(aLine, sizeDelta.width); } } void nsBlockFrame::MoveChildFramesOfLine(nsLineBox* aLine, nscoord aDeltaBCoord) { // Adjust the frames in the line nsIFrame* kid = aLine->mFirstChild; @@ -3677,17 +3676,17 @@ nsBlockFrame::DoReflowInlineFrames(nsBlo printf("nsBlockFrame::DoReflowInlineFrames %p impacted = %d\n", this, aFloatAvailableSpace.mHasFloats); #endif WritingMode outerWM = aState.mReflowState.GetWritingMode(); WritingMode lineWM = GetWritingMode(aLine->mFirstChild); LogicalRect lineRect = aFloatAvailableSpace.mRect.ConvertTo(lineWM, outerWM, - aState.ContainerWidth()); + aState.ContainerSize()); nscoord iStart = lineRect.IStart(lineWM); nscoord availISize = lineRect.ISize(lineWM); nscoord availBSize; if (aState.GetFlag(BRS_UNCONSTRAINEDBSIZE)) { availBSize = NS_UNCONSTRAINEDSIZE; } else { @@ -6150,64 +6149,64 @@ nsBlockFrame::ReflowPushedFloats(nsBlock if (prevBlock) { aState.mFloatBreakType = prevBlock->FindTrailingClear(); } } } void nsBlockFrame::RecoverFloats(nsFloatManager& aFloatManager, WritingMode aWM, - nscoord aContainerWidth) + const nsSize& aContainerSize) { // Recover our own floats nsIFrame* stop = nullptr; // Stop before we reach pushed floats that // belong to our next-in-flow for (nsIFrame* f = mFloats.FirstChild(); f && f != stop; f = f->GetNextSibling()) { - LogicalRect region = nsFloatManager::GetRegionFor(aWM, f, aContainerWidth); - aFloatManager.AddFloat(f, region, aWM, aContainerWidth); + LogicalRect region = nsFloatManager::GetRegionFor(aWM, f, aContainerSize); + aFloatManager.AddFloat(f, region, aWM, aContainerSize); if (!stop && f->GetNextInFlow()) stop = f->GetNextInFlow(); } // Recurse into our overflow container children for (nsIFrame* oc = GetFirstChild(kOverflowContainersList); oc; oc = oc->GetNextSibling()) { - RecoverFloatsFor(oc, aFloatManager, aWM, aContainerWidth); + RecoverFloatsFor(oc, aFloatManager, aWM, aContainerSize); } // Recurse into our normal children for (nsBlockFrame::line_iterator line = begin_lines(); line != end_lines(); ++line) { if (line->IsBlock()) { - RecoverFloatsFor(line->mFirstChild, aFloatManager, aWM, aContainerWidth); + RecoverFloatsFor(line->mFirstChild, aFloatManager, aWM, aContainerSize); } } } void nsBlockFrame::RecoverFloatsFor(nsIFrame* aFrame, nsFloatManager& aFloatManager, WritingMode aWM, - nscoord aContainerWidth) + const nsSize& aContainerSize) { NS_PRECONDITION(aFrame, "null frame"); // Only blocks have floats nsBlockFrame* block = nsLayoutUtils::GetAsBlock(aFrame); // Don't recover any state inside a block that has its own space manager // (we don't currently have any blocks like this, though, thanks to our // use of extra frames for 'overflow') if (block && !nsBlockFrame::BlockNeedsFloatManager(block)) { // If the element is relatively positioned, then adjust x and y // accordingly so that we consider relatively positioned frames // at their original position. - LogicalRect rect(aWM, block->GetNormalRect(), aContainerWidth); - nscoord lineLeft = rect.LineLeft(aWM, aContainerWidth); + LogicalRect rect(aWM, block->GetNormalRect(), aContainerSize); + nscoord lineLeft = rect.LineLeft(aWM, aContainerSize); nscoord blockStart = rect.BStart(aWM); aFloatManager.Translate(lineLeft, blockStart); - block->RecoverFloats(aFloatManager, aWM, aContainerWidth); + block->RecoverFloats(aFloatManager, aWM, aContainerSize); aFloatManager.Translate(-lineLeft, -blockStart); } } ////////////////////////////////////////////////////////////////////// // Painting, event handling #ifdef DEBUG @@ -7046,17 +7045,17 @@ nsBlockFrame::ReflowBullet(nsIFrame* aBu // Approximate the bullets position; vertical alignment will provide // the final vertical location. We pass our writing-mode here, because // it may be different from the bullet frame's mode. nscoord bStart = floatAvailSpace.BStart(wm); aBulletFrame->SetRect(wm, LogicalRect(wm, iStart, bStart, aMetrics.ISize(wm), aMetrics.BSize(wm)), - aState.ContainerWidth()); + aState.ContainerSize()); aBulletFrame->DidReflow(aState.mPresContext, &aState.mReflowState, nsDidReflowStatus::FINISHED); } // This is used to scan frames for any float placeholders, add their // floats to the list represented by aList, and remove the // floats from whatever list they might be in. We don't search descendants // that are float containing blocks. Floats that or not children of 'this'
--- a/layout/generic/nsBlockFrame.h +++ b/layout/generic/nsBlockFrame.h @@ -408,20 +408,20 @@ protected: } /** move the frames contained by aLine by aDeltaBCoord * if aLine is a block, its child floats are added to the state manager */ void SlideLine(nsBlockReflowState& aState, nsLineBox* aLine, nscoord aDeltaBCoord); - void UpdateLineContainerWidth(nsLineBox* aLine, - nscoord aNewContainerWidth); + void UpdateLineContainerSize(nsLineBox* aLine, + const nsSize& aNewContainerSize); - // helper for SlideLine and UpdateLineContainerWidth + // helper for SlideLine and UpdateLineContainerSize void MoveChildFramesOfLine(nsLineBox* aLine, nscoord aDeltaBCoord); void ComputeFinalSize(const nsHTMLReflowState& aReflowState, nsBlockReflowState& aState, nsHTMLReflowMetrics& aMetrics, nscoord* aBottomEdgeOfChildren); void ComputeOverflowAreas(const nsRect& aBounds, @@ -479,17 +479,17 @@ public: /** Load all of aFrame's floats into the float manager iff aFrame is not a * block formatting context. Handles all necessary float manager translations; * assumes float manager is in aFrame's parent's coord system. * Safe to call on non-blocks (does nothing). */ static void RecoverFloatsFor(nsIFrame* aFrame, nsFloatManager& aFloatManager, mozilla::WritingMode aWM, - nscoord aContainerWidth); + const nsSize& aContainerSize); /** * Determine if we have any pushed floats from a previous continuation. * * @returns true, if any of the floats at the beginning of our mFloats list * have the NS_FRAME_IS_PUSHED_FLOAT bit set; false otherwise. */ bool HasPushedFloatsFromPrevContinuation() const { @@ -551,17 +551,17 @@ protected: */ void DrainPushedFloats(); /** Load all our floats into the float manager (without reflowing them). * Assumes float manager is in our own coordinate system. */ void RecoverFloats(nsFloatManager& aFloatManager, mozilla::WritingMode aWM, - nscoord aContainerWidth); + const nsSize& aContainerSize); /** Reflow pushed floats */ void ReflowPushedFloats(nsBlockReflowState& aState, nsOverflowAreas& aOverflowAreas, nsReflowStatus& aStatus); /** Find any trailing BR clear from the last line of the block (or its PIFs)
--- a/layout/generic/nsBlockReflowContext.cpp +++ b/layout/generic/nsBlockReflowContext.cpp @@ -223,17 +223,17 @@ nsBlockReflowContext::ReflowBlock(const bool aIsAdjacentWithBStart, nsLineBox* aLine, nsHTMLReflowState& aFrameRS, nsReflowStatus& aFrameReflowStatus, nsBlockReflowState& aState) { mFrame = aFrameRS.frame; mWritingMode = aState.mReflowState.GetWritingMode(); - mContainerWidth = aState.ContainerWidth(); + mContainerSize = aState.ContainerSize(); mSpace = aSpace; if (!aIsAdjacentWithBStart) { aFrameRS.mFlags.mIsTopOfPage = false; // make sure this is cleared } if (aApplyBStartMargin) { mBStartMargin = aPrevMargin; @@ -275,17 +275,17 @@ nsBlockReflowContext::ReflowBlock(const mICoord = mSpace.IStart(mWritingMode) + usedMargin.IStart(mWritingMode); mBCoord = mSpace.BStart(mWritingMode) + mBStartMargin.get() + aClearance; LogicalRect space(mWritingMode, mICoord, mBCoord, mSpace.ISize(mWritingMode) - usedMargin.IStartEnd(mWritingMode), mSpace.BSize(mWritingMode) - usedMargin.BStartEnd(mWritingMode)); - tI = space.LineLeft(mWritingMode, mContainerWidth); + tI = space.LineLeft(mWritingMode, mContainerSize); tB = mBCoord; if ((mFrame->GetStateBits() & NS_BLOCK_FLOAT_MGR) == 0) aFrameRS.mBlockDelta = mOuterReflowState.mBlockDelta + mBCoord - aLine->BStart(); } #ifdef DEBUG @@ -425,29 +425,30 @@ nsBlockReflowContext::PlaceBlock(const n nsDidReflowStatus::FINISHED); return false; } } aLine->SetBounds(mWritingMode, mICoord, mBCoord - backupContainingBlockAdvance, mMetrics.ISize(mWritingMode), mMetrics.BSize(mWritingMode), - mContainerWidth); + mContainerSize); WritingMode frameWM = mFrame->GetWritingMode(); LogicalPoint logPos = LogicalPoint(mWritingMode, mICoord, mBCoord). - ConvertTo(frameWM, mWritingMode, mContainerWidth - mMetrics.Width()); + ConvertTo(frameWM, mWritingMode, + mContainerSize - mMetrics.PhysicalSize()); // ApplyRelativePositioning in right-to-left writing modes needs to // know the updated frame width mFrame->SetSize(mWritingMode, mMetrics.Size(mWritingMode)); - aReflowState.ApplyRelativePositioning(&logPos, mContainerWidth); + aReflowState.ApplyRelativePositioning(&logPos, mContainerSize); // Now place the frame and complete the reflow process nsContainerFrame::FinishReflowChild(mFrame, mPresContext, mMetrics, &aReflowState, frameWM, logPos, - mContainerWidth, 0); + mContainerSize, 0); aOverflowAreas = mMetrics.mOverflowAreas + mFrame->GetPosition(); return true; }
--- a/layout/generic/nsBlockReflowContext.h +++ b/layout/generic/nsBlockReflowContext.h @@ -78,15 +78,16 @@ public: protected: nsPresContext* mPresContext; const nsHTMLReflowState& mOuterReflowState; nsIFrame* mFrame; mozilla::LogicalRect mSpace; - nscoord mICoord, mBCoord, mContainerWidth; + nscoord mICoord, mBCoord; + nsSize mContainerSize; mozilla::WritingMode mWritingMode; nsHTMLReflowMetrics mMetrics; nsCollapsingMargin mBStartMargin; }; #endif /* nsBlockReflowContext_h___ */
--- a/layout/generic/nsBlockReflowState.cpp +++ b/layout/generic/nsBlockReflowState.cpp @@ -317,17 +317,17 @@ nsBlockReflowState::GetFloatAvailableSpa "bad coord system"); #endif nscoord blockSize = (mContentArea.BSize(wm) == nscoord_MAX) ? nscoord_MAX : std::max(mContentArea.BEnd(wm) - aBCoord, 0); nsFlowAreaRect result = mFloatManager->GetFlowArea(wm, aBCoord, nsFloatManager::BAND_FROM_POINT, blockSize, mContentArea, aState, - ContainerWidth()); + ContainerSize()); // Keep the inline size >= 0 for compatibility with nsSpaceManager. if (result.mRect.ISize(wm) < 0) { result.mRect.ISize(wm) = 0; } #ifdef DEBUG if (nsBlockFrame::gNoisyReflow) { nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); @@ -350,17 +350,17 @@ nsBlockReflowState::GetFloatAvailableSpa nscoord wI, wB; mFloatManager->GetTranslation(wI, wB); NS_ASSERTION((wI == mFloatManagerI) && (wB == mFloatManagerB), "bad coord system"); #endif nsFlowAreaRect result = mFloatManager->GetFlowArea(wm, aBCoord, nsFloatManager::WIDTH_WITHIN_HEIGHT, - aBSize, mContentArea, aState, ContainerWidth()); + aBSize, mContentArea, aState, ContainerSize()); // Keep the width >= 0 for compatibility with nsSpaceManager. if (result.mRect.ISize(wm) < 0) { result.mRect.ISize(wm) = 0; } #ifdef DEBUG if (nsBlockFrame::gNoisyReflow) { nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); @@ -474,31 +474,31 @@ nsBlockReflowState::RecoverFloats(nsLine if (nsBlockFrame::gNoisyReflow || nsBlockFrame::gNoisyFloatManager) { nscoord tI, tB; mFloatManager->GetTranslation(tI, tB); nsFrame::IndentBy(stdout, nsBlockFrame::gNoiseIndent); printf("RecoverFloats: tIB=%d,%d (%d,%d) ", tI, tB, mFloatManagerI, mFloatManagerB); nsFrame::ListTag(stdout, floatFrame); LogicalRect region = nsFloatManager::GetRegionFor(wm, floatFrame, - ContainerWidth()); + ContainerSize()); printf(" aDeltaBCoord=%d region={%d,%d,%d,%d}\n", aDeltaBCoord, region.IStart(wm), region.BStart(wm), region.ISize(wm), region.BSize(wm)); } #endif mFloatManager->AddFloat(floatFrame, nsFloatManager::GetRegionFor(wm, floatFrame, - ContainerWidth()), - wm, ContainerWidth()); + ContainerSize()), + wm, ContainerSize()); fc = fc->Next(); } } else if (aLine->IsBlock()) { nsBlockFrame::RecoverFloatsFor(aLine->mFirstChild, *mFloatManager, wm, - ContainerWidth()); + ContainerSize()); } } /** * Everything done in this function is done O(N) times for each pass of * reflow so it is O(N*M) where M is the number of incremental reflow * passes. That's bad. Don't do stuff here. * @@ -695,17 +695,17 @@ nsBlockReflowState::FlowAndPlaceFloat(ns // FIXME: Should give AutoRestore a getter for the value to avoid this. const nscoord saveBCoord = mBCoord; // Grab the float's display information const nsStyleDisplay* floatDisplay = aFloat->StyleDisplay(); // The float's old region, so we can propagate damage. LogicalRect oldRegion = nsFloatManager::GetRegionFor(wm, aFloat, - ContainerWidth()); + ContainerSize()); // Enforce CSS2 9.5.1 rule [2], i.e., make sure that a float isn't // ``above'' another float that preceded it in the flow. mBCoord = std::max(mFloatManager->GetLowestFloatTop(), mBCoord); // See if the float should clear any preceding floats... // XXX We need to mark this float somehow so that it gets reflowed // when floats are inserted before it. @@ -847,25 +847,17 @@ nsBlockReflowState::FlowAndPlaceFloat(ns // place and size itself as required. // Assign inline and block dir coordinates to the float. We don't use // LineLeft() and LineRight() here, because we would only have to // convert the result back into this block's writing mode. LogicalPoint floatPos(wm); bool leftFloat = NS_STYLE_FLOAT_LEFT == floatDisplay->mFloats; - if (wm.IsVertical()) { - // IStart and IEnd should use the ContainerHeight in vertical modes - // with rtl direction. Since they don't yet (bug 1131451), we'll - // just put left floats at the top of the line and right floats at - // bottom. - floatPos.I(wm) = leftFloat - ? floatAvailableSpace.mRect.Y(wm) - : floatAvailableSpace.mRect.YMost(wm) - floatMarginISize; - } else if (leftFloat == wm.IsBidiLTR()) { + if (leftFloat == wm.IsBidiLTR()) { floatPos.I(wm) = floatAvailableSpace.mRect.IStart(wm); } else { if (!keepFloatOnSameLine) { floatPos.I(wm) = floatAvailableSpace.mRect.IEnd(wm) - floatMarginISize; } else { // this is the IE quirk (see few lines above) @@ -930,49 +922,49 @@ nsBlockReflowState::FlowAndPlaceFloat(ns // Calculate the actual origin of the float frame's border rect // relative to the parent block; the margin must be added in // to get the border rect LogicalPoint origin(wm, floatMargin.IStart(wm) + floatPos.I(wm), floatMargin.BStart(wm) + floatPos.B(wm)); // If float is relatively positioned, factor that in as well nsHTMLReflowState::ApplyRelativePositioning(aFloat, wm, floatOffsets, - &origin, ContainerWidth()); + &origin, ContainerSize()); // Position the float and make sure and views are properly // positioned. We need to explicitly position its child views as // well, since we're moving the float after flowing it. - bool moved = aFloat->GetLogicalPosition(wm, ContainerWidth()) != origin; + bool moved = aFloat->GetLogicalPosition(wm, ContainerSize()) != origin; if (moved) { - aFloat->SetPosition(wm, origin, ContainerWidth()); + aFloat->SetPosition(wm, origin, ContainerSize()); nsContainerFrame::PositionFrameView(aFloat); nsContainerFrame::PositionChildViews(aFloat); } // Update the float combined area state // XXX Floats should really just get invalidated here if necessary mFloatOverflowAreas.UnionWith(aFloat->GetOverflowAreas() + aFloat->GetPosition()); // Place the float in the float manager // calculate region LogicalRect region = nsFloatManager::CalculateRegionFor(wm, aFloat, floatMargin, - ContainerWidth()); + ContainerSize()); // if the float split, then take up all of the vertical height if (NS_FRAME_IS_NOT_COMPLETE(reflowStatus) && (NS_UNCONSTRAINEDSIZE != ContentBSize())) { region.BSize(wm) = std::max(region.BSize(wm), ContentBSize() - floatPos.B(wm)); } DebugOnly<nsresult> rv = mFloatManager->AddFloat(aFloat, region, wm, - ContainerWidth()); + ContainerSize()); MOZ_ASSERT(NS_SUCCEEDED(rv), "bad float placement"); // store region - nsFloatManager::StoreRegionFor(wm, aFloat, region, ContainerWidth()); + nsFloatManager::StoreRegionFor(wm, aFloat, region, ContainerSize()); // If the float's dimensions have changed, note the damage in the // float manager. if (!region.IsEqualEdges(oldRegion)) { // XXXwaterson conservative: we could probably get away with noting // less damage; e.g., if only height has changed, then only note the // area into which the float has grown or from which the float has // shrunk.
--- a/layout/generic/nsBlockReflowState.h +++ b/layout/generic/nsBlockReflowState.h @@ -202,18 +202,17 @@ public: } mozilla::LogicalSize ContentSize(mozilla::WritingMode aWM) const { mozilla::WritingMode wm = mReflowState.GetWritingMode(); return mContentArea.Size(wm).ConvertTo(aWM, wm); } // Physical size. Use only for physical <-> logical coordinate conversion. nsSize mContainerSize; - nscoord ContainerWidth() const { return mContainerSize.width; } - nscoord ContainerHeight() const { return mContainerSize.height; } + const nsSize& ContainerSize() const { return mContainerSize; } // Continuation out-of-flow float frames that need to move to our // next in flow are placed here during reflow. It's a pointer to // a frame list stored in the block's property table. nsFrameList *mPushedFloats; // This method makes sure pushed floats are accessible to // StealFrame. Call it before adding any frames to mPushedFloats. void SetupPushedFloatList();
--- a/layout/generic/nsCanvasFrame.cpp +++ b/layout/generic/nsCanvasFrame.cpp @@ -660,30 +660,30 @@ nsCanvasFrame::Reflow(nsPresContext* (kidFrame->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE)) { // Tell our kid it's being block-dir resized too. Bit of a // hack for framesets. kidReflowState.SetBResize(true); } WritingMode wm = aReflowState.GetWritingMode(); WritingMode kidWM = kidReflowState.GetWritingMode(); - nscoord containerWidth = aReflowState.ComputedWidth(); + nsSize containerSize = aReflowState.ComputedPhysicalSize(); LogicalMargin margin = kidReflowState.ComputedLogicalMargin(); LogicalPoint kidPt(kidWM, margin.IStart(kidWM), margin.BStart(kidWM)); - kidReflowState.ApplyRelativePositioning(&kidPt, containerWidth); + kidReflowState.ApplyRelativePositioning(&kidPt, containerSize); // Reflow the frame ReflowChild(kidFrame, aPresContext, kidDesiredSize, kidReflowState, - kidWM, kidPt, containerWidth, 0, aStatus); + kidWM, kidPt, containerSize, 0, aStatus); // Complete the reflow and position and size the child frame FinishReflowChild(kidFrame, aPresContext, kidDesiredSize, &kidReflowState, - kidWM, kidPt, containerWidth, 0); + kidWM, kidPt, containerSize, 0); if (!NS_FRAME_IS_FULLY_COMPLETE(aStatus)) { nsIFrame* nextFrame = kidFrame->GetNextInFlow(); NS_ASSERTION(nextFrame || aStatus & NS_FRAME_REFLOW_NEXTINFLOW, "If it's incomplete and has no nif yet, it must flag a nif reflow."); if (!nextFrame) { nextFrame = aPresContext->PresShell()->FrameConstructor()-> CreateContinuingFrame(aPresContext, kidFrame, this);
--- a/layout/generic/nsColumnSetFrame.cpp +++ b/layout/generic/nsColumnSetFrame.cpp @@ -355,23 +355,23 @@ nsColumnSetFrame::ReflowColumns(nsHTMLRe aConfig, aLastColumnUnbounded, aCarriedOutBEndMargin, aColData); } return feasible; } static void MoveChildTo(nsIFrame* aChild, LogicalPoint aOrigin, - WritingMode aWM, nscoord aContainerWidth) + WritingMode aWM, const nsSize& aContainerSize) { - if (aChild->GetLogicalPosition(aWM, aContainerWidth) == aOrigin) { + if (aChild->GetLogicalPosition(aWM, aContainerSize) == aOrigin) { return; } - aChild->SetPosition(aWM, aOrigin, aContainerWidth); + aChild->SetPosition(aWM, aOrigin, aContainerSize); nsContainerFrame::PlaceFrameView(aChild); } nscoord nsColumnSetFrame::GetMinISize(nsRenderingContext *aRenderingContext) { nscoord iSize = 0; DISPLAY_MIN_WIDTH(this, iSize); @@ -483,35 +483,37 @@ nsColumnSetFrame::ReflowChildren(nsHTMLR borderPadding.ApplySkipSides(GetLogicalSkipSides(&aReflowState)); nsRect contentRect(0, 0, 0, 0); nsOverflowAreas overflowRects; nsIFrame* child = mFrames.FirstChild(); LogicalPoint childOrigin(wm, borderPadding.IStart(wm), borderPadding.BStart(wm)); - // In vertical-rl mode we can't use the computed width as the - // container width because it may be NS_UNCONSTRAINEDSIZE, so we use 0 - // for now and reposition the columns after reflowing them all. - nscoord containerWidth = wm.IsVerticalRL() ? 0 : aReflowState.ComputedWidth(); + // In vertical-rl mode, columns will not be correctly placed if the + // reflowState's ComputedWidth() is UNCONSTRAINED (in which case we'll get + // a containerSize.width of zero here). In that case, the column positions + // will be adjusted later, after our correct contentSize is known. + nsSize containerSize = aReflowState.ComputedSizeAsContainerIfConstrained(); // For RTL, since the columns might not fill the frame exactly, we // need to account for the slop. Otherwise we'll waste time moving the // columns by some tiny amount // XXX when all of layout is converted to logical coordinates, we // probably won't need to do this hack any more. For now, we // confine it to the legacy horizontal-rl case if (!isVertical && isRTL) { nscoord availISize = aReflowState.AvailableISize(); if (aReflowState.ComputedISize() != NS_INTRINSICSIZE) { availISize = aReflowState.ComputedISize(); } if (availISize != NS_INTRINSICSIZE) { - childOrigin.I(wm) = containerWidth - borderPadding.Left(wm) - availISize; + childOrigin.I(wm) = containerSize.width - borderPadding.Left(wm) - + availISize; #ifdef DEBUG_roc printf("*** childOrigin.iCoord = %d\n", childOrigin.I(wm)); #endif } } int columnCount = 0; int contentBEnd = 0; @@ -562,17 +564,17 @@ nsColumnSetFrame::ReflowChildren(nsHTMLR NS_NOTREACHED("unknown block direction"); break; } } nscoord childContentBEnd = 0; if (!reflowNext && (skipIncremental || skipResizeBSizeShrink)) { // This child does not need to be reflowed, but we may need to move it - MoveChildTo(child, childOrigin, wm, containerWidth); + MoveChildTo(child, childOrigin, wm, containerSize); // If this is the last frame then make sure we get the right status nsIFrame* kidNext = child->GetNextSibling(); if (kidNext) { aStatus = (kidNext->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER) ? NS_FRAME_OVERFLOW_INCOMPLETE : NS_FRAME_NOT_COMPLETE; } else { @@ -629,32 +631,32 @@ nsColumnSetFrame::ReflowChildren(nsHTMLR // Reflow the frame LogicalPoint origin(wm, childOrigin.I(wm) + kidReflowState.ComputedLogicalMargin().IStart(wm), childOrigin.B(wm) + kidReflowState.ComputedLogicalMargin().BStart(wm)); ReflowChild(child, PresContext(), kidDesiredSize, kidReflowState, - wm, origin, containerWidth, 0, aStatus); + wm, origin, containerSize, 0, aStatus); reflowNext = (aStatus & NS_FRAME_REFLOW_NEXTINFLOW) != 0; #ifdef DEBUG_roc printf("*** Reflowed child #%d %p: status = %d, desiredSize=%d,%d CarriedOutBEndMargin=%d\n", columnCount, (void*)child, aStatus, kidDesiredSize.Width(), kidDesiredSize.Height(), kidDesiredSize.mCarriedOutBEndMargin.get()); #endif NS_FRAME_TRACE_REFLOW_OUT("Column::Reflow", aStatus); *aCarriedOutBEndMargin = kidDesiredSize.mCarriedOutBEndMargin; FinishReflowChild(child, PresContext(), kidDesiredSize, - &kidReflowState, wm, childOrigin, containerWidth, 0); + &kidReflowState, wm, childOrigin, containerSize, 0); childContentBEnd = nsLayoutUtils::CalculateContentBEnd(wm, child); if (childContentBEnd > aConfig.mColMaxBSize) { allFit = false; } if (childContentBEnd > availSize.BSize(wm)) { aColData.mMaxOverflowingBSize = std::max(childContentBEnd, aColData.mMaxOverflowingBSize); @@ -796,27 +798,27 @@ nsColumnSetFrame::ReflowChildren(nsHTMLR } contentSize.ISize(wm) += borderPadding.IStartEnd(wm); contentSize.BSize(wm) += borderPadding.BStartEnd(wm); aDesiredSize.SetSize(wm, contentSize); aDesiredSize.mOverflowAreas = overflowRects; aDesiredSize.UnionOverflowAreasWithDesiredBounds(); - // In vertical-rl mode, make a second pass to reposition the columns - // with the correct container width - if (wm.IsVerticalRL()) { - child = mFrames.FirstChild(); - while (child) { - // Get the logical position as set before with containerWidth=0 - // and reset with the correct container width (which is the block - // size in vertical modes). - child->SetPosition(wm, child->GetLogicalPosition(wm, 0), - contentSize.BSize(wm)); - child = child->GetNextSibling(); + // In vertical-rl mode, make a second pass if necessary to reposition the + // columns with the correct container width. (In other writing modes, + // correct containerSize was not required for column positioning so we don't + // need this fixup.) + if (wm.IsVerticalRL() && containerSize.width != contentSize.Width(wm)) { + const nsSize finalContainerSize = aDesiredSize.PhysicalSize(); + for (nsIFrame* child : mFrames) { + // Get the logical position as set previously using a provisional or + // dummy containerSize, and reset with the correct container size. + child->SetPosition(wm, child->GetLogicalPosition(wm, containerSize), + finalContainerSize); } } #ifdef DEBUG_roc printf("*** DONE PASS feasible=%d\n", allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus) && !NS_FRAME_IS_TRUNCATED(aStatus)); #endif return allFit && NS_FRAME_IS_FULLY_COMPLETE(aStatus)
--- a/layout/generic/nsContainerFrame.cpp +++ b/layout/generic/nsContainerFrame.cpp @@ -969,30 +969,30 @@ nsContainerFrame::ComputeAutoSize(nsRend void nsContainerFrame::ReflowChild(nsIFrame* aKidFrame, nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, const WritingMode& aWM, const LogicalPoint& aPos, - nscoord aContainerWidth, + const nsSize& aContainerSize, uint32_t aFlags, nsReflowStatus& aStatus, nsOverflowContinuationTracker* aTracker) { NS_PRECONDITION(aReflowState.frame == aKidFrame, "bad reflow state"); if (aWM.IsVerticalRL() || (!aWM.IsVertical() && !aWM.IsBidiLTR())) { - NS_ASSERTION(aContainerWidth != NS_UNCONSTRAINEDSIZE, + NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE, "ReflowChild with unconstrained container width!"); } // Position the child frame and its view if requested. if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) { - aKidFrame->SetPosition(aWM, aPos, aContainerWidth); + aKidFrame->SetPosition(aWM, aPos, aContainerSize); } if (0 == (aFlags & NS_FRAME_NO_MOVE_VIEW)) { PositionFrameView(aKidFrame); } // Reflow the child frame aKidFrame->Reflow(aPresContext, aDesiredSize, aReflowState, aStatus); @@ -1109,32 +1109,32 @@ nsContainerFrame::PositionChildViews(nsI */ void nsContainerFrame::FinishReflowChild(nsIFrame* aKidFrame, nsPresContext* aPresContext, const nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState* aReflowState, const WritingMode& aWM, const LogicalPoint& aPos, - nscoord aContainerWidth, + const nsSize& aContainerSize, uint32_t aFlags) { if (aWM.IsVerticalRL() || (!aWM.IsVertical() && !aWM.IsBidiLTR())) { - NS_ASSERTION(aContainerWidth != NS_UNCONSTRAINEDSIZE, + NS_ASSERTION(aContainerSize.width != NS_UNCONSTRAINEDSIZE, "FinishReflowChild with unconstrained container width!"); } nsPoint curOrigin = aKidFrame->GetPosition(); WritingMode outerWM = aDesiredSize.GetWritingMode(); LogicalSize convertedSize = aDesiredSize.Size(outerWM).ConvertTo(aWM, outerWM); if (NS_FRAME_NO_MOVE_FRAME != (aFlags & NS_FRAME_NO_MOVE_FRAME)) { aKidFrame->SetRect(aWM, LogicalRect(aWM, aPos, convertedSize), - aContainerWidth); + aContainerSize); } else { aKidFrame->SetSize(aWM, convertedSize); } if (aKidFrame->HasView()) { nsView* view = aKidFrame->GetView(); // Make sure the frame's view is properly sized and positioned and has // things like opacity correct @@ -1256,35 +1256,35 @@ nsContainerFrame::ReflowOverflowContaine if (shouldReflowAllKids || NS_SUBTREE_DIRTY(frame)) { // Get prev-in-flow nsIFrame* prevInFlow = frame->GetPrevInFlow(); NS_ASSERTION(prevInFlow, "overflow container frame must have a prev-in-flow"); NS_ASSERTION(frame->GetStateBits() & NS_FRAME_IS_OVERFLOW_CONTAINER, "overflow container frame must have overflow container bit set"); WritingMode wm = frame->GetWritingMode(); - nscoord containerWidth = aReflowState.AvailableSize(wm).Width(wm); - LogicalRect prevRect = prevInFlow->GetLogicalRect(wm, containerWidth); + nsSize containerSize = aReflowState.AvailableSize(wm).GetPhysicalSize(wm); + LogicalRect prevRect = prevInFlow->GetLogicalRect(wm, containerSize); // Initialize reflow params LogicalSize availSpace(wm, prevRect.ISize(wm), aReflowState.AvailableSize(wm).BSize(wm)); nsHTMLReflowMetrics desiredSize(aReflowState); nsHTMLReflowState frameState(aPresContext, aReflowState, frame, availSpace); nsReflowStatus frameStatus; // Reflow LogicalPoint pos(wm, prevRect.IStart(wm), 0); ReflowChild(frame, aPresContext, desiredSize, frameState, - wm, pos, containerWidth, aFlags, frameStatus, &tracker); + wm, pos, containerSize, aFlags, frameStatus, &tracker); //XXXfr Do we need to override any shrinkwrap effects here? // e.g. desiredSize.Width() = prevRect.width; FinishReflowChild(frame, aPresContext, desiredSize, &frameState, - wm, pos, containerWidth, aFlags); + wm, pos, containerSize, aFlags); // Handle continuations if (!NS_FRAME_IS_FULLY_COMPLETE(frameStatus)) { if (frame->GetStateBits() & NS_FRAME_OUT_OF_FLOW) { // Abspos frames can't cause their parent to be incomplete, // only overflow incomplete. NS_FRAME_SET_OVERFLOW_INCOMPLETE(frameStatus); } @@ -1316,17 +1316,17 @@ nsContainerFrame::ReflowOverflowContaine // but we have some unsplittable frames that, when taller than // availableHeight will push zero-height content into a next-in-flow. } else { tracker.Skip(frame, aStatus); if (aReflowState.mFloatManager) { nsBlockFrame::RecoverFloatsFor(frame, *aReflowState.mFloatManager, aReflowState.GetWritingMode(), - aReflowState.ComputedWidth()); + aReflowState.ComputedPhysicalSize()); } } ConsiderChildOverflow(aOverflowRects, frame); } } void nsContainerFrame::DisplayOverflowContainers(nsDisplayListBuilder* aBuilder,
--- a/layout/generic/nsContainerFrame.h +++ b/layout/generic/nsContainerFrame.h @@ -236,61 +236,61 @@ public: const mozilla::LogicalSize& aPadding, bool aShrinkWrap) override; /** * Positions aChildFrame and its view (if requested), and then calls Reflow(). * If the reflow status after reflowing the child is FULLY_COMPLETE then any * next-in-flows are deleted using DeleteNextInFlowChild(). * - * @param aContainerWidth width of the border-box of the containing frame + * @param aContainerSize size of the border-box of the containing frame * * Flags: * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you * don't want to automatically sync the frame and view * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aPos is ignored in this * case. Also implies NS_FRAME_NO_MOVE_VIEW */ void ReflowChild(nsIFrame* aChildFrame, nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, const mozilla::WritingMode& aWM, const mozilla::LogicalPoint& aPos, - nscoord aContainerWidth, + const nsSize& aContainerSize, uint32_t aFlags, nsReflowStatus& aStatus, nsOverflowContinuationTracker* aTracker = nullptr); /** * The second half of frame reflow. Does the following: * - sets the frame's bounds * - sizes and positions (if requested) the frame's view. If the frame's final * position differs from the current position and the frame itself does not * have a view, then any child frames with views are positioned so they stay * in sync * - sets the view's visibility, opacity, content transparency, and clip * - invoked the DidReflow() function * - * @param aContainerWidth width of the border-box of the containing frame + * @param aContainerSize size of the border-box of the containing frame * * Flags: * NS_FRAME_NO_MOVE_FRAME - don't move the frame. aPos is ignored in this * case. Also implies NS_FRAME_NO_MOVE_VIEW * NS_FRAME_NO_MOVE_VIEW - don't position the frame's view. Set this if you * don't want to automatically sync the frame and view * NS_FRAME_NO_SIZE_VIEW - don't size the frame's view */ static void FinishReflowChild(nsIFrame* aKidFrame, nsPresContext* aPresContext, const nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState* aReflowState, const mozilla::WritingMode& aWM, const mozilla::LogicalPoint& aPos, - nscoord aContainerWidth, + const nsSize& aContainerSize, uint32_t aFlags); //XXX temporary: hold on to a copy of the old physical versions of // ReflowChild and FinishReflowChild so that we can convert callers // incrementally. void ReflowChild(nsIFrame* aKidFrame, nsPresContext* aPresContext, nsHTMLReflowMetrics& aDesiredSize,
--- a/layout/generic/nsFlexContainerFrame.cpp +++ b/layout/generic/nsFlexContainerFrame.cpp @@ -3819,19 +3819,22 @@ nsFlexContainerFrame::DoFlexLayout(nsPre const LogicalSides skipSides = GetLogicalSkipSides(&aReflowState) | LogicalSides(eLogicalSideBitsBEnd); containerBP.ApplySkipSides(skipSides); const LogicalPoint containerContentBoxOrigin(flexWM, containerBP.IStart(flexWM), containerBP.BStart(flexWM)); - nscoord containerWidth = aAxisTracker.IsMainAxisHorizontal() ? - aContentBoxMainSize : contentBoxCrossSize; - containerWidth += aReflowState.ComputedPhysicalBorderPadding().LeftRight(); + // Determine flex container's border-box size (used in positioning children): + LogicalSize logSize = + aAxisTracker.LogicalSizeFromFlexRelativeSizes(aContentBoxMainSize, + contentBoxCrossSize); + logSize += aReflowState.ComputedLogicalBorderPadding().Size(flexWM); + nsSize containerSize = logSize.GetPhysicalSize(flexWM); // FINAL REFLOW: Give each child frame another chance to reflow, now that // we know its final size and position. for (const FlexLine* line = lines.getFirst(); line; line = line->getNext()) { for (const FlexItem* item = line->GetFirstItem(); item; item = item->getNext()) { LogicalPoint framePos = aAxisTracker.LogicalPointFromFlexRelativePoint( item->GetMainPosition(), @@ -3864,23 +3867,23 @@ nsFlexContainerFrame::DoFlexLayout(nsPre // relative bsize. if (!(item->Frame()->GetStateBits() & NS_FRAME_CONTAINS_RELATIVE_BSIZE)) { // Item has the correct size (and its children don't care that // it's now "definite"). Let's just make sure it's at the right // position. itemNeedsReflow = false; MoveFlexItemToFinalPosition(aReflowState, *item, framePos, - containerWidth); + containerSize); } } } if (itemNeedsReflow) { ReflowFlexItem(aPresContext, aAxisTracker, aReflowState, - *item, framePos, containerWidth); + *item, framePos, containerSize); } // If this is our first child and we haven't established a baseline for // the container yet (i.e. if we don't have 'align-self: baseline' on any // children), then use this child's baseline as the container's baseline. if (item->Frame() == mFrames.FirstChild() && flexContainerAscent == nscoord_MIN) { flexContainerAscent = itemNormalBPos + item->ResolvedAscent(); @@ -3955,44 +3958,44 @@ nsFlexContainerFrame::DoFlexLayout(nsPre NS_FRAME_SET_TRUNCATION(aStatus, aReflowState, aDesiredSize) } void nsFlexContainerFrame::MoveFlexItemToFinalPosition( const nsHTMLReflowState& aReflowState, const FlexItem& aItem, LogicalPoint& aFramePos, - nscoord aContainerWidth) + const nsSize& aContainerSize) { WritingMode outerWM = aReflowState.GetWritingMode(); // If item is relpos, look up its offsets (cached from prev reflow) LogicalMargin logicalOffsets(outerWM); if (NS_STYLE_POSITION_RELATIVE == aItem.Frame()->StyleDisplay()->mPosition) { FrameProperties props = aItem.Frame()->Properties(); nsMargin* cachedOffsets = static_cast<nsMargin*>(props.Get(nsIFrame::ComputedOffsetProperty())); MOZ_ASSERT(cachedOffsets, "relpos previously-reflowed frame should've cached its offsets"); logicalOffsets = LogicalMargin(outerWM, *cachedOffsets); } nsHTMLReflowState::ApplyRelativePositioning(aItem.Frame(), outerWM, logicalOffsets, &aFramePos, - aContainerWidth); - aItem.Frame()->SetPosition(outerWM, aFramePos, aContainerWidth); + aContainerSize); + aItem.Frame()->SetPosition(outerWM, aFramePos, aContainerSize); PositionChildViews(aItem.Frame()); } void nsFlexContainerFrame::ReflowFlexItem(nsPresContext* aPresContext, const FlexboxAxisTracker& aAxisTracker, const nsHTMLReflowState& aReflowState, const FlexItem& aItem, LogicalPoint& aFramePos, - nscoord aContainerWidth) + const nsSize& aContainerSize) { WritingMode outerWM = aReflowState.GetWritingMode(); WritingMode wm = aItem.Frame()->GetWritingMode(); LogicalSize availSize = aReflowState.ComputedSize(wm); availSize.BSize(wm) = NS_UNCONSTRAINEDSIZE; nsHTMLReflowState childReflowState(aPresContext, aReflowState, aItem.Frame(), availSize); @@ -4047,36 +4050,36 @@ nsFlexContainerFrame::ReflowFlexItem(nsP // NOTE: Be very careful about doing anything else with childReflowState // after this point, because some of its methods (e.g. SetComputedWidth) // internally call InitResizeFlags and stomp on mVResize & mHResize. nsHTMLReflowMetrics childDesiredSize(childReflowState); nsReflowStatus childReflowStatus; ReflowChild(aItem.Frame(), aPresContext, childDesiredSize, childReflowState, - outerWM, aFramePos, aContainerWidth, + outerWM, aFramePos, aContainerSize, 0, childReflowStatus); // XXXdholbert Once we do pagination / splitting, we'll need to actually // handle incomplete childReflowStatuses. But for now, we give our kids // unconstrained available height, which means they should always // complete. MOZ_ASSERT(NS_FRAME_IS_COMPLETE(childReflowStatus), "We gave flex item unconstrained available height, so it " "should be complete"); LogicalMargin offsets = childReflowState.ComputedLogicalOffsets().ConvertTo(outerWM, wm); nsHTMLReflowState::ApplyRelativePositioning(aItem.Frame(), outerWM, offsets, &aFramePos, - aContainerWidth); + aContainerSize); FinishReflowChild(aItem.Frame(), aPresContext, childDesiredSize, &childReflowState, - outerWM, aFramePos, aContainerWidth, 0); + outerWM, aFramePos, aContainerSize, 0); // Save the first child's ascent; it may establish container's baseline. if (aItem.Frame() == mFrames.FirstChild()) { aItem.SetAscent(childDesiredSize.BlockStartAscent()); } } /* virtual */ nscoord
--- a/layout/generic/nsFlexContainerFrame.h +++ b/layout/generic/nsFlexContainerFrame.h @@ -178,40 +178,40 @@ protected: * This can be used in cases where we've already done a "measuring reflow" * for the flex item at the correct size, and hence can skip its final reflow * (but still need to move it to the right final position). * * @param aReflowState The flex container's reflow state. * @param aItem The flex item whose frame should be moved. * @param aFramePos The position where the flex item's frame should * be placed. (pre-relative positioning) - * @param aContainerWidth The flex container's width (required by some methods + * @param aContainerSize The flex container's size (required by some methods * that we call, to interpret aFramePos correctly). */ void MoveFlexItemToFinalPosition(const nsHTMLReflowState& aReflowState, const FlexItem& aItem, mozilla::LogicalPoint& aFramePos, - nscoord aContainerWidth); + const nsSize& aContainerSize); /** * Helper-function to reflow a child frame, at its final position determined * by flex layout. * * @param aPresContext The presentation context being used in reflow. * @param aAxisTracker A FlexboxAxisTracker with the flex container's axes. * @param aReflowState The flex container's reflow state. * @param aItem The flex item to be reflowed. * @param aFramePos The position where the flex item's frame should * be placed. (pre-relative positioning) - * @param aContainerWidth The flex container's width (required by some methods + * @param aContainerSize The flex container's size (required by some methods * that we call, to interpret aFramePos correctly). */ void ReflowFlexItem(nsPresContext* aPresContext, const FlexboxAxisTracker& aAxisTracker, const nsHTMLReflowState& aReflowState, const FlexItem& aItem, mozilla::LogicalPoint& aFramePos, - nscoord aContainerWidth); + const nsSize& aContainerSize); bool mChildrenHaveBeenReordered; // Have we ever had to reorder our kids // to satisfy their 'order' values? }; #endif /* nsFlexContainerFrame_h___ */
--- a/layout/generic/nsFloatManager.cpp +++ b/layout/generic/nsFloatManager.cpp @@ -117,17 +117,17 @@ void nsFloatManager::Shutdown() #define CHECK_BLOCK_DIR(aWM) \ NS_ASSERTION(aWM.GetBlockDir() == mWritingMode.value.GetBlockDir(), \ "incompatible writing modes") nsFlowAreaRect nsFloatManager::GetFlowArea(WritingMode aWM, nscoord aBOffset, BandInfoType aInfoType, nscoord aBSize, LogicalRect aContentArea, SavedState* aState, - nscoord aContainerWidth) const + const nsSize& aContainerSize) const { CHECK_BLOCK_DIR(aWM); NS_ASSERTION(aBSize >= 0, "unexpected max block size"); NS_ASSERTION(aContentArea.ISize(aWM) >= 0, "unexpected content area inline size"); nscoord blockStart = aBOffset + mBlockStart; if (blockStart < nscoord_MIN) { @@ -164,18 +164,18 @@ nsFloatManager::GetFlowArea(WritingMode blockEnd = nscoord_MAX; } else { blockEnd = blockStart + aBSize; if (blockEnd < blockStart || blockEnd > nscoord_MAX) { NS_WARNING("bad value"); blockEnd = nscoord_MAX; } } - nscoord lineLeft = mLineLeft + aContentArea.LineLeft(aWM, aContainerWidth); - nscoord lineRight = mLineLeft + aContentArea.LineRight(aWM, aContainerWidth); + nscoord lineLeft = mLineLeft + aContentArea.LineLeft(aWM, aContainerSize); + nscoord lineRight = mLineLeft + aContentArea.LineRight(aWM, aContainerSize); if (lineRight < lineLeft) { NS_WARNING("bad value"); lineRight = lineLeft; } // Walk backwards through the floats until we either hit the front of // the list or we're above |blockStart|. bool haveFloats = false; @@ -237,34 +237,35 @@ nsFloatManager::GetFlowArea(WritingMode } } } } nscoord blockSize = (blockEnd == nscoord_MAX) ? nscoord_MAX : (blockEnd - blockStart); // convert back from LineLeft/Right to IStart - nscoord inlineStart = aWM.IsVertical() || aWM.IsBidiLTR() - ? lineLeft - mLineLeft - : mLineLeft + aContainerWidth - lineRight; + nscoord inlineStart = aWM.IsBidiLTR() + ? lineLeft - mLineLeft + : mLineLeft - lineRight + + LogicalSize(aWM, aContainerSize).ISize(aWM); return nsFlowAreaRect(aWM, inlineStart, blockStart - mBlockStart, lineRight - lineLeft, blockSize, haveFloats); } nsresult nsFloatManager::AddFloat(nsIFrame* aFloatFrame, const LogicalRect& aMarginRect, - WritingMode aWM, nscoord aContainerWidth) + WritingMode aWM, const nsSize& aContainerSize) { CHECK_BLOCK_DIR(aWM); NS_ASSERTION(aMarginRect.ISize(aWM) >= 0, "negative inline size!"); NS_ASSERTION(aMarginRect.BSize(aWM) >= 0, "negative block size!"); FloatInfo info(aFloatFrame, - aMarginRect.LineLeft(aWM, aContainerWidth) + mLineLeft, + aMarginRect.LineLeft(aWM, aContainerSize) + mLineLeft, aMarginRect.BStart(aWM) + mBlockStart, aMarginRect.ISize(aWM), aMarginRect.BSize(aWM)); // Set mLeftBEnd and mRightBEnd. if (HasAnyFloats()) { FloatInfo &tail = mFloats[mFloats.Length() - 1]; info.mLeftBEnd = tail.mLeftBEnd; @@ -288,22 +289,22 @@ nsFloatManager::AddFloat(nsIFrame* aFloa return NS_OK; } // static LogicalRect nsFloatManager::CalculateRegionFor(WritingMode aWM, nsIFrame* aFloat, const LogicalMargin& aMargin, - nscoord aContainerWidth) + const nsSize& aContainerSize) { // We consider relatively positioned frames at their original position. LogicalRect region(aWM, nsRect(aFloat->GetNormalPosition(), aFloat->GetSize()), - aContainerWidth); + aContainerSize); // Float region includes its margin region.Inflate(aWM, aMargin); // Don't store rectangles with negative margin-box width or height in // the float manager; it can't deal with them. if (region.ISize(aWM) < 0) { // Preserve the right margin-edge for left floats and the left @@ -319,33 +320,33 @@ nsFloatManager::CalculateRegionFor(Writi } return region; } NS_DECLARE_FRAME_PROPERTY(FloatRegionProperty, DeleteValue<nsMargin>) LogicalRect nsFloatManager::GetRegionFor(WritingMode aWM, nsIFrame* aFloat, - nscoord aContainerWidth) + const nsSize& aContainerSize) { - LogicalRect region = aFloat->GetLogicalRect(aWM, aContainerWidth); + LogicalRect region = aFloat->GetLogicalRect(aWM, aContainerSize); void* storedRegion = aFloat->Properties().Get(FloatRegionProperty()); if (storedRegion) { nsMargin margin = *static_cast<nsMargin*>(storedRegion); region.Inflate(aWM, LogicalMargin(aWM, margin)); } return region; } void nsFloatManager::StoreRegionFor(WritingMode aWM, nsIFrame* aFloat, const LogicalRect& aRegion, - nscoord aContainerWidth) + const nsSize& aContainerSize) { - nsRect region = aRegion.GetPhysicalRect(aWM, aContainerWidth); + nsRect region = aRegion.GetPhysicalRect(aWM, aContainerSize); nsRect rect = aFloat->GetRect(); FrameProperties props = aFloat->Properties(); if (region.IsEqualEdges(rect)) { props.Delete(FloatRegionProperty()); } else { nsMargin* storedMargin = static_cast<nsMargin*> (props.Get(FloatRegionProperty()));
--- a/layout/generic/nsFloatManager.h +++ b/layout/generic/nsFloatManager.h @@ -54,38 +54,38 @@ public: /** * Get float region stored on the frame. (Defaults to mRect if it's * not there.) The float region is the area impacted by this float; * the coordinates are relative to the containing block frame. */ static mozilla::LogicalRect GetRegionFor(mozilla::WritingMode aWM, nsIFrame* aFloatFrame, - nscoord aContainerWidth); + const nsSize& aContainerSize); /** * Calculate the float region for this frame using aMargin and the * frame's mRect. The region includes the margins around the float, * but doesn't include the relative offsets. * Note that if the frame is or has a continuation, aMargin's top * and/or bottom must be zeroed by the caller. */ static mozilla::LogicalRect CalculateRegionFor( mozilla::WritingMode aWM, nsIFrame* aFloatFrame, const mozilla::LogicalMargin& aMargin, - nscoord aContainerWidth); + const nsSize& aContainerSize); /** * Store the float region on the frame. The region is stored * as a delta against the mRect, so repositioning the frame will * also reposition the float region. */ static void StoreRegionFor(mozilla::WritingMode aWM, nsIFrame* aFloat, const mozilla::LogicalRect& aRegion, - nscoord aContainerWidth); + const nsSize& aContainerSize); // Structure that stores the current state of a frame manager for // Save/Restore purposes. struct SavedState { explicit SavedState() {} private: uint32_t mFloatInfoCount; nscoord mLineLeft, mBlockStart; @@ -157,29 +157,30 @@ public: * inline size at all (because they are entirely in the margins) * * aBCoord and aAvailSpace are positioned relative to the current translation */ enum BandInfoType { BAND_FROM_POINT, WIDTH_WITHIN_HEIGHT }; nsFlowAreaRect GetFlowArea(mozilla::WritingMode aWM, nscoord aBCoord, BandInfoType aInfoType, nscoord aBSize, mozilla::LogicalRect aContentArea, - SavedState* aState, nscoord mContainerWidth) const; + SavedState* aState, + const nsSize& aContainerSize) const; /** * Add a float that comes after all floats previously added. Its * block start must be even with or below the top of all previous * floats. * * aMarginRect is relative to the current translation. The caller * must ensure aMarginRect.height >= 0 and aMarginRect.width >= 0. */ nsresult AddFloat(nsIFrame* aFloatFrame, const mozilla::LogicalRect& aMarginRect, - mozilla::WritingMode aWM, nscoord aContainerWidth); + mozilla::WritingMode aWM, const nsSize& aContainerSize); /** * Notify that we tried to place a float that could not fit at all and * had to be pushed to the next page/column? (If so, we can't place * any more floats in this page/column because of the rule that the * top of a float cannot be above the top of an earlier float. It * also means that any clear needs to continue to the next column.) */
--- a/layout/generic/nsFrame.cpp +++ b/layout/generic/nsFrame.cpp @@ -3550,29 +3550,29 @@ static FrameTarget GetSelectionClosestFr { nsIFrame *frame = aLine->mFirstChild; // Account for end of lines (any iterator from the block is valid) if (aLine == aParent->end_lines()) return DrillDownToSelectionFrame(aParent, true, aFlags); nsIFrame *closestFromIStart = nullptr, *closestFromIEnd = nullptr; nscoord closestIStart = aLine->IStart(), closestIEnd = aLine->IEnd(); WritingMode wm = aLine->mWritingMode; - LogicalPoint pt(wm, aPoint, aLine->mContainerWidth); + LogicalPoint pt(wm, aPoint, aLine->mContainerSize); bool canSkipBr = false; for (int32_t n = aLine->GetChildCount(); n; --n, frame = frame->GetNextSibling()) { // Skip brFrames. Can only skip if the line contains at least // one selectable and non-empty frame before if (!SelfIsSelectable(frame, aFlags) || frame->IsEmpty() || (canSkipBr && frame->GetType() == nsGkAtoms::brFrame)) { continue; } canSkipBr = true; LogicalRect frameRect = LogicalRect(wm, frame->GetRect(), - aLine->mContainerWidth); + aLine->mContainerSize); if (pt.I(wm) >= frameRect.IStart(wm)) { if (pt.I(wm) < frameRect.IEnd(wm)) { return GetSelectionClosestFrameForChild(frame, aPoint, aFlags); } if (frameRect.IEnd(wm) >= closestIStart) { closestFromIStart = frame; closestIStart = frameRect.IEnd(wm); } @@ -3620,17 +3620,17 @@ static FrameTarget GetSelectionClosestFr return FrameTarget(aFrame, false, false, true); } return FrameTarget::Null(); } nsBlockFrame::line_iterator curLine = firstLine; nsBlockFrame::line_iterator closestLine = end; // Convert aPoint into a LogicalPoint in the writing-mode of this block WritingMode wm = curLine->mWritingMode; - LogicalPoint pt(wm, aPoint, curLine->mContainerWidth); + LogicalPoint pt(wm, aPoint, curLine->mContainerSize); while (curLine != end) { // Check to see if our point lies within the line's block-direction bounds nscoord BCoord = pt.B(wm) - curLine->BStart(); nscoord BSize = curLine->BSize(); if (BCoord >= 0 && BCoord < BSize) { closestLine = curLine; break; // We found the line; stop looking } @@ -5676,24 +5676,26 @@ nsIFrame::ListGeneric(nsACString& aTo, c wm.IsBidiLTR() ? "ltr" : "rtl", ISize(), BSize()); } nsIFrame* parent = GetParent(); if (parent) { WritingMode pWM = parent->GetWritingMode(); if (pWM.IsVertical() || !pWM.IsBidiLTR()) { - nscoord cw = parent->mRect.width; - LogicalRect lr(pWM, mRect, cw); - aTo += nsPrintfCString(" parent wm=%s-%s,width=%d,logicalRect={%d,%d,%d,%d}", + nsSize containerSize = parent->mRect.Size(); + LogicalRect lr(pWM, mRect, containerSize); + aTo += nsPrintfCString(" parent wm=%s-%s, cs={%d,%d}, " + " logicalRect={%d,%d,%d,%d}", pWM.IsVertical() ? pWM.IsVerticalLR() ? "vlr" : "vrl" : "htb", wm.IsBidiLTR() ? "ltr" : "rtl", - cw, lr.IStart(pWM), lr.BStart(pWM), + containerSize.width, containerSize.height, + lr.IStart(pWM), lr.BStart(pWM), lr.ISize(pWM), lr.BSize(pWM)); } } nsIFrame* f = const_cast<nsIFrame*>(this); if (f->HasOverflowAreas()) { nsRect vo = f->GetVisualOverflowRect(); if (!vo.IsEqualEdges(mRect)) { aTo += nsPrintfCString(" vis-overflow=%d,%d,%d,%d", vo.x, vo.y, vo.width, vo.height);
--- a/layout/generic/nsGfxScrollFrame.cpp +++ b/layout/generic/nsGfxScrollFrame.cpp @@ -509,33 +509,35 @@ nsHTMLScrollFrame::ReflowScrolledFrame(S // Temporarily set mHasHorizontalScrollbar/mHasVerticalScrollbar to // reflect our assumptions while we reflow the child. bool didHaveHorizontalScrollbar = mHelper.mHasHorizontalScrollbar; bool didHaveVerticalScrollbar = mHelper.mHasVerticalScrollbar; mHelper.mHasHorizontalScrollbar = aAssumeHScroll; mHelper.mHasVerticalScrollbar = aAssumeVScroll; nsReflowStatus status; - // No need to pass a container-width to ReflowChild or + // No need to pass a true container-size to ReflowChild or // FinishReflowChild, because it's only used there when positioning // the frame (i.e. if NS_FRAME_NO_MOVE_FRAME isn't set) + const nsSize dummyContainerSize; ReflowChild(mHelper.mScrolledFrame, presContext, *aMetrics, - kidReflowState, wm, LogicalPoint(wm), 0, + kidReflowState, wm, LogicalPoint(wm), dummyContainerSize, NS_FRAME_NO_MOVE_FRAME, status); mHelper.mHasHorizontalScrollbar = didHaveHorizontalScrollbar; mHelper.mHasVerticalScrollbar = didHaveVerticalScrollbar; // Don't resize or position the view (if any) because we're going to resize // it to the correct size anyway in PlaceScrollArea. Allowing it to // resize here would size it to the natural height of the frame, // which will usually be different from the scrollport height; // invalidating the difference will cause unnecessary repainting. FinishReflowChild(mHelper.mScrolledFrame, presContext, - *aMetrics, &kidReflowState, wm, LogicalPoint(wm), 0, + *aMetrics, &kidReflowState, wm, LogicalPoint(wm), + dummyContainerSize, NS_FRAME_NO_MOVE_FRAME | NS_FRAME_NO_SIZE_VIEW); // XXX Some frames (e.g., nsPluginFrame, nsFrameFrame, nsTextFrame) don't bother // setting their mOverflowArea. This is wrong because every frame should // always set mOverflowArea. In fact nsPluginFrame and nsFrameFrame don't // support the 'outline' property because of this. Rather than fix the world // right now, just fix up the overflow area if necessary. Note that we don't // check HasOverflowRect() because it could be set even though the
--- a/layout/generic/nsGridContainerFrame.cpp +++ b/layout/generic/nsGridContainerFrame.cpp @@ -1241,18 +1241,19 @@ nsGridContainerFrame::ReflowChildren(Gri const nsTArray<TrackSize>& aColSizes, const nsTArray<TrackSize>& aRowSizes, nsHTMLReflowMetrics& aDesiredSize, const nsHTMLReflowState& aReflowState, nsReflowStatus& aStatus) { WritingMode wm = aReflowState.GetWritingMode(); const LogicalPoint gridOrigin(aContentArea.Origin(wm)); - const nscoord containerWidth = aContentArea.Width(wm) + - aReflowState.ComputedPhysicalBorderPadding().LeftRight(); + const nsSize containerSize = + (aContentArea.Size(wm) + + aReflowState.ComputedLogicalBorderPadding().Size(wm)).GetPhysicalSize(wm); nsPresContext* pc = PresContext(); for (; !aIter.AtEnd(); aIter.Next()) { nsIFrame* child = *aIter; const bool isGridItem = child->GetType() != nsGkAtoms::placeholderFrame; LogicalRect cb(wm); if (MOZ_LIKELY(isGridItem)) { GridArea* area = GetGridAreaForChild(child); MOZ_ASSERT(area && area->IsDefinite()); @@ -1270,30 +1271,32 @@ nsGridContainerFrame::ReflowChildren(Gri // clamping though, and check the prop value is actually 'stretch'! LogicalMargin bp = childRS.ComputedLogicalBorderPadding(); bp.ApplySkipSides(child->GetLogicalSkipSides()); nscoord bSize = childCBSize.BSize(childWM) - bp.BStartEnd(childWM) - margin.BStartEnd(childWM); childRS.SetComputedBSize(std::max(bSize, 0)); } // We need the width of the child before we can correctly convert - // the writing-mode of its origin, so we reflow at (0, 0) and then - // pass the correct position to FinishReflowChild. + // the writing-mode of its origin, so we reflow at (0, 0) using a dummy + // containerSize, and then pass the correct position to FinishReflowChild. nsHTMLReflowMetrics childSize(childRS); nsReflowStatus childStatus; + const nsSize dummyContainerSize; ReflowChild(child, pc, childSize, childRS, childWM, LogicalPoint(childWM), - 0, 0, childStatus); + dummyContainerSize, 0, childStatus); LogicalPoint childPos = - cb.Origin(wm).ConvertTo(childWM, wm, containerWidth - childSize.Width() - - margin.LeftRight(childWM)); + cb.Origin(wm).ConvertTo(childWM, wm, + containerSize - childSize.PhysicalSize() - + margin.Size(childWM).GetPhysicalSize(childWM)); childPos.I(childWM) += margin.IStart(childWM); childPos.B(childWM) += margin.BStart(childWM); - childRS.ApplyRelativePositioning(&childPos, containerWidth); + childRS.ApplyRelativePositioning(&childPos, containerSize); FinishReflowChild(child, pc, childSize, &childRS, childWM, childPos, - containerWidth, 0); + containerSize, 0); ConsiderChildOverflow(aDesiredSize.mOverflowAreas, child); // XXX deal with 'childStatus' not being COMPLETE } if (IsAbsoluteContainer()) { nsFrameList children(GetChildList(GetAbsoluteListID())); if (!children.IsEmpty()) { LogicalMargin pad(aReflowState.ComputedLogicalPadding()); @@ -1313,17 +1316,17 @@ nsGridContainerFrame::ReflowChildren(Gri gridOrigin, gridCB)); // nsAbsoluteContainingBlock::Reflow uses physical coordinates. nsRect* cb = static_cast<nsRect*>(child->Properties().Get( GridItemContainingBlockRect())); if (!cb) { cb = new nsRect; child->Properties().Set(GridItemContainingBlockRect(), cb); } - *cb = itemCB.GetPhysicalRect(wm, containerWidth); + *cb = itemCB.GetPhysicalRect(wm, containerSize); } // This rect isn't used at all for layout so we use it to optimize // away the virtual GetType() call in the callee in most cases. // @see nsAbsoluteContainingBlock::Reflow nsRect dummyRect(0, 0, VERY_LIKELY_A_GRID_CONTAINER, 0); GetAbsoluteContainingBlock()->Reflow(this, pc, aReflowState, aStatus, dummyRect, true, true, true, // XXX could be optimized
--- a/layout/generic/nsHTMLReflowMetrics.h +++ b/layout/generic/nsHTMLReflowMetrics.h @@ -267,16 +267,21 @@ public: nscoord BlockStartAscent() const { return mBlockStartAscent; } nscoord& Width() { return mWritingMode.IsVertical() ? mBSize : mISize; } nscoord& Height() { return mWritingMode.IsVertical() ? mISize : mBSize; } + nsSize PhysicalSize() + { + return Size(mWritingMode).GetPhysicalSize(mWritingMode); + } + void SetBlockStartAscent(nscoord aAscent) { mBlockStartAscent = aAscent; } enum { ASK_FOR_BASELINE = nscoord_MAX }; // Metrics that _exactly_ enclose the text to allow precise MathML placements.
--- a/layout/generic/nsHTMLReflowState.cpp +++ b/layout/generic/nsHTMLReflowState.cpp @@ -1260,54 +1260,57 @@ nsHTMLReflowState::CalculateHypothetical } } // Get the placeholder x-offset and y-offset in the coordinate // space of its containing block // XXXbz the placeholder is not fully reflowed yet if our containing block is // relatively positioned... WritingMode cbwm = cbrs->GetWritingMode(); - nscoord containerWidth = containingBlock->GetStateBits() & NS_FRAME_IN_REFLOW - ? cbrs->ComputedWidth() + - cbrs->ComputedLogicalBorderPadding().LeftRight(cbwm) - : containingBlock->GetSize().width; + nsSize containerSize = containingBlock->GetStateBits() & NS_FRAME_IN_REFLOW + ? cbrs->ComputedSizeAsContainerIfConstrained() + : containingBlock->GetSize(); LogicalPoint placeholderOffset(wm, aPlaceholderFrame->GetOffsetTo(containingBlock), - containerWidth); + containerSize); // XXX hack to correct for lack of LogicalPoint bidi support in vertical mode if (wm.IsVertical() && !wm.IsBidiLTR()) { placeholderOffset.I(wm) = cbrs->ComputedHeight() + cbrs->ComputedLogicalBorderPadding().TopBottom(cbwm) - placeholderOffset.I(wm); } // First, determine the hypothetical box's mBStart. We want to check the // content insertion frame of containingBlock for block-ness, but make // sure to compute all coordinates in the coordinate system of // containingBlock. nsBlockFrame* blockFrame = nsLayoutUtils::GetAsBlock(containingBlock->GetContentInsertionFrame()); if (blockFrame) { - LogicalPoint blockOffset(wm, blockFrame->GetOffsetTo(containingBlock), 0); + // Use a null containerSize to convert a LogicalPoint functioning as a + // vector into a physical nsPoint vector. + const nsSize nullContainerSize; + LogicalPoint blockOffset(wm, blockFrame->GetOffsetTo(containingBlock), + nullContainerSize); bool isValid; nsBlockInFlowLineIterator iter(blockFrame, aPlaceholderFrame, &isValid); if (!isValid) { // Give up. We're probably dealing with somebody using // position:absolute inside native-anonymous content anyway. aHypotheticalBox.mBStart = placeholderOffset.B(wm); } else { NS_ASSERTION(iter.GetContainer() == blockFrame, "Found placeholder in wrong block!"); nsBlockFrame::line_iterator lineBox = iter.GetLine(); // How we determine the hypothetical box depends on whether the element // would have been inline-level or block-level LogicalRect lineBounds = lineBox->GetBounds().ConvertTo(wm, lineBox->mWritingMode, - lineBox->mContainerWidth); + lineBox->mContainerSize); if (mStyleDisplay->IsOriginalDisplayInlineOutsideStyle()) { // Use the block-start of the inline box which the placeholder lives in // as the hypothetical box's block-start. aHypotheticalBox.mBStart = lineBounds.BStart(wm) + blockOffset.B(wm); } else { // The element would have been block-level which means it would // be below the line containing the placeholder frame, unless // all the frames before it are empty. In that case, it would @@ -1410,19 +1413,20 @@ nsHTMLReflowState::CalculateHypothetical } while (containingBlock != cbrs->frame); } else { // XXXldb We need to either ignore scrolling for the absolute // positioning case too (and take the incompatibility) or figure out // how to make these positioned elements actually *move* when we // scroll, and thus avoid the resulting incremental reflow bugs. cbOffset = containingBlock->GetOffsetTo(cbrs->frame); } - nscoord cbrsWidth = cbrs->ComputedWidth() + - cbrs->ComputedLogicalBorderPadding().LeftRight(cbwm); - LogicalPoint logCBOffs(wm, cbOffset, cbrsWidth - containerWidth); + nsSize cbrsSize = + cbrs->ComputedPhysicalSize() + + cbrs->ComputedLogicalBorderPadding().Size(cbwm).GetPhysicalSize(cbwm); + LogicalPoint logCBOffs(wm, cbOffset, cbrsSize - containerSize); aHypotheticalBox.mIStart += logCBOffs.I(wm); aHypotheticalBox.mIEnd += logCBOffs.I(wm); aHypotheticalBox.mBStart += logCBOffs.B(wm); // The specified offsets are relative to the absolute containing block's // padding edge and our current values are relative to the border edge, so // translate. LogicalMargin border = @@ -1936,21 +1940,21 @@ nsHTMLReflowState::ComputeContainingBloc // that's very hard. LogicalMargin computedBorder = aContainingBlockRS->ComputedLogicalBorderPadding() - aContainingBlockRS->ComputedLogicalPadding(); cbSize.ISize(wm) = aContainingBlockRS->frame->ISize(wm) - computedBorder.IStartEnd(wm); NS_ASSERTION(cbSize.ISize(wm) >= 0, - "Negative containing block width!"); + "Negative containing block isize!"); cbSize.BSize(wm) = aContainingBlockRS->frame->BSize(wm) - computedBorder.BStartEnd(wm); NS_ASSERTION(cbSize.BSize(wm) >= 0, - "Negative containing block height!"); + "Negative containing block bsize!"); } else { // If the ancestor is block-level, the containing block is formed by the // padding edge of the ancestor cbSize.ISize(wm) += aContainingBlockRS->ComputedLogicalPadding().IStartEnd(wm); cbSize.BSize(wm) += aContainingBlockRS->ComputedLogicalPadding().BStartEnd(wm); } @@ -2175,21 +2179,22 @@ nsHTMLReflowState::InitConstraints(nsPre // Initialize offsets to 0 ComputedPhysicalOffsets().SizeTo(0, 0, 0, 0); } // Calculate the computed values for min and max properties. Note that // this MUST come after we've computed our border and padding. ComputeMinMaxValues(cbSize); - // Calculate the computed width and blockSize. This varies by frame type + // Calculate the computed inlineSize and blockSize. + // This varies by frame type. if (NS_CSS_FRAME_TYPE_INTERNAL_TABLE == mFrameType) { // Internal table elements. The rules vary depending on the type. - // Calculate the computed width + // Calculate the computed isize bool rowOrRowGroup = false; const nsStyleCoord &inlineSize = mStylePosition->ISize(wm); nsStyleUnit inlineSizeUnit = inlineSize.GetUnit(); if ((NS_STYLE_DISPLAY_TABLE_ROW == mStyleDisplay->mDisplay) || (NS_STYLE_DISPLAY_TABLE_ROW_GROUP == mStyleDisplay->mDisplay)) { // 'inlineSize' property doesn't apply to table rows and row groups inlineSizeUnit = eStyleUnit_Auto; rowOrRowGroup = true; @@ -2202,21 +2207,21 @@ nsHTMLReflowState::InitConstraints(nsPre if ((ComputedISize() != NS_UNCONSTRAINEDSIZE) && !rowOrRowGroup){ // Internal table elements don't have margins. Only tables and // cells have border and padding ComputedISize() -= ComputedLogicalBorderPadding().IStartEnd(wm); if (ComputedISize() < 0) ComputedISize() = 0; } - NS_ASSERTION(ComputedISize() >= 0, "Bogus computed width"); + NS_ASSERTION(ComputedISize() >= 0, "Bogus computed isize"); } else { NS_ASSERTION(inlineSizeUnit == inlineSize.GetUnit(), - "unexpected width unit change"); + "unexpected inline size unit change"); ComputedISize() = ComputeISizeValue(cbSize.ISize(wm), mStylePosition->mBoxSizing, inlineSize); } // Calculate the computed block size if ((NS_STYLE_DISPLAY_TABLE_COLUMN == mStyleDisplay->mDisplay) || (NS_STYLE_DISPLAY_TABLE_COLUMN_GROUP == mStyleDisplay->mDisplay)) {
--- a/layout/generic/nsHTMLReflowState.h +++ b/layout/generic/nsHTMLReflowState.h @@ -431,27 +431,43 @@ struct nsHTMLReflowState : public nsCSSO } mozilla::LogicalSize ComputedSizeWithMarginBorderPadding(mozilla::WritingMode aWM) const { return ComputedSizeWithMarginBorderPadding().ConvertTo(aWM, GetWritingMode()); } + nsSize + ComputedPhysicalSize() const { + return nsSize(ComputedWidth(), ComputedHeight()); + } + // XXX this will need to change when we make mComputedOffsets logical; // we won't be able to return a reference for the physical offsets const nsMargin& ComputedPhysicalOffsets() const { return mComputedOffsets; } nsMargin& ComputedPhysicalOffsets() { return mComputedOffsets; } const LogicalMargin ComputedLogicalOffsets() const { return LogicalMargin(mWritingMode, mComputedOffsets); } void SetComputedLogicalOffsets(const LogicalMargin& aOffsets) { mComputedOffsets = aOffsets.GetPhysicalMargin(mWritingMode); } + // Return the state's computed size including border-padding, with + // unconstrained dimensions replaced by zero. + nsSize ComputedSizeAsContainerIfConstrained() const { + const nscoord wd = ComputedWidth(); + const nscoord ht = ComputedHeight(); + return nsSize(wd == NS_UNCONSTRAINEDSIZE + ? 0 : wd + ComputedPhysicalBorderPadding().LeftRight(), + ht == NS_UNCONSTRAINEDSIZE + ? 0 : ht + ComputedPhysicalBorderPadding().TopBottom()); + } + private: // the available width in which to reflow the frame. The space // represents the amount of room for the frame's margin, border, // padding, and content area. The frame size you choose should fit // within the available width. nscoord mAvailableWidth; // A value of NS_UNCONSTRAINEDSIZE for the available height means @@ -834,37 +850,37 @@ public: ApplyRelativePositioning(frame, ComputedPhysicalOffsets(), aPosition); } static void ApplyRelativePositioning(nsIFrame* aFrame, mozilla::WritingMode aWritingMode, const mozilla::LogicalMargin& aComputedOffsets, mozilla::LogicalPoint* aPosition, - nscoord aContainerWidth) { - // Subtract the width of the frame from the container width that we + const nsSize& aContainerSize) { + // Subtract the size of the frame from the container size that we // use for converting between the logical and physical origins of // the frame. This accounts for the fact that logical origins in RTL // coordinate systems are at the top right of the frame instead of // the top left. - nscoord frameWidth = aFrame->GetSize().width; + nsSize frameSize = aFrame->GetSize(); nsPoint pos = aPosition->GetPhysicalPoint(aWritingMode, - aContainerWidth - frameWidth); + aContainerSize - frameSize); ApplyRelativePositioning(aFrame, aComputedOffsets.GetPhysicalMargin(aWritingMode), &pos); *aPosition = mozilla::LogicalPoint(aWritingMode, pos, - aContainerWidth - frameWidth); + aContainerSize - frameSize); } void ApplyRelativePositioning(mozilla::LogicalPoint* aPosition, - nscoord aContainerWidth) const { + const nsSize& aContainerSize) const { ApplyRelativePositioning(frame, mWritingMode, ComputedLogicalOffsets(), aPosition, - aContainerWidth); + aContainerSize); } #ifdef DEBUG // Reflow trace methods. Defined in nsFrame.cpp so they have access // to the display-reflow infrastructure. static void* DisplayInitConstraintsEnter(nsIFrame* aFrame, nsHTMLReflowState* aState, nscoord aCBISize,
--- a/layout/generic/nsIFrame.h +++ b/layout/generic/nsIFrame.h @@ -654,49 +654,49 @@ public: nsSize GetSize() const { return mRect.Size(); } nsRect GetRectRelativeToSelf() const { return nsRect(nsPoint(0, 0), mRect.Size()); } /** * Dimensions and position in logical coordinates in the frame's writing mode * or another writing mode */ - mozilla::LogicalRect GetLogicalRect(nscoord aContainerWidth) const { - return GetLogicalRect(GetWritingMode(), aContainerWidth); + mozilla::LogicalRect GetLogicalRect(const nsSize& aContainerSize) const { + return GetLogicalRect(GetWritingMode(), aContainerSize); } - mozilla::LogicalPoint GetLogicalPosition(nscoord aContainerWidth) const { - return GetLogicalPosition(GetWritingMode(), aContainerWidth); + mozilla::LogicalPoint GetLogicalPosition(const nsSize& aContainerSize) const { + return GetLogicalPosition(GetWritingMode(), aContainerSize); } mozilla::LogicalSize GetLogicalSize() const { return GetLogicalSize(GetWritingMode()); } mozilla::LogicalRect GetLogicalRect(mozilla::WritingMode aWritingMode, - nscoord aContainerWidth) const { - return mozilla::LogicalRect(aWritingMode, GetRect(), aContainerWidth); + const nsSize& aContainerSize) const { + return mozilla::LogicalRect(aWritingMode, GetRect(), aContainerSize); } mozilla::LogicalPoint GetLogicalPosition(mozilla::WritingMode aWritingMode, - nscoord aContainerWidth) const { - return GetLogicalRect(aWritingMode, aContainerWidth).Origin(aWritingMode); + const nsSize& aContainerSize) const { + return GetLogicalRect(aWritingMode, aContainerSize).Origin(aWritingMode); } mozilla::LogicalSize GetLogicalSize(mozilla::WritingMode aWritingMode) const { return mozilla::LogicalSize(aWritingMode, GetSize()); } - nscoord IStart(nscoord aContainerWidth) const { - return IStart(GetWritingMode(), aContainerWidth); + nscoord IStart(const nsSize& aContainerSize) const { + return IStart(GetWritingMode(), aContainerSize); } nscoord IStart(mozilla::WritingMode aWritingMode, - nscoord aContainerWidth) const { - return GetLogicalPosition(aWritingMode, aContainerWidth).I(aWritingMode); + const nsSize& aContainerSize) const { + return GetLogicalPosition(aWritingMode, aContainerSize).I(aWritingMode); } - nscoord BStart(nscoord aContainerWidth) const { - return BStart(GetWritingMode(), aContainerWidth); + nscoord BStart(const nsSize& aContainerSize) const { + return BStart(GetWritingMode(), aContainerSize); } nscoord BStart(mozilla::WritingMode aWritingMode, - nscoord aContainerWidth) const { - return GetLogicalPosition(aWritingMode, aContainerWidth).B(aWritingMode); + const nsSize& aContainerSize) const { + return GetLogicalPosition(aWritingMode, aContainerSize).B(aWritingMode); } nscoord ISize() const { return ISize(GetWritingMode()); } nscoord ISize(mozilla::WritingMode aWritingMode) const { return GetLogicalSize(aWritingMode).ISize(aWritingMode); } nscoord BSize() const { return BSize(GetWritingMode()); } nscoord BSize(mozilla::WritingMode aWritingMode) const { return GetLogicalSize(aWritingMode).BSize(aWritingMode); @@ -716,27 +716,28 @@ public: SetOverflowAreas(overflow); } else { mRect = aRect; } } /** * Set this frame's rect from a logical rect in its own writing direction */ - void SetRect(const mozilla::LogicalRect& aRect, nscoord aContainerWidth) { - SetRect(GetWritingMode(), aRect, aContainerWidth); + void SetRect(const mozilla::LogicalRect& aRect, + const nsSize& aContainerSize) { + SetRect(GetWritingMode(), aRect, aContainerSize); } /** * Set this frame's rect from a logical rect in a different writing direction * (GetPhysicalRect will assert if the writing mode doesn't match) */ void SetRect(mozilla::WritingMode aWritingMode, const mozilla::LogicalRect& aRect, - nscoord aContainerWidth) { - SetRect(aRect.GetPhysicalRect(aWritingMode, aContainerWidth)); + const nsSize& aContainerSize) { + SetRect(aRect.GetPhysicalRect(aWritingMode, aContainerSize)); } /** * Set this frame's size from a logical size in its own writing direction. * This leaves the frame's logical position unchanged, which means its * physical position may change (for right-to-left modes). */ void SetSize(const mozilla::LogicalSize& aSize) { @@ -766,22 +767,22 @@ public: */ void SetSize(const nsSize& aSize) { SetRect(nsRect(mRect.TopLeft(), aSize)); } void SetPosition(const nsPoint& aPt) { mRect.MoveTo(aPt); } void SetPosition(mozilla::WritingMode aWritingMode, const mozilla::LogicalPoint& aPt, - nscoord aContainerWidth) { - // We subtract mRect.width from the container width to account for + const nsSize& aContainerSize) { + // We subtract mRect.Size() from the container size to account for // the fact that logical origins in RTL coordinate systems are at // the top right of the frame instead of the top left. mRect.MoveTo(aPt.GetPhysicalPoint(aWritingMode, - aContainerWidth - mRect.width)); + aContainerSize - mRect.Size())); } /** * Move the frame, accounting for relative positioning. Use this when * adjusting the frame's position by a known amount, to properly update its * saved normal position (see GetNormalPosition below). * * This must be used only when moving a frame *after* @@ -793,38 +794,43 @@ public: void MovePositionBy(const nsPoint& aTranslation); /** * As above, using a logical-point delta in a given writing mode. */ void MovePositionBy(mozilla::WritingMode aWritingMode, const mozilla::LogicalPoint& aTranslation) { - MovePositionBy(aTranslation.GetPhysicalPoint(aWritingMode, 0)); + // The LogicalPoint represents a vector rather than a point within a + // rectangular coordinate space, so we use a null containerSize when + // converting logical to physical. + const nsSize nullContainerSize; + MovePositionBy(aTranslation.GetPhysicalPoint(aWritingMode, + nullContainerSize)); } /** * Return frame's rect without relative positioning */ nsRect GetNormalRect() const; /** * Return frame's position without relative positioning */ nsPoint GetNormalPosition() const; mozilla::LogicalPoint GetLogicalNormalPosition(mozilla::WritingMode aWritingMode, - nscoord aContainerWidth) const + const nsSize& aContainerSize) const { - // Subtract the width of this frame from the container width to get + // Subtract the size of this frame from the container size to get // the correct position in rtl frames where the origin is on the // right instead of the left return mozilla::LogicalPoint(aWritingMode, GetNormalPosition(), - aContainerWidth - mRect.width); + aContainerSize - mRect.Size()); } virtual nsPoint GetPositionOfChildIgnoringScrolling(nsIFrame* aChild) { return aChild->GetPosition(); } nsPoint GetPositionIgnoringScrolling(); static void DestroyContentArray(void* aPropertyValue);
--- a/layout/generic/nsLineBox.cpp +++ b/layout/generic/nsLineBox.cpp @@ -27,17 +27,17 @@ int32_t nsLineBox::GetCtorCount() { retu // static nsLineBox constant; initialized in the header file. const uint32_t nsLineBox::kMinChildCountForHashtable; #endif using namespace mozilla; nsLineBox::nsLineBox(nsIFrame* aFrame, int32_t aCount, bool aIsBlock) : mFirstChild(aFrame) - , mContainerWidth(-1) + , mContainerSize(-1, -1) , mBounds(WritingMode()) // mBounds will be initialized with the correct // writing mode when it is set // NOTE: memory is already zeroed since we allocate with AllocateByObjectID. { MOZ_COUNT_CTOR(nsLineBox); #ifdef DEBUG ++ctorCount; NS_ASSERTION(!aIsBlock || aCount == 1, "Blocks must have exactly one child"); @@ -75,17 +75,17 @@ NS_NewLineBox(nsIPresShell* aPresShell, } nsLineBox* NS_NewLineBox(nsIPresShell* aPresShell, nsLineBox* aFromLine, nsIFrame* aFrame, int32_t aCount) { nsLineBox* newLine = new (aPresShell) nsLineBox(aFrame, aCount, false); newLine->NoteFramesMovedFrom(aFromLine); - newLine->mContainerWidth = aFromLine->mContainerWidth; + newLine->mContainerSize = aFromLine->mContainerSize; return newLine; } void nsLineBox::StealHashTableFrom(nsLineBox* aFromLine, uint32_t aFromLineNewCount) { MOZ_ASSERT(!mFlags.mHasHashedFrames); MOZ_ASSERT(GetChildCount() >= int32_t(aFromLineNewCount)); @@ -244,23 +244,23 @@ nsLineBox::List(FILE* out, const char* a StateToString(cbuf, sizeof(cbuf))); if (IsBlock() && !GetCarriedOutBEndMargin().IsZero()) { str += nsPrintfCString("bm=%d ", GetCarriedOutBEndMargin().get()); } nsRect bounds = GetPhysicalBounds(); str += nsPrintfCString("{%d,%d,%d,%d} ", bounds.x, bounds.y, bounds.width, bounds.height); if (mWritingMode.IsVertical() || !mWritingMode.IsBidiLTR()) { - str += nsPrintfCString("{%s-%s: %d,%d,%d,%d; cw=%d} ", + str += nsPrintfCString("{%s-%s: %d,%d,%d,%d; cs=%d,%d} ", mWritingMode.IsVertical() ? mWritingMode.IsVerticalLR() ? "vlr" : "vrl" : "htb", mWritingMode.IsBidiLTR() ? "ltr" : "rtl", IStart(), BStart(), ISize(), BSize(), - mContainerWidth); + mContainerSize.width, mContainerSize.height); } if (mData && (!mData->mOverflowAreas.VisualOverflow().IsEqualEdges(bounds) || !mData->mOverflowAreas.ScrollableOverflow().IsEqualEdges(bounds))) { str += nsPrintfCString("vis-overflow=%d,%d,%d,%d scr-overflow=%d,%d,%d,%d ", mData->mOverflowAreas.VisualOverflow().x, mData->mOverflowAreas.VisualOverflow().y, mData->mOverflowAreas.VisualOverflow().width, @@ -732,37 +732,39 @@ nsLineIterator::FindFrameAt(int32_t aLin if (line->ISize() == 0 && line->BSize() == 0) return NS_ERROR_FAILURE; nsIFrame* frame = line->mFirstChild; nsIFrame* closestFromStart = nullptr; nsIFrame* closestFromEnd = nullptr; WritingMode wm = line->mWritingMode; - nscoord cw = line->mContainerWidth; + nsSize containerSize = line->mContainerSize; - LogicalPoint pos(wm, aPos, cw); + LogicalPoint pos(wm, aPos, containerSize); int32_t n = line->GetChildCount(); while (n--) { - LogicalRect rect = frame->GetLogicalRect(wm, cw); + LogicalRect rect = frame->GetLogicalRect(wm, containerSize); if (rect.ISize(wm) > 0) { // If pos.I() is inside this frame - this is it if (rect.IStart(wm) <= pos.I(wm) && rect.IEnd(wm) > pos.I(wm)) { closestFromStart = closestFromEnd = frame; break; } if (rect.IStart(wm) < pos.I(wm)) { if (!closestFromStart || - rect.IEnd(wm) > closestFromStart->GetLogicalRect(wm, cw).IEnd(wm)) + rect.IEnd(wm) > closestFromStart-> + GetLogicalRect(wm, containerSize).IEnd(wm)) closestFromStart = frame; } else { if (!closestFromEnd || - rect.IStart(wm) < closestFromEnd->GetLogicalRect(wm, cw).IStart(wm)) + rect.IStart(wm) < closestFromEnd-> + GetLogicalRect(wm, containerSize).IStart(wm)) closestFromEnd = frame; } } frame = frame->GetNextSibling(); } if (!closestFromStart && !closestFromEnd) { // All frames were zero-width. Just take the first one. closestFromStart = closestFromEnd = line->mFirstChild; @@ -774,22 +776,25 @@ nsLineIterator::FindFrameAt(int32_t aLin } else if (!closestFromStart) { *aFrameFound = closestFromEnd; } else if (!closestFromEnd) { *aFrameFound = closestFromStart; } else { // we're between two frames - nscoord delta = closestFromEnd->GetLogicalRect(wm, cw).IStart(wm) - - closestFromStart->GetLogicalRect(wm, cw).IEnd(wm); - if (pos.I(wm) < closestFromStart->GetLogicalRect(wm, cw).IEnd(wm) + delta/2) + nscoord delta = + closestFromEnd->GetLogicalRect(wm, containerSize).IStart(wm) - + closestFromStart->GetLogicalRect(wm, containerSize).IEnd(wm); + if (pos.I(wm) < closestFromStart-> + GetLogicalRect(wm, containerSize).IEnd(wm) + delta/2) { *aFrameFound = closestFromStart; - else + } else { *aFrameFound = closestFromEnd; + } } return NS_OK; } NS_IMETHODIMP nsLineIterator::GetNextSiblingOnLine(nsIFrame*& aFrame, int32_t aLineNumber) { aFrame = aFrame->GetNextSibling();
--- a/layout/generic/nsLineBox.h +++ b/layout/generic/nsLineBox.h @@ -443,80 +443,85 @@ public: // Combined area is the area of the line that should influence the // overflow area of its parent block. The combined area should be // used for painting-related things, but should never be used for // layout (except for handling of 'overflow'). void SetOverflowAreas(const nsOverflowAreas& aOverflowAreas); mozilla::LogicalRect GetOverflowArea(nsOverflowType aType, mozilla::WritingMode aWM, - nscoord aContainerWidth) + const nsSize& aContainerSize) { - return mozilla::LogicalRect(aWM, GetOverflowArea(aType), aContainerWidth); + return mozilla::LogicalRect(aWM, GetOverflowArea(aType), aContainerSize); } nsRect GetOverflowArea(nsOverflowType aType) { return mData ? mData->mOverflowAreas.Overflow(aType) : GetPhysicalBounds(); } nsOverflowAreas GetOverflowAreas() { if (mData) { return mData->mOverflowAreas; } nsRect bounds = GetPhysicalBounds(); return nsOverflowAreas(bounds, bounds); } nsRect GetVisualOverflowArea() { return GetOverflowArea(eVisualOverflow); } nsRect GetScrollableOverflowArea() { return GetOverflowArea(eScrollableOverflow); } - void SlideBy(nscoord aDBCoord, nscoord aContainerWidth) { - NS_ASSERTION(aContainerWidth == mContainerWidth || mContainerWidth == -1, - "container width doesn't match"); - mContainerWidth = aContainerWidth; + void SlideBy(nscoord aDBCoord, const nsSize& aContainerSize) { + NS_ASSERTION(aContainerSize == mContainerSize || + mContainerSize == nsSize(-1, -1), + "container size doesn't match"); + mContainerSize = aContainerSize; mBounds.BStart(mWritingMode) += aDBCoord; if (mData) { - nsPoint physicalDelta = mozilla::LogicalPoint(mWritingMode, 0, aDBCoord). - GetPhysicalPoint(mWritingMode, 0); + // Use a null containerSize to convert vector from logical to physical. + const nsSize nullContainerSize; + nsPoint physicalDelta = + mozilla::LogicalPoint(mWritingMode, 0, aDBCoord). + GetPhysicalPoint(mWritingMode, nullContainerSize); NS_FOR_FRAME_OVERFLOW_TYPES(otype) { mData->mOverflowAreas.Overflow(otype) += physicalDelta; } } } - // Container-width for the line is changing (and therefore if writing mode + // Container-size for the line is changing (and therefore if writing mode // was vertical-rl, the line will move physically; this is like SlideBy, - // but it is the container width instead of the line's own logical coord + // but it is the container size instead of the line's own logical coord // that is changing. - nscoord UpdateContainerWidth(nscoord aNewContainerWidth) + nsSize UpdateContainerSize(const nsSize aNewContainerSize) { - NS_ASSERTION(mContainerWidth != -1, "container width not set"); - nscoord delta = mContainerWidth - aNewContainerWidth; - mContainerWidth = aNewContainerWidth; + NS_ASSERTION(mContainerSize != nsSize(-1, -1), "container size not set"); + nsSize delta = mContainerSize - aNewContainerSize; + mContainerSize = aNewContainerSize; // this has a physical-coordinate effect only in vertical-rl mode if (mWritingMode.IsVerticalRL() && mData) { - nsPoint physicalDelta = mozilla::LogicalPoint(mWritingMode, 0, delta). - GetPhysicalPoint(mWritingMode, 0); + nsPoint physicalDelta(-delta.width, 0); NS_FOR_FRAME_OVERFLOW_TYPES(otype) { mData->mOverflowAreas.Overflow(otype) += physicalDelta; } } return delta; } - void IndentBy(nscoord aDICoord, nscoord aContainerWidth) { - NS_ASSERTION(aContainerWidth == mContainerWidth || mContainerWidth == -1, - "container width doesn't match"); - mContainerWidth = aContainerWidth; + void IndentBy(nscoord aDICoord, const nsSize& aContainerSize) { + NS_ASSERTION(aContainerSize == mContainerSize || + mContainerSize == nsSize(-1, -1), + "container size doesn't match"); + mContainerSize = aContainerSize; mBounds.IStart(mWritingMode) += aDICoord; } - void ExpandBy(nscoord aDISize, nscoord aContainerWidth) { - NS_ASSERTION(aContainerWidth == mContainerWidth || mContainerWidth == -1, - "container width doesn't match"); - mContainerWidth = aContainerWidth; + void ExpandBy(nscoord aDISize, const nsSize& aContainerSize) { + NS_ASSERTION(aContainerSize == mContainerSize || + mContainerSize == nsSize(-1, -1), + "container size doesn't match"); + mContainerSize = aContainerSize; mBounds.ISize(mWritingMode) += aDISize; } /** * The logical ascent (distance from block-start to baseline) of the * linebox is the logical ascent of the anonymous inline box (for * which we don't actually create a frame) that wraps all the * consecutive inline children of a block. @@ -603,49 +608,50 @@ public: #ifdef DEBUG static int32_t GetCtorCount(); #endif nsIFrame* mFirstChild; mozilla::WritingMode mWritingMode; - // Physical width. Use only for physical <-> logical coordinate conversion. - nscoord mContainerWidth; + // Physical size. Use only for physical <-> logical coordinate conversion. + nsSize mContainerSize; private: mozilla::LogicalRect mBounds; public: const mozilla::LogicalRect& GetBounds() { return mBounds; } nsRect GetPhysicalBounds() const { if (mBounds.IsAllZero()) { return nsRect(0, 0, 0, 0); } - NS_ASSERTION(mContainerWidth != -1, "mContainerWidth not initialized"); - return mBounds.GetPhysicalRect(mWritingMode, mContainerWidth); + NS_ASSERTION(mContainerSize != nsSize(-1, -1), + "mContainerSize not initialized"); + return mBounds.GetPhysicalRect(mWritingMode, mContainerSize); } void SetBounds(mozilla::WritingMode aWritingMode, nscoord aIStart, nscoord aBStart, nscoord aISize, nscoord aBSize, - nscoord aContainerWidth) + const nsSize& aContainerSize) { mWritingMode = aWritingMode; - mContainerWidth = aContainerWidth; + mContainerSize = aContainerSize; mBounds = mozilla::LogicalRect(aWritingMode, aIStart, aBStart, aISize, aBSize); } void SetBounds(mozilla::WritingMode aWritingMode, - nsRect aRect, nscoord aContainerWidth) + nsRect aRect, const nsSize& aContainerSize) { mWritingMode = aWritingMode; - mContainerWidth = aContainerWidth; - mBounds = mozilla::LogicalRect(aWritingMode, aRect, aContainerWidth); + mContainerSize = aContainerSize; + mBounds = mozilla::LogicalRect(aWritingMode, aRect, aContainerSize); } // mFlags.mHasHashedFrames says which one to use union { nsTHashtable< nsPtrHashKey<nsIFrame> >* mFrames; uint32_t mChildCount; };
--- a/layout/generic/nsLineLayout.cpp +++ b/layout/generic/nsLineLayout.cpp @@ -311,17 +311,17 @@ void nsLineLayout::UpdateBand(WritingMode aWM, const LogicalRect& aNewAvailSpace, nsIFrame* aFloatFrame) { WritingMode lineWM = mRootSpan->mWritingMode; // need to convert to our writing mode, because we might have a different // mode from the caller due to dir: auto LogicalRect availSpace = aNewAvailSpace.ConvertTo(lineWM, aWM, - ContainerWidth()); + ContainerSize()); #ifdef REALLY_NOISY_REFLOW printf("nsLL::UpdateBand %d, %d, %d, %d, (converted to %d, %d, %d, %d); frame=%p\n will set mImpacted to true\n", aNewAvailSpace.x, aNewAvailSpace.y, aNewAvailSpace.width, aNewAvailSpace.height, availSpace.IStart(lineWM), availSpace.BStart(lineWM), availSpace.ISize(lineWM), availSpace.BSize(lineWM), aFloatFrame); #endif @@ -937,17 +937,17 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra // going to end up moving it when we do the block-direction alignment. // Adjust spacemanager coordinate system for the frame. nsHTMLReflowMetrics metrics(lineWM); #ifdef DEBUG metrics.ISize(lineWM) = nscoord(0xdeadbeef); metrics.BSize(lineWM) = nscoord(0xdeadbeef); #endif - nscoord tI = pfd->mBounds.LineLeft(lineWM, ContainerWidth()); + nscoord tI = pfd->mBounds.LineLeft(lineWM, ContainerSize()); nscoord tB = pfd->mBounds.BStart(lineWM); mFloatManager->Translate(tI, tB); int32_t savedOptionalBreakOffset; gfxBreakPriority savedOptionalBreakPriority; nsIFrame* savedOptionalBreakFrame = GetLastOptionalBreakPosition(&savedOptionalBreakOffset, &savedOptionalBreakPriority); @@ -1066,17 +1066,17 @@ nsLineLayout::ReflowFrame(nsIFrame* aFra // just the union of the bounds of any absolute children. That is // added in later by nsLineLayout::ReflowInlineFrames. pfd->mOverflowAreas = metrics.mOverflowAreas; pfd->mBounds.ISize(lineWM) = metrics.ISize(lineWM); pfd->mBounds.BSize(lineWM) = metrics.BSize(lineWM); // Size the frame, but |RelativePositionFrames| will size the view. - aFrame->SetRect(lineWM, pfd->mBounds, ContainerWidthForSpan(psd)); + aFrame->SetRect(lineWM, pfd->mBounds, ContainerSizeForSpan(psd)); // Tell the frame that we're done reflowing it aFrame->DidReflow(mPresContext, isText ? nullptr : reflowStateHolder.ptr(), nsDidReflowStatus::FINISHED); if (aMetrics) { *aMetrics = metrics; @@ -1245,23 +1245,26 @@ nsLineLayout::SyncAnnotationBounds(PerFr MOZ_ASSERT(aRubyFrame->mSpan); PerSpanData* span = aRubyFrame->mSpan; WritingMode lineWM = mRootSpan->mWritingMode; for (PerFrameData* pfd = span->mFirstFrame; pfd; pfd = pfd->mNext) { for (PerFrameData* rtc = pfd->mNextAnnotation; rtc; rtc = rtc->mNextAnnotation) { // When the annotation container is reflowed, the width of the - // ruby container is unknown, hence zero should be used here - // as container width to get the correct logical rect. - LogicalRect rtcBounds(lineWM, rtc->mFrame->GetRect(), 0); + // ruby container is unknown so we use a dummy container size; + // in the case of RTL block direction, the final position will be + // fixed up later. + const nsSize dummyContainerSize; + LogicalRect rtcBounds(lineWM, rtc->mFrame->GetRect(), + dummyContainerSize); rtc->mBounds = rtcBounds; - nscoord rtcWidth = rtcBounds.Width(lineWM); + nsSize rtcSize = rtcBounds.Size(lineWM).GetPhysicalSize(lineWM); for (PerFrameData* rt = rtc->mSpan->mFirstFrame; rt; rt = rt->mNext) { - LogicalRect rtBounds = rt->mFrame->GetLogicalRect(lineWM, rtcWidth); + LogicalRect rtBounds = rt->mFrame->GetLogicalRect(lineWM, rtcSize); MOZ_ASSERT(rt->mBounds.Size(lineWM) == rtBounds.Size(lineWM), "Size of the annotation should not have been changed"); rt->mBounds.SetOrigin(lineWM, rtBounds.Origin(lineWM));