Bug 1292592 - remove xul dependency in sourceeditor
MozReview-Commit-ID: GhQiyrqF138
--- a/devtools/client/inspector/markup/html-editor.js
+++ b/devtools/client/inspector/markup/html-editor.js
@@ -52,20 +52,18 @@ function HTMLEditor(htmlDocument) {
config.extraKeys[ctrl("Enter")] = this.hide;
config.extraKeys.F2 = this.hide;
config.extraKeys.Esc = this.hide.bind(this, false);
this.container.addEventListener("click", this.hide, false);
this.editorInner.addEventListener("click", stopPropagation, false);
this.editor = new Editor(config);
- let iframe = this.editorInner.ownerDocument.createElement("iframe");
- this.editor.appendTo(this.editorInner, iframe).then(() => {
- this.hide(false);
- }).then(null, (err) => console.log(err.message));
+ this.editor.appendToLocalElement(this.editorInner);
+ this.hide(false);
}
HTMLEditor.prototype = {
/**
* Need to refresh position by manually setting CSS values, so this will
* need to be called on resizes and other sizing changes.
*/
--- a/devtools/client/inspector/markup/markup.xhtml
+++ b/devtools/client/inspector/markup/markup.xhtml
@@ -2,22 +2,25 @@
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
- <link rel="stylesheet" href="chrome://devtools/content/inspector/markup/markup.css" type="text/css"/>
<link rel="stylesheet" href="chrome://devtools/skin/markup.css" type="text/css"/>
+ <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/lib/codemirror.css" type="text/css"/>
+ <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/addon/dialog/dialog.css" type="text/css"/>
+ <link rel="stylesheet" href="chrome://devtools/content/sourceeditor/codemirror/mozilla.css" type="text/css"/>
<script type="application/javascript;version=1.8"
src="chrome://devtools/content/shared/theme-switching.js"/>
-
+ <script type="application/javascript;version=1.8"
+ src="chrome://devtools/content/sourceeditor/codemirror/codemirror.bundle.js"/>
</head>
<body class="theme-body devtools-monospace" role="application">
<!-- NOTE THAT WE MAKE EXTENSIVE USE OF HTML COMMENTS IN THIS FILE IN ORDER -->
<!-- TO MAKE SPANS READABLE WHILST AVOIDING SIGNIFICANT WHITESPACE -->
<div id="root-wrapper" role="presentation">
<div id="root" role="presentation"></div>
--- a/devtools/client/locales/en-US/sourceeditor.properties
+++ b/devtools/client/locales/en-US/sourceeditor.properties
@@ -112,8 +112,40 @@ showInformation2.commandkey=Shift-Ctrl-S
# conjunction with accel (Command on Mac or Ctrl on other platforms) to find
# the typed search
find.commandkey=F
# LOCALIZATION NOTE (findAgain.commandkey): This is the key to use in
# conjunction with accel (Command on Mac or Ctrl on other platforms) to find
# again the typed search
findAgain.commandkey=G
+
+# LOCALIZATION NOTE (find.key):
+# Key shortcut used to find the typed search
+find.key=CmdOrCtrl+F
+
+# LOCALIZATION NOTE (findAgain.key):
+# Key shortcut used to find again the typed search
+findNext.key=CmdOrCtrl+G
+
+# LOCALIZATION NOTE (undo.key):
+# Key shortcut used to find the previous typed search
+findPrev.key=Shift+CmdOrCtrl+G
+
+# LOCALIZATION NOTE (jumpToLine.key):
+# Key shortcut used to jump to a specific line in the editor.
+jumpToLine.key=CmdOrCtrl+J
+
+# LOCALIZATION NOTE (undo.key):
+# Key shortcut used to undo the last change.
+undo.key=CmdOrCtrl+Z
+
+# LOCALIZATION NOTE (redo.key):
+# Key shortcut used to redo the last change.
+redo.key=CmdOrCtrl+Y
+
+# LOCALIZATION NOTE (selectAll.key):
+# Key shortcut used to delete the character after the cursor
+delete.key=Delete
+
+# LOCALIZATION NOTE (selectAll.key):
+# Key shortcut used to select the whole content of the editor.
+selectAll.key=CmdOrCtrl+A
--- a/devtools/client/shared/theme-switching.js
+++ b/devtools/client/shared/theme-switching.js
@@ -1,16 +1,20 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* eslint-env browser */
"use strict";
(function () {
- const SCROLLBARS_URL = "chrome://devtools/skin/floating-scrollbars-dark-theme.css";
+ const { utils: Cu } = Components;
+ const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
+ const Services = require("Services");
+ const { gDevTools } = require("devtools/client/framework/devtools");
+ const { watchCSS } = require("devtools/client/shared/css-reload");
let documentElement = document.documentElement;
let os;
let platform = navigator.platform;
if (platform.startsWith("Win")) {
os = "win";
} else if (platform.startsWith("Mac")) {
os = "mac";
@@ -105,29 +109,31 @@
let loadEvents = [];
for (let url of newThemeDef.stylesheets) {
let {styleSheet, loadPromise} = appendStyleSheet(url);
devtoolsStyleSheets.get(newThemeDef).push(styleSheet);
loadEvents.push(loadPromise);
}
- // Floating scroll-bars like in OSX
- let hiddenDOMWindow = Cc["@mozilla.org/appshell/appShellService;1"]
- .getService(Ci.nsIAppShellService)
- .hiddenDOMWindow;
-
- // TODO: extensions might want to customize scrollbar styles too.
- if (!hiddenDOMWindow.matchMedia("(-moz-overlay-scrollbars)").matches) {
- if (newTheme == "dark") {
- StylesheetUtils.loadSheet(window, SCROLLBARS_URL, "agent");
- } else if (oldTheme == "dark") {
- StylesheetUtils.removeSheet(window, SCROLLBARS_URL, "agent");
+ try {
+ const StylesheetUtils = require("sdk/stylesheet/utils");
+ const SCROLLBARS_URL = "chrome://devtools/skin/floating-scrollbars-dark-theme.css";
+ // TODO: extensions might want to customize scrollbar styles too.
+ if (!Services.appShell.hiddenDOMWindow
+ .matchMedia("(-moz-overlay-scrollbars)").matches) {
+ if (newTheme == "dark") {
+ StylesheetUtils.loadSheet(window, SCROLLBARS_URL, "agent");
+ } else if (oldTheme == "dark") {
+ StylesheetUtils.removeSheet(window, SCROLLBARS_URL, "agent");
+ }
+ forceStyle();
}
- forceStyle();
+ } catch (e) {
+ console.warn("customize scrollbar styles is only supported in firefox");
}
Promise.all(loadEvents).then(() => {
// Unload all stylesheets and classes from the old theme.
if (oldThemeDef) {
for (let name of oldThemeDef.classList) {
documentElement.classList.remove(name);
}
@@ -157,23 +163,16 @@
}
function handlePrefChange(event, data) {
if (data.pref == "devtools.theme") {
switchTheme(data.newValue, data.oldValue);
}
}
- const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
- const { require } = Cu.import("resource://devtools/shared/Loader.jsm", {});
- const Services = require("Services");
- const { gDevTools } = require("devtools/client/framework/devtools");
- const StylesheetUtils = require("sdk/stylesheet/utils");
- const { watchCSS } = require("devtools/client/shared/css-reload");
-
if (documentElement.hasAttribute("force-theme")) {
switchTheme(documentElement.getAttribute("force-theme"));
} else {
switchTheme(Services.prefs.getCharPref("devtools.theme"));
gDevTools.on("pref-changed", handlePrefChange);
window.addEventListener("unload", function () {
gDevTools.off("pref-changed", handlePrefChange);
--- a/devtools/client/sourceeditor/editor.js
+++ b/devtools/client/sourceeditor/editor.js
@@ -29,16 +29,17 @@ const MAX_VERTICAL_OFFSET = 3;
const RE_SCRATCHPAD_ERROR = /(?:@Scratchpad\/\d+:|\()(\d+):?(\d+)?(?:\)|\n)/;
const RE_JUMP_TO_LINE = /^(\d+):?(\d+)?/;
const Services = require("Services");
const promise = require("promise");
const events = require("devtools/shared/event-emitter");
const { PrefObserver } = require("devtools/client/styleeditor/utils");
const { getClientCssProperties } = require("devtools/shared/fronts/css-properties");
+const {KeyShortcuts} = require("devtools/client/shared/key-shortcuts");
const {LocalizationHelper} = require("devtools/shared/l10n");
const L10N = new LocalizationHelper("devtools/locale/sourceeditor.properties");
const { OS } = Services.appinfo;
// CM_STYLES, CM_SCRIPTS and CM_IFRAME represent the HTML,
// JavaScript and CSS that is injected into an iframe in
@@ -288,33 +289,35 @@ Editor.prototype = {
env.setAttribute("src", CM_IFRAME);
el.appendChild(env);
this.once("destroy", () => el.removeChild(env));
return def.promise;
},
appendToLocalElement: function (el) {
- this._setup(el);
+ this._setup(el, true);
},
/**
* Do the actual appending and configuring of the CodeMirror instance. This is
* used by both append functions above, and does all the hard work to
* configure CodeMirror with all the right options/modes/etc.
*/
- _setup: function (el) {
+ _setup: function (el, isHtml) {
let win = el.ownerDocument.defaultView;
- let scriptsToInject = CM_SCRIPTS.concat(this.config.externalScripts);
- scriptsToInject.forEach(url => {
- if (url.startsWith("chrome://")) {
- Services.scriptloader.loadSubScript(url, win, "utf8");
- }
- });
+ if (!isHtml) {
+ let scriptsToInject = CM_SCRIPTS.concat(this.config.externalScripts);
+ scriptsToInject.forEach(url => {
+ if (url.startsWith("chrome://")) {
+ Services.scriptloader.loadSubScript(url, win, "utf8");
+ }
+ });
+ }
// Replace the propertyKeywords, colorKeywords and valueKeywords
// properties of the CSS MIME type with the values provided by the CSS properties
// database.
const {
propertyKeywords,
colorKeywords,
valueKeywords
@@ -462,17 +465,21 @@ Editor.prototype = {
this.emit("gutterClick", line, ev.button);
});
win.CodeMirror.defineExtension("l10n", (name) => {
return L10N.getStr(name);
});
- cm.getInputField().controllers.insertControllerAt(0, controller(this));
+ if (!isHtml) {
+ cm.getInputField().controllers.insertControllerAt(0, controller(this));
+ } else {
+ this._initShortcuts(win);
+ }
editors.set(this, cm);
this.reloadPreferences = this.reloadPreferences.bind(this);
this._prefObserver = new PrefObserver("devtools.editor.");
this._prefObserver.on(TAB_SIZE, this.reloadPreferences);
this._prefObserver.on(EXPAND_TAB, this.reloadPreferences);
this._prefObserver.on(KEYMAP, this.reloadPreferences);
@@ -1253,16 +1260,99 @@ Editor.prototype = {
if (foldGutterIndex !== -1) {
let gutters = this.config.gutters.slice();
gutters.splice(foldGutterIndex, 1);
this.setOption("gutters", gutters);
}
this.setOption("foldGutter", false);
}
+ },
+
+ /**
+ * Register all key shortcuts.
+ */
+ _initShortcuts: function (win) {
+ let shortcuts = new KeyShortcuts({
+ window: win,
+ });
+
+ this._onShortcut = this._onShortcut.bind(this);
+
+ // Process generic keys:
+ ["find.key",
+ "findNext.key",
+ "findPrev.key",
+ "jumpToLine.key",
+ "undo.key",
+ "redo.key",
+ "delete.key",
+ "selectAll.key"].forEach(name => {
+ let key = L10N.getStr(name);
+ shortcuts.on(key, (_, event) => this._onShortcut(name, event));
+ });
+ },
+
+ /**
+ * Key shortcut listener.
+ */
+ _onShortcut: function (name, event) {
+ if (!this._isInputOrTextarea(event.target)) {
+ return;
+ }
+ let cm = editors.get(this);
+ switch (name) {
+ // Localizable keys
+ case "find.key":
+ cm.execCommand("find");
+ break;
+ case "findNext.key":
+ if (cm.state.search != null && cm.state.search.query != null) {
+ cm.execCommand("findNext");
+ }
+ break;
+ case "findPrev.key":
+ cm.execCommand("findPrev");
+ break;
+ case "jumpToLine.key":
+ this.jumpToLine();
+ break;
+ case "undo.key":
+ if (this.canUndo()) {
+ cm.execCommand("undo");
+ }
+ break;
+ case "redo.key":
+ if (this.canRedo()) {
+ cm.execCommand("redo");
+ }
+ break;
+ case "delete.key":
+ if (this.somethingSelected()) {
+ cm.execCommand("delCharAfter");
+ }
+ break;
+ case "selectAll.key":
+ cm.execCommand("selectAll");
+ break;
+ default:
+ console.error("Unexpected editor key shortcut", name);
+ return;
+ }
+ // Prevent default for this action
+ event.stopPropagation();
+ event.preventDefault();
+ },
+
+ /**
+ * Check if a node is an input or textarea
+ */
+ _isInputOrTextarea: function (element) {
+ let name = element.tagName.toLowerCase();
+ return name === "input" || name === "textarea";
}
};
// Since Editor is a thin layer over CodeMirror some methods
// are mapped directly—without any changes.
CM_MAPPING.forEach(name => {
Editor.prototype[name] = function (...args) {