Merge m-c to inbound, a=merge CLOSED TREE

MozReview-Commit-ID: 4SEUTTY9jRn
This commit is contained in:
Wes Kocher 2016-03-08 16:08:08 -08:00
commit 2048d9f4a0
224 changed files with 2361 additions and 9240 deletions

View File

@ -124,3 +124,4 @@ fcef8ded82219c89298b4e376cfbdfba79a1d35a FIREFOX_AURORA_43_BASE
67a788db9f07822cfef52351bbbe3745dff8bd7f FIREFOX_AURORA_44_BASE
99137d6d4061f408ae0869122649d8bdf489cc30 FIREFOX_AURORA_45_BASE
67c66c2878aed17ae3096d7db483ddbb2293c503 FIREFOX_AURORA_46_BASE
68d3781deda0d4d58ec9877862830db89669b3a5 FIREFOX_AURORA_47_BASE

View File

@ -23,4 +23,3 @@
# don't change CLOBBER for WebIDL changes any more.
Bug 1250297 - Doesn't actually need a clobber, but updating the tree to an older changeset does.

View File

@ -1403,8 +1403,7 @@ var BookmarkingUI = {
extraCSSClass);
item.addEventListener("command", onItemCommand);
if (icon) {
let iconURL = "moz-anno:favicon:" + icon;
item.setAttribute("image", iconURL);
item.setAttribute("image", icon);
}
fragment.appendChild(item);
}

View File

@ -89,6 +89,7 @@ support-files =
searchSuggestionEngine.xml
searchSuggestionEngine2.xml
subtst_contextmenu.html
subtst_contextmenu_input.html
test-mixedcontent-securityerrors.html
test_bug435035.html
test_bug462673.html
@ -294,6 +295,8 @@ skip-if = os == 'win'
[browser_contentAreaClick.js]
[browser_contextmenu.js]
skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558
[browser_contextmenu_input.js]
skip-if = toolkit == "gtk2" || toolkit == "gtk3" # disabled on Linux due to bug 513558
[browser_ctrlTab.js]
[browser_datachoices_notification.js]
skip-if = !datareporting
@ -412,12 +415,12 @@ run-if = e10s
[browser_subframe_favicons_not_used.js]
[browser_syncui.js]
[browser_tabDrop.js]
skip-if = buildapp == 'mulet' || e10s
skip-if = buildapp == 'mulet'
[browser_tabReorder.js]
skip-if = buildapp == 'mulet'
[browser_tabMatchesInAwesomebar.js]
[browser_tabMatchesInAwesomebar_perwindowpb.js]
skip-if = e10s || os == 'linux' # Bug 1093373, bug 1104755
skip-if = os == 'linux' # Bug 1104755
[browser_tab_detach_restore.js]
[browser_tab_drag_drop_perwindow.js]
skip-if = buildapp == 'mulet'
@ -475,7 +478,6 @@ support-files =
skip-if = buildapp == 'mulet'
[browser_unknownContentType_title.js]
[browser_unloaddialogs.js]
skip-if = e10s # Bug 1100700 - test relies on unload event firing on closed tabs, which it doesn't
[browser_urlHighlight.js]
[browser_urlbarAutoFillTrimURLs.js]
[browser_urlbarCopying.js]

View File

@ -786,105 +786,6 @@ add_task(function* test_cleanup() {
gBrowser.removeCurrentTab();
});
let lastElementSelector = null;
/**
* Right-clicks on the element that matches `selector` and checks the
* context menu that appears against the `menuItems` array.
*
* @param {String} selector
* A selector passed to querySelector to find
* the element that will be referenced.
* @param {Array} menuItems
* An array of menuitem ids and their associated enabled state. A state
* of null means that it will be ignored. Ids of '---' are used for
* menuseparators.
* @param {Object} options, optional
* skipFocusChange: don't move focus to the element before test, useful
* if you want to delay spell-check initialization
* offsetX: horizontal mouse offset from the top-left corner of
* the element, optional
* offsetY: vertical mouse offset from the top-left corner of the
* element, optional
* centered: if true, mouse position is centered in element, defaults
* to true if offsetX and offsetY are not provided
* waitForSpellCheck: wait until spellcheck is initialized before
* starting test
* preCheckContextMenuFn: callback to run before opening menu
* postCheckContextMenuFn: callback to run after opening menu
* @return {Promise} resolved after the test finishes
*/
function* test_contextmenu(selector, menuItems, options={}) {
contextMenu = document.getElementById("contentAreaContextMenu");
is(contextMenu.state, "closed", "checking if popup is closed");
// Default to centered if no positioning is defined.
if (!options.offsetX && !options.offsetY) {
options.centered = true;
}
if (!options.skipFocusChange) {
yield ContentTask.spawn(gBrowser.selectedBrowser,
{lastElementSelector, selector},
function*({lastElementSelector, selector}) {
if (lastElementSelector) {
let lastElement = content.document.querySelector(lastElementSelector);
lastElement.blur();
}
let element = content.document.querySelector(selector);
element.focus();
});
lastElementSelector = selector;
info(`Moved focus to ${selector}`);
}
if (options.waitForSpellCheck) {
info("Waiting for spell check");
yield ContentTask.spawn(gBrowser.selectedBrowser, selector, function*(selector) {
let {onSpellCheck} = Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm", {});
let element = content.document.querySelector(selector);
yield new Promise(resolve => onSpellCheck(element, resolve));
info("Spell check running");
});
}
if (options.preCheckContextMenuFn) {
yield options.preCheckContextMenuFn();
info("Completed preCheckContextMenuFn");
}
let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
yield BrowserTestUtils.synthesizeMouse(selector, options.offsetX || 0, options.offsetY || 0, {
type: "contextmenu",
button: 2,
shiftkey: options.shiftkey,
centered: options.centered
},
gBrowser.selectedBrowser);
yield awaitPopupShown;
info("Popup Shown");
if (menuItems) {
if (Services.prefs.getBoolPref("devtools.inspector.enabled")) {
let inspectItems = ["---", null,
"context-inspect", true];
menuItems = menuItems.concat(inspectItems);
}
checkContextMenu(menuItems);
}
let awaitPopupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
if (options.postCheckContextMenuFn) {
yield options.postCheckContextMenuFn();
info("Completed postCheckContextMenuFn");
}
contextMenu.hidePopup();
yield awaitPopupHidden;
}
/**
* Selects the text of the element that matches the provided `selector`
*

View File

@ -0,0 +1,249 @@
"use strict";
let contextMenu;
add_task(function* test_setup() {
const example_base = "http://example.com/browser/browser/base/content/test/general/";
const url = example_base + "subtst_contextmenu_input.html";
yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
const chrome_base = "chrome://mochitests/content/browser/browser/base/content/test/general/";
const contextmenu_common = chrome_base + "contextmenu_common.js";
Services.scriptloader.loadSubScript(contextmenu_common, this);
});
add_task(function* test_text_input() {
yield test_contextmenu("#input_text",
["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", false,
"---", null,
"spell-check-enabled", true]);
});
add_task(function* test_text_input_spellcheck() {
yield test_contextmenu("#input_spellcheck_no_value",
["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", false,
"---", null,
"spell-check-enabled", true,
"spell-dictionaries", true,
["spell-check-dictionary-en-US", true,
"---", null,
"spell-add-dictionaries", true], null],
{
waitForSpellCheck: true,
// Need to dynamically add/remove the "password" type or LoginManager
// will think that the form inputs on the page are part of a login
// and will add fill-login context menu items.
*preCheckContextMenuFn() {
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
let doc = content.document;
let input = doc.getElementById("input_spellcheck_no_value");
input.setAttribute("spellcheck", "true");
input.clientTop; // force layout flush
});
},
}
);
});
add_task(function* test_text_input_spellcheckwrong() {
yield test_contextmenu("#input_spellcheck_incorrect",
["*prodigality", true, // spelling suggestion
"spell-add-to-dictionary", true,
"---", null,
"context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", true,
"---", null,
"spell-check-enabled", true,
"spell-dictionaries", true,
["spell-check-dictionary-en-US", true,
"---", null,
"spell-add-dictionaries", true], null],
{waitForSpellCheck: true}
);
});
add_task(function* test_text_input_spellcheckcorrect() {
yield test_contextmenu("#input_spellcheck_correct",
["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", true,
"---", null,
"spell-check-enabled", true,
"spell-dictionaries", true,
["spell-check-dictionary-en-US", true,
"---", null,
"spell-add-dictionaries", true], null],
{waitForSpellCheck: true}
);
});
add_task(function* test_text_input_disabled() {
yield test_contextmenu("#input_disabled",
["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", true,
"---", null,
"spell-check-enabled", true],
{skipFocusChange: true}
);
});
add_task(function* test_password_input() {
todo(false, "context-selectall is enabled on osx-e10s, and windows when" +
" it should be disabled");
yield test_contextmenu("#input_password",
["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", null,
"---", null,
"fill-login", null,
["fill-login-no-logins", false,
"---", null,
"fill-login-saved-passwords", true], null],
{
skipFocusChange: true,
// Need to dynamically add/remove the "password" type or LoginManager
// will think that the form inputs on the page are part of a login
// and will add fill-login context menu items.
*preCheckContextMenuFn() {
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
let doc = content.document;
let input = doc.getElementById("input_password");
input.type = "password";
input.clientTop; // force layout flush
});
},
*postCheckContextMenuFn() {
yield ContentTask.spawn(gBrowser.selectedBrowser, null, function*() {
let doc = content.document;
let input = doc.getElementById("input_password");
input.type = "text";
input.clientTop; // force layout flush
});
},
}
);
});
add_task(function* test_tel_email_url_number_input() {
todo(false, "context-selectall is enabled on osx-e10s, and windows when" +
" it should be disabled");
for (let selector of ["#input_email", "#input_url", "#input_tel", "#input_number"]) {
yield test_contextmenu(selector,
["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", null],
{skipFocusChange: true}
);
}
});
add_task(function* test_date_time_color_range_input() {
for (let selector of ["#input_date", "#input_time", "#input_color", "#input_range"]) {
yield test_contextmenu(selector,
["context-navigation", null,
["context-back", false,
"context-forward", false,
"context-reload", true,
"context-bookmarkpage", true], null,
"---", null,
"context-savepage", true,
"---", null,
"context-viewbgimage", false,
"context-selectall", null,
"---", null,
"context-viewsource", true,
"context-viewinfo", true],
{skipFocusChange: true}
);
}
});
add_task(function* test_search_input() {
todo(false, "context-selectall is enabled on osx-e10s, and windows when" +
" it should be disabled");
yield test_contextmenu("#input_search",
["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", null,
"---", null,
"spell-check-enabled", true],
{skipFocusChange: true}
);
});
add_task(function* test_datetime_month_week_datetimelocal_input_todos() {
for (let type of ["datetime", "month", "week", "datetime-local"]) {
let returnedType = yield ContentTask.spawn(gBrowser.selectedBrowser, type, function*(type) {
let doc = content.document;
let input = doc.getElementById("input_" + type);
return input.type;
});
todo_is(returnedType, type, `TODO: add test for ${type} input fields`);
}
});
add_task(function* test_text_input_readonly() {
todo(false, "context-selectall is enabled on osx-e10s, and windows when" +
" it should be disabled");
todo(false, "spell-check should not be enabled for input[readonly]. see bug 1246296");
yield test_contextmenu("#input_readonly",
["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", null],
{skipFocusChange: true}
);
});
add_task(function* test_cleanup() {
yield BrowserTestUtils.removeTab(gBrowser.selectedTab);
});

View File

@ -1,77 +1,61 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
registerCleanupFunction(function* cleanup() {
while (gBrowser.tabs.length > 1) {
yield BrowserTestUtils.removeTab(gBrowser.tabs[gBrowser.tabs.length - 1]);
}
Services.search.currentEngine = originalEngine;
let engine = Services.search.getEngineByName("MozSearch");
Services.search.removeEngine(engine);
});
function test() {
waitForExplicitFinish();
let originalEngine;
add_task(function* test_setup() {
// Stop search-engine loads from hitting the network
Services.search.addEngineWithDetails("MozSearch", "", "", "", "GET",
"http://example.com/?q={searchTerms}");
let engine = Services.search.getEngineByName("MozSearch");
originalEngine = Services.search.currentEngine;
Services.search.currentEngine = engine;
});
let newTab = gBrowser.selectedTab = gBrowser.addTab("about:blank", {skipAnimation: true});
registerCleanupFunction(function () {
gBrowser.removeTab(newTab);
});
add_task(function*() { yield drop("mochi.test/first", true); });
add_task(function*() { yield drop("javascript:'bad'"); });
add_task(function*() { yield drop("jAvascript:'bad'"); });
add_task(function*() { yield drop("search this", true); });
add_task(function*() { yield drop("mochi.test/second", true); });
add_task(function*() { yield drop("data:text/html,bad"); });
add_task(function*() { yield drop("mochi.test/third", true); });
function* drop(text, valid = false) {
info(`Starting test for text:${text}; valid:${valid}`);
let scriptLoader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
let ChromeUtils = {};
scriptLoader.loadSubScript("chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js", ChromeUtils);
let tabContainer = gBrowser.tabContainer;
var receivedDropCount = 0;
function dropListener() {
receivedDropCount++;
if (receivedDropCount == triggeredDropCount) {
is(openedTabs, validDropCount, "correct number of tabs were opened");
executeSoon(finish);
}
let awaitDrop = BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "drop");
let awaitTabOpen = valid && BrowserTestUtils.waitForEvent(gBrowser.tabContainer, "TabOpen");
// A drop type of "link" onto an existing tab would normally trigger a
// load in that same tab, but tabbrowser code in _getDragTargetTab treats
// drops on the outer edges of a tab differently (loading a new tab
// instead). Make events created by synthesizeDrop have all of their
// coordinates set to 0 (screenX/screenY), so they're treated as drops
// on the outer edge of the tab, thus they open new tabs.
var event = {
clientX: 0,
clientY: 0,
screenX: 0,
screenY: 0,
};
ChromeUtils.synthesizeDrop(gBrowser.selectedTab, gBrowser.selectedTab, [[{type: "text/plain", data: text}]], "link", window, undefined, event);
let tabOpened = false;
if (awaitTabOpen) {
let tabOpenEvent = yield awaitTabOpen;
info("Got TabOpen event");
tabOpened = true;
yield BrowserTestUtils.removeTab(tabOpenEvent.target);
}
tabContainer.addEventListener("drop", dropListener, false);
registerCleanupFunction(function () {
tabContainer.removeEventListener("drop", dropListener, false);
});
is(tabOpened, valid, `Tab for ${text} should only open if valid`);
var openedTabs = 0;
function tabOpenListener(e) {
openedTabs++;
let tab = e.target;
executeSoon(function () {
gBrowser.removeTab(tab);
});
}
tabContainer.addEventListener("TabOpen", tabOpenListener, false);
registerCleanupFunction(function () {
tabContainer.removeEventListener("TabOpen", tabOpenListener, false);
});
var triggeredDropCount = 0;
var validDropCount = 0;
function drop(text, valid) {
triggeredDropCount++;
if (valid)
validDropCount++;
executeSoon(function () {
// A drop type of "link" onto an existing tab would normally trigger a
// load in that same tab, but tabbrowser code in _getDragTargetTab treats
// drops on the outer edges of a tab differently (loading a new tab
// instead). Make events created by synthesizeDrop have all of their
// coordinates set to 0 (screenX/screenY), so they're treated as drops
// on the outer edge of the tab, thus they open new tabs.
var event = {
clientX: 0,
clientY: 0,
screenX: 0,
screenY: 0,
};
ChromeUtils.synthesizeDrop(newTab, newTab, [[{type: "text/plain", data: text}]], "link", window, undefined, event);
});
}
// Begin and end with valid drops to make sure we wait for all drops before
// ending the test
drop("mochi.test/first", true);
drop("javascript:'bad'");
drop("jAvascript:'bad'");
drop("search this", true);
drop("mochi.test/second", true);
drop("data:text/html,bad");
drop("mochi.test/third", true);
yield awaitDrop;
ok(true, "Got drop event");
}

View File

@ -1,106 +1,84 @@
/* 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 testURL = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
function test() {
waitForExplicitFinish();
add_task(function*() {
let normalWindow = yield BrowserTestUtils.openNewBrowserWindow();
let privateWindow = yield BrowserTestUtils.openNewBrowserWindow({private: true});
yield runTest(normalWindow, privateWindow, false);
yield BrowserTestUtils.closeWindow(normalWindow);
yield BrowserTestUtils.closeWindow(privateWindow);
let testURL = "http://example.org/browser/browser/base/content/test/general/dummy_page.html";
normalWindow = yield BrowserTestUtils.openNewBrowserWindow();
privateWindow = yield BrowserTestUtils.openNewBrowserWindow({private: true});
yield runTest(privateWindow, normalWindow, false);
yield BrowserTestUtils.closeWindow(normalWindow);
yield BrowserTestUtils.closeWindow(privateWindow);
function testOnWindow(aOptions, aCallback) {
whenNewWindowLoaded(aOptions, function(aWin) {
// execute should only be called when need, like when you are opening
// web pages on the test. If calling executeSoon() is not necesary, then
// call whenNewWindowLoaded() instead of testOnWindow() on your test.
executeSoon(() => aCallback(aWin));
});
};
privateWindow = yield BrowserTestUtils.openNewBrowserWindow({private: true});
yield runTest(privateWindow, privateWindow, false);
yield BrowserTestUtils.closeWindow(privateWindow);
testOnWindow({}, function(aNormalWindow) {
testOnWindow({private: true}, function(aPrivateWindow) {
runTest(aNormalWindow, aPrivateWindow, false, function() {
aNormalWindow.close();
aPrivateWindow.close();
testOnWindow({}, function(aNormalWindow) {
testOnWindow({private: true}, function(aPrivateWindow) {
runTest(aPrivateWindow, aNormalWindow, false, function() {
aNormalWindow.close();
aPrivateWindow.close();
testOnWindow({private: true}, function(aPrivateWindow) {
runTest(aPrivateWindow, aPrivateWindow, false, function() {
aPrivateWindow.close();
testOnWindow({}, function(aNormalWindow) {
runTest(aNormalWindow, aNormalWindow, true, function() {
aNormalWindow.close();
finish();
});
});
});
});
});
});
});
});
normalWindow = yield BrowserTestUtils.openNewBrowserWindow();
yield runTest(normalWindow, normalWindow, true);
yield BrowserTestUtils.closeWindow(normalWindow);
});
function* runTest(aSourceWindow, aDestWindow, aExpectSwitch, aCallback) {
let baseTab = yield BrowserTestUtils.openNewForegroundTab(aSourceWindow.gBrowser, testURL);
let testTab = yield BrowserTestUtils.openNewForegroundTab(aDestWindow.gBrowser);
info("waiting for focus on the window");
yield promiseWaitForFocus(aDestWindow);
info("got focus on the window");
// Select the testTab
aDestWindow.gBrowser.selectedTab = testTab;
// Ensure that this tab has no history entries
let sessionHistoryCount = yield new Promise(resolve => {
SessionStore.getSessionHistory(gBrowser.selectedTab, function(sessionHistory) {
resolve(sessionHistory.entries.length);
});
});
function runTest(aSourceWindow, aDestWindow, aExpectSwitch, aCallback) {
// Open the base tab
let baseTab = aSourceWindow.gBrowser.addTab(testURL);
baseTab.linkedBrowser.addEventListener("load", function() {
// Wait for the tab to be fully loaded so matching happens correctly
if (baseTab.linkedBrowser.currentURI.spec == "about:blank")
return;
baseTab.linkedBrowser.removeEventListener("load", arguments.callee, true);
ok(sessionHistoryCount < 2,
`The test tab has 1 or fewer history entries. sessionHistoryCount=${sessionHistoryCount}`);
// Ensure that this tab is on about:blank
is(testTab.linkedBrowser.currentURI.spec, "about:blank",
"The test tab is on about:blank");
// Ensure that this tab's document has no child nodes
yield ContentTask.spawn(testTab.linkedBrowser, null, function*() {
ok(!content.document.body.hasChildNodes(),
"The test tab has no child nodes");
});
ok(!testTab.hasAttribute("busy"),
"The test tab doesn't have the busy attribute");
let testTab = aDestWindow.gBrowser.addTab();
// Wait for the Awesomebar popup to appear.
yield promiseAutocompleteResultPopup(testURL, aDestWindow);
waitForFocus(function() {
// Select the testTab
aDestWindow.gBrowser.selectedTab = testTab;
info(`awesomebar popup appeared. aExpectSwitch: ${aExpectSwitch}`);
// Make sure the last match is selected.
let {controller, popup} = aDestWindow.gURLBar;
while (popup.selectedIndex < controller.matchCount - 1) {
info("handling key navigation for DOM_VK_DOWN key");
controller.handleKeyNavigation(KeyEvent.DOM_VK_DOWN);
}
// Ensure that this tab has no history entries
ok(testTab.linkedBrowser.sessionHistory.count < 2,
"The test tab has 1 or less history entries");
// Ensure that this tab is on about:blank
is(testTab.linkedBrowser.currentURI.spec, "about:blank",
"The test tab is on about:blank");
// Ensure that this tab's document has no child nodes
ok(!testTab.linkedBrowser.contentDocument.body.hasChildNodes(),
"The test tab has no child nodes");
ok(!testTab.hasAttribute("busy"),
"The test tab doesn't have the busy attribute");
let awaitTabSwitch;
if (aExpectSwitch) {
awaitTabSwitch = BrowserTestUtils.removeTab(testTab, {dontRemove: true})
}
// Wait for the Awesomebar popup to appear.
promiseAutocompleteResultPopup(testURL, aDestWindow).then(() => {
if (aExpectSwitch) {
// If we expect a tab switch then the current tab
// will be closed and we switch to the other tab.
let tabContainer = aDestWindow.gBrowser.tabContainer;
tabContainer.addEventListener("TabClose", function onClose(event) {
if (event.target == testTab) {
tabContainer.removeEventListener("TabClose", onClose);
executeSoon(aCallback);
}
});
} else {
// If we don't expect a tab switch then wait for the tab to load.
testTab.addEventListener("load", function onLoad() {
testTab.removeEventListener("load", onLoad, true);
executeSoon(aCallback);
}, true);
}
// Execute the selected action.
controller.handleEnter(true);
info("sent Enter command to the controller");
// Make sure the last match is selected.
let {controller, popup} = aDestWindow.gURLBar;
while (popup.selectedIndex < controller.matchCount - 1) {
controller.handleKeyNavigation(KeyEvent.DOM_VK_DOWN);
}
// Execute the selected action.
controller.handleEnter(true);
});
}, aDestWindow);
}, true);
if (aExpectSwitch) {
// If we expect a tab switch then the current tab
// will be closed and we switch to the other tab.
yield awaitTabSwitch;
} else {
// If we don't expect a tab switch then wait for the tab to load.
yield BrowserTestUtils.browserLoaded(testTab.linkedBrowser);
}
}

View File

@ -28,46 +28,14 @@ var testUrls =
"window.addEventListener('unload', handle, true);" +
"</script><body>Testing confirm during pagehide/beforeunload/unload</body>",
];
var testsDone = 0;
function test()
{
waitForExplicitFinish();
runTest();
}
function runTest()
{
whenNewTabLoaded(window, function() {
gBrowser.selectedBrowser.addEventListener("load", onLoad, true);
executeSoon(function() {
info("Loading page with pagehide, beforeunload, and unload handlers that attempt to create dialogs");
gBrowser.selectedBrowser.loadURI(testUrls[testsDone]);
});
});
}
function onLoad(event)
{
info("Page loaded");
event.target.removeEventListener("load", onLoad, true);
gBrowser.selectedBrowser.addEventListener("unload", done, true);
executeSoon(function () {
info("Closing page");
gBrowser.removeCurrentTab();
});
}
function done() {
ok(true, "Page closed (hopefully) without timeout");
testsDone++;
if (testsDone == testUrls.length) {
finish();
return;
add_task(function*() {
for (let url of testUrls) {
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, url);
ok(true, "Loaded page " + url);
// Wait one turn of the event loop before closing, so everything settles.
yield new Promise(resolve => setTimeout(resolve, 0));
yield BrowserTestUtils.removeTab(tab);
ok(true, "Closed page " + url + " without timeout");
}
executeSoon(runTest);
}
});

View File

@ -216,3 +216,101 @@ function checkMenu(menu, expectedItems, data) {
// Could find unexpected extra items at the end...
is(actualItems.length, expectedItems.length, "checking expected number of menu entries");
}
let lastElementSelector = null;
/**
* Right-clicks on the element that matches `selector` and checks the
* context menu that appears against the `menuItems` array.
*
* @param {String} selector
* A selector passed to querySelector to find
* the element that will be referenced.
* @param {Array} menuItems
* An array of menuitem ids and their associated enabled state. A state
* of null means that it will be ignored. Ids of '---' are used for
* menuseparators.
* @param {Object} options, optional
* skipFocusChange: don't move focus to the element before test, useful
* if you want to delay spell-check initialization
* offsetX: horizontal mouse offset from the top-left corner of
* the element, optional
* offsetY: vertical mouse offset from the top-left corner of the
* element, optional
* centered: if true, mouse position is centered in element, defaults
* to true if offsetX and offsetY are not provided
* waitForSpellCheck: wait until spellcheck is initialized before
* starting test
* preCheckContextMenuFn: callback to run before opening menu
* postCheckContextMenuFn: callback to run after opening menu
* @return {Promise} resolved after the test finishes
*/
function* test_contextmenu(selector, menuItems, options={}) {
contextMenu = document.getElementById("contentAreaContextMenu");
is(contextMenu.state, "closed", "checking if popup is closed");
// Default to centered if no positioning is defined.
if (!options.offsetX && !options.offsetY) {
options.centered = true;
}
if (!options.skipFocusChange) {
yield ContentTask.spawn(gBrowser.selectedBrowser,
{lastElementSelector, selector},
function*({lastElementSelector, selector}) {
if (lastElementSelector) {
let lastElement = content.document.querySelector(lastElementSelector);
lastElement.blur();
}
let element = content.document.querySelector(selector);
element.focus();
});
lastElementSelector = selector;
info(`Moved focus to ${selector}`);
}
if (options.preCheckContextMenuFn) {
yield options.preCheckContextMenuFn();
info("Completed preCheckContextMenuFn");
}
if (options.waitForSpellCheck) {
info("Waiting for spell check");
yield ContentTask.spawn(gBrowser.selectedBrowser, selector, function*(selector) {
let {onSpellCheck} = Cu.import("resource://gre/modules/AsyncSpellCheckTestHelper.jsm", {});
let element = content.document.querySelector(selector);
yield new Promise(resolve => onSpellCheck(element, resolve));
info("Spell check running");
});
}
let awaitPopupShown = BrowserTestUtils.waitForEvent(contextMenu, "popupshown");
yield BrowserTestUtils.synthesizeMouse(selector, options.offsetX || 0, options.offsetY || 0, {
type: "contextmenu",
button: 2,
shiftkey: options.shiftkey,
centered: options.centered
},
gBrowser.selectedBrowser);
yield awaitPopupShown;
info("Popup Shown");
if (menuItems) {
if (Services.prefs.getBoolPref("devtools.inspector.enabled")) {
let inspectItems = ["---", null,
"context-inspect", true];
menuItems = menuItems.concat(inspectItems);
}
checkContextMenu(menuItems);
}
let awaitPopupHidden = BrowserTestUtils.waitForEvent(contextMenu, "popuphidden");
if (options.postCheckContextMenuFn) {
yield options.postCheckContextMenuFn();
info("Completed postCheckContextMenuFn");
}
contextMenu.hidePopup();
yield awaitPopupHidden;
}

View File

@ -28,8 +28,6 @@ support-files =
[test_bug364677.html]
[test_bug395533.html]
[test_contextmenu_input.html]
skip-if = toolkit == "gtk2" || toolkit == "gtk3" || e10s # disabled on Linux due to bug 513558
[test_feed_discovery.html]
skip-if = e10s
[test_offlineNotification.html]

View File

@ -0,0 +1,29 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Subtest for browser context menu</title>
</head>
<body>
Browser context menu subtest.
<input id="input_text">
<input id="input_spellcheck_no_value">
<input id="input_spellcheck_incorrect" spellcheck="true" value="prodkjfgigrty">
<input id="input_spellcheck_correct" spellcheck="true" value="foo">
<input id="input_disabled" disabled="true">
<input id="input_password">
<input id="input_email" type="email">
<input id="input_tel" type="tel">
<input id="input_url" type="url">
<input id="input_number" type="number">
<input id="input_date" type="date">
<input id="input_time" type="time">
<input id="input_color" type="color">
<input id="input_range" type="range">
<input id="input_search" type="search">
<input id="input_datetime" type="datetime">
<input id="input_month" type="month">
<input id="input_week" type="week">
<input id="input_datetime-local" type="datetime-local">
<input id="input_readonly" readonly="true">
</body>
</html>

View File

@ -1,364 +0,0 @@
<!DOCTYPE HTML>
<html>
<head>
<title>Tests for browser context menu</title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
Browser context menu tests for input.
<p id="display"></p>
<div id="content">
</div>
<pre id="test">
<script type="text/javascript" src="contextmenu_common.js"></script>
<script class="testbody" type="text/javascript">
const Ci = SpecialPowers.Ci;
/*
* runTest
*
* Called by a popupshowing event handler. Each test checks for expected menu
* contents, closes the popup, and finally triggers the popup on a new element
* (thus kicking off another cycle).
*
*/
function runTest(testNum) {
ok(true, "Starting test #" + testNum);
switch (testNum) {
case 1:
openContextMenuFor(input); // Invoke context menu for next test.
break;
case 2:
// Context menu for text input field.
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", false,
"---", null,
"spell-check-enabled", true,
"---", null,
"context-inspect", true]);
closeContextMenu();
input.setAttribute('spellcheck', 'true');
// Invoke context menu for next test and wait for spell check to finish
openContextMenuFor(input, false, true);
break;
case 3:
var value = false;
// Context menu for spell-check input.
checkContextMenu(["context-undo", value,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", value,
"---", null,
"context-selectall", value,
"---", null,
"spell-check-enabled", true,
"spell-dictionaries", true,
["spell-check-dictionary-en-US", true,
"---", null,
"spell-add-dictionaries", true], null,
"---", null,
"context-inspect", true]);
closeContextMenu();
input.removeAttribute('spellcheck');
// Invoke context menu for next test and wait for spell check to finish
openContextMenuFor(inputspellwrong, false, true);
break;
case 4:
var value = false;
// Context menu for spell-check input with a unknwon word.
checkContextMenu(["*prodigality", true, // spelling suggestion
"spell-add-to-dictionary", true,
"---", null,
"context-undo", value,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", value,
"---", null,
"context-selectall", true,
"---", null,
"spell-check-enabled", true,
"spell-dictionaries", true,
["spell-check-dictionary-en-US", true,
"---", null,
"spell-add-dictionaries", true], null,
"---", null,
"context-inspect", true]);
closeContextMenu();
// Invoke context menu for next test and wait for spell check to finish
openContextMenuFor(inputspellcorrect, false, true);
break;
case 5:
var value = false;
// Context menu for spell-check input with a known word.
checkContextMenu(["context-undo", value,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", value,
"---", null,
"context-selectall", true,
"---", null,
"spell-check-enabled", true,
"spell-dictionaries", true,
["spell-check-dictionary-en-US", true,
"---", null,
"spell-add-dictionaries", true], null,
"---", null,
"context-inspect", true]);
closeContextMenu();
input.disabled = true;
openContextMenuFor(input); // Invoke context menu for next test.
break;
case 6:
// Context menu for disabled input.
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", true,
"---", null,
"spell-check-enabled", true,
"---", null,
"context-inspect", true]);
closeContextMenu();
input.disabled = false;
input.type = 'password';
openContextMenuFor(input); // Invoke context menu for next test.
break;
case 7: // password
// Context menu for password input fields.
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", false,
"---", null,
"fill-login", null,
["fill-login-no-logins", false,
"---", null,
"fill-login-saved-passwords", true], null,
"---", null,
"context-inspect", true]);
closeContextMenu();
input.type = 'email';
openContextMenuFor(input); // Invoke context menu for next test.
break;
case 8: // email
case 9: // url
case 10: // tel
case 11: // type='number'
// Context menu for tel, email, url and number input fields.
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", false,
"---", null,
"context-inspect", true]);
closeContextMenu();
if (testNum == 8) {
input.type = 'url';
} else if (testNum == 9) {
input.type = 'tel';
} else if (testNum == 10) {
input.type = 'number';
} else if (testNum == 11) {
input.type = 'date';
}
openContextMenuFor(input); // Invoke context menu for next test.
break;
case 12: // type='date'
case 13: // type='time'
case 14: // type='color'
case 15: // type='range'
checkContextMenu(["context-navigation", null,
["context-back", false,
"context-forward", false,
"context-reload", true,
"context-bookmarkpage", true], null,
"---", null,
"context-savepage", true,
"---", null,
"context-viewbgimage", false,
"context-selectall", null,
"---", null,
"context-viewsource", true,
"context-viewinfo", true,
"---", null,
"context-inspect", true]);
closeContextMenu();
if (testNum == 12) {
input.type = 'time';
} else if (testNum == 13) {
input.type = 'color';
} else if (testNum == 14) {
input.type = 'range';
} else {
input.type = 'search';
}
openContextMenuFor(input);
break;
case 16: // type='search'
// Context menu for search input fields.
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", false,
"---", null,
"spell-check-enabled", true,
"---", null,
"context-inspect", true]);
closeContextMenu();
// Add some todos to make sure all input fields got a test.
var todos = [ "datetime", "month", "week", "datetime-local" ];
todos.forEach(function(type) {
input.type = type;
todo_is(input.type, type, "TODO: add test for " + type + " input fields");
});
input.type = 'text';
input.readOnly = true;
openContextMenuFor(input);
break;
case 17:
// Context menu for a read-only input.
checkContextMenu(["context-undo", false,
"---", null,
"context-cut", true,
"context-copy", true,
"context-paste", null, // ignore clipboard state
"context-delete", false,
"---", null,
"context-selectall", false,
"---", null,
"context-inspect", true]);
closeContextMenu();
// Clean-up.
subwindow.close();
SimpleTest.finish();
return;
default:
ok(false, "Unexpected invocation of test #" + testNum);
subwindow.close();
SimpleTest.finish();
return;
}
}
var gTestNum = 1;
var subwindow, chromeWin, contextMenu;
var input, inputspellwrong, inputspellcorrect;
function startTest() {
chromeWin = SpecialPowers.wrap(subwindow)
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebNavigation)
.QueryInterface(Ci.nsIDocShellTreeItem)
.rootTreeItem
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindow)
.QueryInterface(Ci.nsIDOMChromeWindow);
contextMenu = chromeWin.document.getElementById("contentAreaContextMenu");
ok(contextMenu, "Got context menu XUL");
if (chromeWin.document.getElementById("Browser:Stop").getAttribute("disabled") != "true") {
SimpleTest.executeSoon(startTest);
return;
}
var inputs = subwindow.document.getElementsByTagName('input');
input = inputs[0];
inputspellwrong = inputs[1];
inputspellcorrect = inputs[2];
contextMenu.addEventListener("popupshown", function() { runTest(++gTestNum); }, false);
runTest(1);
}
// We open this in a separate window, because the Mochitests run inside a frame.
// The frame causes an extra menu item, and prevents running the test
// standalone (ie, clicking the test name in the Mochitest window) to see
// success/failure messages.
var painted = false, loaded = false;
function waitForEvents(event)
{
if (event.type == "MozAfterPaint")
painted = true;
else if (event.type == "load")
loaded = true;
if (painted && loaded) {
subwindow.removeEventListener("MozAfterPaint", waitForEvents, false);
subwindow.onload = null;
SimpleTest.waitForFocus(startTest, subwindow);
}
}
var subwindow = window.open("data:text/html,<!DOCTYPE html><input><input spellcheck='true' value='prodkjfgigrty'><input spellcheck='true' value='foo'><input readonly spellcheck='false'>", "contextmenu-subtext", "width=600,height=700");
subwindow.addEventListener("MozAfterPaint", waitForEvents, false);
subwindow.onload = waitForEvents;
SimpleTest.waitForExplicitFinish();
</script>
</pre>
</body>
</html>

View File

@ -66,7 +66,6 @@ skip-if = !crashreporter
[browser_plugins_added_dynamically.js]
[browser_pluginnotification.js]
[browser_plugin_infolink.js]
skip-if = (os == 'win' && os_version == "6.2" && e10s) # Win8 permafail in subsequent tests
[browser_plugin_reloading.js]
[browser_blocklist_content.js]
skip-if = !e10s

View File

@ -1,47 +1,34 @@
var gTestRoot = getRootDirectory(gTestPath).replace("chrome://mochitests/content/", "http://127.0.0.1:8888/");
var gPluginHost = Components.classes["@mozilla.org/plugin/host;1"].getService(Components.interfaces.nsIPluginHost);
var gTestBrowser = null;
var oldBrowserOpenAddonsMgr = window.BrowserOpenAddonsMgr;
add_task(function* () {
registerCleanupFunction(function () {
clearAllPluginPermissions();
Services.prefs.clearUserPref("plugins.click_to_play");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
gTestBrowser = null;
gBrowser.removeCurrentTab();
window.focus();
});
registerCleanupFunction(function* cleanup() {
clearAllPluginPermissions();
Services.prefs.clearUserPref("plugins.click_to_play");
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_ENABLED, "Test Plug-in");
window.BrowserOpenAddonsMgr = oldBrowserOpenAddonsMgr;
window.focus();
});
add_task(function* () {
gBrowser.selectedTab = gBrowser.addTab();
gTestBrowser = gBrowser.selectedBrowser;
Services.prefs.setBoolPref("extensions.blocklist.suppressUI", true);
add_task(function* test_clicking_manage_link_in_plugin_overlay_should_open_about_addons() {
Services.prefs.setBoolPref("plugins.click_to_play", true);
setTestPluginEnabledState(Ci.nsIPluginTag.STATE_DISABLED, "Test Plug-in");
// Prime the blocklist service, the remote service doesn't launch on startup.
yield promiseTabLoadEvent(gBrowser.selectedTab, "data:text/html,<html></html>");
let exmsg = yield promiseInitContentBlocklistSvc(gBrowser.selectedBrowser);
ok(!exmsg, "exception: " + exmsg);
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, gTestRoot + "plugin_test.html");
let browser = tab.linkedBrowser;
yield promiseUpdatePluginBindings(browser);
yield asyncSetAndUpdateBlocklist(gTestRoot + "blockNoPlugins.xml", gTestBrowser);
});
add_task(function* () {
yield promiseTabLoadEvent(gBrowser.selectedTab, gTestRoot + "plugin_test.html");
yield promiseUpdatePluginBindings(gTestBrowser);
let pluginInfo = yield promiseForPluginInfo("test");
let pluginInfo = yield promiseForPluginInfo("test", browser);
is(pluginInfo.pluginFallbackType, Ci.nsIObjectLoadingContent.PLUGIN_DISABLED,
"Test 1a, plugin fallback type should be PLUGIN_DISABLED");
"plugin fallback type should be PLUGIN_DISABLED");
// This test opens a new tab to about:addons
let promise = waitForEvent(gBrowser.tabContainer, "TabOpen", null, true);
yield ContentTask.spawn(gTestBrowser, null, function* () {
let awaitBrowserOpenAddonsMgr = new Promise(resolve => {
window.BrowserOpenAddonsMgr = function(view) {
resolve(view);
}
});
yield ContentTask.spawn(browser, null, function* () {
let pluginNode = content.document.getElementById("test");
let manageLink = content.document.getAnonymousElementByAttribute(pluginNode, "anonid", "managePluginsLink");
let bounds = manageLink.getBoundingClientRect();
@ -54,24 +41,8 @@ add_task(function* () {
Assert.ok(true, "click on manage link");
});
yield promise;
let requestedView = yield awaitBrowserOpenAddonsMgr;
is(requestedView, "addons://list/plugin", "The Add-ons Manager should open the plugin view");
promise = waitForEvent(gBrowser.tabContainer, "TabClose", null, true);
// in-process page, no cpows here
let condition = function() {
let win = gBrowser.selectedBrowser.contentWindow;
if (!!win && !!win.wrappedJSObject && !!win.wrappedJSObject.gViewController) {
return win.wrappedJSObject.gViewController.currentViewId == "addons://list/plugin";
}
return false;
}
yield promiseForCondition(condition, "Waited too long for about:addons to display.", 40, 500);
// remove the tab containing about:addons
gBrowser.removeCurrentTab();
yield promise;
yield BrowserTestUtils.removeTab(tab);
});

View File

@ -106,6 +106,24 @@ AppendDistroSearchDirs(nsIProperties* aDirSvc, nsCOMArray<nsIFile> &array)
localePlugins->AppendNative(NS_LITERAL_CSTRING("locale"));
nsCString defLocale;
rv = prefs->GetCharPref("distribution.searchplugins.defaultLocale",
getter_Copies(defLocale));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIFile> defLocalePlugins;
rv = localePlugins->Clone(getter_AddRefs(defLocalePlugins));
if (NS_SUCCEEDED(rv)) {
defLocalePlugins->AppendNative(defLocale);
rv = defLocalePlugins->Exists(&exists);
if (NS_SUCCEEDED(rv) && exists)
array.AppendObject(defLocalePlugins);
return; // all done
}
}
// we didn't have a defaultLocale, use the user agent locale
nsCString locale;
nsCOMPtr<nsIPrefLocalizedString> prefString;
rv = prefs->GetComplexValue("general.useragent.locale",
@ -133,23 +151,6 @@ AppendDistroSearchDirs(nsIProperties* aDirSvc, nsCOMArray<nsIFile> &array)
}
}
}
// we didn't append the locale dir - try the default one
nsCString defLocale;
rv = prefs->GetCharPref("distribution.searchplugins.defaultLocale",
getter_Copies(defLocale));
if (NS_SUCCEEDED(rv)) {
nsCOMPtr<nsIFile> defLocalePlugins;
rv = localePlugins->Clone(getter_AddRefs(defLocalePlugins));
if (NS_SUCCEEDED(rv)) {
defLocalePlugins->AppendNative(defLocale);
rv = defLocalePlugins->Exists(&exists);
if (NS_SUCCEEDED(rv) && exists)
array.AppendObject(defLocalePlugins);
}
}
}
}

View File

@ -353,7 +353,7 @@ DistributionCustomizer.prototype = {
try {
let value = this._ini.getString("Preferences-" + this._locale, key);
if (value) {
Preferences.set(key, parseValue(value));
defaults.set(key, parseValue(value));
}
usedPreferences.push(key);
} catch (e) { /* ignore bad prefs and move on */ }
@ -368,7 +368,7 @@ DistributionCustomizer.prototype = {
try {
let value = this._ini.getString("Preferences-" + this._language, key);
if (value) {
Preferences.set(key, parseValue(value));
defaults.set(key, parseValue(value));
}
usedPreferences.push(key);
} catch (e) { /* ignore bad prefs and move on */ }
@ -385,7 +385,7 @@ DistributionCustomizer.prototype = {
if (value) {
value = value.replace(/%LOCALE%/g, this._locale);
value = value.replace(/%LANGUAGE%/g, this._language);
Preferences.set(key, parseValue(value));
defaults.set(key, parseValue(value));
}
} catch (e) { /* ignore bad prefs and move on */ }
}

View File

@ -653,7 +653,7 @@ extensions.registerSchemaAPI("tabs", null, (extension, context) => {
};
return context.sendMessage(browser.messageManager, "Extension:Capture",
message, recipient);
message, {recipient});
},
detectLanguage: function(tabId) {
@ -666,7 +666,7 @@ extensions.registerSchemaAPI("tabs", null, (extension, context) => {
let recipient = {innerWindowID: browser.innerWindowID};
return context.sendMessage(browser.messageManager, "Extension:DetectLanguage",
{}, recipient);
{}, {recipient});
},
_execute: function(tabId, details, kind, method) {
@ -724,7 +724,7 @@ extensions.registerSchemaAPI("tabs", null, (extension, context) => {
options.run_at = "document_idle";
}
return context.sendMessage(mm, "Extension:Execute", {options}, recipient);
return context.sendMessage(mm, "Extension:Execute", {options}, {recipient});
},
executeScript: function(tabId, details) {

View File

@ -15,28 +15,35 @@ add_task(function* tabsSendMessageReply() {
},
background: function() {
let firstTab;
let promiseResponse = new Promise(resolve => {
browser.runtime.onMessage.addListener((msg, sender, respond) => {
if (msg == "content-script-ready") {
let tabId = sender.tab.id;
browser.tabs.sendMessage(tabId, "respond-never", response => {
browser.test.fail("Got unexpected response callback");
browser.test.fail(`Got unexpected response callback: ${response}`);
browser.test.notifyFail("sendMessage");
});
Promise.all([
promiseResponse,
browser.tabs.sendMessage(tabId, "respond-now"),
browser.tabs.sendMessage(tabId, "respond-now-2"),
new Promise(resolve => browser.tabs.sendMessage(tabId, "respond-soon", resolve)),
browser.tabs.sendMessage(tabId, "respond-promise"),
browser.tabs.sendMessage(tabId, "respond-never"),
browser.tabs.sendMessage(tabId, "respond-error").catch(error => Promise.resolve({error})),
browser.tabs.sendMessage(tabId, "throw-error").catch(error => Promise.resolve({error})),
]).then(([response, respondNow, respondSoon, respondPromise, respondNever, respondError, throwError]) => {
browser.tabs.sendMessage(firstTab, "no-listener").catch(error => Promise.resolve({error})),
]).then(([response, respondNow, respondNow2, respondSoon, respondPromise, respondNever, respondError, throwError, noListener]) => {
browser.test.assertEq("expected-response", response, "Content script got the expected response");
browser.test.assertEq("respond-now", respondNow, "Got the expected immediate response");
browser.test.assertEq("respond-now-2", respondNow2, "Got the expected immediate response from the second listener");
browser.test.assertEq("respond-soon", respondSoon, "Got the expected delayed response");
browser.test.assertEq("respond-promise", respondPromise, "Got the expected promise response");
browser.test.assertEq(undefined, respondNever, "Got the expected no-response resolution");
@ -44,6 +51,10 @@ add_task(function* tabsSendMessageReply() {
browser.test.assertEq("respond-error", respondError.error.message, "Got the expected error response");
browser.test.assertEq("throw-error", throwError.error.message, "Got the expected thrown error response");
browser.test.assertEq("Could not establish connection. Receiving end does not exist.",
noListener.error.message,
"Got the expected no listener response");
return browser.tabs.remove(tabId);
}).then(() => {
browser.test.notifyPass("sendMessage");
@ -56,7 +67,10 @@ add_task(function* tabsSendMessageReply() {
});
});
browser.tabs.create({url: "http://example.com/"});
browser.tabs.query({currentWindow: true, active: true}).then(tabs => {
firstTab = tabs[0].id;
browser.tabs.create({url: "http://example.com/"});
});
},
files: {
@ -77,6 +91,13 @@ add_task(function* tabsSendMessageReply() {
throw new Error(msg);
}
});
browser.runtime.onMessage.addListener((msg, sender, respond) => {
if (msg == "respond-now") {
respond("hello");
} else if (msg == "respond-now-2") {
respond(msg);
}
});
browser.runtime.sendMessage("content-script-ready").then(response => {
browser.runtime.sendMessage(["got-response", response]);
});
@ -107,7 +128,7 @@ add_task(function* tabsSendMessageNoExceptionOnNonExistentTab() {
exception = e;
}
browser.test.assertEq(undefined, exception, "no exception should be raised on tabs.sendMessage to unexistent tabs");
browser.test.assertEq(undefined, exception, "no exception should be raised on tabs.sendMessage to nonexistent tabs");
browser.tabs.remove(tab.id, function() {
browser.test.notifyPass("tabs.sendMessage");
});

View File

@ -357,6 +357,11 @@ var gAdvancedPane = {
this.observer = {
onNetworkCacheDiskConsumption: function(consumption) {
var size = DownloadUtils.convertByteUnits(consumption);
// The XBL binding for the string bundle may have been destroyed if
// the page was closed before this callback was executed.
if (!prefStrBundle.getFormattedString) {
return;
}
actualSizeLabel.value = prefStrBundle.getFormattedString("actualDiskCacheSize", size);
},
@ -385,6 +390,11 @@ var gAdvancedPane = {
var actualSizeLabel = document.getElementById("actualAppCacheSize");
var sizeStrings = DownloadUtils.convertByteUnits(aConsumption);
var prefStrBundle = document.getElementById("bundlePreferences");
// The XBL binding for the string bundle may have been destroyed if
// the page was closed before this callback was executed.
if (!prefStrBundle.getFormattedString) {
return;
}
var sizeStr = prefStrBundle.getFormattedString("actualAppCacheSize", sizeStrings);
actualSizeLabel.value = sizeStr;
}

View File

@ -29,7 +29,6 @@ skip-if = true || !healthreport # Bug 1185403 for the "true"
[browser_privacypane_4.js]
[browser_privacypane_5.js]
[browser_privacypane_8.js]
skip-if = e10s # Bug ?????? - "leaked until shutdown [nsGlobalWindow #99 about:preferences]"
[browser_sanitizeOnShutdown_prefLocked.js]
[browser_searchsuggestions.js]
[browser_subdialogs.js]

View File

@ -1,25 +1,18 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
function test() {
requestLongerTimeout(2);
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path + '/';
}
loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
run_test_subset([
test_pane_visibility,
test_dependent_elements,
test_dependent_cookie_elements,
test_dependent_clearonclose_elements,
test_dependent_prefs,
]);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path + '/';
}
loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
run_test_subset([
test_pane_visibility,
test_dependent_elements,
test_dependent_cookie_elements,
test_dependent_clearonclose_elements,
test_dependent_prefs,
]);

View File

@ -1,22 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path + '/';
}
loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
run_test_subset([
test_custom_retention("rememberHistory", "remember"),
test_custom_retention("rememberHistory", "custom"),
test_custom_retention("rememberForms", "remember"),
test_custom_retention("rememberForms", "custom"),
test_historymode_retention("remember", "remember"),
]);
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path + '/';
}
loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
run_test_subset([
test_custom_retention("rememberHistory", "remember"),
test_custom_retention("rememberHistory", "custom"),
test_custom_retention("rememberForms", "remember"),
test_custom_retention("rememberForms", "custom"),
test_historymode_retention("remember", "remember"),
]);

View File

@ -1,33 +1,23 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
requestLongerTimeout(2);
function test() {
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path + '/';
}
loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
let runtime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
run_test_subset(Array.concat([
test_custom_retention("acceptCookies", "remember"),
test_custom_retention("acceptCookies", "custom")
],
[
test_custom_retention("acceptThirdPartyMenu", "remember", "visited"),
test_custom_retention("acceptThirdPartyMenu", "custom", "always")
], [
test_custom_retention("keepCookiesUntil", "remember", 1),
test_custom_retention("keepCookiesUntil", "custom", 2),
test_custom_retention("keepCookiesUntil", "custom", 0),
test_custom_retention("alwaysClear", "remember"),
test_custom_retention("alwaysClear", "custom"),
test_historymode_retention("remember", "remember"),
]));
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path + '/';
}
loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
let runtime = Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime);
run_test_subset([
test_custom_retention("acceptCookies", "remember"),
test_custom_retention("acceptCookies", "custom"),
test_custom_retention("acceptThirdPartyMenu", "remember", "visited"),
test_custom_retention("acceptThirdPartyMenu", "custom", "always"),
test_custom_retention("keepCookiesUntil", "remember", 1),
test_custom_retention("keepCookiesUntil", "custom", 2),
test_custom_retention("keepCookiesUntil", "custom", 0),
test_custom_retention("alwaysClear", "remember"),
test_custom_retention("alwaysClear", "custom"),
test_historymode_retention("remember", "remember"),
]);

View File

@ -1,24 +1,17 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path + '/';
}
loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
let tests = [
test_locbar_suggestion_retention("history", true),
test_locbar_suggestion_retention("bookmark", true),
test_locbar_suggestion_retention("openpage", false),
test_locbar_suggestion_retention("history", true),
test_locbar_suggestion_retention("history", false),
];
run_test_subset(tests);
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path + '/';
}
loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
run_test_subset([
test_locbar_suggestion_retention("history", true),
test_locbar_suggestion_retention("bookmark", true),
test_locbar_suggestion_retention("openpage", false),
test_locbar_suggestion_retention("history", true),
test_locbar_suggestion_retention("history", false),
]);

View File

@ -1,31 +1,26 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function test() {
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path + '/';
}
loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
run_test_subset([
// history mode should be initialized to remember
test_historymode_retention("remember", undefined),
// history mode should remain remember; toggle acceptCookies checkbox
test_custom_retention("acceptCookies", "remember"),
// history mode should now be custom; set history mode to dontremember
test_historymode_retention("dontremember", "custom"),
// history mode should remain custom; set history mode to remember
test_historymode_retention("remember", "custom"),
// history mode should now be remember
test_historymode_retention("remember", "remember"),
]);
let loader = Cc["@mozilla.org/moz/jssubscript-loader;1"].
getService(Ci.mozIJSSubScriptLoader);
let rootDir = getRootDirectory(gTestPath);
let jar = getJar(rootDir);
if (jar) {
let tmpdir = extractJarToTmp(jar);
rootDir = "file://" + tmpdir.path + '/';
}
loader.loadSubScript(rootDir + "privacypane_tests_perwindow.js", this);
run_test_subset([
// history mode should be initialized to remember
test_historymode_retention("remember", undefined),
// history mode should remain remember; toggle acceptCookies checkbox
test_custom_retention("acceptCookies", "remember"),
// history mode should now be custom; set history mode to dontremember
test_historymode_retention("dontremember", "custom"),
// history mode should remain custom; set history mode to remember
test_historymode_retention("remember", "custom"),
// history mode should now be remember
test_historymode_retention("remember", "remember"),
]);

View File

@ -1,22 +1,12 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function runTestOnPrivacyPrefPane(testFunc) {
gBrowser.tabContainer.addEventListener("TabOpen", function(aEvent) {
gBrowser.tabContainer.removeEventListener("TabOpen", arguments.callee, true);
let browser = aEvent.originalTarget.linkedBrowser;
browser.addEventListener("Initialized", function(aEvent) {
browser.removeEventListener("Initialized", arguments.callee, true);
is(browser.contentWindow.location.href, "about:preferences", "Checking if the preferences tab was opened");
browser.contentWindow.gotoPref("panePrivacy");
testFunc(browser.contentWindow);
gBrowser.removeCurrentTab();
testRunner.runNext();
}, true);
}, true);
gBrowser.selectedTab = gBrowser.addTab("about:preferences");
function* runTestOnPrivacyPrefPane(testFunc) {
info("runTestOnPrivacyPrefPane entered");
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, "about:preferences", true, true);
let browser = tab.linkedBrowser;
info("loaded about:preferences");
browser.contentWindow.gotoPref("panePrivacy");
info("viewing privacy pane, executing testFunc");
testFunc(browser.contentWindow);
yield BrowserTestUtils.removeTab(tab);
}
function controlChanged(element) {
@ -254,7 +244,7 @@ function test_dependent_prefs(win) {
}
function test_historymode_retention(mode, expect) {
return function(win) {
return function test_historymode_retention_fn(win) {
let historymode = win.document.getElementById("historyMode");
ok(historymode, "history mode menulist should exist");
@ -275,7 +265,7 @@ function test_historymode_retention(mode, expect) {
}
function test_custom_retention(controlToChange, expect, valueIncrement) {
return function(win) {
return function test_custom_retention_fn(win) {
let historymode = win.document.getElementById("historyMode");
ok(historymode, "history mode menulist should exist");
@ -329,31 +319,12 @@ function reset_preferences(win) {
pref.value = gPrefCache.get(pref.name);
}
var testRunner;
function run_test_subset(subset) {
Services.prefs.setBoolPref("browser.preferences.instantApply", true);
dump("subset: " + Array.from(subset, x => x.name).join(",") + "\n");
info("subset: " + Array.from(subset, x => x.name).join(",") + "\n");
SpecialPowers.pushPrefEnv({"set": [["browser.preferences.instantApply", true]]});
waitForExplicitFinish();
registerCleanupFunction(function() {
// Reset pref to its default
Services.prefs.clearUserPref("browser.preferences.instantApply");
});
testRunner = {
tests: [cache_preferences, ...subset, reset_preferences],
counter: 0,
runNext: function() {
if (this.counter == this.tests.length) {
finish();
} else {
let self = this;
setTimeout(function() {
runTestOnPrivacyPrefPane(self.tests[self.counter++]);
}, 0);
}
}
};
testRunner.runNext();
let tests = [cache_preferences, ...subset, reset_preferences];
for (let test of tests) {
add_task(runTestOnPrivacyPrefPane.bind(undefined, test));
}
}

View File

@ -0,0 +1,8 @@
<SearchPlugin xmlns="http://www.mozilla.org/2006/browser/search/">
<ShortName>Google</ShortName>
<Description>override-de-DE</Description>
<InputEncoding>UTF-8</InputEncoding>
<Url type="text/html" method="GET" template="http://searchtest.local">
<Param name="search" value="{searchTerms}"/>
</Url>
</SearchPlugin>

View File

@ -23,6 +23,45 @@ if (commonFile) {
const TOPICDATA_DISTRIBUTION_CUSTOMIZATION = "force-distribution-customization";
const TOPIC_BROWSERGLUE_TEST = "browser-glue-test";
/**
* Copy the engine-distribution.xml engine to a fake distribution
* created in the profile, and registered with the directory service.
* Create an empty en-US directory to make sure it isn't used.
*/
function installDistributionEngine() {
const XRE_APP_DISTRIBUTION_DIR = "XREAppDist";
const gProfD = do_get_profile().QueryInterface(Ci.nsILocalFile);
let dir = gProfD.clone();
dir.append("distribution");
let distDir = dir.clone();
dir.append("searchplugins");
dir.create(dir.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
dir.append("locale");
dir.create(dir.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
let localeDir = dir.clone();
dir.append("en-US");
dir.create(dir.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
localeDir.append("de-DE");
localeDir.create(dir.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY);
do_get_file("data/engine-de-DE.xml").copyTo(localeDir, "engine-de-DE.xml");
Services.dirsvc.registerProvider({
getFile: function(aProp, aPersistent) {
aPersistent.value = true;
if (aProp == XRE_APP_DISTRIBUTION_DIR)
return distDir.clone();
return null;
}
});
}
function run_test() {
// Set special pref to load distribution.ini from the profile folder.
Services.prefs.setBoolPref("distribution.testing.loadFromProfile", true);
@ -42,19 +81,18 @@ function run_test() {
testDistributionFile.copyTo(distroDir, "distribution.ini");
Assert.ok(testDistributionFile.exists());
installDistributionEngine();
run_next_test();
}
do_register_cleanup(function () {
// Remove the distribution file, even if the test failed, otherwise all
// next tests will import it.
let iniFile = gProfD.clone();
iniFile.leafName = "distribution";
iniFile.append("distribution.ini");
if (iniFile.exists()) {
iniFile.remove(false);
}
Assert.ok(!iniFile.exists());
// Remove the distribution dir, even if the test failed, otherwise all
// next tests will use it.
let distDir = gProfD.clone();
distDir.append("distribution");
distDir.remove(true);
Assert.ok(!distDir.exists());
});
add_task(function* () {
@ -62,47 +100,60 @@ add_task(function* () {
let glue = Cc["@mozilla.org/browser/browserglue;1"].getService(Ci.nsIObserver)
glue.observe(null, TOPIC_BROWSERGLUE_TEST, TOPICDATA_DISTRIBUTION_CUSTOMIZATION);
Assert.equal(Services.prefs.getCharPref("distribution.id"), "disttest");
Assert.equal(Services.prefs.getCharPref("distribution.version"), "1.0");
Assert.equal(Services.prefs.getComplexValue("distribution.about", Ci.nsISupportsString).data, "Tèƨƭ δïƨƭřïβúƭïôñ ƒïℓè");
var defaultBranch = Services.prefs.getDefaultBranch(null);
Assert.equal(Services.prefs.getCharPref("distribution.test.string"), "Test String");
Assert.equal(Services.prefs.getCharPref("distribution.test.string.noquotes"), "Test String");
Assert.equal(Services.prefs.getIntPref("distribution.test.int"), 777);
Assert.equal(Services.prefs.getBoolPref("distribution.test.bool.true"), true);
Assert.equal(Services.prefs.getBoolPref("distribution.test.bool.false"), false);
Assert.equal(defaultBranch.getCharPref("distribution.id"), "disttest");
Assert.equal(defaultBranch.getCharPref("distribution.version"), "1.0");
Assert.equal(defaultBranch.getComplexValue("distribution.about", Ci.nsISupportsString).data, "Tèƨƭ δïƨƭřïβúƭïôñ ƒïℓè");
Assert.throws(() => Services.prefs.getCharPref("distribution.test.empty"));
Assert.throws(() => Services.prefs.getIntPref("distribution.test.empty"));
Assert.throws(() => Services.prefs.getBoolPref("distribution.test.empty"));
Assert.equal(defaultBranch.getCharPref("distribution.test.string"), "Test String");
Assert.equal(defaultBranch.getCharPref("distribution.test.string.noquotes"), "Test String");
Assert.equal(defaultBranch.getIntPref("distribution.test.int"), 777);
Assert.equal(defaultBranch.getBoolPref("distribution.test.bool.true"), true);
Assert.equal(defaultBranch.getBoolPref("distribution.test.bool.false"), false);
Assert.equal(Services.prefs.getCharPref("distribution.test.pref.locale"), "en-US");
Assert.equal(Services.prefs.getCharPref("distribution.test.pref.language.en"), "en");
Assert.equal(Services.prefs.getCharPref("distribution.test.pref.locale.en-US"), "en-US");
Assert.throws(() => Services.prefs.getCharPref("distribution.test.pref.language.de"));
Assert.throws(() => defaultBranch.getCharPref("distribution.test.empty"));
Assert.throws(() => defaultBranch.getIntPref("distribution.test.empty"));
Assert.throws(() => defaultBranch.getBoolPref("distribution.test.empty"));
Assert.equal(defaultBranch.getCharPref("distribution.test.pref.locale"), "en-US");
Assert.equal(defaultBranch.getCharPref("distribution.test.pref.language.en"), "en");
Assert.equal(defaultBranch.getCharPref("distribution.test.pref.locale.en-US"), "en-US");
Assert.throws(() => defaultBranch.getCharPref("distribution.test.pref.language.de"));
// This value was never set because of the empty language specific pref
Assert.throws(() => Services.prefs.getCharPref("distribution.test.pref.language.reset"));
Assert.throws(() => defaultBranch.getCharPref("distribution.test.pref.language.reset"));
// This value was never set because of the empty locale specific pref
Assert.throws(() => Services.prefs.getCharPref("distribution.test.pref.locale.reset"));
Assert.throws(() => defaultBranch.getCharPref("distribution.test.pref.locale.reset"));
// This value was overridden by a locale specific setting
Assert.equal(Services.prefs.getCharPref("distribution.test.pref.locale.set"), "Locale Set");
Assert.equal(defaultBranch.getCharPref("distribution.test.pref.locale.set"), "Locale Set");
// This value was overridden by a language specific setting
Assert.equal(Services.prefs.getCharPref("distribution.test.pref.language.set"), "Language Set");
Assert.equal(defaultBranch.getCharPref("distribution.test.pref.language.set"), "Language Set");
// Language should not override locale
Assert.notEqual(Services.prefs.getCharPref("distribution.test.pref.locale.set"), "Language Set");
Assert.notEqual(defaultBranch.getCharPref("distribution.test.pref.locale.set"), "Language Set");
Assert.equal(Services.prefs.getComplexValue("distribution.test.locale", Ci.nsIPrefLocalizedString).data, "en-US");
Assert.equal(Services.prefs.getComplexValue("distribution.test.language.en", Ci.nsIPrefLocalizedString).data, "en");
Assert.equal(Services.prefs.getComplexValue("distribution.test.locale.en-US", Ci.nsIPrefLocalizedString).data, "en-US");
Assert.throws(() => Services.prefs.getComplexValue("distribution.test.language.de", Ci.nsIPrefLocalizedString));
Assert.equal(defaultBranch.getComplexValue("distribution.test.locale", Ci.nsIPrefLocalizedString).data, "en-US");
Assert.equal(defaultBranch.getComplexValue("distribution.test.language.en", Ci.nsIPrefLocalizedString).data, "en");
Assert.equal(defaultBranch.getComplexValue("distribution.test.locale.en-US", Ci.nsIPrefLocalizedString).data, "en-US");
Assert.throws(() => defaultBranch.getComplexValue("distribution.test.language.de", Ci.nsIPrefLocalizedString));
// This value was never set because of the empty language specific pref
Assert.throws(() => Services.prefs.getComplexValue("distribution.test.language.reset", Ci.nsIPrefLocalizedString));
Assert.throws(() => defaultBranch.getComplexValue("distribution.test.language.reset", Ci.nsIPrefLocalizedString));
// This value was never set because of the empty locale specific pref
Assert.throws(() => Services.prefs.getComplexValue("distribution.test.locale.reset", Ci.nsIPrefLocalizedString));
Assert.throws(() => defaultBranch.getComplexValue("distribution.test.locale.reset", Ci.nsIPrefLocalizedString));
// This value was overridden by a locale specific setting
Assert.equal(Services.prefs.getComplexValue("distribution.test.locale.set", Ci.nsIPrefLocalizedString).data, "Locale Set");
Assert.equal(defaultBranch.getComplexValue("distribution.test.locale.set", Ci.nsIPrefLocalizedString).data, "Locale Set");
// This value was overridden by a language specific setting
Assert.equal(Services.prefs.getComplexValue("distribution.test.language.set", Ci.nsIPrefLocalizedString).data, "Language Set");
Assert.equal(defaultBranch.getComplexValue("distribution.test.language.set", Ci.nsIPrefLocalizedString).data, "Language Set");
// Language should not override locale
Assert.notEqual(Services.prefs.getComplexValue("distribution.test.locale.set", Ci.nsIPrefLocalizedString).data, "Language Set");
Assert.notEqual(defaultBranch.getComplexValue("distribution.test.locale.set", Ci.nsIPrefLocalizedString).data, "Language Set");
do_test_pending();
Services.prefs.setCharPref("distribution.searchplugins.defaultLocale", "de-DE");
Services.search.init(function() {
Assert.equal(Services.search.isInitialized, true);
var engine = Services.search.getEngineByName("Google");
Assert.equal(engine.description, "override-de-DE");
do_test_finished();
});
});

View File

@ -3,5 +3,6 @@ firefox-appdir = browser
skip-if = toolkit == 'android' || toolkit == 'gonk'
support-files =
distribution.ini
data/engine-de-DE.xml
[test_distribution.js]

View File

@ -1 +1 @@
47.0a1
48.0a1

View File

@ -1 +1 @@
47.0a1
48.0a1

View File

@ -452,12 +452,12 @@ Experiments.Experiments.prototype = {
this._shutdown = true;
if (this._mainTask) {
if (this._networkRequest) {
try {
this._log.trace("Aborting pending network request: " + this._networkRequest);
this._networkRequest.abort();
} catch (e) {
// pass
}
try {
this._log.trace("Aborting pending network request: " + this._networkRequest);
this._networkRequest.abort();
} catch (e) {
// pass
}
}
try {
this._log.trace("uninit: waiting on _mainTask");
@ -634,7 +634,7 @@ Experiments.Experiments.prototype = {
active: experiment.enabled,
endDate: experiment.endDate.getTime(),
detailURL: experiment._homepageURL,
branch: experiment.branch,
branch: experiment.branch,
});
}
@ -709,7 +709,7 @@ Experiments.Experiments.prototype = {
} else {
e = this._getActiveExperiment();
if (e === null) {
throw new Error("No active experiment");
throw new Error("No active experiment");
}
}
return e.branch;
@ -958,7 +958,7 @@ Experiments.Experiments.prototype = {
if (xhr.status !== 200 && xhr.state !== 0) {
log.error("httpGetRequest::onLoad() - Request to " + url + " returned status " + xhr.status);
deferred.reject(new Error("Experiments - XHR status for " + url + " is " + xhr.status));
this._networkRequest = null;
this._networkRequest = null;
return;
}
@ -1043,6 +1043,14 @@ Experiments.Experiments.prototype = {
if (!entry.initFromCacheData(item)) {
continue;
}
// Discard old experiments if they ended more than 180 days ago.
if (entry.shouldDiscard()) {
// We discarded an experiment, the cache needs to be updated.
this._dirty = true;
continue;
}
experiments.set(entry.id, entry);
}
@ -1543,7 +1551,9 @@ Experiments.ExperimentEntry.prototype = {
}
});
this._lastChangedDate = this._policy.now();
// In order for the experiment's data expiration mechanism to work, use the experiment's
// |_endData| as the |_lastChangedDate| (if available).
this._lastChangedDate = !!this._endDate ? this._endDate : this._policy.now();
return true;
},

View File

@ -80,6 +80,15 @@ function checkExperimentSerializations(experimentEntryIterator) {
}
}
function validateCache(cachedExperiments, experimentIds) {
let cachedExperimentIds = new Set(cachedExperiments);
Assert.equal(cachedExperimentIds.size, experimentIds.length,
"The number of cached experiments does not match with the provided list");
for (let id of experimentIds) {
Assert.ok(cachedExperimentIds.has(id), "The cache must contain the experiment with id " + id);
}
}
// Set up an experiments instance and check if it is properly restored from cache.
add_task(function* test_cache() {
@ -247,3 +256,147 @@ add_task(function* test_cache() {
yield promiseRestartManager();
yield removeCacheFile();
});
add_task(function* test_expiration() {
// The manifest data we test with.
gManifestObject = {
"version": 1,
experiments: [
{
id: EXPERIMENT1_ID,
xpiURL: gDataRoot + EXPERIMENT1_XPI_NAME,
xpiHash: EXPERIMENT1_XPI_SHA1,
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
appName: ["XPCShell"],
channel: ["nightly"],
},
{
id: EXPERIMENT2_ID,
xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
xpiHash: EXPERIMENT2_XPI_SHA1,
maxActiveSeconds: 50 * SEC_IN_ONE_DAY,
appName: ["XPCShell"],
channel: ["nightly"],
},
// The 3rd experiment will never run, so it's ok to use experiment's 2 data.
{
id: EXPERIMENT3_ID,
xpiURL: gDataRoot + EXPERIMENT2_XPI_NAME,
xpiHash: EXPERIMENT2_XPI_SHA1,
maxActiveSeconds: 10 * SEC_IN_ONE_DAY,
appName: ["XPCShell"],
channel: ["nightly"],
}
],
};
// Data to compare the result of Experiments.getExperiments() against.
let experimentListData = [
{
id: EXPERIMENT2_ID,
name: "Test experiment 2",
description: "And yet another experiment that experiments experimentally.",
},
{
id: EXPERIMENT1_ID,
name: EXPERIMENT1_NAME,
description: "Yet another experiment that experiments experimentally.",
},
];
// Setup dates for the experiments.
let baseDate = new Date(2014, 5, 1, 12);
let startDates = [];
let endDates = [];
for (let i=0; i<gManifestObject.experiments.length; ++i) {
let experiment = gManifestObject.experiments[i];
// Spread out experiments in time so that one experiment can end and expire while
// the next is still running.
startDates.push(futureDate(baseDate, (50 + (200 * i)) * MS_IN_ONE_DAY));
endDates .push(futureDate(startDates[i], 50 * MS_IN_ONE_DAY));
experiment.startTime = dateToSeconds(startDates[i]);
experiment.endTime = dateToSeconds(endDates[i]);
}
let now = null;
let experiments = null;
let setDateAndRestartExperiments = new Task.async(function* (newDate) {
now = newDate;
defineNow(gPolicy, now);
yield promiseRestartManager();
experiments = new Experiments.Experiments(gPolicy);
yield experiments._run();
});
// Trigger update & re-init, clock set to before any activation.
now = baseDate;
defineNow(gPolicy, now);
experiments = new Experiments.Experiments(gPolicy);
yield experiments.updateManifest();
let list = yield experiments.getExperiments();
Assert.equal(list.length, 0, "Experiment list should be empty.");
// Re-init, clock set for experiment 1 to start...
yield setDateAndRestartExperiments(startDates[0]);
list = yield experiments.getExperiments();
Assert.equal(list.length, 1, "The first experiment should have started.");
// ... init again, and set the clock so that the first experiment ends.
yield setDateAndRestartExperiments(endDates[0]);
// The experiment just ended, it should still be in the cache, but marked
// as finished.
list = yield experiments.getExperiments();
Assert.equal(list.length, 1, "Experiment list should have 1 entry.");
experimentListData[1].active = false;
experimentListData[1].endDate = now.getTime();
checkExperimentListsEqual(experimentListData.slice(1), list);
validateCache([...experiments._experiments.keys()], [EXPERIMENT1_ID, EXPERIMENT2_ID, EXPERIMENT3_ID]);
// Start the second experiment.
yield setDateAndRestartExperiments(startDates[1]);
// The experiments cache should contain the finished experiment and the
// one that's still running.
list = yield experiments.getExperiments();
Assert.equal(list.length, 2, "Experiment list should have 2 entries.");
experimentListData[0].active = true;
experimentListData[0].endDate = now.getTime() + 50 * MS_IN_ONE_DAY;
checkExperimentListsEqual(experimentListData, list);
// Move the clock in the future, just 31 days after the start date of the second experiment,
// so that the cache for the first experiment expires and the second experiment is still running.
yield setDateAndRestartExperiments(futureDate(startDates[1], 31 * MS_IN_ONE_DAY));
validateCache([...experiments._experiments.keys()], [EXPERIMENT2_ID, EXPERIMENT3_ID]);
// Make sure that the expired experiment is not reported anymore.
let history = yield experiments.getExperiments();
Assert.equal(history.length, 1, "Experiments older than 180 days must be removed from the cache.");
// Test that we don't write expired experiments in the cache.
yield setDateAndRestartExperiments(now);
validateCache([...experiments._experiments.keys()], [EXPERIMENT2_ID, EXPERIMENT3_ID]);
// The first experiment should be expired and not in the cache, it ended more than
// 180 days ago. We should see the one still running in the cache.
history = yield experiments.getExperiments();
Assert.equal(history.length, 1, "Expired experiments must not be saved to cache.");
checkExperimentListsEqual(experimentListData.slice(0, 1), history);
// Test that experiments that are cached locally but never ran are removed from cache
// when they are removed from the manifest (this is cached data, not really history).
gManifestObject["experiments"] = gManifestObject["experiments"].slice(1, 1);
yield experiments.updateManifest();
validateCache([...experiments._experiments.keys()], [EXPERIMENT2_ID]);
// Cleanup.
yield experiments._toggleExperimentsEnabled(false);
yield promiseRestartManager();
yield removeCacheFile();
});

View File

@ -948,6 +948,10 @@ var MozLoopServiceInternal = {
let url = this.getChatURL(windowId);
// Ensure about:loopconversation has access to the camera.
Services.perms.add(Services.io.newURI(url, null, null), "camera",
Services.perms.ALLOW_ACTION, Services.perms.EXPIRE_SESSION);
Chat.registerButton(kChatboxHangupButton);
let callback = chatbox => {

View File

@ -51,3 +51,7 @@
-moz-image-region: rect(36px, 36px, 72px, 0);
}
}
#PanelUI-pocketView[mainview=true] > .panel-subview-body > #pocket-panel-iframe {
border-radius: var(--panel-arrowcontent-border-radius);
}

View File

@ -27,15 +27,17 @@ XPCOMUtils.defineLazyServiceGetter(this, "_focusManager",
// Constants
const TAB_EVENTS = ["TabOpen", "TabSelect"];
const TAB_EVENTS = ["TabOpen", "TabSelect", "TabRemotenessChange"];
const WINDOW_EVENTS = ["activate", "unload"];
// PRIORITY DELTA is -10 because lower priority value is actually a higher priority
const PRIORITY_DELTA = -10;
// lower value means higher priority
const PRIORITY_DELTA = Ci.nsISupportsPriority.PRIORITY_NORMAL - Ci.nsISupportsPriority.PRIORITY_LOW;
// Variables
var _lastFocusedWindow = null;
var _windows = [];
// this is used for restoring the priority after TabRemotenessChange
var _priorityBackup = new WeakMap();
// Exported symbol
@ -56,6 +58,9 @@ function _handleEvent(aEvent) {
case "activate":
WindowHelper.onActivate(aEvent.target);
break;
case "TabRemotenessChange":
BrowserHelper.onRemotenessChange(aEvent.target.linkedBrowser);
break;
case "unload":
WindowHelper.removeWindow(aEvent.currentTarget);
break;
@ -66,6 +71,8 @@ function _handleEvent(aEvent) {
// Methods that impact a browser. Put into single object for organization.
var BrowserHelper = {
onOpen: function NP_BH_onOpen(aBrowser) {
_priorityBackup.set(aBrowser.permanentKey, Ci.nsISupportsPriority.PRIORITY_NORMAL);
// If the tab is in the focused window, leave priority as it is
if (aBrowser.ownerDocument.defaultView != _lastFocusedWindow)
this.decreasePriority(aBrowser);
@ -80,12 +87,20 @@ var BrowserHelper = {
windowEntry.lastSelectedBrowser = aBrowser;
},
onRemotenessChange: function (aBrowser) {
aBrowser.setPriority(_priorityBackup.get(aBrowser.permanentKey));
},
increasePriority: function NP_BH_increasePriority(aBrowser) {
aBrowser.adjustPriority(PRIORITY_DELTA);
_priorityBackup.set(aBrowser.permanentKey,
_priorityBackup.get(aBrowser.permanentKey) + PRIORITY_DELTA);
},
decreasePriority: function NP_BH_decreasePriority(aBrowser) {
aBrowser.adjustPriority(PRIORITY_DELTA * -1);
_priorityBackup.set(aBrowser.permanentKey,
_priorityBackup.get(aBrowser.permanentKey) - PRIORITY_DELTA);
}
};

View File

@ -12,7 +12,6 @@ support-files =
contentSearchSuggestions.sjs
contentSearchSuggestions.xml
[browser_NetworkPrioritizer.js]
skip-if = e10s # Bug 666804 - Support NetworkPrioritizer in e10s
[browser_SelfSupportBackend.js]
support-files =
../../components/uitour/test/uitour.html

View File

@ -2,153 +2,164 @@
* 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/. */
function test() {
/** Tests for NetworkPrioritizer.jsm (Bug 514490) **/
/** Tests for NetworkPrioritizer.jsm (Bug 514490) **/
waitForExplicitFinish();
const LOWEST = Ci.nsISupportsPriority.PRIORITY_LOWEST;
const LOW = Ci.nsISupportsPriority.PRIORITY_LOW;
const NORMAL = Ci.nsISupportsPriority.PRIORITY_NORMAL;
const HIGH = Ci.nsISupportsPriority.PRIORITY_HIGH;
const HIGHEST = Ci.nsISupportsPriority.PRIORITY_HIGHEST;
const PRIORITY_DELTA = -10; // same as in NetworkPrioritizer
const DELTA = NORMAL - LOW; // lower value means higher priority
// Test helper functions.
// getPriority and setPriority can take a Tab or a Browser
function getPriority(aBrowser) {
// Assume we were passed a tab if it's not a browser
if (!aBrowser.webNavigation)
aBrowser = aBrowser.linkedBrowser;
return aBrowser.webNavigation.QueryInterface(Ci.nsIDocumentLoader)
.loadGroup.QueryInterface(Ci.nsISupportsPriority).priority;
}
function setPriority(aBrowser, aPriority) {
if (!aBrowser.webNavigation)
aBrowser = aBrowser.linkedBrowser;
aBrowser.webNavigation.QueryInterface(Ci.nsIDocumentLoader)
.loadGroup.QueryInterface(Ci.nsISupportsPriority).priority = aPriority;
}
function isWindowState(aWindow, aTabPriorities) {
let browsers = aWindow.gBrowser.browsers;
// Make sure we have the right number of tabs & priorities
is(browsers.length, aTabPriorities.length,
"Window has expected number of tabs");
// aState should be in format [ priority, priority, priority ]
for (let i = 0; i < browsers.length; i++) {
is(getPriority(browsers[i]), aTabPriorities[i],
"Tab had expected priority");
}
// Test helper functions.
// getPriority and setPriority can take a tab or a Browser
function* getPriority(aBrowser) {
if (aBrowser.localName == "tab")
aBrowser = aBrowser.linkedBrowser;
return yield ContentTask.spawn(aBrowser, null, function* () {
return docShell.QueryInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocumentLoader)
.loadGroup
.QueryInterface(Components.interfaces.nsISupportsPriority)
.priority;
});
}
function* setPriority(aBrowser, aPriority) {
if (aBrowser.localName == "tab")
aBrowser = aBrowser.linkedBrowser;
yield ContentTask.spawn(aBrowser, aPriority, function* (aPriority) {
docShell.QueryInterface(Components.interfaces.nsIWebNavigation)
.QueryInterface(Components.interfaces.nsIDocumentLoader)
.loadGroup
.QueryInterface(Ci.nsISupportsPriority)
.priority = aPriority;
});
}
function* isWindowState(aWindow, aTabPriorities) {
let browsers = aWindow.gBrowser.browsers;
// Make sure we have the right number of tabs & priorities
is(browsers.length, aTabPriorities.length,
"Window has expected number of tabs");
// aState should be in format [ priority, priority, priority ]
for (let i = 0; i < browsers.length; i++) {
is(yield getPriority(browsers[i]), aTabPriorities[i],
"Tab " + i + " had expected priority");
}
}
function promiseWaitForFocus(aWindow) {
return new Promise((resolve) => {
waitForFocus(resolve, aWindow);
});
}
add_task(function*() {
// This is the real test. It creates multiple tabs & windows, changes focus,
// closes windows/tabs to make sure we behave correctly.
// This test assumes that no priorities have been adjusted and the loadgroup
// priority starts at 0.
function test_behavior() {
// Call window "window_A" to make the test easier to follow
let window_A = window;
// Call window "window_A" to make the test easier to follow
let window_A = window;
// Test 1 window, 1 tab case.
isWindowState(window_A, [-10]);
// Test 1 window, 1 tab case.
yield isWindowState(window_A, [HIGH]);
// Exising tab is tab_A1
let tab_A2 = window_A.gBrowser.addTab("http://example.com");
let tab_A3 = window_A.gBrowser.addTab("about:config");
tab_A3.linkedBrowser.addEventListener("load", function(aEvent) {
tab_A3.linkedBrowser.removeEventListener("load", arguments.callee, true);
// Exising tab is tab_A1
let tab_A2 = window_A.gBrowser.addTab("http://example.com");
let tab_A3 = window_A.gBrowser.addTab("about:config");
yield BrowserTestUtils.browserLoaded(tab_A3.linkedBrowser);
// tab_A2 isn't focused yet
isWindowState(window_A, [-10, 0, 0]);
// tab_A2 isn't focused yet
yield isWindowState(window_A, [HIGH, NORMAL, NORMAL]);
// focus tab_A2 & make sure priority got updated
window_A.gBrowser.selectedTab = tab_A2;
isWindowState(window_A, [0, -10, 0]);
// focus tab_A2 & make sure priority got updated
window_A.gBrowser.selectedTab = tab_A2;
yield isWindowState(window_A, [NORMAL, HIGH, NORMAL]);
window_A.gBrowser.removeTab(tab_A2);
// Next tab is auto selected
isWindowState(window_A, [0, -10]);
window_A.gBrowser.removeTab(tab_A2);
// Next tab is auto selected synchronously as part of removeTab, and we
// expect the priority to be updated immediately.
yield isWindowState(window_A, [NORMAL, HIGH]);
// Open another window then play with focus
let window_B = openDialog(location, "_blank", "chrome,all,dialog=no", "http://example.com");
window_B.addEventListener("load", function(aEvent) {
window_B.removeEventListener("load", arguments.callee, false);
window_B.gBrowser.addEventListener("load", function(aEvent) {
// waitForFocus can attach to the wrong "window" with about:blank loading first
// So just ensure that we're getting the load event for the right URI
if (window_B.gBrowser.currentURI.spec == "about:blank")
return;
window_B.gBrowser.removeEventListener("load", arguments.callee, true);
// Open another window then play with focus
let window_B = yield BrowserTestUtils.openNewBrowserWindow();
waitForFocus(function() {
isWindowState(window_A, [10, 0]);
isWindowState(window_B, [-10]);
yield promiseWaitForFocus(window_B);
yield isWindowState(window_A, [LOW, NORMAL]);
yield isWindowState(window_B, [HIGH]);
waitForFocus(function() {
isWindowState(window_A, [0, -10]);
isWindowState(window_B, [0]);
yield promiseWaitForFocus(window_A);
yield isWindowState(window_A, [NORMAL, HIGH]);
yield isWindowState(window_B, [NORMAL]);
waitForFocus(function() {
isWindowState(window_A, [10, 0]);
isWindowState(window_B, [-10]);
// And we're done. Cleanup & run the next test
window_B.close();
window_A.gBrowser.removeTab(tab_A3);
executeSoon(runNextTest);
}, window_B);
}, window_A);
}, window_B);
}, true);
}, false);
}, true);
}
yield promiseWaitForFocus(window_B);
yield isWindowState(window_A, [LOW, NORMAL]);
yield isWindowState(window_B, [HIGH]);
// Cleanup
window_A.gBrowser.removeTab(tab_A3);
yield BrowserTestUtils.closeWindow(window_B);
});
add_task(function*() {
// This is more a test of nsLoadGroup and how it handles priorities. But since
// we depend on its behavior, it's good to test it. This is testing that there
// are no errors if we adjust beyond nsISupportsPriority's bounds.
function test_extremePriorities() {
let tab_A1 = gBrowser.tabContainer.getItemAtIndex(0);
let oldPriority = getPriority(tab_A1);
// Set the priority of tab_A1 to the lowest possible. Selecting the other tab
// will try to lower it
setPriority(tab_A1, Ci.nsISupportsPriority.PRIORITY_LOWEST);
yield promiseWaitForFocus();
let tab_A2 = gBrowser.addTab("http://example.com");
tab_A2.linkedBrowser.addEventListener("load", function(aEvent) {
tab_A2.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.selectedTab = tab_A2;
is(getPriority(tab_A1), Ci.nsISupportsPriority.PRIORITY_LOWEST - PRIORITY_DELTA,
"Can adjust priority beyond 'lowest'");
let tab1 = gBrowser.tabs[0];
let oldPriority = yield getPriority(tab1);
// Now set priority to "highest" and make sure that no errors occur.
setPriority(tab_A1, Ci.nsISupportsPriority.PRIORITY_HIGHEST);
gBrowser.selectedTab = tab_A1;
// Set the priority of tab1 to the lowest possible. Selecting the other tab
// will try to lower it
yield setPriority(tab1, LOWEST);
is(getPriority(tab_A1), Ci.nsISupportsPriority.PRIORITY_HIGHEST + PRIORITY_DELTA,
"Can adjust priority beyond 'highest'");
let tab2 = gBrowser.addTab("http://example.com");
yield BrowserTestUtils.browserLoaded(tab2.linkedBrowser);
gBrowser.selectedTab = tab2;
is(yield getPriority(tab1), LOWEST - DELTA, "Can adjust priority beyond 'lowest'");
// Cleanup, run next test
gBrowser.removeTab(tab_A2);
executeSoon(function() {
setPriority(tab_A1, oldPriority);
runNextTest();
});
// Now set priority to "highest" and make sure that no errors occur.
yield setPriority(tab1, HIGHEST);
gBrowser.selectedTab = tab1;
}, true);
is(yield getPriority(tab1), HIGHEST + DELTA, "Can adjust priority beyond 'highest'");
// Cleanup
gBrowser.removeTab(tab2);
yield setPriority(tab1, oldPriority);
});
add_task(function*() {
// This tests that the priority doesn't get lost when switching the browser's remoteness
if (!gMultiProcessBrowser) {
return;
}
let browser = gBrowser.selectedBrowser;
let tests = [test_behavior, test_extremePriorities];
function runNextTest() {
if (tests.length) {
// Linux has problems if window isn't focused. Should help prevent [orange].
waitForFocus(tests.shift());
} else {
finish();
}
}
browser.loadURI("http://example.com");
yield BrowserTestUtils.browserLoaded(browser);
ok(browser.isRemoteBrowser, "web page should be loaded in remote browser");
is(yield getPriority(browser), HIGH, "priority of selected tab should be 'high'");
runNextTest();
}
browser.loadURI("about:rights");
yield BrowserTestUtils.browserLoaded(browser);
ok(!browser.isRemoteBrowser, "about:rights should switch browser to non-remote");
is(yield getPriority(browser), HIGH,
"priority of selected tab should be 'high' when going from remote to non-remote");
browser.loadURI("http://example.com");
yield BrowserTestUtils.browserLoaded(browser);
ok(browser.isRemoteBrowser, "going from about:rights to web page should switch browser to remote");
is(yield getPriority(browser), HIGH,
"priority of selected tab should be 'high' when going from non-remote to remote");
});

View File

@ -10,4 +10,4 @@
# hardcoded milestones in the tree from these two files.
#--------------------------------------------------------
47.0a1
48.0a1

View File

@ -224,17 +224,18 @@ var AnimationsController = {
return;
}
this.nodeFront = gInspector.selection.nodeFront;
let done = gInspector.updating("animationscontroller");
if (!gInspector.selection.isConnected() ||
!gInspector.selection.isElementNode()) {
!gInspector.selection.isElementNode() ||
gInspector.selection.isPseudoElementNode()) {
this.destroyAnimationPlayers();
this.emit(this.PLAYERS_UPDATED_EVENT);
done();
return;
}
this.nodeFront = gInspector.selection.nodeFront;
yield this.refreshAnimationPlayers(this.nodeFront);
this.emit(this.PLAYERS_UPDATED_EVENT, this.animationPlayers);

View File

@ -26,7 +26,7 @@
</div>
<div id="players"></div>
<div id="error-message">
<p>&invalidElement;</p>
<p id="error-type"></p>
<p>&selectElement;</p>
<button id="element-picker" standalone="true" class="devtools-button"></button>
</div>

View File

@ -183,6 +183,9 @@ var AnimationsPanel = {
} else {
document.body.setAttribute("empty", "true");
document.body.removeAttribute("timeline");
$("#error-type").textContent = gInspector.selection.isPseudoElementNode()
? L10N.getStr("panel.pseudoElementSelected")
: L10N.getStr("panel.invalidElementSelected");
}
},

View File

@ -1,21 +1,20 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
requestLongerTimeout(2);
// Test that the panel shows no animation data for invalid or not animated nodes
const STRINGS_URI = "chrome://devtools/locale/animationinspector.properties";
const L10N = new ViewHelpers.L10N(STRINGS_URI);
add_task(function*() {
yield addTab(TEST_URL_ROOT + "doc_simple_animation.html");
let {inspector, panel, window} = yield openAnimationInspector();
let {document} = window;
let {inspector, panel} = yield openAnimationInspector();
yield testEmptyPanel(inspector, panel);
});
function* testEmptyPanel(inspector, panel) {
info("Select node .still and check that the panel is empty");
let stillNode = yield getNodeFront(".still", inspector);
let onUpdated = panel.once(panel.UI_UPDATED_EVENT);
@ -26,6 +25,9 @@ function* testEmptyPanel(inspector, panel) {
"No animation players stored in the timeline component for a still node");
is(panel.animationsTimelineComponent.animationsEl.childNodes.length, 0,
"No animation displayed in the timeline component for a still node");
is(document.querySelector("#error-type").textContent,
L10N.getStr("panel.invalidElementSelected"),
"The correct error message is displayed");
info("Select the comment text node and check that the panel is empty");
let commentNode = yield inspector.walker.previousSibling(stillNode);
@ -36,4 +38,25 @@ function* testEmptyPanel(inspector, panel) {
is(panel.animationsTimelineComponent.animations.length, 0,
"No animation players stored in the timeline component for a text node");
is(panel.animationsTimelineComponent.animationsEl.childNodes.length, 0,
"No animation displayed in the timeline component for a text node");}
"No animation displayed in the timeline component for a text node");
is(document.querySelector("#error-type").textContent,
L10N.getStr("panel.invalidElementSelected"),
"The correct error message is displayed");
info("Select the pseudo element node and check that the panel is empty " +
"and contains the special animated pseudo-element message");
let pseudoElParent = yield getNodeFront(".pseudo", inspector);
let {nodes} = yield inspector.walker.children(pseudoElParent);
let pseudoEl = nodes[0];
onUpdated = panel.once(panel.UI_UPDATED_EVENT);
yield selectNode(pseudoEl, inspector);
yield onUpdated;
is(panel.animationsTimelineComponent.animations.length, 0,
"No animation players stored in the timeline component for a pseudo-node");
is(panel.animationsTimelineComponent.animationsEl.childNodes.length, 0,
"No animation displayed in the timeline component for a pseudo-node");
is(document.querySelector("#error-type").textContent,
L10N.getStr("panel.pseudoElementSelected"),
"The correct error message is displayed");
});

View File

@ -82,6 +82,21 @@
animation: no-compositor 10s cubic-bezier(.57,-0.02,1,.31) forwards;
}
.pseudo {
top: 800px;
left: 10px;
}
.pseudo::before {
content: "";
width: 50%;
height: 50%;
border-radius: 50%;
background: black;
position: absolute;
animation: simple-animation 1s infinite alternate;
}
@keyframes simple-animation {
100% {
transform: translateX(300px);
@ -112,5 +127,6 @@
<div class="ball long"></div>
<div class="ball negative-delay"></div>
<div class="ball no-compositor"></div>
<div class="ball pseudo"></div>
</body>
</html>

View File

@ -605,17 +605,19 @@ MarkupView.prototype = {
return;
}
// Ignore keystrokes with modifiers to allow native shortcuts (such as save:
// accel + S) to bubble up.
if (event.metaKey || event.ctrlKey || event.altKey || event.shiftKey) {
return;
}
switch (event.keyCode) {
case Ci.nsIDOMKeyEvent.DOM_VK_H:
if (event.metaKey || event.shiftKey) {
handled = false;
let node = this._selectedContainer.node;
if (node.hidden) {
this.walker.unhideNode(node);
} else {
let node = this._selectedContainer.node;
if (node.hidden) {
this.walker.unhideNode(node);
} else {
this.walker.hideNode(node);
}
this.walker.hideNode(node);
}
break;
case Ci.nsIDOMKeyEvent.DOM_VK_DELETE:

View File

@ -88,6 +88,7 @@ skip-if = (e10s && os == 'mac') # bug 1252345
[browser_markup_keybindings_03.js]
[browser_markup_keybindings_04.js]
[browser_markup_keybindings_delete_attributes.js]
[browser_markup_keybindings_scrolltonode.js]
[browser_markup_mutation_01.js]
[browser_markup_mutation_02.js]
[browser_markup_node_names.js]

View File

@ -0,0 +1,87 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test the keyboard shortcut "S" used to scroll to the selected node.
const HTML =
`<div style="width: 300px; height: 3000px; position:relative;">
<div id="scroll-top"
style="height: 50px; top: 0; position:absolute;">
TOP</div>
<div id="scroll-bottom"
style="height: 50px; bottom: 0; position:absolute;">
BOTTOM</div>
</div>`;
const TEST_URL = "data:text/html;charset=utf-8," + encodeURIComponent(HTML);
add_task(function*() {
let { inspector, testActor } = yield openInspectorForURL(TEST_URL);
info("Make sure the markup frame has the focus");
inspector.markup._frame.focus();
info("Before test starts, #scroll-top is visible, #scroll-bottom is hidden");
yield checkElementIsInViewport("#scroll-top", true, testActor);
yield checkElementIsInViewport("#scroll-bottom", false, testActor);
info("Select the #scroll-bottom node");
yield selectNode("#scroll-bottom", inspector);
info("Press S to scroll to the bottom node");
let waitForScroll = testActor.waitForEventOnNode("scroll");
yield EventUtils.synthesizeKey("S", {}, inspector.panelWin);
yield waitForScroll;
ok(true, "Scroll event received");
info("#scroll-top should be scrolled out, #scroll-bottom should be visible");
yield checkElementIsInViewport("#scroll-top", false, testActor);
yield checkElementIsInViewport("#scroll-bottom", true, testActor);
info("Select the #scroll-top node");
yield selectNode("#scroll-top", inspector);
info("Press S to scroll to the top node");
waitForScroll = testActor.waitForEventOnNode("scroll");
yield EventUtils.synthesizeKey("S", {}, inspector.panelWin);
yield waitForScroll;
ok(true, "Scroll event received");
info("#scroll-top should be visible, #scroll-bottom should be scrolled out");
yield checkElementIsInViewport("#scroll-top", true, testActor);
yield checkElementIsInViewport("#scroll-bottom", false, testActor);
info("Select #scroll-bottom node");
yield selectNode("#scroll-bottom", inspector);
info("Press shift + S, nothing should happen due to the modifier");
yield EventUtils.synthesizeKey("S", {shiftKey: true}, inspector.panelWin);
info("Same state, #scroll-top is visible, #scroll-bottom is scrolled out");
yield checkElementIsInViewport("#scroll-top", true, testActor);
yield checkElementIsInViewport("#scroll-bottom", false, testActor);
});
/**
* Verify that the element matching the provided selector is either in or out
* of the viewport, depending on the provided "expected" argument.
* Returns a promise that will resolve when the test has been performed.
*
* @param {String} selector
* css selector for the element to test
* @param {Boolean} expected
* true if the element is expected to be in the viewport, false otherwise
* @param {TestActor} testActor
* current test actor
* @return {Promise} promise
*/
function* checkElementIsInViewport(selector, expected, testActor) {
let isInViewport = yield testActor.eval(`
let node = content.document.querySelector("${selector}");
let rect = node.getBoundingClientRect();
rect.bottom >= 0 && rect.right >= 0 &&
rect.top <= content.innerHeight && rect.left <= content.innerWidth;
`);
is(isInViewport, expected,
selector + " in the viewport: expected to be " + expected);
}

View File

@ -15,10 +15,6 @@
sidebar tab -->
<!ENTITY animationInspectorTitle "Animations">
<!-- LOCALIZATION NOTE (invalidElement): This is the label shown in the panel
when an invalid node is currently selected in the inspector. -->
<!ENTITY invalidElement "No animations were found for the current element.">
<!-- LOCALIZATION NOTE (selectElement): This is the label shown in the panel
when an invalid node is currently selected in the inspector, to invite the
user to select a new node by clicking on the element-picker icon. -->

View File

@ -10,6 +10,18 @@
# A good criteria is the language in which you'd find the best
# documentation on web development on the web.
# LOCALIZATION NOTE (panel.invalidElementSelected):
# This is the label shown in the panel when an invalid node is currently
# selected in the inspector (i.e. a non-element node or a node that is not
# animated).
panel.invalidElementSelected=No animations were found for the current element.
# LOCALIZATION NOTE (panel.pseudoElementSelected):
# This is the label shown in the panel when a pseudo-element is currently
# selected in the inspector (pseudo-elements can be animated, but the tool
# doesn't yet support them).
panel.pseudoElementSelected=Animated pseudo-elements are not supported yet.
# LOCALIZATION NOTE (player.animationNameLabel):
# This string is displayed in each animation player widget. It is the label
# displayed before the animation name.

View File

@ -14,6 +14,7 @@ const { actions, snapshotState: states, viewState, dominatorTreeState } = requir
const telemetry = require("../telemetry");
const view = require("./view");
const refresh = require("./refresh");
const diffing = require("./diffing");
/**
* A series of actions are fired from this task to save, read and generate the
@ -427,6 +428,10 @@ const clearSnapshots = exports.clearSnapshots = function (heapWorker) {
dispatch({ type: actions.DELETE_SNAPSHOTS_START, ids });
if (getState().diffing) {
dispatch(diffing.toggleDiffing());
}
yield Promise.all(snapshots.map(snapshot => {
return heapWorker.deleteHeapSnapshot(snapshot.path).catch(error => {
reportException("clearSnapshots", error);

View File

@ -13,7 +13,6 @@ module.exports = createClass({
displayName: "Toolbar",
propTypes: {
censusDisplays: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
displayName: PropTypes.string.isRequired,
})).isRequired,
onTakeSnapshotClick: PropTypes.func.isRequired,
@ -29,7 +28,6 @@ module.exports = createClass({
view: PropTypes.string.isRequired,
onViewChange: PropTypes.func.isRequired,
dominatorTreeDisplays: PropTypes.arrayOf(PropTypes.shape({
name: PropTypes.string.isRequired,
displayName: PropTypes.string.isRequired,
})).isRequired,
onDominatorTreeDisplayChange: PropTypes.func.isRequired,

View File

@ -0,0 +1,58 @@
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test that clearSnapshots disables diffing when deleting snapshots
const {
takeSnapshotAndCensus,
clearSnapshots } = require("devtools/client/memory/actions/snapshot");
const {
snapshotState: states,
actions } = require("devtools/client/memory/constants");
const {
toggleDiffing,
selectSnapshotForDiffingAndRefresh
} = require("devtools/client/memory/actions/diffing");
function run_test() {
run_next_test();
}
add_task(function* () {
let front = new StubbedMemoryFront();
let heapWorker = new HeapAnalysesClient();
yield front.attach();
let store = Store();
const { getState, dispatch } = store;
ok(true, "Create 2 snapshots in SAVED_CENSUS state");
dispatch(takeSnapshotAndCensus(front, heapWorker));
dispatch(takeSnapshotAndCensus(front, heapWorker));
ok(true, "Snapshots created in SAVED_CENSUS state");
yield waitUntilSnapshotState(store,
[states.SAVED_CENSUS, states.SAVED_CENSUS]);
dispatch(toggleDiffing());
dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
getState().snapshots[0]));
dispatch(selectSnapshotForDiffingAndRefresh(heapWorker,
getState().snapshots[1]));
ok(getState().diffing, "We should be in diffing view");
ok(true, "Dispatch clearSnapshots action");
let deleteEvents = Promise.all([
waitUntilAction(store, actions.DELETE_SNAPSHOTS_START),
waitUntilAction(store, actions.DELETE_SNAPSHOTS_END)
]);
dispatch(clearSnapshots(heapWorker));
yield deleteEvents;
ok(true, "received delete snapshots events");
ok(getState().snapshots.length === 0, "Snapshots array should be empty");
ok(!getState().diffing, "We should no longer be diffing");
heapWorker.destroy();
yield front.detach();
});

View File

@ -15,6 +15,7 @@ skip-if = toolkit == 'android' || toolkit == 'gonk'
[test_action-clear-snapshots_03.js]
[test_action-clear-snapshots_04.js]
[test_action-clear-snapshots_05.js]
[test_action-clear-snapshots_06.js]
[test_action-export-snapshot.js]
[test_action-filter-01.js]
[test_action-filter-02.js]

View File

@ -23,14 +23,26 @@ let App = createClass({
onExit: PropTypes.func.isRequired,
},
onRotateViewport(id) {
this.props.dispatch(rotateViewport(id));
},
onResizeViewport(id, width, height) {
this.props.dispatch(resizeViewport(id, width, height));
},
render() {
let {
dispatch,
location,
viewports,
onExit,
} = this.props;
let {
onRotateViewport,
onResizeViewport,
} = this;
return dom.div(
{
id: "app",
@ -41,9 +53,8 @@ let App = createClass({
Viewports({
location,
viewports,
onRotateViewport: id => dispatch(rotateViewport(id)),
onResizeViewport: (id, width, height) =>
dispatch(resizeViewport(id, width, height)),
onRotateViewport,
onResizeViewport,
})
);
},

View File

@ -4,7 +4,7 @@
"use strict";
const { DOM: dom, createClass, PropTypes } =
const { DOM: dom, createClass, PropTypes, addons } =
require("devtools/client/shared/vendor/react");
const Types = require("../types");
@ -13,6 +13,8 @@ module.exports = createClass({
displayName: "Browser",
mixins: [ addons.PureRenderMixin ],
propTypes: {
location: Types.location.isRequired,
width: Types.viewport.width.isRequired,

View File

@ -5,13 +5,15 @@
"use strict";
const { getStr } = require("./utils/l10n");
const { DOM: dom, createClass, PropTypes } =
const { DOM: dom, createClass, PropTypes, addons } =
require("devtools/client/shared/vendor/react");
module.exports = createClass({
displayName: "GlobalToolbar",
mixins: [ addons.PureRenderMixin ],
propTypes: {
onExit: PropTypes.func.isRequired,
},

View File

@ -4,13 +4,15 @@
"use strict";
const { DOM: dom, createClass, PropTypes } =
const { DOM: dom, createClass, PropTypes, addons } =
require("devtools/client/shared/vendor/react");
module.exports = createClass({
displayName: "ViewportToolbar",
mixins: [ addons.PureRenderMixin ],
propTypes: {
onRotateViewport: PropTypes.func.isRequired,
},

View File

@ -22,14 +22,35 @@ module.exports = createClass({
onRotateViewport: PropTypes.func.isRequired,
},
onResizeViewport(width, height) {
let {
viewport,
onResizeViewport,
} = this.props;
onResizeViewport(viewport.id, width, height);
},
onRotateViewport() {
let {
viewport,
onRotateViewport,
} = this.props;
onRotateViewport(viewport.id);
},
render() {
let {
location,
viewport,
onResizeViewport,
onRotateViewport,
} = this.props;
let {
onRotateViewport,
onResizeViewport,
} = this;
return dom.div(
{
className: "viewport",

View File

@ -38,9 +38,8 @@ module.exports = createClass({
key: viewport.id,
location,
viewport,
onResizeViewport: (width, height) =>
onResizeViewport(viewport.id, width, height),
onRotateViewport: () => onRotateViewport(viewport.id),
onResizeViewport,
onRotateViewport,
});
})
);

View File

@ -1,9 +1,9 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
function test()
{
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
@ -15,9 +15,7 @@ function test()
content.location = "data:text/html,test context switch in Scratchpad";
}
function runTests()
{
function runTests() {
let sp = gScratchpadWindow.Scratchpad;
let contentMenu = gScratchpadWindow.document.getElementById("sp-menu-content");
let chromeMenu = gScratchpadWindow.document.getElementById("sp-menu-browser");
@ -29,7 +27,7 @@ function runTests()
let tests = [{
method: "run",
prepare: function() {
prepare: function*() {
sp.setContentContext();
is(sp.executionContext, gScratchpadWindow.SCRATCHPAD_CONTEXT_CONTENT,
@ -46,17 +44,18 @@ function runTests()
sp.editor.setText("window.foobarBug636725 = 'aloha';");
ok(!content.wrappedJSObject.foobarBug636725,
"no content.foobarBug636725");
let pageResult = yield inContent(function*() {
return content.wrappedJSObject.foobarBug636725;
});
ok(!pageResult, "no content.foobarBug636725");
},
then: function() {
then: function*() {
is(content.wrappedJSObject.foobarBug636725, "aloha",
"content.foobarBug636725 has been set");
}
},
{
}, {
method: "run",
prepare: function() {
prepare: function*() {
sp.setBrowserContext();
is(sp.executionContext, gScratchpadWindow.SCRATCHPAD_CONTEXT_BROWSER,
@ -77,62 +76,57 @@ function runTests()
is(sp.getText(), "window.foobarBug636725 = 'aloha2';",
"setText() worked");
},
then: function() {
then: function*() {
is(window.foobarBug636725, "aloha2",
"window.foobarBug636725 has been set");
delete window.foobarBug636725;
ok(!window.foobarBug636725, "no window.foobarBug636725");
}
},
{
}, {
method: "run",
prepare: function() {
prepare: function*() {
sp.editor.replaceText("gBrowser", sp.editor.getPosition(7));
is(sp.getText(), "window.gBrowser",
"setText() worked with no end for the replace range");
},
then: function([, , result]) {
then: function*([, , result]) {
is(result.class, "XULElement",
"chrome context has access to chrome objects");
}
},
{
}, {
method: "run",
prepare: function() {
prepare: function*() {
// Check that the sandbox is cached.
sp.editor.setText("typeof foobarBug636725cache;");
},
then: function([, , result]) {
then: function*([, , result]) {
is(result, "undefined", "global variable does not exist");
}
},
{
}, {
method: "run",
prepare: function() {
prepare: function*() {
sp.editor.setText("window.foobarBug636725cache = 'foo';" +
"typeof foobarBug636725cache;");
},
then: function([, , result]) {
then: function*([, , result]) {
is(result, "string",
"global variable exists across two different executions");
}
},
{
}, {
method: "run",
prepare: function() {
prepare: function*() {
sp.editor.setText("window.foobarBug636725cache2 = 'foo';" +
"typeof foobarBug636725cache2;");
},
then: function([, , result]) {
then: function*([, , result]) {
is(result, "string",
"global variable exists across two different executions");
}
},
{
}, {
method: "run",
prepare: function() {
prepare: function*() {
sp.setContentContext();
is(sp.executionContext, gScratchpadWindow.SCRATCHPAD_CONTEXT_CONTENT,
@ -140,7 +134,7 @@ function runTests()
sp.editor.setText("typeof foobarBug636725cache2;");
},
then: function([, , result]) {
then: function*([, , result]) {
is(result, "undefined",
"global variable no longer exists after changing the context");
}

View File

@ -1,9 +1,9 @@
/* vim: set ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
function test()
{
function test() {
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab();
@ -15,49 +15,52 @@ function test()
content.location = "data:text/html,<p>test run() and display() in Scratchpad";
}
function runTests()
{
function runTests() {
let sp = gScratchpadWindow.Scratchpad;
let tests = [{
method: "run",
prepare: function() {
content.wrappedJSObject.foobarBug636725 = 1;
prepare: function*() {
yield inContent(function*() {
content.wrappedJSObject.foobarBug636725 = 1;
});
sp.editor.setText("++window.foobarBug636725");
},
then: function([code, , result]) {
then: function*([code, , result]) {
is(code, sp.getText(), "code is correct");
is(result, content.wrappedJSObject.foobarBug636725,
let pageResult = yield inContent(function*() {
return content.wrappedJSObject.foobarBug636725;
});
is(result, pageResult,
"result is correct");
is(sp.getText(), "++window.foobarBug636725",
"run() does not change the editor content");
is(content.wrappedJSObject.foobarBug636725, 2,
"run() updated window.foobarBug636725");
is(pageResult, 2, "run() updated window.foobarBug636725");
}
},
{
}, {
method: "display",
prepare: function() {},
then: function() {
is(content.wrappedJSObject.foobarBug636725, 3,
"display() updated window.foobarBug636725");
prepare: function*() {},
then: function*() {
let pageResult = yield inContent(function*() {
return content.wrappedJSObject.foobarBug636725;
});
is(pageResult, 3, "display() updated window.foobarBug636725");
is(sp.getText(), "++window.foobarBug636725\n/*\n3\n*/",
"display() shows evaluation result in the textbox");
is(sp.editor.getSelection(), "\n/*\n3\n*/", "getSelection is correct");
}
},
{
}, {
method: "run",
prepare: function() {
prepare: function*() {
sp.editor.setText("window.foobarBug636725 = 'a';\n" +
"window.foobarBug636725 = 'b';");
sp.editor.setSelection({ line: 0, ch: 0 }, { line: 0, ch: 29 });
},
then: function([code, , result]) {
then: function*([code, , result]) {
is(code, "window.foobarBug636725 = 'a';", "code is correct");
is(result, "a", "result is correct");
@ -65,20 +68,23 @@ function runTests()
"window.foobarBug636725 = 'b';",
"run() does not change the textbox value");
is(content.wrappedJSObject.foobarBug636725, "a",
"run() worked for the selected range");
let pageResult = yield inContent(function*() {
return content.wrappedJSObject.foobarBug636725;
});
is(pageResult, "a", "run() worked for the selected range");
}
},
{
}, {
method: "display",
prepare: function() {
prepare: function*() {
sp.editor.setText("window.foobarBug636725 = 'c';\n" +
"window.foobarBug636725 = 'b';");
sp.editor.setSelection({ line: 0, ch: 0 }, { line: 0, ch: 22 });
},
then: function() {
is(content.wrappedJSObject.foobarBug636725, "a",
"display() worked for the selected range");
then: function*() {
let pageResult = yield inContent(function*() {
return content.wrappedJSObject.foobarBug636725;
});
is(pageResult, "a", "display() worked for the selected range");
is(sp.getText(), "window.foobarBug636725" +
"\n/*\na\n*/" +

View File

@ -191,21 +191,19 @@ function runAsyncTests(aScratchpad, aTests)
* @return Promise
* The promise that will be resolved when all tests are finished.
*/
function runAsyncCallbackTests(aScratchpad, aTests)
{
let deferred = promise.defer();
var runAsyncCallbackTests = Task.async(function*(aScratchpad, aTests) {
for (let {prepare, method, then} of aTests) {
yield prepare();
let res = yield aScratchpad[method]();
yield then(res);
}
});
(function runTest() {
if (aTests.length) {
let test = aTests.shift();
test.prepare();
aScratchpad[test.method]().then(test.then.bind(test)).then(runTest);
} else {
deferred.resolve();
}
})();
return deferred.promise;
/**
* A simple wrapper for ContentTask.spawn for more compact code.
*/
function inContent(generator) {
return ContentTask.spawn(gBrowser.selectedBrowser, {}, generator);
}
function cleanup()

View File

@ -226,6 +226,28 @@ var TestActor = exports.TestActor = protocol.ActorClass({
response: {}
}),
/**
* Wait for a specific event on a node matching the provided selector.
* @param {String} eventName The name of the event to listen to
* @param {String} selector Optional: css selector of the node which should
* trigger the event. If ommitted, target will be the content window
*/
waitForEventOnNode: protocol.method(function (eventName, selector) {
return new Promise(resolve => {
let node = selector ? this._querySelector(selector) : this.content;
node.addEventListener(eventName, function onEvent() {
node.removeEventListener(eventName, onEvent);
resolve();
});
});
}, {
request: {
eventName: Arg(0, "string"),
selector: Arg(1, "nullable:string")
},
response: {}
}),
/**
* Change the zoom level of the page.
* Optionally subscribe to the box-model highlighter's update event and waiting

View File

@ -232,6 +232,7 @@ ul.children + .tag-line::before {
margin-top: .3em;
left: 1px;
position: absolute;
z-index: 1;
}
/* Selected nodes in the tree should have light selected text.

View File

@ -674,9 +674,7 @@
.devtools-separator {
margin: 0 2px;
width: 2px;
background-image: linear-gradient(transparent, hsla(204,45%,98%,.1), transparent),
linear-gradient(transparent, hsla(206,37%,4%,.6), transparent),
linear-gradient(transparent, hsla(204,45%,98%,.1), transparent);
background-image: linear-gradient(transparent 15%, var(--theme-splitter-color) 15%, var(--theme-splitter-color) 85%, transparent 85%);
background-size: 1px 100%;
background-repeat: no-repeat;
background-position: 0, 1px, 2px;

View File

@ -212,7 +212,6 @@ tags = mcb
[browser_webconsole_bug_586388_select_all.js]
[browser_webconsole_bug_587617_output_copy.js]
[browser_webconsole_bug_588342_document_focus.js]
skip-if = e10s # Bug 1042253 - webconsole tests disabled with e10s
[browser_webconsole_bug_588730_text_node_insertion.js]
[browser_webconsole_bug_588967_input_expansion.js]
[browser_webconsole_bug_589162_css_filter.js]

View File

@ -7,28 +7,30 @@
const TEST_URI = "data:text/html;charset=utf-8,Web Console test for bug 588342";
var fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
add_task(function* () {
let { browser } = yield loadTab(TEST_URI);
let hud = yield openConsole();
yield consoleOpened(hud);
is(fm.focusedWindow, browser.contentWindow,
"content document has focus");
yield checkConsoleFocus(hud);
fm = null;
let isFocused = yield ContentTask.spawn(browser, { }, function* () {
var fm = Components.classes["@mozilla.org/focus-manager;1"].
getService(Components.interfaces.nsIFocusManager);
return fm.focusedWindow == content;
});
ok(isFocused, "content document has focus");
});
function consoleOpened(hud) {
let deferred = promise.defer();
waitForFocus(function() {
is(hud.jsterm.inputNode.getAttribute("focused"), "true",
"jsterm input is focused on web console open");
isnot(fm.focusedWindow, content, "content document has no focus");
closeConsole(null).then(deferred.resolve);
}, hud.iframeWindow);
function* checkConsoleFocus(hud) {
let fm = Cc["@mozilla.org/focus-manager;1"].getService(Ci.nsIFocusManager);
return deferred.promise;
yield new Promise(resolve => {
waitForFocus(resolve);
});
is(hud.jsterm.inputNode.getAttribute("focused"), "true",
"jsterm input is focused on web console open");
is(fm.focusedWindow, hud.iframeWindow, "hud window is focused");
yield closeConsole(null);
}

View File

@ -2074,17 +2074,6 @@ MediaManager::GetUserMedia(nsPIDOMWindowInner* aWindow,
RemoveFromWindowList(windowID, listener);
return NS_OK;
}
} else if (loop) {
// Record that we gave Loop permission to use camera access.
nsCOMPtr<nsIPermissionManager> permManager =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID, &rv);
NS_ENSURE_SUCCESS(rv, rv);
rv = permManager->Add(docURI, "camera",
nsIPermissionManager::ALLOW_ACTION,
nsIPermissionManager::EXPIRE_SESSION,
0);
NS_ENSURE_SUCCESS(rv, rv);
}
#if defined(MOZ_B2G_CAMERA) && defined(MOZ_WIDGET_GONK)

View File

@ -9,6 +9,7 @@
#include "nsXPCOM.h"
#include "nsIXULRuntime.h"
#include "ServiceWorkerManager.h"
#include "nsICategoryManager.h"
#include "mozilla/Services.h"
#include "mozilla/unused.h"
@ -183,29 +184,43 @@ nsresult
PushNotifier::NotifyPushObservers(const nsACString& aScope,
Maybe<nsTArray<uint8_t>> aData)
{
nsCOMPtr<nsIObserverService> obsService =
mozilla::services::GetObserverService();
if (!obsService) {
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIPushMessage> message = nullptr;
if (aData) {
message = new PushMessage(aData.ref());
}
return obsService->NotifyObservers(message, OBSERVER_TOPIC_PUSH,
NS_ConvertUTF8toUTF16(aScope).get());
return DoNotifyObservers(message, OBSERVER_TOPIC_PUSH, aScope);
}
nsresult
PushNotifier::NotifySubscriptionChangeObservers(const nsACString& aScope)
{
return DoNotifyObservers(nullptr, OBSERVER_TOPIC_SUBSCRIPTION_CHANGE, aScope);
}
nsresult
PushNotifier::DoNotifyObservers(nsISupports *aSubject, const char *aTopic,
const nsACString& aScope)
{
nsCOMPtr<nsIObserverService> obsService =
mozilla::services::GetObserverService();
if (!obsService) {
return NS_ERROR_FAILURE;
}
return obsService->NotifyObservers(nullptr,
OBSERVER_TOPIC_SUBSCRIPTION_CHANGE,
// If there's a service for this push category, make sure it is alive.
nsCOMPtr<nsICategoryManager> catMan =
do_GetService(NS_CATEGORYMANAGER_CONTRACTID);
if (catMan) {
nsXPIDLCString contractId;
nsresult rv = catMan->GetCategoryEntry("push",
PromiseFlatCString(aScope).get(),
getter_Copies(contractId));
if (NS_SUCCEEDED(rv)) {
// Ensure the service is created - we don't need to do anything with
// it though - we assume the service constructor attaches a listener.
nsCOMPtr<nsISupports> service = do_GetService(contractId);
}
}
return obsService->NotifyObservers(aSubject, aTopic,
NS_ConvertUTF8toUTF16(aScope).get());
}

View File

@ -57,6 +57,8 @@ private:
nsresult NotifyPushObservers(const nsACString& aScope,
Maybe<nsTArray<uint8_t>> aData);
nsresult NotifySubscriptionChangeObservers(const nsACString& aScope);
nsresult DoNotifyObservers(nsISupports *aSubject, const char *aTopic,
const nsACString& aScope);
bool ShouldNotifyObservers(nsIPrincipal* aPrincipal);
bool ShouldNotifyWorkers(nsIPrincipal* aPrincipal);
};

View File

@ -46,6 +46,8 @@ UNIFIED_SOURCES += [
'PushNotifier.cpp',
]
TEST_DIRS += ['test/xpcshell']
include('/ipc/chromium/chromium-config.mozbuild')
if CONFIG['GNU_CXX']:

View File

@ -0,0 +1,4 @@
EXTRA_COMPONENTS += [
'PushServiceHandler.js',
'PushServiceHandler.manifest',
]

View File

@ -6,6 +6,8 @@ skip-if = toolkit == 'android'
[test_clear_origin_data.js]
[test_drop_expired.js]
[test_handler_service.js]
support-files = PushServiceHandler.js PushServiceHandler.manifest
[test_notification_ack.js]
[test_notification_data.js]
[test_notification_duplicate.js]

View File

@ -63,8 +63,7 @@ public class AppConstants {
* If our MAX_SDK_VERSION is lower than ICS, we must not be an ICS device.
* Otherwise, we need a range check.
*/
public static final boolean preM = MAX_SDK_VERSION < 23 ||
(MIN_SDK_VERSION < 23 && Build.VERSION.SDK_INT < 23 && !Build.VERSION.RELEASE.equals("M"));
public static final boolean preMarshmallow = MAX_SDK_VERSION < 23 || (MIN_SDK_VERSION < 23 && Build.VERSION.SDK_INT < 23);
public static final boolean preLollipop = MAX_SDK_VERSION < 21 || (MIN_SDK_VERSION < 21 && Build.VERSION.SDK_INT < 21);
public static final boolean preJBMR2 = MAX_SDK_VERSION < 18 || (MIN_SDK_VERSION < 18 && Build.VERSION.SDK_INT < 18);
public static final boolean preJBMR1 = MAX_SDK_VERSION < 17 || (MIN_SDK_VERSION < 17 && Build.VERSION.SDK_INT < 17);

View File

@ -447,8 +447,6 @@ public class Favicons {
}
loadTasks.clear();
}
LoadFaviconTask.closeHTTPClient();
}
/**

View File

@ -4,17 +4,11 @@
package org.mozilla.gecko.favicons;
import android.content.ContentResolver;
import android.content.Context;
import android.graphics.Bitmap;
import ch.boye.httpclientandroidlib.impl.client.DefaultHttpClient;
import android.text.TextUtils;
import android.util.Log;
import ch.boye.httpclientandroidlib.Header;
import ch.boye.httpclientandroidlib.HttpEntity;
import ch.boye.httpclientandroidlib.HttpResponse;
import ch.boye.httpclientandroidlib.client.methods.HttpGet;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.db.BrowserDB;
@ -22,9 +16,12 @@ import org.mozilla.gecko.favicons.decoders.FaviconDecoder;
import org.mozilla.gecko.favicons.decoders.LoadFaviconResult;
import org.mozilla.gecko.util.GeckoJarReader;
import org.mozilla.gecko.util.IOUtils;
import org.mozilla.gecko.util.ProxySelector;
import org.mozilla.gecko.util.ThreadUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
@ -78,14 +75,18 @@ public class LoadFaviconTask {
volatile boolean mCancelled;
// Assuming square favicons, judging by width only is acceptable.
protected int targetWidth;
protected int targetWidthAndHeight;
private LinkedList<LoadFaviconTask> chainees;
private boolean isChaining;
static DefaultHttpClient httpClient = new DefaultHttpClient();
private static class Response {
public final int contentLength;
public final InputStream stream;
public LoadFaviconTask(Context context, String pageURL, String faviconURL, int flags, OnFaviconLoadedListener listener) {
this(context, pageURL, faviconURL, flags, listener, -1, false);
private Response(InputStream stream, int contentLength) {
this.stream = stream;
this.contentLength = contentLength;
}
}
public LoadFaviconTask(Context context, String pageURL, String faviconURL, int flags, OnFaviconLoadedListener listener,
@ -98,7 +99,7 @@ public class LoadFaviconTask {
this.faviconURL = faviconURL;
this.listener = listener;
this.flags = flags;
this.targetWidth = targetWidth;
this.targetWidthAndHeight = targetWidth;
this.onlyFromLocal = onlyFromLocal;
}
@ -127,73 +128,53 @@ public class LoadFaviconTask {
* @param faviconURI URL of Favicon to try and download
* @return The HttpResponse containing the downloaded Favicon if successful, null otherwise.
*/
private HttpResponse tryDownload(URI faviconURI) throws URISyntaxException, IOException {
private Response tryDownload(URI faviconURI) throws URISyntaxException, IOException {
HashSet<String> visitedLinkSet = new HashSet<>();
visitedLinkSet.add(faviconURI.toString());
return tryDownloadRecurse(faviconURI, visitedLinkSet);
}
private HttpResponse tryDownloadRecurse(URI faviconURI, HashSet<String> visited) throws URISyntaxException, IOException {
private Response tryDownloadRecurse(URI faviconURI, HashSet<String> visited) throws URISyntaxException, IOException {
if (visited.size() == MAX_REDIRECTS_TO_FOLLOW) {
return null;
}
HttpGet request = new HttpGet(faviconURI);
request.setHeader("User-Agent", GeckoAppShell.getGeckoInterface().getDefaultUAString());
HttpResponse response = httpClient.execute(request);
if (response == null) {
HttpURLConnection connection = (HttpURLConnection) ProxySelector.openConnectionWithProxy(faviconURI);
connection.setRequestProperty("User-Agent", GeckoAppShell.getGeckoInterface().getDefaultUAString());
connection.connect();
// Was the response a failure?
int status = connection.getResponseCode();
// Handle HTTP status codes requesting a redirect.
if (status >= 300 && status < 400) {
final String newURI = connection.getHeaderField("Location");
// Handle mad webservers.
try {
if (newURI == null || newURI.equals(faviconURI.toString())) {
return null;
}
if (visited.contains(newURI)) {
// Already been redirected here - abort.
return null;
}
visited.add(newURI);
} finally {
connection.disconnect();
}
return tryDownloadRecurse(new URI(newURI), visited);
}
if (status >= 400) {
connection.disconnect();
return null;
}
if (response.getStatusLine() != null) {
// Was the response a failure?
int status = response.getStatusLine().getStatusCode();
// Handle HTTP status codes requesting a redirect.
if (status >= 300 && status < 400) {
Header header = response.getFirstHeader("Location");
// Handle mad webservers.
final String newURI;
try {
if (header == null) {
return null;
}
newURI = header.getValue();
if (newURI == null || newURI.equals(faviconURI.toString())) {
return null;
}
if (visited.contains(newURI)) {
// Already been redirected here - abort.
return null;
}
visited.add(newURI);
} finally {
// Consume the entity before recurse or exit.
try {
response.getEntity().consumeContent();
} catch (Exception e) {
// Doesn't matter.
}
}
return tryDownloadRecurse(new URI(newURI), visited);
}
if (status >= 400) {
// Consume the entity and exit.
try {
response.getEntity().consumeContent();
} catch (Exception e) {
// Doesn't matter.
}
return null;
}
}
return response;
return new Response(connection.getInputStream(), connection.getHeaderFieldInt("Content-Length", -1));
}
/**
@ -256,22 +237,17 @@ public class LoadFaviconTask {
*/
private LoadFaviconResult downloadAndDecodeImage(URI targetFaviconURL) throws IOException, URISyntaxException {
// Try the URL we were given.
HttpResponse response = tryDownload(targetFaviconURL);
Response response = tryDownload(targetFaviconURL);
if (response == null) {
return null;
}
HttpEntity entity = response.getEntity();
if (entity == null) {
return null;
}
// Decode the image from the fetched response.
try {
return decodeImageFromResponse(entity);
return decodeImageFromResponse(response);
} finally {
// Close the stream and free related resources.
entity.consumeContent();
IOUtils.safeStreamClose(response.stream);
}
}
@ -279,27 +255,26 @@ public class LoadFaviconTask {
* Copies the favicon stream to a buffer and decodes downloaded content into bitmaps using the
* FaviconDecoder.
*
* @param entity HttpEntity containing the favicon stream to decode.
* @param response Response containing the favicon stream to decode.
* @return A LoadFaviconResult containing the bitmap(s) extracted from the downloaded file, or
* null if no or corrupt data were received.
* @throws IOException If attempts to fully read the stream result in such an exception, such as
* in the event of a transient connection failure.
*/
private LoadFaviconResult decodeImageFromResponse(HttpEntity entity) throws IOException {
private LoadFaviconResult decodeImageFromResponse(Response response) throws IOException {
// This may not be provided, but if it is, it's useful.
final long entityReportedLength = entity.getContentLength();
int bufferSize;
if (entityReportedLength > 0) {
if (response.contentLength > 0) {
// The size was reported and sane, so let's use that.
// Integer overflow should not be a problem for Favicon sizes...
bufferSize = (int) entityReportedLength + 1;
bufferSize = response.contentLength + 1;
} else {
// No declared size, so guess and reallocate later if it turns out to be too small.
bufferSize = DEFAULT_FAVICON_BUFFER_SIZE;
}
// Read the InputStream into a byte[].
ConsumedInputStream result = IOUtils.readFully(entity.getContent(), bufferSize);
ConsumedInputStream result = IOUtils.readFully(response.stream, bufferSize);
if (result == null) {
return null;
}
@ -474,7 +449,7 @@ public class LoadFaviconTask {
sizes.add(b.getWidth());
}
int bestSize = Favicons.selectBestSizeFromList(sizes, targetWidth);
int bestSize = Favicons.selectBestSizeFromList(sizes, targetWidthAndHeight);
return iconMap.get(bestSize);
}
}
@ -528,8 +503,7 @@ public class LoadFaviconTask {
*/
private Bitmap pushToCacheAndGetResult(LoadFaviconResult loadedBitmaps) {
Favicons.putFaviconsInMemCache(faviconURL, loadedBitmaps.getBitmaps());
Bitmap result = Favicons.getSizedFaviconFromCache(faviconURL, targetWidth);
return result;
return Favicons.getSizedFaviconFromCache(faviconURL, targetWidthAndHeight);
}
private static boolean imageIsValid(final Bitmap image) {
@ -576,9 +550,9 @@ public class LoadFaviconTask {
// Notify listeners, scaling if required.
if ((flags & FLAG_BYPASS_CACHE_WHEN_DOWNLOADING_ICONS) != 0) {
scaled = Bitmap.createScaledBitmap(image, targetWidth, targetWidth, true);
} else if (targetWidth != -1 && image != null && image.getWidth() != targetWidth) {
scaled = Favicons.getSizedFaviconFromCache(faviconURL, targetWidth);
scaled = Bitmap.createScaledBitmap(image, targetWidthAndHeight, targetWidthAndHeight, true);
} else if (targetWidthAndHeight != -1 && image != null && image.getWidth() != targetWidthAndHeight) {
scaled = Favicons.getSizedFaviconFromCache(faviconURL, targetWidthAndHeight);
} else {
scaled = image;
}
@ -629,23 +603,4 @@ public class LoadFaviconTask {
int getId() {
return id;
}
static void closeHTTPClient() {
// This work must be done on a background thread because it shuts down
// the connection pool, which typically involves closing a connection --
// which counts as network activity.
if (ThreadUtils.isOnBackgroundThread()) {
if (httpClient != null) {
httpClient.close();
}
return;
}
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
LoadFaviconTask.closeHTTPClient();
}
});
}
}

View File

@ -28,7 +28,7 @@ class AndroidImportPreference extends MultiPrefMultiChoicePreference {
public static class Handler implements GeckoPreferences.PrefHandler {
public boolean setupPref(Context context, Preference pref) {
// Feature disabled on devices running Android M+ (Bug 1183559)
return Versions.preM && Restrictions.isAllowed(context, Restrictable.IMPORT_SETTINGS);
return Versions.preMarshmallow && Restrictions.isAllowed(context, Restrictable.IMPORT_SETTINGS);
}
public void onChange(Context context, Preference pref, Object newValue) { }

View File

@ -14,15 +14,12 @@ import org.mozilla.gecko.R;
import org.mozilla.gecko.preferences.GeckoPreferences;
import org.mozilla.gecko.util.ThreadUtils;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.os.Build;
import android.provider.Settings;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
@ -64,7 +61,7 @@ public class TabQueueHelper {
* @return true if the specified context can draw on top of other apps, false otherwise.
*/
public static boolean canDrawOverlays(Context context) {
if (AppConstants.Versions.preM) {
if (AppConstants.Versions.preMarshmallow) {
return true; // We got the permission at install time.
}

View File

@ -177,7 +177,7 @@ public class TabQueueService extends Service {
} else {
try {
windowManager.addView(toastLayout, toastLayoutParams);
} catch (final SecurityException e) {
} catch (final SecurityException | WindowManager.BadTokenException e) {
Toast.makeText(this, getText(R.string.tab_queue_toast_message), Toast.LENGTH_SHORT).show();
showSettingsNotification();
}
@ -233,7 +233,7 @@ public class TabQueueService extends Service {
@TargetApi(Build.VERSION_CODES.M)
private void showSettingsNotification() {
if (AppConstants.Versions.preM) {
if (AppConstants.Versions.preMarshmallow) {
return;
}

View File

@ -22,15 +22,14 @@ import org.mozilla.gecko.animation.PropertyAnimator;
import org.mozilla.gecko.animation.ViewHelper;
import org.mozilla.gecko.toolbar.BrowserToolbarTabletBase.ForwardButtonAnimation;
import org.mozilla.gecko.util.ColorUtils;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.StringUtils;
import org.mozilla.gecko.widget.themed.ThemedLinearLayout;
import org.mozilla.gecko.widget.themed.ThemedTextView;
import android.content.Context;
import android.os.SystemClock;
import android.support.annotation.Nullable;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.text.Spannable;
import android.text.SpannableString;
import android.text.SpannableStringBuilder;
@ -125,7 +124,11 @@ public class ToolbarDisplayLayout extends ThemedLinearLayout {
// Icon used for about:home
private static final int LEVEL_SEARCH_ICON = 999;
private final ForegroundColorSpan mBlockedColor;
private final ForegroundColorSpan mUrlColorSpan;
private final ForegroundColorSpan mBlockedColorSpan;
private final ForegroundColorSpan mDomainColorSpan;
private final ForegroundColorSpan mPrivateDomainColorSpan;
private final ForegroundColorSpan mCertificateOwnerColorSpan;
public ToolbarDisplayLayout(Context context, AttributeSet attrs) {
super(context, attrs);
@ -138,7 +141,11 @@ public class ToolbarDisplayLayout extends ThemedLinearLayout {
mTitle = (ThemedTextView) findViewById(R.id.url_bar_title);
mTitlePadding = mTitle.getPaddingRight();
mBlockedColor = new ForegroundColorSpan(ColorUtils.getColor(context, R.color.url_bar_blockedtext));
mUrlColorSpan = new ForegroundColorSpan(ColorUtils.getColor(context, R.color.url_bar_urltext));
mBlockedColorSpan = new ForegroundColorSpan(ColorUtils.getColor(context, R.color.url_bar_blockedtext));
mDomainColorSpan = new ForegroundColorSpan(ColorUtils.getColor(context, R.color.url_bar_domaintext));
mPrivateDomainColorSpan = new ForegroundColorSpan(ColorUtils.getColor(context, R.color.url_bar_domaintext_private));
mCertificateOwnerColorSpan = new ForegroundColorSpan(ColorUtils.getColor(context, R.color.affirmative_green));
mSiteSecurity = (ImageButton) findViewById(R.id.site_security);
@ -249,7 +256,7 @@ public class ToolbarDisplayLayout extends ThemedLinearLayout {
final String title = tab.getDisplayTitle();
final SpannableStringBuilder builder = new SpannableStringBuilder(title);
builder.setSpan(mBlockedColor, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
builder.setSpan(mBlockedColorSpan, 0, title.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
setTitle(builder);
setContentDescription(null);
@ -271,27 +278,48 @@ public class ToolbarDisplayLayout extends ThemedLinearLayout {
final SiteIdentity siteIdentity = tab.getSiteIdentity();
if (siteIdentity.hasOwner()) {
final String title;
if (siteIdentity.hasCountry()) {
title = String.format("%s (%s)", siteIdentity.getOwner(), siteIdentity.getCountry());
} else {
title = siteIdentity.getOwner();
}
final int color = ContextCompat.getColor(getContext(), R.color.affirmative_green);
final SpannableString spannable = new SpannableString(title);
spannable.setSpan(new ForegroundColorSpan(color), 0, title.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
setTitle(spannable);
} else if (!TextUtils.isEmpty(baseDomain)) {
// Show Owner of EV certificate as title
updateTitleFromSiteIdentity(siteIdentity);
} else if (!HardwareUtils.isTablet() && !TextUtils.isEmpty(baseDomain)) {
// Show just the base domain as title
setTitle(baseDomain);
} else {
setTitle(strippedURL);
// Display full URL as title
updateAndColorTitleFromFullURL(strippedURL, baseDomain, tab.isPrivate());
}
}
private void updateTitleFromSiteIdentity(SiteIdentity siteIdentity) {
final String title;
if (siteIdentity.hasCountry()) {
title = String.format("%s (%s)", siteIdentity.getOwner(), siteIdentity.getCountry());
} else {
title = siteIdentity.getOwner();
}
final SpannableString spannable = new SpannableString(title);
spannable.setSpan(mCertificateOwnerColorSpan, 0, title.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
setTitle(spannable);
}
private void updateAndColorTitleFromFullURL(String url, String baseDomain, boolean isPrivate) {
int index = url.indexOf(baseDomain);
if (index == -1) {
setTitle(url);
return;
}
final SpannableStringBuilder builder = new SpannableStringBuilder(url);
builder.setSpan(mUrlColorSpan, 0, url.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
builder.setSpan(isPrivate ? mPrivateDomainColorSpan : mDomainColorSpan,
index, index + baseDomain.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
setTitle(builder);
}
private String stripAboutReaderURL(final String url) {
if (!AboutPages.isAboutReader(url)) {
return url;

View File

@ -12,6 +12,7 @@ import org.mozilla.gecko.R;
import org.mozilla.apache.commons.codec.binary.Hex;
import org.mozilla.gecko.permissions.Permissions;
import org.mozilla.gecko.util.ProxySelector;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
@ -47,8 +48,6 @@ import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
@ -373,21 +372,6 @@ public class UpdateService extends IntentService {
}
}
private URLConnection openConnectionWithProxy(URI uri) throws java.net.MalformedURLException, java.io.IOException {
Log.i(LOGTAG, "opening connection with URI: " + uri);
ProxySelector ps = ProxySelector.getDefault();
Proxy proxy = Proxy.NO_PROXY;
if (ps != null) {
List<Proxy> proxies = ps.select(uri);
if (proxies != null && !proxies.isEmpty()) {
proxy = proxies.get(0);
}
}
return uri.toURL().openConnection(proxy);
}
private UpdateInfo findUpdate(boolean force) {
try {
URI uri = getUpdateURI(force);
@ -398,7 +382,7 @@ public class UpdateService extends IntentService {
}
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
Document dom = builder.parse(openConnectionWithProxy(uri).getInputStream());
Document dom = builder.parse(ProxySelector.openConnectionWithProxy(uri).getInputStream());
NodeList nodes = dom.getElementsByTagName("update");
if (nodes == null || nodes.getLength() == 0)
@ -579,7 +563,7 @@ public class UpdateService extends IntentService {
mWifiLock.acquire();
}
URLConnection conn = openConnectionWithProxy(info.uri);
URLConnection conn = ProxySelector.openConnectionWithProxy(info.uri);
int length = conn.getContentLength();
output = new BufferedOutputStream(new FileOutputStream(downloadFile));

View File

@ -23,8 +23,24 @@ import android.text.TextUtils;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.net.URI;
import java.net.URLConnection;
import java.util.List;
public class ProxySelector {
public static URLConnection openConnectionWithProxy(URI uri) throws IOException {
java.net.ProxySelector ps = java.net.ProxySelector.getDefault();
Proxy proxy = Proxy.NO_PROXY;
if (ps != null) {
List<Proxy> proxies = ps.select(uri);
if (proxies != null && !proxies.isEmpty()) {
proxy = proxies.get(0);
}
}
return uri.toURL().openConnection(proxy);
}
public ProxySelector() {
}

View File

@ -19,8 +19,6 @@ final class UnusedResourcesUtil {
public static final int[] USED_IN_JS = {
R.drawable.ab_search,
R.drawable.alert_app,
R.drawable.alert_app_animation,
R.drawable.alert_camera,
R.drawable.alert_download,
R.drawable.alert_download_animation,

View File

@ -504,7 +504,6 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'RemoteClientsDialogFragment.java',
'RemoteTabsExpandableListAdapter.java',
'Restarter.java',
'Restrictions.java',
'restrictions/DefaultConfiguration.java',
'restrictions/GuestProfileConfiguration.java',
'restrictions/Restrictable.java',
@ -512,6 +511,7 @@ gbjar.sources += ['java/org/mozilla/gecko/' + x for x in [
'restrictions/RestrictionCache.java',
'restrictions/RestrictionConfiguration.java',
'restrictions/RestrictionProvider.java',
'restrictions/Restrictions.java',
'ScreenshotObserver.java',
'ServiceNotificationClient.java',
'SessionParser.java',

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 408 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 377 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 443 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 407 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 544 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 460 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 450 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 408 B

Some files were not shown because too many files have changed in this diff Show More