mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Merge mozilla-central and fx-team
This commit is contained in:
commit
757c230f65
@ -73,10 +73,12 @@
|
|||||||
accesskey="&emailPageCmd.accesskey;"
|
accesskey="&emailPageCmd.accesskey;"
|
||||||
command="Browser:SendLink"/>
|
command="Browser:SendLink"/>
|
||||||
<menuseparator/>
|
<menuseparator/>
|
||||||
|
#ifndef XP_LINUX
|
||||||
<menuitem id="menu_printSetup"
|
<menuitem id="menu_printSetup"
|
||||||
label="&printSetupCmd.label;"
|
label="&printSetupCmd.label;"
|
||||||
accesskey="&printSetupCmd.accesskey;"
|
accesskey="&printSetupCmd.accesskey;"
|
||||||
command="cmd_pageSetup"/>
|
command="cmd_pageSetup"/>
|
||||||
|
#endif
|
||||||
#ifndef XP_MACOSX
|
#ifndef XP_MACOSX
|
||||||
<menuitem id="menu_printPreview"
|
<menuitem id="menu_printPreview"
|
||||||
label="&printPreviewCmd.label;"
|
label="&printPreviewCmd.label;"
|
||||||
|
@ -884,6 +884,10 @@ let PlacesToolbarHelper = {
|
|||||||
if (!viewElt || viewElt._placesView)
|
if (!viewElt || viewElt._placesView)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
// CustomizableUI.addListener is idempotent, so we can safely
|
||||||
|
// call this multiple times.
|
||||||
|
CustomizableUI.addListener(this);
|
||||||
|
|
||||||
// If the bookmarks toolbar item is:
|
// If the bookmarks toolbar item is:
|
||||||
// - not in a toolbar, or;
|
// - not in a toolbar, or;
|
||||||
// - the toolbar is collapsed, or;
|
// - the toolbar is collapsed, or;
|
||||||
@ -899,7 +903,11 @@ let PlacesToolbarHelper = {
|
|||||||
if (forceToolbarOverflowCheck) {
|
if (forceToolbarOverflowCheck) {
|
||||||
viewElt._placesView.updateOverflowStatus();
|
viewElt._placesView.updateOverflowStatus();
|
||||||
}
|
}
|
||||||
this.customizeChange();
|
this._setupPlaceholder();
|
||||||
|
},
|
||||||
|
|
||||||
|
uninit: function PTH_uninit() {
|
||||||
|
CustomizableUI.removeListener(this);
|
||||||
},
|
},
|
||||||
|
|
||||||
customizeStart: function PTH_customizeStart() {
|
customizeStart: function PTH_customizeStart() {
|
||||||
@ -914,10 +922,15 @@ let PlacesToolbarHelper = {
|
|||||||
},
|
},
|
||||||
|
|
||||||
customizeChange: function PTH_customizeChange() {
|
customizeChange: function PTH_customizeChange() {
|
||||||
|
this._setupPlaceholder();
|
||||||
|
},
|
||||||
|
|
||||||
|
_setupPlaceholder: function PTH_setupPlaceholder() {
|
||||||
let placeholder = this._placeholder;
|
let placeholder = this._placeholder;
|
||||||
if (!placeholder) {
|
if (!placeholder) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let shouldWrapNow = this._getShouldWrap();
|
let shouldWrapNow = this._getShouldWrap();
|
||||||
if (this._shouldWrap != shouldWrapNow) {
|
if (this._shouldWrap != shouldWrapNow) {
|
||||||
if (shouldWrapNow) {
|
if (shouldWrapNow) {
|
||||||
@ -958,7 +971,40 @@ let PlacesToolbarHelper = {
|
|||||||
element = element.parentNode;
|
element = element.parentNode;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
},
|
||||||
|
|
||||||
|
onWidgetUnderflow: function(aNode, aContainer) {
|
||||||
|
// The view gets broken by being removed and reinserted by the overflowable
|
||||||
|
// toolbar, so we have to force an uninit and reinit.
|
||||||
|
let win = aNode.ownerDocument.defaultView;
|
||||||
|
if (aNode.id == "personal-bookmarks" && win == window) {
|
||||||
|
this._resetView();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
onWidgetAdded: function(aWidgetId, aArea, aPosition) {
|
||||||
|
if (aWidgetId == "personal-bookmarks" && !this._isCustomizing) {
|
||||||
|
// It's possible (with the "Add to Menu", "Add to Toolbar" context
|
||||||
|
// options) that the Places Toolbar Items have been moved without
|
||||||
|
// letting us prepare and handle it with with customizeStart and
|
||||||
|
// customizeDone. If that's the case, we need to reset the views
|
||||||
|
// since they're probably broken from the DOM reparenting.
|
||||||
|
this._resetView();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
_resetView: function() {
|
||||||
|
if (this._viewElt) {
|
||||||
|
// It's possible that the placesView might not exist, and we need to
|
||||||
|
// do a full init. This could happen if the Bookmarks Toolbar Items are
|
||||||
|
// moved to the Menu Panel, and then to the toolbar with the "Add to Toolbar"
|
||||||
|
// context menu option, outside of customize mode.
|
||||||
|
if (this._viewElt._placesView) {
|
||||||
|
this._viewElt._placesView.uninit();
|
||||||
|
}
|
||||||
|
this.init(true);
|
||||||
|
}
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -1164,6 +1210,16 @@ let BookmarkingUI = {
|
|||||||
// so kill current view and let popupshowing generate a new one.
|
// so kill current view and let popupshowing generate a new one.
|
||||||
if (this.button._placesView)
|
if (this.button._placesView)
|
||||||
this.button._placesView.uninit();
|
this.button._placesView.uninit();
|
||||||
|
|
||||||
|
// We have to do the same thing for the "special" views underneath the
|
||||||
|
// the bookmarks menu.
|
||||||
|
const kSpecialViewNodeIDs = ["BMB_bookmarksToolbar", "BMB_unsortedBookmarks"];
|
||||||
|
for (let viewNodeID of kSpecialViewNodeIDs) {
|
||||||
|
let elem = document.getElementById(viewNodeID);
|
||||||
|
if (elem && elem._placesView) {
|
||||||
|
elem._placesView.uninit();
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onCustomizeStart: function BUI_customizeStart(aWindow) {
|
onCustomizeStart: function BUI_customizeStart(aWindow) {
|
||||||
|
@ -1289,6 +1289,8 @@ var gBrowserInit = {
|
|||||||
} catch (ex) {
|
} catch (ex) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
PlacesToolbarHelper.uninit();
|
||||||
|
|
||||||
BookmarkingUI.uninit();
|
BookmarkingUI.uninit();
|
||||||
|
|
||||||
TabsInTitlebar.uninit();
|
TabsInTitlebar.uninit();
|
||||||
|
@ -93,6 +93,10 @@ skip-if = os == "linux"
|
|||||||
[browser_978084_dragEnd_after_move.js]
|
[browser_978084_dragEnd_after_move.js]
|
||||||
[browser_980155_add_overflow_toolbar.js]
|
[browser_980155_add_overflow_toolbar.js]
|
||||||
[browser_981418-widget-onbeforecreated-handler.js]
|
[browser_981418-widget-onbeforecreated-handler.js]
|
||||||
|
|
||||||
|
[browser_984455_bookmarks_items_reparenting.js]
|
||||||
|
skip-if = os == "linux"
|
||||||
|
|
||||||
[browser_985815_propagate_setToolbarVisibility.js]
|
[browser_985815_propagate_setToolbarVisibility.js]
|
||||||
[browser_981305_separator_insertion.js]
|
[browser_981305_separator_insertion.js]
|
||||||
[browser_987177_destroyWidget_xul.js]
|
[browser_987177_destroyWidget_xul.js]
|
||||||
|
@ -10,7 +10,7 @@ const isOSX = (Services.appinfo.OS === "Darwin");
|
|||||||
// show a context menu with options to move it.
|
// show a context menu with options to move it.
|
||||||
add_task(function() {
|
add_task(function() {
|
||||||
let contextMenu = document.getElementById("toolbar-context-menu");
|
let contextMenu = document.getElementById("toolbar-context-menu");
|
||||||
let shownPromise = contextMenuShown(contextMenu);
|
let shownPromise = popupShown(contextMenu);
|
||||||
let homeButton = document.getElementById("home-button");
|
let homeButton = document.getElementById("home-button");
|
||||||
EventUtils.synthesizeMouse(homeButton, 2, 2, {type: "contextmenu", button: 2 });
|
EventUtils.synthesizeMouse(homeButton, 2, 2, {type: "contextmenu", button: 2 });
|
||||||
yield shownPromise;
|
yield shownPromise;
|
||||||
@ -30,7 +30,7 @@ add_task(function() {
|
|||||||
);
|
);
|
||||||
checkContextMenu(contextMenu, expectedEntries);
|
checkContextMenu(contextMenu, expectedEntries);
|
||||||
|
|
||||||
let hiddenPromise = contextMenuHidden(contextMenu);
|
let hiddenPromise = popupHidden(contextMenu);
|
||||||
contextMenu.hidePopup();
|
contextMenu.hidePopup();
|
||||||
yield hiddenPromise;
|
yield hiddenPromise;
|
||||||
});
|
});
|
||||||
@ -40,7 +40,7 @@ add_task(function() {
|
|||||||
// and a toggle option for the extra toolbar
|
// and a toggle option for the extra toolbar
|
||||||
add_task(function() {
|
add_task(function() {
|
||||||
let contextMenu = document.getElementById("toolbar-context-menu");
|
let contextMenu = document.getElementById("toolbar-context-menu");
|
||||||
let shownPromise = contextMenuShown(contextMenu);
|
let shownPromise = popupShown(contextMenu);
|
||||||
let toolbar = createToolbarWithPlacements("880164_empty_toolbar", []);
|
let toolbar = createToolbarWithPlacements("880164_empty_toolbar", []);
|
||||||
toolbar.setAttribute("context", "toolbar-context-menu");
|
toolbar.setAttribute("context", "toolbar-context-menu");
|
||||||
toolbar.setAttribute("toolbarname", "Fancy Toolbar for Context Menu");
|
toolbar.setAttribute("toolbarname", "Fancy Toolbar for Context Menu");
|
||||||
@ -63,7 +63,7 @@ add_task(function() {
|
|||||||
);
|
);
|
||||||
checkContextMenu(contextMenu, expectedEntries);
|
checkContextMenu(contextMenu, expectedEntries);
|
||||||
|
|
||||||
let hiddenPromise = contextMenuHidden(contextMenu);
|
let hiddenPromise = popupHidden(contextMenu);
|
||||||
contextMenu.hidePopup();
|
contextMenu.hidePopup();
|
||||||
yield hiddenPromise;
|
yield hiddenPromise;
|
||||||
removeCustomToolbars();
|
removeCustomToolbars();
|
||||||
@ -74,7 +74,7 @@ add_task(function() {
|
|||||||
// show a context menu with disabled options to move it.
|
// show a context menu with disabled options to move it.
|
||||||
add_task(function() {
|
add_task(function() {
|
||||||
let contextMenu = document.getElementById("toolbar-context-menu");
|
let contextMenu = document.getElementById("toolbar-context-menu");
|
||||||
let shownPromise = contextMenuShown(contextMenu);
|
let shownPromise = popupShown(contextMenu);
|
||||||
let urlBarContainer = document.getElementById("urlbar-container");
|
let urlBarContainer = document.getElementById("urlbar-container");
|
||||||
// Need to make sure not to click within an edit field.
|
// Need to make sure not to click within an edit field.
|
||||||
let urlbarRect = urlBarContainer.getBoundingClientRect();
|
let urlbarRect = urlBarContainer.getBoundingClientRect();
|
||||||
@ -96,7 +96,7 @@ add_task(function() {
|
|||||||
);
|
);
|
||||||
checkContextMenu(contextMenu, expectedEntries);
|
checkContextMenu(contextMenu, expectedEntries);
|
||||||
|
|
||||||
let hiddenPromise = contextMenuHidden(contextMenu);
|
let hiddenPromise = popupHidden(contextMenu);
|
||||||
contextMenu.hidePopup();
|
contextMenu.hidePopup();
|
||||||
yield hiddenPromise;
|
yield hiddenPromise;
|
||||||
});
|
});
|
||||||
@ -135,7 +135,7 @@ add_task(function() {
|
|||||||
yield shownPanelPromise;
|
yield shownPanelPromise;
|
||||||
|
|
||||||
let contextMenu = document.getElementById("customizationPanelItemContextMenu");
|
let contextMenu = document.getElementById("customizationPanelItemContextMenu");
|
||||||
let shownContextPromise = contextMenuShown(contextMenu);
|
let shownContextPromise = popupShown(contextMenu);
|
||||||
let newWindowButton = document.getElementById("new-window-button");
|
let newWindowButton = document.getElementById("new-window-button");
|
||||||
ok(newWindowButton, "new-window-button was found");
|
ok(newWindowButton, "new-window-button was found");
|
||||||
EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2});
|
EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2});
|
||||||
@ -151,7 +151,7 @@ add_task(function() {
|
|||||||
];
|
];
|
||||||
checkContextMenu(contextMenu, expectedEntries);
|
checkContextMenu(contextMenu, expectedEntries);
|
||||||
|
|
||||||
let hiddenContextPromise = contextMenuHidden(contextMenu);
|
let hiddenContextPromise = popupHidden(contextMenu);
|
||||||
contextMenu.hidePopup();
|
contextMenu.hidePopup();
|
||||||
yield hiddenContextPromise;
|
yield hiddenContextPromise;
|
||||||
|
|
||||||
@ -165,7 +165,7 @@ add_task(function() {
|
|||||||
add_task(function() {
|
add_task(function() {
|
||||||
yield startCustomizing();
|
yield startCustomizing();
|
||||||
let contextMenu = document.getElementById("toolbar-context-menu");
|
let contextMenu = document.getElementById("toolbar-context-menu");
|
||||||
let shownPromise = contextMenuShown(contextMenu);
|
let shownPromise = popupShown(contextMenu);
|
||||||
let homeButton = document.getElementById("wrapper-home-button");
|
let homeButton = document.getElementById("wrapper-home-button");
|
||||||
EventUtils.synthesizeMouse(homeButton, 2, 2, {type: "contextmenu", button: 2});
|
EventUtils.synthesizeMouse(homeButton, 2, 2, {type: "contextmenu", button: 2});
|
||||||
yield shownPromise;
|
yield shownPromise;
|
||||||
@ -185,7 +185,7 @@ add_task(function() {
|
|||||||
);
|
);
|
||||||
checkContextMenu(contextMenu, expectedEntries);
|
checkContextMenu(contextMenu, expectedEntries);
|
||||||
|
|
||||||
let hiddenContextPromise = contextMenuHidden(contextMenu);
|
let hiddenContextPromise = popupHidden(contextMenu);
|
||||||
contextMenu.hidePopup();
|
contextMenu.hidePopup();
|
||||||
yield hiddenContextPromise;
|
yield hiddenContextPromise;
|
||||||
});
|
});
|
||||||
@ -194,7 +194,7 @@ add_task(function() {
|
|||||||
// show a context menu with options to move it.
|
// show a context menu with options to move it.
|
||||||
add_task(function() {
|
add_task(function() {
|
||||||
let contextMenu = document.getElementById("customizationPaletteItemContextMenu");
|
let contextMenu = document.getElementById("customizationPaletteItemContextMenu");
|
||||||
let shownPromise = contextMenuShown(contextMenu);
|
let shownPromise = popupShown(contextMenu);
|
||||||
let openFileButton = document.getElementById("wrapper-open-file-button");
|
let openFileButton = document.getElementById("wrapper-open-file-button");
|
||||||
EventUtils.synthesizeMouse(openFileButton, 2, 2, {type: "contextmenu", button: 2});
|
EventUtils.synthesizeMouse(openFileButton, 2, 2, {type: "contextmenu", button: 2});
|
||||||
yield shownPromise;
|
yield shownPromise;
|
||||||
@ -205,7 +205,7 @@ add_task(function() {
|
|||||||
];
|
];
|
||||||
checkContextMenu(contextMenu, expectedEntries);
|
checkContextMenu(contextMenu, expectedEntries);
|
||||||
|
|
||||||
let hiddenContextPromise = contextMenuHidden(contextMenu);
|
let hiddenContextPromise = popupHidden(contextMenu);
|
||||||
contextMenu.hidePopup();
|
contextMenu.hidePopup();
|
||||||
yield hiddenContextPromise;
|
yield hiddenContextPromise;
|
||||||
});
|
});
|
||||||
@ -214,7 +214,7 @@ add_task(function() {
|
|||||||
// should show a context menu with options to move it.
|
// should show a context menu with options to move it.
|
||||||
add_task(function() {
|
add_task(function() {
|
||||||
let contextMenu = document.getElementById("customizationPanelItemContextMenu");
|
let contextMenu = document.getElementById("customizationPanelItemContextMenu");
|
||||||
let shownPromise = contextMenuShown(contextMenu);
|
let shownPromise = popupShown(contextMenu);
|
||||||
let newWindowButton = document.getElementById("wrapper-new-window-button");
|
let newWindowButton = document.getElementById("wrapper-new-window-button");
|
||||||
EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2});
|
EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2});
|
||||||
yield shownPromise;
|
yield shownPromise;
|
||||||
@ -227,7 +227,7 @@ add_task(function() {
|
|||||||
];
|
];
|
||||||
checkContextMenu(contextMenu, expectedEntries);
|
checkContextMenu(contextMenu, expectedEntries);
|
||||||
|
|
||||||
let hiddenContextPromise = contextMenuHidden(contextMenu);
|
let hiddenContextPromise = popupHidden(contextMenu);
|
||||||
contextMenu.hidePopup();
|
contextMenu.hidePopup();
|
||||||
yield hiddenContextPromise;
|
yield hiddenContextPromise;
|
||||||
yield endCustomizing();
|
yield endCustomizing();
|
||||||
@ -241,7 +241,7 @@ add_task(function() {
|
|||||||
yield startCustomizing(this.otherWin);
|
yield startCustomizing(this.otherWin);
|
||||||
|
|
||||||
let contextMenu = this.otherWin.document.getElementById("customizationPanelItemContextMenu");
|
let contextMenu = this.otherWin.document.getElementById("customizationPanelItemContextMenu");
|
||||||
let shownPromise = contextMenuShown(contextMenu);
|
let shownPromise = popupShown(contextMenu);
|
||||||
let newWindowButton = this.otherWin.document.getElementById("wrapper-new-window-button");
|
let newWindowButton = this.otherWin.document.getElementById("wrapper-new-window-button");
|
||||||
EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2}, this.otherWin);
|
EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2}, this.otherWin);
|
||||||
yield shownPromise;
|
yield shownPromise;
|
||||||
@ -254,7 +254,7 @@ add_task(function() {
|
|||||||
];
|
];
|
||||||
checkContextMenu(contextMenu, expectedEntries, this.otherWin);
|
checkContextMenu(contextMenu, expectedEntries, this.otherWin);
|
||||||
|
|
||||||
let hiddenContextPromise = contextMenuHidden(contextMenu);
|
let hiddenContextPromise = popupHidden(contextMenu);
|
||||||
contextMenu.hidePopup();
|
contextMenu.hidePopup();
|
||||||
yield hiddenContextPromise;
|
yield hiddenContextPromise;
|
||||||
yield endCustomizing(this.otherWin);
|
yield endCustomizing(this.otherWin);
|
||||||
@ -267,13 +267,13 @@ add_task(function() {
|
|||||||
add_task(function() {
|
add_task(function() {
|
||||||
yield startCustomizing();
|
yield startCustomizing();
|
||||||
let contextMenu = document.getElementById("customizationPanelItemContextMenu");
|
let contextMenu = document.getElementById("customizationPanelItemContextMenu");
|
||||||
let shownPromise = contextMenuShown(contextMenu);
|
let shownPromise = popupShown(contextMenu);
|
||||||
let zoomControls = document.getElementById("wrapper-zoom-controls");
|
let zoomControls = document.getElementById("wrapper-zoom-controls");
|
||||||
EventUtils.synthesizeMouse(zoomControls, 2, 2, {type: "contextmenu", button: 2});
|
EventUtils.synthesizeMouse(zoomControls, 2, 2, {type: "contextmenu", button: 2});
|
||||||
yield shownPromise;
|
yield shownPromise;
|
||||||
// Execute the command to move the item from the panel to the toolbar.
|
// Execute the command to move the item from the panel to the toolbar.
|
||||||
contextMenu.childNodes[0].doCommand();
|
contextMenu.childNodes[0].doCommand();
|
||||||
let hiddenPromise = contextMenuHidden(contextMenu);
|
let hiddenPromise = popupHidden(contextMenu);
|
||||||
contextMenu.hidePopup();
|
contextMenu.hidePopup();
|
||||||
yield hiddenPromise;
|
yield hiddenPromise;
|
||||||
yield endCustomizing();
|
yield endCustomizing();
|
||||||
@ -282,7 +282,7 @@ add_task(function() {
|
|||||||
is(zoomControls.parentNode.id, "nav-bar-customization-target", "Zoom-controls should be on the nav-bar");
|
is(zoomControls.parentNode.id, "nav-bar-customization-target", "Zoom-controls should be on the nav-bar");
|
||||||
|
|
||||||
contextMenu = document.getElementById("toolbar-context-menu");
|
contextMenu = document.getElementById("toolbar-context-menu");
|
||||||
shownPromise = contextMenuShown(contextMenu);
|
shownPromise = popupShown(contextMenu);
|
||||||
EventUtils.synthesizeMouse(zoomControls, 2, 2, {type: "contextmenu", button: 2});
|
EventUtils.synthesizeMouse(zoomControls, 2, 2, {type: "contextmenu", button: 2});
|
||||||
yield shownPromise;
|
yield shownPromise;
|
||||||
|
|
||||||
@ -301,7 +301,7 @@ add_task(function() {
|
|||||||
);
|
);
|
||||||
checkContextMenu(contextMenu, expectedEntries);
|
checkContextMenu(contextMenu, expectedEntries);
|
||||||
|
|
||||||
hiddenPromise = contextMenuHidden(contextMenu);
|
hiddenPromise = popupHidden(contextMenu);
|
||||||
contextMenu.hidePopup();
|
contextMenu.hidePopup();
|
||||||
yield hiddenPromise;
|
yield hiddenPromise;
|
||||||
yield resetCustomization();
|
yield resetCustomization();
|
||||||
@ -315,7 +315,7 @@ add_task(function() {
|
|||||||
yield PanelUI.show();
|
yield PanelUI.show();
|
||||||
|
|
||||||
let contextMenu = document.getElementById("customizationPanelItemContextMenu");
|
let contextMenu = document.getElementById("customizationPanelItemContextMenu");
|
||||||
let shownContextPromise = contextMenuShown(contextMenu);
|
let shownContextPromise = popupShown(contextMenu);
|
||||||
let newWindowButton = document.getElementById("new-window-button");
|
let newWindowButton = document.getElementById("new-window-button");
|
||||||
ok(newWindowButton, "new-window-button was found");
|
ok(newWindowButton, "new-window-button was found");
|
||||||
EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2});
|
EventUtils.synthesizeMouse(newWindowButton, 2, 2, {type: "contextmenu", button: 2});
|
||||||
@ -331,7 +331,7 @@ add_task(function() {
|
|||||||
];
|
];
|
||||||
checkContextMenu(contextMenu, expectedEntries);
|
checkContextMenu(contextMenu, expectedEntries);
|
||||||
|
|
||||||
let hiddenContextPromise = contextMenuHidden(contextMenu);
|
let hiddenContextPromise = popupHidden(contextMenu);
|
||||||
contextMenu.hidePopup();
|
contextMenu.hidePopup();
|
||||||
yield hiddenContextPromise;
|
yield hiddenContextPromise;
|
||||||
|
|
||||||
|
@ -27,7 +27,7 @@ add_task(function() {
|
|||||||
yield shownPanelPromise;
|
yield shownPanelPromise;
|
||||||
|
|
||||||
let contextMenu = document.getElementById("toolbar-context-menu");
|
let contextMenu = document.getElementById("toolbar-context-menu");
|
||||||
let shownContextPromise = contextMenuShown(contextMenu);
|
let shownContextPromise = popupShown(contextMenu);
|
||||||
let homeButton = document.getElementById("home-button");
|
let homeButton = document.getElementById("home-button");
|
||||||
ok(homeButton, "home-button was found");
|
ok(homeButton, "home-button was found");
|
||||||
is(homeButton.getAttribute("overflowedItem"), "true", "Home button is overflowing");
|
is(homeButton.getAttribute("overflowedItem"), "true", "Home button is overflowing");
|
||||||
@ -51,7 +51,7 @@ add_task(function() {
|
|||||||
);
|
);
|
||||||
checkContextMenu(contextMenu, expectedEntries);
|
checkContextMenu(contextMenu, expectedEntries);
|
||||||
|
|
||||||
let hiddenContextPromise = contextMenuHidden(contextMenu);
|
let hiddenContextPromise = popupHidden(contextMenu);
|
||||||
let hiddenPromise = promisePanelElementHidden(window, overflowPanel);
|
let hiddenPromise = promisePanelElementHidden(window, overflowPanel);
|
||||||
let moveToPanel = contextMenu.querySelector(".customize-context-moveToPanel");
|
let moveToPanel = contextMenu.querySelector(".customize-context-moveToPanel");
|
||||||
if (moveToPanel) {
|
if (moveToPanel) {
|
||||||
|
@ -0,0 +1,251 @@
|
|||||||
|
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||||
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||||
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||||
|
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
let gNavBar = document.getElementById(CustomizableUI.AREA_NAVBAR);
|
||||||
|
let gOverflowList = document.getElementById(gNavBar.getAttribute("overflowtarget"));
|
||||||
|
|
||||||
|
const kBookmarksButton = "bookmarks-menu-button";
|
||||||
|
const kBookmarksItems = "personal-bookmarks";
|
||||||
|
const kOriginalWindowWidth = window.outerWidth;
|
||||||
|
const kSmallWidth = 400;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function that opens the bookmarks menu, and returns a Promise that
|
||||||
|
* resolves as soon as the menu is ready for interaction.
|
||||||
|
*/
|
||||||
|
function bookmarksMenuPanelShown() {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
let bookmarksMenuPopup = document.getElementById("BMB_bookmarksPopup");
|
||||||
|
let onTransitionEnd = (e) => {
|
||||||
|
if (e.target == bookmarksMenuPopup) {
|
||||||
|
bookmarksMenuPopup.removeEventListener("transitionend", onTransitionEnd);
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bookmarksMenuPopup.addEventListener("transitionend", onTransitionEnd);
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that the placesContext menu is correctly attached to the
|
||||||
|
* controller of some view. Returns a Promise that resolves as soon
|
||||||
|
* as the context menu is closed.
|
||||||
|
*
|
||||||
|
* @param aItemWithContextMenu the item that we need to synthesize hte
|
||||||
|
* right click on in order to open the context menu.
|
||||||
|
*/
|
||||||
|
function checkContextMenu(aItemWithContextMenu) {
|
||||||
|
return Task.spawn(function* () {
|
||||||
|
let contextMenu = document.getElementById("placesContext");
|
||||||
|
let newBookmarkItem = document.getElementById("placesContext_new:bookmark");
|
||||||
|
let shownPromise = popupShown(contextMenu);
|
||||||
|
EventUtils.synthesizeMouseAtCenter(aItemWithContextMenu,
|
||||||
|
{type: "contextmenu", button: 2});
|
||||||
|
yield shownPromise;
|
||||||
|
|
||||||
|
ok(!newBookmarkItem.hasAttribute("disabled"),
|
||||||
|
"New bookmark item shouldn't be disabled");
|
||||||
|
|
||||||
|
yield closePopup(contextMenu);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Opens the bookmarks menu panel, and then opens each of the "special"
|
||||||
|
* submenus in that list. Then it checks that those submenu's context menus
|
||||||
|
* are properly hooked up to a controller.
|
||||||
|
*/
|
||||||
|
function checkSpecialContextMenus() {
|
||||||
|
return Task.spawn(function* () {
|
||||||
|
let contextMenu = document.getElementById("placesContext");
|
||||||
|
let bookmarksMenuButton = document.getElementById(kBookmarksButton);
|
||||||
|
let bookmarksMenuPopup = document.getElementById("BMB_bookmarksPopup");
|
||||||
|
|
||||||
|
const kSpecialItemIDs = {
|
||||||
|
"BMB_bookmarksToolbar": "BMB_bookmarksToolbarPopup",
|
||||||
|
"BMB_unsortedBookmarks": "BMB_unsortedBookmarksPopup",
|
||||||
|
};
|
||||||
|
|
||||||
|
// Open the bookmarks menu button context menus and ensure that
|
||||||
|
// they have the proper views attached.
|
||||||
|
let shownPromise = bookmarksMenuPanelShown();
|
||||||
|
let dropmarker = document.getAnonymousElementByAttribute(bookmarksMenuButton,
|
||||||
|
"anonid", "dropmarker");
|
||||||
|
EventUtils.synthesizeMouseAtCenter(dropmarker, {});
|
||||||
|
info("Waiting for bookmarks menu popup to show after clicking dropmarker.")
|
||||||
|
yield shownPromise;
|
||||||
|
|
||||||
|
for (let menuID in kSpecialItemIDs) {
|
||||||
|
let menuItem = document.getElementById(menuID);
|
||||||
|
let menuPopup = document.getElementById(kSpecialItemIDs[menuID]);
|
||||||
|
let shownPromise = popupShown(menuPopup);
|
||||||
|
EventUtils.synthesizeMouseAtCenter(menuItem, {});
|
||||||
|
yield shownPromise;
|
||||||
|
|
||||||
|
yield checkContextMenu(menuPopup);
|
||||||
|
yield closePopup(menuPopup);
|
||||||
|
}
|
||||||
|
|
||||||
|
yield closePopup(bookmarksMenuPopup);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Closes a focused popup by simulating pressing the Escape key,
|
||||||
|
* and returns a Promise that resolves as soon as the popup is closed.
|
||||||
|
*
|
||||||
|
* @param aPopup the popup node to close.
|
||||||
|
*/
|
||||||
|
function closePopup(aPopup) {
|
||||||
|
let hiddenPromise = popupHidden(aPopup);
|
||||||
|
EventUtils.synthesizeKey("VK_ESCAPE", {});
|
||||||
|
return hiddenPromise;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Helper function that checks that the context menu of the
|
||||||
|
* bookmark toolbar items chevron popup is correctly hooked up
|
||||||
|
* to the controller of a view.
|
||||||
|
*/
|
||||||
|
function checkBookmarksItemsChevronContextMenu() {
|
||||||
|
return Task.spawn(function*() {
|
||||||
|
let chevronPopup = document.getElementById("PlacesChevronPopup");
|
||||||
|
let shownPromise = popupShown(chevronPopup);
|
||||||
|
let chevron = document.getElementById("PlacesChevron");
|
||||||
|
EventUtils.synthesizeMouseAtCenter(chevron, {});
|
||||||
|
yield shownPromise;
|
||||||
|
|
||||||
|
yield checkContextMenu(chevronPopup);
|
||||||
|
yield closePopup(chevronPopup);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Forces the window to a width that causes the nav-bar to overflow
|
||||||
|
* its contents. Returns a Promise that resolves as soon as the
|
||||||
|
* overflowable nav-bar is showing its chevron.
|
||||||
|
*/
|
||||||
|
function overflowEverything() {
|
||||||
|
window.resizeTo(kSmallWidth, window.outerHeight);
|
||||||
|
return waitForCondition(() => gNavBar.hasAttribute("overflowing"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the window to its original size from the start of the test,
|
||||||
|
* and returns a Promise that resolves when the nav-bar is no longer
|
||||||
|
* overflowing.
|
||||||
|
*/
|
||||||
|
function stopOverflowing() {
|
||||||
|
window.resizeTo(kOriginalWindowWidth, window.outerHeight);
|
||||||
|
return waitForCondition(() => !gNavBar.hasAttribute("overflowing"));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that an item with ID aID is overflowing in the nav-bar.
|
||||||
|
*
|
||||||
|
* @param aID the ID of the node to check for overflowingness.
|
||||||
|
*/
|
||||||
|
function checkOverflowing(aID) {
|
||||||
|
ok(!gNavBar.querySelector("#" + aID),
|
||||||
|
"Item with ID " + aID + " should no longer be in the gNavBar");
|
||||||
|
let item = gOverflowList.querySelector("#" + aID);
|
||||||
|
ok(item, "Item with ID " + aID + " should be overflowing");
|
||||||
|
is(item.getAttribute("overflowedItem"), "true",
|
||||||
|
"Item with ID " + aID + " should have overflowedItem attribute");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks that an item with ID aID is not overflowing in the nav-bar.
|
||||||
|
*
|
||||||
|
* @param aID the ID of hte node to check for non-overflowingness.
|
||||||
|
*/
|
||||||
|
function checkNotOverflowing(aID) {
|
||||||
|
ok(!gOverflowList.querySelector("#" + aID),
|
||||||
|
"Item with ID " + aID + " should no longer be overflowing");
|
||||||
|
let item = gNavBar.querySelector("#" + aID);
|
||||||
|
ok(item, "Item with ID " + aID + " should be in the nav bar");
|
||||||
|
ok(!item.hasAttribute("overflowedItem"),
|
||||||
|
"Item with ID " + aID + " should not have overflowedItem attribute");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that overflowing the bookmarks menu button doesn't break the
|
||||||
|
* context menus for the Unsorted and Bookmarks Toolbar menu items.
|
||||||
|
*/
|
||||||
|
add_task(function* testOverflowingBookmarksButtonContextMenu() {
|
||||||
|
ok(!gNavBar.hasAttribute("overflowing"), "Should start with a non-overflowing toolbar.");
|
||||||
|
ok(CustomizableUI.inDefaultState, "Should start in default state.");
|
||||||
|
|
||||||
|
// Open the Unsorted and Bookmarks Toolbar context menus and ensure
|
||||||
|
// that they have views attached.
|
||||||
|
yield checkSpecialContextMenus();
|
||||||
|
|
||||||
|
yield overflowEverything();
|
||||||
|
checkOverflowing(kBookmarksButton);
|
||||||
|
|
||||||
|
yield stopOverflowing();
|
||||||
|
checkNotOverflowing(kBookmarksButton);
|
||||||
|
|
||||||
|
yield checkSpecialContextMenus();
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that the bookmarks toolbar items context menu still works if moved
|
||||||
|
* to the menu from the overflow panel, and then back to the toolbar.
|
||||||
|
*/
|
||||||
|
add_task(function* testOverflowingBookmarksItemsContextMenu() {
|
||||||
|
yield PanelUI.ensureReady();
|
||||||
|
|
||||||
|
let bookmarksToolbarItems = document.getElementById(kBookmarksItems);
|
||||||
|
gCustomizeMode.addToToolbar(bookmarksToolbarItems);
|
||||||
|
yield checkContextMenu(bookmarksToolbarItems);
|
||||||
|
|
||||||
|
yield overflowEverything();
|
||||||
|
checkOverflowing(kBookmarksItems)
|
||||||
|
|
||||||
|
gCustomizeMode.addToPanel(bookmarksToolbarItems);
|
||||||
|
|
||||||
|
yield stopOverflowing();
|
||||||
|
|
||||||
|
gCustomizeMode.addToToolbar(bookmarksToolbarItems);
|
||||||
|
yield checkContextMenu(bookmarksToolbarItems);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that overflowing the bookmarks toolbar items doesn't cause the
|
||||||
|
* context menu in the bookmarks toolbar items chevron to stop working.
|
||||||
|
*/
|
||||||
|
add_task(function* testOverflowingBookmarksItemsChevronContextMenu() {
|
||||||
|
// If it's not already there, let's move the bookmarks toolbar items to
|
||||||
|
// the nav-bar.
|
||||||
|
let bookmarksToolbarItems = document.getElementById(kBookmarksItems);
|
||||||
|
gCustomizeMode.addToToolbar(bookmarksToolbarItems);
|
||||||
|
|
||||||
|
// We make the PlacesToolbarItems element be super tiny in order to force
|
||||||
|
// the bookmarks toolbar items into overflowing and making the chevron
|
||||||
|
// show itself.
|
||||||
|
let placesToolbarItems = document.getElementById("PlacesToolbarItems");
|
||||||
|
let placesChevron = document.getElementById("PlacesChevron");
|
||||||
|
placesToolbarItems.style.maxWidth = "10px";
|
||||||
|
yield waitForCondition(() => !placesChevron.collapsed);
|
||||||
|
|
||||||
|
yield checkBookmarksItemsChevronContextMenu();
|
||||||
|
|
||||||
|
yield overflowEverything();
|
||||||
|
checkOverflowing(kBookmarksItems);
|
||||||
|
|
||||||
|
yield stopOverflowing();
|
||||||
|
checkNotOverflowing(kBookmarksItems);
|
||||||
|
|
||||||
|
yield checkBookmarksItemsChevronContextMenu();
|
||||||
|
|
||||||
|
placesToolbarItems.style.removeProperty("max-width");
|
||||||
|
});
|
||||||
|
|
||||||
|
add_task(function* asyncCleanup() {
|
||||||
|
window.resizeTo(kOriginalWindowWidth, window.outerHeight);
|
||||||
|
yield resetCustomization();
|
||||||
|
});
|
@ -428,36 +428,49 @@ function promiseTabHistoryNavigation(aDirection = -1, aConditionFn) {
|
|||||||
return deferred.promise;
|
return deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
function contextMenuShown(aContextMenu) {
|
function popupShown(aPopup) {
|
||||||
let deferred = Promise.defer();
|
return promisePopupEvent(aPopup, "shown");
|
||||||
let win = aContextMenu.ownerDocument.defaultView;
|
|
||||||
let timeoutId = win.setTimeout(() => {
|
|
||||||
deferred.reject("Context menu (" + aContextMenu.id + ") did not show within 20 seconds.");
|
|
||||||
}, 20000);
|
|
||||||
function onPopupShown(e) {
|
|
||||||
aContextMenu.removeEventListener("popupshown", onPopupShown);
|
|
||||||
win.clearTimeout(timeoutId);
|
|
||||||
deferred.resolve();
|
|
||||||
};
|
|
||||||
aContextMenu.addEventListener("popupshown", onPopupShown);
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
function contextMenuHidden(aContextMenu) {
|
function popupHidden(aPopup) {
|
||||||
let deferred = Promise.defer();
|
return promisePopupEvent(aPopup, "hidden");
|
||||||
let win = aContextMenu.ownerDocument.defaultView;
|
|
||||||
let timeoutId = win.setTimeout(() => {
|
|
||||||
deferred.reject("Context menu (" + aContextMenu.id + ") did not hide within 20 seconds.");
|
|
||||||
}, 20000);
|
|
||||||
function onPopupHidden(e) {
|
|
||||||
win.clearTimeout(timeoutId);
|
|
||||||
aContextMenu.removeEventListener("popuphidden", onPopupHidden);
|
|
||||||
deferred.resolve();
|
|
||||||
};
|
|
||||||
aContextMenu.addEventListener("popuphidden", onPopupHidden);
|
|
||||||
return deferred.promise;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a Promise that resolves when aPopup fires an event of type
|
||||||
|
* aEventType. Times out and rejects after 20 seconds.
|
||||||
|
*
|
||||||
|
* @param aPopup the popup to monitor for events.
|
||||||
|
* @param aEventSuffix the _suffix_ for the popup event type to watch for.
|
||||||
|
*
|
||||||
|
* Example usage:
|
||||||
|
* let popupShownPromise = promisePopupEvent(somePopup, "shown");
|
||||||
|
* // ... something that opens a popup
|
||||||
|
* yield popupShownPromise;
|
||||||
|
*
|
||||||
|
* let popupHiddenPromise = promisePopupEvent(somePopup, "hidden");
|
||||||
|
* // ... something that hides a popup
|
||||||
|
* yield popupHiddenPromise;
|
||||||
|
*/
|
||||||
|
function promisePopupEvent(aPopup, aEventSuffix) {
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
let win = aPopup.ownerDocument.defaultView;
|
||||||
|
let eventType = "popup" + aEventSuffix;
|
||||||
|
|
||||||
|
let timeoutId = win.setTimeout(() => {
|
||||||
|
deferred.reject("Context menu (" + aPopup.id + ") did not fire "
|
||||||
|
+ eventType + " within 20 seconds.");
|
||||||
|
}, 20000);
|
||||||
|
|
||||||
|
function onPopupEvent(e) {
|
||||||
|
win.clearTimeout(timeoutId);
|
||||||
|
aPopup.removeEventListener(eventType, onPopupEvent);
|
||||||
|
deferred.resolve();
|
||||||
|
};
|
||||||
|
|
||||||
|
aPopup.addEventListener(eventType, onPopupEvent);
|
||||||
|
return deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
// This is a simpler version of the context menu check that
|
// This is a simpler version of the context menu check that
|
||||||
// exists in contextmenu_common.js.
|
// exists in contextmenu_common.js.
|
||||||
|
@ -960,6 +960,10 @@ PlacesToolbar.prototype = {
|
|||||||
this._removeEventListeners(window, ["resize", "unload"], false);
|
this._removeEventListeners(window, ["resize", "unload"], false);
|
||||||
this._removeEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false);
|
this._removeEventListeners(gBrowser.tabContainer, ["TabOpen", "TabClose"], false);
|
||||||
|
|
||||||
|
if (this._chevron._placesView) {
|
||||||
|
this._chevron._placesView.uninit();
|
||||||
|
}
|
||||||
|
|
||||||
PlacesViewBase.prototype.uninit.apply(this, arguments);
|
PlacesViewBase.prototype.uninit.apply(this, arguments);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -26,6 +26,10 @@ const BUTTON_POSITION_DONT_SAVE = 2;
|
|||||||
const BUTTON_POSITION_REVERT = 0;
|
const BUTTON_POSITION_REVERT = 0;
|
||||||
const EVAL_FUNCTION_TIMEOUT = 1000; // milliseconds
|
const EVAL_FUNCTION_TIMEOUT = 1000; // milliseconds
|
||||||
|
|
||||||
|
const MAXIMUM_FONT_SIZE = 96;
|
||||||
|
const MINIMUM_FONT_SIZE = 6;
|
||||||
|
const NORMAL_FONT_SIZE = 12;
|
||||||
|
|
||||||
const SCRATCHPAD_L10N = "chrome://browser/locale/devtools/scratchpad.properties";
|
const SCRATCHPAD_L10N = "chrome://browser/locale/devtools/scratchpad.properties";
|
||||||
const DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
|
const DEVTOOLS_CHROME_ENABLED = "devtools.chrome.enabled";
|
||||||
const PREF_RECENT_FILES_MAX = "devtools.scratchpad.recentFilesMax";
|
const PREF_RECENT_FILES_MAX = "devtools.scratchpad.recentFilesMax";
|
||||||
@ -212,7 +216,25 @@ var Scratchpad = {
|
|||||||
},
|
},
|
||||||
"sp-cmd-hideSidebar": () => {
|
"sp-cmd-hideSidebar": () => {
|
||||||
Scratchpad.sidebar.hide();
|
Scratchpad.sidebar.hide();
|
||||||
}
|
},
|
||||||
|
"sp-cmd-line-numbers": () => {
|
||||||
|
Scratchpad.toggleEditorOption('lineNumbers');
|
||||||
|
},
|
||||||
|
"sp-cmd-wrap-text": () => {
|
||||||
|
Scratchpad.toggleEditorOption('lineWrapping');
|
||||||
|
},
|
||||||
|
"sp-cmd-highlight-trailing-space": () => {
|
||||||
|
Scratchpad.toggleEditorOption('showTrailingSpace');
|
||||||
|
},
|
||||||
|
"sp-cmd-larger-font": () => {
|
||||||
|
Scratchpad.increaseFontSize();
|
||||||
|
},
|
||||||
|
"sp-cmd-smaller-font": () => {
|
||||||
|
Scratchpad.decreaseFontSize();
|
||||||
|
},
|
||||||
|
"sp-cmd-normal-font": () => {
|
||||||
|
Scratchpad.normalFontSize();
|
||||||
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let command in commands) {
|
for (let command in commands) {
|
||||||
@ -1779,6 +1801,47 @@ var Scratchpad = {
|
|||||||
return shouldClose;
|
return shouldClose;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Toggle a editor's boolean option.
|
||||||
|
*/
|
||||||
|
toggleEditorOption: function SP_toggleEditorOption(optionName)
|
||||||
|
{
|
||||||
|
let newOptionValue = !this.editor.getOption(optionName);
|
||||||
|
this.editor.setOption(optionName, newOptionValue);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Increase the editor's font size by 1 px.
|
||||||
|
*/
|
||||||
|
increaseFontSize: function SP_increaseFontSize()
|
||||||
|
{
|
||||||
|
let size = this.editor.getFontSize();
|
||||||
|
|
||||||
|
if (size < MAXIMUM_FONT_SIZE) {
|
||||||
|
this.editor.setFontSize(size + 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Decrease the editor's font size by 1 px.
|
||||||
|
*/
|
||||||
|
decreaseFontSize: function SP_decreaseFontSize()
|
||||||
|
{
|
||||||
|
let size = this.editor.getFontSize();
|
||||||
|
|
||||||
|
if (size > MINIMUM_FONT_SIZE) {
|
||||||
|
this.editor.setFontSize(size - 1);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Restore the editor's original font size.
|
||||||
|
*/
|
||||||
|
normalFontSize: function SP_normalFontSize()
|
||||||
|
{
|
||||||
|
this.editor.setFontSize(NORMAL_FONT_SIZE);
|
||||||
|
},
|
||||||
|
|
||||||
_observers: [],
|
_observers: [],
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -47,6 +47,12 @@
|
|||||||
<command id="sp-cmd-saveas" oncommand=";"/>
|
<command id="sp-cmd-saveas" oncommand=";"/>
|
||||||
<command id="sp-cmd-revert" oncommand=";" disabled="true"/>
|
<command id="sp-cmd-revert" oncommand=";" disabled="true"/>
|
||||||
<command id="sp-cmd-close" oncommand=";"/>
|
<command id="sp-cmd-close" oncommand=";"/>
|
||||||
|
<command id="sp-cmd-line-numbers" oncommand=";"/>
|
||||||
|
<command id="sp-cmd-wrap-text" oncommand=";"/>
|
||||||
|
<command id="sp-cmd-highlight-trailing-space" oncommand=";"/>
|
||||||
|
<command id="sp-cmd-larger-font" oncommand=";"/>
|
||||||
|
<command id="sp-cmd-smaller-font" oncommand=";"/>
|
||||||
|
<command id="sp-cmd-normal-font" oncommand=";"/>
|
||||||
<command id="sp-cmd-run" oncommand=";"/>
|
<command id="sp-cmd-run" oncommand=";"/>
|
||||||
<command id="sp-cmd-inspect" oncommand=";"/>
|
<command id="sp-cmd-inspect" oncommand=";"/>
|
||||||
<command id="sp-cmd-display" oncommand=";"/>
|
<command id="sp-cmd-display" oncommand=";"/>
|
||||||
@ -80,6 +86,18 @@
|
|||||||
key="&closeCmd.key;"
|
key="&closeCmd.key;"
|
||||||
command="sp-cmd-close"
|
command="sp-cmd-close"
|
||||||
modifiers="accel"/>
|
modifiers="accel"/>
|
||||||
|
<key id="sp-key-larger-font"
|
||||||
|
key="&largerFont.commandkey;"
|
||||||
|
command="sp-cmd-larger-font"
|
||||||
|
modifiers="accel"/>
|
||||||
|
<key id="sp-key-smaller-font"
|
||||||
|
key="&smallerFont.commandkey;"
|
||||||
|
command="sp-cmd-smaller-font"
|
||||||
|
modifiers="accel"/>
|
||||||
|
<key id="sp-key-normal-size-font"
|
||||||
|
key="&normalSize.commandkey;"
|
||||||
|
command="sp-cmd-normal-font"
|
||||||
|
modifiers="accel"/>
|
||||||
<key id="sp-key-run"
|
<key id="sp-key-run"
|
||||||
key="&run.key;"
|
key="&run.key;"
|
||||||
command="sp-cmd-run"
|
command="sp-cmd-run"
|
||||||
@ -166,6 +184,43 @@
|
|||||||
</menupopup>
|
</menupopup>
|
||||||
</menu>
|
</menu>
|
||||||
|
|
||||||
|
<menu id="sp-view-menu" label="&viewMenu.label;" accesskey="&viewMenu.accesskey;">
|
||||||
|
<menupopup id="sp-menu-viewpopup">
|
||||||
|
<menuitem id="sp-menu-line-numbers"
|
||||||
|
label="&lineNumbers.label;"
|
||||||
|
accesskey="&lineNumbers.accesskey;"
|
||||||
|
type="checkbox"
|
||||||
|
checked="true"
|
||||||
|
command="sp-cmd-line-numbers"/>
|
||||||
|
<menuitem id="sp-menu-word-wrap"
|
||||||
|
label="&wordWrap.label;"
|
||||||
|
accesskey="&wordWrap.accesskey;"
|
||||||
|
type="checkbox"
|
||||||
|
command="sp-cmd-wrap-text"/>
|
||||||
|
<menuitem id="sp-menu-highlight-trailing-space"
|
||||||
|
label="&highlightTrailingSpace.label;"
|
||||||
|
accesskey="&highlightTrailingSpace.accesskey;"
|
||||||
|
type="checkbox"
|
||||||
|
command="sp-cmd-highlight-trailing-space"/>
|
||||||
|
<menuseparator/>
|
||||||
|
<menuitem id="sp-menu-larger-font"
|
||||||
|
label="&largerFont.label;"
|
||||||
|
key="sp-key-larger-font"
|
||||||
|
accesskey="&largerFont.accesskey;"
|
||||||
|
command="sp-cmd-larger-font"/>
|
||||||
|
<menuitem id="sp-menu-smaller-font"
|
||||||
|
label="&smallerFont.label;"
|
||||||
|
key="sp-key-smaller-font"
|
||||||
|
accesskey="&smallerFont.accesskey;"
|
||||||
|
command="sp-cmd-smaller-font"/>
|
||||||
|
<menuitem id="sp-menu-normal-size-font"
|
||||||
|
label="&normalSize.label;"
|
||||||
|
key="sp-menu-normal-font"
|
||||||
|
accesskey="&normalSize.accesskey;"
|
||||||
|
command="sp-cmd-normal-font"/>
|
||||||
|
</menupopup>
|
||||||
|
</menu>
|
||||||
|
|
||||||
<menu id="sp-edit-menu" label="&editMenu.label;"
|
<menu id="sp-edit-menu" label="&editMenu.label;"
|
||||||
accesskey="&editMenu.accesskey;">
|
accesskey="&editMenu.accesskey;">
|
||||||
<menupopup id="sp-menu_editpopup">
|
<menupopup id="sp-menu_editpopup">
|
||||||
|
@ -29,10 +29,16 @@ function runTests()
|
|||||||
"sp-text-run": "run",
|
"sp-text-run": "run",
|
||||||
"sp-text-inspect": "inspect",
|
"sp-text-inspect": "inspect",
|
||||||
"sp-text-display": "display",
|
"sp-text-display": "display",
|
||||||
"sp-text-reloadAndRun" : "reloadAndRun",
|
"sp-text-reloadAndRun": "reloadAndRun",
|
||||||
"sp-menu-content": "setContentContext",
|
"sp-menu-content": "setContentContext",
|
||||||
"sp-menu-browser": "setBrowserContext",
|
"sp-menu-browser": "setBrowserContext",
|
||||||
"sp-menu-pprint":"prettyPrint",
|
"sp-menu-pprint": "prettyPrint",
|
||||||
|
"sp-menu-line-numbers": "toggleEditorOption",
|
||||||
|
"sp-menu-word-wrap": "toggleEditorOption",
|
||||||
|
"sp-menu-highlight-trailing-space": "toggleEditorOption",
|
||||||
|
"sp-menu-larger-font": "increaseFontSize",
|
||||||
|
"sp-menu-smaller-font": "decreaseFontSize",
|
||||||
|
"sp-menu-normal-size-font": "normalFontSize",
|
||||||
};
|
};
|
||||||
|
|
||||||
let lastMethodCalled = null;
|
let lastMethodCalled = null;
|
||||||
|
@ -50,6 +50,30 @@
|
|||||||
<!ENTITY closeCmd.key "W">
|
<!ENTITY closeCmd.key "W">
|
||||||
<!ENTITY closeCmd.accesskey "C">
|
<!ENTITY closeCmd.accesskey "C">
|
||||||
|
|
||||||
|
<!ENTITY viewMenu.label "View">
|
||||||
|
<!ENTITY viewMenu.accesskey "V">
|
||||||
|
|
||||||
|
<!ENTITY lineNumbers.label "Show Line Numbers">
|
||||||
|
<!ENTITY lineNumbers.accesskey "L">
|
||||||
|
|
||||||
|
<!ENTITY wordWrap.label "Wrap Text">
|
||||||
|
<!ENTITY wordWrap.accesskey "W">
|
||||||
|
|
||||||
|
<!ENTITY highlightTrailingSpace.label "Highlight Trailing Space">
|
||||||
|
<!ENTITY highlightTrailingSpace.accesskey "H">
|
||||||
|
|
||||||
|
<!ENTITY largerFont.label "Larger Font">
|
||||||
|
<!ENTITY largerFont.accesskey "a">
|
||||||
|
<!ENTITY largerFont.commandkey "+">
|
||||||
|
|
||||||
|
<!ENTITY smallerFont.label "Smaller Font">
|
||||||
|
<!ENTITY smallerFont.accesskey "M">
|
||||||
|
<!ENTITY smallerFont.commandkey "-">
|
||||||
|
|
||||||
|
<!ENTITY normalSize.label "Normal Size">
|
||||||
|
<!ENTITY normalSize.accesskey "N">
|
||||||
|
<!ENTITY normalSize.commandkey "0">
|
||||||
|
|
||||||
<!ENTITY editMenu.label "Edit">
|
<!ENTITY editMenu.label "Edit">
|
||||||
<!ENTITY editMenu.accesskey "E">
|
<!ENTITY editMenu.accesskey "E">
|
||||||
|
|
||||||
|
@ -54,7 +54,7 @@
|
|||||||
#nav-bar {
|
#nav-bar {
|
||||||
background-image: linear-gradient(@toolbarHighlight@, rgba(255,255,255,0));
|
background-image: linear-gradient(@toolbarHighlight@, rgba(255,255,255,0));
|
||||||
box-shadow: 0 1px 0 @toolbarHighlight@ inset;
|
box-shadow: 0 1px 0 @toolbarHighlight@ inset;
|
||||||
margin-top: -1px; /* Move up 1px into the TabsToolbar */
|
margin-top: -@tabToolbarNavbarOverlap@; /* Move up into the TabsToolbar */
|
||||||
padding-top: 2px;
|
padding-top: 2px;
|
||||||
padding-bottom: 2px;
|
padding-bottom: 2px;
|
||||||
/* Position the toolbar above the bottom of background tabs */
|
/* Position the toolbar above the bottom of background tabs */
|
||||||
@ -1730,7 +1730,7 @@ richlistitem[type~="action"][actiontype="switchtab"] > .ac-url-box > .ac-action-
|
|||||||
#TabsToolbar::after {
|
#TabsToolbar::after {
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 1px;
|
bottom: @tabToolbarNavbarOverlap@;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
@ -98,7 +98,7 @@ toolbarseparator {
|
|||||||
background-position: 0 1px, 0 0;
|
background-position: 0 1px, 0 0;
|
||||||
|
|
||||||
box-shadow: inset 0 1px 0 hsla(0,0%,100%,.4);
|
box-shadow: inset 0 1px 0 hsla(0,0%,100%,.4);
|
||||||
margin-top: -1px;
|
margin-top: -@tabToolbarNavbarOverlap@;
|
||||||
/* Position the toolbar above the bottom of background tabs */
|
/* Position the toolbar above the bottom of background tabs */
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
@ -1617,11 +1617,12 @@ toolbarbutton[sdk-button="true"][cui-areatype="toolbar"] > .toolbarbutton-icon {
|
|||||||
#zoom-controls[cui-areatype="toolbar"]:not([overflowedItem=true]) > #zoom-reset-button {
|
#zoom-controls[cui-areatype="toolbar"]:not([overflowedItem=true]) > #zoom-reset-button {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
|
-moz-box-orient: horizontal;
|
||||||
|
-moz-box-align: center;
|
||||||
}
|
}
|
||||||
|
|
||||||
#zoom-controls[cui-areatype="toolbar"]:not([overflowedItem=true]) > #zoom-reset-button > .toolbarbutton-text {
|
#zoom-controls[cui-areatype="toolbar"]:not([overflowedItem=true]) > #zoom-reset-button > .toolbarbutton-text {
|
||||||
padding-top: 4px;
|
margin-top: 0;
|
||||||
margin: 2px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ----- FULLSCREEN WINDOW CONTROLS ----- */
|
/* ----- FULLSCREEN WINDOW CONTROLS ----- */
|
||||||
@ -2800,7 +2801,7 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
|
|||||||
* ordinal group value (see bug 853415). */
|
* ordinal group value (see bug 853415). */
|
||||||
-moz-box-ordinal-group: 1001;
|
-moz-box-ordinal-group: 1001;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
bottom: 1px;
|
bottom: @tabToolbarNavbarOverlap@;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0;
|
right: 0;
|
||||||
z-index: 0;
|
z-index: 0;
|
||||||
|
@ -11,3 +11,4 @@
|
|||||||
%endif
|
%endif
|
||||||
|
|
||||||
%define inAnyPanel :-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true])
|
%define inAnyPanel :-moz-any(:not([cui-areatype="toolbar"]), [overflowedItem=true])
|
||||||
|
%define tabToolbarNavbarOverlap 1px
|
||||||
|
@ -273,7 +273,7 @@
|
|||||||
|
|
||||||
.tabbrowser-tab[pinned][titlechanged]:not([selected="true"]) > .tab-stack > .tab-content {
|
.tabbrowser-tab[pinned][titlechanged]:not([selected="true"]) > .tab-stack > .tab-content {
|
||||||
background-image: radial-gradient(farthest-corner at center bottom, rgb(255,255,255) 3%, rgba(186,221,251,0.75) 20%, rgba(127,179,255,0.25) 40%, rgba(127,179,255,0) 70%);
|
background-image: radial-gradient(farthest-corner at center bottom, rgb(255,255,255) 3%, rgba(186,221,251,0.75) 20%, rgba(127,179,255,0.25) 40%, rgba(127,179,255,0) 70%);
|
||||||
background-position: center bottom 1px;
|
background-position: center bottom @tabToolbarNavbarOverlap@;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 85% 100%;
|
background-size: 85% 100%;
|
||||||
}
|
}
|
||||||
@ -286,12 +286,11 @@
|
|||||||
-moz-margin-start: -1.5px;
|
-moz-margin-start: -1.5px;
|
||||||
-moz-margin-end: -1.5px;
|
-moz-margin-end: -1.5px;
|
||||||
background-image: url(chrome://browser/skin/tabbrowser/tab-separator.png);
|
background-image: url(chrome://browser/skin/tabbrowser/tab-separator.png);
|
||||||
background-position: left bottom;
|
background-position: left bottom @tabToolbarNavbarOverlap@;
|
||||||
background-repeat: no-repeat;
|
background-repeat: no-repeat;
|
||||||
background-size: 3px 100%;
|
background-size: 3px 100%;
|
||||||
content: "";
|
content: "";
|
||||||
display: -moz-box;
|
display: -moz-box;
|
||||||
margin-bottom: 1px;
|
|
||||||
width: 3px;
|
width: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -267,7 +267,7 @@
|
|||||||
#nav-bar {
|
#nav-bar {
|
||||||
background-image: linear-gradient(@toolbarHighlight@, rgba(255,255,255,0));
|
background-image: linear-gradient(@toolbarHighlight@, rgba(255,255,255,0));
|
||||||
box-shadow: 0 1px 0 @toolbarHighlight@ inset;
|
box-shadow: 0 1px 0 @toolbarHighlight@ inset;
|
||||||
margin-top: -1px; /* Move up 1px into the TabsToolbar */
|
margin-top: -@tabToolbarNavbarOverlap@; /* Move up into the TabsToolbar */
|
||||||
/* Position the toolbar above the bottom of background tabs */
|
/* Position the toolbar above the bottom of background tabs */
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 1;
|
z-index: 1;
|
||||||
|
@ -11,6 +11,7 @@ import android.content.Context;
|
|||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
import android.content.SharedPreferences.Editor;
|
import android.content.SharedPreferences.Editor;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
|
import android.os.StrictMode;
|
||||||
import android.preference.PreferenceManager;
|
import android.preference.PreferenceManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
@ -151,30 +152,20 @@ public final class GeckoSharedPrefs {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ThreadUtils.isOnBackgroundThread()) {
|
// We deliberatly perform the migration in the current thread (which
|
||||||
Log.d(LOGTAG, "Already in background thread, migrating directly");
|
// is likely the UI thread) as this is actually cheaper than enforcing a
|
||||||
|
// context switch to another thread (see bug 940575).
|
||||||
|
if (Build.VERSION.SDK_INT < 9) {
|
||||||
performMigration(context);
|
performMigration(context);
|
||||||
} else {
|
} else {
|
||||||
Log.d(LOGTAG, "Not in background thread, migrating with lock");
|
// Avoid strict mode warnings.
|
||||||
|
final StrictMode.ThreadPolicy savedPolicy = StrictMode.allowThreadDiskReads();
|
||||||
final Object migrationLock = new Object();
|
StrictMode.allowThreadDiskWrites();
|
||||||
|
|
||||||
ThreadUtils.getBackgroundHandler().post(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
synchronized(migrationLock) {
|
|
||||||
performMigration(context);
|
|
||||||
migrationLock.notifyAll();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
synchronized(migrationLock) {
|
performMigration(context);
|
||||||
migrationLock.wait(MIGRATION_COMMIT_TIMEOUT_MSEC);
|
} finally {
|
||||||
}
|
StrictMode.setThreadPolicy(savedPolicy);
|
||||||
} catch (InterruptedException e) {
|
|
||||||
throw new IllegalStateException("Failed to commit migration before timeout");
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ public class BrowserSearch extends HomeFragment
|
|||||||
private static final int ANIMATION_DURATION = 250;
|
private static final int ANIMATION_DURATION = 250;
|
||||||
|
|
||||||
// Holds the current search term to use in the query
|
// Holds the current search term to use in the query
|
||||||
private String mSearchTerm;
|
private volatile String mSearchTerm;
|
||||||
|
|
||||||
// Adapter for the list of search results
|
// Adapter for the list of search results
|
||||||
private SearchAdapter mAdapter;
|
private SearchAdapter mAdapter;
|
||||||
@ -689,26 +689,39 @@ public class BrowserSearch extends HomeFragment
|
|||||||
GeckoAppShell.unregisterEventListener(eventName, this);
|
GeckoAppShell.unregisterEventListener(eventName, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void restartSearchLoader() {
|
||||||
|
SearchLoader.restart(getLoaderManager(), LOADER_ID_SEARCH, mCursorLoaderCallbacks, mSearchTerm);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initSearchLoader() {
|
||||||
|
SearchLoader.init(getLoaderManager(), LOADER_ID_SEARCH, mCursorLoaderCallbacks, mSearchTerm);
|
||||||
|
}
|
||||||
|
|
||||||
public void filter(String searchTerm, AutocompleteHandler handler) {
|
public void filter(String searchTerm, AutocompleteHandler handler) {
|
||||||
if (TextUtils.isEmpty(searchTerm)) {
|
if (TextUtils.isEmpty(searchTerm)) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TextUtils.equals(mSearchTerm, searchTerm)) {
|
final boolean isNewFilter = !TextUtils.equals(mSearchTerm, searchTerm);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
mSearchTerm = searchTerm;
|
mSearchTerm = searchTerm;
|
||||||
mAutocompleteHandler = handler;
|
mAutocompleteHandler = handler;
|
||||||
|
|
||||||
if (isVisible()) {
|
if (isVisible()) {
|
||||||
// The adapter depends on the search term to determine its number
|
if (isNewFilter) {
|
||||||
// of items. Make it we notify the view about it.
|
// The adapter depends on the search term to determine its number
|
||||||
mAdapter.notifyDataSetChanged();
|
// of items. Make it we notify the view about it.
|
||||||
|
mAdapter.notifyDataSetChanged();
|
||||||
|
|
||||||
// Restart loaders with the new search term
|
// Restart loaders with the new search term
|
||||||
SearchLoader.restart(getLoaderManager(), LOADER_ID_SEARCH, mCursorLoaderCallbacks, mSearchTerm);
|
restartSearchLoader();
|
||||||
filterSuggestions();
|
filterSuggestions();
|
||||||
|
} else {
|
||||||
|
// The search term hasn't changed, simply reuse any existing
|
||||||
|
// loader for the current search term. This will ensure autocompletion
|
||||||
|
// is consistently triggered (see bug 933739).
|
||||||
|
initSearchLoader();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -237,6 +237,9 @@ let Scheduler = {
|
|||||||
*/
|
*/
|
||||||
resetTimer: null,
|
resetTimer: null,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Prepare to kill the OS.File worker after a few seconds.
|
||||||
|
*/
|
||||||
restartTimer: function(arg) {
|
restartTimer: function(arg) {
|
||||||
let delay;
|
let delay;
|
||||||
try {
|
try {
|
||||||
@ -249,7 +252,98 @@ let Scheduler = {
|
|||||||
if (this.resetTimer) {
|
if (this.resetTimer) {
|
||||||
clearTimeout(this.resetTimer);
|
clearTimeout(this.resetTimer);
|
||||||
}
|
}
|
||||||
this.resetTimer = setTimeout(File.resetWorker, delay);
|
this.resetTimer = setTimeout(
|
||||||
|
() => Scheduler.kill({reset: true, shutdown: false}),
|
||||||
|
delay
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shutdown OS.File.
|
||||||
|
*
|
||||||
|
* @param {*} options
|
||||||
|
* - {boolean} shutdown If |true|, reject any further request. Otherwise,
|
||||||
|
* further requests will resurrect the worker.
|
||||||
|
* - {boolean} reset If |true|, instruct the worker to shutdown if this
|
||||||
|
* would not cause leaks. Otherwise, assume that the worker will be shutdown
|
||||||
|
* through some other mean.
|
||||||
|
*/
|
||||||
|
kill: function({shutdown, reset}) {
|
||||||
|
return Task.spawn(function*() {
|
||||||
|
|
||||||
|
yield this.queue;
|
||||||
|
|
||||||
|
if (!this.launched || this.shutdown || !worker) {
|
||||||
|
// Nothing to kill
|
||||||
|
this.shutdown = this.shutdown || shutdown;
|
||||||
|
worker = null;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Deactivate the queue, to ensure that no message is sent
|
||||||
|
// to an obsolete worker (we reactivate it in the |finally|).
|
||||||
|
let deferred = Promise.defer();
|
||||||
|
this.queue = deferred.promise;
|
||||||
|
|
||||||
|
let message = ["Meta_shutdown", [reset]];
|
||||||
|
|
||||||
|
try {
|
||||||
|
Scheduler.latestReceived = [];
|
||||||
|
Scheduler.latestSent = [Date.now(), ...message];
|
||||||
|
let promise = worker.post(...message);
|
||||||
|
|
||||||
|
// Wait for result
|
||||||
|
let resources;
|
||||||
|
try {
|
||||||
|
resources = (yield promise).ok;
|
||||||
|
|
||||||
|
Scheduler.latestReceived = [Date.now(), message];
|
||||||
|
} catch (ex) {
|
||||||
|
LOG("Could not dispatch Meta_reset", ex);
|
||||||
|
// It's most likely a programmer error, but we'll assume that
|
||||||
|
// the worker has been shutdown, as it's less risky than the
|
||||||
|
// opposite stance.
|
||||||
|
resources = {openedFiles: [], openedDirectoryIterators: [], killed: true};
|
||||||
|
|
||||||
|
Scheduler.latestReceived = [Date.now(), message, ex];
|
||||||
|
}
|
||||||
|
|
||||||
|
let {openedFiles, openedDirectoryIterators, killed} = resources;
|
||||||
|
if (!reset
|
||||||
|
&& (openedFiles && openedFiles.length
|
||||||
|
|| ( openedDirectoryIterators && openedDirectoryIterators.length))) {
|
||||||
|
// The worker still holds resources. Report them.
|
||||||
|
|
||||||
|
let msg = "";
|
||||||
|
if (openedFiles.length > 0) {
|
||||||
|
msg += "The following files are still open:\n" +
|
||||||
|
openedFiles.join("\n");
|
||||||
|
}
|
||||||
|
if (openedDirectoryIterators.length > 0) {
|
||||||
|
msg += "The following directory iterators are still open:\n" +
|
||||||
|
openedDirectoryIterators.join("\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG("WARNING: File descriptors leaks detected.\n" + msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Make sure that we do not leave an invalid |worker| around.
|
||||||
|
if (killed || shutdown) {
|
||||||
|
worker = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
this.shutdown = shutdown;
|
||||||
|
|
||||||
|
return resources;
|
||||||
|
|
||||||
|
} finally {
|
||||||
|
// Resume accepting messages. If we have set |shutdown| to |true|,
|
||||||
|
// any pending/future request will be rejected. Otherwise, any
|
||||||
|
// pending/future request will spawn a new worker if necessary.
|
||||||
|
deferred.resolve();
|
||||||
|
}
|
||||||
|
|
||||||
|
}.bind(this));
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -310,6 +404,11 @@ let Scheduler = {
|
|||||||
// expensive, we only keep a shortened version of it.
|
// expensive, we only keep a shortened version of it.
|
||||||
Scheduler.Debugging.latestReceived = null;
|
Scheduler.Debugging.latestReceived = null;
|
||||||
Scheduler.Debugging.latestSent = [Date.now(), method, summarizeObject(methodArgs)];
|
Scheduler.Debugging.latestSent = [Date.now(), method, summarizeObject(methodArgs)];
|
||||||
|
|
||||||
|
// Don't kill the worker just yet
|
||||||
|
Scheduler.restartTimer();
|
||||||
|
|
||||||
|
|
||||||
let data;
|
let data;
|
||||||
let reply;
|
let reply;
|
||||||
let isError = false;
|
let isError = false;
|
||||||
@ -346,11 +445,7 @@ let Scheduler = {
|
|||||||
Scheduler._updateTelemetry();
|
Scheduler._updateTelemetry();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't restart the timer when reseting the worker, since that will
|
Scheduler.restartTimer();
|
||||||
// lead to an endless "resetWorker()" loop.
|
|
||||||
if (method != "Meta_reset") {
|
|
||||||
Scheduler.restartTimer();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for duration and return result.
|
// Check for duration and return result.
|
||||||
@ -470,54 +565,9 @@ const WEB_WORKERS_SHUTDOWN_TOPIC = "web-workers-shutdown";
|
|||||||
const PREF_OSFILE_TEST_SHUTDOWN_OBSERVER =
|
const PREF_OSFILE_TEST_SHUTDOWN_OBSERVER =
|
||||||
"toolkit.osfile.test.shutdown.observer";
|
"toolkit.osfile.test.shutdown.observer";
|
||||||
|
|
||||||
/**
|
|
||||||
* A condition function meant to be used during phase
|
|
||||||
* webWorkersShutdown, to warn about unclosed files and directories
|
|
||||||
* and reconfigure the Scheduler to reject further requests.
|
|
||||||
*
|
|
||||||
* @param {bool=} shutdown If true or unspecified, reconfigure
|
|
||||||
* the scheduler to reject further requests. Can be set to |false|
|
|
||||||
* for testing purposes.
|
|
||||||
* @return {promise} A promise satisfied once all pending messages
|
|
||||||
* (including the shutdown warning message) have been answered.
|
|
||||||
*/
|
|
||||||
function warnAboutUnclosedFiles(shutdown = true) {
|
|
||||||
if (!Scheduler.launched || !worker) {
|
|
||||||
// Don't launch the scheduler on our behalf. If no message has been
|
|
||||||
// sent to the worker, we can't have any leaking file/directory
|
|
||||||
// descriptor.
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
let promise = Scheduler.post("Meta_getUnclosedResources");
|
|
||||||
|
|
||||||
// Configure the worker to reject any further message.
|
|
||||||
if (shutdown) {
|
|
||||||
Scheduler.shutdown = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return promise.then(function onSuccess(opened) {
|
|
||||||
let msg = "";
|
|
||||||
if (opened.openedFiles.length > 0) {
|
|
||||||
msg += "The following files are still open:\n" +
|
|
||||||
opened.openedFiles.join("\n");
|
|
||||||
}
|
|
||||||
if (msg) {
|
|
||||||
msg += "\n";
|
|
||||||
}
|
|
||||||
if (opened.openedDirectoryIterators.length > 0) {
|
|
||||||
msg += "The following directory iterators are still open:\n" +
|
|
||||||
opened.openedDirectoryIterators.join("\n");
|
|
||||||
}
|
|
||||||
// Only log if file descriptors leaks detected.
|
|
||||||
if (msg) {
|
|
||||||
LOG("WARNING: File descriptors leaks detected.\n" + msg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
AsyncShutdown.webWorkersShutdown.addBlocker(
|
AsyncShutdown.webWorkersShutdown.addBlocker(
|
||||||
"OS.File: flush pending requests, warn about unclosed files, shut down service.",
|
"OS.File: flush pending requests, warn about unclosed files, shut down service.",
|
||||||
() => warnAboutUnclosedFiles(true)
|
() => Scheduler.kill({reset: false, shutdown: true})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
@ -542,7 +592,7 @@ Services.prefs.addObserver(PREF_OSFILE_TEST_SHUTDOWN_OBSERVER,
|
|||||||
let phase = AsyncShutdown._getPhase(TOPIC);
|
let phase = AsyncShutdown._getPhase(TOPIC);
|
||||||
phase.addBlocker(
|
phase.addBlocker(
|
||||||
"(for testing purposes) OS.File: warn about unclosed files",
|
"(for testing purposes) OS.File: warn about unclosed files",
|
||||||
() => warnAboutUnclosedFiles(false)
|
() => Scheduler.kill({shutdown: false, reset: false})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}, false);
|
}, false);
|
||||||
@ -1366,44 +1416,15 @@ DirectoryIterator.Entry.fromMsg = function fromMsg(value) {
|
|||||||
return new DirectoryIterator.Entry(value);
|
return new DirectoryIterator.Entry(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* Flush all operations currently queued, then kill the underlying
|
|
||||||
* worker to save memory.
|
|
||||||
*
|
|
||||||
* @return {Promise}
|
|
||||||
* @reject {Error} If at least one file or directory iterator instance
|
|
||||||
* is still open and the worker cannot be killed safely.
|
|
||||||
*/
|
|
||||||
File.resetWorker = function() {
|
File.resetWorker = function() {
|
||||||
if (!Scheduler.launched || Scheduler.shutdown) {
|
return Task.spawn(function*() {
|
||||||
// No need to reset
|
let resources = yield Scheduler.kill({shutdown: false, reset: true});
|
||||||
return Promise.resolve();
|
if (resources && !resources.killed) {
|
||||||
}
|
throw new Error("Could not reset worker, this would leak file descriptors: " + JSON.stringify(resources));
|
||||||
return Scheduler.post("Meta_reset").then(
|
|
||||||
function(wouldLeak) {
|
|
||||||
if (!wouldLeak) {
|
|
||||||
// No resource would leak, the worker was stopped.
|
|
||||||
worker = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// Otherwise, resetting would be unsafe and has been canceled.
|
|
||||||
// Turn this into an error
|
|
||||||
let msg = "Cannot reset worker: ";
|
|
||||||
let {openedFiles, openedDirectoryIterators} = wouldLeak;
|
|
||||||
if (openedFiles.length > 0) {
|
|
||||||
msg += "The following files are still open:\n" +
|
|
||||||
openedFiles.join("\n");
|
|
||||||
}
|
|
||||||
if (openedDirectoryIterators.length > 0) {
|
|
||||||
msg += "The following directory iterators are still open:\n" +
|
|
||||||
openedDirectoryIterators.join("\n");
|
|
||||||
}
|
|
||||||
throw new Error(msg);
|
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
// Constants
|
// Constants
|
||||||
File.POS_START = SysAll.POS_START;
|
File.POS_START = SysAll.POS_START;
|
||||||
File.POS_CURRENT = SysAll.POS_CURRENT;
|
File.POS_CURRENT = SysAll.POS_CURRENT;
|
||||||
|
@ -272,34 +272,25 @@ const EXCEPTION_NAMES = {
|
|||||||
GET_DEBUG: function() {
|
GET_DEBUG: function() {
|
||||||
return SharedAll.Config.DEBUG;
|
return SharedAll.Config.DEBUG;
|
||||||
},
|
},
|
||||||
Meta_getUnclosedResources: function() {
|
/**
|
||||||
// Return information about both opened files and opened
|
* Execute shutdown sequence, returning data on leaked file descriptors.
|
||||||
// directory iterators.
|
*
|
||||||
return {
|
* @param {bool} If |true|, kill the worker if this would not cause
|
||||||
|
* leaks.
|
||||||
|
*/
|
||||||
|
Meta_shutdown: function(kill) {
|
||||||
|
let result = {
|
||||||
openedFiles: OpenedFiles.listOpenedResources(),
|
openedFiles: OpenedFiles.listOpenedResources(),
|
||||||
openedDirectoryIterators: OpenedDirectoryIterators.listOpenedResources()
|
openedDirectoryIterators: OpenedDirectoryIterators.listOpenedResources(),
|
||||||
|
killed: false // Placeholder
|
||||||
};
|
};
|
||||||
},
|
|
||||||
Meta_reset: function() {
|
// Is it safe to kill the worker?
|
||||||
// Attempt to stop the worker. This fails if at least one
|
let safe = result.openedFiles.length == 0
|
||||||
// resource is still open. Returns the list of files and
|
&& result.openedDirectoryIterators.length == 0;
|
||||||
// directory iterators that cannot be closed safely (or undefined
|
result.killed = safe && kill;
|
||||||
// if there are no such files/directory iterators).
|
|
||||||
let openedFiles = OpenedFiles.listOpenedResources();
|
return new Meta(result, {shutdown: result.killed});
|
||||||
let openedDirectoryIterators =
|
|
||||||
OpenedDirectoryIterators.listOpenedResources();
|
|
||||||
let canShutdown = openedFiles.length == 0
|
|
||||||
&& openedDirectoryIterators.length == 0;
|
|
||||||
if (canShutdown) {
|
|
||||||
// Succeed. Shutdown the thread, nothing to return
|
|
||||||
return new Meta(null, {shutdown: true});
|
|
||||||
} else {
|
|
||||||
// Fail. Don't shutdown the thread, return info on resources
|
|
||||||
return {
|
|
||||||
openedFiles: openedFiles,
|
|
||||||
openedDirectoryIterators: openedDirectoryIterators
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
// Functions of OS.File
|
// Functions of OS.File
|
||||||
stat: function stat(path, options) {
|
stat: function stat(path, options) {
|
||||||
|
@ -37,7 +37,7 @@ add_task(function file_open_cannot_reset() {
|
|||||||
let thrown = false;
|
let thrown = false;
|
||||||
try {
|
try {
|
||||||
yield OS.File.resetWorker();
|
yield OS.File.resetWorker();
|
||||||
} catch (ex if ex.message.indexOf(TEST_FILE) != -1 ) {
|
} catch (ex if ex.message.indexOf(OS.Path.basename(TEST_FILE)) != -1 ) {
|
||||||
thrown = true;
|
thrown = true;
|
||||||
}
|
}
|
||||||
do_check_true(thrown);
|
do_check_true(thrown);
|
||||||
@ -47,14 +47,14 @@ add_task(function file_open_cannot_reset() {
|
|||||||
yield OS.File.resetWorker();
|
yield OS.File.resetWorker();
|
||||||
});
|
});
|
||||||
|
|
||||||
add_task(function file_open_cannot_reset() {
|
add_task(function dir_open_cannot_reset() {
|
||||||
let TEST_DIR = yield OS.File.getCurrentDirectory();
|
let TEST_DIR = yield OS.File.getCurrentDirectory();
|
||||||
do_print("Leaking directory " + TEST_DIR + ", we shouldn't be able to reset");
|
do_print("Leaking directory " + TEST_DIR + ", we shouldn't be able to reset");
|
||||||
let iterator = new OS.File.DirectoryIterator(TEST_DIR);
|
let iterator = new OS.File.DirectoryIterator(TEST_DIR);
|
||||||
let thrown = false;
|
let thrown = false;
|
||||||
try {
|
try {
|
||||||
yield OS.File.resetWorker();
|
yield OS.File.resetWorker();
|
||||||
} catch (ex if ex.message.indexOf(TEST_DIR) != -1 ) {
|
} catch (ex if ex.message.indexOf(OS.Path.basename(TEST_DIR)) != -1 ) {
|
||||||
thrown = true;
|
thrown = true;
|
||||||
}
|
}
|
||||||
do_check_true(thrown);
|
do_check_true(thrown);
|
||||||
|
@ -89,7 +89,7 @@ add_task(function system_shutdown() {
|
|||||||
do_check_true((yield testLeaksOf(TEST_FILE, "test.shutdown.file.leak")));
|
do_check_true((yield testLeaksOf(TEST_FILE, "test.shutdown.file.leak")));
|
||||||
yield openedFile.close();
|
yield openedFile.close();
|
||||||
do_print("At this stage, we don't leak the file anymore");
|
do_print("At this stage, we don't leak the file anymore");
|
||||||
do_check_false((yield testLeaksOf(TEST_FILE, "test.shutdown.file.leak")));
|
do_check_false((yield testLeaksOf(TEST_FILE, "test.shutdown.file.leak.2")));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
@ -605,18 +605,29 @@ var AddonManagerInternal = {
|
|||||||
try {
|
try {
|
||||||
defaultProvidersEnabled = Services.prefs.getBoolPref(PREF_DEFAULT_PROVIDERS_ENABLED);
|
defaultProvidersEnabled = Services.prefs.getBoolPref(PREF_DEFAULT_PROVIDERS_ENABLED);
|
||||||
} catch (e) {}
|
} catch (e) {}
|
||||||
|
AddonManagerPrivate.recordSimpleMeasure("default_providers", defaultProvidersEnabled);
|
||||||
|
|
||||||
// Ensure all default providers have had a chance to register themselves
|
// Ensure all default providers have had a chance to register themselves
|
||||||
if (defaultProvidersEnabled) {
|
if (defaultProvidersEnabled) {
|
||||||
DEFAULT_PROVIDERS.forEach(function(url) {
|
for (let url of DEFAULT_PROVIDERS) {
|
||||||
try {
|
try {
|
||||||
Components.utils.import(url, {});
|
let scope = {};
|
||||||
|
Components.utils.import(url, scope);
|
||||||
|
// Sanity check - make sure the provider exports a symbol that
|
||||||
|
// has a 'startup' method
|
||||||
|
let syms = Object.keys(scope);
|
||||||
|
if ((syms.length < 1) ||
|
||||||
|
(typeof scope[syms[0]].startup != "function")) {
|
||||||
|
logger.warn("Provider " + url + " has no startup()");
|
||||||
|
AddonManagerPrivate.recordException("AMI", "provider " + url, "no startup()");
|
||||||
|
}
|
||||||
|
logger.debug("Loaded provider scope for " + url + ": " + Object.keys(scope).toSource());
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
AddonManagerPrivate.recordException("AMI", "provider " + url + " load failed", e);
|
AddonManagerPrivate.recordException("AMI", "provider " + url + " load failed", e);
|
||||||
logger.error("Exception loading default provider \"" + url + "\"", e);
|
logger.error("Exception loading default provider \"" + url + "\"", e);
|
||||||
}
|
}
|
||||||
});
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load any providers registered in the category manager
|
// Load any providers registered in the category manager
|
||||||
@ -772,6 +783,7 @@ var AddonManagerInternal = {
|
|||||||
provider[aMethod].apply(provider, aArgs);
|
provider[aMethod].apply(provider, aArgs);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
AddonManagerPrivate.recordException("AMI", "provider " + aMethod, e);
|
||||||
logger.error("Exception calling provider " + aMethod, e);
|
logger.error("Exception calling provider " + aMethod, e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,7 +9,7 @@ const Ci = Components.interfaces;
|
|||||||
const Cr = Components.results;
|
const Cr = Components.results;
|
||||||
const Cu = Components.utils;
|
const Cu = Components.utils;
|
||||||
|
|
||||||
this.EXPORTED_SYMBOLS = [];
|
this.EXPORTED_SYMBOLS = ["XPIProvider"];
|
||||||
|
|
||||||
Components.utils.import("resource://gre/modules/Services.jsm");
|
Components.utils.import("resource://gre/modules/Services.jsm");
|
||||||
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||||
@ -1545,7 +1545,7 @@ function makeSafe(aFunction) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var XPIProvider = {
|
this.XPIProvider = {
|
||||||
// An array of known install locations
|
// An array of known install locations
|
||||||
installLocations: null,
|
installLocations: null,
|
||||||
// A dictionary of known install locations by name
|
// A dictionary of known install locations by name
|
||||||
@ -1758,23 +1758,6 @@ var XPIProvider = {
|
|||||||
* if it is a new profile or the version is unknown
|
* if it is a new profile or the version is unknown
|
||||||
*/
|
*/
|
||||||
startup: function XPI_startup(aAppChanged, aOldAppVersion, aOldPlatformVersion) {
|
startup: function XPI_startup(aAppChanged, aOldAppVersion, aOldPlatformVersion) {
|
||||||
logger.debug("startup");
|
|
||||||
this.runPhase = XPI_STARTING;
|
|
||||||
this.installs = [];
|
|
||||||
this.installLocations = [];
|
|
||||||
this.installLocationsByName = {};
|
|
||||||
// Hook for tests to detect when saving database at shutdown time fails
|
|
||||||
this._shutdownError = null;
|
|
||||||
// Clear this at startup for xpcshell test restarts
|
|
||||||
this._telemetryDetails = {};
|
|
||||||
// Clear the set of enabled experiments (experiments disabled by default).
|
|
||||||
this._enabledExperiments = new Set();
|
|
||||||
// Register our details structure with AddonManager
|
|
||||||
AddonManagerPrivate.setTelemetryDetails("XPI", this._telemetryDetails);
|
|
||||||
|
|
||||||
|
|
||||||
AddonManagerPrivate.recordTimestamp("XPI_startup_begin");
|
|
||||||
|
|
||||||
function addDirectoryInstallLocation(aName, aKey, aPaths, aScope, aLocked) {
|
function addDirectoryInstallLocation(aName, aKey, aPaths, aScope, aLocked) {
|
||||||
try {
|
try {
|
||||||
var dir = FileUtils.getDir(aKey, aPaths);
|
var dir = FileUtils.getDir(aKey, aPaths);
|
||||||
@ -1811,6 +1794,22 @@ var XPIProvider = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
AddonManagerPrivate.recordTimestamp("XPI_startup_begin");
|
||||||
|
|
||||||
|
logger.debug("startup");
|
||||||
|
this.runPhase = XPI_STARTING;
|
||||||
|
this.installs = [];
|
||||||
|
this.installLocations = [];
|
||||||
|
this.installLocationsByName = {};
|
||||||
|
// Hook for tests to detect when saving database at shutdown time fails
|
||||||
|
this._shutdownError = null;
|
||||||
|
// Clear this at startup for xpcshell test restarts
|
||||||
|
this._telemetryDetails = {};
|
||||||
|
// Clear the set of enabled experiments (experiments disabled by default).
|
||||||
|
this._enabledExperiments = new Set();
|
||||||
|
// Register our details structure with AddonManager
|
||||||
|
AddonManagerPrivate.setTelemetryDetails("XPI", this._telemetryDetails);
|
||||||
|
|
||||||
let hasRegistry = ("nsIWindowsRegKey" in Ci);
|
let hasRegistry = ("nsIWindowsRegKey" in Ci);
|
||||||
|
|
||||||
let enabledScopes = Prefs.getIntPref(PREF_EM_ENABLED_SCOPES,
|
let enabledScopes = Prefs.getIntPref(PREF_EM_ENABLED_SCOPES,
|
||||||
|
@ -68,10 +68,18 @@ html|html {
|
|||||||
background-image: linear-gradient(rgba(255, 255, 255, 0),
|
background-image: linear-gradient(rgba(255, 255, 255, 0),
|
||||||
rgba(255, 255, 255, 0.75));
|
rgba(255, 255, 255, 0.75));
|
||||||
border: 1px solid #C3CEDF;
|
border: 1px solid #C3CEDF;
|
||||||
border-radius: 5px;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
%ifdef WINDOWS_AERO
|
%ifdef WINDOWS_AERO
|
||||||
|
@media (-moz-os-version: windows-vista),
|
||||||
|
(-moz-os-version: windows-win7) {
|
||||||
|
%endif
|
||||||
|
*|*.main-content {
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
%ifdef WINDOWS_AERO
|
||||||
|
}
|
||||||
|
|
||||||
@media (-moz-windows-glass) {
|
@media (-moz-windows-glass) {
|
||||||
/* Buttons */
|
/* Buttons */
|
||||||
*|button,
|
*|button,
|
||||||
|
@ -181,15 +181,22 @@
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
.category:-moz-locale-dir(ltr) {
|
%ifdef WINDOWS_AERO
|
||||||
border-top-left-radius: 5px;
|
@media (-moz-os-version: windows-vista),
|
||||||
border-bottom-left-radius: 5px;
|
(-moz-os-version: windows-win7) {
|
||||||
}
|
%endif
|
||||||
|
.category:-moz-locale-dir(ltr) {
|
||||||
|
border-top-left-radius: 5px;
|
||||||
|
border-bottom-left-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
.category:-moz-locale-dir(rtl) {
|
.category:-moz-locale-dir(rtl) {
|
||||||
border-top-right-radius: 5px;
|
border-top-right-radius: 5px;
|
||||||
border-bottom-right-radius: 5px;
|
border-bottom-right-radius: 5px;
|
||||||
|
}
|
||||||
|
%ifdef WINDOWS_AERO
|
||||||
}
|
}
|
||||||
|
%endif
|
||||||
|
|
||||||
.category[disabled] {
|
.category[disabled] {
|
||||||
border-top: 0;
|
border-top: 0;
|
||||||
|
Loading…
Reference in New Issue
Block a user