mirror of
https://github.com/gryf/pentadactyl-pm.git
synced 2025-12-20 09:27:58 +01:00
Fix tab number updates in FF36. Closes issue 300.
This commit is contained in:
@@ -65,7 +65,7 @@ var Browser = Module("browser", {
|
|||||||
|
|
||||||
mappings: function () {
|
mappings: function () {
|
||||||
mappings.add([modes.NORMAL],
|
mappings.add([modes.NORMAL],
|
||||||
["y"], "Yank current location to the clipboard",
|
["y", "<yank-location>"], "Yank current location to the clipboard",
|
||||||
function () { dactyl.clipboardWrite(buffer.uri.spec, true); });
|
function () { dactyl.clipboardWrite(buffer.uri.spec, true); });
|
||||||
|
|
||||||
// opening websites
|
// opening websites
|
||||||
|
|||||||
@@ -1651,7 +1651,7 @@ var Buffer = Module("buffer", {
|
|||||||
mappings: function () {
|
mappings: function () {
|
||||||
var myModes = config.browserModes;
|
var myModes = config.browserModes;
|
||||||
|
|
||||||
mappings.add(myModes, ["."],
|
mappings.add(myModes, [".", "<repeat-key>"],
|
||||||
"Repeat the last key event",
|
"Repeat the last key event",
|
||||||
function (args) {
|
function (args) {
|
||||||
if (mappings.repeat) {
|
if (mappings.repeat) {
|
||||||
@@ -1670,31 +1670,31 @@ var Buffer = Module("buffer", {
|
|||||||
function () { ex.stop(); });
|
function () { ex.stop(); });
|
||||||
|
|
||||||
// scrolling
|
// scrolling
|
||||||
mappings.add(myModes, ["j", "<Down>", "<C-e>"],
|
mappings.add(myModes, ["j", "<Down>", "<C-e>", "<scroll-down-line>"],
|
||||||
"Scroll document down",
|
"Scroll document down",
|
||||||
function (args) { buffer.scrollVertical("lines", Math.max(args.count, 1)); },
|
function (args) { buffer.scrollVertical("lines", Math.max(args.count, 1)); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["k", "<Up>", "<C-y>"],
|
mappings.add(myModes, ["k", "<Up>", "<C-y>", "<scroll-up-line>"],
|
||||||
"Scroll document up",
|
"Scroll document up",
|
||||||
function (args) { buffer.scrollVertical("lines", -Math.max(args.count, 1)); },
|
function (args) { buffer.scrollVertical("lines", -Math.max(args.count, 1)); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, dactyl.has("mail") ? ["h"] : ["h", "<Left>"],
|
mappings.add(myModes, dactyl.has("mail") ? ["h", "<scroll-left-column>"] : ["h", "<Left>", "<scroll-left-column>"],
|
||||||
"Scroll document to the left",
|
"Scroll document to the left",
|
||||||
function (args) { buffer.scrollHorizontal("columns", -Math.max(args.count, 1)); },
|
function (args) { buffer.scrollHorizontal("columns", -Math.max(args.count, 1)); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, dactyl.has("mail") ? ["l"] : ["l", "<Right>"],
|
mappings.add(myModes, dactyl.has("mail") ? ["l", "<scroll-right-column>"] : ["l", "<Right>", "<scroll-right-column>"],
|
||||||
"Scroll document to the right",
|
"Scroll document to the right",
|
||||||
function (args) { buffer.scrollHorizontal("columns", Math.max(args.count, 1)); },
|
function (args) { buffer.scrollHorizontal("columns", Math.max(args.count, 1)); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["0", "^"],
|
mappings.add(myModes, ["0", "^", "<scroll-begin>"],
|
||||||
"Scroll to the absolute left of the document",
|
"Scroll to the absolute left of the document",
|
||||||
function () { buffer.scrollToPercent(0, null); });
|
function () { buffer.scrollToPercent(0, null); });
|
||||||
|
|
||||||
mappings.add(myModes, ["$"],
|
mappings.add(myModes, ["$", "<scroll-end>"],
|
||||||
"Scroll to the absolute right of the document",
|
"Scroll to the absolute right of the document",
|
||||||
function () { buffer.scrollToPercent(100, null); });
|
function () { buffer.scrollToPercent(100, null); });
|
||||||
|
|
||||||
@@ -1708,7 +1708,7 @@ var Buffer = Module("buffer", {
|
|||||||
function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 100); },
|
function (args) { buffer.scrollToPercent(null, args.count != null ? args.count : 100); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["%"],
|
mappings.add(myModes, ["%", "<scroll-percent>"],
|
||||||
"Scroll to {count} percent of the document",
|
"Scroll to {count} percent of the document",
|
||||||
function (args) {
|
function (args) {
|
||||||
dactyl.assert(args.count > 0 && args.count <= 100);
|
dactyl.assert(args.count > 0 && args.count <= 100);
|
||||||
@@ -1716,59 +1716,59 @@ var Buffer = Module("buffer", {
|
|||||||
},
|
},
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["<C-d>"],
|
mappings.add(myModes, ["<C-d>", "<scroll-down>"],
|
||||||
"Scroll window downwards in the buffer",
|
"Scroll window downwards in the buffer",
|
||||||
function (args) { buffer._scrollByScrollSize(args.count, true); },
|
function (args) { buffer._scrollByScrollSize(args.count, true); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["<C-u>"],
|
mappings.add(myModes, ["<C-u>", "<scroll-up>"],
|
||||||
"Scroll window upwards in the buffer",
|
"Scroll window upwards in the buffer",
|
||||||
function (args) { buffer._scrollByScrollSize(args.count, false); },
|
function (args) { buffer._scrollByScrollSize(args.count, false); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["<C-b>", "<PageUp>", "<S-Space>"],
|
mappings.add(myModes, ["<C-b>", "<PageUp>", "<S-Space>", "<scroll-page-up>"],
|
||||||
"Scroll up a full page",
|
"Scroll up a full page",
|
||||||
function (args) { buffer.scrollVertical("pages", -Math.max(args.count, 1)); },
|
function (args) { buffer.scrollVertical("pages", -Math.max(args.count, 1)); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["<C-f>", "<PageDown>", "<Space>"],
|
mappings.add(myModes, ["<C-f>", "<PageDown>", "<Space>", "<scroll-page-down>"],
|
||||||
"Scroll down a full page",
|
"Scroll down a full page",
|
||||||
function (args) { buffer.scrollVertical("pages", Math.max(args.count, 1)); },
|
function (args) { buffer.scrollVertical("pages", Math.max(args.count, 1)); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["]f"],
|
mappings.add(myModes, ["]f", "<previous-frame>"],
|
||||||
"Focus next frame",
|
"Focus next frame",
|
||||||
function (args) { buffer.shiftFrameFocus(Math.max(args.count, 1)); },
|
function (args) { buffer.shiftFrameFocus(Math.max(args.count, 1)); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["[f"],
|
mappings.add(myModes, ["[f", "<next-frame>"],
|
||||||
"Focus previous frame",
|
"Focus previous frame",
|
||||||
function (args) { buffer.shiftFrameFocus(-Math.max(args.count, 1)); },
|
function (args) { buffer.shiftFrameFocus(-Math.max(args.count, 1)); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["]]"],
|
mappings.add(myModes, ["]]", "<next-page>"],
|
||||||
"Follow the link labeled 'next' or '>' if it exists",
|
"Follow the link labeled 'next' or '>' if it exists",
|
||||||
function (args) {
|
function (args) {
|
||||||
buffer.findLink("next", options["nextpattern"], (args.count || 1) - 1, true);
|
buffer.findLink("next", options["nextpattern"], (args.count || 1) - 1, true);
|
||||||
},
|
},
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["[["],
|
mappings.add(myModes, ["[[", "<previous-page>"],
|
||||||
"Follow the link labeled 'prev', 'previous' or '<' if it exists",
|
"Follow the link labeled 'prev', 'previous' or '<' if it exists",
|
||||||
function (args) {
|
function (args) {
|
||||||
buffer.findLink("previous", options["previouspattern"], (args.count || 1) - 1, true);
|
buffer.findLink("previous", options["previouspattern"], (args.count || 1) - 1, true);
|
||||||
},
|
},
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["gf"],
|
mappings.add(myModes, ["gf", "<view-source>"],
|
||||||
"Toggle between rendered and source view",
|
"Toggle between rendered and source view",
|
||||||
function () { buffer.viewSource(null, false); });
|
function () { buffer.viewSource(null, false); });
|
||||||
|
|
||||||
mappings.add(myModes, ["gF"],
|
mappings.add(myModes, ["gF", "<view-source-externally>"],
|
||||||
"View source with an external editor",
|
"View source with an external editor",
|
||||||
function () { buffer.viewSource(null, true); });
|
function () { buffer.viewSource(null, true); });
|
||||||
|
|
||||||
mappings.add(myModes, ["gi"],
|
mappings.add(myModes, ["gi", "<focus-input>"],
|
||||||
"Focus last used input field",
|
"Focus last used input field",
|
||||||
function (args) {
|
function (args) {
|
||||||
let elem = buffer.lastInputField;
|
let elem = buffer.lastInputField;
|
||||||
@@ -1808,7 +1808,7 @@ var Buffer = Module("buffer", {
|
|||||||
dactyl.open(url, { from: "paste", where: dactyl.NEW_TAB, background: true });
|
dactyl.open(url, { from: "paste", where: dactyl.NEW_TAB, background: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
mappings.add(myModes, ["p", "<MiddleMouse>"],
|
mappings.add(myModes, ["p", "<MiddleMouse>", "<open-clipboard-url>"],
|
||||||
"Open (put) a URL based on the current clipboard contents in the current buffer",
|
"Open (put) a URL based on the current clipboard contents in the current buffer",
|
||||||
function () {
|
function () {
|
||||||
let url = dactyl.clipboardRead();
|
let url = dactyl.clipboardRead();
|
||||||
@@ -1816,7 +1816,7 @@ var Buffer = Module("buffer", {
|
|||||||
dactyl.open(url);
|
dactyl.open(url);
|
||||||
});
|
});
|
||||||
|
|
||||||
mappings.add(myModes, ["P"],
|
mappings.add(myModes, ["P", "<tab-open-clipboard-url>"],
|
||||||
"Open (put) a URL based on the current clipboard contents in a new buffer",
|
"Open (put) a URL based on the current clipboard contents in a new buffer",
|
||||||
function () {
|
function () {
|
||||||
let url = dactyl.clipboardRead();
|
let url = dactyl.clipboardRead();
|
||||||
@@ -1825,16 +1825,16 @@ var Buffer = Module("buffer", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// reloading
|
// reloading
|
||||||
mappings.add(myModes, ["r"],
|
mappings.add(myModes, ["r", "<reload>"],
|
||||||
"Reload the current web page",
|
"Reload the current web page",
|
||||||
function () { tabs.reload(tabs.getTab(), false); });
|
function () { tabs.reload(tabs.getTab(), false); });
|
||||||
|
|
||||||
mappings.add(myModes, ["R"],
|
mappings.add(myModes, ["R", "<full-reload>"],
|
||||||
"Reload while skipping the cache",
|
"Reload while skipping the cache",
|
||||||
function () { tabs.reload(tabs.getTab(), true); });
|
function () { tabs.reload(tabs.getTab(), true); });
|
||||||
|
|
||||||
// yanking
|
// yanking
|
||||||
mappings.add(myModes, ["Y"],
|
mappings.add(myModes, ["Y", "<yank-word>"],
|
||||||
"Copy selected text or current word",
|
"Copy selected text or current word",
|
||||||
function () {
|
function () {
|
||||||
let sel = buffer.getCurrentWord();
|
let sel = buffer.getCurrentWord();
|
||||||
@@ -1843,62 +1843,62 @@ var Buffer = Module("buffer", {
|
|||||||
});
|
});
|
||||||
|
|
||||||
// zooming
|
// zooming
|
||||||
mappings.add(myModes, ["zi", "+"],
|
mappings.add(myModes, ["zi", "+", "<text-zoom-in>"],
|
||||||
"Enlarge text zoom of current web page",
|
"Enlarge text zoom of current web page",
|
||||||
function (args) { buffer.zoomIn(Math.max(args.count, 1), false); },
|
function (args) { buffer.zoomIn(Math.max(args.count, 1), false); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["zm"],
|
mappings.add(myModes, ["zm", "<text-zoom-more>"],
|
||||||
"Enlarge text zoom of current web page by a larger amount",
|
"Enlarge text zoom of current web page by a larger amount",
|
||||||
function (args) { buffer.zoomIn(Math.max(args.count, 1) * 3, false); },
|
function (args) { buffer.zoomIn(Math.max(args.count, 1) * 3, false); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["zo", "-"],
|
mappings.add(myModes, ["zo", "-", "<text-zoom-out>"],
|
||||||
"Reduce text zoom of current web page",
|
"Reduce text zoom of current web page",
|
||||||
function (args) { buffer.zoomOut(Math.max(args.count, 1), false); },
|
function (args) { buffer.zoomOut(Math.max(args.count, 1), false); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["zr"],
|
mappings.add(myModes, ["zr", "<text-zoom-reduce>"],
|
||||||
"Reduce text zoom of current web page by a larger amount",
|
"Reduce text zoom of current web page by a larger amount",
|
||||||
function (args) { buffer.zoomOut(Math.max(args.count, 1) * 3, false); },
|
function (args) { buffer.zoomOut(Math.max(args.count, 1) * 3, false); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["zz"],
|
mappings.add(myModes, ["zz", "<text-zoom>"],
|
||||||
"Set text zoom value of current web page",
|
"Set text zoom value of current web page",
|
||||||
function (args) { buffer.setZoom(args.count > 1 ? args.count : 100, false); },
|
function (args) { buffer.setZoom(args.count > 1 ? args.count : 100, false); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["ZI", "zI"],
|
mappings.add(myModes, ["ZI", "zI", "<full-zoom-in>"],
|
||||||
"Enlarge full zoom of current web page",
|
"Enlarge full zoom of current web page",
|
||||||
function (args) { buffer.zoomIn(Math.max(args.count, 1), true); },
|
function (args) { buffer.zoomIn(Math.max(args.count, 1), true); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["ZM", "zM"],
|
mappings.add(myModes, ["ZM", "zM", "<full-zoom-more>"],
|
||||||
"Enlarge full zoom of current web page by a larger amount",
|
"Enlarge full zoom of current web page by a larger amount",
|
||||||
function (args) { buffer.zoomIn(Math.max(args.count, 1) * 3, true); },
|
function (args) { buffer.zoomIn(Math.max(args.count, 1) * 3, true); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["ZO", "zO"],
|
mappings.add(myModes, ["ZO", "zO", "<full-zoom-out>"],
|
||||||
"Reduce full zoom of current web page",
|
"Reduce full zoom of current web page",
|
||||||
function (args) { buffer.zoomOut(Math.max(args.count, 1), true); },
|
function (args) { buffer.zoomOut(Math.max(args.count, 1), true); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["ZR", "zR"],
|
mappings.add(myModes, ["ZR", "zR", "<full-zoom-reduce>"],
|
||||||
"Reduce full zoom of current web page by a larger amount",
|
"Reduce full zoom of current web page by a larger amount",
|
||||||
function (args) { buffer.zoomOut(Math.max(args.count, 1) * 3, true); },
|
function (args) { buffer.zoomOut(Math.max(args.count, 1) * 3, true); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
mappings.add(myModes, ["zZ"],
|
mappings.add(myModes, ["zZ", "<full-zoom>"],
|
||||||
"Set full zoom value of current web page",
|
"Set full zoom value of current web page",
|
||||||
function (args) { buffer.setZoom(args.count > 1 ? args.count : 100, true); },
|
function (args) { buffer.setZoom(args.count > 1 ? args.count : 100, true); },
|
||||||
{ count: true });
|
{ count: true });
|
||||||
|
|
||||||
// page info
|
// page info
|
||||||
mappings.add(myModes, ["<C-g>"],
|
mappings.add(myModes, ["<C-g>", "<page-info>"],
|
||||||
"Print the current file name",
|
"Print the current file name",
|
||||||
function () { buffer.showPageInfo(false); });
|
function () { buffer.showPageInfo(false); });
|
||||||
|
|
||||||
mappings.add(myModes, ["g<C-g>"],
|
mappings.add(myModes, ["g<C-g>", "<more-page-info>"],
|
||||||
"Print file information",
|
"Print file information",
|
||||||
function () { buffer.showPageInfo(true); });
|
function () { buffer.showPageInfo(true); });
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -148,12 +148,6 @@ var CommandWidgets = Class("CommandWidgets", {
|
|||||||
return this.commandbar;
|
return this.commandbar;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let fontSize = util.computedStyle(document.documentElement).fontSize;
|
|
||||||
styles.system.add("font-size", "dactyl://content/buffer.xhtml",
|
|
||||||
"body { font-size: " + fontSize + "; } \
|
|
||||||
html|html > xul|scrollbar { visibility: collapse !important; }",
|
|
||||||
true);
|
|
||||||
},
|
},
|
||||||
addElement: function addElement(obj) {
|
addElement: function addElement(obj) {
|
||||||
const self = this;
|
const self = this;
|
||||||
@@ -301,18 +295,10 @@ var CommandMode = Class("CommandMode", {
|
|||||||
this.keepCommand = userContext.hidden_option_command_afterimage;
|
this.keepCommand = userContext.hidden_option_command_afterimage;
|
||||||
|
|
||||||
if (this.historyKey)
|
if (this.historyKey)
|
||||||
this.history = CommandLine.History(commandline.widgets.active.command.inputField, this.historyKey);
|
this.history = CommandLine.History(commandline.widgets.active.command.inputField, this.historyKey, this);
|
||||||
|
|
||||||
if (this.complete)
|
if (this.complete)
|
||||||
this.completions = CommandLine.Completions(commandline.widgets.active.command.inputField);
|
this.completions = CommandLine.Completions(commandline.widgets.active.command.inputField, this);
|
||||||
|
|
||||||
this.autocompleteTimer = Timer(200, 500, function autocompleteTell(tabPressed) {
|
|
||||||
if (!events.feedingKeys && this.completions && options["autocomplete"].length) {
|
|
||||||
this.completions.complete(true, false);
|
|
||||||
if (this.completions)
|
|
||||||
this.completions.itemList.visible = true;
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
},
|
},
|
||||||
|
|
||||||
open: function (command) {
|
open: function (command) {
|
||||||
@@ -335,17 +321,14 @@ var CommandMode = Class("CommandMode", {
|
|||||||
commandline.commandSession = this;
|
commandline.commandSession = this;
|
||||||
if (this.command || stack.pop && commandline.command) {
|
if (this.command || stack.pop && commandline.command) {
|
||||||
this.onChange(commandline.command);
|
this.onChange(commandline.command);
|
||||||
this.autocompleteTimer.flush(true);
|
if (this.completions)
|
||||||
|
this.completions.autocompleteTimer.flush(true);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
leave: function (stack) {
|
leave: function (stack) {
|
||||||
this.autocompleteTimer.reset();
|
if (this.completions)
|
||||||
|
this.completions.cleanup();
|
||||||
if (this.completions) {
|
|
||||||
this.completions.previewClear();
|
|
||||||
this.completions.tabTimer.reset();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.history)
|
if (this.history)
|
||||||
this.history.save();
|
this.history.save();
|
||||||
@@ -368,9 +351,9 @@ var CommandMode = Class("CommandMode", {
|
|||||||
if (this.completions) {
|
if (this.completions) {
|
||||||
this.resetCompletions();
|
this.resetCompletions();
|
||||||
|
|
||||||
this.autocompleteTimer.tell(false);
|
this.completions.autocompleteTimer.tell(false);
|
||||||
if (!this.completions.itemList.visible)
|
if (!this.completions.itemList.visible)
|
||||||
this.autocompleteTimer.flush();
|
this.completions.autocompleteTimer.flush();
|
||||||
}
|
}
|
||||||
this.onChange(commandline.command);
|
this.onChange(commandline.command);
|
||||||
},
|
},
|
||||||
@@ -614,8 +597,10 @@ var CommandLine = Module("commandline", {
|
|||||||
if (this.widgets.message && this.widgets.message[1] === this._lastClearable)
|
if (this.widgets.message && this.widgets.message[1] === this._lastClearable)
|
||||||
this.widgets.message = null;
|
this.widgets.message = null;
|
||||||
|
|
||||||
if (modes.main != modes.COMMAND_LINE)
|
if (!this.commandSession) {
|
||||||
this.widgets.command = null;
|
this.widgets.command = null;
|
||||||
|
this.hideCompletions();
|
||||||
|
}
|
||||||
|
|
||||||
if (modes.main == modes.OUTPUT_MULTILINE && !mow.isScrollable(1))
|
if (modes.main == modes.OUTPUT_MULTILINE && !mow.isScrollable(1))
|
||||||
modes.pop();
|
modes.pop();
|
||||||
@@ -866,10 +851,11 @@ var CommandLine = Module("commandline", {
|
|||||||
* @param {string} mode The mode for which we need history.
|
* @param {string} mode The mode for which we need history.
|
||||||
*/
|
*/
|
||||||
History: Class("History", {
|
History: Class("History", {
|
||||||
init: function init(inputField, mode) {
|
init: function init(inputField, mode, session) {
|
||||||
this.mode = mode;
|
this.mode = mode;
|
||||||
this.input = inputField;
|
this.input = inputField;
|
||||||
this.reset();
|
this.reset();
|
||||||
|
this.session = session;
|
||||||
},
|
},
|
||||||
get store() commandline._store.get(this.mode, []),
|
get store() commandline._store.get(this.mode, []),
|
||||||
set store(ary) { commandline._store.set(this.mode, ary); },
|
set store(ary) { commandline._store.set(this.mode, ary); },
|
||||||
@@ -915,8 +901,9 @@ var CommandLine = Module("commandline", {
|
|||||||
*/
|
*/
|
||||||
replace: function replace(val) {
|
replace: function replace(val) {
|
||||||
delete this.input.dactylKeyPress;
|
delete this.input.dactylKeyPress;
|
||||||
|
if (this.completions)
|
||||||
|
this.completions.previewClear();
|
||||||
this.input.value = val;
|
this.input.value = val;
|
||||||
commandline.commandSession.onChange(val, "history");
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -928,8 +915,8 @@ var CommandLine = Module("commandline", {
|
|||||||
*/
|
*/
|
||||||
select: function select(backward, matchCurrent) {
|
select: function select(backward, matchCurrent) {
|
||||||
// always reset the tab completion if we use up/down keys
|
// always reset the tab completion if we use up/down keys
|
||||||
if (commandline._completions)
|
if (this.session.completions)
|
||||||
commandline._completions.reset();
|
this.session.completions.reset();
|
||||||
|
|
||||||
let diff = backward ? -1 : 1;
|
let diff = backward ? -1 : 1;
|
||||||
|
|
||||||
@@ -976,22 +963,36 @@ var CommandLine = Module("commandline", {
|
|||||||
* @param {Object} input
|
* @param {Object} input
|
||||||
*/
|
*/
|
||||||
Completions: Class("Completions", {
|
Completions: Class("Completions", {
|
||||||
init: function init(input) {
|
init: function init(input, session) {
|
||||||
this.context = CompletionContext(input.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
|
this.context = CompletionContext(input.QueryInterface(Ci.nsIDOMNSEditableElement).editor);
|
||||||
this.context.onUpdate = this.closure._reset;
|
this.context.onUpdate = this.closure._reset;
|
||||||
this.editor = input.editor;
|
this.editor = input.editor;
|
||||||
this.input = input;
|
this.input = input;
|
||||||
|
this.session = session;
|
||||||
this.selected = null;
|
this.selected = null;
|
||||||
this.wildmode = options.get("wildmode");
|
this.wildmode = options.get("wildmode");
|
||||||
this.wildtypes = this.wildmode.value;
|
this.wildtypes = this.wildmode.value;
|
||||||
this.itemList = commandline.completionList;
|
this.itemList = commandline.completionList;
|
||||||
this.itemList.setItems(this.context);
|
this.itemList.setItems(this.context);
|
||||||
|
|
||||||
|
this.autocompleteTimer = Timer(200, 500, function autocompleteTell(tabPressed) {
|
||||||
|
if (!events.feedingKeys && options["autocomplete"].length) {
|
||||||
|
this.complete(true, false);
|
||||||
|
this.itemList.visible = true;
|
||||||
|
}
|
||||||
|
}, this);
|
||||||
this.tabTimer = Timer(0, 0, function tabTell(event) {
|
this.tabTimer = Timer(0, 0, function tabTell(event) {
|
||||||
this.tab(event.shiftKey, event.altKey && options["altwildmode"]);
|
this.tab(event.shiftKey, event.altKey && options["altwildmode"]);
|
||||||
}, this);
|
}, this);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
cleanup: function () {
|
||||||
|
this.previewClear();
|
||||||
|
this.tabTimer.reset();
|
||||||
|
this.autocompleteTimer.reset();
|
||||||
|
this.itemList.visible = false;
|
||||||
|
},
|
||||||
|
|
||||||
UP: {},
|
UP: {},
|
||||||
DOWN: {},
|
DOWN: {},
|
||||||
PAGE_UP: {},
|
PAGE_UP: {},
|
||||||
@@ -1038,7 +1039,7 @@ var CommandLine = Module("commandline", {
|
|||||||
complete: function complete(show, tabPressed) {
|
complete: function complete(show, tabPressed) {
|
||||||
this.context.reset();
|
this.context.reset();
|
||||||
this.context.tabPressed = tabPressed;
|
this.context.tabPressed = tabPressed;
|
||||||
commandline.commandSession.complete(this.context);
|
this.session.complete(this.context);
|
||||||
this.context.updateAsync = true;
|
this.context.updateAsync = true;
|
||||||
this.reset(show, tabPressed);
|
this.reset(show, tabPressed);
|
||||||
this.wildIndex = 0;
|
this.wildIndex = 0;
|
||||||
@@ -1204,7 +1205,7 @@ var CommandLine = Module("commandline", {
|
|||||||
tabs: [],
|
tabs: [],
|
||||||
|
|
||||||
tab: function tab(reverse, wildmode) {
|
tab: function tab(reverse, wildmode) {
|
||||||
commandline.commandSession.autocompleteTimer.flush();
|
this.autocompleteTimer.flush();
|
||||||
|
|
||||||
if (this._caret != this.caret)
|
if (this._caret != this.caret)
|
||||||
this.reset();
|
this.reset();
|
||||||
|
|||||||
@@ -110,10 +110,10 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
get menuItems() Dactyl.getMenuItems(),
|
get menuItems() Dactyl.getMenuItems(),
|
||||||
|
|
||||||
// Global constants
|
// Global constants
|
||||||
CURRENT_TAB: [],
|
CURRENT_TAB: "here",
|
||||||
NEW_TAB: [],
|
NEW_TAB: "tab",
|
||||||
NEW_BACKGROUND_TAB: [],
|
NEW_BACKGROUND_TAB: "background-tab",
|
||||||
NEW_WINDOW: [],
|
NEW_WINDOW: "window",
|
||||||
|
|
||||||
forceNewTab: false,
|
forceNewTab: false,
|
||||||
forceNewWindow: false,
|
forceNewWindow: false,
|
||||||
@@ -173,7 +173,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
|
|
||||||
let filters = args.map(function (arg) RegExp("\\b" + util.regexp.escape(arg) + "\\b", "i"));
|
let filters = args.map(function (arg) RegExp("\\b" + util.regexp.escape(arg) + "\\b", "i"));
|
||||||
if (filters.length)
|
if (filters.length)
|
||||||
results = results.filter(function (item) filters.every(function (re) re.test(item.name + item.description)));
|
results = results.filter(function (item) filters.every(function (re) re.test(item.name + " " + item.description)));
|
||||||
|
|
||||||
commandline.commandOutput(
|
commandline.commandOutput(
|
||||||
template.usage(results, params.format));
|
template.usage(results, params.format));
|
||||||
@@ -380,15 +380,16 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
},
|
},
|
||||||
|
|
||||||
userEval: function (str, context, fileName, lineNumber) {
|
userEval: function (str, context, fileName, lineNumber) {
|
||||||
|
let ctxt;
|
||||||
if (jsmodules.__proto__ != window)
|
if (jsmodules.__proto__ != window)
|
||||||
str = "with (window) { with (modules) { (this.eval || eval)(" + str.quote() + ") } }";
|
str = "with (window) { with (modules) { (this.eval || eval)(" + str.quote() + ") } }";
|
||||||
|
|
||||||
if (fileName == null)
|
if (fileName == null)
|
||||||
if (io.sourcing && io.sourcing.file[0] !== "[")
|
if (io.sourcing && io.sourcing.file[0] !== "[")
|
||||||
({ file: fileName, line: lineNumber }) = io.sourcing;
|
({ file: fileName, line: lineNumber, context: ctxt }) = io.sourcing;
|
||||||
else try {
|
else try {
|
||||||
if (!context)
|
if (!context)
|
||||||
context = userContext;
|
context = userContext || ctxt;
|
||||||
|
|
||||||
context[EVAL_ERROR] = null;
|
context[EVAL_ERROR] = null;
|
||||||
context[EVAL_STRING] = str;
|
context[EVAL_STRING] = str;
|
||||||
@@ -411,7 +412,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!context)
|
if (!context)
|
||||||
context = _userContext;
|
context = _userContext || ctxt;
|
||||||
return Cu.evalInSandbox(str, context, "1.8", fileName, lineNumber);
|
return Cu.evalInSandbox(str, context, "1.8", fileName, lineNumber);
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -560,10 +561,15 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
initDocument: function initDocument(doc) {
|
initDocument: function initDocument(doc) {
|
||||||
|
try {
|
||||||
if (doc.location.protocol === "dactyl:") {
|
if (doc.location.protocol === "dactyl:") {
|
||||||
dactyl.initHelp();
|
dactyl.initHelp();
|
||||||
config.styleHelp();
|
config.styleHelp();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
util.reportError(e);
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -662,9 +668,9 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
|
|
||||||
let re = util.regexp(<![CDATA[
|
let re = util.regexp(<![CDATA[
|
||||||
^ (?P<space> \s*)
|
^ (?P<space> \s*)
|
||||||
(?P<char> [-*+]) \x20
|
(?P<char> [-*+]) \ //
|
||||||
(?P<content> .*\n
|
(?P<content> .*\n
|
||||||
(?: \1\x20\x20.*\n | \s*\n)* )
|
(?: \1\ \ .*\n | \s*\n)* )
|
||||||
|
|
|
|
||||||
(?P<par>
|
(?P<par>
|
||||||
(?: ^ [^\S\n]*
|
(?: ^ [^\S\n]*
|
||||||
@@ -676,30 +682,55 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
(?: ^ [^\S\n]* \n) +
|
(?: ^ [^\S\n]* \n) +
|
||||||
]]>, "gmy");
|
]]>, "gmy");
|
||||||
|
|
||||||
|
let betas = util.regexp(/\[(b\d)\]/, "g");
|
||||||
|
|
||||||
|
let beta = array(betas.iterate(NEWS))
|
||||||
|
.map(function (m) m[1]).uniq().slice(-1)[0];
|
||||||
|
|
||||||
default xml namespace = NS;
|
default xml namespace = NS;
|
||||||
function rec(text, level) {
|
function rec(text, level, li) {
|
||||||
let res = <></>;
|
let res = <></>;
|
||||||
let list, space;
|
let list, space, i = 0;
|
||||||
|
|
||||||
for (let match in re.iterate(text)) {
|
for (let match in re.iterate(text)) {
|
||||||
if (match.char) {
|
if (match.char) {
|
||||||
if (!list)
|
if (!list)
|
||||||
res += list = <ul/>;
|
res += list = <ul/>;
|
||||||
list.* += <li>{rec(match.content.replace(RegExp("^" + match.space, "gm"), ""), level + 1)}</li>;
|
let li = <li/>;
|
||||||
|
li.* += rec(match.content.replace(RegExp("^" + match.space, "gm"), ""), level + 1, li)
|
||||||
|
list.* += li;
|
||||||
}
|
}
|
||||||
else if (match.par) {
|
else if (match.par) {
|
||||||
|
let [, par, tags] = /([^]*?)\s*((?:\[[^\]]+\])*)\n*$/.exec(match.par);
|
||||||
|
let t = tags;
|
||||||
|
tags = array(betas.iterate(tags)).map(function (m) m[1]);
|
||||||
|
|
||||||
|
let group = tags.length && !tags.some(function (t) t == beta) ? "HelpNewsOld" : "";
|
||||||
|
if (i === 0 && li) {
|
||||||
|
li.@highlight = group;
|
||||||
|
group = "";
|
||||||
|
}
|
||||||
|
|
||||||
list = null;
|
list = null;
|
||||||
if (level == 0 && /^.*:\n$/.test(match.par))
|
if (level == 0 && /^.*:\n$/.test())
|
||||||
res += <h2>{template.linkifyHelp(match.par.slice(0, -1), true)}</h2>;
|
var elem = <h2>{template.linkifyHelp(par.slice(0, -1), true)}</h2>;
|
||||||
else {
|
else {
|
||||||
let [, a, b] = /^(IMPORTANT:?)?([^]*)/.exec(match.par);
|
let [, a, b] = /^(IMPORTANT:?)?([^]*)/.exec(par);
|
||||||
res += <p>{
|
res += <p highlight={group + " HelpNews"}>{
|
||||||
|
!tags.length ? "" :
|
||||||
|
<hl key="HelpNewsTag">{tags.join(" ")}</hl>
|
||||||
|
}{
|
||||||
a ? <hl key="HelpWarning">{a}</hl> : ""
|
a ? <hl key="HelpWarning">{a}</hl> : ""
|
||||||
}{
|
}{
|
||||||
template.linkifyHelp(b, true)
|
template.linkifyHelp(b, true)
|
||||||
}</p>;
|
}</p>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
for each (let attr in res..@highlight) {
|
||||||
|
attr.parent().@NS::highlight = attr;
|
||||||
|
delete attr.parent().@highlight;
|
||||||
}
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
@@ -710,7 +741,7 @@ var Dactyl = Module("dactyl", XPCOM(Ci.nsISupportsWeakReference, ModuleBase), {
|
|||||||
'<?xml-stylesheet type="text/xsl" href="dactyl://content/help.xsl"?>\n' +
|
'<?xml-stylesheet type="text/xsl" href="dactyl://content/help.xsl"?>\n' +
|
||||||
'<!DOCTYPE document SYSTEM "resource://dactyl-content/dactyl.dtd">\n' +
|
'<!DOCTYPE document SYSTEM "resource://dactyl-content/dactyl.dtd">\n' +
|
||||||
unescape(encodeURI( // UTF-8 handling hack.
|
unescape(encodeURI( // UTF-8 handling hack.
|
||||||
<document xmlns={NS}
|
<document xmlns={NS} xmlns:dactyl={NS}
|
||||||
name="versions" title={config.appName + " Versions"}>
|
name="versions" title={config.appName + " Versions"}>
|
||||||
<h1 tag="versions news">{config.appName} Versions</h1>
|
<h1 tag="versions news">{config.appName} Versions</h1>
|
||||||
<toc start="2"/>
|
<toc start="2"/>
|
||||||
|
|||||||
@@ -11,12 +11,14 @@
|
|||||||
var ProcessorStack = Class("ProcessorStack", {
|
var ProcessorStack = Class("ProcessorStack", {
|
||||||
init: function (mode, hives, keyModes) {
|
init: function (mode, hives, keyModes) {
|
||||||
this.main = mode.main;
|
this.main = mode.main;
|
||||||
|
this._actions = [];
|
||||||
this.actions = [];
|
this.actions = [];
|
||||||
this.buffer = "";
|
this.buffer = "";
|
||||||
this.events = [];
|
this.events = [];
|
||||||
|
|
||||||
this.processors = keyModes.map(function (m) hives.map(function (h) KeyProcessor(m, h)))
|
this.processors = keyModes.map(function (m) hives.map(function (h) KeyProcessor(m, h)))
|
||||||
.flatten().array;
|
.flatten().array;
|
||||||
|
this.ownsBuffer = !this.processors.some(function (p) p.main.ownsBuffer);
|
||||||
|
|
||||||
for (let [i, input] in Iterator(this.processors)) {
|
for (let [i, input] in Iterator(this.processors)) {
|
||||||
let params = input.main.params;
|
let params = input.main.params;
|
||||||
@@ -31,9 +33,70 @@ var ProcessorStack = Class("ProcessorStack", {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
notify: function () {
|
||||||
|
this.execute(Events.KILL, true);
|
||||||
|
},
|
||||||
|
|
||||||
|
execute: function execute(result, force) {
|
||||||
|
|
||||||
|
if (force && this.actions.length)
|
||||||
|
this.processors.length = 0;
|
||||||
|
|
||||||
|
if (this.ownsBuffer)
|
||||||
|
statusline.updateInputBuffer(this.processors.length ? this.buffer : "");
|
||||||
|
|
||||||
|
if (this.processors.length) {
|
||||||
|
result = Events.KILL;
|
||||||
|
if (this.actions.length && options["timeout"])
|
||||||
|
this.timer = services.Timer(this, options["timeoutlen"], services.Timer.TYPE_ONE_SHOT);
|
||||||
|
}
|
||||||
|
else if (this.actions.length) {
|
||||||
|
if (this._actions.length == 0) {
|
||||||
|
dactyl.beep();
|
||||||
|
events.feedingKeys = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var res = this.actions[0]; callable(res);)
|
||||||
|
res = res();
|
||||||
|
result = res === Events.PASS ? Events.PASS : Events.KILL;
|
||||||
|
}
|
||||||
|
else if (result !== Events.KILL && !this.actions.length &&
|
||||||
|
this.processors.some(function (p) !p.main.passUnknown)) {
|
||||||
|
result = Events.KILL;
|
||||||
|
dactyl.beep();
|
||||||
|
events.feedingKeys = false;
|
||||||
|
}
|
||||||
|
else if (result === undefined)
|
||||||
|
result = Events.PASS;
|
||||||
|
|
||||||
|
if (result !== Events.PASS)
|
||||||
|
Events.kill(this.events[this.events.length - 1]);
|
||||||
|
|
||||||
|
if (result === Events.PASS || result === Events.ABORT)
|
||||||
|
this.events.filter(function (e) e.getPreventDefault())
|
||||||
|
.forEach(function (event, i) {
|
||||||
|
let elem = event.originalTarget;
|
||||||
|
if (event.originalTarget) {
|
||||||
|
let doc = elem.ownerDocument || elem.document || elem;
|
||||||
|
let evt = events.create(doc, event.type, event);
|
||||||
|
events.dispatch(elem, evt, { skipmap: true, isMacro: true, isReplay: true });
|
||||||
|
}
|
||||||
|
else if (i > 0)
|
||||||
|
events.events.keypress.call(events, event);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (force && this.processors.length === 0)
|
||||||
|
events.processor = null;
|
||||||
|
|
||||||
|
return this.processors.length == 0;
|
||||||
|
},
|
||||||
|
|
||||||
process: function process(event) {
|
process: function process(event) {
|
||||||
function dbg() {}
|
function dbg() {}
|
||||||
|
|
||||||
|
if (this.timer)
|
||||||
|
this.timer.cancel();
|
||||||
|
|
||||||
let key = events.toString(event);
|
let key = events.toString(event);
|
||||||
this.events.push(event);
|
this.events.push(event);
|
||||||
|
|
||||||
@@ -70,9 +133,7 @@ var ProcessorStack = Class("ProcessorStack", {
|
|||||||
dbg("ACTIONS: " + actions.length + " " + this.actions.length);
|
dbg("ACTIONS: " + actions.length + " " + this.actions.length);
|
||||||
dbg("PROCESSORS:", processors);
|
dbg("PROCESSORS:", processors);
|
||||||
|
|
||||||
if (!processors.some(function (p) p.main.ownsBuffer))
|
this._actions = actions;
|
||||||
statusline.updateInputBuffer(processors.length ? this.buffer : "");
|
|
||||||
|
|
||||||
this.actions = actions.concat(this.actions);
|
this.actions = actions.concat(this.actions);
|
||||||
|
|
||||||
if (result === Events.KILL)
|
if (result === Events.KILL)
|
||||||
@@ -87,42 +148,7 @@ var ProcessorStack = Class("ProcessorStack", {
|
|||||||
|
|
||||||
this.processors = processors;
|
this.processors = processors;
|
||||||
|
|
||||||
if (processors.length)
|
return this.execute(result, options["timeout"] && options["timeoutlen"] === 0)
|
||||||
result = Events.KILL;
|
|
||||||
else if (this.actions.length) {
|
|
||||||
if (actions.length == 0)
|
|
||||||
dactyl.beep();
|
|
||||||
|
|
||||||
if (modes.replaying && !events.waitForPageLoad())
|
|
||||||
result = Events.KILL;
|
|
||||||
else {
|
|
||||||
for (var res = this.actions[0]; callable(res);)
|
|
||||||
res = res();
|
|
||||||
result = res === Events.PASS ? Events.PASS : Events.KILL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (result !== Events.KILL && !this.actions.length &&
|
|
||||||
processors.some(function (p) !p.main.passUnknown)) {
|
|
||||||
result = Events.KILL;
|
|
||||||
dactyl.beep();
|
|
||||||
}
|
|
||||||
else if (result === undefined)
|
|
||||||
result = Events.PASS;
|
|
||||||
|
|
||||||
if (result !== Events.PASS)
|
|
||||||
Events.kill(event);
|
|
||||||
|
|
||||||
if (result === Events.PASS || result === Events.ABORT)
|
|
||||||
this.events.filter(function (e) e.getPreventDefault())
|
|
||||||
.forEach(function (event, i) {
|
|
||||||
if (event.originalTarget) {
|
|
||||||
let evt = events.create(event.originalTarget.ownerDocument, event.type, event);
|
|
||||||
events.dispatch(event.originalTarget, evt, { skipmap: true, isMacro: true });
|
|
||||||
}
|
|
||||||
else if (i > 0)
|
|
||||||
events.events.keypress.call(events, event);
|
|
||||||
});
|
|
||||||
return this.processors.length == 0;
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -491,6 +517,7 @@ var Events = Module("events", {
|
|||||||
util.threadYield(1, true);
|
util.threadYield(1, true);
|
||||||
|
|
||||||
for (let [, evt_obj] in Iterator(events.fromString(keys))) {
|
for (let [, evt_obj] in Iterator(events.fromString(keys))) {
|
||||||
|
let now = Date.now();
|
||||||
for (let type in values(["keydown", "keyup", "keypress"])) {
|
for (let type in values(["keydown", "keyup", "keypress"])) {
|
||||||
let evt = update({}, evt_obj, { type: type });
|
let evt = update({}, evt_obj, { type: type });
|
||||||
|
|
||||||
@@ -511,10 +538,6 @@ var Events = Module("events", {
|
|||||||
|
|
||||||
if (!this.feedingKeys)
|
if (!this.feedingKeys)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
// Stop feeding keys if page loading failed.
|
|
||||||
if (modes.replaying && !this.waitForPageLoad())
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
@@ -594,9 +617,14 @@ var Events = Module("events", {
|
|||||||
* of x.
|
* of x.
|
||||||
*
|
*
|
||||||
* @param {string} keys Messy form.
|
* @param {string} keys Messy form.
|
||||||
|
* @param {boolean} unknownOk Whether unknown keys are passed
|
||||||
|
* through rather than being converted to <lt>keyname>.
|
||||||
|
* @default false
|
||||||
* @returns {string} Canonical form.
|
* @returns {string} Canonical form.
|
||||||
*/
|
*/
|
||||||
canonicalKeys: function (keys, unknownOk) {
|
canonicalKeys: function (keys, unknownOk) {
|
||||||
|
if (arguments.length === 1)
|
||||||
|
unknownOk = true;
|
||||||
return events.fromString(keys, unknownOk).map(events.closure.toString).join("");
|
return events.fromString(keys, unknownOk).map(events.closure.toString).join("");
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -651,11 +679,17 @@ var Events = Module("events", {
|
|||||||
* <S-@> where @ is a non-case-changeable, non-space character.
|
* <S-@> where @ is a non-case-changeable, non-space character.
|
||||||
*
|
*
|
||||||
* @param {string} keys The string to parse.
|
* @param {string} keys The string to parse.
|
||||||
|
* @param {boolean} unknownOk Whether unknown keys are passed
|
||||||
|
* through rather than being converted to <lt>keyname>.
|
||||||
|
* @default false
|
||||||
* @returns {Array[Object]}
|
* @returns {Array[Object]}
|
||||||
*/
|
*/
|
||||||
fromString: function (input, unknownOk) {
|
fromString: function (input, unknownOk) {
|
||||||
let out = [];
|
|
||||||
|
|
||||||
|
if (arguments.length === 1)
|
||||||
|
unknownOk = true;
|
||||||
|
|
||||||
|
let out = [];
|
||||||
let re = RegExp("<.*?>?>|[^<]|<(?!.*>)", "g");
|
let re = RegExp("<.*?>?>|[^<]|<(?!.*>)", "g");
|
||||||
let match;
|
let match;
|
||||||
while ((match = re.exec(input))) {
|
while ((match = re.exec(input))) {
|
||||||
@@ -862,10 +896,7 @@ var Events = Module("events", {
|
|||||||
|
|
||||||
isContentNode: function isContentNode(node) {
|
isContentNode: function isContentNode(node) {
|
||||||
let win = (node.ownerDocument || node).defaultView || node;
|
let win = (node.ownerDocument || node).defaultView || node;
|
||||||
for (; win; win = win.parent != win && win.parent)
|
return XPCNativeWrapper(win).top == content;
|
||||||
if (win == content)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -874,39 +905,24 @@ var Events = Module("events", {
|
|||||||
*
|
*
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
waitForPageLoad: function () {
|
waitForPageLoad: function (time) {
|
||||||
util.threadYield(true); // clear queue
|
util.threadYield(true); // clear queue
|
||||||
|
|
||||||
if (buffer.loaded == 1)
|
if (buffer.loaded)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
const maxWaitTime = 25;
|
|
||||||
let start = Date.now();
|
|
||||||
let end = start + (maxWaitTime * 1000); // maximum time to wait - TODO: add option
|
|
||||||
let now;
|
|
||||||
while (now = Date.now(), now < end) {
|
|
||||||
util.threadYield();
|
|
||||||
|
|
||||||
if (!events.feedingKeys)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (buffer.loaded > 0) {
|
|
||||||
util.sleep(250);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
dactyl.echo("Waiting for page to load...", commandline.DISALLOW_MULTILINE);
|
dactyl.echo("Waiting for page to load...", commandline.DISALLOW_MULTILINE);
|
||||||
}
|
|
||||||
|
const maxWaitTime = (time || 25);
|
||||||
|
let start = Date.now();
|
||||||
|
let end = start + (maxWaitTime * 1000);
|
||||||
|
|
||||||
|
util.waitFor(function () !events.feedingKeys || buffer.loaded || Date.now() > end);
|
||||||
commandline.clear();
|
commandline.clear();
|
||||||
|
|
||||||
// TODO: allow macros to be continued when page does not fully load with an option
|
|
||||||
if (!buffer.loaded)
|
if (!buffer.loaded)
|
||||||
dactyl.echoerr("Page did not load completely in " + maxWaitTime + " seconds. Macro stopped.");
|
dactyl.echoerr("Page did not load completely in " + maxWaitTime + " seconds. Macro stopped.");
|
||||||
|
|
||||||
// sometimes the input widget had focus when replaying a macro
|
|
||||||
// maybe this call should be moved somewhere else?
|
|
||||||
// dactyl.focusContent(true);
|
|
||||||
|
|
||||||
return buffer.loaded;
|
return buffer.loaded;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1000,6 +1016,7 @@ var Events = Module("events", {
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
input: function onInput(event) {
|
input: function onInput(event) {
|
||||||
|
if ("dactylKeyPress" in event.originalTarget)
|
||||||
delete event.originalTarget.dactylKeyPress;
|
delete event.originalTarget.dactylKeyPress;
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -1024,18 +1041,17 @@ var Events = Module("events", {
|
|||||||
let duringFeed = this.duringFeed || [];
|
let duringFeed = this.duringFeed || [];
|
||||||
this.duringFeed = [];
|
this.duringFeed = [];
|
||||||
try {
|
try {
|
||||||
if (this.feedingEvent && [!(k in event) || event[k] === v for ([k, v] in Iterator(this.feedingEvent))].every(util.identity)) {
|
if (this.feedingEvent)
|
||||||
for (let [k, v] in Iterator(this.feedingEvent))
|
for (let [k, v] in Iterator(this.feedingEvent))
|
||||||
if (!(k in event))
|
if (!(k in event))
|
||||||
event[k] = v;
|
event[k] = v;
|
||||||
this.feedingEvent = null;
|
this.feedingEvent = null;
|
||||||
}
|
|
||||||
|
|
||||||
let key = events.toString(event);
|
let key = events.toString(event);
|
||||||
if (!key)
|
if (!key)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
if (modes.recording && (!this._input || !mappings.user.hasMap(modes.main, this._input.buffer + key)))
|
if (modes.recording && !event.isReplay)
|
||||||
events._macroKeys.push(key);
|
events._macroKeys.push(key);
|
||||||
|
|
||||||
// feedingKeys needs to be separate from interrupted so
|
// feedingKeys needs to be separate from interrupted so
|
||||||
@@ -1043,8 +1059,6 @@ var Events = Module("events", {
|
|||||||
// interrupting whatever it's started and a real <C-c>
|
// interrupting whatever it's started and a real <C-c>
|
||||||
// interrupting our playback.
|
// interrupting our playback.
|
||||||
if (events.feedingKeys && !event.isMacro) {
|
if (events.feedingKeys && !event.isMacro) {
|
||||||
if (!event.originalTarget)
|
|
||||||
util.dumpStack();
|
|
||||||
if (key == "<C-c>") {
|
if (key == "<C-c>") {
|
||||||
events.feedingKeys = false;
|
events.feedingKeys = false;
|
||||||
if (modes.replaying) {
|
if (modes.replaying) {
|
||||||
@@ -1177,10 +1191,6 @@ var Events = Module("events", {
|
|||||||
// access to the real focus target
|
// access to the real focus target
|
||||||
// Huh? --djk
|
// Huh? --djk
|
||||||
onFocusChange: function onFocusChange(event) {
|
onFocusChange: function onFocusChange(event) {
|
||||||
// command line has its own focus change handler
|
|
||||||
if (modes.main.input)
|
|
||||||
return;
|
|
||||||
|
|
||||||
function hasHTMLDocument(win) win && win.document && win.document instanceof HTMLDocument
|
function hasHTMLDocument(win) win && win.document && win.document instanceof HTMLDocument
|
||||||
|
|
||||||
let win = window.document.commandDispatcher.focusedWindow;
|
let win = window.document.commandDispatcher.focusedWindow;
|
||||||
@@ -1202,7 +1212,7 @@ var Events = Module("events", {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (Events.isInputElement(elem)) {
|
if (Events.isInputElement(elem)) {
|
||||||
if (!(modes.main & (modes.INSERT | modes.TEXT_EDIT | modes.VISUAL)))
|
if (!modes.main.input)
|
||||||
modes.push(modes.INSERT);
|
modes.push(modes.INSERT);
|
||||||
|
|
||||||
if (hasHTMLDocument(win))
|
if (hasHTMLDocument(win))
|
||||||
@@ -1216,7 +1226,7 @@ var Events = Module("events", {
|
|||||||
if (modes.main == modes.VISUAL && elem.selectionEnd == elem.selectionStart)
|
if (modes.main == modes.VISUAL && elem.selectionEnd == elem.selectionStart)
|
||||||
modes.pop();
|
modes.pop();
|
||||||
|
|
||||||
if (!(modes.main & (modes.INSERT | modes.TEXT_EDIT | modes.VISUAL)))
|
if (!modes.main.input)
|
||||||
if (options["insertmode"])
|
if (options["insertmode"])
|
||||||
modes.push(modes.INSERT);
|
modes.push(modes.INSERT);
|
||||||
else {
|
else {
|
||||||
@@ -1323,11 +1333,11 @@ var Events = Module("events", {
|
|||||||
},
|
},
|
||||||
mappings: function () {
|
mappings: function () {
|
||||||
mappings.add(modes.all,
|
mappings.add(modes.all,
|
||||||
["<C-z>"], "Temporarily ignore all " + config.appName + " key bindings",
|
["<C-z>", "<pass-all-keys>"], "Temporarily ignore all " + config.appName + " key bindings",
|
||||||
function () { modes.push(modes.PASS_THROUGH); });
|
function () { modes.push(modes.PASS_THROUGH); });
|
||||||
|
|
||||||
mappings.add(modes.all,
|
mappings.add(modes.all,
|
||||||
["<C-v>"], "Pass through next key",
|
["<C-v>", "<pass-next-key>"], "Pass through next key",
|
||||||
function () {
|
function () {
|
||||||
if (modes.main == modes.QUOTE)
|
if (modes.main == modes.QUOTE)
|
||||||
return Events.PASS;
|
return Events.PASS;
|
||||||
@@ -1340,7 +1350,7 @@ var Events = Module("events", {
|
|||||||
|
|
||||||
// macros
|
// macros
|
||||||
mappings.add([modes.NORMAL, modes.TEXT_AREA, modes.PLAYER].filter(util.identity),
|
mappings.add([modes.NORMAL, modes.TEXT_AREA, modes.PLAYER].filter(util.identity),
|
||||||
["q"], "Record a key sequence into a macro",
|
["q", "<record-macro>"], "Record a key sequence into a macro",
|
||||||
function ({ arg }) {
|
function ({ arg }) {
|
||||||
events._macroKeys.pop();
|
events._macroKeys.pop();
|
||||||
events[modes.recording ? "finishRecording" : "startRecording"](arg);
|
events[modes.recording ? "finishRecording" : "startRecording"](arg);
|
||||||
@@ -1348,13 +1358,33 @@ var Events = Module("events", {
|
|||||||
{ get arg() !modes.recording });
|
{ get arg() !modes.recording });
|
||||||
|
|
||||||
mappings.add([modes.NORMAL, modes.TEXT_AREA, modes.PLAYER].filter(util.identity),
|
mappings.add([modes.NORMAL, modes.TEXT_AREA, modes.PLAYER].filter(util.identity),
|
||||||
["@"], "Play a macro",
|
["@", "<play-macro>"], "Play a macro",
|
||||||
function ({ arg, count }) {
|
function ({ arg, count }) {
|
||||||
count = Math.max(count, 1);
|
count = Math.max(count, 1);
|
||||||
while (count-- && events.playMacro(arg))
|
while (count-- && events.playMacro(arg))
|
||||||
;
|
;
|
||||||
},
|
},
|
||||||
{ arg: true, count: true });
|
{ arg: true, count: true });
|
||||||
|
|
||||||
|
mappings.add([modes.COMMAND],
|
||||||
|
["<A-m>s", "<sleep>"], "Sleep for {count} milliseconds before continuing macro playback",
|
||||||
|
function ({ command, count }) {
|
||||||
|
let now = Date.now();
|
||||||
|
dactyl.assert(count, "Count required for " + command);
|
||||||
|
if (events.feedingKeys)
|
||||||
|
util.sleep(count);
|
||||||
|
},
|
||||||
|
{ count: true });
|
||||||
|
|
||||||
|
mappings.add([modes.COMMAND],
|
||||||
|
["<A-m>l", "<wait-for-page-load>"], "Wait for the current page to finish loading before continuing macro playback",
|
||||||
|
function ({ count }) {
|
||||||
|
if (events.feedingKeys && !events.waitForPageLoad(count)) {
|
||||||
|
util.interrupted = true;
|
||||||
|
throw Error("Interrupted");
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{ count: true });
|
||||||
},
|
},
|
||||||
options: function () {
|
options: function () {
|
||||||
options.add(["passkeys", "pk"],
|
options.add(["passkeys", "pk"],
|
||||||
@@ -1375,9 +1405,18 @@ var Events = Module("events", {
|
|||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
options.add(["strictfocus", "sf"],
|
options.add(["strictfocus", "sf"],
|
||||||
"Prevent scripts from focusing input elements without user intervention",
|
"Prevent scripts from focusing input elements without user intervention",
|
||||||
"boolean", true);
|
"boolean", true);
|
||||||
|
|
||||||
|
options.add(["timeout", "tmo"],
|
||||||
|
"Whether to execute a shorter key command after a timeout when a longer command exists",
|
||||||
|
"boolean", true);
|
||||||
|
|
||||||
|
options.add(["timeoutlen", "tmol"],
|
||||||
|
"Maximum time (milliseconds) to wait for a longer key command when a shorter one exists",
|
||||||
|
"number", 1000);
|
||||||
},
|
},
|
||||||
sanitizer: function () {
|
sanitizer: function () {
|
||||||
sanitizer.addItem("macros", {
|
sanitizer.addItem("macros", {
|
||||||
|
|||||||
@@ -113,9 +113,17 @@ var Map = Class("Map", {
|
|||||||
if (this.executing)
|
if (this.executing)
|
||||||
util.dumpStack("Attempt to execute mapping recursively: " + args.command);
|
util.dumpStack("Attempt to execute mapping recursively: " + args.command);
|
||||||
dactyl.assert(!this.executing, "Attempt to execute mapping recursively: " + args.command);
|
dactyl.assert(!this.executing, "Attempt to execute mapping recursively: " + args.command);
|
||||||
|
|
||||||
|
try {
|
||||||
this.executing = true;
|
this.executing = true;
|
||||||
let res = dactyl.trapErrors(repeat);
|
var res = repeat();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
events.feedingKeys = false;
|
||||||
|
}
|
||||||
|
finally {
|
||||||
this.executing = false;
|
this.executing = false;
|
||||||
|
}
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -507,7 +515,7 @@ var Mappings = Module("mappings", {
|
|||||||
description: "Accept a count before the requisite key press"
|
description: "Accept a count before the requisite key press"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
names: ["-description", "-d"],
|
names: ["-description", "-desc", "-d"],
|
||||||
description: "A description of this mapping",
|
description: "A description of this mapping",
|
||||||
default: "User-defined mapping",
|
default: "User-defined mapping",
|
||||||
type: CommandOption.STRING
|
type: CommandOption.STRING
|
||||||
|
|||||||
@@ -8,6 +8,12 @@
|
|||||||
var MOW = Module("mow", {
|
var MOW = Module("mow", {
|
||||||
init: function () {
|
init: function () {
|
||||||
|
|
||||||
|
let fontSize = util.computedStyle(document.documentElement).fontSize;
|
||||||
|
styles.system.add("font-size", "dactyl://content/buffer.xhtml",
|
||||||
|
"body { font-size: " + fontSize + "; } \
|
||||||
|
html|html > xul|scrollbar { visibility: collapse !important; }",
|
||||||
|
true);
|
||||||
|
|
||||||
XML.ignoreWhitespace = true;
|
XML.ignoreWhitespace = true;
|
||||||
util.overlayWindow(window, {
|
util.overlayWindow(window, {
|
||||||
objects: {
|
objects: {
|
||||||
|
|||||||
@@ -867,9 +867,11 @@ var Tabs = Module("tabs", {
|
|||||||
},
|
},
|
||||||
events: function () {
|
events: function () {
|
||||||
let tabContainer = config.tabbrowser.mTabContainer;
|
let tabContainer = config.tabbrowser.mTabContainer;
|
||||||
["TabMove", "TabOpen", "TabClose"].forEach(function (event) {
|
function callback() {
|
||||||
events.addSessionListener(tabContainer, event, this.closure.updateTabCount, false);
|
tabs.timeout(function () { this.updateTabCount(); });
|
||||||
}, this);
|
}
|
||||||
|
for (let event in values(["TabMove", "TabOpen", "TabClose"]))
|
||||||
|
events.addSessionListener(tabContainer, event, callback, false);
|
||||||
events.addSessionListener(tabContainer, "TabSelect", this.closure._onTabSelect, false);
|
events.addSessionListener(tabContainer, "TabSelect", this.closure._onTabSelect, false);
|
||||||
},
|
},
|
||||||
mappings: function () {
|
mappings: function () {
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to
|
|||||||
&dactyl.host; or to a web page, you have two options:
|
&dactyl.host; or to a web page, you have two options:
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags><![CDATA[pass-through <C-z> CTRL-Z]]></tags>
|
<tags><![CDATA[pass-through <pass-all-keys> <C-z> CTRL-Z]]></tags>
|
||||||
<spec><C-z></spec>
|
<spec><C-z></spec>
|
||||||
<description>
|
<description>
|
||||||
<p>
|
<p>
|
||||||
@@ -33,7 +33,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags><![CDATA[send-key <C-v> CTRL-V]]></tags>
|
<tags><![CDATA[send-key <pass-next-key> <C-v> CTRL-V]]></tags>
|
||||||
<spec><C-v></spec>
|
<spec><C-v></spec>
|
||||||
<description>
|
<description>
|
||||||
<p>
|
<p>
|
||||||
@@ -171,7 +171,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags><![CDATA[<MiddleMouse> p]]></tags>
|
<tags><![CDATA[<open-clipboard-url> <MiddleMouse> p]]></tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec>p</spec>
|
<spec>p</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -185,7 +185,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>P</tags>
|
<tags><tab-open-clipboard-url> P</tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec>P</spec>
|
<spec>P</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -343,7 +343,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to
|
|||||||
<h2 tag="reloading">Reloading</h2>
|
<h2 tag="reloading">Reloading</h2>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>r</tags>
|
<tags><reload> r</tags>
|
||||||
<spec>r</spec>
|
<spec>r</spec>
|
||||||
<description short="true">
|
<description short="true">
|
||||||
<p>Reload the current web page.</p>
|
<p>Reload the current web page.</p>
|
||||||
@@ -351,7 +351,7 @@ want to bypass &dactyl.appName;'s key handling and pass keys directly to
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>R</tags>
|
<tags><full-reload> R</tags>
|
||||||
<spec>R</spec>
|
<spec>R</spec>
|
||||||
<description short="true">
|
<description short="true">
|
||||||
<p>Reload the current web page without using the cache.</p>
|
<p>Reload the current web page without using the cache.</p>
|
||||||
|
|||||||
@@ -22,7 +22,7 @@
|
|||||||
<h2 tag="buffer-information">Buffer information</h2>
|
<h2 tag="buffer-information">Buffer information</h2>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags><![CDATA[<C-g>]]></tags>
|
<tags><![CDATA[<page-info> <C-g>]]></tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><C-g></spec>
|
<spec><C-g></spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -35,7 +35,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags><![CDATA[g<C-g>]]></tags>
|
<tags><![CDATA[<more-page-info> g<C-g>]]></tags>
|
||||||
<spec>g<C-g></spec>
|
<spec>g<C-g></spec>
|
||||||
<description short="true">
|
<description short="true">
|
||||||
<p>Print file information. Same as <ex>:pa<oa>geinfo</oa></ex>.</p>
|
<p>Print file information. Same as <ex>:pa<oa>geinfo</oa></ex>.</p>
|
||||||
@@ -95,7 +95,7 @@
|
|||||||
<h2 tag="motion scrolling">Motion commands</h2>
|
<h2 tag="motion scrolling">Motion commands</h2>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>^ 0</tags>
|
<tags><scroll-begin> ^ 0</tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec>0</spec>
|
<spec>0</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -107,7 +107,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>$</tags>
|
<tags><scroll-end> $</tags>
|
||||||
<spec>$</spec>
|
<spec>$</spec>
|
||||||
<description short="true">
|
<description short="true">
|
||||||
<p>Scroll to the absolute right of the document</p>
|
<p>Scroll to the absolute right of the document</p>
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>N%</tags>
|
<tags><scroll-percent> N%</tags>
|
||||||
<spec><a>count</a>%</spec>
|
<spec><a>count</a>%</spec>
|
||||||
<description short="true">
|
<description short="true">
|
||||||
<p>Scroll to <a>count</a> percent of the document.</p>
|
<p>Scroll to <a>count</a> percent of the document.</p>
|
||||||
@@ -148,7 +148,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags><![CDATA[<Left> h]]></tags>
|
<tags><![CDATA[<scroll-column-left> <Left> h]]></tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa>h</spec>
|
<spec><oa>count</oa>h</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -160,7 +160,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags><![CDATA[<C-e> <Down> j]]></tags>
|
<tags><![CDATA[<scroll-line-down> <C-e> <Down> j]]></tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa>j</spec>
|
<spec><oa>count</oa>j</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -172,7 +172,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags><![CDATA[<C-y> <Up> k]]></tags>
|
<tags><![CDATA[<scroll-line-up> <C-y> <Up> k]]></tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa>k</spec>
|
<spec><oa>count</oa>k</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -184,7 +184,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags><![CDATA[<Right> l]]></tags>
|
<tags><![CDATA[<scroll-column-right> <Right> l]]></tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa>l</spec>
|
<spec><oa>count</oa>l</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -196,7 +196,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags><![CDATA[<C-d>]]></tags>
|
<tags><![CDATA[<scroll-down> <C-d>]]></tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa><C-d></spec>
|
<spec><oa>count</oa><C-d></spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -209,7 +209,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags><![CDATA[<C-u>]]></tags>
|
<tags><![CDATA[<scroll-up> <C-u>]]></tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa><C-u></spec>
|
<spec><oa>count</oa><C-u></spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -222,7 +222,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags><![CDATA[<S-Space> <PageUp> <C-b>]]></tags>
|
<tags><![CDATA[<scroll-page-up> <S-Space> <PageUp> <C-b>]]></tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa><C-b></spec>
|
<spec><oa>count</oa><C-b></spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -234,7 +234,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags><![CDATA[<Space> <PageDown> <C-f>]]></tags>
|
<tags><![CDATA[<scroll-page-down> <Space> <PageDown> <C-f>]]></tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa><C-f></spec>
|
<spec><oa>count</oa><C-f></spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -264,7 +264,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>gi</tags>
|
<tags><focus-input> gi</tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa>gi</spec>
|
<spec><oa>count</oa>gi</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -277,7 +277,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>]f</tags>
|
<tags><next-frame> ]f</tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa>]f</spec>
|
<spec><oa>count</oa>]f</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -290,7 +290,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>[f</tags>
|
<tags><previous-frame> [f</tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa>[f</spec>
|
<spec><oa>count</oa>[f</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -303,7 +303,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>]]</tags>
|
<tags><next-page> ]]</tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa>]]</spec>
|
<spec><oa>count</oa>]]</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -316,7 +316,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>[[</tags>
|
<tags><previous-page> [[</tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa>[[</spec>
|
<spec><oa>count</oa>[[</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -361,7 +361,7 @@
|
|||||||
</note>
|
</note>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>+ zi</tags>
|
<tags><![CDATA[<text-zoom-in> + zi]]></tags>
|
||||||
<spec><oa>count</oa>zi</spec>
|
<spec><oa>count</oa>zi</spec>
|
||||||
<description short="true">
|
<description short="true">
|
||||||
<p>Enlarge text zoom of current web page. Mnemonic: zoom in.</p>
|
<p>Enlarge text zoom of current web page. Mnemonic: zoom in.</p>
|
||||||
@@ -369,7 +369,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>zm</tags>
|
<tags><![CDATA[<text-zoom-more> zm]]></tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa>zm</spec>
|
<spec><oa>count</oa>zm</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -378,7 +378,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>- zo</tags>
|
<tags><![CDATA[<text-zoom-out> - zo]]></tags>
|
||||||
<spec><oa>count</oa>zo</spec>
|
<spec><oa>count</oa>zo</spec>
|
||||||
<description short="true">
|
<description short="true">
|
||||||
<p>Reduce text zoom of current web page. Mnemonic: zoom out.</p>
|
<p>Reduce text zoom of current web page. Mnemonic: zoom out.</p>
|
||||||
@@ -386,7 +386,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>zr</tags>
|
<tags><![CDATA[<text-zoom-reduce> zr]]></tags>
|
||||||
<spec><oa>count</oa>zr</spec>
|
<spec><oa>count</oa>zr</spec>
|
||||||
<description short="true">
|
<description short="true">
|
||||||
<p>Reduce text zoom of current web page by a larger amount. Mnemonic: zoom reduce.</p>
|
<p>Reduce text zoom of current web page by a larger amount. Mnemonic: zoom reduce.</p>
|
||||||
@@ -394,7 +394,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>zz</tags>
|
<tags><![CDATA[<text-zoom> zz]]></tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa>zz</spec>
|
<spec><oa>count</oa>zz</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -407,7 +407,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>ZI zI</tags>
|
<tags><![CDATA[<full-zoom-in> ZI zI]]></tags>
|
||||||
<spec><oa>count</oa>ZI</spec>
|
<spec><oa>count</oa>ZI</spec>
|
||||||
<description short="true">
|
<description short="true">
|
||||||
<p>Enlarge full zoom of current web page. Mnemonic: zoom in.</p>
|
<p>Enlarge full zoom of current web page. Mnemonic: zoom in.</p>
|
||||||
@@ -415,7 +415,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>ZM zM</tags>
|
<tags><![CDATA[<full-zoom-more> ZM zM]]></tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa>ZM</spec>
|
<spec><oa>count</oa>ZM</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -424,7 +424,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>ZO zO</tags>
|
<tags><![CDATA[<full-zoom-out> ZO zO]]></tags>
|
||||||
<spec><oa>count</oa>ZO</spec>
|
<spec><oa>count</oa>ZO</spec>
|
||||||
<description short="true">
|
<description short="true">
|
||||||
<p>Reduce full zoom of current web page. Mnemonic: zoom out.</p>
|
<p>Reduce full zoom of current web page. Mnemonic: zoom out.</p>
|
||||||
@@ -432,7 +432,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>ZR zR</tags>
|
<tags><![CDATA[<full-zoom-reduce> ZR zR]]></tags>
|
||||||
<spec><oa>count</oa>ZR</spec>
|
<spec><oa>count</oa>ZR</spec>
|
||||||
<description short="true">
|
<description short="true">
|
||||||
<p>Reduce full zoom of current web page by a larger amount. Mnemonic: zoom reduce.</p>
|
<p>Reduce full zoom of current web page by a larger amount. Mnemonic: zoom reduce.</p>
|
||||||
@@ -440,7 +440,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>zZ</tags>
|
<tags><![CDATA[<full-zoom> zZ]]></tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa>zZ</spec>
|
<spec><oa>count</oa>zZ</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -489,7 +489,7 @@
|
|||||||
</p>
|
</p>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>y</tags>
|
<tags><yank-location> y</tags>
|
||||||
<spec>y</spec>
|
<spec>y</spec>
|
||||||
<description short="true">
|
<description short="true">
|
||||||
<p>Yank current location to the clipboard.</p>
|
<p>Yank current location to the clipboard.</p>
|
||||||
@@ -497,7 +497,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>Y</tags>
|
<tags><yank-word> Y</tags>
|
||||||
<spec>Y</spec>
|
<spec>Y</spec>
|
||||||
<description short="true">
|
<description short="true">
|
||||||
<p>Copy currently selected text to the system clipboard.</p>
|
<p>Copy currently selected text to the system clipboard.</p>
|
||||||
|
|||||||
@@ -217,6 +217,14 @@
|
|||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
<h3 tag=":map-timeout map-timeout">Mapping timeout</h3>
|
||||||
|
<p>
|
||||||
|
When &dactyl.appName; receives a key event that has a separate binding and
|
||||||
|
at the same time is part of a key chain, values of the <o>timeout</o> and
|
||||||
|
<o>timeoutlen</o> options are used to decide what to do. See the
|
||||||
|
documentation of those options for more information.
|
||||||
|
</p>
|
||||||
|
|
||||||
<h3 tag=":map-arguments">Special arguments</h3>
|
<h3 tag=":map-arguments">Special arguments</h3>
|
||||||
|
|
||||||
<tags>:map-<silent></tags>
|
<tags>:map-<silent></tags>
|
||||||
|
|||||||
@@ -1391,6 +1391,34 @@
|
|||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<tags>'tmo' 'timeout'</tags>
|
||||||
|
<spec>'timeout' 'tmo'</spec>
|
||||||
|
<type>boolean</type>
|
||||||
|
<default>true</default>
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
|
When this option is set and a key sequence interpretable both as a
|
||||||
|
complete command and as a start of a longer command is typed,
|
||||||
|
execute the shorter command after <o>timeoutlen</o> milliseconds.
|
||||||
|
</p>
|
||||||
|
</description>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<tags>'tmol' 'timeoutlen'</tags>
|
||||||
|
<spec>'timeoutlen' 'tmol'</spec>
|
||||||
|
<type>number</type>
|
||||||
|
<default>1000</default>
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
|
Maximum number of milliseconds to wait for a longer key command
|
||||||
|
when a shorter one exists. Only effective when <o>timeout</o> is
|
||||||
|
set.
|
||||||
|
</p>
|
||||||
|
</description>
|
||||||
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>'titlestring'</tags>
|
<tags>'titlestring'</tags>
|
||||||
<spec>'titlestring'</spec>
|
<spec>'titlestring'</spec>
|
||||||
|
|||||||
@@ -21,7 +21,7 @@
|
|||||||
<h2 tag="single-repeat">Single repeats</h2>
|
<h2 tag="single-repeat">Single repeats</h2>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>.</tags>
|
<tags><repeat-key> .</tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec><oa>count</oa>.</spec>
|
<spec><oa>count</oa>.</spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -45,7 +45,7 @@
|
|||||||
<h2 tag="macros complex-repeat">Macros</h2>
|
<h2 tag="macros complex-repeat">Macros</h2>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>q</tags>
|
<tags><record-macro> q</tags>
|
||||||
<strut/>
|
<strut/>
|
||||||
<spec>q<a>0-9a-zA-Z</a></spec>
|
<spec>q<a>0-9a-zA-Z</a></spec>
|
||||||
<description>
|
<description>
|
||||||
@@ -82,7 +82,7 @@
|
|||||||
</item>
|
</item>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
<tags>@</tags>
|
<tags><play-macro> @</tags>
|
||||||
<spec><oa>count</oa>@<a>a-z0-9</a></spec>
|
<spec><oa>count</oa>@<a>a-z0-9</a></spec>
|
||||||
<description>
|
<description>
|
||||||
<p>
|
<p>
|
||||||
@@ -100,6 +100,39 @@
|
|||||||
</description>
|
</description>
|
||||||
</item>
|
</item>
|
||||||
|
|
||||||
|
<h2 tag="macro-utilities">Macro utilities</h2>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
The following key bindings facilitate the recording of efficient
|
||||||
|
macros. They have no effect when typed normally, but are
|
||||||
|
recorded and take effect during macro playback.
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<tags><sleep> <A-m>s</tags>
|
||||||
|
<strut/>
|
||||||
|
<spec><a>count</a><m>s</spec>
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
|
Sleep for <a>count</a> milliseconds before resuming playback.
|
||||||
|
</p>
|
||||||
|
</description>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
<item>
|
||||||
|
<tags><![CDATA[<wait-for-page-load> <A-m>l]]></tags>
|
||||||
|
<strut/>
|
||||||
|
<spec><oa>count</oa><![CDATA[<A-m>l]]></spec>
|
||||||
|
<description>
|
||||||
|
<p>
|
||||||
|
Wait for the current page to finish loading before resuming
|
||||||
|
playback. If <oa>count</oa> is given, wait no more than
|
||||||
|
<oa>count</oa> seconds. Otherwise wait no more than 25 seconds.
|
||||||
|
</p>
|
||||||
|
</description>
|
||||||
|
</item>
|
||||||
|
|
||||||
|
|
||||||
<h2 tag="using-scripts">Using scripts</h2>
|
<h2 tag="using-scripts">Using scripts</h2>
|
||||||
|
|
||||||
<item>
|
<item>
|
||||||
|
|||||||
@@ -1219,6 +1219,15 @@ update(iter, {
|
|||||||
func.call(self, val);
|
func.call(self, val);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
indexOf: function indexOf(iter, elem) {
|
||||||
|
let i = 0;
|
||||||
|
for (let item in iter) {
|
||||||
|
if (item == elem)
|
||||||
|
return i;
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the array that results from applying *func* to each property of
|
* Returns the array that results from applying *func* to each property of
|
||||||
* *obj*.
|
* *obj*.
|
||||||
|
|||||||
@@ -613,6 +613,9 @@ var ConfigBase = Class("ConfigBase", {
|
|||||||
HelpString[delim]::before content: attr(delim);
|
HelpString[delim]::before content: attr(delim);
|
||||||
HelpString[delim]::after content: attr(delim);
|
HelpString[delim]::after content: attr(delim);
|
||||||
|
|
||||||
|
HelpNews position: relative;
|
||||||
|
HelpNewsOld opacity: .7;
|
||||||
|
HelpNewsTag position: absolute; left: 100%; padding-left: 1em; color: #527BBD; opacity: .6; white-space: pre;
|
||||||
|
|
||||||
HelpHead;html|h1,html|h2,html|h3,html|h4;dactyl://help/* {
|
HelpHead;html|h1,html|h2,html|h3,html|h4;dactyl://help/* {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
|
|||||||
@@ -174,13 +174,13 @@ var RangeFinder = Module("rangefinder", {
|
|||||||
description: "Forward Find mode, active when typing search input",
|
description: "Forward Find mode, active when typing search input",
|
||||||
bases: [modes.FIND],
|
bases: [modes.FIND],
|
||||||
input: true
|
input: true
|
||||||
}, { history: "search" });
|
});
|
||||||
modes.addMode("FIND_BACKWARD", {
|
modes.addMode("FIND_BACKWARD", {
|
||||||
extended: true,
|
extended: true,
|
||||||
description: "Backward Find mode, active when typing search input",
|
description: "Backward Find mode, active when typing search input",
|
||||||
bases: [modes.FIND],
|
bases: [modes.FIND],
|
||||||
input: true
|
input: true
|
||||||
}, { history: "search" });
|
});
|
||||||
},
|
},
|
||||||
commands: function (dactyl, modules, window) {
|
commands: function (dactyl, modules, window) {
|
||||||
const { commands, rangefinder } = modules;
|
const { commands, rangefinder } = modules;
|
||||||
|
|||||||
@@ -185,8 +185,10 @@ var IO = Module("io", {
|
|||||||
else if (/\.css$/.test(filename))
|
else if (/\.css$/.test(filename))
|
||||||
styles.registerSheet(uri.spec, false, true);
|
styles.registerSheet(uri.spec, false, true);
|
||||||
else {
|
else {
|
||||||
|
if (!(file.path in plugins))
|
||||||
|
plugins[file.path] = modules.newContext(modules.userContext);
|
||||||
modules.commands.execute(file.read(), null, silent || "loud", null,
|
modules.commands.execute(file.read(), null, silent || "loud", null,
|
||||||
{ file: file.path, line: 1 });
|
{ file: file.path, line: 1, context: plugins[file.path] });
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this._scriptNames.indexOf(file.path) == -1)
|
if (this._scriptNames.indexOf(file.path) == -1)
|
||||||
|
|||||||
@@ -478,7 +478,7 @@ var JavaScript = Module("javascript", {
|
|||||||
if (obj.length) {
|
if (obj.length) {
|
||||||
let func = obj[0][0][funcName];
|
let func = obj[0][0][funcName];
|
||||||
if (callable(func)) {
|
if (callable(func)) {
|
||||||
let [, prefix, args] = /^(function .*?)\((.*)?\)/.exec(Function.prototype.toString.call(func));
|
let [, prefix, args] = /^(function .*?)\((.*?)\)/.exec(Function.prototype.toString.call(func));
|
||||||
let n = this._get(i).comma.length;
|
let n = this._get(i).comma.length;
|
||||||
args = template.map(Iterator(args.split(", ")),
|
args = template.map(Iterator(args.split(", ")),
|
||||||
function ([i, arg]) <span highlight={i == n ? "Filter" : ""}>{arg}</span>,
|
function ([i, arg]) <span highlight={i == n ? "Filter" : ""}>{arg}</span>,
|
||||||
|
|||||||
@@ -1200,7 +1200,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
|||||||
}
|
}
|
||||||
if (post)
|
if (post)
|
||||||
return [url, elems.join('&'), charset, elems];
|
return [url, elems.join('&'), charset, elems];
|
||||||
return [url + "?" + elems.join('&'), null, charset];
|
return [url + "?" + elems.join('&'), null, charset, elems];
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -1270,8 +1270,7 @@ var Util = Module("Util", XPCOM([Ci.nsIObserver, Ci.nsISupportsWeakReference]),
|
|||||||
if (tokens)
|
if (tokens)
|
||||||
expr = String.replace(expr, /(\(?P)?<(\w+)>/g, function (m, n1, n2) !n1 && set.has(tokens, n2) ? tokens[n2].dactylSource || tokens[n2].source || tokens[n2] : m);
|
expr = String.replace(expr, /(\(?P)?<(\w+)>/g, function (m, n1, n2) !n1 && set.has(tokens, n2) ? tokens[n2].dactylSource || tokens[n2].source || tokens[n2] : m);
|
||||||
|
|
||||||
expr = String.replace(expr, /\/\/[^\n]*|\/\*[^]*?\*\//gm, "")
|
expr = String.replace(expr, /(\\.)|\/\/[^\n]*|\/\*[^]*?\*\/|\s+/gm, function (m, m1) m1 || "");
|
||||||
.replace(/\s+/g, "");
|
|
||||||
|
|
||||||
if (/\(\?P</.test(expr)) {
|
if (/\(\?P</.test(expr)) {
|
||||||
var source = expr;
|
var source = expr;
|
||||||
|
|||||||
@@ -1 +0,0 @@
|
|||||||
- shared-modules should be synced with http://hg.mozilla.org/qa/mozmill-tests/
|
|
||||||
@@ -1,140 +0,0 @@
|
|||||||
var elementslib = {}; Components.utils.import("resource://mozmill/modules/elementslib.js", elementslib);
|
|
||||||
var jumlib = {}; Components.utils.import("resource://mozmill/modules/jum.js", jumlib);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A controller for simulating Dactyl related user actions and for making
|
|
||||||
* assertions about the expected outcomes of such actions.
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller The browser's MozMill controller.
|
|
||||||
*/
|
|
||||||
function Controller(controller) {
|
|
||||||
this.controller = controller;
|
|
||||||
this._dactyl = controller.window.dactyl.modules;
|
|
||||||
}
|
|
||||||
|
|
||||||
Controller.prototype = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that the output message line text content matches *text*.
|
|
||||||
*
|
|
||||||
* @param {string|RegExp} text The expected text of the expected message line.
|
|
||||||
* @param {string} message The message to display upon assertion failure.
|
|
||||||
*/
|
|
||||||
assertMessageLine: function (text, message) {
|
|
||||||
let value = this.readMessageLine();
|
|
||||||
jumlib.assertTrue(typeof text == "string" ? text == value : text.test(value), message);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that the output message window text content matches *text*.
|
|
||||||
*
|
|
||||||
* @param {string|RegExp} text The expected text of the message window.
|
|
||||||
* @param {string} message The message to display upon assertion failure.
|
|
||||||
*/
|
|
||||||
assertMessageWindow: function (text, message) {
|
|
||||||
let value = this.readMessageWindow();
|
|
||||||
jumlib.assertTrue(typeof text == "string" ? text == value : text.test(value), message);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that an error message has been echoed to the message line or
|
|
||||||
* appended to the message window with the given *text*.
|
|
||||||
*
|
|
||||||
* @param {string|RegExp} text The expected text of the error message.
|
|
||||||
* @param {string} message The message to display upon assertion failure.
|
|
||||||
*/
|
|
||||||
// TODO: test against the tail of the MOW too.
|
|
||||||
assertErrorMessage: function (text, message) {
|
|
||||||
this.controller.sleep(0); // XXX
|
|
||||||
let messageBox = new elementslib.ID(this.controller.window.document, "dactyl-message").getNode();
|
|
||||||
jumlib.assertTrue(messageBox.value == text && /\bErrorMsg\b/.test(messageBox.getAttribute("highlight")), message);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Asserts that the current window selection matches *text*.
|
|
||||||
*
|
|
||||||
* @param {string|RegExp} text The expected text of the current selection.
|
|
||||||
* @param {string} message The message to display upon assertion failure.
|
|
||||||
*/
|
|
||||||
assertSelection: function (text, message) {
|
|
||||||
let selection = String(this.controller.window.content.getSelection());
|
|
||||||
jumlib.assertTrue(typeof text == "string" ? text == selection : text.test(selection), message);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs a Vi command.
|
|
||||||
*
|
|
||||||
* @param {string|Array} keys Either a string of simple keys suitable for
|
|
||||||
* {@link MozMillController#type} or an array of keysym - modifier
|
|
||||||
* pairs suitable for {@link MozMillController#keypress}.
|
|
||||||
*/
|
|
||||||
runViCommand: function (keys) {
|
|
||||||
if (typeof keys == "string")
|
|
||||||
keys = [[k] for each (k in keys)];
|
|
||||||
let self = this;
|
|
||||||
keys.forEach(function ([key, modifiers]) { self.controller.keypress(null, key, modifiers || {}); });
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Runs an Ex command.
|
|
||||||
*
|
|
||||||
* @param {string} cmd The Ex command string as entered on the command
|
|
||||||
* line.
|
|
||||||
*/
|
|
||||||
runExCommand: function (cmd) {
|
|
||||||
this.controller.keypress(null, ":", {});
|
|
||||||
this.controller.type(null, cmd);
|
|
||||||
this.controller.keypress(null, "VK_RETURN", {});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the text content of the output message line.
|
|
||||||
*
|
|
||||||
* @returns {string} The message line text content.
|
|
||||||
*/
|
|
||||||
readMessageLine: function () {
|
|
||||||
this.controller.sleep(0); // XXX
|
|
||||||
return new elementslib.ID(this.controller.window.document, "dactyl-message").getNode().value;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the text content of the output message window.
|
|
||||||
*
|
|
||||||
* @returns {string} The message window text content.
|
|
||||||
*/
|
|
||||||
readMessageWindow: function () {
|
|
||||||
let messageWindow = new elementslib.ID(this.controller.window.document, "dactyl-multiline-output").getNode();
|
|
||||||
try {
|
|
||||||
this.controller.waitForEval("subject.collapsed == false", 1000, 100, messageWindow.parentNode);
|
|
||||||
return messageWindow.contentDocument.body.textContent;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens the output message window by echoing a single newline character.
|
|
||||||
*/
|
|
||||||
openMessageWindow: function() {
|
|
||||||
//this.runExCommand("echo '\\n'");
|
|
||||||
this.runExCommand("echo " + "\n".quote());
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Closes the output message window if open.
|
|
||||||
*/
|
|
||||||
closeMessageWindow: function() {
|
|
||||||
if (!this._dactyl.commandline.widgets.mowContainer.collapsed) // XXX
|
|
||||||
this.runViCommand([["VK_RETURN"]]);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @property {string} The specific Dactyl application. Eg. Pentadactyl
|
|
||||||
*/
|
|
||||||
get applicationName() this._dactyl.config.appName // XXX
|
|
||||||
};
|
|
||||||
|
|
||||||
exports.Controller = Controller;
|
|
||||||
|
|
||||||
// vim: sw=4 ts=8 et:
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
<title>Test Find Commands</title>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
A (play /ˈeɪ/; named a, plural aes) is the first letter and a vowel in the basic modern Latin alphabet. It is similar to the Ancient Greek letter Alpha, from which it derives.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<p>
|
|
||||||
π (sometimes written pi) is a mathematical constant whose value is the ratio of any circle's circumference to its diameter in the Euclidean plane; this is the same value as the ratio of a circle's area to the square of its radius. It is approximately equal to 3.14159265 in the usual decimal notation. Many formulae from mathematics, science, and engineering involve π, which makes it one of the most important mathematical constants.
|
|
||||||
</p>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,685 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
* Adrian Kalla <akalla@aviary.pl>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
// Include required modules
|
|
||||||
var modalDialog = require("modal-dialog");
|
|
||||||
var utils = require("utils");
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Unwraps a node which is wrapped into a XPCNativeWrapper or XrayWrapper
|
|
||||||
*
|
|
||||||
* @param {DOMnode} Wrapped DOM node
|
|
||||||
* @returns {DOMNode} Unwrapped DOM node
|
|
||||||
*/
|
|
||||||
function unwrapNode(aNode) {
|
|
||||||
var node = aNode;
|
|
||||||
|
|
||||||
if (node) {
|
|
||||||
// unwrap is not available on older branches (3.5 and 3.6) - Bug 533596
|
|
||||||
if ("unwrap" in XPCNativeWrapper) {
|
|
||||||
node = XPCNativeWrapper.unwrap(node);
|
|
||||||
}
|
|
||||||
else if ("wrappedJSObject" in node) {
|
|
||||||
node = node.wrappedJSObject;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return node;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DOMWalker Constructor
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMill controller of the window to operate on.
|
|
||||||
* @param {Function} callbackFilter
|
|
||||||
* callback-method to filter nodes
|
|
||||||
* @param {Function} callbackNodeTest
|
|
||||||
* callback-method to test accepted nodes
|
|
||||||
* @param {Function} callbackResults
|
|
||||||
* callback-method to process the results
|
|
||||||
* [optional - default: undefined]
|
|
||||||
*/
|
|
||||||
function DOMWalker(controller, callbackFilter, callbackNodeTest,
|
|
||||||
callbackResults) {
|
|
||||||
|
|
||||||
this._controller = controller;
|
|
||||||
this._callbackFilter = callbackFilter;
|
|
||||||
this._callbackNodeTest = callbackNodeTest;
|
|
||||||
this._callbackResults = callbackResults;
|
|
||||||
}
|
|
||||||
|
|
||||||
DOMWalker.FILTER_ACCEPT = 1;
|
|
||||||
DOMWalker.FILTER_REJECT = 2;
|
|
||||||
DOMWalker.FILTER_SKIP = 3;
|
|
||||||
|
|
||||||
DOMWalker.GET_BY_ID = "id";
|
|
||||||
DOMWalker.GET_BY_SELECTOR = "selector";
|
|
||||||
|
|
||||||
DOMWalker.WINDOW_CURRENT = 1;
|
|
||||||
DOMWalker.WINDOW_MODAL = 2;
|
|
||||||
DOMWalker.WINDOW_NEW = 4;
|
|
||||||
|
|
||||||
DOMWalker.prototype = {
|
|
||||||
/**
|
|
||||||
* Returns the filter-callback
|
|
||||||
*
|
|
||||||
* @returns Function
|
|
||||||
*/
|
|
||||||
get callbackFilter() {
|
|
||||||
return this._callbackFilter;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the node-testing-callback
|
|
||||||
*
|
|
||||||
* @returns Function
|
|
||||||
*/
|
|
||||||
get callbackNodeTest() {
|
|
||||||
return this._callbackNodeTest;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the results-callback
|
|
||||||
*
|
|
||||||
* @returns Function
|
|
||||||
*/
|
|
||||||
get callbackResults() {
|
|
||||||
return this._callbackResults;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the MozMill controller
|
|
||||||
*
|
|
||||||
* @returns Mozmill controller
|
|
||||||
* @type {MozMillController}
|
|
||||||
*/
|
|
||||||
get controller() {
|
|
||||||
return this._controller;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The main DOMWalker function.
|
|
||||||
*
|
|
||||||
* It start's the _walk-method for a given window or other dialog, runs
|
|
||||||
* a callback to process the results for that window/dialog.
|
|
||||||
* After that switches to provided new windows/dialogs.
|
|
||||||
*
|
|
||||||
* @param {array of objects} ids
|
|
||||||
* Contains informations on the elements to open while
|
|
||||||
* Object-elements: getBy - attribute-name of the attribute
|
|
||||||
* containing the identification
|
|
||||||
* information for the opener-element
|
|
||||||
* subContent - array of ids of the opener-elements
|
|
||||||
* in the window with the value of
|
|
||||||
* the above getBy-attribute
|
|
||||||
* target - information, where the new
|
|
||||||
* elements will be opened
|
|
||||||
* [1|2|4]
|
|
||||||
* title - title of the opened dialog/window
|
|
||||||
* waitFunction - The function used as an argument
|
|
||||||
* for MozmillController.waitFor to
|
|
||||||
* wait before starting the walk.
|
|
||||||
* [optional - default: no waiting]
|
|
||||||
* windowHandler - Window instance
|
|
||||||
* [only needed for some tests]
|
|
||||||
*
|
|
||||||
* @param {Node} root
|
|
||||||
* Node to start testing from
|
|
||||||
* [optional - default: this._controller.window.document.documentElement]
|
|
||||||
* @param {Function} waitFunction
|
|
||||||
* The function used as an argument for MozmillController.waitFor to
|
|
||||||
* wait before starting the walk.
|
|
||||||
* [optional - default: no waiting]
|
|
||||||
*/
|
|
||||||
walk : function DOMWalker_walk(ids, root, waitFunction) {
|
|
||||||
if (typeof waitFunction == 'function')
|
|
||||||
this._controller.waitFor(waitFunction());
|
|
||||||
|
|
||||||
if (!root)
|
|
||||||
root = this._controller.window.document.documentElement;
|
|
||||||
|
|
||||||
var resultsArray = this._walk(root);
|
|
||||||
|
|
||||||
if (typeof this._callbackResults == 'function')
|
|
||||||
this._callbackResults(this._controller, resultsArray);
|
|
||||||
|
|
||||||
if (ids)
|
|
||||||
this._prepareTargetWindows(ids);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DOMWalker_filter filters a given node by submitting it to the
|
|
||||||
* this._callbackFilter method to decide, if it should be submitted to
|
|
||||||
* a provided this._callbackNodeTest method for testing (that hapens in case
|
|
||||||
* of FILTER_ACCEPT).
|
|
||||||
* In case of FILTER_ACCEPT and FILTER_SKIP, the children of such a node
|
|
||||||
* will be filtered recursively.
|
|
||||||
* Nodes with the nodeStatus "FILTER_REJECT" and their descendants will be
|
|
||||||
* completetly ignored.
|
|
||||||
*
|
|
||||||
* @param {Node} node
|
|
||||||
* Node to filter
|
|
||||||
* @param {array of elements} collectedResults
|
|
||||||
* An array with gathered all results from testing a given element
|
|
||||||
* @returns An array with gathered all results from testing a given element
|
|
||||||
* @type {array of elements}
|
|
||||||
*/
|
|
||||||
_filter : function DOMWalker_filter(node, collectedResults) {
|
|
||||||
var nodeStatus = this._callbackFilter(node);
|
|
||||||
|
|
||||||
var nodeTestResults = [];
|
|
||||||
|
|
||||||
switch (nodeStatus) {
|
|
||||||
case DOMWalker.FILTER_ACCEPT:
|
|
||||||
nodeTestResults = this._callbackNodeTest(node);
|
|
||||||
collectedResults = collectedResults.concat(nodeTestResults);
|
|
||||||
// no break here as we have to perform the _walk below too
|
|
||||||
case DOMWalker.FILTER_SKIP:
|
|
||||||
nodeTestResults = this._walk(node);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
collectedResults = collectedResults.concat(nodeTestResults);
|
|
||||||
|
|
||||||
return collectedResults;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves and returns a wanted node based on the provided identification
|
|
||||||
* set.
|
|
||||||
*
|
|
||||||
* @param {array of objects} idSet
|
|
||||||
* Contains informations on the elements to open while
|
|
||||||
* Object-elements: getBy - attribute-name of the attribute
|
|
||||||
* containing the identification
|
|
||||||
* information for the opener-element
|
|
||||||
* subContent - array of ids of the opener-elements
|
|
||||||
* in the window with the value of
|
|
||||||
* the above getBy-attribute
|
|
||||||
* target - information, where the new
|
|
||||||
* elements will be opened
|
|
||||||
* [1|2|4]
|
|
||||||
* title - title of the opened dialog/window
|
|
||||||
* waitFunction - The function used as an argument
|
|
||||||
* for MozmillController.waitFor to
|
|
||||||
* wait before starting the walk.
|
|
||||||
* [optional - default: no waiting]
|
|
||||||
* windowHandler - Window instance
|
|
||||||
* [only needed for some tests]
|
|
||||||
*
|
|
||||||
* @returns Node
|
|
||||||
* @type {Node}
|
|
||||||
*/
|
|
||||||
_getNode : function DOMWalker_getNode(idSet) {
|
|
||||||
var doc = this._controller.window.document;
|
|
||||||
|
|
||||||
// QuerySelector seems to be unusuale for id's in this case:
|
|
||||||
// https://developer.mozilla.org/En/Code_snippets/QuerySelector
|
|
||||||
switch (idSet.getBy) {
|
|
||||||
case DOMWalker.GET_BY_ID:
|
|
||||||
return doc.getElementById(idSet[idSet.getBy]);
|
|
||||||
case DOMWalker.GET_BY_SELECTOR:
|
|
||||||
return doc.querySelector(idSet[idSet.getBy]);
|
|
||||||
default:
|
|
||||||
throw new Error("Not supported getBy-attribute: " + idSet.getBy);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Main entry point to open new elements like windows, tabpanels, prefpanes,
|
|
||||||
* dialogs
|
|
||||||
*
|
|
||||||
* @param {array of objects} ids
|
|
||||||
* Contains informations on the elements to open while
|
|
||||||
* Object-elements: getBy - attribute-name of the attribute
|
|
||||||
* containing the identification
|
|
||||||
* information for the opener-element
|
|
||||||
* subContent - array of ids of the opener-elements
|
|
||||||
* in the window with the value of
|
|
||||||
* the above getBy-attribute
|
|
||||||
* target - information, where the new
|
|
||||||
* elements will be opened
|
|
||||||
* [1|2|4]
|
|
||||||
* title - title of the opened dialog/window
|
|
||||||
* waitFunction - The function used as an argument
|
|
||||||
* for MozmillController.waitFor to
|
|
||||||
* wait before starting the walk.
|
|
||||||
* [optional - default: no waiting]
|
|
||||||
* windowHandler - Window instance
|
|
||||||
* [only needed for some tests]
|
|
||||||
*/
|
|
||||||
_prepareTargetWindows : function DOMWalker_prepareTargetWindows(ids) {
|
|
||||||
var doc = this._controller.window.document;
|
|
||||||
|
|
||||||
// Go through all the provided ids
|
|
||||||
for (var i = 0; i < ids.length; i++) {
|
|
||||||
var node = this._getNode(ids[i]);
|
|
||||||
|
|
||||||
// Go further only, if the needed element exists
|
|
||||||
if (node) {
|
|
||||||
var idSet = ids[i];
|
|
||||||
|
|
||||||
// Decide if what we want to open is a new normal/modal window or if it
|
|
||||||
// will be opened in the current window.
|
|
||||||
switch (idSet.target) {
|
|
||||||
case DOMWalker.WINDOW_CURRENT:
|
|
||||||
this._processNode(node, idSet);
|
|
||||||
break;
|
|
||||||
case DOMWalker.WINDOW_MODAL:
|
|
||||||
// Modal windows have to be able to access that informations
|
|
||||||
var modalInfos = {ids : idSet.subContent,
|
|
||||||
callbackFilter : this._callbackFilter,
|
|
||||||
callbackNodeTest : this._callbackNodeTest,
|
|
||||||
callbackResults : this._callbackResults,
|
|
||||||
waitFunction : idSet.waitFunction}
|
|
||||||
persisted.modalInfos = modalInfos;
|
|
||||||
|
|
||||||
var md = new modalDialog.modalDialog(this._controller.window);
|
|
||||||
md.start(this._modalWindowHelper);
|
|
||||||
|
|
||||||
this._processNode(node, idSet);
|
|
||||||
md.waitForDialog();
|
|
||||||
break;
|
|
||||||
case DOMWalker.WINDOW_NEW:
|
|
||||||
this._processNode(node, idSet);
|
|
||||||
|
|
||||||
// Get the new non-modal window controller
|
|
||||||
var controller = utils.handleWindow('title', idSet.title,
|
|
||||||
undefined, false);
|
|
||||||
|
|
||||||
// Start a new DOMWalker instance
|
|
||||||
let domWalker = new DOMWalker(controller, this._callbackFilter,
|
|
||||||
this._callbackNodeTest,
|
|
||||||
this._callbackResults);
|
|
||||||
domWalker.walk(idSet.subContent,
|
|
||||||
controller.window.document.documentElement,
|
|
||||||
idSet.waitFunction);
|
|
||||||
|
|
||||||
// Close the window
|
|
||||||
controller.window.close();
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error("Node does not exist: " + ids[i][ids[i].getBy]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Opens new windows/dialog and starts the DOMWalker.walk() in case of dialogs
|
|
||||||
* in existing windows.
|
|
||||||
*
|
|
||||||
* @param {Node} activeNode
|
|
||||||
* Node that holds the information which way
|
|
||||||
* to open the new window/dialog
|
|
||||||
* @param {object} idSet
|
|
||||||
* ID set for the element to open
|
|
||||||
*/
|
|
||||||
_processNode: function DOMWalker_processNode(activeNode, idSet) {
|
|
||||||
var doc = this._controller.window.document;
|
|
||||||
var nodeToProcess = this._getNode(idSet);
|
|
||||||
|
|
||||||
// Opens a new window/dialog through a menulist and runs DOMWalker.walk()
|
|
||||||
// for it.
|
|
||||||
// If the wanted window/dialog is already selected, just run this function
|
|
||||||
// recursively for it's descendants.
|
|
||||||
if (activeNode.localName == "menulist") {
|
|
||||||
if (nodeToProcess.value != idSet.value) {
|
|
||||||
var dropDown = new elementslib.Elem(nodeToProcess);
|
|
||||||
this._controller.waitForElement(dropDown);
|
|
||||||
|
|
||||||
this._controller.select(dropDown, null, null, idSet.value);
|
|
||||||
|
|
||||||
this._controller.waitFor(function() {
|
|
||||||
return nodeToProcess.value == idSet.value;
|
|
||||||
}, "The menu item did not load in time: " + idSet.value);
|
|
||||||
|
|
||||||
// If the target is a new modal/non-modal window, this.walk() has to be
|
|
||||||
// started by the method opening that window. If not, we do it here.
|
|
||||||
if (idSet.target == DOMWalker.WINDOW_CURRENT)
|
|
||||||
this.walk(idSet.subContent, null, idSet.waitFunction);
|
|
||||||
} else if (nodeToProcess.selected && idSet.subContent &&
|
|
||||||
idSet.subContent.length > 0) {
|
|
||||||
this._prepareTargetWindows(idSet.subContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opens a new prefpane using a provided windowHandler object
|
|
||||||
// and runs DOMWalker.walk() for it.
|
|
||||||
// If the wanted prefpane is already selected, just run this function
|
|
||||||
// recursively for it's descendants.
|
|
||||||
else if (activeNode.localName == "prefpane") {
|
|
||||||
var windowHandler = idSet.windowHandler;
|
|
||||||
|
|
||||||
if (windowHandler.paneId != idSet.id) {
|
|
||||||
windowHandler.paneId = idSet.id;
|
|
||||||
|
|
||||||
// Wait for the pane's content to load and to be fully displayed
|
|
||||||
this._controller.waitFor(function() {
|
|
||||||
return (nodeToProcess.loaded &&
|
|
||||||
(!mozmill.isMac ||
|
|
||||||
nodeToProcess.style.opacity == 1 ||
|
|
||||||
nodeToProcess.style.opacity == null));
|
|
||||||
}, "The pane did not load in time: " + idSet.id);
|
|
||||||
|
|
||||||
// If the target is a new modal/non-modal window, this.walk() has to be
|
|
||||||
// started by the method opening that window. If not, we do it here.
|
|
||||||
if (idSet.target == DOMWalker.WINDOW_CURRENT)
|
|
||||||
this.walk(idSet.subContent, null, idSet.waitFunction);
|
|
||||||
} else if (windowHandler.paneId == idSet.id && idSet.subContent &&
|
|
||||||
idSet.subContent.length > 0) {
|
|
||||||
this._prepareTargetWindows(idSet.subContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Switches to another tab and runs DOMWalker.walk() for it.
|
|
||||||
// If the wanted tabpanel is already selected, just run this function
|
|
||||||
// recursively for it's descendants.
|
|
||||||
else if (activeNode.localName == "tab") {
|
|
||||||
if (nodeToProcess.selected != true) {
|
|
||||||
this._controller.click(new elementslib.Elem(nodeToProcess));
|
|
||||||
|
|
||||||
// If the target is a new modal/non-modal window, this.walk() has to be
|
|
||||||
// started by the method opening that window. If not, we do it here.
|
|
||||||
if (idSet.target == DOMWalker.WINDOW_CURRENT)
|
|
||||||
this.walk(idSet.subContent, null, idSet.waitFunction);
|
|
||||||
} else if (nodeToProcess.selected && idSet.subContent
|
|
||||||
&& idSet.subContent.length > 0) {
|
|
||||||
this._prepareTargetWindows(idSet.subContent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opens a new dialog/window by clicking on an object and runs
|
|
||||||
// DOMWalker.walk() for it.
|
|
||||||
else {
|
|
||||||
this._controller.click(new elementslib.Elem(nodeToProcess));
|
|
||||||
|
|
||||||
// If the target is a new modal/non-modal window, this.walk() has to be
|
|
||||||
// started by the method opening that window. If not, we do it here.
|
|
||||||
if (idSet.target == DOMWalker.WINDOW_CURRENT)
|
|
||||||
this.walk(idSet.subContent, null, idSet.waitFunction);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* DOMWalker_walk goes recursively through the DOM, starting with a provided
|
|
||||||
* root-node and filters the nodes using the this._filter method.
|
|
||||||
*
|
|
||||||
* @param {Node} root
|
|
||||||
* Node to start testing from
|
|
||||||
* [optional - default: this._controller.window.document.documentElement]
|
|
||||||
* @returns An array with gathered all results from testing a given element
|
|
||||||
* @type {array of elements}
|
|
||||||
*/
|
|
||||||
_walk : function DOMWalker__walk(root) {
|
|
||||||
if (!root.childNodes)
|
|
||||||
throw new Error("root.childNodes does not exist");
|
|
||||||
|
|
||||||
var collectedResults = [];
|
|
||||||
|
|
||||||
// There seems to be no other way to get to the nodes hidden in the
|
|
||||||
// "_buttons" object (see Bug 614949)
|
|
||||||
if (root._buttons) {
|
|
||||||
for each (button in root._buttons) {
|
|
||||||
collectedResults = this._filter(button, collectedResults);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < root.childNodes.length; i++) {
|
|
||||||
collectedResults = this._filter(root.childNodes[i], collectedResults);
|
|
||||||
}
|
|
||||||
|
|
||||||
return collectedResults;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback function to handle new windows
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMill controller of the new window to operate on.
|
|
||||||
*/
|
|
||||||
_modalWindowHelper: function DOMWalker_modalWindowHelper(controller) {
|
|
||||||
let domWalker = new DOMWalker(controller,
|
|
||||||
persisted.modalInfos.callbackFilter,
|
|
||||||
persisted.modalInfos.callbackNodeTest,
|
|
||||||
persisted.modalInfos.callbackResults);
|
|
||||||
domWalker.walk(persisted.modalInfos.ids,
|
|
||||||
controller.window.document.documentElement,
|
|
||||||
persisted.modalInfos.waitFunction);
|
|
||||||
|
|
||||||
delete persisted.modalInfos;
|
|
||||||
|
|
||||||
controller.window.close();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Default constructor
|
|
||||||
*
|
|
||||||
* @param {object} aRoot
|
|
||||||
* Root node in the DOM to use as parent
|
|
||||||
*/
|
|
||||||
function nodeCollector(aRoot) {
|
|
||||||
this._root = aRoot.wrappedJSObject ? aRoot.wrappedJSObject : aRoot;
|
|
||||||
this._document = this._root.ownerDocument ? this._root.ownerDocument : this._root;
|
|
||||||
this._nodes = [ ];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Node collector class
|
|
||||||
*/
|
|
||||||
nodeCollector.prototype = {
|
|
||||||
/**
|
|
||||||
* Converts current nodes to elements
|
|
||||||
*
|
|
||||||
* @returns List of elements
|
|
||||||
* @type {array of ElemBase}
|
|
||||||
*/
|
|
||||||
get elements() {
|
|
||||||
var elements = [ ];
|
|
||||||
|
|
||||||
Array.forEach(this._nodes, function(element) {
|
|
||||||
elements.push(new elementslib.Elem(element));
|
|
||||||
});
|
|
||||||
|
|
||||||
return elements;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the current list of DOM nodes
|
|
||||||
*
|
|
||||||
* @returns List of nodes
|
|
||||||
* @type {array of object}
|
|
||||||
*/
|
|
||||||
get nodes() {
|
|
||||||
return this._nodes;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets current nodes to entries from the node list
|
|
||||||
*
|
|
||||||
* @param {array of objects} aNodeList
|
|
||||||
* List of DOM nodes to set
|
|
||||||
*/
|
|
||||||
set nodes(aNodeList) {
|
|
||||||
if (aNodeList) {
|
|
||||||
this._nodes = [ ];
|
|
||||||
|
|
||||||
Array.forEach(aNodeList, function(node) {
|
|
||||||
this._nodes.push(node);
|
|
||||||
}, this);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the root node used as parent for a node collection
|
|
||||||
*
|
|
||||||
* @returns Current root node
|
|
||||||
* @type {object}
|
|
||||||
*/
|
|
||||||
get root() {
|
|
||||||
return this._root;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets root node to the specified DOM node
|
|
||||||
*
|
|
||||||
* @param {object} aRoot
|
|
||||||
* DOM node to use as root for node collection
|
|
||||||
*/
|
|
||||||
set root(aRoot) {
|
|
||||||
if (aRoot) {
|
|
||||||
this._root = aRoot;
|
|
||||||
this._nodes = [ ];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter nodes given by the specified callback function
|
|
||||||
*
|
|
||||||
* @param {function} aCallback
|
|
||||||
* Function to test each element of the array.
|
|
||||||
* Elements: node, index (optional) , array (optional)
|
|
||||||
* @param {object} aThisObject
|
|
||||||
* Object to use as 'this' when executing callback.
|
|
||||||
* [optional - default: function scope]
|
|
||||||
*
|
|
||||||
* @returns The class instance
|
|
||||||
* @type {object}
|
|
||||||
*/
|
|
||||||
filter : function nodeCollector_filter(aCallback, aThisObject) {
|
|
||||||
if (!aCallback)
|
|
||||||
throw new Error(arguments.callee.name + ": No callback specified");
|
|
||||||
|
|
||||||
this.nodes = Array.filter(this.nodes, aCallback, aThisObject);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter nodes by DOM property and its value
|
|
||||||
*
|
|
||||||
* @param {string} aProperty
|
|
||||||
* Property to filter for
|
|
||||||
* @param {string} aValue
|
|
||||||
* Expected value of the DOM property
|
|
||||||
* [optional - default: n/a]
|
|
||||||
*
|
|
||||||
* @returns The class instance
|
|
||||||
* @type {object}
|
|
||||||
*/
|
|
||||||
filterByDOMProperty : function nodeCollector_filterByDOMProperty(aProperty, aValue) {
|
|
||||||
return this.filter(function(node) {
|
|
||||||
if (aProperty && aValue)
|
|
||||||
return node.getAttribute(aProperty) == aValue;
|
|
||||||
else if (aProperty)
|
|
||||||
return node.hasAttribute(aProperty);
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filter nodes by JS property and its value
|
|
||||||
*
|
|
||||||
* @param {string} aProperty
|
|
||||||
* Property to filter for
|
|
||||||
* @param {string} aValue
|
|
||||||
* Expected value of the JS property
|
|
||||||
* [optional - default: n/a]
|
|
||||||
*
|
|
||||||
* @returns The class instance
|
|
||||||
* @type {object}
|
|
||||||
*/
|
|
||||||
filterByJSProperty : function nodeCollector_filterByJSProperty(aProperty, aValue) {
|
|
||||||
return this.filter(function(node) {
|
|
||||||
if (aProperty && aValue)
|
|
||||||
return node.aProperty == aValue;
|
|
||||||
else if (aProperty)
|
|
||||||
return node.aProperty !== undefined;
|
|
||||||
else
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find anonymouse nodes with the specified attribute and value
|
|
||||||
*
|
|
||||||
* @param {string} aAttribute
|
|
||||||
* DOM attribute of the wanted node
|
|
||||||
* @param {string} aValue
|
|
||||||
* Value of the DOM attribute
|
|
||||||
*
|
|
||||||
* @returns The class instance
|
|
||||||
* @type {object}
|
|
||||||
*/
|
|
||||||
queryAnonymousNodes : function nodeCollector_queryAnonymousNodes(aAttribute, aValue) {
|
|
||||||
var node = this._document.getAnonymousElementByAttribute(this._root,
|
|
||||||
aAttribute,
|
|
||||||
aValue);
|
|
||||||
this.nodes = node ? [node] : [ ];
|
|
||||||
|
|
||||||
return this;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Find nodes with the specified selector
|
|
||||||
*
|
|
||||||
* @param {string} aSelector
|
|
||||||
* jQuery like element selector string
|
|
||||||
*
|
|
||||||
* @returns The class instance
|
|
||||||
* @type {object}
|
|
||||||
*/
|
|
||||||
queryNodes : function nodeCollector_queryNodes(aSelector) {
|
|
||||||
this.nodes = this._root.querySelectorAll(aSelector);
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exports of functions
|
|
||||||
exports.unwrapNode = unwrapNode;
|
|
||||||
|
|
||||||
// Exports of classes
|
|
||||||
exports.DOMWalker = DOMWalker;
|
|
||||||
exports.nodeCollector = nodeCollector;
|
|
||||||
@@ -1,411 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
* Anthony Hughes <anthony.s.hughes@gmail.com>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileoverview
|
|
||||||
* The DownloadsAPI adds support for download related functions. It also gives
|
|
||||||
* access to the Download Manager.
|
|
||||||
*
|
|
||||||
* @version 1.0.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Include required modules
|
|
||||||
var utils = require("utils");
|
|
||||||
|
|
||||||
const gTimeout = 5000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* List of available download states
|
|
||||||
*/
|
|
||||||
const downloadState = {
|
|
||||||
notStarted : -1,
|
|
||||||
downloading : 0,
|
|
||||||
finished : 1,
|
|
||||||
failed : 2,
|
|
||||||
canceled : 3,
|
|
||||||
paused : 4,
|
|
||||||
queued : 5,
|
|
||||||
blockedParental : 6,
|
|
||||||
scanning : 7,
|
|
||||||
dirty : 8,
|
|
||||||
blockedPolicy : 9
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
function downloadManager() {
|
|
||||||
this._controller = null;
|
|
||||||
this.downloadState = downloadState;
|
|
||||||
|
|
||||||
this._dms = Cc["@mozilla.org/download-manager;1"].
|
|
||||||
getService(Ci.nsIDownloadManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Download Manager class
|
|
||||||
*/
|
|
||||||
downloadManager.prototype = {
|
|
||||||
/**
|
|
||||||
* Returns the controller of the current window
|
|
||||||
*
|
|
||||||
* @returns Mozmill Controller
|
|
||||||
* @type {MozMillController}
|
|
||||||
*/
|
|
||||||
get controller() {
|
|
||||||
return this._controller;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of currently active downloads
|
|
||||||
*
|
|
||||||
* @returns Number of active downloads
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
get activeDownloadCount() {
|
|
||||||
return this._dms.activeDownloadCount;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel all active downloads
|
|
||||||
*/
|
|
||||||
cancelActiveDownloads : function downloadManager_cancelActiveDownloads() {
|
|
||||||
// Get a list of all active downloads (nsISimpleEnumerator)
|
|
||||||
var downloads = this._dms.activeDownloads;
|
|
||||||
|
|
||||||
// Iterate through each active download and cancel it
|
|
||||||
while (downloads.hasMoreElements()) {
|
|
||||||
var download = downloads.getNext().QueryInterface(Ci.nsIDownload);
|
|
||||||
this._dms.cancelDownload(download.id);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all downloads from the database
|
|
||||||
*/
|
|
||||||
cleanUp : function downloadManager_cleanUp()
|
|
||||||
{
|
|
||||||
this._dms.cleanUp();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cancel any active downloads, remove the files, and clean
|
|
||||||
* up the Download Manager database
|
|
||||||
*
|
|
||||||
* @param {Array of download} downloads
|
|
||||||
* Downloaded files which should be deleted (optional)
|
|
||||||
*/
|
|
||||||
cleanAll : function downloadManager_cleanAll(downloads) {
|
|
||||||
// Cancel any active downloads
|
|
||||||
this.cancelActiveDownloads();
|
|
||||||
|
|
||||||
// If no downloads have been specified retrieve the list from the database
|
|
||||||
if (downloads === undefined || downloads.length == 0)
|
|
||||||
downloads = this.getAllDownloads();
|
|
||||||
else
|
|
||||||
downloads = downloads.concat(this.getAllDownloads());
|
|
||||||
|
|
||||||
// Delete all files referred to in the Download Manager
|
|
||||||
this.deleteDownloadedFiles(downloads);
|
|
||||||
|
|
||||||
// Clean any entries from the Download Manager database
|
|
||||||
this.cleanUp();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the download manager
|
|
||||||
*
|
|
||||||
* @param {boolean} force
|
|
||||||
* Force the closing of the DM window
|
|
||||||
*/
|
|
||||||
close : function downloadManager_close(force) {
|
|
||||||
var windowCount = mozmill.utils.getWindows().length;
|
|
||||||
|
|
||||||
if (this._controller) {
|
|
||||||
// Check if we should force the closing of the DM window
|
|
||||||
if (force) {
|
|
||||||
this._controller.window.close();
|
|
||||||
} else {
|
|
||||||
var cmdKey = utils.getEntity(this.getDtds(), "cmd.close.commandKey");
|
|
||||||
this._controller.keypress(null, cmdKey, {accelKey: true});
|
|
||||||
}
|
|
||||||
|
|
||||||
this._controller.waitForEval("subject.getWindows().length == " + (windowCount - 1),
|
|
||||||
gTimeout, 100, mozmill.utils);
|
|
||||||
this._controller = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete all downloads from the local drive
|
|
||||||
*
|
|
||||||
* @param {download} downloads
|
|
||||||
* List of downloaded files
|
|
||||||
*/
|
|
||||||
deleteDownloadedFiles : function downloadManager_deleteDownloadedFiles(downloads) {
|
|
||||||
downloads.forEach(function(download) {
|
|
||||||
try {
|
|
||||||
var file = getLocalFileFromNativePathOrUrl(download.target);
|
|
||||||
file.remove(false);
|
|
||||||
} catch (ex) {
|
|
||||||
}
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the list of all downloaded files in the database
|
|
||||||
*
|
|
||||||
* @returns List of downloads
|
|
||||||
* @type {Array of download}
|
|
||||||
*/
|
|
||||||
getAllDownloads : function downloadManager_getAllDownloads() {
|
|
||||||
var dbConn = this._dms.DBConnection;
|
|
||||||
var stmt = null;
|
|
||||||
|
|
||||||
if (dbConn.schemaVersion < 3)
|
|
||||||
return new Array();
|
|
||||||
|
|
||||||
// Run a SQL query and iterate through all results which have been found
|
|
||||||
var downloads = [];
|
|
||||||
stmt = dbConn.createStatement("SELECT * FROM moz_downloads");
|
|
||||||
while (stmt.executeStep()) {
|
|
||||||
downloads.push({
|
|
||||||
id: stmt.row.id, name: stmt.row.name, target: stmt.row.target,
|
|
||||||
tempPath: stmt.row.tempPath, startTime: stmt.row.startTime,
|
|
||||||
endTime: stmt.row.endTime, state: stmt.row.state,
|
|
||||||
referrer: stmt.row.referrer, entityID: stmt.row.entityID,
|
|
||||||
currBytes: stmt.row.currBytes, maxBytes: stmt.row.maxBytes,
|
|
||||||
mimeType : stmt.row.mimeType, autoResume: stmt.row.autoResume,
|
|
||||||
preferredApplication: stmt.row.preferredApplication,
|
|
||||||
preferredAction: stmt.row.preferredAction
|
|
||||||
});
|
|
||||||
};
|
|
||||||
stmt.reset();
|
|
||||||
|
|
||||||
return downloads;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the download state of the given download
|
|
||||||
*
|
|
||||||
* @param {ElemBase} download
|
|
||||||
* Download which state should be checked
|
|
||||||
*/
|
|
||||||
getDownloadState : function downloadManager_getDownloadState(download) {
|
|
||||||
return download.getNode().getAttribute('state');
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all the needed external DTD urls as an array
|
|
||||||
*
|
|
||||||
* @returns Array of external DTD urls
|
|
||||||
* @type [string]
|
|
||||||
*/
|
|
||||||
getDtds : function downloadManager_getDtds() {
|
|
||||||
var dtds = ["chrome://browser/locale/browser.dtd",
|
|
||||||
"chrome://mozapps/locale/downloads/downloads.dtd"];
|
|
||||||
return dtds;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an UI element based on the given spec
|
|
||||||
*
|
|
||||||
* @param {object} spec
|
|
||||||
* Information of the UI element which should be retrieved
|
|
||||||
* type: General type information
|
|
||||||
* subtype: Specific element or property
|
|
||||||
* value: Value of the element or property
|
|
||||||
* @returns Element which has been created
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
getElement : function downloadManager_getElement(spec) {
|
|
||||||
var elem = null;
|
|
||||||
|
|
||||||
switch(spec.type) {
|
|
||||||
/**
|
|
||||||
* subtype: subtype of property to match
|
|
||||||
* value: value of property to match
|
|
||||||
*/
|
|
||||||
case "download":
|
|
||||||
// Use a temporary lookup to get the download item
|
|
||||||
var download = new elementslib.Lookup(this._controller.window.document,
|
|
||||||
'/id("downloadManager")/id("downloadView")/' +
|
|
||||||
'{"' + spec.subtype + '":"' + spec.value + '"}');
|
|
||||||
this._controller.waitForElement(download, gTimeout);
|
|
||||||
|
|
||||||
// Use its download id to construct the real lookup expression
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document,
|
|
||||||
'/id("downloadManager")/id("downloadView")/' +
|
|
||||||
'id("' + download.getNode().getAttribute('id') + '")');
|
|
||||||
break;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* subtype: Identifier of the specified download button (cancel, pause, resume, retry)
|
|
||||||
* value: Entry (download) of the download list
|
|
||||||
*/
|
|
||||||
case "download_button":
|
|
||||||
// XXX: Bug 555347 - There are outstanding events to process
|
|
||||||
this._controller.sleep(0);
|
|
||||||
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, spec.value.expression +
|
|
||||||
'/anon({"flex":"1"})/[1]/[1]/{"cmd":"cmd_' + spec.subtype + '"}');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown element type - " + spec.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open the Download Manager
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
* @param {boolean} shortcut
|
|
||||||
* If true the keyboard shortcut is used
|
|
||||||
*/
|
|
||||||
open : function downloadManager_open(controller, shortcut) {
|
|
||||||
if (shortcut) {
|
|
||||||
if (mozmill.isLinux) {
|
|
||||||
var cmdKey = utils.getEntity(this.getDtds(), "downloadsUnix.commandkey");
|
|
||||||
controller.keypress(null, cmdKey, {ctrlKey: true, shiftKey: true});
|
|
||||||
} else {
|
|
||||||
var cmdKey = utils.getEntity(this.getDtds(), "downloads.commandkey");
|
|
||||||
controller.keypress(null, cmdKey, {accelKey: true});
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
controller.click(new elementslib.Elem(controller.menus["tools-menu"].menu_openDownloads));
|
|
||||||
}
|
|
||||||
|
|
||||||
controller.sleep(500);
|
|
||||||
this.waitForOpened(controller);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for the given download state
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
* @param {downloadState} state
|
|
||||||
* Expected state of the download
|
|
||||||
* @param {number} timeout
|
|
||||||
* Timeout for waiting for the download state (optional)
|
|
||||||
*/
|
|
||||||
waitForDownloadState : function downloadManager_waitForDownloadState(download, state, timeout) {
|
|
||||||
this._controller.waitForEval("subject.manager.getDownloadState(subject.download) == subject.state", timeout, 100,
|
|
||||||
{manager: this, download: download, state: state});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait until the Download Manager has been opened
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
*/
|
|
||||||
waitForOpened : function downloadManager_waitForOpened(controller) {
|
|
||||||
this._controller = utils.handleWindow("type", "Download:Manager",
|
|
||||||
undefined, false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Download the file of unkown type from the given location by saving it
|
|
||||||
* automatically to disk
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController of the browser window
|
|
||||||
* @param {string} url
|
|
||||||
* URL of the file which has to be downloaded
|
|
||||||
*/
|
|
||||||
var downloadFileOfUnknownType = function(controller, url) {
|
|
||||||
controller.open(url);
|
|
||||||
|
|
||||||
// Wait until the unknown content type dialog has been opened
|
|
||||||
controller.waitForEval("subject.getMostRecentWindow('').document.documentElement.id == 'unknownContentType'",
|
|
||||||
gTimeout, 100, mozmill.wm);
|
|
||||||
|
|
||||||
utils.handleWindow("type", "", function (controller) {
|
|
||||||
// Select to save the file directly
|
|
||||||
var saveFile = new elementslib.ID(controller.window.document, "save");
|
|
||||||
controller.waitThenClick(saveFile, gTimeout);
|
|
||||||
controller.waitForEval("subject.selected == true", gTimeout, 100,
|
|
||||||
saveFile.getNode());
|
|
||||||
|
|
||||||
// Wait until the OK button has been enabled and click on it
|
|
||||||
var button = new elementslib.Lookup(controller.window.document,
|
|
||||||
'/id("unknownContentType")/anon({"anonid":"buttons"})/{"dlgtype":"accept"}');
|
|
||||||
controller.waitForElement(button, gTimeout);
|
|
||||||
controller.waitForEval("subject.okButton.hasAttribute('disabled') == false", gTimeout, 100,
|
|
||||||
{okButton: button.getNode()});
|
|
||||||
controller.click(button);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a local file from a native path or URL
|
|
||||||
*
|
|
||||||
* @param {string} aPathOrUrl
|
|
||||||
* Native path or URL of the file
|
|
||||||
* @see http://mxr.mozilla.org/mozilla-central/source/toolkit/mozapps/downloads/content/downloads.js#1309
|
|
||||||
*/
|
|
||||||
function getLocalFileFromNativePathOrUrl(aPathOrUrl) {
|
|
||||||
if (aPathOrUrl.substring(0,7) == "file://") {
|
|
||||||
// if this is a URL, get the file from that
|
|
||||||
let ioSvc = Cc["@mozilla.org/network/io-service;1"]
|
|
||||||
.getService(Ci.nsIIOService);
|
|
||||||
|
|
||||||
// XXX it's possible that using a null char-set here is bad
|
|
||||||
const fileUrl = ioSvc.newURI(aPathOrUrl, null, null)
|
|
||||||
.QueryInterface(Ci.nsIFileURL);
|
|
||||||
return fileUrl.file.clone().QueryInterface(Ci.nsILocalFile);
|
|
||||||
} else {
|
|
||||||
// if it's a pathname, create the nsILocalFile directly
|
|
||||||
var f = new nsLocalFile(aPathOrUrl);
|
|
||||||
return f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export of variables
|
|
||||||
exports.downloadState = downloadState;
|
|
||||||
|
|
||||||
// Export of functions
|
|
||||||
exports.downloadFileOfUnknownType = downloadFileOfUnknownType;
|
|
||||||
exports.getLocalFileFromNativePathOrUrl = getLocalFileFromNativePathOrUrl;
|
|
||||||
|
|
||||||
// Export of classes
|
|
||||||
exports.downloadManager = downloadManager;
|
|
||||||
@@ -1,307 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Adrian Kalla <akalla@aviary.pl>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
// Include required modules
|
|
||||||
var domUtils = require("dom-utils");
|
|
||||||
var screenshot = require("screenshot");
|
|
||||||
var utils = require("utils");
|
|
||||||
|
|
||||||
const jumlib = {};
|
|
||||||
Components.utils.import("resource://mozmill/modules/jum.js", jumlib);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback function for parsing the results of testing for duplicated
|
|
||||||
* access keys.
|
|
||||||
*
|
|
||||||
* This function processes the access keys found in one access keys scope
|
|
||||||
* looking for access keys that are listed more than one time.
|
|
||||||
* At the end, it calls the screenshot.create to create a screenshot with the
|
|
||||||
* elements containing the broken access keys highlighted.
|
|
||||||
*
|
|
||||||
* @param {array of array of object} accessKeysSet
|
|
||||||
* @param {MozmillController} controller
|
|
||||||
*/
|
|
||||||
function checkAccessKeysResults(controller, accessKeysSet) {
|
|
||||||
// Sort the access keys to have them in a A->Z order
|
|
||||||
var accessKeysList = accessKeysSet.sort();
|
|
||||||
|
|
||||||
// List of access keys
|
|
||||||
var aKeysList = [];
|
|
||||||
|
|
||||||
// List of values to identify the access keys
|
|
||||||
var valueList = [];
|
|
||||||
|
|
||||||
// List of rectangles of nodes containing access keys
|
|
||||||
var rects = [];
|
|
||||||
|
|
||||||
// List of rectangles of nodes with broken access keys
|
|
||||||
var badRects = [];
|
|
||||||
|
|
||||||
// Makes lists of all access keys and the values the access keys are in
|
|
||||||
for (var i = 0; i < accessKeysList.length; i++) {
|
|
||||||
var accessKey = accessKeysList[i][0];
|
|
||||||
var node = accessKeysList[i][1];
|
|
||||||
|
|
||||||
// Set the id and label to be shown in the console
|
|
||||||
var id = node.id || "(id is undefined)";
|
|
||||||
var label = node.label || "(label is undefined)";
|
|
||||||
|
|
||||||
var box = node.boxObject;
|
|
||||||
|
|
||||||
var innerIds = [];
|
|
||||||
var innerRects = [];
|
|
||||||
|
|
||||||
// if the access key is already in our list, take it out to replace it
|
|
||||||
// later
|
|
||||||
if (accessKey == aKeysList[aKeysList.length-1]) {
|
|
||||||
innerIds = valueList.pop();
|
|
||||||
innerRects = rects.pop();
|
|
||||||
} else {
|
|
||||||
aKeysList.push([accessKey]);
|
|
||||||
}
|
|
||||||
innerIds.push("[id: " + id + ", label: " + label + "]");
|
|
||||||
valueList.push(innerIds);
|
|
||||||
innerRects.push([box.x, box.y, box.width, box.height]);
|
|
||||||
rects.push(innerRects);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Go through all access keys and find the duplicated ones
|
|
||||||
for (var i = 0; i < valueList.length; i++) {
|
|
||||||
// Only access keys contained in more than one node are the ones we are
|
|
||||||
// looking for
|
|
||||||
if (valueList[i].length > 1) {
|
|
||||||
for (var j = 0; j < rects[i].length; j++) {
|
|
||||||
badRects.push(rects[i][j]);
|
|
||||||
}
|
|
||||||
jumlib.assert(false, 'accessKey: ' + aKeysList[i] +
|
|
||||||
' found in string\'s: ' + valueList[i].join(", "));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we have found broken access keys, make a screenshot
|
|
||||||
if (badRects.length > 0) {
|
|
||||||
screenshot.create(controller, badRects);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback function for testing for cropped elements.
|
|
||||||
*
|
|
||||||
* Checks if the XUL boxObject has screen coordinates outside of
|
|
||||||
* the screen coordinates of its parent. If there's no parent, return.
|
|
||||||
*
|
|
||||||
* @param {node} child
|
|
||||||
* @returns List of boxes that can be highlighted on a screenshot
|
|
||||||
* @type {array of array of int}
|
|
||||||
*/
|
|
||||||
function checkDimensions(child) {
|
|
||||||
if (!child.boxObject)
|
|
||||||
return [];
|
|
||||||
var childBox = child.boxObject;
|
|
||||||
var parent = childBox.parentBox;
|
|
||||||
|
|
||||||
// toplevel element or hidden elements, like script tags
|
|
||||||
if (!parent || parent == child.element || !parent.boxObject) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
var parentBox = parent.boxObject;
|
|
||||||
|
|
||||||
var badRects = [];
|
|
||||||
|
|
||||||
// check width
|
|
||||||
if (childBox.height && childBox.screenX < parentBox.screenX) {
|
|
||||||
badRects.push([childBox.x, childBox.y, parentBox.x - childBox.x,
|
|
||||||
childBox.height]);
|
|
||||||
jumlib.assert(false, 'Node is cut off at the left: ' +
|
|
||||||
_reportNode(child) + '. Parent node: ' + _reportNode(parent));
|
|
||||||
}
|
|
||||||
if (childBox.height && childBox.screenX + childBox.width >
|
|
||||||
parentBox.screenX + parentBox.width) {
|
|
||||||
badRects.push([parentBox.x + parentBox.width, childBox.y,
|
|
||||||
childBox.x + childBox.width - parentBox.x - parentBox.width,
|
|
||||||
childBox.height]);
|
|
||||||
jumlib.assert(false, 'Node is cut off at the right: ' +
|
|
||||||
_reportNode(child) + '. Parent node: ' + _reportNode(parent));
|
|
||||||
}
|
|
||||||
|
|
||||||
// check height
|
|
||||||
// We don't want to test menupopup's, as they always report the full height
|
|
||||||
// of all items in the popup
|
|
||||||
if (child.nodeName != 'menupopup' && parent.nodeName != 'menupopup') {
|
|
||||||
if (childBox.width && childBox.screenY < parentBox.screenY) {
|
|
||||||
badRects.push([childBox.x, childBox.y, parentBox.y - childBox.y,
|
|
||||||
childBox.width]);
|
|
||||||
jumlib.assert(false, 'Node is cut off at the top: ' +
|
|
||||||
_reportNode(child) + '. Parent node: ' + _reportNode(parent));
|
|
||||||
}
|
|
||||||
if (childBox.width && childBox.screenY + childBox.height >
|
|
||||||
parentBox.screenY + parentBox.height) {
|
|
||||||
badRects.push([childBox.x, parentBox.y + parentBox.height,
|
|
||||||
childBox.width,
|
|
||||||
childBox.y + childBox.height - parentBox.y - parentBox.height]);
|
|
||||||
jumlib.assert(false, 'Node is cut off at the bottom: ' +
|
|
||||||
_reportNode(child) + '. Parent node: ' + _reportNode(parent));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return badRects;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters out nodes which should not be tested because they are not in the
|
|
||||||
* current access key scope.
|
|
||||||
*
|
|
||||||
* @param {node} node
|
|
||||||
* @returns Filter status of the given node
|
|
||||||
* @type {array of array of int}
|
|
||||||
*/
|
|
||||||
function filterAccessKeys(node) {
|
|
||||||
// Menus will need a separate filter set
|
|
||||||
var notAllowedLocalNames = ["menu", "menubar", "menupopup", "popupset"];
|
|
||||||
|
|
||||||
if (!node.disabled && !node.collapsed && !node.hidden &&
|
|
||||||
notAllowedLocalNames.indexOf(node.localName) == -1) {
|
|
||||||
// Code specific to the preferences panes to reject out not visible nodes
|
|
||||||
// in the panes.
|
|
||||||
if (node.parentNode && (node.parentNode.localName == "prefwindow" &&
|
|
||||||
node.parentNode.currentPane.id != node.id) ||
|
|
||||||
((node.parentNode.localName == "tabpanels" ||
|
|
||||||
node.parentNode.localName == "deck") &&
|
|
||||||
node.parentNode.selectedPanel.id != node.id)) {
|
|
||||||
return domUtils.DOMWalker.FILTER_REJECT;
|
|
||||||
// end of the specific code
|
|
||||||
} else if (node.accessKey) {
|
|
||||||
return domUtils.DOMWalker.FILTER_ACCEPT;
|
|
||||||
} else {
|
|
||||||
return domUtils.DOMWalker.FILTER_SKIP;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// we don't want to test not visible elements
|
|
||||||
return domUtils.DOMWalker.FILTER_REJECT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Filters out nodes which should not be tested because they are not visible
|
|
||||||
*
|
|
||||||
* @param {node} node
|
|
||||||
* @returns Filter status of the given node
|
|
||||||
* @type {array of array of int}
|
|
||||||
*/
|
|
||||||
function filterCroppedNodes(node) {
|
|
||||||
if (!node.boxObject) {
|
|
||||||
return domUtils.DOMWalker.FILTER_SKIP;
|
|
||||||
} else {
|
|
||||||
if (!node.disabled && !node.collapsed && !node.hidden) {
|
|
||||||
// Code specific to the preferences panes to reject out not visible nodes
|
|
||||||
// in the panes.
|
|
||||||
if (node.parentNode && (node.parentNode.localName == "prefwindow" &&
|
|
||||||
node.parentNode.currentPane.id != node.id) ||
|
|
||||||
((node.parentNode.localName == "tabpanels" ||
|
|
||||||
node.parentNode.localName == "deck") &&
|
|
||||||
node.parentNode.selectedPanel.id != node.id)) {
|
|
||||||
return domUtils.DOMWalker.FILTER_REJECT;
|
|
||||||
// end of the specific code
|
|
||||||
} else {
|
|
||||||
return domUtils.DOMWalker.FILTER_ACCEPT;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// we don't want to test not visible elements
|
|
||||||
return domUtils.DOMWalker.FILTER_REJECT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback function for testing access keys. To be used with the DOMWalker.
|
|
||||||
*
|
|
||||||
* It packs a submitted node and its access key into a double array
|
|
||||||
*
|
|
||||||
* @param {node} node Node containing the access key
|
|
||||||
* @returns lower-cased access key and its node in a nested array
|
|
||||||
* @type {array of array}
|
|
||||||
*/
|
|
||||||
function prepareAccessKey(node) {
|
|
||||||
return [[node.accessKey.toLowerCase(), node]];
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Callback function for parsing the results of testing for cropped elements.
|
|
||||||
*
|
|
||||||
* This function calls the screenshot.create method if there is at least one
|
|
||||||
* box.
|
|
||||||
*
|
|
||||||
* @param {array of array of int} boxes
|
|
||||||
* @param {MozmillController} controller
|
|
||||||
*/
|
|
||||||
function processDimensionsResults(controller, boxes) {
|
|
||||||
if (boxes && boxes.length > 0) {
|
|
||||||
screenshot.create(controller, boxes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tries to return a useful string identificator of the given node
|
|
||||||
*
|
|
||||||
* @param {node} node
|
|
||||||
* @returns Identificator of the node
|
|
||||||
* @type {String}
|
|
||||||
*/
|
|
||||||
function _reportNode(node) {
|
|
||||||
if (node.id) {
|
|
||||||
return "id: " + node.id;
|
|
||||||
} else if (node.label) {
|
|
||||||
return "label: " + node.label;
|
|
||||||
} else if (node.value) {
|
|
||||||
return "value: " + node.value;
|
|
||||||
} else if (node.hasAttributes()) {
|
|
||||||
var attrs = "node attributes: ";
|
|
||||||
for (var i = node.attributes.length - 1; i >= 0; i--) {
|
|
||||||
attrs += node.attributes[i].name + "->" + node.attributes[i].value + ";";
|
|
||||||
}
|
|
||||||
return attrs;
|
|
||||||
} else {
|
|
||||||
return "anonymous node";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export of functions
|
|
||||||
exports.checkAccessKeysResults = checkAccessKeysResults;
|
|
||||||
exports.checkDimensions = checkDimensions;
|
|
||||||
exports.filterAccessKeys = filterAccessKeys;
|
|
||||||
exports.filterCroppedNodes = filterCroppedNodes;
|
|
||||||
exports.prepareAccessKey = prepareAccessKey;
|
|
||||||
exports.processDimensionsResults = processDimensionsResults;
|
|
||||||
@@ -1,236 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Clint Talbert <ctalbert@mozilla.com>
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
// Include required modules
|
|
||||||
var domUtils = require("dom-utils");
|
|
||||||
|
|
||||||
const TIMEOUT_MODAL_DIALOG = 5000;
|
|
||||||
const DELAY_CHECK = 100;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Observer object to find the modal dialog spawned by a controller
|
|
||||||
*
|
|
||||||
* @constructor
|
|
||||||
* @class Observer used to find a modal dialog
|
|
||||||
*
|
|
||||||
* @param {object} aOpener
|
|
||||||
* Window which is the opener of the modal dialog
|
|
||||||
* @param {function} aCallback
|
|
||||||
* The callback handler to use to interact with the modal dialog
|
|
||||||
*/
|
|
||||||
function mdObserver(aOpener, aCallback) {
|
|
||||||
this._opener = aOpener;
|
|
||||||
this._callback = aCallback;
|
|
||||||
this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
||||||
}
|
|
||||||
|
|
||||||
mdObserver.prototype = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set our default values for our internal properties
|
|
||||||
*/
|
|
||||||
_opener : null,
|
|
||||||
_callback: null,
|
|
||||||
_timer: null,
|
|
||||||
exception: null,
|
|
||||||
finished: false,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the modal dialog has been opened
|
|
||||||
*
|
|
||||||
* @returns {object} The modal dialog window found, or null.
|
|
||||||
*/
|
|
||||||
findWindow : function mdObserver_findWindow() {
|
|
||||||
// If a window has been opened from content, it has to be unwrapped.
|
|
||||||
var window = domUtils.unwrapNode(mozmill.wm.getMostRecentWindow(''));
|
|
||||||
|
|
||||||
// Get the WebBrowserChrome and check if it's a modal window
|
|
||||||
var chrome = window.QueryInterface(Ci.nsIInterfaceRequestor).
|
|
||||||
getInterface(Ci.nsIWebNavigation).
|
|
||||||
QueryInterface(Ci.nsIDocShellTreeItem).
|
|
||||||
treeOwner.
|
|
||||||
QueryInterface(Ci.nsIInterfaceRequestor).
|
|
||||||
getInterface(Ci.nsIWebBrowserChrome);
|
|
||||||
if (!chrome.isWindowModal()) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Opening a modal dialog from a modal dialog would fail, if we wouldn't
|
|
||||||
// check for the opener of the modal dialog
|
|
||||||
var found = false;
|
|
||||||
if (window.opener) {
|
|
||||||
// XXX Bug 614757 - an already unwrapped node returns a wrapped node
|
|
||||||
var opener = domUtils.unwrapNode(window.opener);
|
|
||||||
found = (mozmill.utils.getChromeWindow(opener) == this._opener);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Also note that it could happen that dialogs don't have an opener
|
|
||||||
// (i.e. clear recent history). In such a case make sure that the most
|
|
||||||
// recent window is not the passed in reference opener
|
|
||||||
found = (window != this._opener);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (found ? window : null);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Called by the timer in the given interval to check if the modal dialog has
|
|
||||||
* been opened. Once it has been found the callback gets executed
|
|
||||||
*
|
|
||||||
* @param {object} aSubject Not used.
|
|
||||||
* @param {string} aTopic Not used.
|
|
||||||
* @param {string} aData Not used.
|
|
||||||
*/
|
|
||||||
observe : function mdObserver_observe(aSubject, aTopic, aData) {
|
|
||||||
// Once the window has been found and loaded we can execute the callback
|
|
||||||
var window = this.findWindow();
|
|
||||||
if (window && ("documentLoaded" in window)) {
|
|
||||||
try {
|
|
||||||
this._callback(new mozmill.controller.MozMillController(window));
|
|
||||||
}
|
|
||||||
catch (ex) {
|
|
||||||
// Store the exception, so it can be forwarded if a modal dialog has
|
|
||||||
// been opened by another modal dialog
|
|
||||||
this.exception = ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (window) {
|
|
||||||
window.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
this.finished = true;
|
|
||||||
this.stop();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// otherwise try again in a bit
|
|
||||||
this._timer.init(this, DELAY_CHECK, Ci.nsITimer.TYPE_ONE_SHOT);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop the timer which checks for new modal dialogs
|
|
||||||
*/
|
|
||||||
stop : function mdObserver_stop() {
|
|
||||||
delete this._timer;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a new instance of modalDialog.
|
|
||||||
*
|
|
||||||
* @constructor
|
|
||||||
* @class Handler for modal dialogs
|
|
||||||
*
|
|
||||||
* @param {object} aWindow [optional - default: null]
|
|
||||||
* Window which is the opener of the modal dialog
|
|
||||||
*/
|
|
||||||
function modalDialog(aWindow) {
|
|
||||||
this._window = aWindow || null;
|
|
||||||
}
|
|
||||||
|
|
||||||
modalDialog.prototype = {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Simply checks if the modal dialog has been processed
|
|
||||||
*
|
|
||||||
* @returns {boolean} True, if the dialog has been processed
|
|
||||||
*/
|
|
||||||
get finished() {
|
|
||||||
return (!this._observer || this._observer.finished);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start timer to wait for the modal dialog.
|
|
||||||
*
|
|
||||||
* @param {function} aCallback
|
|
||||||
* The callback handler to use to interact with the modal dialog
|
|
||||||
*/
|
|
||||||
start : function modalDialog_start(aCallback) {
|
|
||||||
if (!aCallback)
|
|
||||||
throw new Error(arguments.callee.name + ": Callback not specified.");
|
|
||||||
|
|
||||||
this._observer = new mdObserver(this._window, aCallback);
|
|
||||||
|
|
||||||
this._timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
|
|
||||||
this._timer.init(this._observer, DELAY_CHECK, Ci.nsITimer.TYPE_ONE_SHOT);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop the timer which checks for new modal dialogs
|
|
||||||
*/
|
|
||||||
stop : function modalDialog_stop() {
|
|
||||||
delete this._timer;
|
|
||||||
|
|
||||||
if (this._observer) {
|
|
||||||
this._observer.stop();
|
|
||||||
this._observer = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait until the modal dialog has been processed.
|
|
||||||
*
|
|
||||||
* @param {Number} aTimeout (optional - default 5s)
|
|
||||||
* Duration to wait
|
|
||||||
*/
|
|
||||||
waitForDialog : function modalDialog_waitForDialog(aTimeout) {
|
|
||||||
var timeout = aTimeout || TIMEOUT_MODAL_DIALOG;
|
|
||||||
|
|
||||||
if (!this._observer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
mozmill.utils.waitFor(function () {
|
|
||||||
return this.finished;
|
|
||||||
}, "Modal dialog has been found and processed", timeout, undefined, this);
|
|
||||||
|
|
||||||
// Forward the raised exception so we can detect failures in modal dialogs
|
|
||||||
if (this._observer.exception) {
|
|
||||||
throw this._observer.exception;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
this.stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// Export of classes
|
|
||||||
exports.modalDialog = modalDialog;
|
|
||||||
@@ -1,208 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Geo Mealer <gmealer@mozilla.com>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
// Paths for mapped memory and allocated memory, respectively. Use as
|
|
||||||
// keys to access the appropriate memory reporters.
|
|
||||||
const PATH_MAPPED = "malloc/mapped";
|
|
||||||
const PATH_ALLOCATED = "malloc/allocated";
|
|
||||||
|
|
||||||
// Returning this as a numeric constant to simplify memory calculations
|
|
||||||
// Neither allocated nor mapped should be 0 in real life.
|
|
||||||
const MEMORY_UNAVAILABLE = "0";
|
|
||||||
|
|
||||||
// INITIALIZE MEMORY REPORTERS
|
|
||||||
|
|
||||||
// gMemReporters will be a dictionary, key=path and val=reporter
|
|
||||||
// See initMemReporters() for how it's used.
|
|
||||||
var gMemReporters = {};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize the static memory reporters
|
|
||||||
*
|
|
||||||
* Called during module initialization, below.
|
|
||||||
* See also aboutMemory.js in Firefox code
|
|
||||||
*/
|
|
||||||
function initMemReporters() {
|
|
||||||
var memMgr = Cc["@mozilla.org/memory-reporter-manager;1"].
|
|
||||||
getService(Ci.nsIMemoryReporterManager);
|
|
||||||
|
|
||||||
// Grab all the memory reporters, load into gMemReporters as a dictionary
|
|
||||||
var e = memMgr.enumerateReporters();
|
|
||||||
while (e.hasMoreElements()) {
|
|
||||||
var memReporter = e.getNext().QueryInterface(Ci.nsIMemoryReporter);
|
|
||||||
gMemReporters[memReporter.path] = memReporter;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
initMemReporters();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PERFORMANCE TRACER
|
|
||||||
*
|
|
||||||
* Keeps a trace log of both actions and performance statistics
|
|
||||||
* throughout a test run.
|
|
||||||
*
|
|
||||||
* Performance stats currently include mapped and allocated memory.
|
|
||||||
* More stats will be added as methods to read them are discovered.
|
|
||||||
*
|
|
||||||
* Usage:
|
|
||||||
* Before test, create a new PerfTracer named after the test.
|
|
||||||
* Ex: var perf = new performance.PerfTracer("MyTestFunc");
|
|
||||||
*
|
|
||||||
* During test, after notable actions call PerfTracer.addCheckpoint(label)
|
|
||||||
* Ex: perf.addCheckpoint("Opened preferences dialog");
|
|
||||||
*
|
|
||||||
* After test, call PerfTracer.finish()
|
|
||||||
* Ex: perf.finish();
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* PerfTracer constructor
|
|
||||||
*
|
|
||||||
* @param {string} name
|
|
||||||
* Name of the tracer, currently used in the output title
|
|
||||||
*/
|
|
||||||
function PerfTracer(name) {
|
|
||||||
if (!name) {
|
|
||||||
throw new Error(arguments.callee.name + ": name not supplied.");
|
|
||||||
}
|
|
||||||
|
|
||||||
this.clearLog();
|
|
||||||
this._name = name;
|
|
||||||
}
|
|
||||||
|
|
||||||
PerfTracer.prototype = {
|
|
||||||
// UTILITY METHODS
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format a single result for printing
|
|
||||||
*
|
|
||||||
* @param {object} result
|
|
||||||
* Result as created by addCheckpoint()
|
|
||||||
* Elements: timestamp {Date} - date/time
|
|
||||||
* allocated {number} - allocated memory
|
|
||||||
* mapped {number} - mapped memory
|
|
||||||
* label {string} - label for result
|
|
||||||
*
|
|
||||||
* @returns Result string formatted for output
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
_formatResult : function PerfTracer_formatResult(result) {
|
|
||||||
var resultString = result.timestamp.toUTCString() + " | " +
|
|
||||||
result.allocated + " | " +
|
|
||||||
result.mapped + " | " +
|
|
||||||
result.label + "\n";
|
|
||||||
|
|
||||||
return resultString;
|
|
||||||
},
|
|
||||||
|
|
||||||
// PUBLIC INTERFACE
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get a memory value from a reporter
|
|
||||||
*
|
|
||||||
* @param {string} path
|
|
||||||
* Path of memory reporter (e.g. PATH_MAPPED)
|
|
||||||
* @returns Memory value from requested reporter, MEMORY_UNAVAILABLE if
|
|
||||||
* not found
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
getMemory : function PerfTracer_getMemory(path) {
|
|
||||||
var val = MEMORY_UNAVAILABLE;
|
|
||||||
if (path in gMemReporters) {
|
|
||||||
val = gMemReporters[path].memoryUsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
return val;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clears the tracker log and starts over
|
|
||||||
*/
|
|
||||||
clearLog : function PerfTracer_clearLog() {
|
|
||||||
this._log = new Array();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Adds a checkpoint to the tracker log, with time and performance info
|
|
||||||
*
|
|
||||||
* @param {string} aLabel
|
|
||||||
* Label attached to performance results. Typically should be
|
|
||||||
* whatever the test just did.
|
|
||||||
*/
|
|
||||||
addCheckpoint : function PerfTracer_addCheckpoint(aLabel) {
|
|
||||||
var result = {
|
|
||||||
label: aLabel,
|
|
||||||
timestamp: new Date(),
|
|
||||||
mapped: this.getMemory(PATH_MAPPED),
|
|
||||||
allocated: this.getMemory(PATH_ALLOCATED)
|
|
||||||
};
|
|
||||||
|
|
||||||
this._log.push(result);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prints all results to console.
|
|
||||||
* XXX: make this work with output files
|
|
||||||
*/
|
|
||||||
finish : function PerfTracer_finish() {
|
|
||||||
// Title
|
|
||||||
var title = "Performance Trace (" + this._name + ")";
|
|
||||||
|
|
||||||
// Separator
|
|
||||||
var sep = "";
|
|
||||||
for(var i = 0; i < title.length; i++) {
|
|
||||||
sep += "=";
|
|
||||||
}
|
|
||||||
|
|
||||||
dump(sep + "\n");
|
|
||||||
dump(title + "\n");
|
|
||||||
dump(sep + "\n");
|
|
||||||
|
|
||||||
// Log
|
|
||||||
for(i = 0; i < this._log.length; i++) {
|
|
||||||
dump(this._formatResult(this._log[i]));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Exported constants
|
|
||||||
exports.PATH_MAPPED = PATH_MAPPED;
|
|
||||||
exports.PATH_ALLOCATED = PATH_ALLOCATED;
|
|
||||||
exports.MEMORY_UNAVAILABLE = MEMORY_UNAVAILABLE;
|
|
||||||
|
|
||||||
// Exported class
|
|
||||||
exports.PerfTracer = PerfTracer;
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
* Geo Mealer <gmealer@mozilla.com>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileoverview
|
|
||||||
* The ModalDialogAPI adds support for handling modal dialogs. It
|
|
||||||
* has to be used e.g. for alert boxes and other commonDialog instances.
|
|
||||||
*
|
|
||||||
* @version 1.0.2
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Include required modules
|
|
||||||
var utils = require("utils");
|
|
||||||
|
|
||||||
const gTimeout = 5000;
|
|
||||||
|
|
||||||
// Default bookmarks.html file lives in omni.jar, get via resource URI
|
|
||||||
const BOOKMARKS_RESOURCE = "resource:///defaults/profile/bookmarks.html";
|
|
||||||
|
|
||||||
// Bookmarks can take up to ten seconds to restore
|
|
||||||
const BOOKMARKS_TIMEOUT = 10000;
|
|
||||||
|
|
||||||
// Observer topics we need to watch to know whether we're finished
|
|
||||||
const TOPIC_BOOKMARKS_RESTORE_SUCCESS = "bookmarks-restore-success";
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance of the bookmark service to gain access to the bookmark API.
|
|
||||||
*
|
|
||||||
* @see http://mxr.mozilla.org/mozilla-central (nsINavBookmarksService.idl)
|
|
||||||
*/
|
|
||||||
var bookmarksService = Cc["@mozilla.org/browser/nav-bookmarks-service;1"].
|
|
||||||
getService(Ci.nsINavBookmarksService);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance of the history service to gain access to the history API.
|
|
||||||
*
|
|
||||||
* @see http://mxr.mozilla.org/mozilla-central (nsINavHistoryService.idl)
|
|
||||||
*/
|
|
||||||
var historyService = Cc["@mozilla.org/browser/nav-history-service;1"].
|
|
||||||
getService(Ci.nsINavHistoryService);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance of the livemark service to gain access to the livemark API
|
|
||||||
*
|
|
||||||
* @see http://mxr.mozilla.org/mozilla-central (nsILivemarkService.idl)
|
|
||||||
*/
|
|
||||||
var livemarkService = Cc["@mozilla.org/browser/livemark-service;2"].
|
|
||||||
getService(Ci.nsILivemarkService);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance of the browser history interface to gain access to
|
|
||||||
* browser-specific history API
|
|
||||||
*
|
|
||||||
* @see http://mxr.mozilla.org/mozilla-central (nsIBrowserHistory.idl)
|
|
||||||
*/
|
|
||||||
var browserHistory = Cc["@mozilla.org/browser/nav-history-service;1"].
|
|
||||||
getService(Ci.nsIBrowserHistory);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Instance of the observer service to gain access to the observer API
|
|
||||||
*
|
|
||||||
* @see http://mxr.mozilla.org/mozilla-central (nsIObserverService.idl)
|
|
||||||
*/
|
|
||||||
var observerService = Cc["@mozilla.org/observer-service;1"].
|
|
||||||
getService(Ci.nsIObserverService);
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if an URI is bookmarked within the specified folder
|
|
||||||
*
|
|
||||||
* @param (nsIURI) uri
|
|
||||||
* URI of the bookmark
|
|
||||||
* @param {String} folderId
|
|
||||||
* Folder in which the search has to take place
|
|
||||||
* @return Returns if the URI exists in the given folder
|
|
||||||
* @type Boolean
|
|
||||||
*/
|
|
||||||
function isBookmarkInFolder(uri, folderId)
|
|
||||||
{
|
|
||||||
var ids = bookmarksService.getBookmarkIdsForURI(uri, {});
|
|
||||||
for (let i = 0; i < ids.length; i++) {
|
|
||||||
if (bookmarksService.getFolderIdForItem(ids[i]) == folderId)
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restore the default bookmarks for the current profile
|
|
||||||
*/
|
|
||||||
function restoreDefaultBookmarks() {
|
|
||||||
// Set up the observer -- we're only checking for success here, so we'll simply
|
|
||||||
// time out and throw on failure. It makes the code much clearer than handling
|
|
||||||
// finished state and success state separately.
|
|
||||||
var importSuccessful = false;
|
|
||||||
var importObserver = {
|
|
||||||
observe: function (aSubject, aTopic, aData) {
|
|
||||||
if (aTopic == TOPIC_BOOKMARKS_RESTORE_SUCCESS) {
|
|
||||||
importSuccessful = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
observerService.addObserver(importObserver, TOPIC_BOOKMARKS_RESTORE_SUCCESS, false);
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Fire off the import
|
|
||||||
var bookmarksURI = utils.createURI(BOOKMARKS_RESOURCE);
|
|
||||||
var importer = Cc["@mozilla.org/browser/places/import-export-service;1"].
|
|
||||||
getService(Ci.nsIPlacesImportExportService);
|
|
||||||
importer.importHTMLFromURI(bookmarksURI, true);
|
|
||||||
|
|
||||||
// Wait for it to be finished--the observer above will flip this flag
|
|
||||||
mozmill.utils.waitFor(function () {
|
|
||||||
return importSuccessful;
|
|
||||||
}, "Default bookmarks have finished importing", BOOKMARKS_TIMEOUT);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Whatever happens, remove the observer afterwards
|
|
||||||
observerService.removeObserver(importObserver, TOPIC_BOOKMARKS_RESTORE_SUCCESS);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Synchronous wrapper around browserHistory.removeAllPages()
|
|
||||||
* Removes history and blocks until done
|
|
||||||
*/
|
|
||||||
function removeAllHistory() {
|
|
||||||
const TOPIC_EXPIRATION_FINISHED = "places-expiration-finished";
|
|
||||||
|
|
||||||
// Create flag visible to both the eval and the observer object
|
|
||||||
var finishedFlag = {
|
|
||||||
state: false
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up an observer so we get notified when remove completes
|
|
||||||
let observer = {
|
|
||||||
observe: function(aSubject, aTopic, aData) {
|
|
||||||
observerService.removeObserver(this, TOPIC_EXPIRATION_FINISHED);
|
|
||||||
finishedFlag.state = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
observerService.addObserver(observer, TOPIC_EXPIRATION_FINISHED, false);
|
|
||||||
|
|
||||||
// Remove the pages, then block until we're done or until timeout is reached
|
|
||||||
browserHistory.removeAllPages();
|
|
||||||
mozmill.controller.waitForEval("subject.state == true", gTimeout, 100, finishedFlag);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export of variables
|
|
||||||
exports.bookmarksService = bookmarksService;
|
|
||||||
exports.historyService = historyService;
|
|
||||||
exports.livemarkService = livemarkService;
|
|
||||||
exports.browserHistory = browserHistory;
|
|
||||||
|
|
||||||
// Export of functions
|
|
||||||
exports.isBookmarkInFolder = isBookmarkInFolder;
|
|
||||||
exports.restoreDefaultBookmarks = restoreDefaultBookmarks;
|
|
||||||
exports.removeAllHistory = removeAllHistory;
|
|
||||||
@@ -1,384 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
* Clint Talbert <ctalbert@mozilla.com>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileoverview
|
|
||||||
* The PrefsAPI adds support for preferences related functions. It gives access
|
|
||||||
* to the preferences system and allows to handle the preferences dialog
|
|
||||||
*
|
|
||||||
* @version 1.0.1
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Include required modules
|
|
||||||
var modalDialog = require("modal-dialog");
|
|
||||||
var utils = require("utils");
|
|
||||||
|
|
||||||
|
|
||||||
const gTimeout = 5000;
|
|
||||||
|
|
||||||
// Preferences dialog element templates
|
|
||||||
const PREF_DIALOG_BUTTONS = '/{"type":"prefwindow"}/anon({"anonid":"dlg-buttons"})';
|
|
||||||
const PREF_DIALOG_DECK = '/{"type":"prefwindow"}/anon({"class":"paneDeckContainer"})/anon({"anonid":"paneDeck"})';
|
|
||||||
const PREF_DIALOG_SELECTOR = '/{"type":"prefwindow"}/anon({"orient":"vertical"})/anon({"anonid":"selector"})';
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMill controller of the browser window to operate on.
|
|
||||||
*/
|
|
||||||
function preferencesDialog(controller) {
|
|
||||||
this._controller = controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preferences dialog object to simplify the access to this dialog
|
|
||||||
*/
|
|
||||||
preferencesDialog.prototype = {
|
|
||||||
/**
|
|
||||||
* Returns the MozMill controller
|
|
||||||
*
|
|
||||||
* @returns Mozmill controller
|
|
||||||
* @type {MozMillController}
|
|
||||||
*/
|
|
||||||
get controller() {
|
|
||||||
return this._controller;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the currently selected panel
|
|
||||||
*
|
|
||||||
* @returns The panel element
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
get selectedPane() {
|
|
||||||
return this.getElement({type: "deck_pane"});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the given pane id
|
|
||||||
*/
|
|
||||||
get paneId() {
|
|
||||||
// Check if the selector and the pane are consistent
|
|
||||||
var selector = this.getElement({type: "selector"});
|
|
||||||
|
|
||||||
this._controller.waitForEval("subject.selector.getAttribute('pane') == subject.dlg.selectedPane.getNode().id", gTimeout, 100,
|
|
||||||
{selector: selector.getNode().selectedItem, dlg: this});
|
|
||||||
|
|
||||||
return this.selectedPane.getNode().id;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the given pane by id
|
|
||||||
*
|
|
||||||
* @param {string} id of the pane
|
|
||||||
*/
|
|
||||||
set paneId(id) {
|
|
||||||
var button = this.getElement({type: "selector_button", value: id});
|
|
||||||
this._controller.waitThenClick(button, gTimeout);
|
|
||||||
|
|
||||||
// Check if the correct selector is selected
|
|
||||||
var selector = this.getElement({type: "selector"});
|
|
||||||
this._controller.waitForEval("subject.selector.getAttribute('pane') == subject.newPane", gTimeout, 100,
|
|
||||||
{selector: selector.getNode().selectedItem, newPane: id});
|
|
||||||
return this.paneId;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the preferences dialog
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
* @param {boolean} saveChanges
|
|
||||||
* (Optional) If true the OK button is clicked on Windows which saves
|
|
||||||
* the changes. On OS X and Linux changes are applied immediately
|
|
||||||
*/
|
|
||||||
close : function preferencesDialog_close(saveChanges) {
|
|
||||||
saveChanges = (saveChanges == undefined) ? false : saveChanges;
|
|
||||||
|
|
||||||
if (mozmill.isWindows) {
|
|
||||||
var button = this.getElement({type: "button", subtype: (saveChanges ? "accept" : "cancel")});
|
|
||||||
this._controller.click(button);
|
|
||||||
} else {
|
|
||||||
this._controller.keypress(null, 'w', {accelKey: true});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all the needed external DTD urls as an array
|
|
||||||
*
|
|
||||||
* @returns Array of external DTD urls
|
|
||||||
* @type [string]
|
|
||||||
*/
|
|
||||||
getDtds : function preferencesDialog_getDtds() {
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an UI element based on the given spec
|
|
||||||
*
|
|
||||||
* @param {object} spec
|
|
||||||
* Information of the UI element which should be retrieved
|
|
||||||
* type: General type information
|
|
||||||
* subtype: Specific element or property
|
|
||||||
* value: Value of the element or property
|
|
||||||
* @returns Element which has been created
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
getElement : function aboutSessionRestore_getElement(spec) {
|
|
||||||
var elem = null;
|
|
||||||
|
|
||||||
switch(spec.type) {
|
|
||||||
case "button":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, PREF_DIALOG_BUTTONS +
|
|
||||||
'/{"dlgtype":"' + spec.subtype + '"}');
|
|
||||||
break;
|
|
||||||
case "deck":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, PREF_DIALOG_DECK);
|
|
||||||
break;
|
|
||||||
case "deck_pane":
|
|
||||||
var deck = this.getElement({type: "deck"}).getNode();
|
|
||||||
|
|
||||||
// XXX: Bug 390724 - selectedPane is broken. So iterate through all elements
|
|
||||||
var panel = deck.boxObject.firstChild;
|
|
||||||
for (var ii = 0; ii < deck.selectedIndex; ii++)
|
|
||||||
panel = panel.nextSibling;
|
|
||||||
|
|
||||||
elem = new elementslib.Elem(panel);
|
|
||||||
break;
|
|
||||||
case "selector":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, PREF_DIALOG_SELECTOR);
|
|
||||||
break;
|
|
||||||
case "selector_button":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, PREF_DIALOG_SELECTOR +
|
|
||||||
'/{"pane":"' + spec.value + '"}');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown element type - " + spec.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Preferences object to simplify the access to the nsIPrefBranch.
|
|
||||||
*/
|
|
||||||
var preferences = {
|
|
||||||
_prefService : Cc["@mozilla.org/preferences-service;1"].
|
|
||||||
getService(Ci.nsIPrefService),
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use branch to access low level functions of nsIPrefBranch
|
|
||||||
*
|
|
||||||
* @return Instance of the preferences branch
|
|
||||||
* @type nsIPrefBranch
|
|
||||||
*/
|
|
||||||
get prefBranch() {
|
|
||||||
return this._prefService.QueryInterface(Ci.nsIPrefBranch);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use defaultPrefBranch to access low level functions of the default branch
|
|
||||||
*
|
|
||||||
* @return Instance of the preferences branch
|
|
||||||
* @type nsIPrefBranch
|
|
||||||
*/
|
|
||||||
get defaultPrefBranch() {
|
|
||||||
return this._prefService.getDefaultBranch("");
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Use prefService to access low level functions of nsIPrefService
|
|
||||||
*
|
|
||||||
* @return Instance of the pref service
|
|
||||||
* @type nsIPrefService
|
|
||||||
*/
|
|
||||||
get prefService() {
|
|
||||||
return this._prefService;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear a user set preference
|
|
||||||
*
|
|
||||||
* @param {string} prefName
|
|
||||||
* The user-set preference to clear
|
|
||||||
* @return False if the preference had the default value
|
|
||||||
* @type boolean
|
|
||||||
**/
|
|
||||||
clearUserPref : function preferences_clearUserPref(prefName) {
|
|
||||||
try {
|
|
||||||
this.prefBranch.clearUserPref(prefName);
|
|
||||||
return true;
|
|
||||||
} catch (e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the value of an individual preference.
|
|
||||||
*
|
|
||||||
* @param {string} prefName
|
|
||||||
* The preference to get the value of.
|
|
||||||
* @param {boolean/number/string} defaultValue
|
|
||||||
* The default value if preference cannot be found.
|
|
||||||
* @param {boolean/number/string} defaultBranch
|
|
||||||
* If true the value will be read from the default branch (optional)
|
|
||||||
* @param {string} interfaceType
|
|
||||||
* Interface to use for the complex value (optional)
|
|
||||||
* (nsILocalFile, nsISupportsString, nsIPrefLocalizedString)
|
|
||||||
*
|
|
||||||
* @return The value of the requested preference
|
|
||||||
* @type boolean/int/string/complex
|
|
||||||
*/
|
|
||||||
getPref : function preferences_getPref(prefName, defaultValue, defaultBranch,
|
|
||||||
interfaceType) {
|
|
||||||
try {
|
|
||||||
branch = defaultBranch ? this.defaultPrefBranch : this.prefBranch;
|
|
||||||
|
|
||||||
// If interfaceType has been set, handle it differently
|
|
||||||
if (interfaceType != undefined) {
|
|
||||||
return branch.getComplexValue(prefName, interfaceType);
|
|
||||||
}
|
|
||||||
|
|
||||||
switch (typeof defaultValue) {
|
|
||||||
case ('boolean'):
|
|
||||||
return branch.getBoolPref(prefName);
|
|
||||||
case ('string'):
|
|
||||||
return branch.getCharPref(prefName);
|
|
||||||
case ('number'):
|
|
||||||
return branch.getIntPref(prefName);
|
|
||||||
default:
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
} catch(e) {
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the value of an individual preference.
|
|
||||||
*
|
|
||||||
* @param {string} prefName
|
|
||||||
* The preference to set the value of.
|
|
||||||
* @param {boolean/number/string/complex} value
|
|
||||||
* The value to set the preference to.
|
|
||||||
* @param {string} interfaceType
|
|
||||||
* Interface to use for the complex value
|
|
||||||
* (nsILocalFile, nsISupportsString, nsIPrefLocalizedString)
|
|
||||||
*
|
|
||||||
* @return Returns if the value was successfully set.
|
|
||||||
* @type boolean
|
|
||||||
*/
|
|
||||||
setPref : function preferences_setPref(prefName, value, interfaceType) {
|
|
||||||
try {
|
|
||||||
switch (typeof value) {
|
|
||||||
case ('boolean'):
|
|
||||||
this.prefBranch.setBoolPref(prefName, value);
|
|
||||||
break;
|
|
||||||
case ('string'):
|
|
||||||
this.prefBranch.setCharPref(prefName, value);
|
|
||||||
break;
|
|
||||||
case ('number'):
|
|
||||||
this.prefBranch.setIntPref(prefName, value);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
this.prefBranch.setComplexValue(prefName, interfaceType, value);
|
|
||||||
}
|
|
||||||
} catch(e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open the preferences dialog and call the given handler
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController which is the opener of the preferences dialog
|
|
||||||
* @param {function} callback
|
|
||||||
* The callback handler to use to interact with the preference dialog
|
|
||||||
* @param {function} launcher
|
|
||||||
* (Optional) A callback handler to launch the preference dialog
|
|
||||||
*/
|
|
||||||
function openPreferencesDialog(controller, callback, launcher) {
|
|
||||||
if (!controller)
|
|
||||||
throw new Error("No controller given for Preferences Dialog");
|
|
||||||
if (typeof callback != "function")
|
|
||||||
throw new Error("No callback given for Preferences Dialog");
|
|
||||||
|
|
||||||
if (mozmill.isWindows) {
|
|
||||||
// Preference dialog is modal on windows, set up our callback
|
|
||||||
var prefModal = new modalDialog.modalDialog(controller.window);
|
|
||||||
prefModal.start(callback);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Launch the preference dialog
|
|
||||||
if (launcher) {
|
|
||||||
launcher();
|
|
||||||
} else {
|
|
||||||
mozmill.getPreferencesController();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mozmill.isWindows) {
|
|
||||||
prefModal.waitForDialog();
|
|
||||||
} else {
|
|
||||||
// Get the window type of the preferences window depending on the application
|
|
||||||
var prefWindowType = null;
|
|
||||||
switch (mozmill.Application) {
|
|
||||||
case "Thunderbird":
|
|
||||||
prefWindowType = "Mail:Preferences";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
prefWindowType = "Browser:Preferences";
|
|
||||||
}
|
|
||||||
|
|
||||||
utils.handleWindow("type", prefWindowType, callback);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export of variables
|
|
||||||
exports.preferences = preferences;
|
|
||||||
|
|
||||||
// Export of functions
|
|
||||||
exports.openPreferencesDialog = openPreferencesDialog;
|
|
||||||
|
|
||||||
// Export of classes
|
|
||||||
exports.preferencesDialog = preferencesDialog;
|
|
||||||
@@ -1,237 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileoverview
|
|
||||||
* The PrivateBrowsingAPI adds support for handling the private browsing mode.
|
|
||||||
*
|
|
||||||
* @version 1.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Include required modules
|
|
||||||
var modalDialog = require("modal-dialog");
|
|
||||||
var prefs = require("prefs");
|
|
||||||
var utils = require("utils");
|
|
||||||
|
|
||||||
// Preference for confirmation dialog when entering Private Browsing mode
|
|
||||||
const PB_NO_PROMPT_PREF = 'browser.privatebrowsing.dont_prompt_on_enter';
|
|
||||||
|
|
||||||
const gTimeout = 5000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new privateBrowsing instance.
|
|
||||||
*
|
|
||||||
* @class This class adds support for the Private Browsing mode
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController to use for the modal entry dialog
|
|
||||||
*/
|
|
||||||
function privateBrowsing(controller) {
|
|
||||||
this._controller = controller;
|
|
||||||
this._handler = null;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Menu item in the main menu to enter/leave Private Browsing mode
|
|
||||||
* @private
|
|
||||||
*/
|
|
||||||
this._pbMenuItem = new elementslib.Elem(this._controller.menus['tools-menu'].privateBrowsingItem);
|
|
||||||
this._pbTransitionItem = new elementslib.ID(this._controller.window.document, "Tools:PrivateBrowsing");
|
|
||||||
|
|
||||||
this.__defineGetter__('_pbs', function() {
|
|
||||||
delete this._pbs;
|
|
||||||
return this._pbs = Cc["@mozilla.org/privatebrowsing;1"].
|
|
||||||
getService(Ci.nsIPrivateBrowsingService);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Prototype definition of the privateBrowsing class
|
|
||||||
*/
|
|
||||||
privateBrowsing.prototype = {
|
|
||||||
/**
|
|
||||||
* Returns the controller of the current window
|
|
||||||
*
|
|
||||||
* @returns Mozmill Controller
|
|
||||||
* @type {MozMillController}
|
|
||||||
*/
|
|
||||||
get controller() {
|
|
||||||
return this._controller;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the state of the Private Browsing mode
|
|
||||||
*
|
|
||||||
* @returns Enabled state
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
get enabled() {
|
|
||||||
return this._pbs.privateBrowsingEnabled;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the state of the Private Browsing mode
|
|
||||||
*
|
|
||||||
* @param {boolean} value
|
|
||||||
* New state of the Private Browsing mode
|
|
||||||
*/
|
|
||||||
set enabled(value) {
|
|
||||||
this._pbs.privateBrowsingEnabled = value;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the callback handler for the confirmation dialog
|
|
||||||
*
|
|
||||||
* @param {function} callback
|
|
||||||
* Callback handler for the confirmation dialog
|
|
||||||
*/
|
|
||||||
set handler(callback) {
|
|
||||||
this._handler = callback;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the enabled state of the confirmation dialog
|
|
||||||
*
|
|
||||||
* @returns Enabled state
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
get showPrompt() {
|
|
||||||
return !prefs.preferences.getPref(PB_NO_PROMPT_PREF, true);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the enabled state of the confirmation dialog
|
|
||||||
*
|
|
||||||
* @param {boolean} value
|
|
||||||
* New enabled state of the confirmation dialog
|
|
||||||
*/
|
|
||||||
set showPrompt(value){
|
|
||||||
prefs.preferences.setPref(PB_NO_PROMPT_PREF, !value);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all the needed external DTD urls as an array
|
|
||||||
*
|
|
||||||
* @returns Array of external DTD urls
|
|
||||||
* @type [string]
|
|
||||||
*/
|
|
||||||
getDtds : function downloadManager_getDtds() {
|
|
||||||
var dtds = ["chrome://branding/locale/brand.dtd",
|
|
||||||
"chrome://browser/locale/browser.dtd",
|
|
||||||
"chrome://browser/locale/aboutPrivateBrowsing.dtd"];
|
|
||||||
return dtds;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Turn off Private Browsing mode and reset all changes
|
|
||||||
*/
|
|
||||||
reset : function privateBrowsing_reset() {
|
|
||||||
try {
|
|
||||||
this.stop(true);
|
|
||||||
} catch (ex) {
|
|
||||||
// Do a hard reset
|
|
||||||
this.enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
this.showPrompt = true;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start the Private Browsing mode
|
|
||||||
*
|
|
||||||
* @param {boolean} useShortcut
|
|
||||||
* Use the keyboard shortcut if true otherwise the menu entry is used
|
|
||||||
*/
|
|
||||||
start: function privateBrowsing_start(useShortcut) {
|
|
||||||
var dialog = null;
|
|
||||||
|
|
||||||
if (this.enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (this.showPrompt) {
|
|
||||||
dialog = new modalDialog.modalDialog(this._controller.window);
|
|
||||||
dialog.start(this._handler);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (useShortcut) {
|
|
||||||
var cmdKey = utils.getEntity(this.getDtds(), "privateBrowsingCmd.commandkey");
|
|
||||||
this._controller.keypress(null, cmdKey, {accelKey: true, shiftKey: true});
|
|
||||||
} else {
|
|
||||||
this._controller.click(this._pbMenuItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dialog) {
|
|
||||||
dialog.waitForDialog();
|
|
||||||
}
|
|
||||||
this.waitForTransistionComplete(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Stop the Private Browsing mode
|
|
||||||
*
|
|
||||||
* @param {boolean} useShortcut
|
|
||||||
* Use the keyboard shortcut if true otherwise the menu entry is used
|
|
||||||
*/
|
|
||||||
stop: function privateBrowsing_stop(useShortcut)
|
|
||||||
{
|
|
||||||
if (!this.enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (useShortcut) {
|
|
||||||
var privateBrowsingCmdKey = utils.getEntity(this.getDtds(), "privateBrowsingCmd.commandkey");
|
|
||||||
this._controller.keypress(null, privateBrowsingCmdKey, {accelKey: true, shiftKey: true});
|
|
||||||
} else {
|
|
||||||
this._controller.click(this._pbMenuItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.waitForTransistionComplete(false);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Waits until the transistion into or out of the Private Browsing mode happened
|
|
||||||
*
|
|
||||||
* @param {boolean} state
|
|
||||||
* Expected target state of the Private Browsing mode
|
|
||||||
*/
|
|
||||||
waitForTransistionComplete : function privateBrowsing_waitForTransitionComplete(state) {
|
|
||||||
// We have to wait until the transition has been finished
|
|
||||||
this._controller.waitForEval("subject.hasAttribute('disabled') == false", gTimeout, 100,
|
|
||||||
this._pbTransitionItem.getNode());
|
|
||||||
this._controller.waitForEval("subject.privateBrowsing.enabled == subject.state", gTimeout, 100,
|
|
||||||
{privateBrowsing: this, state: state});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export of classes
|
|
||||||
exports.privateBrowsing = privateBrowsing;
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
# Shared Modules #
|
|
||||||
|
|
||||||
Many common elements are referenced across the Mozmill tests. To make it easier
|
|
||||||
to work with these elements and execute common actions, shared modules have been
|
|
||||||
implemented. These modules contain helper classes and helper functions with a
|
|
||||||
focus on user interface. Some of these shared modules are unique to Firefox,
|
|
||||||
while others can also be used in other applications based on the Gecko platform.
|
|
||||||
|
|
||||||
For more information on Shared Modules visit:
|
|
||||||
https://developer.mozilla.org/en/Mozmill_Tests/Shared_Modules
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Adrian Kalla <akalla@aviary.pl>
|
|
||||||
* Axel Hecht <axel@pike.org>
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
// Include the required modules
|
|
||||||
var utils = require("utils");
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This function creates a screenshot of the window provided in the given
|
|
||||||
* controller and highlights elements from the coordinates provided in the
|
|
||||||
* given boxes-array.
|
|
||||||
*
|
|
||||||
* @param {array of array of int} boxes
|
|
||||||
* @param {MozmillController} controller
|
|
||||||
*/
|
|
||||||
function create(controller, boxes) {
|
|
||||||
var doc = controller.window.document;
|
|
||||||
var maxWidth = doc.documentElement.boxObject.width;
|
|
||||||
var maxHeight = doc.documentElement.boxObject.height;
|
|
||||||
var rect = [];
|
|
||||||
for (var i = 0, j = boxes.length; i < j; ++i) {
|
|
||||||
rect = boxes[i];
|
|
||||||
if (rect[0] + rect[2] > maxWidth) maxWidth = rect[0] + rect[2];
|
|
||||||
if (rect[1] + rect[3] > maxHeight) maxHeight = rect[1] + rect[3];
|
|
||||||
}
|
|
||||||
var canvas = doc.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
|
|
||||||
var width = doc.documentElement.boxObject.width;
|
|
||||||
var height = doc.documentElement.boxObject.height;
|
|
||||||
canvas.width = maxWidth;
|
|
||||||
canvas.height = maxHeight;
|
|
||||||
var ctx = canvas.getContext("2d");
|
|
||||||
ctx.clearRect(0,0, canvas.width, canvas.height);
|
|
||||||
ctx.save();
|
|
||||||
ctx.drawWindow(controller.window, 0, 0, width, height, "rgb(0,0,0)");
|
|
||||||
ctx.restore();
|
|
||||||
ctx.save();
|
|
||||||
ctx.fillStyle = "rgba(255,0,0,0.4)";
|
|
||||||
for (var i = 0, j = boxes.length; i < j; ++i) {
|
|
||||||
rect = boxes[i];
|
|
||||||
ctx.fillRect(rect[0], rect[1], rect[2], rect[3]);
|
|
||||||
}
|
|
||||||
ctx.restore();
|
|
||||||
|
|
||||||
_saveCanvas(canvas);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Saves a given Canvas object to a file.
|
|
||||||
* The path to save the file under should be given on the command line. If not,
|
|
||||||
* it will be saved in the temporary folder of the system.
|
|
||||||
*
|
|
||||||
* @param {canvas} canvas
|
|
||||||
*/
|
|
||||||
function _saveCanvas(canvas) {
|
|
||||||
// Use the path given on the command line and saved under
|
|
||||||
// persisted.screenshotPath, if available. If not, use the path to the
|
|
||||||
// temporary folder as a fallback.
|
|
||||||
var file = null;
|
|
||||||
if ("screenshotPath" in persisted) {
|
|
||||||
file = Cc["@mozilla.org/file/local;1"].createInstance(Ci.nsILocalFile);
|
|
||||||
file.initWithPath(persisted.screenshotPath);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
file = Cc["@mozilla.org/file/directory_service;1"].
|
|
||||||
getService(Ci.nsIProperties).
|
|
||||||
get("TmpD", Ci.nsIFile);
|
|
||||||
}
|
|
||||||
|
|
||||||
var fileName = utils.appInfo.name + "-" +
|
|
||||||
utils.appInfo.locale + "." +
|
|
||||||
utils.appInfo.version + "." +
|
|
||||||
utils.appInfo.buildID + "." +
|
|
||||||
utils.appInfo.os + ".png";
|
|
||||||
file.append(fileName);
|
|
||||||
|
|
||||||
// if a file already exists, don't overwrite it and create a new name
|
|
||||||
file.createUnique(Ci.nsIFile.NORMAL_FILE_TYPE, parseInt("0666", 8));
|
|
||||||
|
|
||||||
// create a data url from the canvas and then create URIs of the source
|
|
||||||
// and targets
|
|
||||||
var io = Cc["@mozilla.org/network/io-service;1"].getService(Ci.nsIIOService);
|
|
||||||
var source = io.newURI(canvas.toDataURL("image/png", ""), "UTF8", null);
|
|
||||||
var target = io.newFileURI(file)
|
|
||||||
|
|
||||||
// prepare to save the canvas data
|
|
||||||
var wbPersist = Cc["@mozilla.org/embedding/browser/nsWebBrowserPersist;1"].
|
|
||||||
createInstance(Ci.nsIWebBrowserPersist);
|
|
||||||
|
|
||||||
wbPersist.persistFlags = Ci.nsIWebBrowserPersist.PERSIST_FLAGS_REPLACE_EXISTING_FILES;
|
|
||||||
wbPersist.persistFlags |= Ci.nsIWebBrowserPersist.PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION;
|
|
||||||
|
|
||||||
// save the canvas data to the file
|
|
||||||
wbPersist.saveURI(source, null, null, null, null, file);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export of functions
|
|
||||||
exports.create = create;
|
|
||||||
@@ -1,836 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileoverview
|
|
||||||
* The SearchAPI adds support for search related functions like the search bar.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Include required modules
|
|
||||||
var modalDialog = require("modal-dialog");
|
|
||||||
var utils = require("utils");
|
|
||||||
var widgets = require("widgets");
|
|
||||||
|
|
||||||
const TIMEOUT = 5000;
|
|
||||||
const TIMEOUT_REQUEST_SUGGESTIONS = 750;
|
|
||||||
|
|
||||||
// Helper lookup constants for the engine manager elements
|
|
||||||
const MANAGER_BUTTONS = '/id("engineManager")/anon({"anonid":"buttons"})';
|
|
||||||
|
|
||||||
// Helper lookup constants for the search bar elements
|
|
||||||
const NAV_BAR = '/id("main-window")/id("tab-view-deck")/{"flex":"1"}' +
|
|
||||||
'/id("navigator-toolbox")/id("nav-bar")';
|
|
||||||
const SEARCH_BAR = NAV_BAR + '/id("search-container")/id("searchbar")';
|
|
||||||
const SEARCH_TEXTBOX = SEARCH_BAR + '/anon({"anonid":"searchbar-textbox"})';
|
|
||||||
const SEARCH_DROPDOWN = SEARCH_TEXTBOX + '/[0]/anon({"anonid":"searchbar-engine-button"})';
|
|
||||||
const SEARCH_POPUP = SEARCH_DROPDOWN + '/anon({"anonid":"searchbar-popup"})';
|
|
||||||
const SEARCH_INPUT = SEARCH_TEXTBOX + '/anon({"class":"autocomplete-textbox-container"})' +
|
|
||||||
'/anon({"anonid":"textbox-input-box"})' +
|
|
||||||
'/anon({"anonid":"input"})';
|
|
||||||
const SEARCH_CONTEXT = SEARCH_TEXTBOX + '/anon({"anonid":"textbox-input-box"})' +
|
|
||||||
'/anon({"anonid":"input-box-contextmenu"})';
|
|
||||||
const SEARCH_GO_BUTTON = SEARCH_TEXTBOX + '/anon({"class":"search-go-container"})' +
|
|
||||||
'/anon({"class":"search-go-button"})';
|
|
||||||
const SEARCH_AUTOCOMPLETE = '/id("main-window")/id("mainPopupSet")/id("PopupAutoComplete")';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController of the engine manager
|
|
||||||
*/
|
|
||||||
function engineManager(controller)
|
|
||||||
{
|
|
||||||
this._controller = controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search Manager class
|
|
||||||
*/
|
|
||||||
engineManager.prototype = {
|
|
||||||
/**
|
|
||||||
* Get the controller of the associated engine manager dialog
|
|
||||||
*
|
|
||||||
* @returns Controller of the browser window
|
|
||||||
* @type MozMillController
|
|
||||||
*/
|
|
||||||
get controller()
|
|
||||||
{
|
|
||||||
return this._controller;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the list of search engines
|
|
||||||
*
|
|
||||||
* @returns List of engines
|
|
||||||
* @type object
|
|
||||||
*/
|
|
||||||
get engines() {
|
|
||||||
var engines = [ ];
|
|
||||||
var tree = this.getElement({type: "engine_list"}).getNode();
|
|
||||||
|
|
||||||
for (var ii = 0; ii < tree.view.rowCount; ii ++) {
|
|
||||||
engines.push({name: tree.view.getCellText(ii, tree.columns.getColumnAt(0)),
|
|
||||||
keyword: tree.view.getCellText(ii, tree.columns.getColumnAt(1))});
|
|
||||||
}
|
|
||||||
|
|
||||||
return engines;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the name of the selected search engine
|
|
||||||
*
|
|
||||||
* @returns Name of the selected search engine
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
get selectedEngine() {
|
|
||||||
var treeNode = this.getElement({type: "engine_list"}).getNode();
|
|
||||||
|
|
||||||
if(this.selectedIndex != -1) {
|
|
||||||
return treeNode.view.getCellText(this.selectedIndex,
|
|
||||||
treeNode.columns.getColumnAt(0));
|
|
||||||
} else {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select the engine with the given name
|
|
||||||
*
|
|
||||||
* @param {string} name
|
|
||||||
* Name of the search engine to select
|
|
||||||
*/
|
|
||||||
set selectedEngine(name) {
|
|
||||||
var treeNode = this.getElement({type: "engine_list"}).getNode();
|
|
||||||
|
|
||||||
for (var ii = 0; ii < treeNode.view.rowCount; ii ++) {
|
|
||||||
if (name == treeNode.view.getCellText(ii, treeNode.columns.getColumnAt(0))) {
|
|
||||||
this.selectedIndex = ii;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the index of the selected search engine
|
|
||||||
*
|
|
||||||
* @returns Index of the selected search engine
|
|
||||||
* @type number
|
|
||||||
*/
|
|
||||||
get selectedIndex() {
|
|
||||||
var tree = this.getElement({type: "engine_list"});
|
|
||||||
var treeNode = tree.getNode();
|
|
||||||
|
|
||||||
return treeNode.view.selection.currentIndex;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select the engine with the given index
|
|
||||||
*
|
|
||||||
* @param {number} index
|
|
||||||
* Index of the search engine to select
|
|
||||||
*/
|
|
||||||
set selectedIndex(index) {
|
|
||||||
var tree = this.getElement({type: "engine_list"});
|
|
||||||
var treeNode = tree.getNode();
|
|
||||||
|
|
||||||
if (index < treeNode.view.rowCount) {
|
|
||||||
widgets.clickTreeCell(this._controller, tree, index, 0, {});
|
|
||||||
}
|
|
||||||
|
|
||||||
this._controller.waitForEval("subject.manager.selectedIndex == subject.newIndex", TIMEOUT, 100,
|
|
||||||
{manager: this, newIndex: index});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets the suggestions enabled state
|
|
||||||
*/
|
|
||||||
get suggestionsEnabled() {
|
|
||||||
var checkbox = this.getElement({type: "suggest"});
|
|
||||||
|
|
||||||
return checkbox.getNode().checked;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Sets the suggestions enabled state
|
|
||||||
*/
|
|
||||||
set suggestionsEnabled(state) {
|
|
||||||
var checkbox = this.getElement({type: "suggest"});
|
|
||||||
this._controller.check(checkbox, state);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the engine manager
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
* @param {boolean} saveChanges
|
|
||||||
* (Optional) If true the OK button is clicked otherwise Cancel
|
|
||||||
*/
|
|
||||||
close : function preferencesDialog_close(saveChanges) {
|
|
||||||
saveChanges = (saveChanges == undefined) ? false : saveChanges;
|
|
||||||
|
|
||||||
var button = this.getElement({type: "button", subtype: (saveChanges ? "accept" : "cancel")});
|
|
||||||
this._controller.click(button);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Edit the keyword associated to a search engine
|
|
||||||
*
|
|
||||||
* @param {string} name
|
|
||||||
* Name of the engine to remove
|
|
||||||
* @param {function} handler
|
|
||||||
* Callback function for Engine Manager
|
|
||||||
*/
|
|
||||||
editKeyword : function engineManager_editKeyword(name, handler)
|
|
||||||
{
|
|
||||||
// Select the search engine
|
|
||||||
this.selectedEngine = name;
|
|
||||||
|
|
||||||
// Setup the modal dialog handler
|
|
||||||
md = new modalDialog.modalDialog(this._controller.window);
|
|
||||||
md.start(handler);
|
|
||||||
|
|
||||||
var button = this.getElement({type: "engine_button", subtype: "edit"});
|
|
||||||
this._controller.click(button);
|
|
||||||
md.waitForDialog();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all the needed external DTD urls as an array
|
|
||||||
*
|
|
||||||
* @returns Array of external DTD urls
|
|
||||||
* @type [string]
|
|
||||||
*/
|
|
||||||
getDtds : function engineManager_getDtds() {
|
|
||||||
var dtds = ["chrome://browser/locale/engineManager.dtd"];
|
|
||||||
return dtds;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an UI element based on the given spec
|
|
||||||
*
|
|
||||||
* @param {object} spec
|
|
||||||
* Information of the UI element which should be retrieved
|
|
||||||
* type: General type information
|
|
||||||
* subtype: Specific element or property
|
|
||||||
* value: Value of the element or property
|
|
||||||
* @returns Element which has been created
|
|
||||||
* @type ElemBase
|
|
||||||
*/
|
|
||||||
getElement : function engineManager_getElement(spec) {
|
|
||||||
var elem = null;
|
|
||||||
|
|
||||||
switch(spec.type) {
|
|
||||||
/**
|
|
||||||
* subtype: subtype to match
|
|
||||||
* value: value to match
|
|
||||||
*/
|
|
||||||
case "more_engines":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "addEngines");
|
|
||||||
break;
|
|
||||||
case "button":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, MANAGER_BUTTONS +
|
|
||||||
'/{"dlgtype":"' + spec.subtype + '"}');
|
|
||||||
break;
|
|
||||||
case "engine_button":
|
|
||||||
switch(spec.subtype) {
|
|
||||||
case "down":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "dn");
|
|
||||||
break;
|
|
||||||
case "edit":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "edit");
|
|
||||||
break;
|
|
||||||
case "remove":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "remove");
|
|
||||||
break;
|
|
||||||
case "up":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "up");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "engine_list":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "engineList");
|
|
||||||
break;
|
|
||||||
case "suggest":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "enableSuggest");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown element type - " + spec.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clicks the "Get more search engines..." link
|
|
||||||
*/
|
|
||||||
getMoreSearchEngines : function engineManager_getMoreSearchEngines() {
|
|
||||||
var link = this.getElement({type: "more_engines"});
|
|
||||||
this._controller.click(link);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move down the engine with the given name
|
|
||||||
*
|
|
||||||
* @param {string} name
|
|
||||||
* Name of the engine to remove
|
|
||||||
*/
|
|
||||||
moveDownEngine : function engineManager_moveDownEngine(name) {
|
|
||||||
this.selectedEngine = name;
|
|
||||||
var index = this.selectedIndex;
|
|
||||||
|
|
||||||
var button = this.getElement({type: "engine_button", subtype: "down"});
|
|
||||||
this._controller.click(button);
|
|
||||||
|
|
||||||
this._controller.waitForEval("subject.manager.selectedIndex == subject.oldIndex + 1", TIMEOUT, 100,
|
|
||||||
{manager: this, oldIndex: index});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Move up the engine with the given name
|
|
||||||
*
|
|
||||||
* @param {string} name
|
|
||||||
* Name of the engine to remove
|
|
||||||
*/
|
|
||||||
moveUpEngine : function engineManager_moveUpEngine(name) {
|
|
||||||
this.selectedEngine = name;
|
|
||||||
var index = this.selectedIndex;
|
|
||||||
|
|
||||||
var button = this.getElement({type: "engine_button", subtype: "up"});
|
|
||||||
this._controller.click(button);
|
|
||||||
|
|
||||||
this._controller.waitForEval("subject.manager.selectedIndex == subject.oldIndex - 1", TIMEOUT, 100,
|
|
||||||
{manager: this, oldIndex: index});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the engine with the given name
|
|
||||||
*
|
|
||||||
* @param {string} name
|
|
||||||
* Name of the engine to remove
|
|
||||||
*/
|
|
||||||
removeEngine : function engineManager_removeEngine(name) {
|
|
||||||
this.selectedEngine = name;
|
|
||||||
|
|
||||||
var button = this.getElement({type: "engine_button", subtype: "remove"});
|
|
||||||
this._controller.click(button);
|
|
||||||
|
|
||||||
this._controller.waitForEval("subject.manager.selectedEngine != subject.removedEngine", TIMEOUT, 100,
|
|
||||||
{manager: this, removedEngine: name});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restores the defaults for search engines
|
|
||||||
*/
|
|
||||||
restoreDefaults : function engineManager_restoreDefaults() {
|
|
||||||
var button = this.getElement({type: "button", subtype: "extra2"});
|
|
||||||
this._controller.click(button);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController of the browser window to operate on
|
|
||||||
*/
|
|
||||||
function searchBar(controller)
|
|
||||||
{
|
|
||||||
this._controller = controller;
|
|
||||||
this._bss = Cc["@mozilla.org/browser/search-service;1"]
|
|
||||||
.getService(Ci.nsIBrowserSearchService);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Search Manager class
|
|
||||||
*/
|
|
||||||
searchBar.prototype = {
|
|
||||||
/**
|
|
||||||
* Get the controller of the associated browser window
|
|
||||||
*
|
|
||||||
* @returns Controller of the browser window
|
|
||||||
* @type MozMillController
|
|
||||||
*/
|
|
||||||
get controller()
|
|
||||||
{
|
|
||||||
return this._controller;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the names of all installed engines
|
|
||||||
*/
|
|
||||||
get engines()
|
|
||||||
{
|
|
||||||
var engines = [ ];
|
|
||||||
var popup = this.getElement({type: "searchBar_dropDownPopup"});
|
|
||||||
|
|
||||||
for (var ii = 0; ii < popup.getNode().childNodes.length; ii++) {
|
|
||||||
var entry = popup.getNode().childNodes[ii];
|
|
||||||
if (entry.className.indexOf("searchbar-engine") != -1) {
|
|
||||||
engines.push({name: entry.id,
|
|
||||||
selected: entry.selected,
|
|
||||||
tooltipText: entry.getAttribute('tooltiptext')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return engines;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the search engines drop down open state
|
|
||||||
*/
|
|
||||||
get enginesDropDownOpen()
|
|
||||||
{
|
|
||||||
var popup = this.getElement({type: "searchBar_dropDownPopup"});
|
|
||||||
return popup.getNode().state != "closed";
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Set the search engines drop down open state
|
|
||||||
*/
|
|
||||||
set enginesDropDownOpen(newState)
|
|
||||||
{
|
|
||||||
if (this.enginesDropDownOpen != newState) {
|
|
||||||
var button = this.getElement({type: "searchBar_dropDown"});
|
|
||||||
this._controller.click(button);
|
|
||||||
|
|
||||||
this._controller.waitForEval("subject.searchBar.enginesDropDownOpen == subject.newState", TIMEOUT, 100,
|
|
||||||
{searchBar: this, newState: newState });
|
|
||||||
this._controller.sleep(0);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the names of all installable engines
|
|
||||||
*/
|
|
||||||
get installableEngines()
|
|
||||||
{
|
|
||||||
var engines = [ ];
|
|
||||||
var popup = this.getElement({type: "searchBar_dropDownPopup"});
|
|
||||||
|
|
||||||
for (var ii = 0; ii < popup.getNode().childNodes.length; ii++) {
|
|
||||||
var entry = popup.getNode().childNodes[ii];
|
|
||||||
if (entry.className.indexOf("addengine-item") != -1) {
|
|
||||||
engines.push({name: entry.getAttribute('title'),
|
|
||||||
selected: entry.selected,
|
|
||||||
tooltipText: entry.getAttribute('tooltiptext')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return engines;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently selected search engine
|
|
||||||
*
|
|
||||||
* @return Name of the currently selected engine
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
get selectedEngine()
|
|
||||||
{
|
|
||||||
// Open drop down which updates the list of search engines
|
|
||||||
var state = this.enginesDropDownOpen;
|
|
||||||
this.enginesDropDownOpen = true;
|
|
||||||
|
|
||||||
var engine = this.getElement({type: "engine", subtype: "selected", value: "true"});
|
|
||||||
this._controller.waitForElement(engine, TIMEOUT);
|
|
||||||
|
|
||||||
this.enginesDropDownOpen = state;
|
|
||||||
|
|
||||||
return engine.getNode().id;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select the search engine with the given name
|
|
||||||
*
|
|
||||||
* @param {string} name
|
|
||||||
* Name of the search engine to select
|
|
||||||
*/
|
|
||||||
set selectedEngine(name) {
|
|
||||||
// Open drop down and click on search engine
|
|
||||||
this.enginesDropDownOpen = true;
|
|
||||||
|
|
||||||
var engine = this.getElement({type: "engine", subtype: "id", value: name});
|
|
||||||
this._controller.waitThenClick(engine, TIMEOUT);
|
|
||||||
|
|
||||||
// Wait until the drop down has been closed
|
|
||||||
this._controller.waitForEval("subject.searchBar.enginesDropDownOpen == false", TIMEOUT, 100,
|
|
||||||
{searchBar: this});
|
|
||||||
|
|
||||||
this._controller.waitForEval("subject.searchBar.selectedEngine == subject.newEngine", TIMEOUT, 100,
|
|
||||||
{searchBar: this, newEngine: name});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns all the visible search engines (API call)
|
|
||||||
*/
|
|
||||||
get visibleEngines()
|
|
||||||
{
|
|
||||||
return this._bss.getVisibleEngines({});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if the correct target URL has been opened for the search
|
|
||||||
*
|
|
||||||
* @param {string} searchTerm
|
|
||||||
* Text which should be checked for
|
|
||||||
*/
|
|
||||||
checkSearchResultPage : function searchBar_checkSearchResultPage(searchTerm) {
|
|
||||||
// Retrieve the URL which is used for the currently selected search engine
|
|
||||||
var targetUrl = this._bss.currentEngine.getSubmission(searchTerm, null).uri;
|
|
||||||
var currentUrl = this._controller.tabs.activeTabWindow.document.location;
|
|
||||||
|
|
||||||
var domainRegex = /[^\.]+\.([^\.]+)\..+$/gi;
|
|
||||||
var targetDomainName = targetUrl.host.replace(domainRegex, "$1");
|
|
||||||
var currentDomainName = currentUrl.host.replace(domainRegex, "$1");
|
|
||||||
|
|
||||||
this._controller.assert(function () {
|
|
||||||
return currentDomainName === targetDomainName;
|
|
||||||
}, "Current domain name matches target domain name - got '" +
|
|
||||||
currentDomainName + "', expected '" + targetDomainName + "'");
|
|
||||||
|
|
||||||
// Check if search term is listed in URL
|
|
||||||
this._controller.assert(function () {
|
|
||||||
return currentUrl.href.toLowerCase().indexOf(searchTerm.toLowerCase()) != -1;
|
|
||||||
}, "Current URL contains the search term - got '" +
|
|
||||||
currentUrl.href.toLowerCase() + "', expected '" + searchTerm.toLowerCase() + "'");
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the search field
|
|
||||||
*/
|
|
||||||
clear : function searchBar_clear()
|
|
||||||
{
|
|
||||||
var activeElement = this._controller.window.document.activeElement;
|
|
||||||
|
|
||||||
var searchInput = this.getElement({type: "searchBar_input"});
|
|
||||||
var cmdKey = utils.getEntity(this.getDtds(), "selectAllCmd.key");
|
|
||||||
this._controller.keypress(searchInput, cmdKey, {accelKey: true});
|
|
||||||
this._controller.keypress(searchInput, 'VK_DELETE', {});
|
|
||||||
|
|
||||||
if (activeElement)
|
|
||||||
activeElement.focus();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Focus the search bar text field
|
|
||||||
*
|
|
||||||
* @param {object} event
|
|
||||||
* Specifies the event which has to be used to focus the search bar
|
|
||||||
*/
|
|
||||||
focus : function searchBar_focus(event)
|
|
||||||
{
|
|
||||||
var input = this.getElement({type: "searchBar_input"});
|
|
||||||
|
|
||||||
switch (event.type) {
|
|
||||||
case "click":
|
|
||||||
this._controller.click(input);
|
|
||||||
break;
|
|
||||||
case "shortcut":
|
|
||||||
if (mozmill.isLinux) {
|
|
||||||
var cmdKey = utils.getEntity(this.getDtds(), "searchFocusUnix.commandkey");
|
|
||||||
} else {
|
|
||||||
var cmdKey = utils.getEntity(this.getDtds(), "searchFocus.commandkey");
|
|
||||||
}
|
|
||||||
this._controller.keypress(null, cmdKey, {accelKey: true});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown element type - " + event.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if the search bar has the focus
|
|
||||||
var activeElement = this._controller.window.document.activeElement;
|
|
||||||
this._controller.assertJS("subject.isFocused == true",
|
|
||||||
{isFocused: input.getNode() == activeElement});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all the needed external DTD urls as an array
|
|
||||||
*
|
|
||||||
* @returns Array of external DTD urls
|
|
||||||
* @type [string]
|
|
||||||
*/
|
|
||||||
getDtds : function searchBar_getDtds() {
|
|
||||||
var dtds = ["chrome://browser/locale/browser.dtd"];
|
|
||||||
return dtds;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an UI element based on the given spec
|
|
||||||
*
|
|
||||||
* @param {object} spec
|
|
||||||
* Information of the UI element which should be retrieved
|
|
||||||
* type: General type information
|
|
||||||
* subtype: Specific element or property
|
|
||||||
* value: Value of the element or property
|
|
||||||
* @returns Element which has been created
|
|
||||||
* @type ElemBase
|
|
||||||
*/
|
|
||||||
getElement : function searchBar_getElement(spec) {
|
|
||||||
var elem = null;
|
|
||||||
|
|
||||||
switch(spec.type) {
|
|
||||||
/**
|
|
||||||
* subtype: subtype to match
|
|
||||||
* value: value to match
|
|
||||||
*/
|
|
||||||
case "engine":
|
|
||||||
// XXX: bug 555938 - Mozmill can't fetch the element via a lookup here.
|
|
||||||
// That means we have to grab it temporarily by iterating through all childs.
|
|
||||||
var popup = this.getElement({type: "searchBar_dropDownPopup"}).getNode();
|
|
||||||
for (var ii = 0; ii < popup.childNodes.length; ii++) {
|
|
||||||
var entry = popup.childNodes[ii];
|
|
||||||
if (entry.getAttribute(spec.subtype) == spec.value) {
|
|
||||||
elem = new elementslib.Elem(entry);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//elem = new elementslib.Lookup(this._controller.window.document, SEARCH_POPUP +
|
|
||||||
// '/anon({"' + spec.subtype + '":"' + spec.value + '"})');
|
|
||||||
break;
|
|
||||||
case "engine_manager":
|
|
||||||
// XXX: bug 555938 - Mozmill can't fetch the element via a lookup here.
|
|
||||||
// That means we have to grab it temporarily by iterating through all childs.
|
|
||||||
var popup = this.getElement({type: "searchBar_dropDownPopup"}).getNode();
|
|
||||||
for (var ii = popup.childNodes.length - 1; ii >= 0; ii--) {
|
|
||||||
var entry = popup.childNodes[ii];
|
|
||||||
if (entry.className == "open-engine-manager") {
|
|
||||||
elem = new elementslib.Elem(entry);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//elem = new elementslib.Lookup(this._controller.window.document, SEARCH_POPUP +
|
|
||||||
// '/anon({"anonid":"open-engine-manager"})');
|
|
||||||
break;
|
|
||||||
case "searchBar":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, SEARCH_BAR);
|
|
||||||
break;
|
|
||||||
case "searchBar_autoCompletePopup":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, SEARCH_AUTOCOMPLETE);
|
|
||||||
break;
|
|
||||||
case "searchBar_contextMenu":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, SEARCH_CONTEXT);
|
|
||||||
break;
|
|
||||||
case "searchBar_dropDown":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, SEARCH_DROPDOWN);
|
|
||||||
break;
|
|
||||||
case "searchBar_dropDownPopup":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, SEARCH_POPUP);
|
|
||||||
break;
|
|
||||||
case "searchBar_goButton":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, SEARCH_GO_BUTTON);
|
|
||||||
break;
|
|
||||||
case "searchBar_input":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, SEARCH_INPUT);
|
|
||||||
break;
|
|
||||||
case "searchBar_suggestions":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, SEARCH_AUTOCOMPLETE +
|
|
||||||
'/anon({"anonid":"tree"})');
|
|
||||||
break;
|
|
||||||
case "searchBar_textBox":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, SEARCH_TEXTBOX);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown element type - " + spec.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the search suggestions for the search term
|
|
||||||
*/
|
|
||||||
getSuggestions : function(searchTerm) {
|
|
||||||
var suggestions = [ ];
|
|
||||||
var popup = this.getElement({type: "searchBar_autoCompletePopup"});
|
|
||||||
var treeElem = this.getElement({type: "searchBar_suggestions"});
|
|
||||||
|
|
||||||
// XXX Bug 542990, Bug 392633
|
|
||||||
// Typing too fast can cause several issue like the suggestions not to appear.
|
|
||||||
// Lets type the letters one by one and wait for the popup or the timeout
|
|
||||||
for (var i = 0; i < searchTerm.length; i++) {
|
|
||||||
try {
|
|
||||||
this.type(searchTerm[i]);
|
|
||||||
this._controller.waitFor(function () {
|
|
||||||
return popup.getNode().state === 'open';
|
|
||||||
}, "", TIMEOUT_REQUEST_SUGGESTIONS);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
// We are not interested in handling the timeout for now
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// After entering the search term the suggestions have to be visible
|
|
||||||
this._controller.assert(function () {
|
|
||||||
return popup.getNode().state === 'open';
|
|
||||||
}, "Search suggestions are visible");
|
|
||||||
this._controller.waitForElement(treeElem, TIMEOUT);
|
|
||||||
|
|
||||||
// Get all suggestions
|
|
||||||
var tree = treeElem.getNode();
|
|
||||||
this._controller.waitForEval("subject.tree.view != null", TIMEOUT, 100,
|
|
||||||
{tree: tree});
|
|
||||||
for (var i = 0; i < tree.view.rowCount; i ++) {
|
|
||||||
suggestions.push(tree.view.getCellText(i, tree.columns.getColumnAt(0)));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Close auto-complete popup
|
|
||||||
this._controller.keypress(popup, "VK_ESCAPE", {});
|
|
||||||
this._controller.waitForEval("subject.popup.state == 'closed'", TIMEOUT, 100,
|
|
||||||
{popup: popup.getNode()});
|
|
||||||
|
|
||||||
return suggestions;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if a search engine is installed (API call)
|
|
||||||
*
|
|
||||||
* @param {string} name
|
|
||||||
* Name of the search engine to check
|
|
||||||
*/
|
|
||||||
isEngineInstalled : function searchBar_isEngineInstalled(name)
|
|
||||||
{
|
|
||||||
var engine = this._bss.getEngineByName(name);
|
|
||||||
return (engine != null);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open the Engine Manager
|
|
||||||
*
|
|
||||||
* @param {function} handler
|
|
||||||
* Callback function for Engine Manager
|
|
||||||
*/
|
|
||||||
openEngineManager : function searchBar_openEngineManager(handler)
|
|
||||||
{
|
|
||||||
this.enginesDropDownOpen = true;
|
|
||||||
var engineManager = this.getElement({type: "engine_manager"});
|
|
||||||
|
|
||||||
// Setup the modal dialog handler
|
|
||||||
md = new modalDialog.modalDialog(this._controller.window);
|
|
||||||
md.start(handler);
|
|
||||||
|
|
||||||
// XXX: Bug 555347 - Process any outstanding events before clicking the entry
|
|
||||||
this._controller.sleep(0);
|
|
||||||
this._controller.click(engineManager);
|
|
||||||
md.waitForDialog();
|
|
||||||
|
|
||||||
this._controller.assert(function () {
|
|
||||||
return this.enginesDropDownOpen == false;
|
|
||||||
}, "The search engine drop down menu has been closed", this);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove the search engine with the given name (API call)
|
|
||||||
*
|
|
||||||
* @param {string} name
|
|
||||||
* Name of the search engine to remove
|
|
||||||
*/
|
|
||||||
removeEngine : function searchBar_removeEngine(name)
|
|
||||||
{
|
|
||||||
if (this.isEngineInstalled(name)) {
|
|
||||||
var engine = this._bss.getEngineByName(name);
|
|
||||||
this._bss.removeEngine(engine);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restore the default set of search engines (API call)
|
|
||||||
*/
|
|
||||||
restoreDefaultEngines : function searchBar_restoreDefaults()
|
|
||||||
{
|
|
||||||
// XXX: Bug 556477 - Restore default sorting
|
|
||||||
this.openEngineManager(function(controller) {
|
|
||||||
var manager = new engineManager(controller);
|
|
||||||
|
|
||||||
// We have to do any action so the restore button gets enabled
|
|
||||||
manager.moveDownEngine(manager.engines[0].name);
|
|
||||||
manager.restoreDefaults();
|
|
||||||
manager.close(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Update the visibility status for each engine and reset the default engine
|
|
||||||
this._bss.restoreDefaultEngines();
|
|
||||||
this._bss.currentEngine = this._bss.defaultEngine;
|
|
||||||
|
|
||||||
// Clear any entered search term
|
|
||||||
this.clear();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Start a search with the given search term and check if the resulting URL
|
|
||||||
* contains the search term.
|
|
||||||
*
|
|
||||||
* @param {object} data
|
|
||||||
* Object which contains the search term and the action type
|
|
||||||
*/
|
|
||||||
search : function searchBar_search(data)
|
|
||||||
{
|
|
||||||
var searchBar = this.getElement({type: "searchBar"});
|
|
||||||
this.type(data.text);
|
|
||||||
|
|
||||||
switch (data.action) {
|
|
||||||
case "returnKey":
|
|
||||||
this._controller.keypress(searchBar, 'VK_RETURN', {});
|
|
||||||
break;
|
|
||||||
case "goButton":
|
|
||||||
default:
|
|
||||||
this._controller.click(this.getElement({type: "searchBar_goButton"}));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
this._controller.waitForPageLoad();
|
|
||||||
this.checkSearchResultPage(data.text);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Enter a search term into the search bar
|
|
||||||
*
|
|
||||||
* @param {string} searchTerm
|
|
||||||
* Text which should be searched for
|
|
||||||
*/
|
|
||||||
type : function searchBar_type(searchTerm) {
|
|
||||||
var searchBar = this.getElement({type: "searchBar"});
|
|
||||||
this._controller.type(searchBar, searchTerm);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Export of classes
|
|
||||||
exports.engineManager = engineManager;
|
|
||||||
exports.searchBar = searchBar;
|
|
||||||
@@ -1,318 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
* Aaron Train <aaron.train@gmail.com>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileoverview
|
|
||||||
* The SessionStoreAPI adds support for accessing session related elements and features
|
|
||||||
*
|
|
||||||
* @version 1.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Include required modules
|
|
||||||
var prefs = require("prefs");
|
|
||||||
var utils = require("utils");
|
|
||||||
var widgets = require("widgets");
|
|
||||||
|
|
||||||
// Session Store service
|
|
||||||
var sessionStoreService = Cc["@mozilla.org/browser/sessionstore;1"]
|
|
||||||
.getService(Ci.nsISessionStore);
|
|
||||||
|
|
||||||
// Preference for indicating the amount of restorable tabs
|
|
||||||
const SESSIONSTORE_MAXTABS_PREF = 'browser.sessionstore.max_tabs_undo';
|
|
||||||
|
|
||||||
const gTimeout = 5000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMill controller of the browser window to operate on.
|
|
||||||
*/
|
|
||||||
function aboutSessionRestore(controller)
|
|
||||||
{
|
|
||||||
this._controller = controller;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* This class handles the about:sessionrestore page.
|
|
||||||
*/
|
|
||||||
aboutSessionRestore.prototype = {
|
|
||||||
/**
|
|
||||||
* Returns the MozMill controller
|
|
||||||
*
|
|
||||||
* @returns Mozmill controller
|
|
||||||
* @type {MozMillController}
|
|
||||||
*/
|
|
||||||
get controller() {
|
|
||||||
return this._controller;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the tree which contains the windows and tabs
|
|
||||||
*
|
|
||||||
* @returns Tree with windows and tabs to restore
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
get tabList() {
|
|
||||||
return this.getElement({type: "tabList"});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all the needed external DTD urls as an array
|
|
||||||
*
|
|
||||||
* @returns Array of external DTD urls
|
|
||||||
* @type [string]
|
|
||||||
*/
|
|
||||||
getDtds : function aboutSessionRestore_getDtds() {
|
|
||||||
var dtds = ["chrome://browser/locale/browser.dtd",
|
|
||||||
"chrome://browser/locale/aboutSessionRestore.dtd"];
|
|
||||||
return dtds;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an UI element based on the given spec
|
|
||||||
*
|
|
||||||
* @param {object} spec
|
|
||||||
* Information of the UI element which should be retrieved
|
|
||||||
* type: General type information
|
|
||||||
* subtype: Specific element or property
|
|
||||||
* value: Value of the element or property
|
|
||||||
* @returns Element which has been created
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
getElement : function aboutSessionRestore_getElement(spec) {
|
|
||||||
var elem = null;
|
|
||||||
|
|
||||||
switch(spec.type) {
|
|
||||||
case "button_restoreSession":
|
|
||||||
elem = new elementslib.ID(this._controller.tabs.activeTab, "errorTryAgain");
|
|
||||||
break;
|
|
||||||
case "error_longDesc":
|
|
||||||
elem = new elementslib.ID(this._controller.tabs.activeTab, "errorLongDesc");
|
|
||||||
break;
|
|
||||||
case "error_pageContainer":
|
|
||||||
elem = new elementslib.ID(this._controller.tabs.activeTab, "errorPageContainer");
|
|
||||||
break;
|
|
||||||
case "error_shortDesc":
|
|
||||||
elem = new elementslib.ID(this._controller.tabs.activeTab, "errorShortDescText");
|
|
||||||
break;
|
|
||||||
case "error_title":
|
|
||||||
elem = new elementslib.ID(this._controller.tabs.activeTab, "errorTitleText");
|
|
||||||
break;
|
|
||||||
case "tabList":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "tabList");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown element type - " + spec.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current restore state of the given element
|
|
||||||
*
|
|
||||||
* @param {object} element
|
|
||||||
* Element which restore state should be retrieved
|
|
||||||
* @returns True if the element should be restored
|
|
||||||
* @type {boolean}
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
getRestoreState : function aboutSessionRestore_getRestoreState(element) {
|
|
||||||
var tree = this.tabList.getNode();
|
|
||||||
|
|
||||||
return tree.view.getCellValue(element.listIndex, tree.columns.getColumnAt(0));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get restorable tabs under the given window
|
|
||||||
*
|
|
||||||
* @param {object} window
|
|
||||||
* Window inside the tree
|
|
||||||
* @returns List of tabs
|
|
||||||
* @type {array of object}
|
|
||||||
*/
|
|
||||||
getTabs : function aboutSessionRestore_getTabs(window) {
|
|
||||||
var tabs = [ ];
|
|
||||||
var tree = this.tabList.getNode();
|
|
||||||
|
|
||||||
// Add entries when they are tabs (no container)
|
|
||||||
var ii = window.listIndex + 1;
|
|
||||||
while (ii < tree.view.rowCount && !tree.view.isContainer(ii)) {
|
|
||||||
tabs.push({
|
|
||||||
index: tabs.length,
|
|
||||||
listIndex : ii,
|
|
||||||
restore: tree.view.getCellValue(ii, tree.columns.getColumnAt(0)),
|
|
||||||
title: tree.view.getCellText(ii, tree.columns.getColumnAt(2))
|
|
||||||
});
|
|
||||||
ii++;
|
|
||||||
}
|
|
||||||
|
|
||||||
return tabs;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get restorable windows
|
|
||||||
*
|
|
||||||
* @returns List of windows
|
|
||||||
* @type {array of object}
|
|
||||||
*/
|
|
||||||
getWindows : function aboutSessionRestore_getWindows() {
|
|
||||||
var windows = [ ];
|
|
||||||
var tree = this.tabList.getNode();
|
|
||||||
|
|
||||||
for (var ii = 0; ii < tree.view.rowCount; ii++) {
|
|
||||||
if (tree.view.isContainer(ii)) {
|
|
||||||
windows.push({
|
|
||||||
index: windows.length,
|
|
||||||
listIndex : ii,
|
|
||||||
open: tree.view.isContainerOpen(ii),
|
|
||||||
restore: tree.view.getCellValue(ii, tree.columns.getColumnAt(0)),
|
|
||||||
title: tree.view.getCellText(ii, tree.columns.getColumnAt(2))
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return windows;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Toggles the restore state for the element
|
|
||||||
*
|
|
||||||
* @param {object} element
|
|
||||||
* Specifies the element which restore state should be toggled
|
|
||||||
*/
|
|
||||||
toggleRestoreState : function aboutSessionRestore_toggleRestoreState(element) {
|
|
||||||
var state = this.getRestoreState(element);
|
|
||||||
|
|
||||||
widgets.clickTreeCell(this._controller, this.tabList, element.listIndex, 0, {});
|
|
||||||
this._controller.sleep(0);
|
|
||||||
|
|
||||||
this._controller.assertJS("subject.newState != subject.oldState",
|
|
||||||
{newState : this.getRestoreState(element), oldState : state});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Resets the list of recently closed tabs by setting and clearing the user preference
|
|
||||||
*/
|
|
||||||
function resetRecentlyClosedTabs()
|
|
||||||
{
|
|
||||||
prefs.preferences.setPref(SESSIONSTORE_MAXTABS_PREF, 0);
|
|
||||||
prefs.preferences.clearUserPref(SESSIONSTORE_MAXTABS_PREF);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the number of restorable tabs for a given window
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
* @returns The number of restorable tabs in the window
|
|
||||||
*/
|
|
||||||
function getClosedTabCount(controller)
|
|
||||||
{
|
|
||||||
return sessionStoreService.getClosedTabCount(controller.window);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restores the tab which has been recently closed
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
* @param {object} event
|
|
||||||
* Specifies the event to use to execute the command
|
|
||||||
*/
|
|
||||||
function undoClosedTab(controller, event)
|
|
||||||
{
|
|
||||||
var count = sessionStoreService.getClosedTabCount(controller.window);
|
|
||||||
|
|
||||||
switch (event.type) {
|
|
||||||
case "menu":
|
|
||||||
throw new Error("Menu gets build dynamically and cannot be accessed.");
|
|
||||||
break;
|
|
||||||
case "shortcut":
|
|
||||||
var cmdKey = utils.getEntity(this.getDtds(), "tabCmd.commandkey");
|
|
||||||
controller.keypress(null, cmdKey, {accelKey: true, shiftKey: true});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 0)
|
|
||||||
controller.assertJS("subject.newTabCount < subject.oldTabCount",
|
|
||||||
{
|
|
||||||
newTabCount : sessionStoreService.getClosedTabCount(controller.window),
|
|
||||||
oldTabCount : count
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Restores the window which has been recently closed
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
* @param {object} event
|
|
||||||
* Specifies the event to use to execute the command
|
|
||||||
*/
|
|
||||||
function undoClosedWindow(controller, event)
|
|
||||||
{
|
|
||||||
var count = sessionStoreService.getClosedWindowCount(controller.window);
|
|
||||||
|
|
||||||
switch (event.type) {
|
|
||||||
case "menu":
|
|
||||||
throw new Error("Menu gets build dynamically and cannot be accessed.");
|
|
||||||
break;
|
|
||||||
case "shortcut":
|
|
||||||
var cmdKey = utils.getEntity(this.getDtds(), "newNavigatorCmd.key");
|
|
||||||
controller.keypress(null, cmdKey, {accelKey: true, shiftKey: true});
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (count > 0)
|
|
||||||
controller.assertJS("subject.newWindowCount < subject.oldWindowCount",
|
|
||||||
{
|
|
||||||
newWindowCount : sessionStoreService.getClosedWindowCount(controller.window),
|
|
||||||
oldWindowCount : count
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export of functions
|
|
||||||
exports.getClosedTabCount = getClosedTabCount;
|
|
||||||
exports.resetRecentlyClosedTabs = resetRecentlyClosedTabs;
|
|
||||||
exports.undoClosedTab = undoClosedTab;
|
|
||||||
exports.undoClosedWindow = undoClosedWindow;
|
|
||||||
|
|
||||||
// Export of classes
|
|
||||||
exports.aboutSessionRestore = aboutSessionRestore;
|
|
||||||
@@ -1,530 +0,0 @@
|
|||||||
/* * ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* **** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileoverview
|
|
||||||
* The SoftwareUpdateAPI adds support for an easy access to the update process.
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Include required modules
|
|
||||||
var prefs = require("prefs");
|
|
||||||
var utils = require("utils");
|
|
||||||
|
|
||||||
const gTimeoutUpdateCheck = 10000;
|
|
||||||
const gTimeoutUpdateDownload = 360000;
|
|
||||||
|
|
||||||
const PREF_DISABLED_ADDONS = "extensions.disabledAddons";
|
|
||||||
|
|
||||||
// Helper lookup constants for elements of the software update dialog
|
|
||||||
const WIZARD = '/id("updates")';
|
|
||||||
const WIZARD_BUTTONS = WIZARD + '/anon({"anonid":"Buttons"})';
|
|
||||||
const WIZARD_DECK = WIZARD + '/anon({"anonid":"Deck"})';
|
|
||||||
|
|
||||||
const WIZARD_PAGES = {
|
|
||||||
dummy: 'dummy',
|
|
||||||
checking: 'checking',
|
|
||||||
pluginUpdatesFound: 'pluginupdatesfound',
|
|
||||||
noUpdatesFound: 'noupdatesfound',
|
|
||||||
manualUpdate: 'manualUpdate',
|
|
||||||
incompatibleCheck: 'incompatibleCheck',
|
|
||||||
updatesFoundBasic: 'updatesfoundbasic',
|
|
||||||
updatesFoundBillboard: 'updatesfoundbillboard',
|
|
||||||
license: 'license',
|
|
||||||
incompatibleList: 'incompatibleList',
|
|
||||||
downloading: 'downloading',
|
|
||||||
errors: 'errors',
|
|
||||||
errorPatching: 'errorpatching',
|
|
||||||
finished: 'finished',
|
|
||||||
finishedBackground: 'finishedBackground',
|
|
||||||
installed: 'installed'
|
|
||||||
}
|
|
||||||
|
|
||||||
// On Mac there is another DOM structure used as on Windows and Linux
|
|
||||||
if (mozmill.isMac) {
|
|
||||||
var WIZARD_BUTTONS_BOX = WIZARD_BUTTONS +
|
|
||||||
'/anon({"flex":"1"})/{"class":"wizard-buttons-btm"}/';
|
|
||||||
var WIZARD_BUTTON = {
|
|
||||||
back: '{"dlgtype":"back"}',
|
|
||||||
next: '{"dlgtype":"next"}',
|
|
||||||
cancel: '{"dlgtype":"cancel"}',
|
|
||||||
finish: '{"dlgtype":"finish"}',
|
|
||||||
extra1: '{"dlgtype":"extra1"}',
|
|
||||||
extra2: '{"dlgtype":"extra2"}'
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
var WIZARD_BUTTONS_BOX = WIZARD_BUTTONS +
|
|
||||||
'/anon({"flex":"1"})/{"class":"wizard-buttons-box-2"}/';
|
|
||||||
var WIZARD_BUTTON = {
|
|
||||||
back: '{"dlgtype":"back"}',
|
|
||||||
next: 'anon({"anonid":"WizardButtonDeck"})/[1]/{"dlgtype":"next"}',
|
|
||||||
cancel: '{"dlgtype":"cancel"}',
|
|
||||||
finish: 'anon({"anonid":"WizardButtonDeck"})/[0]/{"dlgtype":"finish"}',
|
|
||||||
extra1: '{"dlgtype":"extra1"}',
|
|
||||||
extra2: '{"dlgtype":"extra2"}'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor for software update class
|
|
||||||
*/
|
|
||||||
function softwareUpdate() {
|
|
||||||
this._controller = null;
|
|
||||||
this._wizard = null;
|
|
||||||
this._downloadDuration = -1;
|
|
||||||
|
|
||||||
this._aus = Cc["@mozilla.org/updates/update-service;1"].
|
|
||||||
getService(Ci.nsIApplicationUpdateService);
|
|
||||||
this._ums = Cc["@mozilla.org/updates/update-manager;1"].
|
|
||||||
getService(Ci.nsIUpdateManager);
|
|
||||||
this._vc = Cc["@mozilla.org/xpcom/version-comparator;1"].
|
|
||||||
getService(Ci.nsIVersionComparator);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Class for software updates
|
|
||||||
*/
|
|
||||||
softwareUpdate.prototype = {
|
|
||||||
/**
|
|
||||||
* Returns the active update
|
|
||||||
*
|
|
||||||
* @returns The currently selected update
|
|
||||||
* @type nsIUpdate
|
|
||||||
*/
|
|
||||||
get activeUpdate() {
|
|
||||||
return this._ums.activeUpdate;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the user has permissions to run the software update
|
|
||||||
*
|
|
||||||
* @returns Status if the user has the permissions.
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
get allowed() {
|
|
||||||
return this._aus.canCheckForUpdates && this._aus.canApplyUpdates;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns information of the current build version
|
|
||||||
*/
|
|
||||||
get buildInfo() {
|
|
||||||
return {
|
|
||||||
buildid : utils.appInfo.buildID,
|
|
||||||
disabled_addons : prefs.preferences.getPref(PREF_DISABLED_ADDONS, ''),
|
|
||||||
locale : utils.appInfo.locale,
|
|
||||||
user_agent : utils.appInfo.userAgent,
|
|
||||||
version : utils.appInfo.version
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current update channel
|
|
||||||
*/
|
|
||||||
get channel() {
|
|
||||||
return prefs.preferences.getPref('app.update.channel', '');
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the controller of the associated engine manager dialog
|
|
||||||
*
|
|
||||||
* @returns Controller of the browser window
|
|
||||||
* @type MozMillController
|
|
||||||
*/
|
|
||||||
get controller() {
|
|
||||||
return this._controller;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the current step of the software update dialog wizard
|
|
||||||
*/
|
|
||||||
get currentPage() {
|
|
||||||
return this._wizard.getNode().getAttribute('currentpageid');
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns true if the offered update is a complete update
|
|
||||||
*/
|
|
||||||
get isCompleteUpdate() {
|
|
||||||
// Throw when isCompleteUpdate is called without an update. This should
|
|
||||||
// never happen except if the test is incorrectly written.
|
|
||||||
if (!this.activeUpdate)
|
|
||||||
throw new Error(arguments.callee.name + ": isCompleteUpdate called " +
|
|
||||||
"when activeUpdate is null!");
|
|
||||||
|
|
||||||
var patchCount = this.activeUpdate.patchCount;
|
|
||||||
if ((patchCount < 1) || (patchCount > 2)) {
|
|
||||||
throw new Error("An update must have one or two patches included.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// XXX: After Firefox 4 has been released and we do not have to test any
|
|
||||||
// beta release anymore uncomment out the following code
|
|
||||||
// if (this.activeUpdate.patchCount == 2) {
|
|
||||||
// var patch0URL = this.activeUpdate.getPatchAt(0).URL;
|
|
||||||
// var patch1URL = this.activeUpdate.getPatchAt(1).URL;
|
|
||||||
// Test that the update snippet created by releng doesn't have the same
|
|
||||||
// url for both patches (bug 514040).
|
|
||||||
// controller.assertJS("subject.patch0URL != subject.patch1URL",
|
|
||||||
// {patch0URL: patch0URL, patch1URL: patch1URL});
|
|
||||||
// }
|
|
||||||
|
|
||||||
return (this.activeUpdate.selectedPatch.type == "complete");
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns information of the active update in the queue.
|
|
||||||
*/
|
|
||||||
get patchInfo() {
|
|
||||||
this._controller.assert(function() {
|
|
||||||
return !!this.activeUpdate;
|
|
||||||
}, "An active update is in the queue.", this);
|
|
||||||
|
|
||||||
return {
|
|
||||||
buildid : this.activeUpdate.buildID,
|
|
||||||
channel : this.channel,
|
|
||||||
is_complete : this.isCompleteUpdate,
|
|
||||||
size : this.activeUpdate.selectedPatch.size,
|
|
||||||
type : this.activeUpdate.type,
|
|
||||||
url : this.activeUpdate.selectedPatch.finalURL || "n/a",
|
|
||||||
download_duration : this._downloadDuration,
|
|
||||||
version : this.activeUpdate.version
|
|
||||||
};
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the update type (minor or major)
|
|
||||||
*
|
|
||||||
* @returns The update type
|
|
||||||
*/
|
|
||||||
get updateType() {
|
|
||||||
return this.activeUpdate.type;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if updates have been found
|
|
||||||
*/
|
|
||||||
get updatesFound() {
|
|
||||||
return this.currentPage.indexOf("updatesfound") == 0;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks if an update has been applied correctly
|
|
||||||
*
|
|
||||||
* @param {object} updateData
|
|
||||||
* All the data collected during the update process
|
|
||||||
*/
|
|
||||||
assertUpdateApplied : function softwareUpdate_assertUpdateApplied(updateData) {
|
|
||||||
// Get the information from the last update
|
|
||||||
var info = updateData.updates[updateData.updateIndex];
|
|
||||||
|
|
||||||
// The upgraded version should be identical with the version given by
|
|
||||||
// the update and we shouldn't have run a downgrade
|
|
||||||
var check = this._vc.compare(info.build_post.version, info.build_pre.version);
|
|
||||||
this._controller.assert(function () {
|
|
||||||
return check >= 0;
|
|
||||||
}, "The version number of the upgraded build is higher or equal.");
|
|
||||||
|
|
||||||
// The build id should be identical with the one from the update
|
|
||||||
this._controller.assert(function () {
|
|
||||||
return info.build_post.buildid === info.patch.buildid;
|
|
||||||
}, "The build id is equal to the build id of the update.");
|
|
||||||
|
|
||||||
// If a target build id has been given, check if it matches the updated build
|
|
||||||
info.target_buildid = updateData.targetBuildID;
|
|
||||||
if (info.target_buildid) {
|
|
||||||
this._controller.assert(function () {
|
|
||||||
return info.build_post.buildid === info.target_buildid;
|
|
||||||
}, "Target build id matches id of updated build.");
|
|
||||||
}
|
|
||||||
|
|
||||||
// An upgrade should not change the builds locale
|
|
||||||
this._controller.assert(function () {
|
|
||||||
return info.build_post.locale === info.build_pre.locale;
|
|
||||||
}, "The locale of the updated build is identical to the original locale.");
|
|
||||||
|
|
||||||
// Check that no application-wide add-ons have been disabled
|
|
||||||
this._controller.assert(function () {
|
|
||||||
return info.build_post.disabled_addons === info.build_pre.disabled_addons;
|
|
||||||
}, "No application-wide add-ons have been disabled by the update.");
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the software update dialog
|
|
||||||
*/
|
|
||||||
closeDialog: function softwareUpdate_closeDialog() {
|
|
||||||
if (this._controller) {
|
|
||||||
this._controller.keypress(null, "VK_ESCAPE", {});
|
|
||||||
this._controller.sleep(500);
|
|
||||||
this._controller = null;
|
|
||||||
this._wizard = null;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Download the update of the given channel and type
|
|
||||||
* @param {string} channel
|
|
||||||
* Update channel to use
|
|
||||||
* @param {boolean} waitForFinish
|
|
||||||
* Sets if the function should wait until the download has been finished
|
|
||||||
* @param {number} timeout
|
|
||||||
* Timeout the download has to stop
|
|
||||||
*/
|
|
||||||
download : function softwareUpdate_download(channel, waitForFinish, timeout) {
|
|
||||||
waitForFinish = waitForFinish ? waitForFinish : true;
|
|
||||||
|
|
||||||
// Check that the correct channel has been set
|
|
||||||
this._controller.assert(function() {
|
|
||||||
return channel == this.channel;
|
|
||||||
}, "The current update channel is identical to the specified one.", this);
|
|
||||||
|
|
||||||
// Retrieve the timestamp, so we can measure the duration of the download
|
|
||||||
var startTime = Date.now();
|
|
||||||
|
|
||||||
// Click the next button
|
|
||||||
var next = this.getElement({type: "button", subtype: "next"});
|
|
||||||
this._controller.click(next);
|
|
||||||
|
|
||||||
// Wait for the download page - if it fails the update was already cached
|
|
||||||
try {
|
|
||||||
this.waitForWizardPage(WIZARD_PAGES.downloading);
|
|
||||||
|
|
||||||
if (waitForFinish)
|
|
||||||
this.waitforDownloadFinished(timeout);
|
|
||||||
} catch (ex) {
|
|
||||||
this.waitForWizardPage(WIZARD_PAGES.finished);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Calculate the duration in ms
|
|
||||||
this._downloadDuration = Date.now() - startTime;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Update the update.status file and set the status to 'failed:6'
|
|
||||||
*/
|
|
||||||
forceFallback : function softwareUpdate_forceFallback() {
|
|
||||||
var dirService = Cc["@mozilla.org/file/directory_service;1"].
|
|
||||||
getService(Ci.nsIProperties);
|
|
||||||
|
|
||||||
var updateDir;
|
|
||||||
var updateStatus;
|
|
||||||
|
|
||||||
// Check the global update folder first
|
|
||||||
try {
|
|
||||||
updateDir = dirService.get("UpdRootD", Ci.nsIFile);
|
|
||||||
updateDir.append("updates");
|
|
||||||
updateDir.append("0");
|
|
||||||
|
|
||||||
updateStatus = updateDir.clone();
|
|
||||||
updateStatus.append("update.status");
|
|
||||||
} catch (ex) {
|
|
||||||
}
|
|
||||||
|
|
||||||
if (updateStatus == undefined || !updateStatus.exists()) {
|
|
||||||
updateDir = dirService.get("XCurProcD", Ci.nsIFile);
|
|
||||||
updateDir.append("updates");
|
|
||||||
updateDir.append("0");
|
|
||||||
|
|
||||||
updateStatus = updateDir.clone();
|
|
||||||
updateStatus.append("update.status");
|
|
||||||
}
|
|
||||||
|
|
||||||
var foStream = Cc["@mozilla.org/network/file-output-stream;1"].
|
|
||||||
createInstance(Ci.nsIFileOutputStream);
|
|
||||||
var status = "failed: 6\n";
|
|
||||||
foStream.init(updateStatus, 0x02 | 0x08 | 0x20, -1, 0);
|
|
||||||
foStream.write(status, status.length);
|
|
||||||
foStream.close();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all the needed external DTD urls as an array
|
|
||||||
*
|
|
||||||
* @returns Array of external DTD urls
|
|
||||||
* @type [string]
|
|
||||||
*/
|
|
||||||
getDtds : function softwareUpdate_getDtds() {
|
|
||||||
var dtds = ["chrome://mozapps/locale/update/history.dtd",
|
|
||||||
"chrome://mozapps/locale/update/updates.dtd"]
|
|
||||||
return dtds;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an UI element based on the given spec
|
|
||||||
*
|
|
||||||
* @param {object} spec
|
|
||||||
* Information of the UI element which should be retrieved
|
|
||||||
* type: General type information
|
|
||||||
* subtype: Specific element or property
|
|
||||||
* value: Value of the element or property
|
|
||||||
* @returns Element which has been created
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
getElement : function softwareUpdate_getElement(spec) {
|
|
||||||
var elem = null;
|
|
||||||
|
|
||||||
switch(spec.type) {
|
|
||||||
/**
|
|
||||||
* subtype: subtype to match
|
|
||||||
* value: value to match
|
|
||||||
*/
|
|
||||||
case "button":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document,
|
|
||||||
WIZARD_BUTTONS_BOX + WIZARD_BUTTON[spec.subtype]);
|
|
||||||
break;
|
|
||||||
case "wizard":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, WIZARD);
|
|
||||||
break;
|
|
||||||
case "wizard_page":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, WIZARD_DECK +
|
|
||||||
'/id(' + spec.subtype + ')');
|
|
||||||
break;
|
|
||||||
case "download_progress":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "downloadProgress");
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown element type - " + spec.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open software update dialog
|
|
||||||
*
|
|
||||||
* @param {MozMillController} browserController
|
|
||||||
* Mozmill controller of the browser window
|
|
||||||
*/
|
|
||||||
openDialog: function softwareUpdate_openDialog(browserController) {
|
|
||||||
// XXX: After Firefox 4 has been released and we do not have to test any
|
|
||||||
// beta release anymore uncomment out the following code
|
|
||||||
|
|
||||||
// With version >= 4.0b7pre the update dialog is reachable from within the
|
|
||||||
// about window now.
|
|
||||||
var appVersion = utils.appInfo.version;
|
|
||||||
|
|
||||||
if (this._vc.compare(appVersion, "4.0b7pre") >= 0) {
|
|
||||||
// XXX: We can't open the about window, otherwise a parallel download of
|
|
||||||
// the update will let us fallback to a complete one all the time
|
|
||||||
|
|
||||||
// Open the about window and check the update button
|
|
||||||
//var aboutItem = new elementslib.Elem(browserController.menus.helpMenu.aboutName);
|
|
||||||
//browserController.click(aboutItem);
|
|
||||||
//
|
|
||||||
//utils.handleWindow("type", "Browser:About", function(controller) {
|
|
||||||
// // XXX: Bug 599290 - Check for updates has been completely relocated
|
|
||||||
// // into the about window. We can't check the in-about ui yet.
|
|
||||||
// var updateButton = new elementslib.ID(controller.window.document,
|
|
||||||
// "checkForUpdatesButton");
|
|
||||||
// //controller.click(updateButton);
|
|
||||||
// controller.waitForElement(updateButton, gTimeout);
|
|
||||||
//});
|
|
||||||
|
|
||||||
// For now just call the old ui until we have support for the about window.
|
|
||||||
var updatePrompt = Cc["@mozilla.org/updates/update-prompt;1"].
|
|
||||||
createInstance(Ci.nsIUpdatePrompt);
|
|
||||||
updatePrompt.checkForUpdates();
|
|
||||||
} else {
|
|
||||||
// For builds <4.0b7pre
|
|
||||||
updateItem = new elementslib.Elem(browserController.menus.helpMenu.checkForUpdates);
|
|
||||||
browserController.click(updateItem);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.waitForDialogOpen(browserController);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait that check for updates has been finished
|
|
||||||
* @param {number} timeout
|
|
||||||
*/
|
|
||||||
waitForCheckFinished : function softwareUpdate_waitForCheckFinished(timeout) {
|
|
||||||
timeout = timeout ? timeout : gTimeoutUpdateCheck;
|
|
||||||
|
|
||||||
this._controller.waitFor(function() {
|
|
||||||
return this.currentPage != WIZARD_PAGES.checking;
|
|
||||||
}, "Check for updates has been completed.", timeout, null, this);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for the software update dialog
|
|
||||||
*
|
|
||||||
* @param {MozMillController} browserController
|
|
||||||
* Mozmill controller of the browser window
|
|
||||||
*/
|
|
||||||
waitForDialogOpen : function softwareUpdate_waitForDialogOpen(browserController) {
|
|
||||||
this._controller = utils.handleWindow("type", "Update:Wizard",
|
|
||||||
undefined, false);
|
|
||||||
this._wizard = this.getElement({type: "wizard"});
|
|
||||||
|
|
||||||
this._controller.waitFor(function () {
|
|
||||||
return this.currentPage !== WIZARD_PAGES.dummy;
|
|
||||||
}, "Dummy wizard page has been made invisible - got " + this.currentPage,
|
|
||||||
undefined, undefined, this);
|
|
||||||
|
|
||||||
this._controller.window.focus();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait until the download has been finished
|
|
||||||
*
|
|
||||||
* @param {number} timeout
|
|
||||||
* Timeout the download has to stop
|
|
||||||
*/
|
|
||||||
waitforDownloadFinished: function softwareUpdate_waitForDownloadFinished(timeout) {
|
|
||||||
timeout = timeout ? timeout : gTimeoutUpdateDownload;
|
|
||||||
|
|
||||||
var progress = this.getElement({type: "download_progress"});
|
|
||||||
this._controller.waitFor(function () {
|
|
||||||
return progress.getNode().value === '100';
|
|
||||||
}, "Update has been finished downloading.", timeout);
|
|
||||||
|
|
||||||
this.waitForWizardPage(WIZARD_PAGES.finished);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Waits for the given page of the update dialog wizard
|
|
||||||
*/
|
|
||||||
waitForWizardPage : function softwareUpdate_waitForWizardPage(step) {
|
|
||||||
this._controller.waitFor(function () {
|
|
||||||
return this.currentPage === step;
|
|
||||||
}, "New wizard page has been selected - got " + this.currentPage +
|
|
||||||
", expected " + step, undefined, undefined, this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export of variables
|
|
||||||
exports.WIZARD_PAGES = WIZARD_PAGES;
|
|
||||||
|
|
||||||
// Export of classes
|
|
||||||
exports.softwareUpdate = softwareUpdate;
|
|
||||||
@@ -1,503 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
* Anthony Hughes <ahughes@mozilla.com>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileoverview
|
|
||||||
* The TabbedBrowsingAPI adds support for accessing and interacting with tab elements
|
|
||||||
*
|
|
||||||
* @version 1.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Include required modules
|
|
||||||
var utils = require("utils");
|
|
||||||
var prefs = require("prefs");
|
|
||||||
|
|
||||||
const TIMEOUT = 5000;
|
|
||||||
|
|
||||||
const PREF_TABS_ANIMATE = "browser.tabs.animate";
|
|
||||||
|
|
||||||
const TABS_VIEW = '/id("main-window")/id("tab-view-deck")/{"flex":"1"}';
|
|
||||||
const TABS_BROWSER = TABS_VIEW + '/id("browser")/id("appcontent")/id("content")';
|
|
||||||
const TABS_TOOLBAR = TABS_VIEW + '/id("navigator-toolbox")/id("TabsToolbar")';
|
|
||||||
const TABS_TABS = TABS_TOOLBAR + '/id("tabbrowser-tabs")';
|
|
||||||
const TABS_ARROW_SCROLLBOX = TABS_TABS + '/anon({"anonid":"arrowscrollbox"})';
|
|
||||||
const TABS_STRIP = TABS_ARROW_SCROLLBOX + '/anon({"anonid":"scrollbox"})/anon({"flex":"1"})';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close all tabs and open about:blank
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
*/
|
|
||||||
function closeAllTabs(controller)
|
|
||||||
{
|
|
||||||
var browser = new tabBrowser(controller);
|
|
||||||
browser.closeAllTabs();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check and return all open tabs with the specified URL
|
|
||||||
*
|
|
||||||
* @param {string} aUrl
|
|
||||||
* URL to check for
|
|
||||||
*
|
|
||||||
* @returns Array of tabs
|
|
||||||
*/
|
|
||||||
function getTabsWithURL(aUrl) {
|
|
||||||
var tabs = [ ];
|
|
||||||
|
|
||||||
var uri = utils.createURI(aUrl, null, null);
|
|
||||||
|
|
||||||
var wm = Cc["@mozilla.org/appshell/window-mediator;1"].
|
|
||||||
getService(Ci.nsIWindowMediator);
|
|
||||||
var winEnum = wm.getEnumerator("navigator:browser");
|
|
||||||
|
|
||||||
// Iterate through all windows
|
|
||||||
while (winEnum.hasMoreElements()) {
|
|
||||||
var window = winEnum.getNext();
|
|
||||||
|
|
||||||
// Don't check windows which are about to close or don't have gBrowser set
|
|
||||||
if (window.closed || !("gBrowser" in window))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Iterate through all tabs in the current window
|
|
||||||
var browsers = window.gBrowser.browsers;
|
|
||||||
for (var i = 0; i < browsers.length; i++) {
|
|
||||||
var browser = browsers[i];
|
|
||||||
if (browser.currentURI.equals(uri)) {
|
|
||||||
tabs.push({
|
|
||||||
controller : new mozmill.controller.MozMillController(window),
|
|
||||||
index : i
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return tabs;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMill controller of the window to operate on
|
|
||||||
*/
|
|
||||||
function tabBrowser(controller)
|
|
||||||
{
|
|
||||||
this._controller = controller;
|
|
||||||
this._tabs = this.getElement({type: "tabs"});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tabbed Browser class
|
|
||||||
*/
|
|
||||||
tabBrowser.prototype = {
|
|
||||||
/**
|
|
||||||
* Returns the MozMill controller
|
|
||||||
*
|
|
||||||
* @returns Mozmill controller
|
|
||||||
* @type {MozMillController}
|
|
||||||
*/
|
|
||||||
get controller() {
|
|
||||||
return this._controller;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the amount of open tabs
|
|
||||||
*
|
|
||||||
* @returns Number of tabs
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
get length() {
|
|
||||||
return this._tabs.getNode().itemCount;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the currently selected tab index
|
|
||||||
*
|
|
||||||
* @returns Index of currently selected tab
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
get selectedIndex() {
|
|
||||||
return this._tabs.getNode().selectedIndex;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Select the tab with the given index
|
|
||||||
*
|
|
||||||
* @param {number} index
|
|
||||||
* Index of the tab which should be selected
|
|
||||||
*/
|
|
||||||
set selectedIndex(index) {
|
|
||||||
this._controller.click(this.getTab(index));
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close all tabs of the window except the last one and open a blank page.
|
|
||||||
*/
|
|
||||||
closeAllTabs : function tabBrowser_closeAllTabs()
|
|
||||||
{
|
|
||||||
while (this._controller.tabs.length > 1) {
|
|
||||||
this.closeTab({type: "menu"});
|
|
||||||
}
|
|
||||||
|
|
||||||
this._controller.open("about:blank");
|
|
||||||
this._controller.waitForPageLoad();
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close an open tab
|
|
||||||
*
|
|
||||||
* @param {object} aEvent
|
|
||||||
* The event specifies how to close a tab
|
|
||||||
* Elements: type - Type of event (closeButton, menu, middleClick, shortcut)
|
|
||||||
* [optional - default: menu]
|
|
||||||
*/
|
|
||||||
closeTab : function tabBrowser_closeTab(aEvent) {
|
|
||||||
var event = aEvent || { };
|
|
||||||
var type = (event.type == undefined) ? "menu" : event.type;
|
|
||||||
|
|
||||||
// Disable tab closing animation for default behavior
|
|
||||||
prefs.preferences.setPref(PREF_TABS_ANIMATE, false);
|
|
||||||
|
|
||||||
// Add event listener to wait until the tab has been closed
|
|
||||||
var self = { closed: false };
|
|
||||||
function checkTabClosed() { self.closed = true; }
|
|
||||||
this._controller.window.addEventListener("TabClose", checkTabClosed, false);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case "closeButton":
|
|
||||||
var button = this.getElement({type: "tabs_tabCloseButton",
|
|
||||||
subtype: "tab", value: this.getTab()});
|
|
||||||
this._controller.click(button);
|
|
||||||
break;
|
|
||||||
case "menu":
|
|
||||||
var menuitem = new elementslib.Elem(this._controller.menus['file-menu'].menu_close);
|
|
||||||
this._controller.click(menuitem);
|
|
||||||
break;
|
|
||||||
case "middleClick":
|
|
||||||
var tab = this.getTab(event.index);
|
|
||||||
this._controller.middleClick(tab);
|
|
||||||
break;
|
|
||||||
case "shortcut":
|
|
||||||
var cmdKey = utils.getEntity(this.getDtds(), "closeCmd.key");
|
|
||||||
this._controller.keypress(null, cmdKey, {accelKey: true});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown event type - " + type);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
this._controller.waitForEval("subject.tab.closed == true", TIMEOUT, 100,
|
|
||||||
{tab: self});
|
|
||||||
} finally {
|
|
||||||
this._controller.window.removeEventListener("TabClose", checkTabClosed, false);
|
|
||||||
prefs.preferences.clearUserPref(PREF_TABS_ANIMATE);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all the needed external DTD urls as an array
|
|
||||||
*
|
|
||||||
* @returns Array of external DTD urls
|
|
||||||
* @type [string]
|
|
||||||
*/
|
|
||||||
getDtds : function tabBrowser_getDtds() {
|
|
||||||
var dtds = ["chrome://browser/locale/browser.dtd",
|
|
||||||
"chrome://browser/locale/tabbrowser.dtd",
|
|
||||||
"chrome://global/locale/global.dtd"];
|
|
||||||
return dtds;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an UI element based on the given spec
|
|
||||||
*
|
|
||||||
* @param {object} spec
|
|
||||||
* Information of the UI element which should be retrieved
|
|
||||||
* type: General type information
|
|
||||||
* subtype: Specific element or property
|
|
||||||
* value: Value of the element or property
|
|
||||||
* @returns Element which has been created
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
getElement : function tabBrowser_getElement(spec) {
|
|
||||||
var document = this._controller.window.document;
|
|
||||||
var elem = null;
|
|
||||||
|
|
||||||
switch(spec.type) {
|
|
||||||
/**
|
|
||||||
* subtype: subtype to match
|
|
||||||
* value: value to match
|
|
||||||
*/
|
|
||||||
case "tabs":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document,
|
|
||||||
TABS_TABS);
|
|
||||||
break;
|
|
||||||
case "tabs_allTabsButton":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document,
|
|
||||||
TABS_TOOLBAR + '/id("alltabs-button")');
|
|
||||||
break;
|
|
||||||
case "tabs_allTabsPopup":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, TABS_TOOLBAR +
|
|
||||||
'/id("alltabs-button")/id("alltabs-popup")');
|
|
||||||
break;
|
|
||||||
case "tabs_newTabButton":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document,
|
|
||||||
TABS_ARROW_SCROLLBOX + '/anon({"class":"tabs-newtab-button"})');
|
|
||||||
break;
|
|
||||||
case "tabs_scrollButton":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document,
|
|
||||||
TABS_ARROW_SCROLLBOX +
|
|
||||||
'/anon({"anonid":"scrollbutton-' + spec.subtype + '"})');
|
|
||||||
break;
|
|
||||||
case "tabs_strip":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, TABS_STRIP);
|
|
||||||
break;
|
|
||||||
case "tabs_tab":
|
|
||||||
switch (spec.subtype) {
|
|
||||||
case "index":
|
|
||||||
elem = new elementslib.Elem(this._tabs.getNode().getItemAtIndex(spec.value));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case "tabs_tabCloseButton":
|
|
||||||
var node = document.getAnonymousElementByAttribute(
|
|
||||||
spec.value.getNode(),
|
|
||||||
"anonid",
|
|
||||||
"close-button"
|
|
||||||
);
|
|
||||||
elem = new elementslib.Elem(node);
|
|
||||||
break;
|
|
||||||
case "tabs_tabFavicon":
|
|
||||||
var node = document.getAnonymousElementByAttribute(
|
|
||||||
spec.value.getNode(),
|
|
||||||
"class",
|
|
||||||
"tab-icon-image"
|
|
||||||
);
|
|
||||||
|
|
||||||
elem = new elementslib.Elem(node);
|
|
||||||
break;
|
|
||||||
case "tabs_tabPanel":
|
|
||||||
var panelId = spec.value.getNode().getAttribute("linkedpanel");
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, TABS_BROWSER +
|
|
||||||
'/anon({"anonid":"tabbox"})/anon({"anonid":"panelcontainer"})' +
|
|
||||||
'/{"id":"' + panelId + '"}');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown element type - " + spec.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the tab at the specified index
|
|
||||||
*
|
|
||||||
* @param {number} index
|
|
||||||
* Index of the tab
|
|
||||||
* @returns The requested tab
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
getTab : function tabBrowser_getTab(index) {
|
|
||||||
if (index === undefined)
|
|
||||||
index = this.selectedIndex;
|
|
||||||
|
|
||||||
return this.getElement({type: "tabs_tab", subtype: "index", value: index});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates the child element of the tab's notification bar
|
|
||||||
*
|
|
||||||
* @param {number} tabIndex
|
|
||||||
* (Optional) Index of the tab to check
|
|
||||||
* @param {string} elemString
|
|
||||||
* (Optional) Lookup string of the notification bar's child element
|
|
||||||
* @return The created child element
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
getTabPanelElement : function tabBrowser_getTabPanelElement(tabIndex, elemString)
|
|
||||||
{
|
|
||||||
var index = tabIndex ? tabIndex : this.selectedIndex;
|
|
||||||
var elemStr = elemString ? elemString : "";
|
|
||||||
|
|
||||||
// Get the tab panel and check if an element has to be fetched
|
|
||||||
var panel = this.getElement({type: "tabs_tabPanel", subtype: "tab", value: this.getTab(index)});
|
|
||||||
var elem = new elementslib.Lookup(this._controller.window.document, panel.expression + elemStr);
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open element (link) in a new tab
|
|
||||||
*
|
|
||||||
* @param {object} aEvent
|
|
||||||
* The event specifies how to open the element in a new tab
|
|
||||||
* Elements: type - Type of event (contextMenu, middleClick)
|
|
||||||
* [optional - default: middleClick]
|
|
||||||
* target - Element to click on
|
|
||||||
* [optional - default: -]
|
|
||||||
*/
|
|
||||||
openInNewTab : function tabBrowser_openInNewTab(aEvent) {
|
|
||||||
var event = aEvent || { };
|
|
||||||
var type = (event.type == undefined) ? "middleClick" : event.type;
|
|
||||||
var target = event.target;
|
|
||||||
|
|
||||||
// Disable tab closing animation for default behavior
|
|
||||||
prefs.preferences.setPref(PREF_TABS_ANIMATE, false);
|
|
||||||
|
|
||||||
// Add event listener to wait until the tab has been opened
|
|
||||||
var self = { opened: false };
|
|
||||||
function checkTabOpened() { self.opened = true; }
|
|
||||||
this._controller.window.addEventListener("TabOpen", checkTabOpened, false);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case "contextMenu":
|
|
||||||
var contextMenuItem = new elementslib.ID(this._controller.window.document,
|
|
||||||
"context-openlinkintab");
|
|
||||||
this._controller.rightClick(target);
|
|
||||||
this._controller.click(contextMenuItem);
|
|
||||||
utils.closeContentAreaContextMenu(this._controller);
|
|
||||||
break;
|
|
||||||
case "middleClick":
|
|
||||||
this._controller.middleClick(target);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown event type - " + type);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
this._controller.waitForEval("subject.tab.opened == true", TIMEOUT, 100,
|
|
||||||
{tab: self});
|
|
||||||
} finally {
|
|
||||||
this._controller.window.removeEventListener("TabOpen", checkTabOpened, false);
|
|
||||||
prefs.preferences.clearUserPref(PREF_TABS_ANIMATE);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a new tab
|
|
||||||
*
|
|
||||||
* @param {object} aEvent
|
|
||||||
* The event specifies how to open a new tab (menu, shortcut,
|
|
||||||
* Elements: type - Type of event (menu, newTabButton, shortcut, tabStrip)
|
|
||||||
* [optional - default: menu]
|
|
||||||
*/
|
|
||||||
openTab : function tabBrowser_openTab(aEvent) {
|
|
||||||
var event = aEvent || { };
|
|
||||||
var type = (event.type == undefined) ? "menu" : event.type;
|
|
||||||
|
|
||||||
// Disable tab closing animation for default behavior
|
|
||||||
prefs.preferences.setPref(PREF_TABS_ANIMATE, false);
|
|
||||||
|
|
||||||
// Add event listener to wait until the tab has been opened
|
|
||||||
var self = { opened: false };
|
|
||||||
function checkTabOpened() { self.opened = true; }
|
|
||||||
this._controller.window.addEventListener("TabOpen", checkTabOpened, false);
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case "menu":
|
|
||||||
var menuitem = new elementslib.Elem(this._controller.menus['file-menu'].menu_newNavigatorTab);
|
|
||||||
this._controller.click(menuitem);
|
|
||||||
break;
|
|
||||||
case "shortcut":
|
|
||||||
var cmdKey = utils.getEntity(this.getDtds(), "tabCmd.commandkey");
|
|
||||||
this._controller.keypress(null, cmdKey, {accelKey: true});
|
|
||||||
break;
|
|
||||||
case "newTabButton":
|
|
||||||
var newTabButton = this.getElement({type: "tabs_newTabButton"});
|
|
||||||
this._controller.click(newTabButton);
|
|
||||||
break;
|
|
||||||
case "tabStrip":
|
|
||||||
var tabStrip = this.getElement({type: "tabs_strip"});
|
|
||||||
|
|
||||||
// RTL-locales need to be treated separately
|
|
||||||
if (utils.getEntity(this.getDtds(), "locale.dir") == "rtl") {
|
|
||||||
// XXX: Workaround until bug 537968 has been fixed
|
|
||||||
this._controller.click(tabStrip, 100, 3);
|
|
||||||
// Todo: Calculate the correct x position
|
|
||||||
this._controller.doubleClick(tabStrip, 100, 3);
|
|
||||||
} else {
|
|
||||||
// XXX: Workaround until bug 537968 has been fixed
|
|
||||||
this._controller.click(tabStrip, tabStrip.getNode().clientWidth - 100, 3);
|
|
||||||
// Todo: Calculate the correct x position
|
|
||||||
this._controller.doubleClick(tabStrip, tabStrip.getNode().clientWidth - 100, 3);
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown event type - " + type);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
this._controller.waitForEval("subject.tab.opened == true", TIMEOUT, 100,
|
|
||||||
{tab: self});
|
|
||||||
} finally {
|
|
||||||
this._controller.window.removeEventListener("TabOpen", checkTabOpened, false);
|
|
||||||
prefs.preferences.clearUserPref(PREF_TABS_ANIMATE);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Waits for a particular tab panel element to display and stop animating
|
|
||||||
*
|
|
||||||
* @param {number} tabIndex
|
|
||||||
* Index of the tab to check
|
|
||||||
* @param {string} elemString
|
|
||||||
* Lookup string of the tab panel element
|
|
||||||
*/
|
|
||||||
waitForTabPanel: function tabBrowser_waitForTabPanel(tabIndex, elemString) {
|
|
||||||
// Get the specified tab panel element
|
|
||||||
var tabPanel = this.getTabPanelElement(tabIndex, elemString);
|
|
||||||
|
|
||||||
// Get the style information for the tab panel element
|
|
||||||
var style = this._controller.window.getComputedStyle(tabPanel.getNode(), null);
|
|
||||||
|
|
||||||
// Wait for the top margin to be 0px - ie. has stopped animating
|
|
||||||
// XXX: A notification bar starts at a negative pixel margin and drops down
|
|
||||||
// to 0px. This creates a race condition where a test may click
|
|
||||||
// before the notification bar appears at it's anticipated screen location
|
|
||||||
this._controller.waitFor(function () {
|
|
||||||
return style.marginTop == '0px';
|
|
||||||
}, "Expected notification bar to be visible: '" + elemString + "' ");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export of functions
|
|
||||||
exports.closeAllTabs = closeAllTabs;
|
|
||||||
exports.getTabsWithURL = getTabsWithURL;
|
|
||||||
|
|
||||||
// Export of classes
|
|
||||||
exports.tabBrowser = tabBrowser;
|
|
||||||
@@ -1,629 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2010
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
// Include required modules
|
|
||||||
var domUtils = require("dom-utils");
|
|
||||||
var tabs = require("tabs");
|
|
||||||
var utils = require("utils");
|
|
||||||
|
|
||||||
const TIMEOUT = 5000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*/
|
|
||||||
function tabView(aController) {
|
|
||||||
this._controller = aController;
|
|
||||||
this._tabView = null;
|
|
||||||
this._tabViewDoc = this._controller.window.document;
|
|
||||||
this._tabViewObject = this._controller.window.TabView;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Tab View class
|
|
||||||
*/
|
|
||||||
tabView.prototype = {
|
|
||||||
|
|
||||||
///////////////////////////////
|
|
||||||
// Global section
|
|
||||||
///////////////////////////////
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the MozMill controller
|
|
||||||
*
|
|
||||||
* @returns Mozmill controller
|
|
||||||
* @type {MozMillController}
|
|
||||||
*/
|
|
||||||
get controller() {
|
|
||||||
return this._controller;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the Tab View is open
|
|
||||||
*
|
|
||||||
* @returns True if the Tab View is open
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
get isOpen() {
|
|
||||||
var deck = this.getElement({type: "deck"});
|
|
||||||
return deck.getNode().getAttribute("selectedIndex") == "1";
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open the Tab View
|
|
||||||
*/
|
|
||||||
open : function tabView_open() {
|
|
||||||
var menuitem = new elementslib.Elem(this._controller.menus['view-menu'].menu_tabview);
|
|
||||||
this._controller.click(menuitem);
|
|
||||||
this.waitForOpened();
|
|
||||||
|
|
||||||
this._tabView = this.getElement({type: "tabView"});
|
|
||||||
this._tabViewDoc = this._tabView.getNode().webNavigation.document;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reset the Tab View settings for the current window
|
|
||||||
*/
|
|
||||||
reset : function tabView_reset() {
|
|
||||||
// Make sure to close TabView before resetting its ui
|
|
||||||
if (this.isOpen) {
|
|
||||||
this.close();
|
|
||||||
}
|
|
||||||
|
|
||||||
var self = this;
|
|
||||||
this._tabViewObject._initFrame(function () {
|
|
||||||
var contentWindow = self._tabViewObject._window;
|
|
||||||
contentWindow.UI.reset();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Make sure all tabs will be shown
|
|
||||||
Array.forEach(this._controller.window.gBrowser.tabs, function (tab) {
|
|
||||||
this._controller.window.gBrowser.showTab(tab);
|
|
||||||
}, this);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait until the Tab View has been opened
|
|
||||||
*/
|
|
||||||
waitForOpened : function tabView_waitForOpened() {
|
|
||||||
// Add event listener to wait until the tabview has been opened
|
|
||||||
var self = { opened: false };
|
|
||||||
function checkOpened() { self.opened = true; }
|
|
||||||
this._controller.window.addEventListener("tabviewshown", checkOpened, false);
|
|
||||||
|
|
||||||
try {
|
|
||||||
mozmill.utils.waitFor(function () {
|
|
||||||
return self.opened == true;
|
|
||||||
}, "TabView is not open.");
|
|
||||||
|
|
||||||
this._groupItemsObject = this._tabViewObject._window.GroupItems;
|
|
||||||
this._tabItemsObject = this._tabViewObject._window.TabItems;
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
this._controller.window.removeEventListener("tabviewshown", checkOpened, false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the Tab View
|
|
||||||
*/
|
|
||||||
close : function tabView_close() {
|
|
||||||
var menuitem = new elementslib.Elem(this._controller.menus['view-menu'].menu_tabview);
|
|
||||||
this._controller.click(menuitem);
|
|
||||||
this.waitForClosed();
|
|
||||||
|
|
||||||
this._tabView = null;
|
|
||||||
this._tabViewDoc = this._controller.window.document;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait until the Tab View has been closed
|
|
||||||
*/
|
|
||||||
waitForClosed : function tabView_waitForClosed() {
|
|
||||||
// Add event listener to wait until the tabview has been closed
|
|
||||||
var self = { closed: false };
|
|
||||||
function checkClosed() { self.closed = true; }
|
|
||||||
this._controller.window.addEventListener("tabviewhidden", checkClosed, false);
|
|
||||||
|
|
||||||
try {
|
|
||||||
mozmill.utils.waitFor(function () {
|
|
||||||
return self.closed == true;
|
|
||||||
}, "TabView is still open.");
|
|
||||||
} finally {
|
|
||||||
this._controller.window.removeEventListener("tabviewhidden", checkClosed, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
this._groupItemsObject = null;
|
|
||||||
this._tabItemsObject = null;
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////
|
|
||||||
// Groups section
|
|
||||||
///////////////////////////////
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the tab groups which match the filter criteria
|
|
||||||
*
|
|
||||||
* @param {object} aSpec
|
|
||||||
* Information about the filter to apply
|
|
||||||
* Elements: filter - Type of filter to apply
|
|
||||||
* (active, title)
|
|
||||||
* [optional - default: ""]
|
|
||||||
* value - Value of the element
|
|
||||||
* [optional - default: ""]
|
|
||||||
*
|
|
||||||
* @returns List of groups
|
|
||||||
* @type {array of ElemBase}
|
|
||||||
*/
|
|
||||||
getGroups : function tabView_getGroups(aSpec) {
|
|
||||||
var spec = aSpec || {};
|
|
||||||
|
|
||||||
return this.getElements({
|
|
||||||
type: "groups",
|
|
||||||
subtype: spec.filter,
|
|
||||||
value: spec.value
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the group's title box
|
|
||||||
*
|
|
||||||
* @param {object} aSpec
|
|
||||||
* Information on which group to operate on
|
|
||||||
* Elements: group - Group element
|
|
||||||
*
|
|
||||||
* @returns Group title box
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
getGroupTitleBox : function tabView_getGroupTitleBox(aSpec) {
|
|
||||||
var spec = aSpec || {};
|
|
||||||
var group = spec.group;
|
|
||||||
|
|
||||||
if (!group) {
|
|
||||||
throw new Error(arguments.callee.name + ": Group not specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.getElement({
|
|
||||||
type: "group_titleBox",
|
|
||||||
parent: group
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the specified tab group
|
|
||||||
*
|
|
||||||
* @param {object} aSpec
|
|
||||||
* Information on which group to operate on
|
|
||||||
* Elements: group - Group
|
|
||||||
*/
|
|
||||||
closeGroup : function tabView_closeGroup(aSpec) {
|
|
||||||
var spec = aSpec || {};
|
|
||||||
var group = spec.group;
|
|
||||||
|
|
||||||
if (!group) {
|
|
||||||
throw new Error(arguments.callee.name + ": Group not specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var button = this.getElement({
|
|
||||||
type: "group_closeButton",
|
|
||||||
parent: group
|
|
||||||
});
|
|
||||||
this._controller.click(button);
|
|
||||||
|
|
||||||
this.waitForGroupClosed({group: group});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait until the specified tab group has been closed
|
|
||||||
*
|
|
||||||
* @param {object} aSpec
|
|
||||||
* Information on which group to operate on
|
|
||||||
* Elements: group - Group
|
|
||||||
*/
|
|
||||||
waitForGroupClosed : function tabView_waitForGroupClosed(aSpec) {
|
|
||||||
var spec = aSpec || {};
|
|
||||||
var group = spec.group;
|
|
||||||
var groupObj = null;
|
|
||||||
|
|
||||||
var self = { closed: false };
|
|
||||||
function checkClosed() { self.closed = true; }
|
|
||||||
|
|
||||||
if (!group) {
|
|
||||||
throw new Error(arguments.callee.name + ": Group not specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
this._groupItemsObject.groupItems.forEach(function (node) {
|
|
||||||
if (node.container == group.getNode()) {
|
|
||||||
groupObj = node;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!groupObj) {
|
|
||||||
throw new Error(arguments.callee.name + ": Group not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
groupObj.addSubscriber(groupObj, "groupHidden", checkClosed);
|
|
||||||
mozmill.utils.waitFor(function () {
|
|
||||||
return self.closed;
|
|
||||||
}, "Tab Group has not been closed.");
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
groupObj.removeSubscriber(groupObj, "groupHidden");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Undo the closing of the specified tab group
|
|
||||||
*
|
|
||||||
* @param {object} aSpec
|
|
||||||
* Information on which group to operate on
|
|
||||||
* Elements: group - Group
|
|
||||||
*/
|
|
||||||
undoCloseGroup : function tabView_undoCloseGroup(aSpec) {
|
|
||||||
var spec = aSpec || {};
|
|
||||||
var group = spec.group;
|
|
||||||
|
|
||||||
if (!group) {
|
|
||||||
throw new Error(arguments.callee.name + ": Group not specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var undo = this.getElement({
|
|
||||||
type: "group_undoButton",
|
|
||||||
parent: group
|
|
||||||
});
|
|
||||||
this._controller.waitThenClick(undo);
|
|
||||||
|
|
||||||
this.waitForGroupUndo({group: group});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait until the specified tab group has been reopened
|
|
||||||
*
|
|
||||||
* @param {object} aSpec
|
|
||||||
* Information on which group to operate on
|
|
||||||
* Elements: group - Group
|
|
||||||
*/
|
|
||||||
waitForGroupUndo : function tabView_waitForGroupUndo(aSpec) {
|
|
||||||
var spec = aSpec || {};
|
|
||||||
var group = spec.group;
|
|
||||||
var groupObj = null;
|
|
||||||
|
|
||||||
var self = { reopened: false };
|
|
||||||
function checkClosed() { self.reopened = true; }
|
|
||||||
|
|
||||||
if (!group) {
|
|
||||||
throw new Error(arguments.callee.name + ": Group not specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var groupObj = null;
|
|
||||||
this._groupItemsObject.groupItems.forEach(function(node) {
|
|
||||||
if (node.container == group.getNode()) {
|
|
||||||
groupObj = node;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!groupObj) {
|
|
||||||
throw new Error(arguments.callee.name + ": Group not found.");
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
groupObj.addSubscriber(groupObj, "groupShown", checkClosed);
|
|
||||||
mozmill.utils.waitFor(function () {
|
|
||||||
return self.reopened;
|
|
||||||
}, "Tab Group has not been reopened.");
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
groupObj.removeSubscriber(groupObj, "groupShown");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////
|
|
||||||
// Tabs section
|
|
||||||
///////////////////////////////
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the tabs which match the filter criteria
|
|
||||||
*
|
|
||||||
* @param {object} aSpec
|
|
||||||
* Information about the filter to apply
|
|
||||||
* Elements: filter - Type of filter to apply
|
|
||||||
* (active, title)
|
|
||||||
* [optional - default: ""]
|
|
||||||
* value - Value of the element
|
|
||||||
* [optional - default: ""]
|
|
||||||
*
|
|
||||||
* @returns List of tabs
|
|
||||||
* @type {array of ElemBase}
|
|
||||||
*/
|
|
||||||
getTabs : function tabView_getTabs(aSpec) {
|
|
||||||
var spec = aSpec || {};
|
|
||||||
|
|
||||||
return this.getElements({
|
|
||||||
type: "tabs",
|
|
||||||
subtype: spec.filter,
|
|
||||||
value: spec.value
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close a tab
|
|
||||||
*
|
|
||||||
* @param {object} aSpec
|
|
||||||
* Information about the element to operate on
|
|
||||||
* Elements: tab - Tab to close
|
|
||||||
*/
|
|
||||||
closeTab : function tabView_closeTab(aSpec) {
|
|
||||||
var spec = aSpec || {};
|
|
||||||
var tab = spec.tab;
|
|
||||||
|
|
||||||
if (!tab) {
|
|
||||||
throw new Error(arguments.callee.name + ": Tab not specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var button = this.getElement({
|
|
||||||
type: "tab_closeButton",
|
|
||||||
value: tab}
|
|
||||||
);
|
|
||||||
this._controller.click(button);
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the tab's title box
|
|
||||||
*
|
|
||||||
* @param {object} aSpec
|
|
||||||
* Information on which tab to operate on
|
|
||||||
* Elements: tab - Tab
|
|
||||||
*
|
|
||||||
* @returns Tab title box
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
getTabTitleBox : function tabView_getTabTitleBox(aSpec) {
|
|
||||||
var spec = aSpec || {};
|
|
||||||
var tab = spec.tab;
|
|
||||||
|
|
||||||
if (!tab) {
|
|
||||||
throw new Error(arguments.callee.name + ": Tab not specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
return this.getElement({
|
|
||||||
type: "tab_titleBox",
|
|
||||||
parent: spec.tab
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Open a new tab in the specified group
|
|
||||||
*
|
|
||||||
* @param {object} aSpec
|
|
||||||
* Information about the element to operate on
|
|
||||||
* Elements: group - Group to create a new tab in
|
|
||||||
*/
|
|
||||||
openTab : function tabView_openTab(aSpec) {
|
|
||||||
var spec = aSpec || {};
|
|
||||||
var group = spec.group;
|
|
||||||
|
|
||||||
if (!group) {
|
|
||||||
throw new Error(arguments.callee.name + ": Group not specified.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var button = this.getElement({
|
|
||||||
type: "group_newTabButton",
|
|
||||||
parent: group
|
|
||||||
});
|
|
||||||
|
|
||||||
this._controller.click(button);
|
|
||||||
this.waitForClosed();
|
|
||||||
},
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////
|
|
||||||
// UI Elements section
|
|
||||||
///////////////////////////////
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an UI element based on the given specification
|
|
||||||
*
|
|
||||||
* @param {object} aSpec
|
|
||||||
* Information of the UI elements which should be retrieved
|
|
||||||
* Elements: type - Identifier of the element
|
|
||||||
* subtype - Attribute of the element to filter
|
|
||||||
* [optional - default: ""]
|
|
||||||
* value - Value of the attribute to filter
|
|
||||||
* [optional - default: ""]
|
|
||||||
* parent - Parent of the to find element
|
|
||||||
* [optional - default: document]
|
|
||||||
*
|
|
||||||
* @returns Element which has been found
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
getElement : function tabView_getElement(aSpec) {
|
|
||||||
var elements = this.getElements(aSpec);
|
|
||||||
|
|
||||||
return (elements.length > 0) ? elements[0] : undefined;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve list of UI elements based on the given specification
|
|
||||||
*
|
|
||||||
* @param {object} aSpec
|
|
||||||
* Information of the UI elements which should be retrieved
|
|
||||||
* Elements: type - Identifier of the element
|
|
||||||
* subtype - Attribute of the element to filter
|
|
||||||
* [optional - default: ""]
|
|
||||||
* value - Value of the attribute to filter
|
|
||||||
* [optional - default: ""]
|
|
||||||
* parent - Parent of the to find element
|
|
||||||
* [optional - default: document]
|
|
||||||
*
|
|
||||||
* @returns Elements which have been found
|
|
||||||
* @type {array of ElemBase}
|
|
||||||
*/
|
|
||||||
getElements : function tabView_getElement(aSpec) {
|
|
||||||
var spec = aSpec || { };
|
|
||||||
var type = spec.type;
|
|
||||||
var subtype = spec.subtype;
|
|
||||||
var value = spec.value;
|
|
||||||
var parent = spec.parent;
|
|
||||||
|
|
||||||
var root = parent ? parent.getNode() : this._tabViewDoc;
|
|
||||||
var nodeCollector = new domUtils.nodeCollector(root);
|
|
||||||
|
|
||||||
switch(type) {
|
|
||||||
// Top level elements
|
|
||||||
case "tabView":
|
|
||||||
nodeCollector.root = this._controller.window.document;
|
|
||||||
nodeCollector.queryNodes("#tab-view");
|
|
||||||
break;
|
|
||||||
case "contentArea":
|
|
||||||
nodeCollector.queryNodes("#content");
|
|
||||||
break;
|
|
||||||
case "deck":
|
|
||||||
nodeCollector.root = this._controller.window.document;
|
|
||||||
nodeCollector.queryNodes("#tab-view-deck");
|
|
||||||
break;
|
|
||||||
case "exitButton":
|
|
||||||
nodeCollector.queryNodes("#exit-button");
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Group elements
|
|
||||||
case "group_appTabs":
|
|
||||||
nodeCollector.queryNodes(".appTabIcon");
|
|
||||||
break;
|
|
||||||
case "group_closeButton":
|
|
||||||
nodeCollector.queryNodes(".close");
|
|
||||||
break;
|
|
||||||
case "group_newTabButton":
|
|
||||||
nodeCollector.queryNodes(".newTabButton");
|
|
||||||
break;
|
|
||||||
case "group_resizer":
|
|
||||||
nodeCollector.queryNodes(".iq-resizable-handle");
|
|
||||||
break;
|
|
||||||
case "group_stackExpander":
|
|
||||||
nodeCollector.queryNodes(".stackExpander");
|
|
||||||
break;
|
|
||||||
case "group_titleBox":
|
|
||||||
nodeCollector.queryNodes(".name");
|
|
||||||
break;
|
|
||||||
case "group_undoButton":
|
|
||||||
// Bug 596504 - No reference to the undo button
|
|
||||||
nodeCollector.root = this._tabViewDoc;
|
|
||||||
nodeCollector.queryNodes(".undo").filter(function (node) {
|
|
||||||
var groups = this._groupItemsObject.groupItems;
|
|
||||||
for (var i = 0; i < groups.length; i++) {
|
|
||||||
var group = groups[i];
|
|
||||||
if (group.container == parent.getNode() &&
|
|
||||||
group.$undoContainer.length == 1) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}, this);
|
|
||||||
break;
|
|
||||||
case "groups":
|
|
||||||
nodeCollector.queryNodes(".groupItem").filter(function (node) {
|
|
||||||
switch(subtype) {
|
|
||||||
case "active":
|
|
||||||
return node.className.indexOf("activeGroup") != -1;
|
|
||||||
case "title":
|
|
||||||
// If no title is given the default name is used
|
|
||||||
if (!value) {
|
|
||||||
value = utils.getProperty("chrome://browser/locale/tabview.properties",
|
|
||||||
"tabview.groupItem.defaultName");
|
|
||||||
}
|
|
||||||
var title = node.querySelector(".name");
|
|
||||||
return (value == title.value);
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Search elements
|
|
||||||
case "search_box":
|
|
||||||
nodeCollector.queryNodes("#searchbox");
|
|
||||||
break;
|
|
||||||
case "search_button":
|
|
||||||
nodeCollector.queryNodes("#searchbutton");
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Tab elements
|
|
||||||
case "tab_closeButton":
|
|
||||||
nodeCollector.queryNodes(".tab .close");
|
|
||||||
break;
|
|
||||||
case "tab_favicon":
|
|
||||||
nodeCollector.queryNodes(".tab .favicon");
|
|
||||||
break;
|
|
||||||
case "tab_titleBox":
|
|
||||||
nodeCollector.queryNodes(".tab .tab-title");
|
|
||||||
break;
|
|
||||||
case "tabs":
|
|
||||||
nodeCollector.queryNodes(".tab").filter(function (node) {
|
|
||||||
switch (subtype) {
|
|
||||||
case "active":
|
|
||||||
return (node.className.indexOf("focus") != -1);
|
|
||||||
case "group":
|
|
||||||
var group = value ? value.getNode() : null;
|
|
||||||
if (group) {
|
|
||||||
var tabs = this._tabItemsObject.getItems();
|
|
||||||
for (var i = 0; i < tabs.length; i++) {
|
|
||||||
var tab = tabs[i];
|
|
||||||
if (tab.parent && tab.parent.container == group) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return (node.className.indexOf("tabInGroupItem") == -1);
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}, this);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown element type - " +
|
|
||||||
aSpec.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return nodeCollector.elements;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export of classes
|
|
||||||
exports.tabView = tabView;
|
|
||||||
@@ -1,509 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
* Aaron Train <atrain@mozilla.com>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileoverview
|
|
||||||
* The ToolbarAPI adds support for accessing and interacting with toolbar elements
|
|
||||||
*
|
|
||||||
* @version 1.0.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Include required modules
|
|
||||||
var utils = require("utils");
|
|
||||||
|
|
||||||
const TIMEOUT = 5000;
|
|
||||||
|
|
||||||
const AUTOCOMPLETE_POPUP = '/id("main-window")/id("mainPopupSet")/id("PopupAutoCompleteRichResult")';
|
|
||||||
const NOTIFICATION_POPUP = '/id("main-window")/id("mainPopupSet")/id("notification-popup")';
|
|
||||||
const URLBAR_CONTAINER = '/id("main-window")/id("tab-view-deck")/{"flex":"1"}' +
|
|
||||||
'/id("navigator-toolbox")/id("nav-bar")/id("urlbar-container")';
|
|
||||||
const URLBAR_INPUTBOX = URLBAR_CONTAINER + '/id("urlbar")/anon({"anonid":"stack"})' +
|
|
||||||
'/anon({"anonid":"textbox-container"})' +
|
|
||||||
'/anon({"anonid":"textbox-input-box"})';
|
|
||||||
const CONTEXT_MENU = URLBAR_INPUTBOX + '/anon({"anonid":"input-box-contextmenu"})';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param {MozmillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
*/
|
|
||||||
function autoCompleteResults(controller) {
|
|
||||||
this._controller = controller;
|
|
||||||
this._popup = this.getElement({type: "popup"});
|
|
||||||
this._results = this.getElement({type: "results"});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* AutoComplete Result class
|
|
||||||
*/
|
|
||||||
autoCompleteResults.prototype = {
|
|
||||||
/**
|
|
||||||
* Returns all autocomplete results
|
|
||||||
*
|
|
||||||
* @returns Autocomplete results
|
|
||||||
* @type {Array of ElemBase}
|
|
||||||
*/
|
|
||||||
get allResults() {
|
|
||||||
var results = [];
|
|
||||||
for (ii = 0; ii < this.length; ii++) {
|
|
||||||
results.push(this.getResult(ii));
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the controller of the current window
|
|
||||||
*
|
|
||||||
* @returns Mozmill Controller
|
|
||||||
* @type MozMillController
|
|
||||||
*/
|
|
||||||
get controller() {
|
|
||||||
return this._controller;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the autocomplete popup is open
|
|
||||||
*
|
|
||||||
* @returns True if the panel is open
|
|
||||||
* @type {boolean}
|
|
||||||
*/
|
|
||||||
get isOpened() {
|
|
||||||
return (this._popup.getNode().state == 'open');
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Return the amount of autocomplete entries
|
|
||||||
*
|
|
||||||
* @returns Number of all entries
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
get length() {
|
|
||||||
return this._results.getNode().itemCount;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently selected index
|
|
||||||
*
|
|
||||||
* @returns Selected index
|
|
||||||
* @type {number}
|
|
||||||
*/
|
|
||||||
get selectedIndex() {
|
|
||||||
return this._results.getNode().selectedIndex;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the visible autocomplete results
|
|
||||||
*
|
|
||||||
* @returns Results
|
|
||||||
* @type {Array of ElemBase}
|
|
||||||
*/
|
|
||||||
get visibleResults() {
|
|
||||||
var results = [];
|
|
||||||
for (ii = 0; ii < this.length; ii++) {
|
|
||||||
var result = this.getResult(ii);
|
|
||||||
if (!result.getNode().hasAttribute("collapsed"))
|
|
||||||
results.push(result);
|
|
||||||
}
|
|
||||||
return results;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the underlined text of all results from the text or URL
|
|
||||||
*
|
|
||||||
* @param {ElemBase} result
|
|
||||||
* Autocomplete result which has to be checked
|
|
||||||
* @param {string} type
|
|
||||||
* Type of element to check (text or url)
|
|
||||||
*
|
|
||||||
* @returns An array of substrings which are underlined
|
|
||||||
* @type {Array of string}
|
|
||||||
*/
|
|
||||||
getUnderlinedText : function autoCompleteResults_getUnderlinedText(result, type) {
|
|
||||||
this._controller.assertJS("subject.resultNode != null",
|
|
||||||
{resultNode: result.getNode()});
|
|
||||||
|
|
||||||
// Get the description element of the given title or url
|
|
||||||
var description = null;
|
|
||||||
switch (type) {
|
|
||||||
case "title":
|
|
||||||
description = result.getNode().boxObject.firstChild.childNodes[1].childNodes[0];
|
|
||||||
break;
|
|
||||||
case "url":
|
|
||||||
description = result.getNode().boxObject.lastChild.childNodes[2].childNodes[0];
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Type unknown - " + type);
|
|
||||||
}
|
|
||||||
|
|
||||||
let values = [ ];
|
|
||||||
for each (node in description.childNodes) {
|
|
||||||
if (node.nodeName == 'span') {
|
|
||||||
// Only add underlined text to the results
|
|
||||||
values.push(node.innerHTML);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return values;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all the needed external DTD urls as an array
|
|
||||||
*
|
|
||||||
* @returns Array of external DTD urls
|
|
||||||
* @type [string]
|
|
||||||
*/
|
|
||||||
getDtds : function autoCompleteResults_getDtds() {
|
|
||||||
return null;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an UI element based on the given spec
|
|
||||||
*
|
|
||||||
* @param {object} spec
|
|
||||||
* Information of the UI element which should be retrieved
|
|
||||||
* type: General type information
|
|
||||||
* subtype: Specific element or property
|
|
||||||
* value: Value of the element or property
|
|
||||||
* @returns Element which has been created
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
getElement : function autoCompleteResults_getElement(spec) {
|
|
||||||
var elem = null;
|
|
||||||
|
|
||||||
switch (spec.type) {
|
|
||||||
/**
|
|
||||||
* subtype: subtype to match
|
|
||||||
* value: value to match
|
|
||||||
*/
|
|
||||||
case "popup":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, AUTOCOMPLETE_POPUP);
|
|
||||||
break;
|
|
||||||
case "results":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document,
|
|
||||||
AUTOCOMPLETE_POPUP + '/anon({"anonid":"richlistbox"})');
|
|
||||||
break;
|
|
||||||
case "result":
|
|
||||||
elem = new elementslib.Elem(this._results.getNode().getItemAtIndex(spec.value));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown element type - " + spec.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the autocomplete result element of the given index
|
|
||||||
*
|
|
||||||
* @param {number} index
|
|
||||||
* Index of the result to return
|
|
||||||
* @returns Autocomplete result element
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
getResult : function autoCompleteResults_getResult(index) {
|
|
||||||
return this.getElement({type: "result", value: index});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the autocomplete popup
|
|
||||||
*
|
|
||||||
* @param {boolean} force
|
|
||||||
* Force the closing of the autocomplete popup
|
|
||||||
*/
|
|
||||||
close : function autoCompleteResults_close(force) {
|
|
||||||
if (this.isOpened) {
|
|
||||||
if (force) {
|
|
||||||
this._popup.getNode().hidePopup();
|
|
||||||
} else {
|
|
||||||
this._controller.keypress(locationBar.urlbar, "VK_ESCAPE", {});
|
|
||||||
}
|
|
||||||
this._controller.waitFor(function () {
|
|
||||||
return !this.isOpened;
|
|
||||||
}, "Autocomplete list should not be open.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Constructor
|
|
||||||
*
|
|
||||||
* @param {MozmillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
*/
|
|
||||||
function locationBar(controller)
|
|
||||||
{
|
|
||||||
this._controller = controller;
|
|
||||||
this._autoCompleteResults = new autoCompleteResults(controller);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Location Bar class
|
|
||||||
*/
|
|
||||||
locationBar.prototype = {
|
|
||||||
/**
|
|
||||||
* Returns the autocomplete object
|
|
||||||
*
|
|
||||||
* @returns Autocomplete object
|
|
||||||
* @type {object}
|
|
||||||
*/
|
|
||||||
get autoCompleteResults() {
|
|
||||||
return this._autoCompleteResults;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the controller of the current window
|
|
||||||
*
|
|
||||||
* @returns Mozmill controller
|
|
||||||
* @type {MozMillController}
|
|
||||||
*/
|
|
||||||
get controller() {
|
|
||||||
return this._controller;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the urlbar element
|
|
||||||
*
|
|
||||||
* @returns URL bar
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
get urlbar() {
|
|
||||||
return this.getElement({type: "urlbar"});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the currently shown URL
|
|
||||||
*
|
|
||||||
* @returns Text inside the location bar
|
|
||||||
* @type {string}
|
|
||||||
*/
|
|
||||||
get value() {
|
|
||||||
return this.urlbar.getNode().value;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clear the location bar
|
|
||||||
*/
|
|
||||||
clear : function locationBar_clear() {
|
|
||||||
this.focus({type: "shortcut"});
|
|
||||||
this._controller.keypress(this.urlbar, "VK_DELETE", {});
|
|
||||||
this._controller.waitForEval("subject.value == ''",
|
|
||||||
TIMEOUT, 100, this.urlbar.getNode());
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the context menu of the urlbar input field
|
|
||||||
*/
|
|
||||||
closeContextMenu : function locationBar_closeContextMenu() {
|
|
||||||
var menu = this.getElement({type: "contextMenu"});
|
|
||||||
this._controller.keypress(menu, "VK_ESCAPE", {});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Check if the location bar contains the given text
|
|
||||||
*
|
|
||||||
* @param {string} text
|
|
||||||
* Text which should be checked against
|
|
||||||
*/
|
|
||||||
contains : function locationBar_contains(text) {
|
|
||||||
return this.urlbar.getNode().value.indexOf(text) != -1;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Focus the location bar
|
|
||||||
*
|
|
||||||
* @param {object} event
|
|
||||||
* Focus the location bar with the given event (click or shortcut)
|
|
||||||
*/
|
|
||||||
focus : function locationBar_focus(event) {
|
|
||||||
switch (event.type) {
|
|
||||||
case "click":
|
|
||||||
this._controller.click(this.urlbar);
|
|
||||||
break;
|
|
||||||
case "shortcut":
|
|
||||||
var cmdKey = utils.getEntity(this.getDtds(), "openCmd.commandkey");
|
|
||||||
this._controller.keypress(null, cmdKey, {accelKey: true});
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unkown event type - " + event.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait until the location bar has been focused
|
|
||||||
this._controller.waitForEval("subject.getAttribute('focused') == 'true'",
|
|
||||||
TIMEOUT, 100, this.urlbar.getNode());
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Gets all the needed external DTD urls as an array
|
|
||||||
*
|
|
||||||
* @returns Array of external DTD urls
|
|
||||||
* @type [string]
|
|
||||||
*/
|
|
||||||
getDtds : function locationBar_getDtds() {
|
|
||||||
var dtds = ["chrome://branding/locale/brand.dtd",
|
|
||||||
"chrome://browser/locale/browser.dtd"];
|
|
||||||
return dtds;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve an UI element based on the given spec
|
|
||||||
*
|
|
||||||
* @param {object} spec
|
|
||||||
* Information of the UI element which should be retrieved
|
|
||||||
* type: General type information
|
|
||||||
* subtype: Specific element or property
|
|
||||||
* value: Value of the element or property
|
|
||||||
* @returns Element which has been created
|
|
||||||
* @type ElemBase
|
|
||||||
*/
|
|
||||||
getElement : function locationBar_getElement(spec) {
|
|
||||||
var elem = null;
|
|
||||||
|
|
||||||
switch(spec.type) {
|
|
||||||
/**
|
|
||||||
* subtype: subtype to match
|
|
||||||
* value: value to match
|
|
||||||
*/
|
|
||||||
case "contextMenu":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, CONTEXT_MENU);
|
|
||||||
break;
|
|
||||||
case "contextMenu_entry":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, CONTEXT_MENU +
|
|
||||||
'/{"cmd":"cmd_' + spec.subtype + '"}');
|
|
||||||
break;
|
|
||||||
case "favicon":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "page-proxy-favicon");
|
|
||||||
break;
|
|
||||||
case "feedButton":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "feed-button");
|
|
||||||
break;
|
|
||||||
case "goButton":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "urlbar-go-button");
|
|
||||||
break;
|
|
||||||
case "historyDropMarker":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document,
|
|
||||||
URLBAR_CONTAINER + '/id("urlbar")/anon({"anonid":"historydropmarker"})');
|
|
||||||
break;
|
|
||||||
case "identityBox":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "identity-box");
|
|
||||||
break;
|
|
||||||
case "notification_element":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, NOTIFICATION_POPUP +
|
|
||||||
spec.subtype);
|
|
||||||
break;
|
|
||||||
case "notification_popup":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, NOTIFICATION_POPUP);
|
|
||||||
break;
|
|
||||||
case "starButton":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "star-button");
|
|
||||||
break;
|
|
||||||
case "stopButton":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "urlbar-stop-button");
|
|
||||||
break;
|
|
||||||
case "urlbar":
|
|
||||||
elem = new elementslib.ID(this._controller.window.document, "urlbar");
|
|
||||||
break;
|
|
||||||
case "urlbar_input":
|
|
||||||
elem = new elementslib.Lookup(this._controller.window.document, URLBAR_INPUTBOX +
|
|
||||||
'/anon({"anonid":"input"})');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown element type - " + spec.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return elem;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the notification popup
|
|
||||||
*
|
|
||||||
* @return The notification popup element
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
getNotification : function locationBar_getNotification() {
|
|
||||||
return this.getElement({type: "notification_popup"});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieves the specified element of the door hanger notification bar
|
|
||||||
*
|
|
||||||
* @param {string} aType
|
|
||||||
* Type of the notification bar to look for
|
|
||||||
* @param {string} aLookupString
|
|
||||||
* Lookup string of the notification bar's child element
|
|
||||||
* [optional - default: ""]
|
|
||||||
*
|
|
||||||
* @return The created element
|
|
||||||
* @type {ElemBase}
|
|
||||||
*/
|
|
||||||
getNotificationElement : function locationBar_getNotificationElement(aType, aLookupString)
|
|
||||||
{
|
|
||||||
var lookup = '/id("' + aType + '")';
|
|
||||||
lookup = aLookupString ? lookup + aLookupString : lookup;
|
|
||||||
|
|
||||||
// Get the notification and fetch the child element if wanted
|
|
||||||
return this.getElement({type: "notification_element", subtype: lookup});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Load the given URL
|
|
||||||
*
|
|
||||||
* @param {string} url
|
|
||||||
* URL of web page to load
|
|
||||||
*/
|
|
||||||
loadURL : function locationBar_loadURL(url) {
|
|
||||||
this.focus({type: "shortcut"});
|
|
||||||
this.type(url);
|
|
||||||
this._controller.keypress(this.urlbar, "VK_RETURN", {});
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Type the given text into the location bar
|
|
||||||
*
|
|
||||||
* @param {string} text
|
|
||||||
* Text to enter into the location bar
|
|
||||||
*/
|
|
||||||
type : function locationBar_type(text) {
|
|
||||||
this._controller.type(this.urlbar, text);
|
|
||||||
this.contains(text);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export of classes
|
|
||||||
exports.locationBar = locationBar;
|
|
||||||
exports.autoCompleteResults = autoCompleteResults;
|
|
||||||
|
|
||||||
@@ -1,445 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is the Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
* Anthony Hughes <ahughes@mozilla.com>
|
|
||||||
* M.-A. Darche <mozdev@cynode.org>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileoverview
|
|
||||||
* The UtilsAPI offers various helper functions for any other API which is
|
|
||||||
* not already covered by another shared module.
|
|
||||||
*
|
|
||||||
* @version 1.0.3
|
|
||||||
*/
|
|
||||||
|
|
||||||
// Include required modules
|
|
||||||
var prefs = require("prefs");
|
|
||||||
|
|
||||||
const gTimeout = 5000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get application specific informations
|
|
||||||
* @see http://mxr.mozilla.org/mozilla-central/source/xpcom/system/nsIXULAppInfo.idl
|
|
||||||
*/
|
|
||||||
var appInfo = {
|
|
||||||
_service: null,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the application info service
|
|
||||||
* @returns XUL runtime object
|
|
||||||
* @type nsiXULRuntime
|
|
||||||
*/
|
|
||||||
get appInfo() {
|
|
||||||
if (!this._appInfo) {
|
|
||||||
this._service = Cc["@mozilla.org/xre/app-info;1"]
|
|
||||||
.getService(Ci.nsIXULAppInfo)
|
|
||||||
.QueryInterface(Ci.nsIXULRuntime);
|
|
||||||
}
|
|
||||||
return this._service;
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the build id
|
|
||||||
* @returns Build id
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
get buildID() this.appInfo.appBuildID,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the application id
|
|
||||||
* @returns Application id
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
get ID() this.appInfo.ID,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the application name
|
|
||||||
* @returns Application name
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
get name() this.appInfo.name,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the operation system
|
|
||||||
* @returns Operation system name
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
get os() this.appInfo.OS,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the product vendor
|
|
||||||
* @returns Vendor name
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
get vendor() this.appInfo.vendor,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the application version
|
|
||||||
* @returns Application version
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
get version() this.appInfo.version,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the build id of the Gecko platform
|
|
||||||
* @returns Platform build id
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
get platformBuildID() this.appInfo.platformBuildID,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the version of the Gecko platform
|
|
||||||
* @returns Platform version
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
get platformVersion() this.appInfo.platformVersion,
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the currently used locale
|
|
||||||
* @returns Current locale
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
get locale() {
|
|
||||||
var registry = Cc["@mozilla.org/chrome/chrome-registry;1"]
|
|
||||||
.getService(Ci.nsIXULChromeRegistry);
|
|
||||||
return registry.getSelectedLocale("global");
|
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get the user agent string
|
|
||||||
* @returns User agent
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
get userAgent() {
|
|
||||||
var window = mozmill.wm.getMostRecentWindow("navigator:browser");
|
|
||||||
if (window)
|
|
||||||
return window.navigator.userAgent;
|
|
||||||
return "";
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Checks the visibility of an element.
|
|
||||||
* XXX: Mozmill doesn't check if an element is visible and also operates on
|
|
||||||
* elements which are invisible. (Bug 490548)
|
|
||||||
*
|
|
||||||
* @param {MozmillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
* @param {ElemBase} elem
|
|
||||||
* Element to check its visibility
|
|
||||||
* @param {boolean} expectedVisibility
|
|
||||||
* Expected visibility state of the element
|
|
||||||
*/
|
|
||||||
function assertElementVisible(controller, elem, expectedVisibility) {
|
|
||||||
var element = elem.getNode();
|
|
||||||
var visible;
|
|
||||||
|
|
||||||
switch (element.nodeName) {
|
|
||||||
case 'panel':
|
|
||||||
visible = (element.state == 'open');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
var style = controller.window.getComputedStyle(element, '');
|
|
||||||
var state = style.getPropertyValue('visibility');
|
|
||||||
visible = (state == 'visible');
|
|
||||||
}
|
|
||||||
|
|
||||||
controller.assertJS('subject.visible == subject.expectedVisibility', {
|
|
||||||
visible: visible,
|
|
||||||
expectedVisibility: expectedVisibility
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Assert if the current URL is identical to the target URL.
|
|
||||||
* With this function also redirects can be tested.
|
|
||||||
*
|
|
||||||
* @param {MozmillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
* @param {string} targetURL
|
|
||||||
* URL to check
|
|
||||||
*/
|
|
||||||
function assertLoadedUrlEqual(controller, targetUrl) {
|
|
||||||
var locationBar = new elementslib.ID(controller.window.document, "urlbar");
|
|
||||||
var currentURL = locationBar.getNode().value;
|
|
||||||
|
|
||||||
// Load the target URL
|
|
||||||
controller.open(targetUrl);
|
|
||||||
controller.waitForPageLoad();
|
|
||||||
|
|
||||||
// Check the same web page has been opened
|
|
||||||
controller.waitFor(function () {
|
|
||||||
return locationBar.getNode().value === currentURL;
|
|
||||||
}, "Current URL should be identical to the target URL - got " +
|
|
||||||
locationBar.getNode().value + ", expected " + currentURL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close the context menu inside the content area of the currently open tab
|
|
||||||
*
|
|
||||||
* @param {MozmillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
*/
|
|
||||||
function closeContentAreaContextMenu(controller) {
|
|
||||||
var contextMenu = new elementslib.ID(controller.window.document, "contentAreaContextMenu");
|
|
||||||
controller.keypress(contextMenu, "VK_ESCAPE", {});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Run tests against a given search form
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController of the window to operate on
|
|
||||||
* @param {ElemBase} searchField
|
|
||||||
* The HTML input form element to test
|
|
||||||
* @param {string} searchTerm
|
|
||||||
* The search term for the test
|
|
||||||
* @param {ElemBase} submitButton
|
|
||||||
* (Optional) The forms submit button
|
|
||||||
* @param {number} timeout
|
|
||||||
* The timeout value for the single tests
|
|
||||||
*/
|
|
||||||
function checkSearchField(controller, searchField,
|
|
||||||
searchTerm, submitButton,
|
|
||||||
timeout) {
|
|
||||||
controller.waitThenClick(searchField, timeout);
|
|
||||||
controller.type(searchField, searchTerm);
|
|
||||||
|
|
||||||
if (submitButton != undefined) {
|
|
||||||
controller.waitThenClick(submitButton, timeout);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Create a new URI
|
|
||||||
*
|
|
||||||
* @param {string} spec
|
|
||||||
* The URI string in UTF-8 encoding.
|
|
||||||
* @param {string} originCharset
|
|
||||||
* The charset of the document from which this URI string originated.
|
|
||||||
* @param {string} baseURI
|
|
||||||
* If null, spec must specify an absolute URI. Otherwise, spec may be
|
|
||||||
* resolved relative to baseURI, depending on the protocol.
|
|
||||||
* @return A URI object
|
|
||||||
* @type nsIURI
|
|
||||||
*/
|
|
||||||
function createURI(spec, originCharset, baseURI)
|
|
||||||
{
|
|
||||||
let iosvc = Cc["@mozilla.org/network/io-service;1"].
|
|
||||||
getService(Ci.nsIIOService);
|
|
||||||
|
|
||||||
return iosvc.newURI(spec, originCharset, baseURI);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Empty the clipboard by assigning an empty string
|
|
||||||
*/
|
|
||||||
function emptyClipboard() {
|
|
||||||
var clipboard = Cc["@mozilla.org/widget/clipboardhelper;1"].
|
|
||||||
getService(Ci.nsIClipboardHelper);
|
|
||||||
clipboard.copyString("");
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Format a URL by replacing all placeholders
|
|
||||||
*
|
|
||||||
* @param {string} prefName
|
|
||||||
* The preference name which contains the URL
|
|
||||||
* @return The formatted URL
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
function formatUrlPref(prefName) {
|
|
||||||
var formatter = Cc["@mozilla.org/toolkit/URLFormatterService;1"]
|
|
||||||
.getService(Ci.nsIURLFormatter);
|
|
||||||
|
|
||||||
return formatter.formatURLPref(prefName);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the default home page
|
|
||||||
*
|
|
||||||
* @return The URL of the default homepage
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
function getDefaultHomepage() {
|
|
||||||
var preferences = prefs.preferences;
|
|
||||||
|
|
||||||
var prefValue = preferences.getPref("browser.startup.homepage", "",
|
|
||||||
true, Ci.nsIPrefLocalizedString);
|
|
||||||
return prefValue.data;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value of an individual entity in a DTD file.
|
|
||||||
*
|
|
||||||
* @param [string] urls
|
|
||||||
* Array of DTD urls.
|
|
||||||
* @param {string} entityId
|
|
||||||
* The ID of the entity to get the value of.
|
|
||||||
*
|
|
||||||
* @return The value of the requested entity
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
function getEntity(urls, entityId) {
|
|
||||||
// Add xhtml11.dtd to prevent missing entity errors with XHTML files
|
|
||||||
urls.push("resource:///res/dtd/xhtml11.dtd");
|
|
||||||
|
|
||||||
// Build a string of external entities
|
|
||||||
var extEntities = "";
|
|
||||||
for (i = 0; i < urls.length; i++) {
|
|
||||||
extEntities += '<!ENTITY % dtd' + i + ' SYSTEM "' +
|
|
||||||
urls[i] + '">%dtd' + i + ';';
|
|
||||||
}
|
|
||||||
|
|
||||||
var parser = Cc["@mozilla.org/xmlextras/domparser;1"]
|
|
||||||
.createInstance(Ci.nsIDOMParser);
|
|
||||||
var header = '<?xml version="1.0"?><!DOCTYPE elem [' + extEntities + ']>';
|
|
||||||
var elem = '<elem id="elementID">&' + entityId + ';</elem>';
|
|
||||||
var doc = parser.parseFromString(header + elem, 'text/xml');
|
|
||||||
var elemNode = doc.querySelector('elem[id="elementID"]');
|
|
||||||
|
|
||||||
if (elemNode == null)
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown entity - " + entityId);
|
|
||||||
|
|
||||||
return elemNode.textContent;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the value of an individual property.
|
|
||||||
*
|
|
||||||
* @param {string} url
|
|
||||||
* URL of the string bundle.
|
|
||||||
* @param {string} prefName
|
|
||||||
* The property to get the value of.
|
|
||||||
*
|
|
||||||
* @return The value of the requested property
|
|
||||||
* @type string
|
|
||||||
*/
|
|
||||||
function getProperty(url, prefName) {
|
|
||||||
var sbs = Cc["@mozilla.org/intl/stringbundle;1"]
|
|
||||||
.getService(Ci.nsIStringBundleService);
|
|
||||||
var bundle = sbs.createBundle(url);
|
|
||||||
|
|
||||||
try {
|
|
||||||
return bundle.GetStringFromName(prefName);
|
|
||||||
} catch (ex) {
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown property - " + prefName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Function to handle non-modal windows
|
|
||||||
*
|
|
||||||
* @param {string} type
|
|
||||||
* Specifies how to check for the new window (possible values: type or title)
|
|
||||||
* @param {string} text
|
|
||||||
* The window type of title string to search for
|
|
||||||
* @param {function} callback (optional)
|
|
||||||
* Callback function to call for window specific tests
|
|
||||||
* @param {boolean} close (optional - default: true)
|
|
||||||
* Make sure the window is closed after the return from the callback handler
|
|
||||||
* @returns The MozMillController of the window (if the window hasn't been closed)
|
|
||||||
*/
|
|
||||||
function handleWindow(type, text, callback, close) {
|
|
||||||
// Set the window opener function to use depending on the type
|
|
||||||
var func_ptr = null;
|
|
||||||
switch (type) {
|
|
||||||
case "type":
|
|
||||||
func_ptr = mozmill.utils.getWindowByType;
|
|
||||||
break;
|
|
||||||
case "title":
|
|
||||||
func_ptr = mozmill.utils.getWindowByTitle;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Error(arguments.callee.name + ": Unknown opener type - " + type);
|
|
||||||
}
|
|
||||||
|
|
||||||
var window = null;
|
|
||||||
var controller = null;
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Wait until the window has been opened
|
|
||||||
mozmill.utils.waitFor(function () {
|
|
||||||
window = func_ptr(text);
|
|
||||||
return window != null;
|
|
||||||
}, "Window has been found.");
|
|
||||||
|
|
||||||
// XXX: We still have to find a reliable way to wait until the new window
|
|
||||||
// content has been finished loading. Let's wait for now.
|
|
||||||
controller = new mozmill.controller.MozMillController(window);
|
|
||||||
controller.sleep(200);
|
|
||||||
|
|
||||||
if (callback) {
|
|
||||||
callback(controller);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check if we have to close the window
|
|
||||||
if (close === undefined)
|
|
||||||
close = true;
|
|
||||||
|
|
||||||
if (close && window) {
|
|
||||||
controller.window.close();
|
|
||||||
mozmill.utils.waitFor(function () {
|
|
||||||
return func_ptr(text) != window;
|
|
||||||
}, "Window has been closed.");
|
|
||||||
|
|
||||||
window = null;
|
|
||||||
controller = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return controller;
|
|
||||||
} catch (ex) {
|
|
||||||
if (window)
|
|
||||||
window.close();
|
|
||||||
|
|
||||||
throw ex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export of variables
|
|
||||||
exports.appInfo = appInfo;
|
|
||||||
|
|
||||||
// Export of functions
|
|
||||||
exports.assertElementVisible = assertElementVisible;
|
|
||||||
exports.assertLoadedUrlEqual = assertLoadedUrlEqual;
|
|
||||||
exports.closeContentAreaContextMenu = closeContentAreaContextMenu;
|
|
||||||
exports.checkSearchField = checkSearchField;
|
|
||||||
exports.createURI = createURI;
|
|
||||||
exports.formatUrlPref = formatUrlPref;
|
|
||||||
exports.emptyClipboard = emptyClipboard;
|
|
||||||
exports.getDefaultHomepage = getDefaultHomepage;
|
|
||||||
exports.getEntity = getEntity;
|
|
||||||
exports.getProperty = getProperty;
|
|
||||||
exports.handleWindow = handleWindow;
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
/* ***** BEGIN LICENSE BLOCK *****
|
|
||||||
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
||||||
*
|
|
||||||
* The contents of this file are subject to the Mozilla Public License Version
|
|
||||||
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
||||||
* the License. You may obtain a copy of the License at
|
|
||||||
* http://www.mozilla.org/MPL/
|
|
||||||
*
|
|
||||||
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
||||||
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
||||||
* for the specific language governing rights and limitations under the
|
|
||||||
* License.
|
|
||||||
*
|
|
||||||
* The Original Code is MozMill Test code.
|
|
||||||
*
|
|
||||||
* The Initial Developer of the Original Code is Mozilla Foundation.
|
|
||||||
* Portions created by the Initial Developer are Copyright (C) 2009
|
|
||||||
* the Initial Developer. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* Contributor(s):
|
|
||||||
* Henrik Skupin <hskupin@mozilla.com>
|
|
||||||
*
|
|
||||||
* Alternatively, the contents of this file may be used under the terms of
|
|
||||||
* either the GNU General Public License Version 2 or later (the "GPL"), or
|
|
||||||
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
||||||
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
||||||
* of those above. If you wish to allow use of your version of this file only
|
|
||||||
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
||||||
* use your version of this file under the terms of the MPL, indicate your
|
|
||||||
* decision by deleting the provisions above and replace them with the notice
|
|
||||||
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
||||||
* the provisions above, a recipient may use your version of this file under
|
|
||||||
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
||||||
*
|
|
||||||
* ***** END LICENSE BLOCK ***** */
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @fileoverview
|
|
||||||
* The WidgetsAPI adds support for handling objects like trees.
|
|
||||||
*/
|
|
||||||
|
|
||||||
var EventUtils = {};
|
|
||||||
Components.utils.import('resource://mozmill/stdlib/EventUtils.js', EventUtils);
|
|
||||||
|
|
||||||
const gTimeout = 5000;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Click the specified tree cell
|
|
||||||
*
|
|
||||||
* @param {MozMillController} controller
|
|
||||||
* MozMillController of the browser window to operate on
|
|
||||||
* @param {tree} tree
|
|
||||||
* Tree to operate on
|
|
||||||
* @param {number } rowIndex
|
|
||||||
* Index of the row
|
|
||||||
* @param {number} columnIndex
|
|
||||||
* Index of the column
|
|
||||||
* @param {object} eventDetails
|
|
||||||
* Details about the mouse event
|
|
||||||
*/
|
|
||||||
function clickTreeCell(controller, tree, rowIndex, columnIndex, eventDetails)
|
|
||||||
{
|
|
||||||
tree = tree.getNode();
|
|
||||||
|
|
||||||
var selection = tree.view.selection;
|
|
||||||
selection.select(rowIndex);
|
|
||||||
tree.treeBoxObject.ensureRowIsVisible(rowIndex);
|
|
||||||
|
|
||||||
// get cell coordinates
|
|
||||||
var x = {}, y = {}, width = {}, height = {};
|
|
||||||
var column = tree.columns[columnIndex];
|
|
||||||
tree.treeBoxObject.getCoordsForCellItem(rowIndex, column, "text",
|
|
||||||
x, y, width, height);
|
|
||||||
|
|
||||||
controller.sleep(0);
|
|
||||||
EventUtils.synthesizeMouse(tree.body, x.value + 4, y.value + 4,
|
|
||||||
eventDetails, tree.ownerDocument.defaultView);
|
|
||||||
controller.sleep(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Export of functions
|
|
||||||
exports.clickTreeCell = clickTreeCell;
|
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
var setupModule = function (module) {
|
|
||||||
controller = mozmill.getBrowserController();
|
|
||||||
};
|
|
||||||
|
|
||||||
var testAboutPage_WhenOpened_PageIsLoadedWithExpectedTitle = function () {
|
|
||||||
const ABOUT_PAGE_URL = "about:pentadactyl";
|
|
||||||
const EXPECTED_TITLE = "About Pentadactyl";
|
|
||||||
const BLANK_PAGE_URL = "about:blank";
|
|
||||||
|
|
||||||
controller.open(BLANK_PAGE_URL);
|
|
||||||
controller.waitForPageLoad(controller.tabs.activeTab);
|
|
||||||
controller.open(ABOUT_PAGE_URL);
|
|
||||||
controller.waitForPageLoad(controller.tabs.activeTab);
|
|
||||||
|
|
||||||
controller.assert(function () controller.tabs.activeTab.title === EXPECTED_TITLE);
|
|
||||||
};
|
|
||||||
|
|
||||||
// vim: sw=4 ts=8 et:
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
var dactyllib = require("dactyl");
|
|
||||||
|
|
||||||
var setupModule = function (module) {
|
|
||||||
controller = mozmill.getBrowserController();
|
|
||||||
dactyl = new dactyllib.Controller(controller);
|
|
||||||
};
|
|
||||||
|
|
||||||
var teardownTest = function (test) {
|
|
||||||
dactyl.closeMessageWindow();
|
|
||||||
};
|
|
||||||
|
|
||||||
var testEchoCommand_SingleLineMessageAndClosedMOW_MessageDisplayedInMessageLine = function () {
|
|
||||||
const output = "foobar";
|
|
||||||
|
|
||||||
assertEchoGeneratesLineOutput({
|
|
||||||
ECHO_COMMAND: "echo " + output.quote(),
|
|
||||||
EXPECTED_OUTPUT: output
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var testEchoCommand_SingleLineMessageAndOpenMOW_MessageAppendedToMOW = function () {
|
|
||||||
const output = "foobar";
|
|
||||||
|
|
||||||
dactyl.openMessageWindow();
|
|
||||||
|
|
||||||
assertEchoGeneratesWindowOutput({
|
|
||||||
ECHO_COMMAND: "echo " + output.quote(),
|
|
||||||
EXPECTED_OUTPUT: RegExp(output)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var testEchoCommand_MultilineMessageAndClosedMOW_MessageDisplayedInMOW = function () {
|
|
||||||
const output = "foo\nbar";
|
|
||||||
|
|
||||||
assertEchoGeneratesWindowOutput({
|
|
||||||
ECHO_COMMAND: "echo " + output.quote(),
|
|
||||||
EXPECTED_OUTPUT: output
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var testEchoCommand_MultilineMessageAndOpenMOW_MessageAppendedToMOW = function () {
|
|
||||||
const output = "foo\nbar";
|
|
||||||
|
|
||||||
dactyl.openMessageWindow();
|
|
||||||
|
|
||||||
assertEchoGeneratesWindowOutput({
|
|
||||||
ECHO_COMMAND: "echo " + output.quote(),
|
|
||||||
EXPECTED_OUTPUT: RegExp(output)
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var testEchoCommand_ObjectArgumentAndClosedMOW_MessageDisplayedInMOW = function () {
|
|
||||||
assertEchoGeneratesWindowOutput({
|
|
||||||
ECHO_COMMAND: "echo var obj = { x: 1, y: 2 }; obj;",
|
|
||||||
EXPECTED_OUTPUT: "[object\u00A0Object]::\nx: 1\ny: 2\n"
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
function assertEchoGeneratesWindowOutput({ ECHO_COMMAND, EXPECTED_OUTPUT }) {
|
|
||||||
dactyl.runExCommand(ECHO_COMMAND);
|
|
||||||
dactyl.assertMessageWindow(EXPECTED_OUTPUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertEchoGeneratesLineOutput({ ECHO_COMMAND, EXPECTED_OUTPUT }) {
|
|
||||||
dactyl.runExCommand(ECHO_COMMAND);
|
|
||||||
dactyl.assertMessageLine(EXPECTED_OUTPUT);
|
|
||||||
}
|
|
||||||
|
|
||||||
// vim: sw=4 ts=8 et:
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
var dactyllib = require("dactyl");
|
|
||||||
|
|
||||||
const FIND_TEST_PAGE = collector.addHttpResource("./data/") + "find.html";
|
|
||||||
|
|
||||||
var setupModule = function (module) {
|
|
||||||
controller = mozmill.getBrowserController();
|
|
||||||
dactyl = new dactyllib.Controller(controller);
|
|
||||||
};
|
|
||||||
|
|
||||||
var setupTest = function (test) {
|
|
||||||
controller.open(FIND_TEST_PAGE);
|
|
||||||
controller.waitForPageLoad(controller.tabs.activeTab);
|
|
||||||
};
|
|
||||||
|
|
||||||
var testFindCommand_PresentAlphabeticText_TextSelected = function () {
|
|
||||||
assertTextFoundInPage("letter")
|
|
||||||
};
|
|
||||||
|
|
||||||
var testFindCommand_PresentNumericText_TextSelected = function () {
|
|
||||||
assertTextFoundInPage("3.141")
|
|
||||||
};
|
|
||||||
|
|
||||||
var testFindCommand_MissingText_ErrorMessageDisplayed = function () {
|
|
||||||
const MISSING_TEXT = "8c307545a017f60add90ef08955e148e";
|
|
||||||
const PATTERN_NOT_FOUND_ERROR = "E486: Pattern not found: " + MISSING_TEXT;
|
|
||||||
|
|
||||||
runTextSearchCommand(MISSING_TEXT);
|
|
||||||
|
|
||||||
dactyl.assertErrorMessage(PATTERN_NOT_FOUND_ERROR);
|
|
||||||
};
|
|
||||||
|
|
||||||
function runTextSearchCommand(str) {
|
|
||||||
dactyl.runViCommand("/" + str);
|
|
||||||
dactyl.runViCommand([["VK_RETURN"]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
function assertTextFoundInPage(text) {
|
|
||||||
runTextSearchCommand(text);
|
|
||||||
dactyl.assertSelection(text);
|
|
||||||
}
|
|
||||||
|
|
||||||
// vim: sw=4 ts=8 et:
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
var jumlib = {}; Components.utils.import("resource://mozmill/modules/jum.js", jumlib);
|
|
||||||
var dactyllib = require("dactyl");
|
|
||||||
|
|
||||||
var setupModule = function (module) {
|
|
||||||
controller = mozmill.getBrowserController();
|
|
||||||
dactyl = new dactyllib.Controller(controller);
|
|
||||||
};
|
|
||||||
|
|
||||||
var setupTest = function (test) {
|
|
||||||
dactyl.runViCommand([["VK_ESCAPE"]]);
|
|
||||||
};
|
|
||||||
|
|
||||||
const HELP_FILES = ["all", "tutorial", "intro", "starting", "browsing",
|
|
||||||
"buffer", "cmdline", "insert", "options", "pattern", "tabs", "hints",
|
|
||||||
"map", "eval", "marks", "repeat", "autocommands", "print", "gui",
|
|
||||||
"styling", "message", "developer", "various", "faq", "index", "plugins"];
|
|
||||||
|
|
||||||
var testViHelpCommand_OpensIntroHelpPage = function () {
|
|
||||||
assertHelpOpensPageWithTag({
|
|
||||||
HELP_COMMAND: function () { dactyl.runViCommand([["VK_F1"]]); },
|
|
||||||
EXPECTED_HELP_TAG: "intro.xml"
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var testViHelpAllCommand_OpensAllHelpPage = function () {
|
|
||||||
assertHelpOpensPageWithTag({
|
|
||||||
HELP_COMMAND: function () { dactyl.runViCommand([["VK_F1", { altKey: true }]]); },
|
|
||||||
EXPECTED_HELP_TAG: "all.xml"
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var testExHelpCommand_NoArgs_OpensIntroHelpPage = function () {
|
|
||||||
assertHelpOpensPageWithTag({
|
|
||||||
HELP_COMMAND: function () { dactyl.runExCommand("help"); },
|
|
||||||
EXPECTED_HELP_TAG: "intro.xml"
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var testExHelpAllCommand_NoArgs_OpensAllHelpPage = function () {
|
|
||||||
assertHelpOpensPageWithTag({
|
|
||||||
HELP_COMMAND: function () { dactyl.runExCommand("helpall"); },
|
|
||||||
EXPECTED_HELP_TAG: "all.xml"
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var testExHelpCommand_PageTagArg_OpensHelpPageContainingTag = function () {
|
|
||||||
for (let [, file] in Iterator(HELP_FILES)) {
|
|
||||||
let tag = file + ".xml";
|
|
||||||
assertHelpOpensPageWithTag({
|
|
||||||
HELP_COMMAND: function () { dactyl.runExCommand("help " + tag); },
|
|
||||||
EXPECTED_HELP_TAG: tag
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function assertHelpOpensPageWithTag({ HELP_COMMAND, EXPECTED_HELP_TAG }) {
|
|
||||||
HELP_COMMAND();
|
|
||||||
controller.waitForPageLoad(controller.tabs.activeTab);
|
|
||||||
controller.assertNode(new elementslib.ID(controller.tabs.activeTab, EXPECTED_HELP_TAG));
|
|
||||||
}
|
|
||||||
|
|
||||||
// vim: sw=4 ts=8 et:
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
var dactyllib = require("dactyl");
|
|
||||||
|
|
||||||
var setupModule = function (module) {
|
|
||||||
controller = mozmill.getBrowserController();
|
|
||||||
dactyl = new dactyllib.Controller(controller);
|
|
||||||
};
|
|
||||||
|
|
||||||
var teardownTest = function (test) {
|
|
||||||
dactyl.closeMessageWindow();
|
|
||||||
};
|
|
||||||
|
|
||||||
var testRunCommand_ExecutingOutputCommand_OutputDisplayed = function () {
|
|
||||||
const EXPECTED_OUTPUT = "foobar";
|
|
||||||
const COMMAND = "run echo " + EXPECTED_OUTPUT;
|
|
||||||
|
|
||||||
dactyl.runExCommand(COMMAND);
|
|
||||||
|
|
||||||
dactyl.assertMessageWindow(RegExp(EXPECTED_OUTPUT));
|
|
||||||
};
|
|
||||||
|
|
||||||
var testRunCommand_RepeatArg_LastCommandRepeated = function () {
|
|
||||||
const EXPECTED_OUTPUT = /foobar$/; // XXX
|
|
||||||
const COMMAND = "run echo 'foobar'";
|
|
||||||
const REPEAT_COMMAND = "run!";
|
|
||||||
|
|
||||||
dactyl.runExCommand(COMMAND);
|
|
||||||
dactyl.closeMessageWindow();
|
|
||||||
dactyl.runExCommand(REPEAT_COMMAND);
|
|
||||||
|
|
||||||
dactyl.assertMessageWindow(EXPECTED_OUTPUT);
|
|
||||||
};
|
|
||||||
|
|
||||||
// vim: sw=4 ts=8 et:
|
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
var dactyllib = require("dactyl");
|
|
||||||
|
|
||||||
var setupModule = function (module) {
|
|
||||||
controller = mozmill.getBrowserController();
|
|
||||||
dactyl = new dactyllib.Controller(controller);
|
|
||||||
};
|
|
||||||
|
|
||||||
var setupTest = function (test) {
|
|
||||||
dactyl.closeMessageWindow();
|
|
||||||
};
|
|
||||||
|
|
||||||
var testVersionCommand_NoArg_VersionStringDisplayed = function () {
|
|
||||||
const EXPECTED_OUTPUT = RegExp(dactyl.applicationName + ".+ (.+) running on:.+"); // XXX
|
|
||||||
|
|
||||||
dactyl.runExCommand("version");
|
|
||||||
|
|
||||||
dactyl.assertMessageWindow(EXPECTED_OUTPUT);
|
|
||||||
};
|
|
||||||
|
|
||||||
var testVersionCommand_BangArg_HostAppVersionPageDisplayed = function () {
|
|
||||||
const EXPECTED_URL = "about:";
|
|
||||||
const EXPECTED_TITLE = "About:";
|
|
||||||
const BLANK_PAGE_URL = "about:blank";
|
|
||||||
|
|
||||||
controller.open(BLANK_PAGE_URL);
|
|
||||||
controller.waitForPageLoad(controller.tabs.activeTab);
|
|
||||||
dactyl.runExCommand("version!");
|
|
||||||
controller.waitForPageLoad(controller.tabs.activeTab);
|
|
||||||
|
|
||||||
controller.assert(function () controller.tabs.activeTab.location.href === EXPECTED_URL);
|
|
||||||
controller.assert(function () controller.tabs.activeTab.title === EXPECTED_TITLE);
|
|
||||||
};
|
|
||||||
|
|
||||||
// vim: sw=4 ts=8 et:
|
|
||||||
184
pentadactyl/NEWS
184
pentadactyl/NEWS
@@ -2,23 +2,24 @@
|
|||||||
* Extensive Firefox 4 support, including:
|
* Extensive Firefox 4 support, including:
|
||||||
- Fully restartless. Can now be installed, uninstalled,
|
- Fully restartless. Can now be installed, uninstalled,
|
||||||
enabled, disabled, and upgraded without restarting Firefox.
|
enabled, disabled, and upgraded without restarting Firefox.
|
||||||
|
[b4]
|
||||||
- Tabs in :buffer completions and listings are grouped
|
- Tabs in :buffer completions and listings are grouped
|
||||||
by panorama groups.
|
by panorama groups. [b1]
|
||||||
- Only visible tabs are considered in tab numbering,
|
- Only visible tabs are considered in tab numbering,
|
||||||
gt/gn/gN, etc.
|
gt/gn/gN, etc. [b1]
|
||||||
* Improved startup time by a factor of 7.
|
* Improved startup time by a factor of 7. [b1]
|
||||||
* Significant completion speed improvements, especially for
|
* Significant completion speed improvements, especially for
|
||||||
JavaScript.
|
JavaScript. [b1]
|
||||||
* Greatly improved private mode support and :sanitize command.
|
* Greatly improved private mode support and :sanitize command.
|
||||||
- Full integration with Firefox data clearing dialogs.
|
- Full integration with Firefox data clearing dialogs. [b3]
|
||||||
- Support for sanitizing items at shutdown.
|
- Support for sanitizing items at shutdown. [b3]
|
||||||
- Fine-grained control over what data is sanitized and for
|
- Fine-grained control over what data is sanitized and for
|
||||||
what timespan.
|
what timespan. [b1]
|
||||||
- Support for sanitizing reference to particular hosts,
|
- Support for sanitizing reference to particular hosts,
|
||||||
everywhere from command-line and message history to option
|
everywhere from command-line and message history to option
|
||||||
values and cookies.
|
values and cookies. [b1]
|
||||||
* New and much more powerful incremental search implementation.
|
* New and much more powerful incremental search implementation.
|
||||||
Improvements over the standard Firefox find include:
|
Improvements over the standard Firefox find include: [b1]
|
||||||
- Starts at the cursor position in the currently selected
|
- Starts at the cursor position in the currently selected
|
||||||
frame, unlike Firefox, which always starts at the start of
|
frame, unlike Firefox, which always starts at the start of
|
||||||
the first frame.
|
the first frame.
|
||||||
@@ -28,135 +29,146 @@
|
|||||||
backspace.
|
backspace.
|
||||||
- Supports reverse incremental search.
|
- Supports reverse incremental search.
|
||||||
- Input boxes are not focused when matches are highlighted.
|
- Input boxes are not focused when matches are highlighted.
|
||||||
* It's now possible to map keys in many more modes, including
|
|
||||||
Hint, Multi-line Output, and Menu.
|
|
||||||
* Ex command parsing improvements, including:
|
* Ex command parsing improvements, including:
|
||||||
- Multiple Ex commands may now be separated by |
|
- Multiple Ex commands may now be separated by | [b1]
|
||||||
- Commands can continue over multiple lines in RC files by
|
- Commands can continue over multiple lines in RC files by
|
||||||
prefixing the continuation lines with a \
|
prefixing the continuation lines with a \ [b3]
|
||||||
- The \ character is no longer treated specially within single
|
- The \ character is no longer treated specially within single
|
||||||
quotes, i.e., 'fo\o''bar' ⇒ fo\o'bar
|
quotes, i.e., 'fo\o''bar' ⇒ fo\o'bar [b1]
|
||||||
* Command line is now hidden by default. Added c, C, and M to
|
* Command line is now hidden by default. Added c, C, and M to
|
||||||
'guioptions'.
|
'guioptions'. [b4]
|
||||||
* Hint mode improvements, including:
|
* Hint mode improvements, including:
|
||||||
- Added g; continued extended hint mode, which allows
|
- Added g; continued extended hint mode, which allows
|
||||||
selecting multiple hints. Removed ;F.
|
selecting multiple hints. Removed ;F. [b1]
|
||||||
- Hints are now updated after scrolling and window resizing.
|
- Hints are now updated after scrolling and window resizing. [b3]
|
||||||
- ;s now prompts for a filename on the command-line rather
|
- ;s now prompts for a filename on the command-line rather
|
||||||
than in a dialog.
|
than in a dialog. [b5]
|
||||||
- Added ;a and ;S modes for creating bookmarks and search keywords.
|
- Added ;a and ;S modes for creating bookmarks and search keywords. [b4][b3]
|
||||||
- Added 'hintkeys' option.
|
- Added 'hintkeys' option. [b2]
|
||||||
- Added "transliterated" option to 'hintmatching'.
|
- Added "transliterated" option to 'hintmatching'. [b1]
|
||||||
* JavaScript completion improvements, including:
|
* JavaScript completion improvements, including: [b2]
|
||||||
- The prototype of the function whose arguments are currently
|
- The prototype of the function whose arguments are currently
|
||||||
being typed is displayed during completion.
|
being typed is displayed during completion.
|
||||||
- Non-enumerable global properties are now completed for the
|
- Non-enumerable global properties are now completed for the
|
||||||
global object, including XMLHttpRequest and encodeURI.
|
global object, including XMLHttpRequest and encodeURI.
|
||||||
* The concept of completion contexts is now exposed to the user
|
* The concept of completion contexts is now exposed to the user
|
||||||
(see :h :contexts), allowing for powerful and fine-grained
|
(see :h :contexts), allowing for powerful and fine-grained
|
||||||
completion system customization.
|
completion system customization. [b1]
|
||||||
* The external editor can now be configured to open to a given
|
* The external editor can now be configured to open to a given
|
||||||
line number and column, used for opening source links and
|
line number and column, used for opening source links and
|
||||||
editing input fields with i_<C-i>. See :h 'editor'.
|
editing input fields with i_<C-i>. See :h 'editor'. [b4]
|
||||||
|
* Mapping changes:
|
||||||
|
- It's now possible to map keys in many more modes, including
|
||||||
|
Hint, Multi-line Output, and Menu. [b4]
|
||||||
|
- Added site-specific mapping groups and related command
|
||||||
|
changes. [b6]
|
||||||
|
- Added 'timeout' and 'timeoutlen' options. [b6]
|
||||||
|
- Added <A-m>l and <A-m>s to aid in the construction of
|
||||||
|
macros. [b6]
|
||||||
|
- Removed the implicit page load delays during macro playback. [b6]
|
||||||
|
- Added the base modes Base, Main, and Command which other
|
||||||
|
modes inherit key bindings from. [b6]
|
||||||
* Command changes:
|
* Command changes:
|
||||||
- :viusage, :optionusage and :exusage were replaced with :listkeys,
|
- :viusage, :optionusage and :exusage were replaced with :listkeys,
|
||||||
:listoptions and :listcommands, providing more powerful and
|
:listoptions and :listcommands, providing more powerful and
|
||||||
consistent interactive help facility (improvements include
|
consistent interactive help facility (improvements include
|
||||||
listing keys for modes other than Normal, filtering the output
|
listing keys for modes other than Normal, filtering the output
|
||||||
and linking to source code locations).
|
and linking to source code locations). [b4]
|
||||||
- :downloads now opens a download list in the multi-line output
|
- :downloads now opens a download list in the multi-line output
|
||||||
buffer.
|
buffer.
|
||||||
|
- Added :mapgroup command and -group flag to :map, :unmap, and
|
||||||
|
:mapclear. [b6]
|
||||||
- :extensions has been replaced with a more powerful :addons.
|
- :extensions has been replaced with a more powerful :addons.
|
||||||
- Added :cookies command.
|
- Added :cookies command. [b3]
|
||||||
- :extadd now supports remote URLs as well as local files on Firefox 4.
|
- :extadd now supports remote URLs as well as local files on Firefox 4. [b2]
|
||||||
- Added :if/:elseif/:else/:endif conditionals.
|
- Added :if/:elseif/:else/:endif conditionals. [b3]
|
||||||
- Added -charset and -post to :bmark.
|
- Added -charset and -post to :bmark. [b5]
|
||||||
- Added -keyword, -tags, -title to :delbmarks.
|
- Added -keyword, -tags, -title to :delbmarks. [b2]
|
||||||
- Added :extrehash, :exttoggle, :extupdate, and :rehash commands.
|
- Added :extrehash, :exttoggle, :extupdate, and :rehash commands. [b5]
|
||||||
- Added :feedkeys command.
|
- Added :feedkeys command. [b4]
|
||||||
- Added -sort option to :history.
|
- Added -sort option to :history. [b4]
|
||||||
- Added several new options, including -javascript, to :abbreviate and
|
- Added several new options, including -javascript, to :abbreviate and
|
||||||
:map.
|
:map. [b2]
|
||||||
- Added :mksyntax command to auto-generate Vim syntax files.
|
- Added :mksyntax command to auto-generate Vim syntax files. [b4]
|
||||||
- :open now only opens files beginning with /, ./, ../, or ~/
|
- :open now only opens files beginning with /, ./, ../, or ~/ [b1]
|
||||||
- :saveas now provides completions for default file names, and
|
- :saveas now provides completions for default file names, and
|
||||||
automatically chooses a filename when the save target is a
|
automatically chooses a filename when the save target is a
|
||||||
directory.
|
directory. [b4]
|
||||||
- :sidebar now accepts a ! flag to toggle the sidebar rather
|
- :sidebar now accepts a ! flag to toggle the sidebar rather
|
||||||
than open it unconditionally.
|
than open it unconditionally. [b6]
|
||||||
- Added :write !cmd and :write >>file.
|
- Added :write !cmd and :write >>file. [b3]
|
||||||
- Added :yank command.
|
- Added :yank command. [b3]
|
||||||
- :delmarks, :marks and :qmarks now also accept ranges, same as
|
- :delmarks, :marks and :qmarks now also accept ranges, same as
|
||||||
:delqmarks.
|
:delqmarks.
|
||||||
- :command now accepts comma-separated alternative command names.
|
- :command now accepts comma-separated alternative command names.
|
||||||
- :command -complete custom now also accepts a completions array, see
|
- :command -complete custom now also accepts a completions array, see
|
||||||
:h :command-completion-custom.
|
:h :command-completion-custom.
|
||||||
* Improvements to :style and :highlight:
|
* Improvements to :style and :highlight:
|
||||||
- Added -agent flag to :style.
|
- Added -link flag to :highlight. [b4]
|
||||||
|
- Added -agent flag to :style. [b2]
|
||||||
- The -append flag now updates existing properties rather than
|
- The -append flag now updates existing properties rather than
|
||||||
simply appending its arguments to the previous value.
|
simply appending its arguments to the previous value. [b4]
|
||||||
- Active filters are now highlighted in :style listings.
|
- Active filters are now highlighted in :style listings. [b4]
|
||||||
- :style-related commands now divide their completions between
|
- :style-related commands now divide their completions between
|
||||||
those active and inactive for the current site.
|
those active and inactive for the current site. [b4]
|
||||||
- CSS property name completion is now available.
|
- CSS property name completion is now available. [b4]
|
||||||
* IMPORTANT option changes:
|
* IMPORTANT option changes:
|
||||||
- Boolean options no longer accept an argument.
|
- Option value quoting has changed. List options will
|
||||||
|
no longer be split at quoted commas and the option name, operators, and
|
||||||
|
= sign may no longer be quoted. This will break certain
|
||||||
|
automatically-generated configuration files. See :help stringlist. [b2]
|
||||||
|
- Boolean options no longer accept an argument. [b4]
|
||||||
- 'cdpath' and 'runtimepath' no longer treat ",,"
|
- 'cdpath' and 'runtimepath' no longer treat ",,"
|
||||||
specially. Use "." instead.
|
specially. Use "." instead. [b2]
|
||||||
- 'incsearch', 'hlsearch', 'ignorecase', and 'smartcase' have
|
- 'incsearch', 'hlsearch', 'ignorecase', and 'smartcase' have
|
||||||
been replaced with 'incfind', 'hlfind', and 'findcase'.
|
been replaced with 'incfind', 'hlfind', and 'findcase'. [b4]
|
||||||
- 'extendedhinttags' is now a regexpmap rather than a
|
- 'extendedhinttags' is now a regexpmap rather than a
|
||||||
string.
|
string. [b2]
|
||||||
- 'guioptions' default value has changed.
|
- 'guioptions' default value has changed. [b4]
|
||||||
- 'laststatus' has been replaced with the "s" flag in
|
- 'laststatus' has been replaced with the "s" flag in
|
||||||
'guioptions'.
|
'guioptions'. [b4]
|
||||||
- 'linksearch' has been removed. The \l search modifier can
|
- 'linksearch' has been removed. The \l search modifier can
|
||||||
still be used for this purpose.
|
still be used for this purpose. [b4]
|
||||||
- 'loadplugins' is now a regexplist option rather than
|
- 'loadplugins' is now a regexplist option rather than
|
||||||
a boolean.
|
a boolean. [b2]
|
||||||
- 'mapleader' is now an option rather than a :let
|
- 'mapleader' is now an option rather than a :let
|
||||||
variable.
|
variable. [b4]
|
||||||
- 'showstatuslinks' and 'showtabline' are now string options.
|
- 'showstatuslinks' and 'showtabline' are now string options. [b4]
|
||||||
* IMPORTANT: Command script files now use the *.penta file extension.
|
* IMPORTANT: Command script files now use the *.penta file extension. [b2]
|
||||||
* IMPORTANT: Plugins are now loaded from the 'plugins/'
|
* IMPORTANT: Plugins are now loaded from the 'plugins/'
|
||||||
directory in 'runtimepath' rather than 'plugin/'.
|
directory in 'runtimepath' rather than 'plugin/'. [b1]
|
||||||
* IMPORTANT: Option value quoting has changed. List options will
|
|
||||||
no longer be split at quoted commas and the option name,
|
|
||||||
operators, and = sign may no longer be quoted. This will break
|
|
||||||
certain automatically-generated configuration files.
|
|
||||||
See :help stringlist.
|
|
||||||
* Option changes:
|
* Option changes:
|
||||||
- Added "bookmarks", "diverted", and "links" to 'activate'
|
- Added "bookmarks", "diverted", and "links" to 'activate' [b2]
|
||||||
option.
|
option.
|
||||||
- Added 'altwildmode' and c_<A-Tab> command-line key binding.
|
- Added 'altwildmode' and c_<A-Tab> command-line key binding. [b2]
|
||||||
- Added 'autocomplete' option for specifying which completion
|
- Added 'autocomplete' option for specifying which completion
|
||||||
groups should be auto-completed.
|
groups should be auto-completed. [b2]
|
||||||
- Added 'banghist' option.
|
- Added 'banghist' option. [b1]
|
||||||
- Replaced 'focuscontent' with 'strictfocus'.
|
- Replaced 'focuscontent' with 'strictfocus'. [b1]
|
||||||
- 'complete' now defaults to "slf" but file completion only
|
- 'complete' now defaults to "slf" but file completion only
|
||||||
triggers when the URL begins as above.
|
triggers when the URL begins as above. [b1]
|
||||||
- Added 'passkeys' option.
|
- Added 'passkeys' option. [b3]
|
||||||
- Changed 'urlseparator' default value to "|".
|
- Changed 'urlseparator' default value to "|". [b3]
|
||||||
- Added "passwords" and "venkman" dialogs to :dialog.
|
- Added "passwords" and "venkman" dialogs to :dialog. [b2]
|
||||||
- Added 'wildanchor' option.
|
- Added 'wildanchor' option. [b2]
|
||||||
- Added 'cookies', 'cookieaccept', and 'cookielifetime' options.
|
- Added 'cookies', 'cookieaccept', and 'cookielifetime' options. [b3]
|
||||||
* Added BookmarkChange, BookmarkRemove autocommands.
|
* Added BookmarkChange, BookmarkRemove autocommands. [b2]
|
||||||
* Removed the :source line at the end of files generated by
|
* Removed the :source line at the end of files generated by
|
||||||
:mkpentadactylrc.
|
:mkpentadactylrc. [b2]
|
||||||
* gf now toggles between source and content view.
|
* gf now toggles between source and content view.
|
||||||
The | key binding has been removed.
|
The | key binding has been removed. [b1]
|
||||||
* Page zoom information is now shown in the status bar, and
|
* Page zoom information is now shown in the status bar, and
|
||||||
change in zoom status no longer appears in :messages.
|
change in zoom status no longer appears in :messages. [b1]
|
||||||
* Added ZO, ZI, ZM, and ZR as aliases for zO, zI, zM, and zR.
|
* Added ZO, ZI, ZM, and ZR as aliases for zO, zI, zM, and zR. [b1]
|
||||||
* Completion list now behaves better when the multi-line output
|
* Completion list now behaves better when the multi-line output
|
||||||
window is displayed.
|
window is displayed. [b1]
|
||||||
* Major help system improvements:
|
* Major help system improvements:
|
||||||
- Plugins may now provide full-fledged :help documentation.
|
- Plugins may now provide full-fledged :help documentation. [b1]
|
||||||
- Add basic plugin authorship documentation.
|
- Add basic plugin authorship documentation. [b1]
|
||||||
- The help system is newly modularized and features significant
|
- The help system is newly modularized and features significant
|
||||||
updates, rewrites, and formatting improvements.
|
updates, rewrites, and formatting improvements. [b1]
|
||||||
- Added <A-F1> to open the single unchunked help page.
|
- Added <A-F1> to open the single unchunked help page. [b5]
|
||||||
* Removed :beep.
|
* Removed :beep. [b2]
|
||||||
* Removed :edit, :tabedit, and :winedit aliases.
|
* Removed :edit, :tabedit, and :winedit aliases. [b2]
|
||||||
* Removed :play.
|
* Removed :play. [b2]
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user