This commit is contained in:
Wes Kocher 2016-05-25 15:39:57 -07:00
Родитель f1dea21d39 cbb588d022
Коммит 9ca4638c24
30 изменённых файлов: 1235 добавлений и 593 удалений

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

@ -207,16 +207,22 @@ AC_DEFUN([MOZ_ANDROID_AAR],[
MOZ_ANDROID_AAR_COMPONENT(concat(local_aar_var, _ASSETS), concat(root, assets), $6)
])
ANDROID_SUPPORT_LIBRARY_VERSION="23.0.1"
AC_SUBST(ANDROID_SUPPORT_LIBRARY_VERSION)
ANDROID_GOOGLE_PLAY_SERVICES_VERSION="8.1.0"
AC_SUBST(ANDROID_GOOGLE_PLAY_SERVICES_VERSION)
AC_DEFUN([MOZ_ANDROID_GOOGLE_PLAY_SERVICES],
[
if test -n "$MOZ_NATIVE_DEVICES" ; then
AC_SUBST(MOZ_NATIVE_DEVICES)
MOZ_ANDROID_AAR(play-services-base, 8.1.0, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-basement, 8.1.0, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-cast, 8.1.0, google, com/google/android/gms)
MOZ_ANDROID_AAR(mediarouter-v7, 23.0.1, android, com/android/support, REQUIRED_INTERNAL_IMPL)
MOZ_ANDROID_AAR(play-services-base, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-basement, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-cast, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms)
MOZ_ANDROID_AAR(mediarouter-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support, REQUIRED_INTERNAL_IMPL)
fi
])
@ -225,9 +231,9 @@ AC_DEFUN([MOZ_ANDROID_GOOGLE_CLOUD_MESSAGING],
[
if test -n "$MOZ_ANDROID_GCM" ; then
MOZ_ANDROID_AAR(play-services-base, 8.1.0, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-basement, 8.1.0, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-gcm, 8.1.0, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-base, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-basement, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-gcm, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms)
fi
])
@ -237,10 +243,10 @@ AC_DEFUN([MOZ_ANDROID_INSTALL_TRACKING],
if test -n "$MOZ_INSTALL_TRACKING"; then
AC_SUBST(MOZ_INSTALL_TRACKING)
MOZ_ANDROID_AAR(play-services-ads, 8.1.0, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-analytics, 8.1.0, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-appindexing, 8.1.0, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-basement, 8.1.0, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-ads, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-analytics, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-appindexing, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms)
MOZ_ANDROID_AAR(play-services-basement, $ANDROID_GOOGLE_PLAY_SERVICES_VERSION, google, com/google/android/gms)
fi
])
@ -334,19 +340,21 @@ case "$target" in
ANDROID_SDK="${android_sdk}"
ANDROID_SDK_ROOT="${android_sdk_root}"
ANDROID_TOOLS="${android_tools}"
ANDROID_BUILD_TOOLS_VERSION="$2"
AC_DEFINE_UNQUOTED(ANDROID_TARGET_SDK,$ANDROID_TARGET_SDK)
AC_SUBST(ANDROID_TARGET_SDK)
AC_SUBST(ANDROID_SDK_ROOT)
AC_SUBST(ANDROID_SDK)
AC_SUBST(ANDROID_TOOLS)
AC_SUBST(ANDROID_BUILD_TOOLS_VERSION)
MOZ_ANDROID_AAR(appcompat-v7, 23.0.1, android, com/android/support)
MOZ_ANDROID_AAR(cardview-v7, 23.0.1, android, com/android/support)
MOZ_ANDROID_AAR(design, 23.0.1, android, com/android/support)
MOZ_ANDROID_AAR(recyclerview-v7, 23.0.1, android, com/android/support)
MOZ_ANDROID_AAR(support-v4, 23.0.1, android, com/android/support, REQUIRED_INTERNAL_IMPL)
MOZ_ANDROID_AAR(appcompat-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
MOZ_ANDROID_AAR(cardview-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
MOZ_ANDROID_AAR(design, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
MOZ_ANDROID_AAR(recyclerview-v7, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support)
MOZ_ANDROID_AAR(support-v4, $ANDROID_SUPPORT_LIBRARY_VERSION, android, com/android/support, REQUIRED_INTERNAL_IMPL)
ANDROID_SUPPORT_ANNOTATIONS_JAR="$ANDROID_SDK_ROOT/extras/android/m2repository/com/android/support/support-annotations/23.0.1/support-annotations-23.0.1.jar"
ANDROID_SUPPORT_ANNOTATIONS_JAR="$ANDROID_SDK_ROOT/extras/android/m2repository/com/android/support/support-annotations/$ANDROID_SUPPORT_LIBRARY_VERSION/support-annotations-$ANDROID_SUPPORT_LIBRARY_VERSION.jar"
AC_MSG_CHECKING([for support-annotations JAR])
if ! test -e $ANDROID_SUPPORT_ANNOTATIONS_JAR ; then
AC_MSG_ERROR([You must download the support-annotations lib. Run the Android SDK tool and install the Android Support Repository under Extras. See https://developer.android.com/tools/extras/support-library.html for more info. (looked for $ANDROID_SUPPORT_ANNOTATIONS_JAR)])

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

@ -1,2 +1,8 @@
component {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32} devtools-startup.js
contract @mozilla.org/toolkit/console-clh;1 {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}
contract @mozilla.org/devtools/startup-clh;1 {9e9a9283-0ce9-4e4a-8f1c-ba129a032c32}
# We want this to override toolkit's --jsconsole handling, so it must have a
# a higher priority than the entry in jsconsole-clhandler.manifest. Higher
# priority means the "m-devtools" value below needs to be something that sorts
# before the one in jsconsole-clhandler.manifest. See details in
# nsICommandLineHandler.idl.
category command-line-handler m-devtools @mozilla.org/devtools/startup-clh;1

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

@ -10,6 +10,8 @@
const URL = "data:text/html;charset=utf8,test page";
var {Toolbox} = require("devtools/client/framework/toolbox");
var strings = Services.strings.createBundle(
"chrome://devtools/locale/toolbox.properties");
add_task(function* () {
info("Create a test tab and open the toolbox");
@ -33,26 +35,26 @@ add_task(function* () {
});
function zoomWithKey(toolbox, key) {
if (!key) {
let shortcut = strings.GetStringFromName(key);
if (!shortcut) {
info("Key was empty, skipping zoomWithKey");
return;
}
info("Zooming with key: " + key);
let currentZoom = toolbox.zoomValue;
EventUtils.synthesizeKey(key, {accelKey: true}, toolbox.win);
synthesizeKeyShortcut(shortcut);
isnot(toolbox.zoomValue, currentZoom, "The zoom level was changed in the toolbox");
}
function* checkKeyBindings(toolbox) {
zoomWithKey(toolbox, toolbox.doc.getElementById("toolbox-zoom-in-key").getAttribute("key"));
zoomWithKey(toolbox, toolbox.doc.getElementById("toolbox-zoom-in-key2").getAttribute("key"));
zoomWithKey(toolbox, toolbox.doc.getElementById("toolbox-zoom-in-key3").getAttribute("key"));
zoomWithKey(toolbox, "toolbox.zoomIn.key");
zoomWithKey(toolbox, "toolbox.zoomIn2.key");
zoomWithKey(toolbox, "toolbox.zoomIn3.key");
zoomWithKey(toolbox, toolbox.doc.getElementById("toolbox-zoom-reset-key").getAttribute("key"));
zoomWithKey(toolbox, "toolbox.zoomReset.key");
zoomWithKey(toolbox, toolbox.doc.getElementById("toolbox-zoom-out-key").getAttribute("key"));
zoomWithKey(toolbox, toolbox.doc.getElementById("toolbox-zoom-out-key2").getAttribute("key"));
zoomWithKey(toolbox, "toolbox.zoomOut.key");
zoomWithKey(toolbox, "toolbox.zoomOut2.key");
zoomWithKey(toolbox, toolbox.doc.getElementById("toolbox-zoom-reset-key2").getAttribute("key"));
zoomWithKey(toolbox, "toolbox.zoomReset2.key");
}

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

@ -11,6 +11,8 @@
const URL = "data:text/html;charset=utf8,test page for toolbox switching";
var {Toolbox} = require("devtools/client/framework/toolbox");
var strings = Services.strings.createBundle(
"chrome://devtools/locale/toolbox.properties");
add_task(function* () {
info("Create a test tab and open the toolbox");
@ -18,19 +20,21 @@ add_task(function* () {
let target = TargetFactory.forTab(tab);
let toolbox = yield gDevTools.showToolbox(target, "webconsole");
let keyElement = toolbox.doc.getElementById("toolbox-toggle-host-key");
let shortcut = strings.GetStringFromName("toolbox.toggleHost.key");
let {SIDE, BOTTOM, WINDOW} = Toolbox.HostType;
checkHostType(toolbox, BOTTOM, SIDE);
info("Switching from bottom to side");
synthesizeKeyElement(keyElement);
yield toolbox.once("host-changed");
let onHostChanged = toolbox.once("host-changed");
synthesizeKeyShortcut(shortcut, toolbox.win);
yield onHostChanged;
checkHostType(toolbox, SIDE, BOTTOM);
info("Switching from side to bottom");
synthesizeKeyElement(keyElement);
yield toolbox.once("host-changed");
onHostChanged = toolbox.once("host-changed");
synthesizeKeyShortcut(shortcut, toolbox.win);
yield onHostChanged;
checkHostType(toolbox, BOTTOM, SIDE);
info("Switching to window");
@ -38,8 +42,9 @@ add_task(function* () {
checkHostType(toolbox, WINDOW, BOTTOM);
info("Switching from window to bottom");
synthesizeKeyElement(keyElement);
yield toolbox.once("host-changed");
onHostChanged = toolbox.once("host-changed");
synthesizeKeyShortcut(shortcut, toolbox.win);
yield onHostChanged;
checkHostType(toolbox, BOTTOM, WINDOW);
yield toolbox.destroy();

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

@ -10,6 +10,8 @@
// and toggles appropriate things in the toolbox.
var doc = null, toolbox = null, panelWin = null, modifiedPrefs = [];
var strings = Services.strings.createBundle(
"chrome://devtools/locale/toolbox.properties");
add_task(function* () {
const URL = "data:text/html;charset=utf8,test for dynamically registering " +
@ -58,18 +60,18 @@ function* testOptionsShortcut() {
yield toolbox.selectTool("webconsole");
is(toolbox.currentToolId, "webconsole", "webconsole is selected");
synthesizeKeyFromKeyTag(doc.getElementById("toolbox-options-key"));
synthesizeKeyShortcut(strings.GetStringFromName("toolbox.options.key"));
is(toolbox.currentToolId, "options", "Toolbox selected via shortcut key (1)");
synthesizeKeyFromKeyTag(doc.getElementById("toolbox-options-key"));
synthesizeKeyShortcut(strings.GetStringFromName("toolbox.options.key"));
is(toolbox.currentToolId, "webconsole", "webconsole is selected (1)");
yield toolbox.selectTool("webconsole");
is(toolbox.currentToolId, "webconsole", "webconsole is selected");
synthesizeKeyFromKeyTag(doc.getElementById("toolbox-options-key2"));
synthesizeKeyShortcut(strings.GetStringFromName("toolbox.help.key"));
is(toolbox.currentToolId, "options", "Toolbox selected via shortcut key (2)");
synthesizeKeyFromKeyTag(doc.getElementById("toolbox-options-key"));
synthesizeKeyShortcut(strings.GetStringFromName("toolbox.options.key"));
is(toolbox.currentToolId, "webconsole", "webconsole is reselected (2)");
synthesizeKeyFromKeyTag(doc.getElementById("toolbox-options-key2"));
synthesizeKeyShortcut(strings.GetStringFromName("toolbox.help.key"));
is(toolbox.currentToolId, "options", "Toolbox selected via shortcut key (2)");
}

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

@ -6,6 +6,8 @@
requestLongerTimeout(2);
var {Toolbox} = require("devtools/client/framework/toolbox");
var strings = Services.strings.createBundle(
"chrome://devtools/locale/toolbox.properties");
add_task(function* () {
let tab = yield addTab("about:blank");
@ -18,45 +20,43 @@ add_task(function* () {
let toolbox = yield gDevTools.showToolbox(target, toolIDs[0],
Toolbox.HostType.BOTTOM);
let nextKey = toolbox.doc.getElementById("toolbox-next-tool-key")
.getAttribute("key");
let prevKey = toolbox.doc.getElementById("toolbox-previous-tool-key")
.getAttribute("key");
let nextShortcut = strings.GetStringFromName("toolbox.nextTool.key")
let prevShortcut = strings.GetStringFromName("toolbox.previousTool.key")
// Iterate over all tools, starting from options to netmonitor, in normal
// order.
for (let i = 1; i < toolIDs.length; i++) {
yield testShortcuts(toolbox, i, nextKey, toolIDs);
yield testShortcuts(toolbox, i, nextShortcut, toolIDs);
}
// Iterate again, in the same order, starting from netmonitor (so next one is
// 0: options).
for (let i = 0; i < toolIDs.length; i++) {
yield testShortcuts(toolbox, i, nextKey, toolIDs);
yield testShortcuts(toolbox, i, nextShortcut, toolIDs);
}
// Iterate over all tools in reverse order, starting from netmonitor to
// options.
for (let i = toolIDs.length - 2; i >= 0; i--) {
yield testShortcuts(toolbox, i, prevKey, toolIDs);
yield testShortcuts(toolbox, i, prevShortcut, toolIDs);
}
// Iterate again, in reverse order again, starting from options (so next one
// is length-1: netmonitor).
for (let i = toolIDs.length - 1; i >= 0; i--) {
yield testShortcuts(toolbox, i, prevKey, toolIDs);
yield testShortcuts(toolbox, i, prevShortcut, toolIDs);
}
yield toolbox.destroy();
gBrowser.removeCurrentTab();
});
function* testShortcuts(toolbox, index, key, toolIDs) {
function* testShortcuts(toolbox, index, shortcut, toolIDs) {
info("Testing shortcut to switch to tool " + index + ":" + toolIDs[index] +
" using key " + key);
" using shortcut " + shortcut);
let onToolSelected = toolbox.once("select");
EventUtils.synthesizeKey(key, {accelKey: true}, toolbox.win);
synthesizeKeyShortcut(shortcut);
let id = yield onToolSelected;
info("toolbox-select event from " + id);

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

@ -10,6 +10,8 @@ const TEST_URL = "data:text/html;charset=utf-8," +
"<body><h1>Testing reload from devtools</h1></body></html>";
var {Toolbox} = require("devtools/client/framework/toolbox");
var strings = Services.strings.createBundle(
"chrome://devtools/locale/toolbox.properties");
var target, toolbox, description, reloadsSent, toolIDs;
@ -60,10 +62,10 @@ function testAllTheTools(docked, callback, toolNum = 0) {
return callback();
}
toolbox.selectTool(toolIDs[toolNum]).then(() => {
testReload("toolbox-reload-key", docked, toolIDs[toolNum], () => {
testReload("toolbox-reload-key2", docked, toolIDs[toolNum], () => {
testReload("toolbox-force-reload-key", docked, toolIDs[toolNum], () => {
testReload("toolbox-force-reload-key2", docked, toolIDs[toolNum], () => {
testReload("toolbox.reload.key", docked, toolIDs[toolNum], () => {
testReload("toolbox.reload2.key", docked, toolIDs[toolNum], () => {
testReload("toolbox.forceReload.key", docked, toolIDs[toolNum], () => {
testReload("toolbox.forceReload2.key", docked, toolIDs[toolNum], () => {
testAllTheTools(docked, callback, toolNum + 1);
});
});
@ -72,17 +74,16 @@ function testAllTheTools(docked, callback, toolNum = 0) {
});
}
function testReload(key, docked, toolID, callback) {
function testReload(shortcut, docked, toolID, callback) {
let complete = () => {
gBrowser.selectedBrowser.messageManager.removeMessageListener("devtools:test:load", complete);
return callback();
};
gBrowser.selectedBrowser.messageManager.addMessageListener("devtools:test:load", complete);
description = docked + " devtools with tool " + toolID + ", key #" + key;
description = docked + " devtools with tool " + toolID + ", shortcut #" + shortcut;
info("Testing reload in " + description);
let el = toolbox.doc.getElementById(key);
synthesizeKeyElement(el);
synthesizeKeyShortcut(strings.GetStringFromName(shortcut), toolbox.win);
reloadsSent++;
}

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

@ -8,6 +8,8 @@ var modifiers = {
};
var toolbox;
var strings = Services.strings.createBundle(
"chrome://devtools/locale/toolbox.properties");
function test() {
addTab("about:blank").then(openToolbox);
@ -25,15 +27,15 @@ function openToolbox() {
function testZoom() {
info("testing zoom keys");
testZoomLevel("in", 2, 1.2);
testZoomLevel("out", 3, 0.9);
testZoomLevel("reset", 1, 1);
testZoomLevel("In", 2, 1.2);
testZoomLevel("Out", 3, 0.9);
testZoomLevel("Reset", 1, 1);
tidyUp();
}
function testZoomLevel(type, times, expected) {
sendZoomKey("toolbox-zoom-" + type + "-key", times);
sendZoomKey("toolbox.zoom" + type + ".key", times);
let zoom = getCurrentZoom(toolbox);
is(zoom.toFixed(2), expected, "zoom level correct after zoom " + type);
@ -42,10 +44,9 @@ function testZoomLevel(type, times, expected) {
"saved zoom level is correct after zoom " + type);
}
function sendZoomKey(id, times) {
let key = toolbox.doc.getElementById(id).getAttribute("key");
function sendZoomKey(shortcut, times) {
for (let i = 0; i < times; i++) {
EventUtils.synthesizeKey(key, modifiers, toolbox.win);
synthesizeKeyShortcut(strings.GetStringFromName(shortcut));
}
}

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

@ -168,8 +168,10 @@ function synthesizeKeyFromKeyTag(key) {
* https://github.com/electron/electron/blob/master/docs/api/accelerator.md
*
* @param {String} key
* @param {DOMWindow} target
* Optional window where to fire the key event
*/
function synthesizeKeyShortcut(key) {
function synthesizeKeyShortcut(key, target) {
// parseElectronKey requires any window, just to access `KeyboardEvent`
let window = Services.appShell.hiddenDOMWindow;
let shortcut = KeyShortcuts.parseElectronKey(window, key);
@ -181,7 +183,7 @@ function synthesizeKeyShortcut(key) {
ctrlKey: shortcut.ctrl,
metaKey: shortcut.meta,
shiftKey: shortcut.shift
});
}, target);
}
/**

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

@ -68,6 +68,9 @@ loader.lazyRequireGetter(this, "system",
"devtools/shared/system");
loader.lazyRequireGetter(this, "getPreferenceFront",
"devtools/server/actors/preference", true);
loader.lazyRequireGetter(this, "KeyShortcuts",
"devtools/client/shared/key-shortcuts", true);
loader.lazyGetter(this, "osString", () => {
return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
});
@ -417,19 +420,20 @@ Toolbox.prototype = {
this.textboxContextMenuPopup.addEventListener("popupshowing",
this._updateTextboxMenuItems, true);
var shortcuts = new KeyShortcuts({
window: this.doc.defaultView
});
this._buildDockButtons();
this._buildOptions();
this._buildOptions(shortcuts);
this._buildTabs();
this._applyCacheSettings();
this._applyServiceWorkersTestingSettings();
this._addKeysToWindow();
this._addReloadKeys();
this._addHostListeners();
this._addReloadKeys(shortcuts);
this._addHostListeners(shortcuts);
this._registerOverlays();
if (this._hostOptions && this._hostOptions.zoom === false) {
this._disableZoomKeys();
} else {
this._addZoomKeys();
if (!this._hostOptions || this._hostOptions.zoom === true) {
this._addZoomKeys(shortcuts);
this._loadInitialZoom();
}
this._setToolbarKeyboardNavigation();
@ -523,8 +527,8 @@ Toolbox.prototype = {
}
},
_buildOptions: function () {
let selectOptions = () => {
_buildOptions: function (shortcuts) {
let selectOptions = (name, event) => {
// Flip back to the last used panel if we are already
// on the options panel.
if (this.currentToolId === "options" &&
@ -533,11 +537,11 @@ Toolbox.prototype = {
} else {
this.selectTool("options");
}
// Prevent the opening of bookmarks window on toolbox.options.key
event.preventDefault();
};
let key = this.doc.getElementById("toolbox-options-key");
key.addEventListener("command", selectOptions, true);
let key2 = this.doc.getElementById("toolbox-options-key2");
key2.addEventListener("command", selectOptions, true);
shortcuts.on(toolboxStrings("toolbox.options.key"), selectOptions);
shortcuts.on(toolboxStrings("toolbox.help.key"), selectOptions);
},
_splitConsoleOnKeypress: function (e) {
@ -551,6 +555,7 @@ Toolbox.prototype = {
}
}
},
/**
* Add a shortcut key that should work when a split console
* has focus to the toolbox.
@ -574,34 +579,31 @@ Toolbox.prototype = {
this.doc.getElementById("toolbox-keyset").appendChild(cloned);
},
_addReloadKeys: function () {
_addReloadKeys: function (shortcuts) {
[
["toolbox-reload-key", false],
["toolbox-reload-key2", false],
["toolbox-force-reload-key", true],
["toolbox-force-reload-key2", true]
["reload", false],
["reload2", false],
["forceReload", true],
["forceReload2", true]
].forEach(([id, force]) => {
this.doc.getElementById(id).addEventListener("command", () => {
this.reloadTarget(force);
}, true);
let key = toolboxStrings("toolbox." + id + ".key");
shortcuts.on(key, this.reloadTarget.bind(this, force));
});
},
_addHostListeners: function () {
let nextKey = this.doc.getElementById("toolbox-next-tool-key");
nextKey.addEventListener("command", this.selectNextTool.bind(this), true);
_addHostListeners: function (shortcuts) {
shortcuts.on(toolboxStrings("toolbox.nextTool.key"),
this.selectNextTool.bind(this));
shortcuts.on(toolboxStrings("toolbox.previousTool.key"),
this.selectPreviousTool.bind(this));
shortcuts.on(toolboxStrings("toolbox.minimize.key"),
this._toggleMinimizeMode.bind(this));
shortcuts.on(toolboxStrings("toolbox.toggleHost.key"),
(name, event) => {
this.switchToPreviousHost();
event.preventDefault();
});
let prevKey = this.doc.getElementById("toolbox-previous-tool-key");
prevKey.addEventListener("command", this.selectPreviousTool.bind(this), true);
let minimizeKey = this.doc.getElementById("toolbox-minimize-key");
minimizeKey.addEventListener("command", this._toggleMinimizeMode, true);
let toggleKey = this.doc.getElementById("toolbox-toggle-host-key");
toggleKey.addEventListener("command", this.switchToPreviousHost.bind(this), true);
// Split console uses keypress instead of command so the event can be
// cancelled with stopPropagation on the keypress, and not preventDefault.
this.doc.addEventListener("keypress", this._splitConsoleOnKeypress, false);
this.doc.addEventListener("focus", this._onFocus, true);
@ -653,50 +655,31 @@ Toolbox.prototype = {
/**
* Wire up the listeners for the zoom keys.
*/
_addZoomKeys: function () {
let inKey = this.doc.getElementById("toolbox-zoom-in-key");
inKey.addEventListener("command", this.zoomIn.bind(this), true);
_addZoomKeys: function (shortcuts) {
shortcuts.on(toolboxStrings("toolbox.zoomIn.key"),
this.zoomIn.bind(this));
let zoomIn2 = toolboxStrings("toolbox.zoomIn2.key");
if (zoomIn2) {
shortcuts.on(zoomIn2, this.zoomIn.bind(this));
}
let zoomIn3 = toolboxStrings("toolbox.zoomIn2.key");
if (zoomIn3) {
shortcuts.on(zoomIn3, this.zoomIn.bind(this));
}
let inKey2 = this.doc.getElementById("toolbox-zoom-in-key2");
inKey2.addEventListener("command", this.zoomIn.bind(this), true);
shortcuts.on(toolboxStrings("toolbox.zoomOut.key"),
this.zoomOut.bind(this));
let zoomOut2 = toolboxStrings("toolbox.zoomOut2.key");
if (zoomOut2) {
shortcuts.on(zoomOut2, this.zoomOut.bind(this));
}
let inKey3 = this.doc.getElementById("toolbox-zoom-in-key3");
inKey3.addEventListener("command", this.zoomIn.bind(this), true);
let outKey = this.doc.getElementById("toolbox-zoom-out-key");
outKey.addEventListener("command", this.zoomOut.bind(this), true);
let outKey2 = this.doc.getElementById("toolbox-zoom-out-key2");
outKey2.addEventListener("command", this.zoomOut.bind(this), true);
let resetKey = this.doc.getElementById("toolbox-zoom-reset-key");
resetKey.addEventListener("command", this.zoomReset.bind(this), true);
let resetKey2 = this.doc.getElementById("toolbox-zoom-reset-key2");
resetKey2.addEventListener("command", this.zoomReset.bind(this), true);
},
_disableZoomKeys: function () {
let inKey = this.doc.getElementById("toolbox-zoom-in-key");
inKey.setAttribute("disabled", "true");
let inKey2 = this.doc.getElementById("toolbox-zoom-in-key2");
inKey2.setAttribute("disabled", "true");
let inKey3 = this.doc.getElementById("toolbox-zoom-in-key3");
inKey3.setAttribute("disabled", "true");
let outKey = this.doc.getElementById("toolbox-zoom-out-key");
outKey.setAttribute("disabled", "true");
let outKey2 = this.doc.getElementById("toolbox-zoom-out-key2");
outKey2.setAttribute("disabled", "true");
let resetKey = this.doc.getElementById("toolbox-zoom-reset-key");
resetKey.setAttribute("disabled", "true");
let resetKey2 = this.doc.getElementById("toolbox-zoom-reset-key2");
resetKey2.setAttribute("disabled", "true");
shortcuts.on(toolboxStrings("toolbox.zoomReset.key"),
this.zoomReset.bind(this));
let zoomReset2 = toolboxStrings("toolbox.zoomReset2.key");
if (zoomReset2) {
shortcuts.on(zoomReset2, this.zoomReset.bind(this));
}
},
/**
@ -709,22 +692,25 @@ Toolbox.prototype = {
/**
* Increase zoom level of toolbox window - make things bigger.
*/
zoomIn: function () {
zoomIn: function (name, event) {
this.setZoom(this.zoomValue + 0.1);
event.preventDefault();
},
/**
* Decrease zoom level of toolbox window - make things smaller.
*/
zoomOut: function () {
zoomOut: function (name, event) {
this.setZoom(this.zoomValue - 0.1);
event.preventDefault();
},
/**
* Reset zoom level of the toolbox window.
*/
zoomReset: function () {
zoomReset: function (name, event) {
this.setZoom(1);
event.preventDefault();
},
/**
@ -900,10 +886,9 @@ Toolbox.prototype = {
},
_getMinimizeButtonShortcutTooltip: function () {
let key = this.doc.getElementById("toolbox-minimize-key")
.getAttribute("key");
return "(" + (osString == "Darwin" ? "Cmd+Shift+" : "Ctrl+Shift+") +
key.toUpperCase() + ")";
let str = toolboxStrings("toolbox.minimize.key");
let key = KeyShortcuts.parseElectronKey(this.win, str);
return "(" + KeyShortcuts.stringify(key) + ")";
},
_onBottomHostMinimized: function () {

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

@ -29,75 +29,7 @@
<commandset id="editMenuCommands"/>
<keyset id="editMenuKeys"/>
<keyset id="toolbox-keyset">
<key id="toolbox-options-key"
key="&toolboxOptionsButton.key;"
oncommand="void(0);"
modifiers="shift, accel"/>
<key id="toolbox-options-key2"
keycode="&openHelp.commandkey;"
oncommand="void(0);"/>
<key id="toolbox-next-tool-key"
key="&toolboxNextTool.key;"
oncommand="void(0);"
modifiers="accel"/>
<key id="toolbox-previous-tool-key"
key="&toolboxPreviousTool.key;"
oncommand="void(0);"
modifiers="accel"/>
<key id="toolbox-zoom-in-key"
key="&fullZoomEnlargeCmd.commandkey;"
oncommand="void(0);"
modifiers="accel"/>
<key id="toolbox-zoom-in-key2"
key="&fullZoomEnlargeCmd.commandkey2;"
oncommand="void(0);"
modifiers="accel"/>
<key id="toolbox-zoom-in-key3"
key="&fullZoomEnlargeCmd.commandkey3;"
oncommand="void(0);"
modifiers="accel"/>
<key id="toolbox-zoom-out-key"
key="&fullZoomReduceCmd.commandkey;"
oncommand="void(0);"
modifiers="accel"/>
<key id="toolbox-zoom-out-key2"
key="&fullZoomReduceCmd.commandkey2;"
oncommand="void(0);"
modifiers="accel"/>
<key id="toolbox-zoom-reset-key"
key="&fullZoomResetCmd.commandkey;"
oncommand="void(0);"
modifiers="accel"/>
<key id="toolbox-zoom-reset-key2"
key="&fullZoomResetCmd.commandkey2;"
oncommand="void(0);"
modifiers="accel"/>
<key id="toolbox-reload-key"
key="&toolboxReload.key;"
oncommand="void(0);"
modifiers="accel"/>
<key id="toolbox-force-reload-key"
key="&toolboxReload.key;"
oncommand="void(0);"
modifiers="accel shift"/>
<key id="toolbox-reload-key2"
keycode="VK_F5"
oncommand="void(0);"
modifiers=""/>
<key id="toolbox-force-reload-key2"
keycode="VK_F5"
oncommand="void(0);"
modifiers="accel"/>
<key id="toolbox-minimize-key"
key="&toolboxToggleMinimize.key;"
oncommand="void(0);"
modifiers="shift, accel"/>
<key id="toolbox-toggle-host-key"
key="&toolboxToggle.key;"
oncommand="void(0);"
modifiers="accel shift"/>
</keyset>
<keyset id="toolbox-keyset"/>
<popupset>
<menupopup id="toolbox-textbox-context-popup">

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

@ -11,31 +11,7 @@
<!ENTITY toggleToolboxF12.keytext "F12">
<!ENTITY toolboxCloseButton.tooltip "Close Developer Tools">
<!ENTITY toolboxOptionsButton.key "O">
<!ENTITY toolboxNextTool.key "]">
<!ENTITY toolboxPreviousTool.key "[">
<!-- LOCALIZATION NOTE :
fullZoomEnlargeCmd.commandkey, fullZoomEnlargeCmd.commandkey2,
fullZoomEnlargeCmd.commandkey3, fullZoomReduceCmd.commandkey,
fullZoomReduceCmd.commandkey2, fullZoomResetCmd.commandkey,
and fullZoomResetCmd.commandkey2 should all match the corresponding
values from browser.dtd. -->
<!ENTITY fullZoomEnlargeCmd.commandkey "+">
<!ENTITY fullZoomEnlargeCmd.commandkey2 "=">
<!ENTITY fullZoomEnlargeCmd.commandkey3 "">
<!ENTITY fullZoomReduceCmd.commandkey "-">
<!ENTITY fullZoomReduceCmd.commandkey2 "">
<!ENTITY fullZoomResetCmd.commandkey "0">
<!ENTITY fullZoomResetCmd.commandkey2 "">
<!ENTITY toolboxReload.key "r">
<!-- This key is used with the accel+shift modifiers to minimize the toolbox -->
<!ENTITY toolboxToggleMinimize.key "U">
<!ENTITY toolboxToggle.key "d">
<!-- LOCALIZATION NOTE (toolboxFramesButton): This is the label for
- the iframes menu list that appears only when the document has some.
- It allows you to switch the context of the whole toolbox. -->

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

@ -135,3 +135,50 @@ toolbox.viewCssSourceInStyleEditor.label=Open File in Style-Editor
toolbox.viewJsSourceInDebugger.label=Open File in Debugger
toolbox.resumeOrderWarning=Page did not resume after the debugger was attached. To fix this, please close and re-open the toolbox.
# LOCALIZATION NOTE (toolbox.options.key)
# Key shortcut used to open the options panel
toolbox.options.key=CmdOrCtrl+Shift+O
# LOCALIZATION NOTE (toolbox.help.key)
# Key shortcut used to open the options panel
toolbox.help.key=F1
# LOCALIZATION NOTE (toolbox.nextTool.key)
# Key shortcut used to select the next tool
toolbox.nextTool.key=CmdOrCtrl+]
# LOCALIZATION NOTE (toolbox.previousTool.key)
# Key shortcut used to select the previous tool
toolbox.previousTool.key=CmdOrCtrl+[
# LOCALIZATION NOTE (toolbox.zoom*.key)
# Key shortcuts used to zomm in/out or reset the toolbox
# Should match fullZoom*Cmd.commandkey values from browser.dtd
toolbox.zoomIn.key=CmdOrCtrl+Plus
toolbox.zoomIn2.key=CmdOrCtrl+=
toolbox.zoomIn3.key=
toolbox.zoomOut.key=CmdOrCtrl+-
toolbox.zoomOut2.key=
toolbox.zoomReset.key=CmdOrCtrl+0
toolbox.zoomReset2.key=
# LOCALIZATION NOTE (toolbox.reload*.key)
# Key shortcuts used to reload the page
toolbox.reload.key=CmdOrCtrl+R
toolbox.reload2.key=F5
# LOCALIZATION NOTE (toolbox.forceReload*.key)
# Key shortcuts used to force reload of the page by bypassing caches
toolbox.forceReload.key=CmdOrCtrl+Shift+R
toolbox.forceReload2.key=CmdOrCtrl+F5
# LOCALIZATION NOTE (toolbox.minimize.key)
# Key shortcut used to minimize the toolbox
toolbox.minimize.key=CmdOrCtrl+Shift+U
# LOCALIZATION NOTE (toolbox.toggleHost.key)
# Key shortcut used to move the toolbox in bottom or side of the browser window
toolbox.toggleHost.key=CmdOrCtrl+Shift+D

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

@ -34,7 +34,6 @@ const ElectronKeysMapping = {
"F22": "DOM_VK_F22",
"F23": "DOM_VK_F23",
"F24": "DOM_VK_F24",
"Plus": "DOM_VK_PLUS",
"Space": "DOM_VK_SPACE",
"Backspace": "DOM_VK_BACK_SPACE",
"Delete": "DOM_VK_DELETE",
@ -122,13 +121,21 @@ KeyShortcuts.parseElectronKey = function (window, str) {
}
}
if (typeof (key) === "string" && key.length === 1) {
// Plus is a special case. It's a character key and shouldn't be matched
// against a keycode as it is only accessible via Shift/Capslock
if (key === "Plus") {
key = "+";
}
if (typeof key === "string" && key.length === 1) {
// Match any single character
shortcut.key = key.toLowerCase();
} else if (key in ElectronKeysMapping) {
// Maps the others manually to DOM API DOM_VK_*
key = ElectronKeysMapping[key];
shortcut.keyCode = window.KeyboardEvent[key];
// Used only to stringify the shortcut
shortcut.keyCodeString = key;
} else {
throw new Error("Unsupported key: " + key);
}
@ -136,6 +143,30 @@ KeyShortcuts.parseElectronKey = function (window, str) {
return shortcut;
};
KeyShortcuts.stringify = function (shortcut) {
let list = [];
if (shortcut.alt) {
list.push("Alt");
}
if (shortcut.ctrl) {
list.push("Ctrl");
}
if (shortcut.meta) {
list.push("Cmd");
}
if (shortcut.shift) {
list.push("Shift");
}
let key;
if (shortcut.key) {
key = shortcut.key.toUpperCase();
} else {
key = shortcut.keyCodeString;
}
list.push(key);
return list.join("+");
};
KeyShortcuts.prototype = {
destroy() {
this.window.removeEventListener("keydown", this);
@ -161,7 +192,12 @@ KeyShortcuts.prototype = {
if (shortcut.keyCode) {
return event.keyCode == shortcut.keyCode;
}
return event.key.toLowerCase() == shortcut.key;
// For character keys, we match if the final character is the expected one.
// But for digits we also accept indirect match to please azerty keyboard,
// which requires Shift to be pressed to get digits.
return event.key.toLowerCase() == shortcut.key ||
(shortcut.key.match(/[0-9]/) &&
event.keyCode == shortcut.key.charCodeAt(0));
},
handleEvent(event) {

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

@ -117,6 +117,8 @@ skip-if = e10s # Bug 1221911, bug 1222289, frequent e10s timeouts
[browser_html_tooltip-03.js]
[browser_html_tooltip-04.js]
[browser_html_tooltip-05.js]
[browser_html_tooltip_arrow-01.js]
[browser_html_tooltip_arrow-02.js]
[browser_inplace-editor-01.js]
[browser_inplace-editor-02.js]
[browser_inplace-editor_maxwidth.js]

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

@ -11,6 +11,7 @@
const HTML_NS = "http://www.w3.org/1999/xhtml";
const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Tooltip test">
<vbox flex="1">

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

@ -11,6 +11,7 @@
const HTML_NS = "http://www.w3.org/1999/xhtml";
const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css"?>
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
title="Tooltip test">
<vbox flex="1">

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

@ -0,0 +1,95 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from helper_html_tooltip.js */
"use strict";
/**
* Test the HTMLTooltip "arrow" type on small anchors. The arrow should remain
* aligned with the anchors as much as possible
*/
const HTML_NS = "http://www.w3.org/1999/xhtml";
const getAnchor = function (position) {
return `<html:div class="anchor" style="width:10px;
height: 10px;
position: absolute;
background: red;
${position}"></html:div>`;
};
const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css"?>
<?xml-stylesheet href="chrome://devtools/skin/light-theme.css"?>
<window class="theme-light"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
title="Tooltip test">
<vbox flex="1" style="position: relative">
${getAnchor("top: 0; left: 0;")}
${getAnchor("top: 0; left: 25px;")}
${getAnchor("top: 0; left: 50px;")}
${getAnchor("top: 0; left: 75px;")}
${getAnchor("bottom: 0; left: 0;")}
${getAnchor("bottom: 0; left: 25px;")}
${getAnchor("bottom: 0; left: 50px;")}
${getAnchor("bottom: 0; left: 75px;")}
${getAnchor("bottom: 0; right: 0;")}
${getAnchor("bottom: 0; right: 25px;")}
${getAnchor("bottom: 0; right: 50px;")}
${getAnchor("bottom: 0; right: 75px;")}
${getAnchor("top: 0; right: 0;")}
${getAnchor("top: 0; right: 25px;")}
${getAnchor("top: 0; right: 50px;")}
${getAnchor("top: 0; right: 75px;")}
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
add_task(function* () {
// Force the toolbox to be 200px high;
yield pushPref("devtools.toolbox.footer.height", 200);
yield addTab("about:blank");
let [,, doc] = yield createHost("bottom", TEST_URI);
info("Create HTML tooltip");
let tooltip = new HTMLTooltip({doc}, {type: "arrow"});
let div = doc.createElementNS(HTML_NS, "div");
div.style.height = "100%";
yield tooltip.setContent(div, 200, 35);
let {right: docRight} = doc.documentElement.getBoundingClientRect();
let elements = [...doc.querySelectorAll(".anchor")];
for (let el of elements) {
info("Display the tooltip on an anchor.");
yield showTooltip(tooltip, el);
let arrow = tooltip.arrow;
ok(arrow, "Tooltip has an arrow");
// Get the geometry of the anchor, the tooltip frame & arrow.
let arrowBounds = arrow.getBoxQuads({relativeTo: doc})[0].bounds;
let frameBounds = tooltip.frame.getBoxQuads({relativeTo: doc})[0].bounds;
let anchorBounds = el.getBoxQuads({relativeTo: doc})[0].bounds;
let intersects = arrowBounds.left <= anchorBounds.right &&
arrowBounds.right >= anchorBounds.left;
let isBlockedByViewport = arrowBounds.left == 0 ||
arrowBounds.right == docRight;
ok(intersects || isBlockedByViewport,
"Tooltip arrow is aligned with the anchor, or stuck on viewport's edge.");
let isInFrame = arrowBounds.left >= frameBounds.left &&
arrowBounds.right <= frameBounds.right;
ok(isInFrame,
"The tooltip arrow remains inside the tooltip frame horizontally");
yield hideTooltip(tooltip);
}
});

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

@ -0,0 +1,88 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
/* import-globals-from helper_html_tooltip.js */
"use strict";
/**
* Test the HTMLTooltip "arrow" type on wide anchors. The arrow should remain
* aligned with the anchors as much as possible
*/
const HTML_NS = "http://www.w3.org/1999/xhtml";
const getAnchor = function (position) {
return `<html:div class="anchor" style="height: 5px;
position: absolute;
background: red;
${position}"></html:div>`;
};
const TEST_URI = `data:text/xml;charset=UTF-8,<?xml version="1.0"?>
<?xml-stylesheet href="chrome://global/skin/global.css"?>
<?xml-stylesheet href="chrome://devtools/skin/common.css"?>
<?xml-stylesheet href="chrome://devtools/skin/light-theme.css"?>
<window class="theme-light"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
title="Tooltip test">
<vbox flex="1" style="position: relative">
${getAnchor("top: 0; left: 0; width: 50px;")}
${getAnchor("top: 10px; left: 0; width: 100px;")}
${getAnchor("top: 20px; left: 0; width: 150px;")}
${getAnchor("top: 30px; left: 0; width: 200px;")}
${getAnchor("top: 40px; left: 0; width: 250px;")}
${getAnchor("top: 50px; left: 100px; width: 250px;")}
${getAnchor("top: 100px; width: 50px; right: 0;")}
${getAnchor("top: 110px; width: 100px; right: 0;")}
${getAnchor("top: 120px; width: 150px; right: 0;")}
${getAnchor("top: 130px; width: 200px; right: 0;")}
${getAnchor("top: 140px; width: 250px; right: 0;")}
</vbox>
</window>`;
const {HTMLTooltip} = require("devtools/client/shared/widgets/HTMLTooltip");
loadHelperScript("helper_html_tooltip.js");
add_task(function* () {
// Force the toolbox to be 200px high;
yield pushPref("devtools.toolbox.footer.height", 200);
yield addTab("about:blank");
let [,, doc] = yield createHost("bottom", TEST_URI);
info("Create HTML tooltip");
let tooltip = new HTMLTooltip({doc}, {type: "arrow"});
let div = doc.createElementNS(HTML_NS, "div");
div.style.height = "100%";
yield tooltip.setContent(div, 200, 35);
let {right: docRight} = doc.documentElement.getBoundingClientRect();
let elements = [...doc.querySelectorAll(".anchor")];
for (let el of elements) {
info("Display the tooltip on an anchor.");
yield showTooltip(tooltip, el);
let arrow = tooltip.arrow;
ok(arrow, "Tooltip has an arrow");
// Get the geometry of the anchor, the tooltip frame & arrow.
let arrowBounds = arrow.getBoxQuads({relativeTo: doc})[0].bounds;
let frameBounds = tooltip.frame.getBoxQuads({relativeTo: doc})[0].bounds;
let anchorBounds = el.getBoxQuads({relativeTo: doc})[0].bounds;
let intersects = arrowBounds.left <= anchorBounds.right &&
arrowBounds.right >= anchorBounds.left;
let isBlockedByViewport = arrowBounds.left == 0 ||
arrowBounds.right == docRight;
ok(intersects || isBlockedByViewport,
"Tooltip arrow is aligned with the anchor, or stuck on viewport's edge.");
let isInFrame = arrowBounds.left >= frameBounds.left &&
arrowBounds.right <= frameBounds.right;
ok(isInFrame,
"The tooltip arrow remains inside the tooltip frame horizontally");
yield hideTooltip(tooltip);
}
});

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

@ -9,7 +9,9 @@ add_task(function* () {
});
yield testSimple(shortcuts);
yield testNonLetterCharacter(shortcuts);
yield testPlusCharacter(shortcuts);
yield testMixup(shortcuts);
yield testLooseDigits(shortcuts);
yield testExactModifiers(shortcuts);
yield testLooseShiftModifier(shortcuts);
yield testStrictLetterShiftModifier(shortcuts);
@ -61,6 +63,20 @@ function testNonLetterCharacter(shortcuts) {
yield onKey;
}
// Plus is special. It's keycode is the one for "=". That's because it requires
// shift to be pressed and is behind "=" key. So it should be considered as a
// character key
function testPlusCharacter(shortcuts) {
info("Test 'Plus' key shortcuts");
let onKey = once(shortcuts, "Plus", (key, event) => {
is(event.key, "+");
});
EventUtils.synthesizeKey("+", { keyCode: 61, shiftKey: true }, window);
yield onKey;
}
// Test they listeners are not mixed up between shortcuts
function testMixup(shortcuts) {
info("Test possible listener mixup");
@ -95,6 +111,40 @@ function testMixup(shortcuts) {
ok(hitSecond, "Got the second shortcut notified once it is actually fired");
}
// On azerty keyboard, digits are only available by pressing Shift/Capslock,
// but we accept them even if we omit doing that.
function testLooseDigits(shortcuts) {
info("Test Loose digits");
let onKey = once(shortcuts, "0", (key, event) => {
is(event.key, "à");
ok(!event.altKey);
ok(!event.ctrlKey);
ok(!event.metaKey);
ok(!event.shiftKey);
});
// Simulate a press on the "0" key, without shift pressed on a french
// keyboard
EventUtils.synthesizeKey(
"à",
{ keyCode: 48 },
window);
yield onKey;
onKey = once(shortcuts, "0", (key, event) => {
is(event.key, "0");
ok(!event.altKey);
ok(!event.ctrlKey);
ok(!event.metaKey);
ok(event.shiftKey);
});
// Simulate the same press with shift pressed
EventUtils.synthesizeKey(
"0",
{ keyCode: 48, shiftKey: true },
window);
yield onKey;
}
// Test that shortcuts is notified only when the modifiers match exactly
function testExactModifiers(shortcuts) {
info("Test exact modifiers match");

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

@ -52,10 +52,10 @@ function* hideTooltip(tooltip) {
* have been executed.
*/
function waitForReflow(tooltip) {
let {document} = tooltip;
let {doc} = tooltip;
return new Promise(resolve => {
document.documentElement.offsetWidth;
document.defaultView.requestAnimationFrame(resolve);
doc.documentElement.offsetWidth;
doc.defaultView.requestAnimationFrame(resolve);
});
}

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

@ -13,6 +13,36 @@ const XHTML_NS = "http://www.w3.org/1999/xhtml";
const IFRAME_URL = "chrome://devtools/content/shared/widgets/tooltip-frame.xhtml";
const IFRAME_CONTAINER_ID = "tooltip-iframe-container";
const POSITION = {
TOP: "top",
BOTTOM: "bottom",
};
module.exports.POSITION = POSITION;
const TYPE = {
NORMAL: "normal",
ARROW: "arrow",
};
module.exports.TYPE = TYPE;
const ARROW_WIDTH = 32;
// Default offset between the tooltip's left edge and the tooltip arrow.
const ARROW_OFFSET = 20;
const EXTRA_HEIGHT = {
"normal": 0,
// The arrow is 16px tall, but merges on 3px with the panel border
"arrow": 13,
};
const EXTRA_BORDER = {
"normal": 0,
"arrow": 3,
};
/**
* The HTMLTooltip can display HTML content in a tooltip popup.
*
@ -20,7 +50,7 @@ const IFRAME_CONTAINER_ID = "tooltip-iframe-container";
* The devtools toolbox, needed to get the devtools main window.
* @param {Object}
* - {String} type
* Display type of the tooltip. Possible values: "normal"
* Display type of the tooltip. Possible values: "normal", "arrow"
* - {Boolean} autofocus
* Defaults to true. Should the tooltip be focused when opening it.
* - {Boolean} consumeOutsideClicks
@ -31,13 +61,13 @@ function HTMLTooltip(toolbox,
{type = "normal", autofocus = true, consumeOutsideClicks = true} = {}) {
EventEmitter.decorate(this);
this.document = toolbox.doc;
this.doc = toolbox.doc;
this.type = type;
this.autofocus = autofocus;
this.consumeOutsideClicks = consumeOutsideClicks;
// Use the topmost window to listen for click events to close the tooltip
this.topWindow = this.document.defaultView.top;
this.topWindow = this.doc.defaultView.top;
this._onClick = this._onClick.bind(this);
@ -48,14 +78,16 @@ function HTMLTooltip(toolbox,
if (this._isXUL()) {
// In XUL context, load a placeholder document in the iframe container.
let onLoad = () => {
this.container.removeEventListener("load", onLoad, true);
this.frame.removeEventListener("load", onLoad, true);
resolve();
};
this.container.addEventListener("load", onLoad, true);
this.container.setAttribute("src", IFRAME_URL);
this.frame.addEventListener("load", onLoad, true);
this.frame.setAttribute("src", IFRAME_URL);
this.doc.querySelector("window").appendChild(this.container);
} else {
// In non-XUL context the container is ready to use as is.
this.doc.body.appendChild(this.container);
resolve();
}
});
@ -64,18 +96,35 @@ function HTMLTooltip(toolbox,
module.exports.HTMLTooltip = HTMLTooltip;
HTMLTooltip.prototype = {
position: {
TOP: "top",
BOTTOM: "bottom",
/**
* The tooltip frame is the child of the tooltip container that will only
* contain the tooltip content (and not the arrow or any other tooltip styling
* element).
* In XUL contexts, this is an iframe. In non XUL contexts this is a div,
* which also happens to be the tooltip.panel property.
*/
get frame() {
return this.container.querySelector(".tooltip-panel");
},
get parent() {
if (this._isXUL()) {
// In XUL context, we are wrapping the HTML content in an iframe.
let win = this.container.contentWindow.wrappedJSObject;
return win.document.getElementById(IFRAME_CONTAINER_ID);
/**
* The tooltip panel is the parentNode of the tooltip content provided in
* setContent().
*/
get panel() {
if (!this._isXUL()) {
return this.frame;
}
return this.container;
// In XUL context, the content is wrapped in an iframe.
let win = this.frame.contentWindow.wrappedJSObject;
return win.document.getElementById(IFRAME_CONTAINER_ID);
},
/**
* The arrow element. Might be null depending on the tooltip type.
*/
get arrow() {
return this.container.querySelector(".tooltip-arrow");
},
/**
@ -92,12 +141,15 @@ HTMLTooltip.prototype = {
* added in the tooltip container.
*/
setContent: function (content, width, height) {
this.preferredWidth = width;
this.preferredHeight = height;
let themeHeight = EXTRA_HEIGHT[this.type] + 2 * EXTRA_BORDER[this.type];
let themeWidth = 2 * EXTRA_BORDER[this.type];
this.preferredWidth = width + themeWidth;
this.preferredHeight = height + themeHeight;
return this.containerReady.then(() => {
this.parent.innerHTML = "";
this.parent.appendChild(content);
this.panel.innerHTML = "";
this.panel.appendChild(content);
});
},
@ -115,25 +167,27 @@ HTMLTooltip.prototype = {
*/
show: function (anchor, {position} = {}) {
this.containerReady.then(() => {
let {top, left, width, height} = this._findBestPosition(anchor, position);
let computedPosition = this._findBestPosition(anchor, position);
if (this._isXUL()) {
this.container.setAttribute("width", width);
this.container.setAttribute("height", height);
} else {
this.container.style.width = width + "px";
this.container.style.height = height + "px";
let isTop = computedPosition.position === POSITION.TOP;
this.container.classList.toggle("tooltip-top", isTop);
this.container.classList.toggle("tooltip-bottom", !isTop);
this.container.style.width = computedPosition.width + "px";
this.container.style.height = computedPosition.height + "px";
this.container.style.top = computedPosition.top + "px";
this.container.style.left = computedPosition.left + "px";
if (this.type === TYPE.ARROW) {
this.arrow.style.left = computedPosition.arrowLeft + "px";
}
this.container.style.top = top + "px";
this.container.style.left = left + "px";
this.container.style.display = "block";
this.container.classList.add("tooltip-visible");
if (this.autofocus) {
this.container.focus();
}
this.attachEventsTimer = this.document.defaultView.setTimeout(() => {
this.attachEventsTimer = this.doc.defaultView.setTimeout(() => {
if (this.autofocus) {
this.frame.focus();
}
this.topWindow.addEventListener("click", this._onClick, true);
this.emit("shown");
}, 0);
@ -145,11 +199,11 @@ HTMLTooltip.prototype = {
* is hidden.
*/
hide: function () {
this.document.defaultView.clearTimeout(this.attachEventsTimer);
this.doc.defaultView.clearTimeout(this.attachEventsTimer);
if (this.isVisible()) {
this.topWindow.removeEventListener("click", this._onClick, true);
this.container.style.display = "none";
this.container.classList.remove("tooltip-visible");
this.emit("hidden");
}
},
@ -159,8 +213,7 @@ HTMLTooltip.prototype = {
* @return {Boolean} true if the tooltip is visible
*/
isVisible: function () {
let win = this.document.defaultView;
return win.getComputedStyle(this.container).display != "none";
return this.container.classList.contains("tooltip-visible");
},
/**
@ -173,19 +226,21 @@ HTMLTooltip.prototype = {
},
_createContainer: function () {
let container;
let container = this.doc.createElementNS(XHTML_NS, "div");
container.setAttribute("type", this.type);
container.classList.add("tooltip-container");
let html;
if (this._isXUL()) {
container = this.document.createElementNS(XHTML_NS, "iframe");
container.classList.add("devtools-tooltip-iframe");
this.document.querySelector("window").appendChild(container);
html = '<iframe class="devtools-tooltip-iframe tooltip-panel"></iframe>';
} else {
container = this.document.createElementNS(XHTML_NS, "div");
this.document.body.appendChild(container);
html = '<div class="tooltip-panel theme-body"></div>';
}
container.classList.add("theme-body");
container.classList.add("devtools-htmltooltip-container");
if (this.type === TYPE.ARROW) {
html += '<div class="tooltip-arrow"></div>';
}
container.innerHTML = html;
return container;
},
@ -202,13 +257,13 @@ HTMLTooltip.prototype = {
},
_isInTooltipContainer: function (node) {
let contentWindow = this.parent.ownerDocument.defaultView;
let contentWindow = this.panel.ownerDocument.defaultView;
let win = node.ownerDocument.defaultView;
if (win === contentWindow) {
// If node is in the same window as the tooltip, check if the tooltip
// parent contains node.
return this.parent.contains(node);
return this.panel.contains(node);
}
// Otherwise check if the node window is in the tooltip window.
@ -221,48 +276,80 @@ HTMLTooltip.prototype = {
return false;
},
/**
* Calculates the best possible position to display the tooltip near the
* provided anchor. An optional position can be provided, but will be
* respected only if it doesn't force the tooltip to be resized.
*
* If the tooltip has to be resized, the position will be wherever the most
* space is available.
*
*/
_findBestPosition: function (anchor, position) {
let top, left;
let {TOP, BOTTOM} = this.position;
let {TOP, BOTTOM} = POSITION;
let {left: anchorLeft, top: anchorTop, height: anchorHeight}
= this._getRelativeRect(anchor, this.document);
// Get anchor geometry
let {
left: anchorLeft, top: anchorTop,
height: anchorHeight, width: anchorWidth
} = this._getRelativeRect(anchor, this.doc);
// Get document geometry
let {bottom: docBottom, right: docRight} =
this.document.documentElement.getBoundingClientRect();
this.doc.documentElement.getBoundingClientRect();
let height = this.preferredHeight;
// Check if the popup can fit above the anchor.
// Calculate available space for the tooltip.
let availableTop = anchorTop;
let fitsAbove = availableTop >= height;
// Check if the popup can fit below the anchor.
let availableBelow = docBottom - (anchorTop + anchorHeight);
let fitsBelow = availableBelow >= height;
let availableBottom = docBottom - (anchorTop + anchorHeight);
let isPositionSuitable = (fitsAbove && position === TOP)
|| (fitsBelow && position === BOTTOM);
if (!isPositionSuitable) {
// If the preferred position does not fit the preferred height,
// pick the position offering the most height.
position = availableTop > availableBelow ? TOP : BOTTOM;
// Find POSITION
let keepPosition = false;
if (position === TOP) {
keepPosition = availableTop >= this.preferredHeight;
} else if (position === BOTTOM) {
keepPosition = availableBottom >= this.preferredHeight;
}
if (!keepPosition) {
position = availableTop > availableBottom ? TOP : BOTTOM;
}
// Calculate height, capped by the maximum height available.
height = Math.min(height, Math.max(availableTop, availableBelow));
top = position === TOP ? anchorTop - height : anchorTop + anchorHeight;
// Calculate HEIGHT.
let availableHeight = position === TOP ? availableTop : availableBottom;
let height = Math.min(this.preferredHeight, availableHeight);
height = Math.floor(height);
// Calculate TOP.
let top = position === TOP ? anchorTop - height : anchorTop + anchorHeight;
// Calculate WIDTH.
let availableWidth = docRight;
let width = Math.min(this.preferredWidth, availableWidth);
// By default, align the tooltip's left edge with the anchor left edge.
if (anchorLeft + width <= docRight) {
left = anchorLeft;
} else {
// If the tooltip cannot fit, shift to the left just enough to fit.
left = docRight - width;
// Calculate LEFT.
// By default the tooltip is aligned with the anchor left edge. Unless this
// makes it overflow the viewport, in which case is shifts to the left.
let left = Math.min(anchorLeft, docRight - width);
// Calculate ARROW LEFT (tooltip's LEFT might be updated)
let arrowLeft;
// Arrow style tooltips may need to be shifted to the left
if (this.type === TYPE.ARROW) {
let arrowCenter = left + ARROW_OFFSET + ARROW_WIDTH / 2;
let anchorCenter = anchorLeft + anchorWidth / 2;
// If the anchor is too narrow, align the arrow and the anchor center.
if (arrowCenter > anchorCenter) {
left = Math.max(0, left - (arrowCenter - anchorCenter));
}
// Arrow's feft offset relative to the anchor.
arrowLeft = Math.min(ARROW_OFFSET, (anchorWidth - ARROW_WIDTH) / 2) | 0;
// Translate the coordinate to tooltip container
arrowLeft += anchorLeft - left;
// Make sure the arrow remains in the tooltip container.
arrowLeft = Math.min(arrowLeft, width - ARROW_WIDTH);
arrowLeft = Math.max(arrowLeft, 0);
}
return {top, left, width, height};
return {top, left, width, height, position, arrowLeft};
},
/**
@ -274,13 +361,9 @@ HTMLTooltip.prototype = {
// Width and Height can be taken from the rect.
let {width, height} = node.getBoundingClientRect();
// Find the smallest top/left coordinates from all quads.
let top = Infinity, left = Infinity;
let quads = node.getBoxQuads({relativeTo: relativeTo});
for (let quad of quads) {
top = Math.min(top, quad.bounds.top);
left = Math.min(left, quad.bounds.left);
}
let quads = node.getBoxQuads({relativeTo});
let top = quads[0].bounds.top;
let left = quads[0].bounds.left;
// Compute right and bottom coordinates using the rest of the data.
let right = left + width;
@ -290,6 +373,6 @@ HTMLTooltip.prototype = {
},
_isXUL: function () {
return this.document.documentElement.namespaceURI === XUL_NS;
return this.doc.documentElement.namespaceURI === XUL_NS;
},
};

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

@ -15,6 +15,7 @@
width: 100%;
height: 100%;
overflow: hidden;
color: var(--theme-body-color);
}
</style>
</head>

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

@ -246,10 +246,88 @@
background: transparent;
}
.devtools-htmltooltip-container {
.tooltip-container {
display: none;
position: fixed;
z-index: 9999;
display: none;
background: transparent;
}
.tooltip-panel{
background-color: var(--theme-tooltip-background);
}
.tooltip-visible {
display: block;
}
.tooltip-container[type="normal"] > .tooltip-panel {
height: 100%;
width: 100%;
}
/* Tooltip : arrow style */
.tooltip-container[type="arrow"] {
filter: drop-shadow(0 3px 4px var(--theme-tooltip-shadow));
}
.tooltip-container[type="arrow"] > .tooltip-panel {
position: absolute;
box-sizing: border-box;
height: calc(100% - 13px);
width: 100%;
border: 3px solid var(--theme-tooltip-border);
border-radius: 5px;
}
.tooltip-top[type="arrow"] .tooltip-panel {
top: 0;
}
.tooltip-bottom[type="arrow"] .tooltip-panel {
bottom: 0;
}
.tooltip-arrow {
position: absolute;
height: 16px;
width: 32px;
overflow: hidden;
}
.tooltip-top .tooltip-arrow {
bottom: 0;
}
.tooltip-bottom .tooltip-arrow {
top: 0;
}
.tooltip-arrow:before {
content: "";
position: absolute;
width: 21px;
height: 21px;
margin-left: 4px;
background: linear-gradient(-45deg,
var(--theme-tooltip-background) 50%, transparent 50%);
border-color: var(--theme-tooltip-border);
border-style: solid;
border-width: 0px 3px 3px 0px;
border-radius: 3px;
}
.tooltip-bottom .tooltip-arrow:before {
margin-top: 4px;
transform: rotate(225deg);
}
.tooltip-top .tooltip-arrow:before {
margin-top: -12px;
transform: rotate(45deg);
}
/* links to source code, like displaying `myfile.js:45` */

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

@ -63,6 +63,11 @@
/* Images */
--theme-pane-collapse-image: url(chrome://devtools/skin/images/pane-collapse.svg);
--theme-pane-expand-image: url(chrome://devtools/skin/images/pane-expand.svg);
/* Tooltips */
--theme-tooltip-border: #d9e1e8;
--theme-tooltip-background: rgba(255, 255, 255, .9);
--theme-tooltip-shadow: rgba(155, 155, 155, 0.26);
}
:root.theme-dark {
@ -114,6 +119,11 @@
/* Images */
--theme-pane-collapse-image: url(chrome://devtools/skin/images/pane-collapse.svg);
--theme-pane-expand-image: url(chrome://devtools/skin/images/pane-expand.svg);
/* Tooltips */
--theme-tooltip-border: #434850;
--theme-tooltip-background: rgba(19, 28, 38, .9);
--theme-tooltip-shadow: rgba(25, 25, 25, 0.76);
}
:root.theme-firebug {

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

@ -32,241 +32,6 @@ transition-property: all !important;\
var LOAD_ERROR = "error-load";
types.addActorType("old-stylesheet");
/**
* Creates a StyleEditorActor. StyleEditorActor provides remote access to the
* stylesheets of a document.
*/
var StyleEditorActor = exports.StyleEditorActor = protocol.ActorClass({
typeName: "styleeditor",
/**
* The window we work with, taken from the parent actor.
*/
get window() {
return this.parentActor.window;
},
/**
* The current content document of the window we work with.
*/
get document() {
return this.window.document;
},
events: {
"document-load" : {
type: "documentLoad",
styleSheets: Arg(0, "array:old-stylesheet")
}
},
form: function ()
{
return { actor: this.actorID };
},
initialize: function (conn, tabActor) {
protocol.Actor.prototype.initialize.call(this, null);
this.parentActor = tabActor;
// keep a map of sheets-to-actors so we don't create two actors for one sheet
this._sheets = new Map();
},
/**
* Destroy the current StyleEditorActor instance.
*/
destroy: function ()
{
this._sheets.clear();
},
/**
* Called by client when target navigates to a new document.
* Adds load listeners to document.
*/
newDocument: method(function () {
// delete previous document's actors
this._clearStyleSheetActors();
// Note: listening for load won't be necessary once
// https://bugzilla.mozilla.org/show_bug.cgi?id=839103 is fixed
if (this.document.readyState == "complete") {
this._onDocumentLoaded();
}
else {
this.window.addEventListener("load", this._onDocumentLoaded, false);
}
return {};
}),
/**
* Event handler for document loaded event. Add actor for each stylesheet
* and send an event notifying of the load
*/
_onDocumentLoaded: function (event) {
if (event) {
this.window.removeEventListener("load", this._onDocumentLoaded, false);
}
let documents = [this.document];
var forms = [];
for (let doc of documents) {
let sheetForms = this._addStyleSheets(doc.styleSheets);
forms = forms.concat(sheetForms);
// Recursively handle style sheets of the documents in iframes.
for (let iframe of doc.getElementsByTagName("iframe")) {
documents.push(iframe.contentDocument);
}
}
events.emit(this, "document-load", forms);
},
/**
* Add all the stylesheets to the map and create an actor for each one
* if not already created. Send event that there are new stylesheets.
*
* @param {[DOMStyleSheet]} styleSheets
* Stylesheets to add
* @return {[object]}
* Array of actors for each StyleSheetActor created
*/
_addStyleSheets: function (styleSheets)
{
let sheets = [];
for (let i = 0; i < styleSheets.length; i++) {
let styleSheet = styleSheets[i];
sheets.push(styleSheet);
// Get all sheets, including imported ones
let imports = this._getImported(styleSheet);
sheets = sheets.concat(imports);
}
let actors = sheets.map(this._createStyleSheetActor.bind(this));
return actors;
},
/**
* Create a new actor for a style sheet, if it hasn't already been created.
*
* @param {DOMStyleSheet} styleSheet
* The style sheet to create an actor for.
* @return {StyleSheetActor}
* The actor for this style sheet
*/
_createStyleSheetActor: function (styleSheet)
{
if (this._sheets.has(styleSheet)) {
return this._sheets.get(styleSheet);
}
let actor = new OldStyleSheetActor(styleSheet, this);
this.manage(actor);
this._sheets.set(styleSheet, actor);
return actor;
},
/**
* Get all the stylesheets @imported from a stylesheet.
*
* @param {DOMStyleSheet} styleSheet
* Style sheet to search
* @return {array}
* All the imported stylesheets
*/
_getImported: function (styleSheet) {
let imported = [];
for (let i = 0; i < styleSheet.cssRules.length; i++) {
let rule = styleSheet.cssRules[i];
if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) {
// Associated styleSheet may be null if it has already been seen due to
// duplicate @imports for the same URL.
if (!rule.styleSheet) {
continue;
}
imported.push(rule.styleSheet);
// recurse imports in this stylesheet as well
imported = imported.concat(this._getImported(rule.styleSheet));
}
else if (rule.type != Ci.nsIDOMCSSRule.CHARSET_RULE) {
// @import rules must precede all others except @charset
break;
}
}
return imported;
},
/**
* Clear all the current stylesheet actors in map.
*/
_clearStyleSheetActors: function () {
for (let actor in this._sheets) {
this.unmanage(this._sheets[actor]);
}
this._sheets.clear();
},
/**
* Create a new style sheet in the document with the given text.
* Return an actor for it.
*
* @param {object} request
* Debugging protocol request object, with 'text property'
* @return {object}
* Object with 'styelSheet' property for form on new actor.
*/
newStyleSheet: method(function (text) {
let parent = this.document.documentElement;
let style = this.document.createElementNS("http://www.w3.org/1999/xhtml", "style");
style.setAttribute("type", "text/css");
if (text) {
style.appendChild(this.document.createTextNode(text));
}
parent.appendChild(style);
let actor = this._createStyleSheetActor(style.sheet);
return actor;
}, {
request: { text: Arg(0, "string") },
response: { styleSheet: RetVal("old-stylesheet") }
})
});
/**
* The corresponding Front object for the StyleEditorActor.
*/
var StyleEditorFront = protocol.FrontClass(StyleEditorActor, {
initialize: function (client, tabForm) {
protocol.Front.prototype.initialize.call(this, client);
this.actorID = tabForm.styleEditorActor;
this.manage(this);
},
getStyleSheets: function () {
let deferred = promise.defer();
events.once(this, "document-load", (styleSheets) => {
deferred.resolve(styleSheets);
});
this.newDocument();
return deferred.promise;
},
addStyleSheet: function (text) {
return this.newStyleSheet(text);
}
});
/**
* A StyleSheetActor represents a stylesheet on the server.
*/
@ -650,6 +415,472 @@ var OldStyleSheetFront = protocol.FrontClass(OldStyleSheetActor, {
}
});
/**
* Creates a StyleEditorActor. StyleEditorActor provides remote access to the
* stylesheets of a document.
*/
var StyleEditorActor = exports.StyleEditorActor = protocol.ActorClass({
typeName: "styleeditor",
/**
* The window we work with, taken from the parent actor.
*/
get window() {
return this.parentActor.window;
},
/**
* The current content document of the window we work with.
*/
get document() {
return this.window.document;
},
events: {
"document-load" : {
type: "documentLoad",
styleSheets: Arg(0, "array:old-stylesheet")
}
},
form: function ()
{
return { actor: this.actorID };
},
initialize: function (conn, tabActor) {
protocol.Actor.prototype.initialize.call(this, null);
this.parentActor = tabActor;
// keep a map of sheets-to-actors so we don't create two actors for one sheet
this._sheets = new Map();
},
/**
* Destroy the current StyleEditorActor instance.
*/
destroy: function ()
{
this._sheets.clear();
},
/**
* Called by client when target navigates to a new document.
* Adds load listeners to document.
*/
newDocument: method(function () {
// delete previous document's actors
this._clearStyleSheetActors();
// Note: listening for load won't be necessary once
// https://bugzilla.mozilla.org/show_bug.cgi?id=839103 is fixed
if (this.document.readyState == "complete") {
this._onDocumentLoaded();
}
else {
this.window.addEventListener("load", this._onDocumentLoaded, false);
}
return {};
}),
/**
* Event handler for document loaded event. Add actor for each stylesheet
* and send an event notifying of the load
*/
_onDocumentLoaded: function (event) {
if (event) {
this.window.removeEventListener("load", this._onDocumentLoaded, false);
}
let documents = [this.document];
var forms = [];
for (let doc of documents) {
let sheetForms = this._addStyleSheets(doc.styleSheets);
forms = forms.concat(sheetForms);
// Recursively handle style sheets of the documents in iframes.
for (let iframe of doc.getElementsByTagName("iframe")) {
documents.push(iframe.contentDocument);
}
}
events.emit(this, "document-load", forms);
},
/**
* Add all the stylesheets to the map and create an actor for each one
* if not already created. Send event that there are new stylesheets.
*
* @param {[DOMStyleSheet]} styleSheets
* Stylesheets to add
* @return {[object]}
* Array of actors for each StyleSheetActor created
*/
_addStyleSheets: function (styleSheets)
{
let sheets = [];
for (let i = 0; i < styleSheets.length; i++) {
let styleSheet = styleSheets[i];
sheets.push(styleSheet);
// Get all sheets, including imported ones
let imports = this._getImported(styleSheet);
sheets = sheets.concat(imports);
}
let actors = sheets.map(this._createStyleSheetActor.bind(this));
return actors;
},
/**
* Create a new actor for a style sheet, if it hasn't already been created.
*
* @param {DOMStyleSheet} styleSheet
* The style sheet to create an actor for.
* @return {StyleSheetActor}
* The actor for this style sheet
*/
_createStyleSheetActor: function (styleSheet)
{
if (this._sheets.has(styleSheet)) {
return this._sheets.get(styleSheet);
}
let actor = new OldStyleSheetActor(styleSheet, this);
this.manage(actor);
this._sheets.set(styleSheet, actor);
return actor;
},
/**
* Get all the stylesheets @imported from a stylesheet.
*
* @param {DOMStyleSheet} styleSheet
* Style sheet to search
* @return {array}
* All the imported stylesheets
*/
_getImported: function (styleSheet) {
let imported = [];
for (let i = 0; i < styleSheet.cssRules.length; i++) {
let rule = styleSheet.cssRules[i];
if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) {
// Associated styleSheet may be null if it has already been seen due to
// duplicate @imports for the same URL.
if (!rule.styleSheet) {
continue;
}
imported.push(rule.styleSheet);
// recurse imports in this stylesheet as well
imported = imported.concat(this._getImported(rule.styleSheet));
}
else if (rule.type != Ci.nsIDOMCSSRule.CHARSET_RULE) {
// @import rules must precede all others except @charset
break;
}
}
return imported;
},
/**
* Clear all the current stylesheet actors in map.
*/
_clearStyleSheetActors: function () {
for (let actor in this._sheets) {
this.unmanage(this._sheets[actor]);
}
this._sheets.clear();
},
/**
* Create a new style sheet in the document with the given text.
* Return an actor for it.
*
* @param {object} request
* Debugging protocol request object, with 'text property'
* @return {object}
* Object with 'styelSheet' property for form on new actor.
*/
newStyleSheet: method(function (text) {
let parent = this.document.documentElement;
let style = this.document.createElementNS("http://www.w3.org/1999/xhtml", "style");
style.setAttribute("type", "text/css");
if (text) {
style.appendChild(this.document.createTextNode(text));
}
parent.appendChild(style);
let actor = this._createStyleSheetActor(style.sheet);
return actor;
}, {
request: { text: Arg(0, "string") },
response: { styleSheet: RetVal("old-stylesheet") }
})
});
/**
* The corresponding Front object for the StyleEditorActor.
*/
var StyleEditorFront = protocol.FrontClass(StyleEditorActor, {
initialize: function (client, tabForm) {
protocol.Front.prototype.initialize.call(this, client);
this.actorID = tabForm.styleEditorActor;
this.manage(this);
},
getStyleSheets: function () {
let deferred = promise.defer();
events.once(this, "document-load", (styleSheets) => {
deferred.resolve(styleSheets);
});
this.newDocument();
return deferred.promise;
},
addStyleSheet: function (text) {
return this.newStyleSheet(text);
}
});
/**
* Creates a StyleEditorActor. StyleEditorActor provides remote access to the
* stylesheets of a document.
*/
var StyleEditorActor = exports.StyleEditorActor = protocol.ActorClass({
typeName: "styleeditor",
/**
* The window we work with, taken from the parent actor.
*/
get window() {
return this.parentActor.window;
},
/**
* The current content document of the window we work with.
*/
get document() {
return this.window.document;
},
events: {
"document-load" : {
type: "documentLoad",
styleSheets: Arg(0, "array:old-stylesheet")
}
},
form: function ()
{
return { actor: this.actorID };
},
initialize: function (conn, tabActor) {
protocol.Actor.prototype.initialize.call(this, null);
this.parentActor = tabActor;
// keep a map of sheets-to-actors so we don't create two actors for one sheet
this._sheets = new Map();
},
/**
* Destroy the current StyleEditorActor instance.
*/
destroy: function ()
{
this._sheets.clear();
},
/**
* Called by client when target navigates to a new document.
* Adds load listeners to document.
*/
newDocument: method(function () {
// delete previous document's actors
this._clearStyleSheetActors();
// Note: listening for load won't be necessary once
// https://bugzilla.mozilla.org/show_bug.cgi?id=839103 is fixed
if (this.document.readyState == "complete") {
this._onDocumentLoaded();
}
else {
this.window.addEventListener("load", this._onDocumentLoaded, false);
}
return {};
}),
/**
* Event handler for document loaded event. Add actor for each stylesheet
* and send an event notifying of the load
*/
_onDocumentLoaded: function (event) {
if (event) {
this.window.removeEventListener("load", this._onDocumentLoaded, false);
}
let documents = [this.document];
var forms = [];
for (let doc of documents) {
let sheetForms = this._addStyleSheets(doc.styleSheets);
forms = forms.concat(sheetForms);
// Recursively handle style sheets of the documents in iframes.
for (let iframe of doc.getElementsByTagName("iframe")) {
documents.push(iframe.contentDocument);
}
}
events.emit(this, "document-load", forms);
},
/**
* Add all the stylesheets to the map and create an actor for each one
* if not already created. Send event that there are new stylesheets.
*
* @param {[DOMStyleSheet]} styleSheets
* Stylesheets to add
* @return {[object]}
* Array of actors for each StyleSheetActor created
*/
_addStyleSheets: function (styleSheets)
{
let sheets = [];
for (let i = 0; i < styleSheets.length; i++) {
let styleSheet = styleSheets[i];
sheets.push(styleSheet);
// Get all sheets, including imported ones
let imports = this._getImported(styleSheet);
sheets = sheets.concat(imports);
}
let actors = sheets.map(this._createStyleSheetActor.bind(this));
return actors;
},
/**
* Create a new actor for a style sheet, if it hasn't already been created.
*
* @param {DOMStyleSheet} styleSheet
* The style sheet to create an actor for.
* @return {StyleSheetActor}
* The actor for this style sheet
*/
_createStyleSheetActor: function (styleSheet)
{
if (this._sheets.has(styleSheet)) {
return this._sheets.get(styleSheet);
}
let actor = new OldStyleSheetActor(styleSheet, this);
this.manage(actor);
this._sheets.set(styleSheet, actor);
return actor;
},
/**
* Get all the stylesheets @imported from a stylesheet.
*
* @param {DOMStyleSheet} styleSheet
* Style sheet to search
* @return {array}
* All the imported stylesheets
*/
_getImported: function (styleSheet) {
let imported = [];
for (let i = 0; i < styleSheet.cssRules.length; i++) {
let rule = styleSheet.cssRules[i];
if (rule.type == Ci.nsIDOMCSSRule.IMPORT_RULE) {
// Associated styleSheet may be null if it has already been seen due to
// duplicate @imports for the same URL.
if (!rule.styleSheet) {
continue;
}
imported.push(rule.styleSheet);
// recurse imports in this stylesheet as well
imported = imported.concat(this._getImported(rule.styleSheet));
}
else if (rule.type != Ci.nsIDOMCSSRule.CHARSET_RULE) {
// @import rules must precede all others except @charset
break;
}
}
return imported;
},
/**
* Clear all the current stylesheet actors in map.
*/
_clearStyleSheetActors: function () {
for (let actor in this._sheets) {
this.unmanage(this._sheets[actor]);
}
this._sheets.clear();
},
/**
* Create a new style sheet in the document with the given text.
* Return an actor for it.
*
* @param {object} request
* Debugging protocol request object, with 'text property'
* @return {object}
* Object with 'styelSheet' property for form on new actor.
*/
newStyleSheet: method(function (text) {
let parent = this.document.documentElement;
let style = this.document.createElementNS("http://www.w3.org/1999/xhtml", "style");
style.setAttribute("type", "text/css");
if (text) {
style.appendChild(this.document.createTextNode(text));
}
parent.appendChild(style);
let actor = this._createStyleSheetActor(style.sheet);
return actor;
}, {
request: { text: Arg(0, "string") },
response: { styleSheet: RetVal("old-stylesheet") }
})
});
/**
* The corresponding Front object for the StyleEditorActor.
*/
var StyleEditorFront = protocol.FrontClass(StyleEditorActor, {
initialize: function (client, tabForm) {
protocol.Front.prototype.initialize.call(this, client);
this.actorID = tabForm.styleEditorActor;
this.manage(this);
},
getStyleSheets: function () {
let deferred = promise.defer();
events.once(this, "document-load", (styleSheets) => {
deferred.resolve(styleSheets);
});
this.newDocument();
return deferred.promise;
},
addStyleSheet: function (text) {
return this.newStyleSheet(text);
}
});
XPCOMUtils.defineLazyGetter(this, "DOMUtils", function () {
return Cc["@mozilla.org/inspector/dom-utils;1"].getService(Ci.inIDOMUtils);
});
@ -660,7 +891,6 @@ exports.StyleEditorFront = StyleEditorFront;
exports.OldStyleSheetActor = OldStyleSheetActor;
exports.OldStyleSheetFront = OldStyleSheetFront;
/**
* Normalize multiple relative paths towards the base paths on the right.
*/

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

@ -6,7 +6,7 @@ apply plugin: 'checkstyle'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
buildToolsVersion mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
defaultConfig {
targetSdkVersion 23
@ -171,30 +171,30 @@ android {
}
dependencies {
compile 'com.android.support:support-v4:23.0.1'
compile 'com.android.support:appcompat-v7:23.0.1'
compile 'com.android.support:cardview-v7:23.0.1'
compile 'com.android.support:recyclerview-v7:23.0.1'
compile 'com.android.support:design:23.0.1'
compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
compile "com.android.support:appcompat-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
compile "com.android.support:cardview-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
compile "com.android.support:recyclerview-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
compile "com.android.support:design:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
if (mozconfig.substs.MOZ_NATIVE_DEVICES) {
compile 'com.android.support:mediarouter-v7:23.0.1'
compile 'com.google.android.gms:play-services-basement:8.1.0'
compile 'com.google.android.gms:play-services-base:8.1.0'
compile 'com.google.android.gms:play-services-cast:8.1.0'
compile "com.android.support:mediarouter-v7:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
compile "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
compile "com.google.android.gms:play-services-base:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
compile "com.google.android.gms:play-services-cast:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
}
if (mozconfig.substs.MOZ_INSTALL_TRACKING) {
compile 'com.google.android.gms:play-services-ads:8.1.0'
compile 'com.google.android.gms:play-services-analytics:8.1.0'
compile 'com.google.android.gms:play-services-appindexing:8.1.0'
compile 'com.google.android.gms:play-services-basement:8.1.0'
compile "com.google.android.gms:play-services-ads:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
compile "com.google.android.gms:play-services-analytics:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
compile "com.google.android.gms:play-services-appindexing:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
compile "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
}
if (mozconfig.substs.MOZ_ANDROID_GCM) {
compile 'com.google.android.gms:play-services-basement:8.1.0'
compile 'com.google.android.gms:play-services-base:8.1.0'
compile 'com.google.android.gms:play-services-gcm:8.1.0'
compile "com.google.android.gms:play-services-basement:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
compile "com.google.android.gms:play-services-base:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
compile "com.google.android.gms:play-services-gcm:${mozconfig.substs.ANDROID_GOOGLE_PLAY_SERVICES_VERSION}"
}
// Gradle based builds include LeakCanary. Gradle based tests include the no-op version. Mach

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

@ -4,7 +4,7 @@ apply plugin: 'com.android.application'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
buildToolsVersion mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
defaultConfig {
targetSdkVersion 23

4
mobile/android/thirdparty/build.gradle поставляемый
Просмотреть файл

@ -4,7 +4,7 @@ apply plugin: 'com.android.library'
android {
compileSdkVersion 23
buildToolsVersion "23.0.1"
buildToolsVersion mozconfig.substs.ANDROID_BUILD_TOOLS_VERSION
defaultConfig {
targetSdkVersion 23
@ -38,7 +38,7 @@ android {
}
dependencies {
compile 'com.android.support:support-v4:23.0.1'
compile "com.android.support:support-v4:${mozconfig.substs.ANDROID_SUPPORT_LIBRARY_VERSION}"
}
apply plugin: 'idea'

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

@ -1,3 +1,3 @@
component {2cd0c310-e127-44d0-88fc-4435c9ab4d4b} jsconsole-clhandler.js
contract @mozilla.org/toolkit/console-clh;1 {2cd0c310-e127-44d0-88fc-4435c9ab4d4b}
category command-line-handler b-jsconsole @mozilla.org/toolkit/console-clh;1
category command-line-handler t-jsconsole @mozilla.org/toolkit/console-clh;1