Merge fx-team to m-c

This commit is contained in:
Wes Kocher 2014-01-21 17:27:53 -08:00
commit 2546e3f69f
21 changed files with 267 additions and 70 deletions

View File

@ -999,7 +999,7 @@ let BookmarkingUI = {
if (widget.overflowed) {
// Don't open a popup in the overflow popup, rather just open the Library.
event.preventDefault();
widget.node.removeAttribute("noautoclose");
widget.node.removeAttribute("closemenu");
PlacesCommandHook.showPlacesOrganizer("BookmarksMenu");
return;
}
@ -1172,7 +1172,7 @@ let BookmarkingUI = {
let view = document.getElementById("PanelUI-bookmarks");
view.addEventListener("ViewShowing", this.onPanelMenuViewShowing);
view.addEventListener("ViewHiding", this.onPanelMenuViewHiding);
widget.node.setAttribute("noautoclose", "true");
widget.node.setAttribute("closemenu", "none");
PanelUI.showSubView("PanelUI-bookmarks", widget.node,
CustomizableUI.AREA_PANEL);
return;
@ -1181,9 +1181,9 @@ let BookmarkingUI = {
// Allow to close the panel if the page is already bookmarked, cause
// we are going to open the edit bookmark panel.
if (this._itemIds.length > 0)
widget.node.removeAttribute("noautoclose");
widget.node.removeAttribute("closemenu");
else
widget.node.setAttribute("noautoclose", "true");
widget.node.setAttribute("closemenu", "none");
}
// Ignore clicks on the star if we are updating its state.

View File

@ -1171,7 +1171,7 @@ SocialStatus = {
if (inMenuPanel) {
panel = document.getElementById("PanelUI-socialapi");
this._attachNotificatonPanel(panel, aToolbarButton, provider);
widget.node.setAttribute("noautoclose", "true");
widget.node.setAttribute("closemenu", "none");
showingEvent = "ViewShowing";
hidingEvent = "ViewHiding";
} else {

View File

@ -23,7 +23,7 @@
let widget = widgetGroup.forWindow(window);
this.inMenuPanel = widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL;
if (this.inMenuPanel) {
widget.node.setAttribute("noautoclose", "true");
widget.node.setAttribute("closemenu", "none");
return document.getElementById("PanelUI-socialapi");
}
return document.getAnonymousElementByAttribute(this, "anonid", "panel");

View File

@ -7,8 +7,17 @@
<label id="customization-header">
&customizeMode.menuAndToolbars.header;
</label>
<hbox id="customization-empty" hidden="true">
<label>&customizeMode.menuAndToolbars.empty;</label>
<label onclick="BrowserOpenAddonsMgr('addons://discovery/');"
onkeypress="BrowserOpenAddonsMgr('addons://discovery/');"
id="customization-more-tools"
class="text-link">
&customizeMode.menuAndToolbars.emptyLink;
</label>
</hbox>
<vbox id="customization-palette" flex="1"/>
<spacer flex="1"/>
<spacer id="customization-spacer" flex="1"/>
<hbox>
<button id="customization-toolbar-visibility-button" label="&customizeMode.toolbars;" class="customizationmode-button" type="menu">
<menupopup id="customization-toolbar-menu" onpopupshowing="onViewToolbarsPopupShowing(event)"/>

View File

@ -330,9 +330,15 @@ const PanelUI = {
* so that the panel knows if and when to close itself.
*/
onCommandHandler: function(aEvent) {
if (!aEvent.originalTarget.hasAttribute("noautoclose")) {
PanelUI.hide();
let closemenu = aEvent.originalTarget.getAttribute("closemenu");
if (closemenu == "none") {
return;
}
if (closemenu == "single") {
this.showMainView();
return;
}
this.hide();
},
/**

View File

@ -1230,7 +1230,8 @@ let CustomizableUIInternal = {
}
// If the user hit enter/return, we don't check preventDefault - it makes sense
// that this was prevented, but we probably still want to close the panel.
// If consumers don't want this to happen, they should specify noautoclose.
// If consumers don't want this to happen, they should specify the closemenu
// attribute.
} else if (aEvent.type != "command") { // mouse events:
if (aEvent.defaultPrevented || aEvent.button != 0) {
@ -1243,7 +1244,7 @@ let CustomizableUIInternal = {
}
}
if (aEvent.target.getAttribute("noautoclose") == "true" ||
if (aEvent.target.getAttribute("closemenu") == "none" ||
aEvent.target.getAttribute("widget-type") == "view") {
return;
}

View File

@ -44,12 +44,12 @@ function setAttributes(aNode, aAttrs) {
}
}
function updateCombinedWidgetStyle(aNode, aArea, aModifyAutoclose) {
function updateCombinedWidgetStyle(aNode, aArea, aModifyCloseMenu) {
let inPanel = (aArea == CustomizableUI.AREA_PANEL);
let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1";
let attrs = {class: cls};
if (aModifyAutoclose) {
attrs.noautoclose = inPanel ? true : null;
if (aModifyCloseMenu) {
attrs.closemenu = inPanel ? "none" : null;
}
for (let i = 0, l = aNode.childNodes.length; i < l; ++i) {
if (aNode.childNodes[i].localName == "separator")
@ -311,7 +311,7 @@ const CustomizableWidgets = [{
let areaType = CustomizableUI.getAreaType(this.currentArea);
let inPanel = areaType == CustomizableUI.TYPE_MENU_PANEL;
let inToolbar = areaType == CustomizableUI.TYPE_TOOLBAR;
let noautoclose = inPanel ? "true" : null;
let closeMenu = inPanel ? "none" : null;
let cls = inPanel ? "panel-combined-button" : "toolbarbutton-1";
if (!this.currentArea)
@ -319,20 +319,20 @@ const CustomizableWidgets = [{
let buttons = [{
id: "zoom-out-button",
noautoclose: noautoclose,
closemenu: closeMenu,
command: "cmd_fullZoomReduce",
class: cls,
label: true,
tooltiptext: true
}, {
id: "zoom-reset-button",
noautoclose: noautoclose,
closemenu: closeMenu,
command: "cmd_fullZoomReset",
class: cls,
tooltiptext: true
}, {
id: "zoom-in-button",
noautoclose: noautoclose,
closemenu: closeMenu,
command: "cmd_fullZoomEnlarge",
class: cls,
label: true,

View File

@ -48,6 +48,8 @@ function CustomizeMode(aWindow) {
// user. Then there's the visible palette, which gets populated and displayed
// to the user when in customizing mode.
this.visiblePalette = this.document.getElementById(kPaletteId);
this.paletteEmptyNotice = this.document.getElementById("customization-empty");
this.paletteSpacer = this.document.getElementById("customization-spacer");
};
CustomizeMode.prototype = {
@ -228,6 +230,8 @@ CustomizeMode.prototype = {
// Show the palette now that the transition has finished.
this.visiblePalette.hidden = false;
this.paletteSpacer.hidden = true;
this._updateEmptyPaletteNotice();
this._handler.isEnteringCustomizeMode = false;
this.dispatchToolboxEvent("customizationready");
@ -273,7 +277,9 @@ CustomizeMode.prototype = {
let documentElement = document.documentElement;
// Hide the palette before starting the transition for increased perf.
this.paletteSpacer.hidden = false;
this.visiblePalette.hidden = true;
this.paletteEmptyNotice.hidden = true;
this._transitioning = true;
@ -913,9 +919,15 @@ CustomizeMode.prototype = {
_onUIChange: function() {
this._changed = true;
this._updateResetButton();
this._updateEmptyPaletteNotice();
this.dispatchToolboxEvent("customizationchange");
},
_updateEmptyPaletteNotice: function() {
let paletteItems = this.visiblePalette.getElementsByTagName("toolbarpaletteitem");
this.paletteEmptyNotice.hidden = !!paletteItems.length;
},
_updateResetButton: function() {
let btn = this.document.getElementById("customization-reset-button");
btn.disabled = CustomizableUI.inDefaultState;

View File

@ -37,6 +37,7 @@ skip-if = true
[browser_918049_skipintoolbarset_dnd.js]
[browser_923857_customize_mode_event_wrapping_during_reset.js]
[browser_927717_customize_drag_empty_toolbar.js]
[browser_932928_show_notice_when_palette_empty.js]
[browser_934113_menubar_removable.js]
# Because this test is about the menubar, it can't be run on mac

View File

@ -0,0 +1,35 @@
/* 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";
// There should be an advert to get more addons when the palette is empty.
add_task(function() {
yield startCustomizing();
let visiblePalette = document.getElementById("customization-palette");
let emptyPaletteNotice = document.getElementById("customization-empty");
is(emptyPaletteNotice.hidden, true, "The empty palette notice should not be shown when there are items in the palette.");
while (visiblePalette.childElementCount) {
gCustomizeMode.addToToolbar(visiblePalette.children[0]);
}
is(visiblePalette.childElementCount, 0, "There shouldn't be any items remaining in the visible palette.");
is(emptyPaletteNotice.hidden, false, "The empty palette notice should be shown when there are no items in the palette.");
yield endCustomizing();
yield startCustomizing();
visiblePalette = document.getElementById("customization-palette");
emptyPaletteNotice = document.getElementById("customization-empty");
is(emptyPaletteNotice.hidden, false,
"The empty palette notice should be shown when there are no items in the palette and cust. mode is re-entered.");
gCustomizeMode.removeFromArea(document.getElementById("wrapper-home-button"));
is(emptyPaletteNotice.hidden, true,
"The empty palette notice should not be shown when there is at least one item in the palette.");
});
add_task(function asyncCleanup() {
yield endCustomizing();
yield resetCustomization();
});

View File

@ -3,6 +3,7 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
/* Bug 661762 */
function test()
{
waitForExplicitFinish();
@ -25,22 +26,15 @@ function test()
openScratchpad(function () {
let sw = gScratchpadWindow;
let {devtools} = Cu.import("resource://gre/modules/devtools/Loader.jsm", {});
openScratchpad(function () {
function onWebConsoleOpen(subj) {
Services.obs.removeObserver(onWebConsoleOpen,
"web-console-created");
subj.QueryInterface(Ci.nsISupportsString);
let hud = HUDService.getHudReferenceById(subj.data);
let target = devtools.TargetFactory.forTab(gBrowser.selectedTab);
gDevTools.showToolbox(target, "webconsole").then((toolbox) => {
let hud = toolbox.getCurrentPanel().hud;
hud.jsterm.clearOutput(true);
executeSoon(testFocus.bind(null, sw, hud));
}
Services.obs.
addObserver(onWebConsoleOpen, "web-console-created", false);
HUDService.toggleWebConsole();
testFocus(sw, hud);
});
});
});
}, true);

View File

@ -142,24 +142,6 @@ HUD_SERVICE.prototype =
return this.consoles.get(aId);
},
/**
* Toggle the Web Console for the current tab.
*
* @return object
* A promise for either the opening of the toolbox that holds the Web
* Console, or a Promise for the closing of the toolbox.
*/
toggleWebConsole: function HS_toggleWebConsole()
{
let window = this.currentContext();
let target = devtools.TargetFactory.forTab(window.gBrowser.selectedTab);
let toolbox = gDevTools.getToolbox(target);
return toolbox && toolbox.currentToolId == "webconsole" ?
toolbox.destroy() :
gDevTools.showToolbox(target, "webconsole");
},
/**
* Find if there is a Web Console open for the current tab and return the
* instance.
@ -744,7 +726,7 @@ BrowserConsole.prototype = Heritage.extend(WebConsole.prototype,
const HUDService = new HUD_SERVICE();
(() => {
let methods = ["openWebConsole", "openBrowserConsole", "toggleWebConsole",
let methods = ["openWebConsole", "openBrowserConsole",
"toggleBrowserConsole", "getOpenWebConsole",
"getBrowserConsole", "getHudByWindow", "getHudReferenceById"];
for (let method of methods) {

View File

@ -115,6 +115,7 @@ support-files =
[browser_console.js]
[browser_console_addonsdk_loader_exception.js]
[browser_console_clear_on_reload.js]
[browser_console_click_focus.js]
[browser_console_consolejsm_output.js]
[browser_console_dead_objects.js]
[browser_console_error_source_click.js]

View File

@ -0,0 +1,39 @@
/* vim:set ts=2 sw=2 sts=2 et: */
/* 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/. */
// Tests that the input field is focused when the console is opened.
const TEST_URI = "http://example.com/browser/browser/devtools/webconsole/test/test-console.html";
function test() {
addTab(TEST_URI);
browser.addEventListener("DOMContentLoaded", testInputFocus, false);
}
function testInputFocus() {
browser.removeEventListener("DOMContentLoaded", testInputFocus, false);
openConsole().then((hud) => {
let inputNode = hud.jsterm.inputNode;
ok(inputNode.getAttribute("focused"), "input node is focused");
let lostFocus = () => {
inputNode.removeEventListener("blur", lostFocus);
info("input node lost focus");
}
inputNode.addEventListener("blur", lostFocus);
browser.ownerDocument.getElementById("urlbar").click();
ok(!inputNode.getAttribute("focused"), "input node is not focused");
hud.outputNode.click();
ok(inputNode.getAttribute("focused"), "input node is focused");
finishTest();
});
}

View File

@ -569,6 +569,19 @@ WebConsoleFrame.prototype = {
toolbox.on("webconsole-selected", this._onPanelSelected);
}
/*
* Focus input line whenever the output area is clicked.
* Only focus when the target node (or parent, as in source links) is
* not an anchor.
*/
this.outputNode.addEventListener("click", (e) => {
if ((e.button == 0) &&
(e.target.nodeName.toLowerCase() != "a") &&
(e.target.parentNode.nodeName.toLowerCase() != "a")) {
this.jsterm.inputNode.focus();
}
});
// Toggle the timestamp on preference change
gDevTools.on("pref-changed", this._onToolboxPrefChanged);
this._onToolboxPrefChanged("pref-changed", {

View File

@ -677,6 +677,8 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY customizeMode.tabTitle "Customize &brandShortName;">
<!ENTITY customizeMode.menuAndToolbars.label "Menu and toolbars">
<!ENTITY customizeMode.menuAndToolbars.header "More Tools to Add to the Menu and Toolbar">
<!ENTITY customizeMode.menuAndToolbars.empty "Want more tools?">
<!ENTITY customizeMode.menuAndToolbars.emptyLink "Choose from thousands of add-ons">
<!ENTITY customizeMode.restoreDefaults "Restore Defaults">
<!ENTITY customizeMode.toolbars "Show / Hide Toolbars">

View File

@ -83,19 +83,25 @@ let Util = {
aElement instanceof Ci.nsIDOMHTMLTextAreaElement);
},
/**
* Checks whether aElement's content can be edited either if it(or any of its
* parents) has "contenteditable" attribute set to "true" or aElement's
* ownerDocument is in design mode.
*/
isEditableContent: function isEditableContent(aElement) {
if (!aElement)
return false;
if (aElement.isContentEditable || aElement.designMode == "on")
return true;
return false;
return !!aElement && (aElement.isContentEditable ||
this.isOwnerDocumentInDesignMode(aElement));
},
isEditable: function isEditable(aElement) {
if (!aElement)
if (!aElement) {
return false;
if (this.isTextInput(aElement) || this.isEditableContent(aElement))
}
if (this.isTextInput(aElement) || this.isEditableContent(aElement)) {
return true;
}
// If a body element is editable and the body is the child of an
// iframe or div we can assume this is an advanced HTML editor
@ -106,7 +112,15 @@ let Util = {
return true;
}
return aElement.ownerDocument && aElement.ownerDocument.designMode == "on";
return false;
},
/**
* Checks whether aElement's owner document has design mode turned on.
*/
isOwnerDocumentInDesignMode: function(aElement) {
return !!aElement && !!aElement.ownerDocument &&
aElement.ownerDocument.designMode == "on";
},
isMultilineInput: function isMultilineInput(aElement) {

View File

@ -105,6 +105,8 @@ var ContextMenuHandler = {
if (Util.isTextInput(this._target)) {
// select all text in the input control
this._target.select();
} else if (Util.isEditableContent(this._target)) {
this._target.ownerDocument.execCommand("selectAll", false);
} else {
// select the entire document
content.getSelection().selectAllChildren(content.document);
@ -121,7 +123,7 @@ var ContextMenuHandler = {
} else {
Util.dumpLn("error: target element does not support nsIDOMNSEditableElement");
}
} else if (this._target.isContentEditable) {
} else if (Util.isEditableContent(this._target)) {
try {
this._target.ownerDocument.execCommand("paste",
false,
@ -145,7 +147,7 @@ var ContextMenuHandler = {
} else {
Util.dumpLn("error: target element does not support nsIDOMNSEditableElement");
}
} else if (this._target.isContentEditable) {
} else if (Util.isEditableContent(this._target)) {
try {
this._target.ownerDocument.execCommand("cut", false);
} catch (ex) {
@ -259,7 +261,9 @@ var ContextMenuHandler = {
break;
}
// is the target contentEditable (not just inheriting contentEditable)
else if (elem.contentEditable == "true") {
// or the entire document in designer mode.
else if (elem.contentEditable == "true" ||
Util.isOwnerDocumentInDesignMode(elem)) {
this._target = elem;
isEditableText = true;
isText = true;

View File

@ -709,6 +709,84 @@ gTests.push({
run: getReopenTest(sendContextMenuClickToElement, sendTap)
});
gTests.push({
desc: "Bug 947505 - Right-click in a designMode document should display a " +
"context menu",
run: function test() {
info(chromeRoot + "browser_context_menu_tests_02.html");
yield addTab(chromeRoot + "browser_context_menu_tests_02.html");
purgeEventQueue();
emptyClipboard();
ContextUI.dismiss();
yield waitForCondition(() => !ContextUI.navbarVisible);
let tabWindow = Browser.selectedTab.browser.contentWindow;
let testSpan = tabWindow.document.getElementById("text1");
// Case #1: Document isn't in design mode and nothing is selected.
tabWindow.document.designMode = "off";
// Simulate right mouse click to reproduce the same step as noted in the
// appropriate bug. It's valid for non-touch case only.
synthesizeNativeMouseRDown(Browser.selectedTab.browser, 10, 10);
synthesizeNativeMouseRUp(Browser.selectedTab.browser, 10, 10);
yield waitForCondition(() => ContextUI.navbarVisible);
ok(ContextUI.navbarVisible, "Navbar is visible on context menu action.");
ok(ContextUI.tabbarVisible, "Tabbar is visible on context menu action.");
ContextUI.dismiss();
yield waitForCondition(() => !ContextUI.navbarVisible);
// Case #2: Document isn't in design mode and text is selected.
tabWindow.getSelection().selectAllChildren(testSpan);
let promise = waitForEvent(tabWindow.document, "popupshown");
sendContextMenuClickToSelection(tabWindow);
yield promise;
checkContextUIMenuItemVisibility(["context-copy", "context-search"]);
promise = waitForEvent(document, "popuphidden");
ContextMenuUI.hide();
yield promise;
// Case #3: Document is in design mode and nothing is selected.
tabWindow.document.designMode = "on";
tabWindow.getSelection().removeAllRanges();
promise = waitForEvent(tabWindow.document, "popupshown");
sendContextMenuClickToElement(tabWindow, testSpan);
yield promise;
checkContextUIMenuItemVisibility(["context-select-all", "context-select"]);
promise = waitForEvent(document, "popuphidden");
ContextMenuUI.hide();
yield promise;
// Case #4: Document is in design mode and text is selected.
tabWindow.getSelection().selectAllChildren(testSpan);
promise = waitForEvent(tabWindow.document, "popupshown");
sendContextMenuClickToSelection(tabWindow);
yield promise;
checkContextUIMenuItemVisibility(["context-cut", "context-copy",
"context-select-all", "context-select",
"context-search"]);
promise = waitForEvent(document, "popuphidden");
ContextMenuUI.hide();
yield promise;
Browser.closeTab(Browser.selectedTab, { forceClose: true });
}
});
function test() {
setDevPixelEqualToPx();
runTests();

View File

@ -3960,7 +3960,7 @@ window > chatbox {
%include ../shared/customizableui/customizeMode.inc.css
#main-window[customizing] #titlebar {
#main-window[customize-entered] #titlebar {
padding-top: 0;
}

View File

@ -51,7 +51,12 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene
private double mY;
private double mW;
private double mH;
private boolean mIsAutoComplete = true;
private enum PopupType {
AUTOCOMPLETE,
VALIDATIONMESSAGE;
}
private PopupType mPopupType;
private static int sAutoCompleteMinWidth = 0;
private static int sAutoCompleteRowHeight = 0;
@ -208,7 +213,8 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene
return false;
}
mIsAutoComplete = isAutoComplete;
mPopupType = (isAutoComplete ?
PopupType.AUTOCOMPLETE : PopupType.VALIDATIONMESSAGE);
return true;
}
@ -227,9 +233,9 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene
// Hide/show the appropriate popup contents
if (mAutoCompleteList != null)
mAutoCompleteList.setVisibility(mIsAutoComplete ? VISIBLE : GONE);
mAutoCompleteList.setVisibility((mPopupType == PopupType.AUTOCOMPLETE) ? VISIBLE : GONE);
if (mValidationMessage != null)
mValidationMessage.setVisibility(mIsAutoComplete ? GONE : VISIBLE);
mValidationMessage.setVisibility((mPopupType == PopupType.AUTOCOMPLETE) ? GONE : VISIBLE);
if (sAutoCompleteMinWidth == 0) {
Resources res = mContext.getResources();
@ -255,7 +261,7 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene
// For autocomplete suggestions, if the input is smaller than the screen-width,
// shrink the popup's width. Otherwise, keep it as FILL_PARENT.
if (mIsAutoComplete && (left + width) < viewport.width) {
if ((mPopupType == PopupType.AUTOCOMPLETE) && (left + width) < viewport.width) {
popupWidth = left < 0 ? left + width : width;
// Ensure the popup has a minimum width.
@ -269,14 +275,14 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene
}
int popupHeight;
if (mIsAutoComplete)
if (mPopupType == PopupType.AUTOCOMPLETE)
popupHeight = sAutoCompleteRowHeight * mAutoCompleteList.getAdapter().getCount();
else
popupHeight = sValidationMessageHeight;
int popupTop = top + height;
if (!mIsAutoComplete) {
if (mPopupType == PopupType.VALIDATIONMESSAGE) {
mValidationMessageText.setLayoutParams(sValidationTextLayoutNormal);
mValidationMessageArrow.setVisibility(VISIBLE);
mValidationMessageArrowInverted.setVisibility(GONE);
@ -299,7 +305,7 @@ public class FormAssistPopup extends RelativeLayout implements GeckoEventListene
popupHeight = top;
}
if (!mIsAutoComplete) {
if (mPopupType == PopupType.VALIDATIONMESSAGE) {
mValidationMessageText.setLayoutParams(sValidationTextLayoutInverted);
mValidationMessageArrow.setVisibility(GONE);
mValidationMessageArrowInverted.setVisibility(VISIBLE);