Bug 1401991 - Ensure that we don't hide panelviews that are already reparented to another multi-view and ensure to hide other panels consistently. r=Gijs

* Harden the new `hideAllViewsExcept()` to not do erroneous things if called when
   the binding is already gone.
 * Generalize things into `hideAllViewsExcept(thisOne)`:
    - Clear `_viewShowing` in there and do the descriptionHeightWorkaround thing
      in there too,
    - For Photon panels, do all the 'current' attribute setting in there. To show
      a panel during transition, I introduced the 'in-transition' attribute.
 * I had to make sure not to over-eagerly dispatch 'ViewShowing' events, because
   that confuses some,
 * Move the temporary panel handling, which contains an ephemeral panelmultiview
   instance, internally. This cleans up the hacky, duplicate PanelUI.js code nicely.
 * Keep a local copy of `_transitionDetails` to ensure it's still there after transition,
 * Harden `_cleanupTransitionPhase()` to only clear the phase that belongs to a
   specific transition, _if_ that's passed in as an argument. This resolves any
   potential raciness that might occur when `showSubView()` is called again mid-transition.
 * Skip the UITour element visibility check when it's inside a panelview, because
   too many things need to happen and that check is too simple to be useful in
   that case.

MozReview-Commit-ID: 5HpJKs1Ny5j

--HG--
extra : rebase_source : b810e1de2dbd75932a42d68e751fdaecd9fee69a
This commit is contained in:
Mike de Boer 2017-09-29 13:51:51 +02:00
Родитель cf92431595
Коммит 882fa06e25
11 изменённых файлов: 225 добавлений и 212 удалений

Просмотреть файл

@ -90,12 +90,12 @@ panel[hidden] panelview {
transition: transform var(--panelui-subview-transition-duration);
}
panelview:not([mainview]):not([current]) {
panelview:not([mainview]):not([current]):not([in-transition]) {
transition: visibility 0s linear var(--panelui-subview-transition-duration);
visibility: collapse;
}
photonpanelmultiview panelview:not([current]) {
photonpanelmultiview panelview:not([current]):not([in-transition]) {
transition: none;
visibility: collapse;
}

Просмотреть файл

@ -68,7 +68,7 @@ const EXPECTED_APPMENU_SUBVIEW_REFLOWS = [
{
stack: [
"descriptionHeightWorkaround@resource:///modules/PanelMultiView.jsm",
"_cleanupTransitionPhase@resource:///modules/PanelMultiView.jsm",
"hideAllViewsExcept@resource:///modules/PanelMultiView.jsm",
],
times: 2, // This number should only ever go down - never up.

Просмотреть файл

@ -191,6 +191,15 @@ this.PanelMultiView = class {
}
}
/**
* @return {Boolean} |true| when the 'ephemeral' attribute is set, which means
* that this instance should be ready to be thrown away at
* any time.
*/
get _ephemeral() {
return this.node.hasAttribute("ephemeral");
}
get panelViews() {
// If there's a dedicated subViews container, we're not in the right binding
// to use SlidingPanelViews.
@ -217,10 +226,9 @@ this.PanelMultiView = class {
.getService(Ci.nsIScreenManager);
}
/**
* Getter that returns the currently visible subview OR the subview that is
* about to be shown whilst a 'ViewShowing' event is being dispatched.
*
* @return {panelview}
* @return {panelview} the currently visible subview OR the subview that is
* about to be shown whilst a 'ViewShowing' event is being
* dispatched.
*/
get current() {
return this._viewShowing || this._currentSubView
@ -235,6 +243,13 @@ this.PanelMultiView = class {
this.__currentSubView = panel;
return panel;
}
/**
* @return {Promise} showSubView() returns a promise, which is kept here for
* random access.
*/
get currentShowPromise() {
return this._currentShowPromise || Promise.resolve();
}
get _keyNavigationMap() {
if (!this.__keyNavigationMap)
this.__keyNavigationMap = new Map();
@ -316,9 +331,11 @@ this.PanelMultiView = class {
value: (...args) => this[method](...args)
});
});
Object.defineProperty(this.node, "current", {
enumerable: true,
get: () => this.current
["current", "currentShowPromise"].forEach(property => {
Object.defineProperty(this.node, property, {
enumerable: true,
get: () => this[property]
});
});
}
@ -327,8 +344,10 @@ this.PanelMultiView = class {
if (!this.node)
return;
if (this._mainView) {
let mainView = this._mainView;
if (this._ephemeral)
this.hideAllViewsExcept(null);
let mainView = this._mainView;
if (mainView) {
if (this._panelViewCache)
this._panelViewCache.appendChild(mainView);
mainView.removeAttribute("mainview");
@ -395,10 +414,13 @@ this.PanelMultiView = class {
* @return {Boolean}
*/
_canGoBack(view = this._currentSubView) {
return view != this._mainView;
return view.id != this._mainViewId;
}
setMainView(aNewMainView) {
if (!aNewMainView)
return;
if (this._mainView) {
if (!this.panelViews)
this._subViews.appendChild(this._mainView);
@ -418,38 +440,59 @@ this.PanelMultiView = class {
}
showMainView() {
if (!this._mainViewId)
return Promise.resolve();
if (this.panelViews)
return this.showSubView(this._mainView);
if (this.showingSubView) {
let viewNode = this._currentSubView;
this._dispatchViewEvent(viewNode, "ViewHiding");
if (this.panelViews) {
this._transitionHeight(() => {
viewNode.removeAttribute("current");
this.showSubView(this._mainViewId);
} else {
this._transitionHeight(() => {
viewNode.removeAttribute("current");
this._currentSubView = null;
this.node.setAttribute("viewtype", "main");
});
}
} else if (this.panelViews) {
// Make sure to hide all subviews, except for the mainView.
let mainView = this._mainView;
for (let panelview of this._panelViews) {
if (panelview == mainView)
panelview.setAttribute("current", true);
else
panelview.removeAttribute("current");
}
this.node.setAttribute("viewtype", "main");
this._currentSubView = null;
this.node.setAttribute("viewtype", "main");
});
}
if (!this.panelViews) {
this._shiftMainView();
this._shiftMainView();
return Promise.resolve();
}
/**
* Ensures that all the panelviews, that are currently part of this instance,
* are hidden, except one specifically.
*
* @param {panelview} [theOne] The panelview DOM node to ensure is visible.
* Optional.
*/
hideAllViewsExcept(theOne = null) {
for (let panelview of this._panelViews) {
// When the panelview was already reparented, don't interfere any more.
if (panelview == theOne || !this.node || panelview.panelMultiView != this.node)
continue;
if (panelview.hasAttribute("current"))
this._dispatchViewEvent(panelview, "ViewHiding");
panelview.removeAttribute("current");
}
this._viewShowing = null;
if (!this.node || !theOne)
return;
this._currentSubView = theOne;
if (!theOne.hasAttribute("current")) {
theOne.setAttribute("current", true);
this.descriptionHeightWorkaround(theOne);
this._dispatchViewEvent(theOne, "ViewShown");
}
this.node.setAttribute("viewtype", (theOne.id == this._mainViewId) ? "main" : "subview");
}
showSubView(aViewId, aAnchor, aPreviousView) {
return (async () => {
this._currentShowPromise = (async () => {
// Support passing in the node directly.
let viewNode = typeof aViewId == "string" ? this.node.querySelector("#" + aViewId) : aViewId;
if (!viewNode) {
@ -465,8 +508,10 @@ this.PanelMultiView = class {
let reverse = !!aPreviousView;
let previousViewNode = aPreviousView || this._currentSubView;
let playTransition = (!!previousViewNode && previousViewNode != viewNode &&
this._panel.state == "open");
// If the panelview to show is the same as the previous one, the 'ViewShowing'
// event has already been dispatched. Don't do it twice.
let showingSameView = viewNode == previousViewNode;
let playTransition = (!!previousViewNode && !showingSameView && this._panel.state == "open");
let dwu, previousRect;
if (playTransition || this.panelViews) {
@ -492,6 +537,13 @@ this.PanelMultiView = class {
}
this._viewShowing = viewNode;
// Because the 'mainview' attribute may be out-of-sync, due to view node
// reparenting in combination with ephemeral PanelMultiView instances,
// this is the best place to correct it (just before showing).
if (viewNode.id == this._mainViewId)
viewNode.setAttribute("mainview", true);
else
viewNode.removeAttribute("mainview");
// Make sure that new panels always have a title set.
if (this.panelViews && aAnchor) {
@ -502,53 +554,45 @@ this.PanelMultiView = class {
if (this.panelViews && this._mainViewWidth)
viewNode.style.maxWidth = viewNode.style.minWidth = this._mainViewWidth + "px";
// Emit the ViewShowing event so that the widget definition has a chance
// to lazily populate the subview with things or perhaps even cancel this
// whole operation.
let detail = {
blockers: new Set(),
addBlocker(promise) {
this.blockers.add(promise);
if (!showingSameView || !viewNode.hasAttribute("current")) {
// Emit the ViewShowing event so that the widget definition has a chance
// to lazily populate the subview with things or perhaps even cancel this
// whole operation.
let detail = {
blockers: new Set(),
addBlocker(promise) {
this.blockers.add(promise);
}
};
let cancel = this._dispatchViewEvent(viewNode, "ViewShowing", aAnchor, detail);
if (detail.blockers.size) {
try {
let results = await Promise.all(detail.blockers);
cancel = cancel || results.some(val => val === false);
} catch (e) {
Cu.reportError(e);
cancel = true;
}
}
};
let cancel = this._dispatchViewEvent(viewNode, "ViewShowing", aAnchor, detail);
if (detail.blockers.size) {
try {
let results = await Promise.all(detail.blockers);
cancel = cancel || results.some(val => val === false);
} catch (e) {
Cu.reportError(e);
cancel = true;
}
}
this._viewShowing = null;
if (cancel) {
return;
}
this._currentSubView = viewNode;
if (this.panelViews) {
if (viewNode.id == this._mainViewId) {
this.node.setAttribute("viewtype", "main");
} else {
this.node.setAttribute("viewtype", "subview");
}
// If we've got an older transition still running, make sure to clean it up.
await this._cleanupTransitionPhase();
if (!playTransition) {
viewNode.setAttribute("current", true);
this.descriptionHeightWorkaround(viewNode);
if (cancel) {
this._viewShowing = null;
return;
}
}
// Now we have to transition the panel.
if (this.panelViews && playTransition) {
await this._transitionViews(previousViewNode, viewNode, reverse, previousRect, aAnchor);
this._dispatchViewEvent(viewNode, "ViewShown");
this._updateKeyboardFocus(viewNode);
} else if (!this.panelViews) {
if (this.panelViews) {
// If we've got an older transition still running, make sure to clean it up.
await this._cleanupTransitionPhase();
if (playTransition) {
await this._transitionViews(previousViewNode, viewNode, reverse, previousRect, aAnchor);
this._updateKeyboardFocus(viewNode);
} else {
this.hideAllViewsExcept(viewNode);
}
} else {
this._currentSubView = viewNode;
this._transitionHeight(() => {
viewNode.setAttribute("current", true);
if (viewNode.id == this._mainViewId) {
@ -564,6 +608,7 @@ this.PanelMultiView = class {
this._shiftMainView(aAnchor);
}
})().catch(e => Cu.reportError(e));
return this._currentShowPromise;
}
/**
@ -596,7 +641,7 @@ this.PanelMultiView = class {
if (this._autoResizeWorkaroundTimer)
window.clearTimeout(this._autoResizeWorkaroundTimer);
this._transitionDetails = {
let details = this._transitionDetails = {
phase: TRANSITION_PHASES.START,
previousViewNode, viewNode, reverse, anchor
};
@ -604,6 +649,10 @@ this.PanelMultiView = class {
if (anchor)
anchor.setAttribute("open", "true");
// Since we're going to show two subview at the same time, don't abuse the
// 'current' attribute, since it's needed for other state-keeping, but use
// a separate 'in-transition' attribute instead.
previousViewNode.setAttribute("in-transition", true);
// Set the viewContainer dimensions to make sure only the current view is
// visible.
this._viewContainer.style.height = Math.max(previousRect.height, this._mainViewHeight) + "px";
@ -616,7 +665,7 @@ this.PanelMultiView = class {
let viewRect;
if (viewNode.__lastKnownBoundingRect) {
viewRect = viewNode.__lastKnownBoundingRect;
viewNode.setAttribute("current", true);
viewNode.setAttribute("in-transition", true);
} else if (viewNode.customRectGetter) {
// Can't use Object.assign directly with a DOM Rect object because its properties
// aren't enumerable.
@ -626,11 +675,11 @@ this.PanelMultiView = class {
if (header) {
viewRect.height += this._dwu.getBoundsWithoutFlushing(header).height;
}
viewNode.setAttribute("current", true);
viewNode.setAttribute("in-transition", true);
} else {
let oldSibling = viewNode.nextSibling || null;
this._offscreenViewStack.appendChild(viewNode);
viewNode.setAttribute("current", true);
viewNode.setAttribute("in-transition", true);
viewRect = await BrowserUtils.promiseLayoutFlushed(this.document, "layout", () => {
return this._dwu.getBoundsWithoutFlushing(viewNode);
@ -644,7 +693,7 @@ this.PanelMultiView = class {
}
this._transitioning = true;
this._transitionDetails.phase = TRANSITION_PHASES.PREPARE;
details.phase = TRANSITION_PHASES.PREPARE;
// The 'magic' part: build up the amount of pixels to move right or left.
let moveToLeft = (this._dir == "rtl" && !reverse) || (this._dir == "ltr" && reverse);
@ -679,26 +728,26 @@ this.PanelMultiView = class {
await BrowserUtils.promiseLayoutFlushed(document, "layout", () => {});
// Kick off the transition!
this._transitionDetails.phase = TRANSITION_PHASES.TRANSITION;
details.phase = TRANSITION_PHASES.TRANSITION;
this._viewStack.style.transform = "translateX(" + (moveToLeft ? "" : "-") + deltaX + "px)";
await new Promise(resolve => {
this._transitionDetails.resolve = resolve;
this._viewContainer.addEventListener("transitionend", this._transitionDetails.listener = ev => {
details.resolve = resolve;
this._viewContainer.addEventListener("transitionend", details.listener = ev => {
// It's quite common that `height` on the view container doesn't need
// to transition, so we make sure to do all the work on the transform
// transition-end, because that is guaranteed to happen.
if (ev.target != this._viewStack || ev.propertyName != "transform")
return;
this._viewContainer.removeEventListener("transitionend", this._transitionDetails.listener);
delete this._transitionDetails.listener;
this._viewContainer.removeEventListener("transitionend", details.listener);
delete details.listener;
resolve();
});
});
this._transitionDetails.phase = TRANSITION_PHASES.END;
details.phase = TRANSITION_PHASES.END;
await this._cleanupTransitionPhase();
await this._cleanupTransitionPhase(details);
}
/**
@ -706,8 +755,9 @@ this.PanelMultiView = class {
* above. Which attributes and properties depends on the phase the transition
* was left from - normally that'd be `TRANSITION_PHASES.END`.
*/
async _cleanupTransitionPhase() {
if (!this._transitionDetails)
async _cleanupTransitionPhase(details = this._transitionDetails) {
// Make sure to only clean up a phase from the most recent transition.
if (!this._transitionDetails || details != this._transitionDetails)
return;
let {phase, previousViewNode, viewNode, reverse, resolve, listener, anchor} = this._transitionDetails;
@ -715,11 +765,11 @@ this.PanelMultiView = class {
// Do the things we _always_ need to do whenever the transition ends or is
// interrupted.
this._dispatchViewEvent(previousViewNode, "ViewHiding");
previousViewNode.removeAttribute("current");
this.hideAllViewsExcept(viewNode);
previousViewNode.removeAttribute("in-transition");
viewNode.removeAttribute("in-transition");
if (reverse)
this._resetKeyNavigation(previousViewNode);
this.descriptionHeightWorkaround(viewNode);
if (anchor)
anchor.removeAttribute("open");
@ -915,7 +965,7 @@ this.PanelMultiView = class {
case "mousemove":
this._resetKeyNavigation();
break;
case "popupshowing":
case "popupshowing": {
this.node.setAttribute("panelopen", "true");
// Bug 941196 - The panel can get taller when opening a subview. Disabling
// autoPositioning means that the panel won't jump around if an opened
@ -971,13 +1021,14 @@ this.PanelMultiView = class {
// without any scrolling (using "display: flex;"), and only if the view
// exceeds the available space we set the height explicitly and enable
// scrolling.
if (this._mainView.hasAttribute("blockinboxworkaround")) {
let mainView = this._mainView;
if (mainView && mainView.hasAttribute("blockinboxworkaround")) {
let blockInBoxWorkaround = () => {
let mainViewHeight =
this._dwu.getBoundsWithoutFlushing(this._mainView).height;
this._dwu.getBoundsWithoutFlushing(mainView).height;
if (mainViewHeight > maxHeight) {
this._mainView.style.height = maxHeight + "px";
this._mainView.setAttribute("exceeding", "true");
mainView.style.height = maxHeight + "px";
mainView.setAttribute("exceeding", "true");
}
};
// On Windows, we cannot measure the full height of the main view
@ -993,12 +1044,14 @@ this.PanelMultiView = class {
}
}
break;
}
case "popupshown":
// Now that the main view is visible, we can check the height of the
// description elements it contains.
this.descriptionHeightWorkaround();
if (!this.panelViews)
this.descriptionHeightWorkaround();
break;
case "popuphidden":
case "popuphidden": {
// WebExtensions consumers can hide the popup from viewshowing, or
// mid-transition, which disrupts our state:
this._viewShowing = null;
@ -1029,12 +1082,14 @@ this.PanelMultiView = class {
// Always try to layout the panel normally when reopening it. This is
// also the layout that will be used in customize mode.
if (this._mainView.hasAttribute("blockinboxworkaround")) {
this._mainView.style.removeProperty("height");
this._mainView.removeAttribute("exceeding");
let mainView = this._mainView;
if (mainView && mainView.hasAttribute("blockinboxworkaround")) {
mainView.style.removeProperty("height");
mainView.removeAttribute("exceeding");
}
this._dispatchViewEvent(this.node, "PanelMultiViewHidden");
break;
}
}
}
@ -1257,7 +1312,7 @@ this.PanelMultiView = class {
* view if omitted.
*/
descriptionHeightWorkaround(viewNode = this._mainView) {
if (!viewNode.hasAttribute("descriptionheightworkaround")) {
if (!viewNode || !viewNode.hasAttribute("descriptionheightworkaround")) {
// This view does not require the workaround.
return;
}

Просмотреть файл

@ -419,31 +419,37 @@ const PanelUI = {
tempPanel.setAttribute("animate", "false");
}
tempPanel.setAttribute("context", "");
tempPanel.setAttribute("photon", true);
document.getElementById(CustomizableUI.AREA_NAVBAR).appendChild(tempPanel);
// If the view has a footer, set a convenience class on the panel.
tempPanel.classList.toggle("cui-widget-panelWithFooter",
viewNode.querySelector(".panel-subview-footer"));
// If the panelview is already selected in another PanelMultiView instance
// as a subview, make sure to properly hide it there.
let oldMultiView = viewNode.panelMultiView;
if (oldMultiView && oldMultiView.current == viewNode) {
await oldMultiView.showMainView();
}
let viewShown = false;
let listener = () => viewShown = true;
viewNode.addEventListener("ViewShown", listener, {once: true});
let multiView = document.createElement("photonpanelmultiview");
multiView.setAttribute("id", "customizationui-widget-multiview");
multiView.setAttribute("nosubviews", "true");
multiView.setAttribute("viewCacheId", "appMenu-viewCache");
tempPanel.setAttribute("photon", true);
multiView.setAttribute("mainViewId", viewNode.id);
multiView.appendChild(viewNode);
multiView.setAttribute("ephemeral", true);
document.getElementById("appMenu-viewCache").appendChild(viewNode);
tempPanel.appendChild(multiView);
viewNode.classList.add("cui-widget-panelview");
let viewShown = false;
let panelRemover = () => {
viewNode.classList.remove("cui-widget-panelview");
if (viewShown) {
CustomizableUI.removePanelCloseListeners(tempPanel);
tempPanel.removeEventListener("popuphidden", panelRemover);
let currentView = multiView.current || viewNode;
let evt = new CustomEvent("ViewHiding", {detail: currentView});
currentView.dispatchEvent(evt);
}
aAnchor.open = false;
@ -453,35 +459,15 @@ const PanelUI = {
tempPanel.remove();
};
// Emit the ViewShowing event so that the widget definition has a chance
// to lazily populate the subview with things.
let detail = {
blockers: new Set(),
addBlocker(aPromise) {
this.blockers.add(aPromise);
},
};
// Wait until all the tasks needed to show a view are done.
await multiView.currentShowPromise;
let evt = new CustomEvent("ViewShowing", { bubbles: true, cancelable: true, detail });
viewNode.dispatchEvent(evt);
let cancel = evt.defaultPrevented;
if (detail.blockers.size) {
try {
let results = await Promise.all(detail.blockers);
cancel = cancel || results.some(val => val === false);
} catch (e) {
Components.utils.reportError(e);
cancel = true;
}
}
if (cancel) {
if (!viewShown) {
viewNode.removeEventListener("ViewShown", listener);
panelRemover();
return;
}
viewShown = true;
CustomizableUI.addPanelCloseListeners(tempPanel);
tempPanel.addEventListener("popuphidden", panelRemover);
@ -539,7 +525,8 @@ const PanelUI = {
withFavicons: true
});
// If there's nothing to display, or the panel is already hidden, get out.
if (!highlights.length || viewNode.panelMultiView.getAttribute("panelopen") != "true") {
let multiView = viewNode.panelMultiView;
if (!highlights.length || (multiView && multiView.getAttribute("panelopen") != "true")) {
this._loadingRecentHighlights = false;
return;
}

Просмотреть файл

@ -35,15 +35,16 @@ function checkSeparatorInsertion(menuId, buttonId, subviewId) {
await document.getElementById("nav-bar").overflowable.show();
let subview = document.getElementById(subviewId);
let button = document.getElementById(buttonId);
button.click();
await BrowserTestUtils.waitForEvent(subview, "ViewShown");
await BrowserTestUtils.waitForEvent(PanelUI.overflowPanel, "ViewShown");
let subview = document.getElementById(subviewId);
ok(subview.firstChild, "Subview should have a kid");
is(subview.firstChild.localName, "toolbarbutton", "There should be no separators to start with");
let subviewBody = subview.firstChild;
ok(subviewBody.firstChild, "Subview should have a kid");
is(subviewBody.firstChild.localName, "toolbarbutton", "There should be no separators to start with");
for (let kid of subview.children) {
for (let kid of subviewBody.children) {
if (kid.localName == "menuseparator") {
ok(kid.previousSibling && kid.previousSibling.localName != "menuseparator",
"Separators should never have another separator next to them, and should never be the first node.");
@ -58,7 +59,7 @@ function checkSeparatorInsertion(menuId, buttonId, subviewId) {
};
}
add_task(checkSeparatorInsertion("menuWebDeveloperPopup", "developer-button", "PanelUI-developerItems"));
add_task(checkSeparatorInsertion("menuWebDeveloperPopup", "developer-button", "PanelUI-developer"));
registerCleanupFunction(function() {
for (let el of tempElements) {

Просмотреть файл

@ -8,7 +8,6 @@ const kWidgetId = "test-981418-widget-onbeforecreated";
// Should be able to add broken view widget
add_task(async function testAddOnBeforeCreatedWidget() {
let viewShownDeferred = Promise.defer();
let onBeforeCreatedCalled = false;
let widgetSpec = {
id: kWidgetId,
@ -17,73 +16,47 @@ add_task(async function testAddOnBeforeCreatedWidget() {
onBeforeCreated(doc) {
let view = doc.createElement("panelview");
view.id = kWidgetId + "idontexistyet";
document.getElementById("PanelUI-multiView").appendChild(view);
document.getElementById("appMenu-viewCache").appendChild(view);
onBeforeCreatedCalled = true;
},
onViewShowing() {
viewShownDeferred.resolve();
}
};
let noError = true;
try {
CustomizableUI.createWidget(widgetSpec);
CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_NAVBAR);
} catch (ex) {
Cu.reportError(ex);
noError = false;
}
ok(noError, "Should not throw an exception trying to add the widget.");
CustomizableUI.createWidget(widgetSpec);
CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_NAVBAR);
ok(onBeforeCreatedCalled, "onBeforeCreated should have been called");
let widgetNode = document.getElementById(kWidgetId);
let viewNode = document.getElementById(kWidgetId + "idontexistyet");
ok(widgetNode, "Widget should exist");
if (widgetNode) {
try {
widgetNode.click();
ok(viewNode, "Panelview should exist");
widgetNode.click();
let tempPanel = document.getElementById("customizationui-widget-panel");
let panelShownPromise = promisePanelElementShown(window, tempPanel);
let tempPanel = document.getElementById("customizationui-widget-panel");
let panelShownPromise = promisePanelElementShown(window, tempPanel);
let shownTimeout = setTimeout(() => viewShownDeferred.reject("Panel not shown within 20s"), 20000);
await viewShownDeferred.promise;
await panelShownPromise;
clearTimeout(shownTimeout);
ok(true, "Found view shown");
await Promise.all([
BrowserTestUtils.waitForEvent(viewNode, "ViewShown"),
panelShownPromise
]);
let panelHiddenPromise = promisePanelElementHidden(window, tempPanel);
tempPanel.hidePopup();
await panelHiddenPromise;
let panelHiddenPromise = promisePanelElementHidden(window, tempPanel);
tempPanel.hidePopup();
await panelHiddenPromise;
CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
await waitForOverflowButtonShown();
await document.getElementById("nav-bar").overflowable.show();
CustomizableUI.addWidgetToArea(kWidgetId, CustomizableUI.AREA_FIXED_OVERFLOW_PANEL);
await waitForOverflowButtonShown();
await document.getElementById("nav-bar").overflowable.show();
viewShownDeferred = Promise.defer();
widgetNode.click();
widgetNode.click();
shownTimeout = setTimeout(() => viewShownDeferred.reject("Panel not shown within 20s"), 20000);
await viewShownDeferred.promise;
clearTimeout(shownTimeout);
ok(true, "Found view shown");
await BrowserTestUtils.waitForEvent(viewNode, "ViewShown");
let panelHidden = promiseOverflowHidden(window);
PanelUI.overflowPanel.hidePopup();
await panelHidden;
} catch (ex) {
ok(false, "Unexpected exception (like a timeout for one of the yields) " +
"when testing view widget.");
}
}
let panelHidden = promiseOverflowHidden(window);
PanelUI.overflowPanel.hidePopup();
await panelHidden;
noError = true;
try {
CustomizableUI.destroyWidget(kWidgetId);
} catch (ex) {
Cu.reportError(ex);
noError = false;
}
ok(noError, "Should not throw an exception trying to remove the broken view widget.");
CustomizableUI.destroyWidget(kWidgetId);
});
add_task(async function asyncCleanup() {

Просмотреть файл

@ -7,7 +7,7 @@
let syncService = {};
Components.utils.import("resource://services-sync/service.js", syncService);
const service = syncService.Service;
Components.utils.import("resource://services-sync/UIState.jsm");
const {UIState} = Components.utils.import("resource://services-sync/UIState.jsm", {});
let getState;
let originalSync;

Просмотреть файл

@ -316,12 +316,12 @@ function subviewShown(aSubview) {
let timeoutId = win.setTimeout(() => {
reject("Subview (" + aSubview.id + ") did not show within 20 seconds.");
}, 20000);
function onViewShowing(e) {
aSubview.removeEventListener("ViewShowing", onViewShowing);
function onViewShown(e) {
aSubview.removeEventListener("ViewShown", onViewShown);
win.clearTimeout(timeoutId);
resolve();
}
aSubview.addEventListener("ViewShowing", onViewShowing);
aSubview.addEventListener("ViewShown", onViewShown);
});
}

Просмотреть файл

@ -150,7 +150,7 @@ this.browserAction = class extends ExtensionAPI {
view.setAttribute("flex", "1");
view.setAttribute("extension", true);
document.getElementById("PanelUI-multiView").appendChild(view);
document.getElementById("appMenu-viewCache").appendChild(view);
if (this.extension.hasPermission("menus") ||
this.extension.hasPermission("contextMenus")) {

Просмотреть файл

@ -1078,15 +1078,16 @@ this.UITour = {
shouldOpenAppMenu = true;
} else if (this.targetIsInPageActionPanel(aTarget)) {
shouldOpenPageActionPanel = true;
// Ensure the panel visibility so as to ensure the visibility of
// the target element inside the panel otherwise
// we would be rejected in the below `isElementVisible` checking.
// Ensure the panel visibility so as to ensure the visibility of the target
// element inside the panel otherwise we would be rejected in the below
// `isElementVisible` checking.
aChromeWindow.BrowserPageActions.panelNode.hidden = false;
}
// Prevent showing a panel at an undefined position.
if (!this.isElementVisible(aTarget.node)) {
return Promise.reject(`_ensureTarget: Reject the ${aTarget.name} target since it isn't visible.`);
// Prevent showing a panel at an undefined position, but when it's tucked
// away inside a panel, we skip this check.
if (!aTarget.node.closest("panelview") && !this.isElementVisible(aTarget.node)) {
return Promise.reject(`_ensureTarget: Reject the ${aTarget.name || aTarget.targetName} target since it isn't visible.`);
}
let menuToOpen = null;

Просмотреть файл

@ -194,10 +194,6 @@
transform: translateX(-@menuPanelWidth@);
}
panelmultiview[nosubviews=true] > .panel-viewcontainer > .panel-viewstack > .panel-subviews {
display: none;
}
panelview {
-moz-box-orient: vertical;
-moz-box-flex: 1;