зеркало из https://github.com/mozilla/gecko-dev.git
Merge m-c to b2g-inbound.
This commit is contained in:
Коммит
9cc0ca802c
|
@ -20,6 +20,10 @@ let promise = require("sdk/core/promise");
|
|||
Cu.import("resource:///modules/devtools/LayoutHelpers.jsm");
|
||||
Cu.import("resource://gre/modules/devtools/Templater.jsm");
|
||||
Cu.import("resource://gre/modules/Services.jsm");
|
||||
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
|
||||
XPCOMUtils.defineLazyGetter(this, "AutocompletePopup", function() {
|
||||
return Cu.import("resource:///modules/devtools/AutocompletePopup.jsm", {}).AutocompletePopup;
|
||||
});
|
||||
|
||||
/**
|
||||
* Vocabulary for the purposes of this file:
|
||||
|
@ -53,6 +57,14 @@ function MarkupView(aInspector, aFrame, aControllerWindow)
|
|||
this.maxChildren = DEFAULT_MAX_CHILDREN;
|
||||
}
|
||||
|
||||
// Creating the popup to be used to show CSS suggestions.
|
||||
let options = {
|
||||
fixedWidth: true,
|
||||
autoSelect: true,
|
||||
theme: "auto"
|
||||
};
|
||||
this.popup = new AutocompletePopup(this.doc.defaultView.parent.document, options);
|
||||
|
||||
this.undo = new UndoStack();
|
||||
this.undo.installController(aControllerWindow);
|
||||
|
||||
|
@ -676,6 +688,9 @@ MarkupView.prototype = {
|
|||
this.undo.destroy();
|
||||
delete this.undo;
|
||||
|
||||
this.popup.destroy();
|
||||
delete this.popup;
|
||||
|
||||
this._frame.removeEventListener("focus", this._boundFocus, false);
|
||||
delete this._boundFocus;
|
||||
|
||||
|
@ -1130,6 +1145,8 @@ function ElementEditor(aContainer, aNode)
|
|||
element: this.newAttr,
|
||||
trigger: "dblclick",
|
||||
stopOnReturn: true,
|
||||
contentType: InplaceEditor.CONTENT_TYPES.CSS_MIXED,
|
||||
popup: this.markup.popup,
|
||||
done: (aVal, aCommit) => {
|
||||
if (!aCommit) {
|
||||
return;
|
||||
|
@ -1222,6 +1239,8 @@ ElementEditor.prototype = {
|
|||
trigger: "dblclick",
|
||||
stopOnReturn: true,
|
||||
selectAll: false,
|
||||
contentType: InplaceEditor.CONTENT_TYPES.CSS_MIXED,
|
||||
popup: this.markup.popup,
|
||||
start: (aEditor, aEvent) => {
|
||||
// If the editing was started inside the name or value areas,
|
||||
// select accordingly.
|
||||
|
|
|
@ -19,6 +19,7 @@ MOCHITEST_BROWSER_FILES := \
|
|||
browser_inspector_markup_edit.js \
|
||||
browser_inspector_markup_subset.html \
|
||||
browser_inspector_markup_subset.js \
|
||||
browser_bug896181_css_mixed_completion_new_attribute.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -0,0 +1,167 @@
|
|||
/* vim: set ts=2 et sw=2 tw=80: */
|
||||
/* Any copyright is dedicated to the Public Domain.
|
||||
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||
|
||||
// Test CSS state is correctly determined and the corresponding suggestions are
|
||||
// displayed. i.e. CSS property suggestions are shown when cursor is like:
|
||||
// ```style="di|"``` where | is teh cursor; And CSS value suggestion is
|
||||
// displayed when the cursor is like: ```style="display:n|"``` properly. No
|
||||
// suggestions should ever appear when the attribute is not a style attribute.
|
||||
// The correctness and cycling of the suggestions is covered in the ruleview
|
||||
// tests.
|
||||
|
||||
function test() {
|
||||
let inspector;
|
||||
let {
|
||||
getInplaceEditorForSpan: inplaceEditor
|
||||
} = devtools.require("devtools/shared/inplace-editor");
|
||||
|
||||
waitForExplicitFinish();
|
||||
|
||||
// Will hold the doc we're viewing
|
||||
let doc;
|
||||
|
||||
// Holds the MarkupTool object we're testing.
|
||||
let markup;
|
||||
let editor;
|
||||
let state;
|
||||
// format :
|
||||
// [
|
||||
// what key to press,
|
||||
// expected input box value after keypress,
|
||||
// expected input.selectionStart,
|
||||
// expected input.selectionEnd,
|
||||
// is popup expected to be open ?
|
||||
// ]
|
||||
let testData = [
|
||||
['s', 's', 1, 1, false],
|
||||
['t', 'st', 2, 2, false],
|
||||
['y', 'sty', 3, 3, false],
|
||||
['l', 'styl', 4, 4, false],
|
||||
['e', 'style', 5, 5, false],
|
||||
['=', 'style=', 6, 6, false],
|
||||
['"', 'style="', 7, 7, false],
|
||||
['d', 'style="direction', 8, 16, true],
|
||||
['VK_DOWN', 'style="display', 8, 14, true],
|
||||
['VK_RIGHT', 'style="display', 14, 14, false],
|
||||
[':', 'style="display:', 15, 15, false],
|
||||
['n', 'style="display:none', 16, 19, false],
|
||||
['VK_BACK_SPACE', 'style="display:n', 16, 16, false],
|
||||
['VK_BACK_SPACE', 'style="display:', 15, 15, false],
|
||||
[' ', 'style="display: ', 16, 16, false],
|
||||
[' ', 'style="display: ', 17, 17, false],
|
||||
['i', 'style="display: inherit', 18, 24, true],
|
||||
['VK_RIGHT', 'style="display: inherit', 24, 24, false],
|
||||
[';', 'style="display: inherit;', 25, 25, false],
|
||||
[' ', 'style="display: inherit; ', 26, 26, false],
|
||||
[' ', 'style="display: inherit; ', 27, 27, false],
|
||||
['VK_LEFT', 'style="display: inherit; ', 26, 26, false],
|
||||
['c', 'style="display: inherit; caption-side ', 27, 38, true],
|
||||
['o', 'style="display: inherit; color ', 28, 31, true],
|
||||
['VK_RIGHT', 'style="display: inherit; color ', 31, 31, false],
|
||||
[' ', 'style="display: inherit; color ', 32, 32, false],
|
||||
['c', 'style="display: inherit; color c ', 33, 33, false],
|
||||
['VK_BACK_SPACE', 'style="display: inherit; color ', 32, 32, false],
|
||||
[':', 'style="display: inherit; color : ', 33, 33, false],
|
||||
['c', 'style="display: inherit; color :cadetblue ', 34, 42, true],
|
||||
['VK_DOWN', 'style="display: inherit; color :chartreuse ', 34, 43, true],
|
||||
['VK_RETURN', 'style="display: inherit; color :chartreuse"', -1, -1, false]
|
||||
];
|
||||
|
||||
function startTests() {
|
||||
markup = inspector.markup;
|
||||
markup.expandAll().then(() => {
|
||||
let node = getContainerForRawNode(markup, doc.querySelector("#node14")).editor;
|
||||
let attr = node.newAttr;
|
||||
attr.focus();
|
||||
EventUtils.sendKey("return", inspector.panelWin);
|
||||
editor = inplaceEditor(attr);
|
||||
checkStateAndMoveOn(0);
|
||||
});
|
||||
}
|
||||
|
||||
function checkStateAndMoveOn(index) {
|
||||
if (index == testData.length) {
|
||||
finishUp();
|
||||
return;
|
||||
}
|
||||
|
||||
let [key] = testData[index];
|
||||
state = index;
|
||||
|
||||
info("pressing key " + key + " to get result: [" + testData[index].slice(1) +
|
||||
"] for state " + state);
|
||||
if (/(down|left|right|back_space|return)/ig.test(key)) {
|
||||
info("added event listener for down|left|right|back_space|return keys");
|
||||
editor.input.addEventListener("keypress", function onKeypress() {
|
||||
if (editor.input) {
|
||||
editor.input.removeEventListener("keypress", onKeypress);
|
||||
}
|
||||
info("inside event listener");
|
||||
checkState();
|
||||
})
|
||||
}
|
||||
else {
|
||||
editor.once("after-suggest", checkState);
|
||||
}
|
||||
EventUtils.synthesizeKey(key, {}, inspector.panelWin);
|
||||
}
|
||||
|
||||
function checkState() {
|
||||
executeSoon(() => {
|
||||
info("After keypress for state " + state);
|
||||
let [key, completion, selStart, selEnd, popupOpen] = testData[state];
|
||||
if (selEnd != -1) {
|
||||
is(editor.input.value, completion,
|
||||
"Correct value is autocompleted for state " + state);
|
||||
is(editor.input.selectionStart, selStart,
|
||||
"Selection is starting at the right location for state " + state);
|
||||
is(editor.input.selectionEnd, selEnd,
|
||||
"Selection is ending at the right location for state " + state);
|
||||
if (popupOpen) {
|
||||
ok(editor.popup._panel.state == "open" ||
|
||||
editor.popup._panel.state == "showing",
|
||||
"Popup is open for state " + state);
|
||||
}
|
||||
else {
|
||||
ok(editor.popup._panel.state != "open" &&
|
||||
editor.popup._panel.state != "showing",
|
||||
"Popup is closed for state " + state);
|
||||
}
|
||||
}
|
||||
else {
|
||||
let editor = getContainerForRawNode(markup, doc.querySelector("#node14")).editor;
|
||||
let attr = editor.attrs["style"].querySelector(".editable");
|
||||
is(attr.textContent, completion,
|
||||
"Correct value is persisted after pressing Enter for state " + state);
|
||||
}
|
||||
checkStateAndMoveOn(state + 1);
|
||||
});
|
||||
}
|
||||
|
||||
// Create the helper tab for parsing...
|
||||
gBrowser.selectedTab = gBrowser.addTab();
|
||||
gBrowser.selectedBrowser.addEventListener("load", function onload() {
|
||||
gBrowser.selectedBrowser.removeEventListener("load", onload, true);
|
||||
doc = content.document;
|
||||
waitForFocus(setupTest, content);
|
||||
}, true);
|
||||
content.location = "http://mochi.test:8888/browser/browser/devtools/markupview/test/browser_inspector_markup_edit.html";
|
||||
|
||||
function setupTest() {
|
||||
var target = TargetFactory.forTab(gBrowser.selectedTab);
|
||||
gDevTools.showToolbox(target, "inspector").then(function(toolbox) {
|
||||
inspector = toolbox.getCurrentPanel();
|
||||
startTests();
|
||||
});
|
||||
}
|
||||
|
||||
function finishUp() {
|
||||
while (markup.undo.canUndo()) {
|
||||
markup.undo.undo();
|
||||
}
|
||||
doc = inspector = null;
|
||||
gBrowser.removeCurrentTab();
|
||||
finish();
|
||||
}
|
||||
}
|
|
@ -759,7 +759,13 @@ InplaceEditor.prototype = {
|
|||
} else {
|
||||
this.popup.selectNextItem();
|
||||
}
|
||||
this.input.value = this.popup.selectedItem.label;
|
||||
let input = this.input;
|
||||
let pre = input.value.slice(0, input.selectionStart);
|
||||
let post = input.value.slice(input.selectionEnd, input.value.length);
|
||||
let item = this.popup.selectedItem;
|
||||
let toComplete = item.label.slice(item.preLabel.length);
|
||||
input.value = pre + toComplete + post;
|
||||
input.setSelectionRange(pre.length, pre.length + toComplete.length);
|
||||
this._updateSize();
|
||||
// This emit is mainly for the purpose of making the test flow simpler.
|
||||
this.emit("after-suggest");
|
||||
|
@ -898,6 +904,7 @@ InplaceEditor.prototype = {
|
|||
return;
|
||||
}
|
||||
let query = input.value.slice(0, input.selectionStart);
|
||||
let startCheckQuery = query;
|
||||
if (!query) {
|
||||
return;
|
||||
}
|
||||
|
@ -906,28 +913,52 @@ InplaceEditor.prototype = {
|
|||
if (this.contentType == CONTENT_TYPES.CSS_PROPERTY) {
|
||||
list = CSSPropertyList;
|
||||
} else if (this.contentType == CONTENT_TYPES.CSS_VALUE) {
|
||||
list = domUtils.getCSSValuesForProperty(this.property.name).sort();
|
||||
list = domUtils.getCSSValuesForProperty(this.property.name);
|
||||
} else if (this.contentType == CONTENT_TYPES.CSS_MIXED &&
|
||||
/^\s*style\s*=/.test(query)) {
|
||||
// Detecting if cursor is at property or value;
|
||||
let match = query.match(/([:;"'=]?)\s*([^"';:= ]+)$/);
|
||||
if (match && match.length == 3) {
|
||||
if (match[1] == ":") { // We are in CSS value completion
|
||||
let propertyName =
|
||||
query.match(/[;"'=]\s*([^"';:= ]+)\s*:\s*[^"';:= ]+$/)[1];
|
||||
list = domUtils.getCSSValuesForProperty(propertyName);
|
||||
startCheckQuery = match[2];
|
||||
} else if (match[1]) { // We are in CSS property name completion
|
||||
list = CSSPropertyList;
|
||||
startCheckQuery = match[2];
|
||||
}
|
||||
if (!startCheckQuery) {
|
||||
// This emit is mainly to make the test flow simpler.
|
||||
this.emit("after-suggest", "nothing to autocomplete");
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
list.some(item => {
|
||||
if (item.startsWith(query)) {
|
||||
input.value = item;
|
||||
input.setSelectionRange(query.length, item.length);
|
||||
if (item.startsWith(startCheckQuery)) {
|
||||
input.value = query + item.slice(startCheckQuery.length) +
|
||||
input.value.slice(query.length);
|
||||
input.setSelectionRange(query.length, query.length + item.length -
|
||||
startCheckQuery.length);
|
||||
this._updateSize();
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
||||
if (!this.popup) {
|
||||
// This emit is mainly to make the test flow simpler.
|
||||
this.emit("after-suggest", "no popup");
|
||||
return;
|
||||
}
|
||||
let finalList = [];
|
||||
let length = list.length;
|
||||
for (let i = 0, count = 0; i < length && count < MAX_POPUP_ENTRIES; i++) {
|
||||
if (list[i].startsWith(query)) {
|
||||
if (list[i].startsWith(startCheckQuery)) {
|
||||
count++;
|
||||
finalList.push({
|
||||
preLabel: query,
|
||||
preLabel: startCheckQuery,
|
||||
label: list[i]
|
||||
});
|
||||
}
|
||||
|
@ -936,7 +967,7 @@ InplaceEditor.prototype = {
|
|||
// which would have started with query, assuming that list is sorted.
|
||||
break;
|
||||
}
|
||||
else if (list[i][0] > query[0]) {
|
||||
else if (list[i][0] > startCheckQuery[0]) {
|
||||
// We have crossed all possible matches alphabetically.
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -39,11 +39,6 @@ let testData = [
|
|||
["VK_UP", "direction", 0, 3],
|
||||
["VK_UP", "dominant-baseline", 2, 3],
|
||||
["VK_UP", "display", 1, 3],
|
||||
["VK_BACK_SPACE", "displa", -1, 0],
|
||||
["VK_BACK_SPACE", "displ", -1, 0],
|
||||
["VK_BACK_SPACE", "disp", -1, 0],
|
||||
["VK_BACK_SPACE", "dis", -1, 0],
|
||||
["VK_BACK_SPACE", "di", -1, 0],
|
||||
["VK_BACK_SPACE", "d", -1, 0],
|
||||
["i", "direction", 0, 2],
|
||||
["s", "display", -1, 0],
|
||||
|
|
|
@ -27,11 +27,6 @@ let testData = [
|
|||
["VK_UP", "direction", 0, 3],
|
||||
["VK_UP", "dominant-baseline", 2, 3],
|
||||
["VK_UP", "display", 1, 3],
|
||||
["VK_BACK_SPACE", "displa", -1, 0],
|
||||
["VK_BACK_SPACE", "displ", -1, 0],
|
||||
["VK_BACK_SPACE", "disp", -1, 0],
|
||||
["VK_BACK_SPACE", "dis", -1, 0],
|
||||
["VK_BACK_SPACE", "di", -1, 0],
|
||||
["VK_BACK_SPACE", "d", -1, 0],
|
||||
["i", "direction", 0, 2],
|
||||
["s", "display", -1, 0],
|
||||
|
|
|
@ -144,6 +144,7 @@ MOCHITEST_BROWSER_FILES = \
|
|||
browser_console_iframe_messages.js \
|
||||
browser_console_variables_view_while_debugging_and_inspecting.js \
|
||||
browser_webconsole_bug_686937_autocomplete_JSTerm_helpers.js \
|
||||
browser_webconsole_cached_autocomplete.js \
|
||||
head.js \
|
||||
$(NULL)
|
||||
|
||||
|
|
|
@ -0,0 +1,125 @@
|
|||
/* vim:set ts=2 sw=2 sts=2 et: */
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
// Tests that the cached autocomplete results are used when the new
|
||||
// user input is a subset of the existing completion results.
|
||||
|
||||
const TEST_URI = "data:text/html;charset=utf8,<p>test cached autocompletion results";
|
||||
|
||||
let testDriver;
|
||||
|
||||
function test() {
|
||||
requestLongerTimeout(2);
|
||||
addTab(TEST_URI);
|
||||
browser.addEventListener("load", function onLoad() {
|
||||
browser.removeEventListener("load", onLoad, true);
|
||||
openConsole(null, function(hud) {
|
||||
testDriver = testCompletion(hud);
|
||||
testDriver.next();
|
||||
});
|
||||
}, true);
|
||||
}
|
||||
|
||||
function testNext() {
|
||||
executeSoon(function() {
|
||||
testDriver.next();
|
||||
});
|
||||
}
|
||||
|
||||
function testCompletion(hud) {
|
||||
let jsterm = hud.jsterm;
|
||||
let input = jsterm.inputNode;
|
||||
let popup = jsterm.autocompletePopup;
|
||||
|
||||
// Test if 'doc' gives 'document'
|
||||
input.value = "doc";
|
||||
input.setSelectionRange(3, 3);
|
||||
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
|
||||
yield undefined;
|
||||
|
||||
is(input.value, "doc", "'docu' completion (input.value)");
|
||||
is(jsterm.completeNode.value, " ument", "'docu' completion (completeNode)");
|
||||
|
||||
// Test typing 'window.'.
|
||||
input.value = "window.";
|
||||
input.setSelectionRange(7, 7);
|
||||
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
|
||||
yield undefined;
|
||||
|
||||
ok(popup.getItems().length > 0, "'window.' gave a list of suggestions")
|
||||
|
||||
content.wrappedJSObject.docfoobar = true;
|
||||
|
||||
// Test typing 'window.doc'.
|
||||
input.value = "window.doc";
|
||||
input.setSelectionRange(10, 10);
|
||||
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
|
||||
yield undefined;
|
||||
|
||||
let newItems = popup.getItems();
|
||||
ok(newItems.every(function(item) {
|
||||
return item.label != "docfoobar";
|
||||
}), "autocomplete cached results do not contain docfoobar. list has not been updated");
|
||||
|
||||
// Test that backspace does not cause a request to the server
|
||||
input.value = "window.do";
|
||||
input.setSelectionRange(9, 9);
|
||||
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
|
||||
yield undefined;
|
||||
|
||||
newItems = popup.getItems();
|
||||
ok(newItems.every(function(item) {
|
||||
return item.label != "docfoobar";
|
||||
}), "autocomplete cached results do not contain docfoobar. list has not been updated");
|
||||
|
||||
delete content.wrappedJSObject.docfoobar;
|
||||
|
||||
// Test if 'window.getC' gives 'getComputedStyle'
|
||||
input.value = "window."
|
||||
input.setSelectionRange(7, 7);
|
||||
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
|
||||
yield undefined;
|
||||
input.value = "window.getC";
|
||||
input.setSelectionRange(11, 11);
|
||||
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
|
||||
yield undefined;
|
||||
newItems = popup.getItems();
|
||||
ok(!newItems.every(function(item) {
|
||||
return item.label != "getComputedStyle";
|
||||
}), "autocomplete results do contain getComputedStyle");
|
||||
|
||||
// Test if 'dump(d' gives non-zero results
|
||||
input.value = "dump(d";
|
||||
input.setSelectionRange(6, 6);
|
||||
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
|
||||
yield undefined;
|
||||
|
||||
ok(popup.getItems().length > 0, "'dump(d' gives non-zero results");
|
||||
|
||||
// Test that 'dump(window.)' works.
|
||||
input.value = "dump(window.)";
|
||||
input.setSelectionRange(12, 12);
|
||||
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
|
||||
yield undefined;
|
||||
|
||||
content.wrappedJSObject.docfoobar = true;
|
||||
|
||||
// Make sure 'dump(window.doc)' does not contain 'docfoobar'.
|
||||
input.value = "dump(window.doc)";
|
||||
input.setSelectionRange(15, 15);
|
||||
jsterm.complete(jsterm.COMPLETE_HINT_ONLY, testNext);
|
||||
yield undefined;
|
||||
|
||||
newItems = popup.getItems();
|
||||
ok(newItems.every(function(item) {
|
||||
return item.label != "docfoobar";
|
||||
}), "autocomplete cached results do not contain docfoobar. list has not been updated");
|
||||
|
||||
delete content.wrappedJSObject.docfoobar;
|
||||
|
||||
testDriver = null;
|
||||
executeSoon(finishTest);
|
||||
yield undefined;
|
||||
}
|
|
@ -2844,6 +2844,21 @@ JSTerm.prototype = {
|
|||
*/
|
||||
lastCompletion: null,
|
||||
|
||||
/**
|
||||
* Array that caches the user input suggestions received from the server.
|
||||
* @private
|
||||
* @type array
|
||||
*/
|
||||
_autocompleteCache: null,
|
||||
|
||||
/**
|
||||
* The input that caused the last request to the server, whose response is
|
||||
* cached in the _autocompleteCache array.
|
||||
* @private
|
||||
* @type string
|
||||
*/
|
||||
_autocompleteQuery: null,
|
||||
|
||||
/**
|
||||
* The Web Console sidebar.
|
||||
* @see this._createSidebar()
|
||||
|
@ -4125,11 +4140,46 @@ JSTerm.prototype = {
|
|||
}
|
||||
|
||||
let requestId = gSequenceId();
|
||||
let input = this.inputNode.value;
|
||||
let cursor = this.inputNode.selectionStart;
|
||||
let input = this.inputNode.value.substring(0, cursor);
|
||||
let cache = this._autocompleteCache;
|
||||
|
||||
// If the current input starts with the previous input, then we already
|
||||
// have a list of suggestions and we just need to filter the cached
|
||||
// suggestions. When the current input ends with a non-alphanumeric
|
||||
// character we ask the server again for suggestions.
|
||||
|
||||
// Check if last character is non-alphanumeric
|
||||
if (!/[a-zA-Z0-9]$/.test(input)) {
|
||||
this._autocompleteQuery = null;
|
||||
this._autocompleteCache = null;
|
||||
}
|
||||
|
||||
if (this._autocompleteQuery && input.startsWith(this._autocompleteQuery)) {
|
||||
let filterBy = input;
|
||||
// Find the last non-alphanumeric if exists.
|
||||
let lastNonAlpha = input.match(/[^a-zA-Z0-9][a-zA-Z0-9]*$/);
|
||||
// If input contains non-alphanumerics, use the part after the last one
|
||||
// to filter the cache
|
||||
if (lastNonAlpha) {
|
||||
filterBy = input.substring(input.lastIndexOf(lastNonAlpha) + 1);
|
||||
}
|
||||
|
||||
let newList = cache.sort().filter(function(l) {
|
||||
return l.startsWith(filterBy);
|
||||
});
|
||||
|
||||
this.lastCompletion = {
|
||||
requestId: null,
|
||||
completionType: aType,
|
||||
value: null,
|
||||
};
|
||||
|
||||
let response = { matches: newList, matchProp: filterBy };
|
||||
this._receiveAutocompleteProperties(null, aCallback, response);
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: Bug 787986 - throttle/disable updates, deal with slow/high latency
|
||||
// network connections.
|
||||
this.lastCompletion = {
|
||||
requestId: requestId,
|
||||
completionType: aType,
|
||||
|
@ -4164,6 +4214,15 @@ JSTerm.prototype = {
|
|||
return;
|
||||
}
|
||||
|
||||
// Cache whatever came from the server if the last char is alphanumeric or '.'
|
||||
let cursor = inputNode.selectionStart;
|
||||
let inputUntilCursor = inputValue.substring(0, cursor);
|
||||
|
||||
if (aRequestId != null && /[a-zA-Z0-9.]$/.test(inputUntilCursor)) {
|
||||
this._autocompleteCache = aMessage.matches;
|
||||
this._autocompleteQuery = inputUntilCursor;
|
||||
}
|
||||
|
||||
let matches = aMessage.matches;
|
||||
let lastPart = aMessage.matchProp;
|
||||
if (!matches.length) {
|
||||
|
|
|
@ -68,6 +68,8 @@ var ContextCommands = {
|
|||
|
||||
SelectionHelperUI.closeEditSession(true);
|
||||
}
|
||||
} else if (ContextMenuUI.popupState.string) {
|
||||
this.clipboard.copyString(ContextMenuUI.popupState.string, this.docRef);
|
||||
} else {
|
||||
// chrome
|
||||
target.editor.copy();
|
||||
|
|
|
@ -73,10 +73,5 @@
|
|||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
<!-- Work around for bug 835175 -->
|
||||
<handler event="click">false;</handler>
|
||||
</handlers>
|
||||
</binding>
|
||||
</bindings>
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<bindings xmlns="http://www.mozilla.org/xbl"
|
||||
xmlns:xbl="http://www.mozilla.org/xbl"
|
||||
xmlns:xul="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
|
||||
xmlns:html="http://www.w3.org/1999/xhtml">
|
||||
<binding id="circular-progress-indicator">
|
||||
<content>
|
||||
<xul:stack>
|
||||
<xul:toolbarbutton anonid="progressButton" class="circularprogressindicator-progressButton appbar-secondary"/>
|
||||
<html:canvas anonid="progressRing" class="circularprogressindicator-progressRing" width="46" height="46"></html:canvas>
|
||||
</xul:stack>
|
||||
</content>
|
||||
<implementation>
|
||||
<field name="_progressCanvas">
|
||||
document.getAnonymousElementByAttribute(this, "anonid", "progressRing");
|
||||
</field>
|
||||
<field name="_progressCircleCtx">null</field>
|
||||
<field name="_img">null</field>
|
||||
<constructor>
|
||||
<![CDATA[
|
||||
this._progressCircleCtx = this._progressCanvas.getContext('2d');
|
||||
this._img = new Image();
|
||||
]]>
|
||||
</constructor>
|
||||
<method name="updateProgress">
|
||||
<parameter name="percentComplete"/>
|
||||
<body>
|
||||
<![CDATA[
|
||||
const PROGRESS_RING_IMG = "chrome://browser/skin/images/progresscircle.png";
|
||||
|
||||
let startAngle = 1.5 * Math.PI;
|
||||
let endAngle = startAngle + (2 * Math.PI * (percentComplete / 100));
|
||||
|
||||
let ctx = this._progressCircleCtx;
|
||||
ctx.clearRect(0, 0,
|
||||
this._progressCanvas.width, this._progressCanvas.height);
|
||||
|
||||
// Save the state, so we can undo the clipping
|
||||
ctx.save();
|
||||
|
||||
ctx.beginPath();
|
||||
let center = this._progressCanvas.width / 2;
|
||||
ctx.arc(center, center, center, startAngle, endAngle, false);
|
||||
ctx.lineTo(center, center);
|
||||
ctx.closePath();
|
||||
ctx.clip();
|
||||
|
||||
// Draw circle image.
|
||||
if (this._img && this._img.src) {
|
||||
ctx.drawImage(this._img, 0, 0);
|
||||
} else {
|
||||
this._img.onload = function() {
|
||||
ctx.drawImage(this._img, 0, 0);
|
||||
}.bind(this);
|
||||
this._img.src = PROGRESS_RING_IMG;
|
||||
}
|
||||
|
||||
ctx.restore();
|
||||
return [startAngle, endAngle];
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
<method name="reset">
|
||||
<body>
|
||||
<![CDATA[
|
||||
this._progressCircleCtx.clearRect(0, 0,
|
||||
this._progressCanvas.width, this._progressCanvas.height);
|
||||
]]>
|
||||
</body>
|
||||
</method>
|
||||
</implementation>
|
||||
</binding>
|
||||
</bindings>
|
|
@ -99,10 +99,5 @@
|
|||
document.getAnonymousElementByAttribute(this, "class", "flyoutpanel-contents");
|
||||
]]></field>
|
||||
</implementation>
|
||||
|
||||
<handlers>
|
||||
<!-- Work around for bug 835175 -->
|
||||
<handler event="click">false;</handler>
|
||||
</handlers>
|
||||
</binding>
|
||||
</bindings>
|
||||
|
|
|
@ -42,6 +42,10 @@ autoscroller {
|
|||
-moz-binding: url('chrome://browser/content/bindings/popup.xml#element-popup');
|
||||
}
|
||||
|
||||
circularprogressindicator {
|
||||
-moz-binding: url('chrome://browser/content/bindings/circularprogress.xml#circular-progress-indicator');
|
||||
}
|
||||
|
||||
setting[type="bool"] {
|
||||
display: -moz-box;
|
||||
-moz-binding: url("chrome://browser/content/bindings/toggleswitch.xml#setting-fulltoggle-bool");
|
||||
|
|
|
@ -181,8 +181,7 @@
|
|||
<vbox id="page">
|
||||
<vbox id="tray" class="tray-toolbar" observes="bcast_windowState" >
|
||||
<!-- Tabs -->
|
||||
<!-- onclick handler to work around bug 837242 -->
|
||||
<hbox id="tabs-container" observes="bcast_windowState" onclick="void(0);">
|
||||
<hbox id="tabs-container" observes="bcast_windowState">
|
||||
<box id="tabs" flex="1"
|
||||
observes="bcast_preciseInput"
|
||||
onselect="BrowserUI.selectTabAndDismiss(this);"
|
||||
|
@ -243,8 +242,7 @@
|
|||
<box id="horizontal-scroller" class="scroller" orient="horizontal" left="0" bottom="0"/>
|
||||
|
||||
<!-- Content touch selection overlay -->
|
||||
<!-- onclick addresses dom bug 835175 -->
|
||||
<box onclick="false" class="selection-overlay-hidden" id="content-selection-overlay"/>
|
||||
<box class="selection-overlay-hidden" id="content-selection-overlay"/>
|
||||
</stack>
|
||||
</vbox>
|
||||
|
||||
|
@ -293,6 +291,8 @@
|
|||
command="cmd_stop"/>
|
||||
</hbox>
|
||||
|
||||
<circularprogressindicator id="download-progress"
|
||||
oncommand="Appbar.onDownloadButton()"/>
|
||||
<stack id="toolbar-contextual">
|
||||
<observes element="bcast_windowState" attribute="*"/>
|
||||
<observes element="bcast_urlbarState" attribute="*"/>
|
||||
|
@ -736,11 +736,9 @@
|
|||
</flyoutpanel>
|
||||
|
||||
<!-- Chrome touch selection overlay -->
|
||||
<!-- onclick addresses dom bug 835175 -->
|
||||
<box onclick="false" class="selection-overlay-hidden" id="chrome-selection-overlay"/>
|
||||
<box class="selection-overlay-hidden" id="chrome-selection-overlay"/>
|
||||
|
||||
<box onclick="event.stopPropagation();" id="context-container" class="menu-container" hidden="true">
|
||||
<!-- onclick is dom bug 835175 -->
|
||||
<vbox id="context-popup" class="menu-popup">
|
||||
<richlistbox id="context-commands" bindingType="contextmenu" flex="1">
|
||||
<!-- priority="low" items are hidden by default when a context is being displayed
|
||||
|
|
|
@ -243,7 +243,9 @@ let ConsolePanelView = {
|
|||
target: row,
|
||||
json: {
|
||||
types: ["copy"],
|
||||
string: text
|
||||
string: text,
|
||||
xPos: aEvent.clientX,
|
||||
yPos: aEvent.clientY
|
||||
}
|
||||
});
|
||||
},
|
||||
|
|
|
@ -12,6 +12,7 @@ var Downloads = {
|
|||
* downloads if it starts before other downloads have completed.
|
||||
*/
|
||||
_downloadCount: 0,
|
||||
_downloadsInProgress: 0,
|
||||
_inited: false,
|
||||
_progressAlert: null,
|
||||
_lastSec: Infinity,
|
||||
|
@ -57,6 +58,8 @@ var Downloads = {
|
|||
|
||||
this._progress = new DownloadProgressListener(this);
|
||||
this.manager.addListener(this._progress);
|
||||
|
||||
this._downloadProgressIndicator = document.getElementById("download-progress");
|
||||
},
|
||||
|
||||
uninit: function dh_uninit() {
|
||||
|
@ -97,25 +100,26 @@ var Downloads = {
|
|||
},
|
||||
|
||||
cancelDownload: function dh_cancelDownload(aDownload) {
|
||||
this._progressNotificationInfo.delete(aDownload.guid);
|
||||
this._runDownloadBooleanMap.delete(aDownload.targetFile.path);
|
||||
this._downloadCount--;
|
||||
if (this._progressNotificationInfo.size == 0) {
|
||||
this._notificationBox.removeNotification(this._progressNotification);
|
||||
this._progressNotification = null;
|
||||
}
|
||||
|
||||
let fileURI = aDownload.target;
|
||||
if (!(fileURI && fileURI.spec)) {
|
||||
Util.dumpLn("Cant remove download file for: "+aDownload.id+", fileURI is invalid");
|
||||
return;
|
||||
}
|
||||
|
||||
let file = this._getLocalFile(fileURI);
|
||||
try {
|
||||
this.manager.cancelDownload(aDownload.id);
|
||||
let file = this._getLocalFile(fileURI);
|
||||
if (file && file.exists())
|
||||
file.remove(false);
|
||||
this.manager.cancelDownload(aDownload.id);
|
||||
|
||||
// If cancelling was successful, stop tracking the download.
|
||||
this._progressNotificationInfo.delete(aDownload.guid);
|
||||
this._runDownloadBooleanMap.delete(aDownload.targetFile.path);
|
||||
this._downloadCount--;
|
||||
this._downloadsInProgress--;
|
||||
if (this._downloadsInProgress <= 0) {
|
||||
this._notificationBox.removeNotification(this._progressNotification);
|
||||
this._progressNotification = null;
|
||||
}
|
||||
} catch (ex) {
|
||||
Util.dumpLn("Failed to cancel download, with id: "+aDownload.id+", download target URI spec: " + fileURI.spec);
|
||||
Util.dumpLn("Failed download source:"+(aDownload.source && aDownload.source.spec));
|
||||
|
@ -196,6 +200,7 @@ var Downloads = {
|
|||
accessKey: "",
|
||||
callback: function() {
|
||||
Downloads.manager.retryDownload(aDownload.id);
|
||||
Downloads._downloadProgressIndicator.reset();
|
||||
}
|
||||
},
|
||||
{
|
||||
|
@ -203,6 +208,7 @@ var Downloads = {
|
|||
accessKey: "",
|
||||
callback: function() {
|
||||
Downloads.cancelDownload(aDownload);
|
||||
Downloads._downloadProgressIndicator.reset();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
@ -222,6 +228,7 @@ var Downloads = {
|
|||
let fileURI = aDownload.target;
|
||||
let file = Downloads._getLocalFile(fileURI);
|
||||
file.reveal();
|
||||
Downloads._downloadProgressIndicator.reset();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
@ -242,6 +249,7 @@ var Downloads = {
|
|||
accessKey: "",
|
||||
callback: function() {
|
||||
Downloads.openDownload(aDownload);
|
||||
Downloads._downloadProgressIndicator.reset();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -249,6 +257,22 @@ var Downloads = {
|
|||
this._notificationBox.PRIORITY_WARNING_MEDIUM);
|
||||
},
|
||||
|
||||
_updateCircularProgressMeter: function dv_updateCircularProgressMeter() {
|
||||
if (!this._progressNotificationInfo) {
|
||||
return;
|
||||
}
|
||||
|
||||
let totPercent = 0;
|
||||
for (let info of this._progressNotificationInfo) {
|
||||
// info[0] => download guid
|
||||
// info[1].download => nsIDownload
|
||||
totPercent += info[1].download.percentComplete;
|
||||
}
|
||||
|
||||
let percentComplete = totPercent / this._progressNotificationInfo.size;
|
||||
this._downloadProgressIndicator.updateProgress(percentComplete);
|
||||
},
|
||||
|
||||
_computeDownloadProgressString: function dv_computeDownloadProgressString(aDownload) {
|
||||
let totTransferred = 0, totSize = 0, totSecondsLeft = 0;
|
||||
for (let info of this._progressNotificationInfo) {
|
||||
|
@ -260,6 +284,7 @@ var Downloads = {
|
|||
totSize += size;
|
||||
totSecondsLeft += ((size - amountTransferred) / speed);
|
||||
}
|
||||
|
||||
// Compute progress in bytes.
|
||||
let amountTransferred = Util.getDownloadSize(totTransferred);
|
||||
let size = Util.getDownloadSize(totSize);
|
||||
|
@ -296,6 +321,7 @@ var Downloads = {
|
|||
updateInfobar: function dv_updateInfobar(aDownload) {
|
||||
this._saveDownloadData(aDownload);
|
||||
let message = this._computeDownloadProgressString(aDownload);
|
||||
this._updateCircularProgressMeter();
|
||||
|
||||
if (this._progressNotification == null ||
|
||||
!this._notificationBox.getNotificationWithValue("download-progress")) {
|
||||
|
@ -310,6 +336,7 @@ var Downloads = {
|
|||
accessKey: "",
|
||||
callback: function() {
|
||||
Downloads.cancelDownloads();
|
||||
Downloads._downloadProgressIndicator.reset();
|
||||
}
|
||||
}
|
||||
];
|
||||
|
@ -327,6 +354,7 @@ var Downloads = {
|
|||
this._saveDownloadData(aDownload);
|
||||
this._progressNotification.label =
|
||||
this._computeDownloadProgressString(aDownload);
|
||||
this._updateCircularProgressMeter();
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -341,6 +369,7 @@ var Downloads = {
|
|||
break;
|
||||
case "dl-start":
|
||||
this._downloadCount++;
|
||||
this._downloadsInProgress++;
|
||||
let download = aSubject.QueryInterface(Ci.nsIDownload);
|
||||
if (!this._progressNotificationInfo.get(download.guid)) {
|
||||
this._progressNotificationInfo.set(download.guid, {});
|
||||
|
@ -352,18 +381,19 @@ var Downloads = {
|
|||
this.updateInfobar(download);
|
||||
break;
|
||||
case "dl-done":
|
||||
this._downloadsInProgress--;
|
||||
download = aSubject.QueryInterface(Ci.nsIDownload);
|
||||
let runAfterDownload = this._runDownloadBooleanMap.get(download.targetFile.path);
|
||||
if (runAfterDownload) {
|
||||
this.openDownload(download);
|
||||
}
|
||||
|
||||
this._progressNotificationInfo.delete(download.guid);
|
||||
this._runDownloadBooleanMap.delete(download.targetFile.path);
|
||||
if (this._progressNotificationInfo.size == 0) {
|
||||
if (this._downloadsInProgress == 0) {
|
||||
if (this._downloadCount > 1 || !runAfterDownload) {
|
||||
this._showDownloadCompleteNotification(download);
|
||||
}
|
||||
this._progressNotificationInfo.clear();
|
||||
this._downloadCount = 0;
|
||||
this._notificationBox.removeNotification(this._progressNotification);
|
||||
this._progressNotification = null;
|
||||
|
@ -371,6 +401,7 @@ var Downloads = {
|
|||
break;
|
||||
case "dl-failed":
|
||||
download = aSubject.QueryInterface(Ci.nsIDownload);
|
||||
this._showDownloadFailedNotification(download);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
|
|
@ -26,6 +26,7 @@ chrome.jar:
|
|||
content/bindings/selectionoverlay.xml (content/bindings/selectionoverlay.xml)
|
||||
content/bindings/cssthrobber.xml (content/bindings/cssthrobber.xml)
|
||||
content/bindings/popup.xml (content/bindings/popup.xml)
|
||||
content/bindings/circularprogress.xml (content/bindings/circularprogress.xml)
|
||||
|
||||
* content/flyoutpanels/FlyoutPanelsUI.js (content/flyoutpanels/FlyoutPanelsUI.js)
|
||||
* content/flyoutpanels/AboutFlyoutPanel.js (content/flyoutpanels/AboutFlyoutPanel.js)
|
||||
|
|
|
@ -10,25 +10,29 @@ relativesrcdir = @relativesrcdir@
|
|||
|
||||
include $(DEPTH)/config/autoconf.mk
|
||||
|
||||
# Disabled for intermittent failures (bug 880739)
|
||||
# Disabled for intermittent failures
|
||||
# Bug 880739
|
||||
# browser_context_menu_tests.js \
|
||||
# browser_context_menu_tests_01.html \
|
||||
# browser_context_menu_tests_02.html \
|
||||
# browser_context_menu_tests_03.html \
|
||||
# Bug 897175
|
||||
# browser_findbar.js \
|
||||
# browser_findbar.html \
|
||||
|
||||
MOCHITEST_METRO_FILES = \
|
||||
head.js \
|
||||
browser_urlbar.js \
|
||||
browser_bookmarks.js \
|
||||
browser_canonizeURL.js \
|
||||
browser_context_menu_tests_01.html \
|
||||
browser_context_menu_tests_02.html \
|
||||
browser_context_menu_tests_03.html \
|
||||
browser_circular_progress_indicator.js \
|
||||
browser_context_ui.js \
|
||||
browser_downloads.js \
|
||||
browser_findbar.js \
|
||||
browser_findbar.html \
|
||||
browser_history.js \
|
||||
browser_onscreen_keyboard.js \
|
||||
browser_onscreen_keyboard.html \
|
||||
browser_prefs_ui.js \
|
||||
browser_progress_indicator.xul \
|
||||
browser_remotetabs.js \
|
||||
browser_tabs.js \
|
||||
browser_test.js \
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
let doc;
|
||||
|
||||
function test() {
|
||||
waitForExplicitFinish();
|
||||
Task.spawn(function(){
|
||||
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
|
||||
|
||||
info(chromeRoot + "browser_progress_indicator.xul");
|
||||
yield addTab(chromeRoot + "browser_progress_indicator.xul");
|
||||
doc = Browser.selectedTab.browser.contentWindow.document;
|
||||
}).then(runTests);
|
||||
}
|
||||
|
||||
gTests.push({
|
||||
desc: "circular progress indicator binding is applied.",
|
||||
run: function() {
|
||||
ok(doc, "doc got defined");
|
||||
|
||||
let progressIndicator = doc.querySelector("#progress-indicator");
|
||||
ok(progressIndicator, "progress-indicator is found");
|
||||
is(typeof progressIndicator.reset, "function", "#progress-indicator has a reset() function");
|
||||
is(typeof progressIndicator.updateProgress, "function", "#progress-indicator has a updateProgress() function");
|
||||
}
|
||||
});
|
||||
|
||||
gTests.push({
|
||||
desc: "start and end angles are correct for various percents complete",
|
||||
run: function() {
|
||||
let progressIndicator = doc.querySelector("#progress-indicator");
|
||||
ok(progressIndicator, "progress-indicator is found");
|
||||
is(typeof progressIndicator.updateProgress, "function", "#progress-indicator has a updateProgress() function");
|
||||
|
||||
let expectedStartAngle = 1.5 * Math.PI;
|
||||
|
||||
let percentComplete = 0;
|
||||
let [startAngle, endAngle] = progressIndicator.updateProgress(percentComplete);
|
||||
is(startAngle, expectedStartAngle, "start angle is correct");
|
||||
is(endAngle, startAngle + (2 * Math.PI * (percentComplete / 100)), "end angle is correct");
|
||||
|
||||
percentComplete = 0.05;
|
||||
[startAngle, endAngle] = progressIndicator.updateProgress(percentComplete);
|
||||
is(startAngle, expectedStartAngle, "start angle is correct");
|
||||
is(endAngle, startAngle + (2 * Math.PI * (percentComplete / 100)), "end angle is correct");
|
||||
|
||||
percentComplete = 0.5;
|
||||
[startAngle, endAngle] = progressIndicator.updateProgress(percentComplete);
|
||||
is(startAngle, expectedStartAngle, "start angle is correct");
|
||||
is(endAngle, startAngle + (2 * Math.PI * (percentComplete / 100)), "end angle is correct");
|
||||
|
||||
percentComplete = 1;
|
||||
[startAngle, endAngle] = progressIndicator.updateProgress(percentComplete);
|
||||
is(startAngle, expectedStartAngle, "start angle is correct");
|
||||
is(endAngle, startAngle + (2 * Math.PI * (percentComplete / 100)), "end angle is correct");
|
||||
}
|
||||
});
|
|
@ -331,4 +331,14 @@ gTests.push({
|
|||
}
|
||||
});
|
||||
|
||||
|
||||
/**
|
||||
* Make sure the cancelled/aborted downloads are handled correctly.
|
||||
*/
|
||||
gTests.push({
|
||||
desc: "Cancel/Abort Downloads",
|
||||
run: function(){
|
||||
todo(false, "Ensure that a cancelled/aborted download is in the correct state \
|
||||
including correct values for state variables (e.g. _downloadCount, _downloadsInProgress) \
|
||||
and the existence of the downloaded file.");
|
||||
}
|
||||
});
|
|
@ -0,0 +1,15 @@
|
|||
<?xml version="1.0"?>
|
||||
|
||||
<!-- This Source Code Form is subject to the terms of the Mozilla Public
|
||||
- License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
|
||||
|
||||
<?xml-stylesheet href="chrome://browser/skin/platform.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/skin/browser.css" type="text/css"?>
|
||||
<?xml-stylesheet href="chrome://browser/content/browser.css" type="text/css"?>
|
||||
|
||||
<!DOCTYPE window []>
|
||||
|
||||
<window xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<circularprogressindicator id="progress-indicator" oncommand=""/>
|
||||
</window>
|
|
@ -444,6 +444,12 @@ documenttab[selected] .documenttab-selection {
|
|||
|
||||
/* Navigation bar ========================================================== */
|
||||
|
||||
.circularprogressindicator-progressRing {
|
||||
margin: 0 @toolbar_horizontal_spacing@;
|
||||
pointer-events:none;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
/* Progress meter ---------------------------------------------------------- */
|
||||
|
||||
#progress-container {
|
||||
|
@ -689,13 +695,14 @@ documenttab[selected] .documenttab-selection {
|
|||
pointer-events: none;
|
||||
}
|
||||
|
||||
#download-button {
|
||||
.circularprogressindicator-progressButton {
|
||||
margin: 0 @toolbar_horizontal_spacing@;
|
||||
-moz-image-region: rect(0px, 40px, 40px, 0px) !important;
|
||||
}
|
||||
#download-button:hover:not(:active) {
|
||||
.circularprogressindicator-progressButton:hover {
|
||||
-moz-image-region: rect(40px, 40px, 80px, 0px) !important;
|
||||
}
|
||||
#download-button:active {
|
||||
.circularprogressindicator-progressButton:active {
|
||||
-moz-image-region: rect(80px, 40px, 120px, 0px) !important;
|
||||
}
|
||||
|
||||
|
|
Двоичный файл не отображается.
После Ширина: | Высота: | Размер: 363 B |
|
@ -99,6 +99,7 @@ chrome.jar:
|
|||
skin/images/tile-selected-check-hdpi.png (images/tile-selected-check-hdpi.png)
|
||||
skin/images/plus-34.png (images/plus-34.png)
|
||||
skin/images/plus-24.png (images/plus-24.png)
|
||||
skin/images/progresscircle.png (images/progresscircle.png)
|
||||
|
||||
skin/images/overlay-back.png (images/overlay-back.png)
|
||||
skin/images/overlay-plus.png (images/overlay-plus.png)
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
}
|
||||
|
||||
.theme-body {
|
||||
color: hsl(210,100%,85%);
|
||||
color: hsl(210,100%,85%) !important;
|
||||
-moz-box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
}
|
||||
|
||||
.theme-body {
|
||||
color: hsl(210,100%,85%);
|
||||
color: hsl(210,100%,85%) !important;
|
||||
-moz-box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,10 @@
|
|||
-moz-image-region: rect(0px, 16px, 16px, 0px);
|
||||
}
|
||||
|
||||
#developer-toolbar-toolbox-button > label {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#developer-toolbar-toolbox-button:hover {
|
||||
-moz-image-region: rect(0px, 32px, 16px, 16px);
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@
|
|||
width: 24px;
|
||||
height: 7px;
|
||||
cursor: ns-resize;
|
||||
transform: translate(12px, 12px);
|
||||
transform: translate(-12px, 12px);
|
||||
background-image: url("chrome://browser/skin/devtools/responsive-horizontal-resizer.png");
|
||||
}
|
||||
|
||||
|
|
|
@ -229,6 +229,7 @@
|
|||
|
||||
.jsterm-input-node {
|
||||
background: -moz-image-rect(url("chrome://browser/skin/devtools/commandline-icon.png"), 0, 32, 16, 16) no-repeat;
|
||||
background-position: 0%;
|
||||
}
|
||||
|
||||
:-moz-any(.jsterm-input-node,
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
}
|
||||
|
||||
.theme-body {
|
||||
color: hsl(210,100%,85%);
|
||||
color: hsl(210,100%,85%) !important;
|
||||
-moz-box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
|
|
@ -1733,6 +1733,7 @@ abstract public class GeckoApp
|
|||
protected int getSessionRestoreState(Bundle savedInstanceState) {
|
||||
final SharedPreferences prefs = GeckoApp.getAppSharedPreferences();
|
||||
int restoreMode = RESTORE_NONE;
|
||||
boolean allowCrashRestore = true;
|
||||
|
||||
// If the version has changed, the user has done an upgrade, so restore
|
||||
// previous tabs.
|
||||
|
@ -1748,9 +1749,19 @@ abstract public class GeckoApp
|
|||
});
|
||||
|
||||
restoreMode = RESTORE_NORMAL;
|
||||
} else if (savedInstanceState != null ||
|
||||
PreferenceManager.getDefaultSharedPreferences(this).getBoolean(GeckoPreferences.PREFS_RESTORE_SESSION, false)) {
|
||||
} else if (savedInstanceState != null) {
|
||||
restoreMode = RESTORE_NORMAL;
|
||||
} else {
|
||||
String restorePref = PreferenceManager.getDefaultSharedPreferences(this)
|
||||
.getString(GeckoPreferences.PREFS_RESTORE_SESSION, "crash");
|
||||
if ("always".equals(restorePref)) {
|
||||
restoreMode = RESTORE_NORMAL;
|
||||
} else {
|
||||
restoreMode = RESTORE_NONE;
|
||||
if ("never".equals(restorePref)) {
|
||||
allowCrashRestore = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// We record crashes in the crash reporter. If sessionstore.js
|
||||
|
@ -1767,7 +1778,9 @@ abstract public class GeckoApp
|
|||
}
|
||||
});
|
||||
|
||||
restoreMode = RESTORE_CRASH;
|
||||
if (allowCrashRestore) {
|
||||
restoreMode = RESTORE_CRASH;
|
||||
}
|
||||
}
|
||||
|
||||
return restoreMode;
|
||||
|
|
|
@ -77,7 +77,7 @@ public class GeckoPreferences
|
|||
private static String PREFS_HEALTHREPORT_LINK = NON_PREF_PREFIX + "healthreport.link";
|
||||
private static String PREFS_DEVTOOLS_REMOTE_ENABLED = "devtools.debugger.remote-enabled";
|
||||
|
||||
public static String PREFS_RESTORE_SESSION = NON_PREF_PREFIX + "restoreSession";
|
||||
public static String PREFS_RESTORE_SESSION = NON_PREF_PREFIX + "restoreSession2";
|
||||
|
||||
// These values are chosen to be distinct from other Activity constants.
|
||||
private static int REQUEST_CODE_PREF_SCREEN = 5;
|
||||
|
@ -321,6 +321,15 @@ public class GeckoPreferences
|
|||
return true;
|
||||
}
|
||||
});
|
||||
} else if (PREFS_RESTORE_SESSION.equals(key)) {
|
||||
// Set the summary string to the current entry. The summary
|
||||
// for other list prefs will be set in the PrefsHelper
|
||||
// callback, but since this pref doesn't live in Gecko, we
|
||||
// need to handle it separately.
|
||||
ListPreference listPref = (ListPreference) pref;
|
||||
CharSequence selectedEntry = listPref.getEntry();
|
||||
listPref.setSummary(selectedEntry);
|
||||
continue;
|
||||
}
|
||||
|
||||
// Some Preference UI elements are not actually preferences,
|
||||
|
@ -452,6 +461,9 @@ public class GeckoPreferences
|
|||
String prefName = preference.getKey();
|
||||
if (PREFS_MP_ENABLED.equals(prefName)) {
|
||||
showDialog((Boolean) newValue ? DIALOG_CREATE_MASTER_PASSWORD : DIALOG_REMOVE_MASTER_PASSWORD);
|
||||
|
||||
// We don't want the "use master password" pref to change until the
|
||||
// user has gone through the dialog.
|
||||
return false;
|
||||
} else if (PREFS_MENU_CHAR_ENCODING.equals(prefName)) {
|
||||
setCharEncodingState(((String) newValue).equals("true"));
|
||||
|
@ -467,20 +479,16 @@ public class GeckoPreferences
|
|||
// background uploader service, which will start or stop the
|
||||
// repeated background upload attempts.
|
||||
broadcastHealthReportUploadPref(GeckoAppShell.getContext(), ((Boolean) newValue).booleanValue());
|
||||
return true;
|
||||
} else if (PREFS_GEO_REPORTING.equals(prefName)) {
|
||||
// Translate boolean value to int for geo reporting pref.
|
||||
PrefsHelper.setPref(prefName, (Boolean) newValue ? 1 : 0);
|
||||
return true;
|
||||
} else if (PREFS_RESTORE_SESSION.equals(prefName)) {
|
||||
// Do nothing else; the pref will be persisted in the shared prefs,
|
||||
// and it will be read at startup in Java before a session restore.
|
||||
return true;
|
||||
newValue = ((Boolean) newValue) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(prefName)) {
|
||||
// Send Gecko-side pref changes to Gecko
|
||||
if (!TextUtils.isEmpty(prefName) && !prefName.startsWith(NON_PREF_PREFIX)) {
|
||||
PrefsHelper.setPref(prefName, newValue);
|
||||
}
|
||||
|
||||
if (preference instanceof ListPreference) {
|
||||
// We need to find the entry for the new value
|
||||
int newIndex = ((ListPreference) preference).findIndexOfValue((String) newValue);
|
||||
|
@ -493,6 +501,7 @@ public class GeckoPreferences
|
|||
final FontSizePreference fontSizePref = (FontSizePreference) preference;
|
||||
fontSizePref.setSummary(fontSizePref.getSavedFontSizeName());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -217,6 +217,10 @@ public class ProfileInformationCache implements ProfileInformationProvider {
|
|||
addons.put(id, new JSONObject(json));
|
||||
}
|
||||
|
||||
public void removeAddon(String id) {
|
||||
addons.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Will throw if you haven't done a full update at least once.
|
||||
*/
|
||||
|
|
|
@ -63,6 +63,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
private static final String PREF_BLOCKLIST_ENABLED = "extensions.blocklist.enabled";
|
||||
private static final String EVENT_ADDONS_ALL = "Addons:All";
|
||||
private static final String EVENT_ADDONS_CHANGE = "Addons:Change";
|
||||
private static final String EVENT_ADDONS_UNINSTALLING = "Addons:Uninstalling";
|
||||
private static final String EVENT_PREF_CHANGE = "Pref:Change";
|
||||
|
||||
// This is raised from Gecko. It avoids browser.js having to know about the
|
||||
|
@ -138,6 +139,20 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
return new SessionInformation(wallStartTime, realStartTime, wasOOM, wasStopped);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize a new SessionInformation instance to 'split' the current
|
||||
* session.
|
||||
*/
|
||||
public static SessionInformation forRuntimeTransition() {
|
||||
final boolean wasOOM = false;
|
||||
final boolean wasStopped = true;
|
||||
final long wallStartTime = System.currentTimeMillis();
|
||||
final long realStartTime = android.os.SystemClock.elapsedRealtime();
|
||||
Log.v(LOG_TAG, "Recording runtime session transition: " +
|
||||
wallStartTime + ", " + realStartTime);
|
||||
return new SessionInformation(wallStartTime, realStartTime, wasOOM, wasStopped);
|
||||
}
|
||||
|
||||
public boolean wasKilled() {
|
||||
return wasOOM || !wasStopped;
|
||||
}
|
||||
|
@ -285,6 +300,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
private void unregisterEventListeners() {
|
||||
this.dispatcher.unregisterEventListener(EVENT_ADDONS_ALL, this);
|
||||
this.dispatcher.unregisterEventListener(EVENT_ADDONS_CHANGE, this);
|
||||
this.dispatcher.unregisterEventListener(EVENT_ADDONS_UNINSTALLING, this);
|
||||
this.dispatcher.unregisterEventListener(EVENT_PREF_CHANGE, this);
|
||||
this.dispatcher.unregisterEventListener(EVENT_KEYWORD_SEARCH, this);
|
||||
this.dispatcher.unregisterEventListener(EVENT_SEARCH, this);
|
||||
|
@ -309,17 +325,29 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
public void onAddonUninstalling(String id) {
|
||||
this.profileCache.beginInitialization();
|
||||
try {
|
||||
this.profileCache.removeAddon(id);
|
||||
} catch (IllegalStateException e) {
|
||||
Log.w(LOG_TAG, "Attempted to update add-on cache prior to full init.", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Call this when a material change has occurred in the running environment,
|
||||
* such that a new environment should be computed and prepared for use in
|
||||
* future events.
|
||||
* Call this when a material change might have occurred in the running
|
||||
* environment, such that a new environment should be computed and prepared
|
||||
* for use in future events.
|
||||
*
|
||||
* Invoke this method after calls that mutate the environment, such as
|
||||
* {@link #onBlocklistPrefChanged(boolean)}.
|
||||
*
|
||||
* TODO: record a session transition with the new environment.
|
||||
* If this change resulted in a transition between two environments, {@link
|
||||
* #onEnvironmentTransition(int, int)} will be invoked on the background
|
||||
* thread.
|
||||
*/
|
||||
public synchronized void onEnvironmentChanged() {
|
||||
final int previousEnv = this.env;
|
||||
this.env = -1;
|
||||
try {
|
||||
profileCache.completeInitialization();
|
||||
|
@ -328,7 +356,24 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
this.state = State.INITIALIZATION_FAILED;
|
||||
return;
|
||||
}
|
||||
ensureEnvironment();
|
||||
|
||||
final int updatedEnv = ensureEnvironment();
|
||||
|
||||
if (updatedEnv == -1 ||
|
||||
updatedEnv == previousEnv) {
|
||||
Log.v(LOG_TAG, "Environment didn't change.");
|
||||
return;
|
||||
}
|
||||
ThreadUtils.postToBackgroundThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
onEnvironmentTransition(previousEnv, updatedEnv);
|
||||
} catch (Exception e) {
|
||||
Log.w(LOG_TAG, "Could not record environment transition.", e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected synchronized int ensureEnvironment() {
|
||||
|
@ -493,6 +538,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
|
||||
try {
|
||||
// Listen for add-ons and prefs changes.
|
||||
dispatcher.registerEventListener(EVENT_ADDONS_UNINSTALLING, self);
|
||||
dispatcher.registerEventListener(EVENT_ADDONS_CHANGE, self);
|
||||
dispatcher.registerEventListener(EVENT_PREF_CHANGE, self);
|
||||
|
||||
|
@ -567,6 +613,27 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
Log.d(LOG_TAG, "Requested prefs.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoked in the background whenever the environment transitions between
|
||||
* two valid values.
|
||||
*/
|
||||
protected void onEnvironmentTransition(int prev, int env) {
|
||||
if (this.state != State.INITIALIZED) {
|
||||
Log.d(LOG_TAG, "Not initialized: not recording env transition (" + prev + " => " + env + ").");
|
||||
return;
|
||||
}
|
||||
|
||||
final SharedPreferences prefs = GeckoApp.getAppSharedPreferences();
|
||||
final SharedPreferences.Editor editor = prefs.edit();
|
||||
|
||||
recordSessionEnd("E", editor, prev);
|
||||
|
||||
final SessionInformation newSession = SessionInformation.forRuntimeTransition();
|
||||
setCurrentSession(newSession);
|
||||
newSession.recordBegin(editor);
|
||||
editor.commit();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMessage(String event, JSONObject message) {
|
||||
try {
|
||||
|
@ -591,12 +658,19 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
|
||||
return;
|
||||
}
|
||||
|
||||
if (EVENT_ADDONS_UNINSTALLING.equals(event)) {
|
||||
this.onAddonUninstalling(message.getString("id"));
|
||||
this.onEnvironmentChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
if (EVENT_ADDONS_CHANGE.equals(event)) {
|
||||
Log.d(LOG_TAG, "Add-on changed: " + message.getString("id"));
|
||||
this.onAddonChanged(message.getString("id"), message.getJSONObject("json"));
|
||||
this.onEnvironmentChanged();
|
||||
return;
|
||||
}
|
||||
|
||||
if (EVENT_PREF_CHANGE.equals(event)) {
|
||||
final String pref = message.getString("pref");
|
||||
Log.d(LOG_TAG, "Pref changed: " + pref);
|
||||
|
@ -871,14 +945,21 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
/**
|
||||
* Logic shared between crashed and normal sessions.
|
||||
*/
|
||||
private void recordSessionEntry(String field, SessionInformation session, JSONObject value) {
|
||||
private void recordSessionEntry(String field, SessionInformation session, final int environment, JSONObject value) {
|
||||
final HealthReportDatabaseStorage storage = this.storage;
|
||||
if (storage == null) {
|
||||
Log.d(LOG_TAG, "No storage: not recording session entry. Shutting down?");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final int sessionField = storage.getField(MEASUREMENT_NAME_SESSIONS,
|
||||
MEASUREMENT_VERSION_SESSIONS,
|
||||
field)
|
||||
.getID();
|
||||
final int day = storage.getDay(session.wallStartTime);
|
||||
storage.recordDailyDiscrete(env, day, sessionField, value);
|
||||
storage.recordDailyDiscrete(environment, day, sessionField, value);
|
||||
Log.v(LOG_TAG, "Recorded session entry for env " + environment + ", current is " + env);
|
||||
} catch (Exception e) {
|
||||
Log.w(LOG_TAG, "Unable to record session completion.", e);
|
||||
}
|
||||
|
@ -905,7 +986,8 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
}
|
||||
|
||||
try {
|
||||
recordSessionEntry("abnormal", this.previousSession, this.previousSession.getCrashedJSON());
|
||||
recordSessionEntry("abnormal", this.previousSession, this.env,
|
||||
this.previousSession.getCrashedJSON());
|
||||
} catch (Exception e) {
|
||||
Log.w(LOG_TAG, "Unable to generate session JSON.", e);
|
||||
|
||||
|
@ -913,10 +995,17 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
}
|
||||
}
|
||||
|
||||
public void recordSessionEnd(String reason, SharedPreferences.Editor editor) {
|
||||
recordSessionEnd(reason, editor, env);
|
||||
}
|
||||
|
||||
/**
|
||||
* Record that the current session ended. Does not commit the provided editor.
|
||||
*
|
||||
* @param environment An environment ID. This allows callers to record the
|
||||
* end of a session due to an observed environment change.
|
||||
*/
|
||||
public void recordSessionEnd(String reason, SharedPreferences.Editor editor) {
|
||||
public void recordSessionEnd(String reason, SharedPreferences.Editor editor, final int environment) {
|
||||
Log.d(LOG_TAG, "Recording session end: " + reason);
|
||||
if (state != State.INITIALIZED) {
|
||||
// Something has gone awry.
|
||||
|
@ -940,7 +1029,7 @@ public class BrowserHealthRecorder implements GeckoEventListener {
|
|||
long realEndTime = android.os.SystemClock.elapsedRealtime();
|
||||
try {
|
||||
JSONObject json = session.getCompletionJSON(reason, realEndTime);
|
||||
recordSessionEntry("normal", session, json);
|
||||
recordSessionEntry("normal", session, environment, json);
|
||||
} catch (JSONException e) {
|
||||
Log.w(LOG_TAG, "Unable to generate session JSON.", e);
|
||||
|
||||
|
|
|
@ -105,7 +105,10 @@
|
|||
<!ENTITY pref_plugins_disabled "Disabled">
|
||||
<!ENTITY pref_text_size "Text size">
|
||||
<!ENTITY pref_reflow_on_zoom4 "Text reflow">
|
||||
<!ENTITY pref_restore_session "Always restore tabs">
|
||||
<!ENTITY pref_restore "Tabs">
|
||||
<!ENTITY pref_restore_always "Always restore">
|
||||
<!ENTITY pref_restore_crash "Restore after a crash">
|
||||
<!ENTITY pref_restore_never "Never restore">
|
||||
<!ENTITY pref_font_size_tiny "Tiny">
|
||||
<!ENTITY pref_font_size_small "Small">
|
||||
<!ENTITY pref_font_size_medium "Medium">
|
||||
|
|
|
@ -99,6 +99,16 @@
|
|||
<item>private.data.offlineApps</item>
|
||||
<item>private.data.siteSettings</item>
|
||||
</string-array>
|
||||
<string-array name="pref_restore_entries">
|
||||
<item>@string/pref_restore_always</item>
|
||||
<item>@string/pref_restore_crash</item>
|
||||
<item>@string/pref_restore_never</item>
|
||||
</string-array>
|
||||
<string-array name="pref_restore_values">
|
||||
<item>always</item>
|
||||
<item>crash</item>
|
||||
<item>never</item>
|
||||
</string-array>
|
||||
<string-array name="pref_update_autodownload_entries">
|
||||
<item>@string/pref_update_autodownload_enabled</item>
|
||||
<item>@string/pref_update_autodownload_wifi</item>
|
||||
|
|
|
@ -25,10 +25,12 @@
|
|||
android:negativeButtonText="@string/button_cancel"
|
||||
android:persistent="false" />
|
||||
|
||||
<CheckBoxPreference android:key="android.not_a_preference.restoreSession"
|
||||
android:title="@string/pref_restore_session"
|
||||
android:defaultValue="false"
|
||||
android:persistent="true" />
|
||||
<ListPreference android:key="android.not_a_preference.restoreSession2"
|
||||
android:title="@string/pref_restore"
|
||||
android:defaultValue="crash"
|
||||
android:entries="@array/pref_restore_entries"
|
||||
android:entryValues="@array/pref_restore_values"
|
||||
android:persistent="true" />
|
||||
|
||||
<ListPreference android:key="app.update.autodownload"
|
||||
android:title="@string/pref_update_autodownload"
|
||||
|
|
|
@ -31,10 +31,12 @@
|
|||
android:negativeButtonText="@string/button_cancel"
|
||||
android:persistent="false" />
|
||||
|
||||
<CheckBoxPreference android:key="android.not_a_preference.restoreSession"
|
||||
android:title="@string/pref_restore_session"
|
||||
android:defaultValue="false"
|
||||
android:persistent="true" />
|
||||
<ListPreference android:key="android.not_a_preference.restoreSession2"
|
||||
android:title="@string/pref_restore"
|
||||
android:defaultValue="crash"
|
||||
android:entries="@array/pref_restore_entries"
|
||||
android:entryValues="@array/pref_restore_values"
|
||||
android:persistent="true" />
|
||||
|
||||
<ListPreference android:key="app.update.autodownload"
|
||||
android:title="@string/pref_update_autodownload"
|
||||
|
|
|
@ -27,10 +27,12 @@
|
|||
android:negativeButtonText="@string/button_cancel"
|
||||
android:persistent="false" />
|
||||
|
||||
<CheckBoxPreference android:key="android.not_a_preference.restoreSession"
|
||||
android:title="@string/pref_restore_session"
|
||||
android:defaultValue="false"
|
||||
android:persistent="true" />
|
||||
<ListPreference android:key="android.not_a_preference.restoreSession2"
|
||||
android:title="@string/pref_restore"
|
||||
android:defaultValue="crash"
|
||||
android:entries="@array/pref_restore_entries"
|
||||
android:entryValues="@array/pref_restore_values"
|
||||
android:persistent="true" />
|
||||
|
||||
<ListPreference android:key="app.update.autodownload"
|
||||
android:title="@string/pref_update_autodownload"
|
||||
|
|
|
@ -125,7 +125,10 @@
|
|||
<string name="pref_font_size_adjust_char">&pref_font_size_adjust_char;</string>
|
||||
<string name="pref_font_size_preview_text">&pref_font_size_preview_text;</string>
|
||||
<string name="pref_reflow_on_zoom">&pref_reflow_on_zoom4;</string>
|
||||
<string name="pref_restore_session">&pref_restore_session;</string>
|
||||
<string name="pref_restore">&pref_restore;</string>
|
||||
<string name="pref_restore_always">&pref_restore_always;</string>
|
||||
<string name="pref_restore_crash">&pref_restore_crash;</string>
|
||||
<string name="pref_restore_never">&pref_restore_never;</string>
|
||||
<string name="pref_show_product_announcements">&pref_show_product_announcements;</string>
|
||||
<string name="pref_sync">&pref_sync;</string>
|
||||
<string name="pref_search_suggestions">&pref_search_suggestions;</string>
|
||||
|
|
|
@ -26,7 +26,7 @@ public class testSettingsMenuItems extends PixelTest {
|
|||
String[][] OPTIONS_CUSTOMIZE = {
|
||||
{ "Search settings", "", "Show search suggestions", "Installed search engines" },
|
||||
{ "Import from Android", "", "Bookmarks", "History", "Import" },
|
||||
{ "Always restore tabs" },
|
||||
{ "Tabs", "Restore after a crash", "Always restore", "Restore after a crash", "Never restore" },
|
||||
{ "Automatic updates", "Only over Wi-Fi", "Enabled", "Only over Wi-Fi", "Disabled" },
|
||||
};
|
||||
|
||||
|
|
|
@ -5355,12 +5355,12 @@ let HealthReportStatusListener = {
|
|||
return o;
|
||||
},
|
||||
|
||||
notifyJava: function (aAddon, aNeedsRestart) {
|
||||
notifyJava: function (aAddon, aNeedsRestart, aAction="Addons:Change") {
|
||||
let json = this.jsonForAddon(aAddon);
|
||||
if (this._shouldIgnore(aAddon)) {
|
||||
json.ignore = true;
|
||||
}
|
||||
sendMessageToJava({ type: "Addons:Change", id: aAddon.id, json: json });
|
||||
sendMessageToJava({ type: aAction, id: aAddon.id, json: json });
|
||||
},
|
||||
|
||||
// Add-on listeners.
|
||||
|
@ -5374,11 +5374,14 @@ let HealthReportStatusListener = {
|
|||
this.notifyJava(aAddon, aNeedsRestart);
|
||||
},
|
||||
onUninstalling: function (aAddon, aNeedsRestart) {
|
||||
this.notifyJava(aAddon, aNeedsRestart);
|
||||
this.notifyJava(aAddon, aNeedsRestart, "Addons:Uninstalling");
|
||||
},
|
||||
onPropertyChanged: function (aAddon, aProperties) {
|
||||
this.notifyJava(aAddon);
|
||||
},
|
||||
onOperationCancelled: function (aAddon) {
|
||||
this.notifyJava(aAddon);
|
||||
},
|
||||
|
||||
sendAllAddonsToJava: function () {
|
||||
AddonManager.getAllAddons(function (aAddons) {
|
||||
|
|
|
@ -1200,12 +1200,14 @@ DownloadCopySaver.prototype = {
|
|||
// If we have data that we can use to resume the download from where
|
||||
// it stopped, try to use it.
|
||||
let resumeAttempted = false;
|
||||
let resumeFromBytes = 0;
|
||||
if (channel instanceof Ci.nsIResumableChannel && this.entityID &&
|
||||
partFilePath && keepPartialData) {
|
||||
try {
|
||||
let stat = yield OS.File.stat(partFilePath);
|
||||
channel.resumeAt(stat.size, this.entityID);
|
||||
resumeAttempted = true;
|
||||
resumeFromBytes = stat.size;
|
||||
} catch (ex if ex instanceof OS.File.Error &&
|
||||
ex.becauseNoSuchFile) { }
|
||||
}
|
||||
|
@ -1216,7 +1218,10 @@ DownloadCopySaver.prototype = {
|
|||
onProgress: function DCSE_onProgress(aRequest, aContext, aProgress,
|
||||
aProgressMax)
|
||||
{
|
||||
aSetProgressBytesFn(aProgress, aProgressMax, aProgress > 0 &&
|
||||
let currentBytes = resumeFromBytes + aProgress;
|
||||
let totalBytes = aProgressMax == -1 ? -1 : (resumeFromBytes +
|
||||
aProgressMax);
|
||||
aSetProgressBytesFn(currentBytes, totalBytes, aProgress > 0 &&
|
||||
partFilePath && keepPartialData);
|
||||
},
|
||||
onStatus: function () { },
|
||||
|
|
|
@ -222,6 +222,8 @@ DownloadLegacyTransfer.prototype = {
|
|||
}.bind(this)).then(null, Cu.reportError);
|
||||
},
|
||||
|
||||
setSha256Hash: function () { },
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////
|
||||
//// Private methods and properties
|
||||
|
||||
|
|
|
@ -644,9 +644,30 @@ add_task(function test_cancel_midway_restart_tryToKeepPartialData()
|
|||
// Verify that the server sent the response from the start.
|
||||
do_check_eq(gMostRecentFirstBytePos, 0);
|
||||
|
||||
// The second time, we'll request and obtain the second part of the response.
|
||||
// The second time, we'll request and obtain the second part of the response,
|
||||
// but we still stop when half of the remaining progress is reached.
|
||||
let deferMidway = Promise.defer();
|
||||
download.onchange = function () {
|
||||
if (!download.stopped && !download.canceled &&
|
||||
download.currentBytes == Math.floor(TEST_DATA_SHORT.length * 3 / 2)) {
|
||||
download.onchange = null;
|
||||
deferMidway.resolve();
|
||||
}
|
||||
};
|
||||
|
||||
mustInterruptResponses();
|
||||
let promiseAttempt = download.start();
|
||||
|
||||
// Continue when the number of bytes we received is correct, then check that
|
||||
// progress is at about 75 percent. The exact figure may vary because of
|
||||
// rounding issues, since the total number of bytes in the response might not
|
||||
// be a multiple of four.
|
||||
yield deferMidway.promise;
|
||||
do_check_true(download.progress > 72 && download.progress < 78);
|
||||
|
||||
// Now we allow the download to finish.
|
||||
continueResponses();
|
||||
yield download.start();
|
||||
yield promiseAttempt;
|
||||
|
||||
// Check that the server now sent the second part only.
|
||||
do_check_eq(gMostRecentFirstBytePos, TEST_DATA_SHORT.length);
|
||||
|
|
|
@ -141,16 +141,16 @@ FrameWorker.prototype = {
|
|||
'clearInterval', 'clearTimeout', 'dump',
|
||||
'setInterval', 'setTimeout', 'XMLHttpRequest',
|
||||
'FileReader', 'Blob', 'EventSource', 'indexedDB',
|
||||
'location'];
|
||||
'location', 'Worker'];
|
||||
|
||||
// Only expose localStorage if the caller opted-in
|
||||
if (this.exposeLocalStorage) {
|
||||
workerAPI.push('localStorage');
|
||||
}
|
||||
|
||||
// Bug 798660 - XHR and WebSocket have issues in a sandbox and need
|
||||
// Bug 798660 - XHR, WebSocket and Worker have issues in a sandbox and need
|
||||
// to be unwrapped to work
|
||||
let needsWaive = ['XMLHttpRequest', 'WebSocket'];
|
||||
let needsWaive = ['XMLHttpRequest', 'WebSocket', 'Worker'];
|
||||
// Methods need to be bound with the proper |this|.
|
||||
let needsBind = ['atob', 'btoa', 'dump', 'setInterval', 'clearInterval',
|
||||
'setTimeout', 'clearTimeout'];
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
function makeWorkerUrl(runner) {
|
||||
let prefix = "http://example.com/browser/toolkit/components/social/test/browser/echo.sjs?";
|
||||
if (typeof runner == "function") {
|
||||
runner = "let run=" + runner.toSource() + ";run();";
|
||||
runner = "var run=" + runner.toSource() + ";run();";
|
||||
}
|
||||
return prefix + encodeURI(runner);
|
||||
}
|
||||
|
@ -694,4 +694,35 @@ let tests = {
|
|||
}
|
||||
worker.port.postMessage({topic: "test-indexeddb-create"})
|
||||
},
|
||||
|
||||
testSubworker: function(cbnext) {
|
||||
// the main "frameworker"...
|
||||
let mainworker = function() {
|
||||
onconnect = function(e) {
|
||||
let port = e.ports[0];
|
||||
port.onmessage = function(e) {
|
||||
if (e.data.topic == "go") {
|
||||
let suburl = e.data.data;
|
||||
let worker = new Worker(suburl);
|
||||
worker.onmessage = function(sube) {
|
||||
port.postMessage({topic: "sub-message", data: sube.data});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The "subworker" that is actually a real, bona-fide worker.
|
||||
let subworker = function() {
|
||||
postMessage("hello");
|
||||
}
|
||||
let worker = getFrameWorkerHandle(makeWorkerUrl(mainworker), undefined, "testSubWorker");
|
||||
worker.port.onmessage = function(e) {
|
||||
if (e.data.topic == "sub-message" && e.data.data == "hello") {
|
||||
worker.terminate();
|
||||
cbnext();
|
||||
}
|
||||
}
|
||||
worker.port.postMessage({topic: "go", data: makeWorkerUrl(subworker)});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -614,7 +614,7 @@ const OPEN_CLOSE_BODY = {
|
|||
"(": ")",
|
||||
};
|
||||
|
||||
const MAX_COMPLETIONS = 256;
|
||||
const MAX_COMPLETIONS = 1500;
|
||||
|
||||
/**
|
||||
* Analyses a given string to find the last statement that is interesting for
|
||||
|
|
|
@ -23,7 +23,7 @@ NS_IMETHODIMP nsMacWebAppUtils::PathForAppWithIdentifier(const nsAString& bundle
|
|||
|
||||
outPath.Truncate();
|
||||
|
||||
NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
|
||||
nsAutoreleasePool localPool;
|
||||
|
||||
//note that the result of this expression might be nil, meaning no matching app was found.
|
||||
NSString* temp = [[NSWorkspace sharedWorkspace] absolutePathForAppBundleWithIdentifier:
|
||||
|
@ -34,7 +34,6 @@ NS_IMETHODIMP nsMacWebAppUtils::PathForAppWithIdentifier(const nsAString& bundle
|
|||
nsCocoaUtils::GetStringForNSString(temp, outPath);
|
||||
}
|
||||
|
||||
[ap release];
|
||||
return NS_OK;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
@ -43,7 +42,7 @@ NS_IMETHODIMP nsMacWebAppUtils::PathForAppWithIdentifier(const nsAString& bundle
|
|||
NS_IMETHODIMP nsMacWebAppUtils::LaunchAppWithIdentifier(const nsAString& bundleIdentifier) {
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
|
||||
|
||||
NSAutoreleasePool* ap = [[NSAutoreleasePool alloc] init];
|
||||
nsAutoreleasePool localPool;
|
||||
|
||||
// Note this might return false, meaning the app wasnt launched for some reason.
|
||||
BOOL success = [[NSWorkspace sharedWorkspace] launchAppWithBundleIdentifier:
|
||||
|
@ -52,9 +51,7 @@ NS_IMETHODIMP nsMacWebAppUtils::LaunchAppWithIdentifier(const nsAString& bundleI
|
|||
additionalEventParamDescriptor: nil
|
||||
launchIdentifier: NULL];
|
||||
|
||||
|
||||
[ap release];
|
||||
return NS_OK;
|
||||
return success ? NS_OK : NS_ERROR_FAILURE;
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
|
||||
}
|
||||
|
|
Загрузка…
Ссылка в новой задаче