зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to inbound, a=merge
This commit is contained in:
Коммит
9ca4638c24
|
@ -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,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
|
||||
|
|
Загрузка…
Ссылка в новой задаче