Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2016-03-03 11:57:42 +01:00
Родитель f61d05c3cf 82cf3c3383
Коммит 78ab08e8be
52 изменённых файлов: 988 добавлений и 480 удалений

Просмотреть файл

@ -1379,44 +1379,37 @@ var BookmarkingUI = {
aHeaderItem.nextSibling.remove();
}
PlacesUtils.history.QueryInterface(Ci.nsPIPlacesDatabase)
.asyncExecuteLegacyQueries([query], 1, options, {
handleResult: function (aResultSet) {
let onItemCommand = function (aEvent) {
let item = aEvent.target;
openUILink(item.getAttribute("targetURI"), aEvent);
CustomizableUI.hidePanelForNode(item);
};
let onItemCommand = function (aEvent) {
let item = aEvent.target;
openUILink(item.getAttribute("targetURI"), aEvent);
CustomizableUI.hidePanelForNode(item);
};
let fragment = document.createDocumentFragment();
let row;
while ((row = aResultSet.getNextRow())) {
let uri = row.getResultByIndex(1);
let title = row.getResultByIndex(2);
let icon = row.getResultByIndex(6);
let fragment = document.createDocumentFragment();
let root = PlacesUtils.history.executeQuery(query, options).root;
root.containerOpen = true;
for (let i = 0; i < root.childCount; i++) {
let node = root.getChild(i);
let uri = node.uri;
let title = node.title;
let icon = node.icon;
let item =
document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
"menuitem");
item.setAttribute("label", title || uri);
item.setAttribute("targetURI", uri);
item.setAttribute("class", "menuitem-iconic menuitem-with-favicon bookmark-item " +
extraCSSClass);
item.addEventListener("command", onItemCommand);
if (icon) {
let iconURL = "moz-anno:favicon:" + icon;
item.setAttribute("image", iconURL);
}
fragment.appendChild(item);
}
aHeaderItem.parentNode.insertBefore(fragment, aHeaderItem.nextSibling);
},
handleError: function (aError) {
Cu.reportError("Error while attempting to show recent bookmarks: " + aError);
},
handleCompletion: function (aReason) {
},
});
let item =
document.createElementNS("http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul",
"menuitem");
item.setAttribute("label", title || uri);
item.setAttribute("targetURI", uri);
item.setAttribute("class", "menuitem-iconic menuitem-with-favicon bookmark-item " +
extraCSSClass);
item.addEventListener("command", onItemCommand);
if (icon) {
let iconURL = "moz-anno:favicon:" + icon;
item.setAttribute("image", iconURL);
}
fragment.appendChild(item);
}
root.containerOpen = false;
aHeaderItem.parentNode.insertBefore(fragment, aHeaderItem.nextSibling);
},
/**

Просмотреть файл

@ -252,7 +252,8 @@ ContentSearchUIController.prototype = {
let searchText = this.input;
let searchTerms;
if (this._table.hidden ||
aEvent.originalTarget.id == "contentSearchDefaultEngineHeader") {
aEvent.originalTarget.id == "contentSearchDefaultEngineHeader" ||
aEvent instanceof KeyboardEvent) {
searchTerms = searchText.value;
}
else {

Просмотреть файл

@ -290,7 +290,6 @@ skip-if = os == 'win' || e10s # Bug 1159268 - Need a content-process safe versio
[browser_bug1070778.js]
[browser_accesskeys.js]
[browser_canonizeURL.js]
skip-if = e10s # Bug 1094510 - test hits the network in e10s mode only
[browser_clipboard.js]
[browser_contentAreaClick.js]
[browser_contextmenu.js]

Просмотреть файл

@ -1,8 +1,3 @@
function test() {
waitForExplicitFinish();
testNext();
}
var pairs = [
["example", "http://www.example.net/"],
["ex-ample", "http://www.ex-ample.net/"],
@ -20,37 +15,56 @@ var pairs = [
["ex ample", Services.search.defaultEngine.getSubmission("ex ample", null, "keyword").uri.spec],
];
function testNext() {
if (!pairs.length) {
finish();
return;
}
add_task(function*() {
for (let [inputValue, expectedURL] of pairs) {
let focusEventPromise = BrowserTestUtils.waitForEvent(gURLBar, "focus");
let messagePromise = BrowserTestUtils.waitForMessage(gBrowser.selectedBrowser.messageManager,
"browser_canonizeURL:start");
let [inputValue, expectedURL] = pairs.shift();
let stoppedLoadPromise = ContentTask.spawn(gBrowser.selectedBrowser, [inputValue, expectedURL],
function([inputValue, expectedURL]) {
return new Promise(resolve => {
let wpl = {
onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
if (aStateFlags & Ci.nsIWebProgressListener.STATE_START &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
if (!aRequest || !(aRequest instanceof Ci.nsIChannel)) {
return;
}
aRequest.QueryInterface(Ci.nsIChannel);
is(aRequest.originalURI.spec, expectedURL,
"entering '" + inputValue + "' loads expected URL");
gBrowser.addProgressListener({
onStateChange: function onStateChange(aWebProgress, aRequest, aStateFlags, aStatus) {
if (aStateFlags & Ci.nsIWebProgressListener.STATE_START &&
aStateFlags & Ci.nsIWebProgressListener.STATE_IS_NETWORK) {
is(aRequest.originalURI.spec, expectedURL,
"entering '" + inputValue + "' loads expected URL");
webProgress.removeProgressListener(filter);
filter.removeProgressListener(wpl);
docShell.QueryInterface(Ci.nsIWebNavigation);
docShell.stop(docShell.STOP_ALL);
resolve();
}
},
};
let filter = Cc["@mozilla.org/appshell/component/browser-status-filter;1"]
.createInstance(Ci.nsIWebProgress);
filter.addProgressListener(wpl, Ci.nsIWebProgress.NOTIFY_ALL);
gBrowser.removeProgressListener(this);
gBrowser.stop();
executeSoon(testNext);
let webProgress = docShell.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIWebProgress);
webProgress.addProgressListener(filter, Ci.nsIWebProgress.NOTIFY_ALL);
// We're sending this off to trigger the start of the this test, when all the
// listeners are in place:
sendAsyncMessage("browser_canonizeURL:start");
});
}
}
});
);
gBrowser.selectedBrowser.focus();
gURLBar.focus();
yield Promise.all([focusEventPromise, messagePromise]);
gURLBar.addEventListener("focus", function onFocus() {
gURLBar.removeEventListener("focus", onFocus);
gURLBar.inputField.value = inputValue.slice(0, -1);
EventUtils.synthesizeKey(inputValue.slice(-1) , {});
EventUtils.synthesizeKey("VK_RETURN", { shiftKey: true });
});
gBrowser.selectedBrowser.focus();
gURLBar.focus();
}
yield stoppedLoadPromise;
}
});

Просмотреть файл

@ -105,7 +105,7 @@
when-connection="secure secure-ev"/>
</vbox>
<vbox id="identity-popup-securityView-body">
<vbox id="identity-popup-securityView-body" flex="1">
<!-- (EV) Certificate Information -->
<description id="identity-popup-content-verified-by"
when-connection="secure-ev">&identity.connectionVerified1;</description>
@ -168,7 +168,9 @@
label="&identity.enableMixedContentBlocking.label;"
accesskey="&identity.enableMixedContentBlocking.accesskey;"
oncommand="gIdentityHandler.enableMixedContentProtection()"/>
</vbox>
<vbox id="identity-popup-securityView-footer">
<!-- More Security Information -->
<button label="&identity.moreInfoLinkText2;"
oncommand="gIdentityHandler.handleMoreInfoClick(event);"/>

Просмотреть файл

@ -346,11 +346,47 @@ DistributionCustomizer.prototype = {
Cu.reportError(e);
}
var usedPreferences = [];
if (sections["Preferences-" + this._locale]) {
for (let key of enumerate(this._ini.getKeys("Preferences-" + this._locale))) {
try {
let value = this._ini.getString("Preferences-" + this._locale, key);
if (value) {
Preferences.set(key, parseValue(value));
}
usedPreferences.push(key);
} catch (e) { /* ignore bad prefs and move on */ }
}
}
if (sections["Preferences-" + this._language]) {
for (let key of enumerate(this._ini.getKeys("Preferences-" + this._language))) {
if (usedPreferences.indexOf(key) > -1) {
continue;
}
try {
let value = this._ini.getString("Preferences-" + this._language, key);
if (value) {
Preferences.set(key, parseValue(value));
}
usedPreferences.push(key);
} catch (e) { /* ignore bad prefs and move on */ }
}
}
if (sections["Preferences"]) {
for (let key of enumerate(this._ini.getKeys("Preferences"))) {
if (usedPreferences.indexOf(key) > -1) {
continue;
}
try {
let value = parseValue(this._ini.getString("Preferences", key));
Preferences.set(key, value);
let value = this._ini.getString("Preferences", key);
if (value) {
value = value.replace(/%LOCALE%/g, this._locale);
value = value.replace(/%LANGUAGE%/g, this._language);
Preferences.set(key, parseValue(value));
}
} catch (e) { /* ignore bad prefs and move on */ }
}
}
@ -363,8 +399,9 @@ DistributionCustomizer.prototype = {
if (sections["LocalizablePreferences-" + this._locale]) {
for (let key of enumerate(this._ini.getKeys("LocalizablePreferences-" + this._locale))) {
try {
let value = parseValue(this._ini.getString("LocalizablePreferences-" + this._locale, key));
if (value !== undefined) {
let value = this._ini.getString("LocalizablePreferences-" + this._locale, key);
if (value) {
value = parseValue(value);
localizedStr.data = "data:text/plain," + key + "=" + value;
defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
}
@ -379,8 +416,9 @@ DistributionCustomizer.prototype = {
continue;
}
try {
let value = parseValue(this._ini.getString("LocalizablePreferences-" + this._language, key));
if (value !== undefined) {
let value = this._ini.getString("LocalizablePreferences-" + this._language, key);
if (value) {
value = parseValue(value);
localizedStr.data = "data:text/plain," + key + "=" + value;
defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
}
@ -395,13 +433,14 @@ DistributionCustomizer.prototype = {
continue;
}
try {
let value = parseValue(this._ini.getString("LocalizablePreferences", key));
if (value !== undefined) {
let value = this._ini.getString("LocalizablePreferences", key);
if (value) {
value = parseValue(value);
value = value.replace(/%LOCALE%/g, this._locale);
value = value.replace(/%LANGUAGE%/g, this._language);
localizedStr.data = "data:text/plain," + key + "=" + value;
defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
}
defaults._prefBranch.setComplexValue(key, Ci.nsIPrefLocalizedString, localizedStr);
} catch (e) { /* ignore bad prefs and move on */ }
}
}

Просмотреть файл

@ -125,6 +125,19 @@ global.makeWidgetId = id => {
return id.replace(/[^a-z0-9_-]/g, "_");
};
function promisePopupShown(popup) {
return new Promise(resolve => {
if (popup.state == "open") {
resolve();
} else {
popup.addEventListener("popupshown", function onPopupShown(event) {
popup.removeEventListener("popupshown", onPopupShown);
resolve();
});
}
});
}
class BasePopup {
constructor(extension, viewNode, popupURL) {
let popupURI = Services.io.newURI(popupURL, null, extension.baseURI);
@ -254,6 +267,10 @@ class BasePopup {
// Resizes the browser to match the preferred size of the content.
resizeBrowser() {
if (!this.browser) {
return;
}
let width, height;
try {
let w = {}, h = {};
@ -310,7 +327,12 @@ global.PanelPopup = class PanelPopup extends BasePopup {
}
closePopup() {
this.viewNode.hidePopup();
promisePopupShown(this.viewNode).then(() => {
// Make sure we're not already destroyed.
if (this.viewNode) {
this.viewNode.hidePopup();
}
});
}
};

Просмотреть файл

@ -3,6 +3,8 @@
"use strict";
function* testInArea(area) {
let scriptPage = url => `<html><head><meta charset="utf-8"><script src="${url}"></script></head></html>`;
let extension = ExtensionTestUtils.loadExtension({
manifest: {
"background": {
@ -14,17 +16,22 @@ function* testInArea(area) {
},
files: {
"popup-a.html": `<script src="popup-a.js"></script>`,
"popup-a.html": scriptPage("popup-a.js"),
"popup-a.js": function() {
browser.runtime.sendMessage("from-popup-a");
browser.runtime.onMessage.addListener(msg => {
if (msg == "close-popup") {
window.close();
}
});
},
"data/popup-b.html": `<script src="popup-b.js"></script>`,
"data/popup-b.html": scriptPage("popup-b.js"),
"data/popup-b.js": function() {
browser.runtime.sendMessage("from-popup-b");
},
"data/background.html": `<script src="background.js"></script>`,
"data/background.html": scriptPage("background.js"),
"data/background.js": function() {
let sendClick;
@ -51,26 +58,36 @@ function* testInArea(area) {
},
() => {
browser.browserAction.setPopup({popup: "/popup-a.html"});
sendClick({expectEvent: false, expectPopup: "a"});
sendClick({expectEvent: false, expectPopup: "a", runNextTest: true});
},
() => {
browser.test.sendMessage("next-test", {expectClosed: true});
},
];
let expect = {};
sendClick = ({expectEvent, expectPopup}) => {
expect = {event: expectEvent, popup: expectPopup};
sendClick = ({expectEvent, expectPopup, runNextTest}) => {
expect = {event: expectEvent, popup: expectPopup, runNextTest};
browser.test.sendMessage("send-click");
};
browser.runtime.onMessage.addListener(msg => {
if (expect.popup) {
if (msg == "close-popup") {
return;
} else if (expect.popup) {
browser.test.assertEq(msg, `from-popup-${expect.popup}`,
"expected popup opened");
} else {
browser.test.fail("unexpected popup");
browser.test.fail(`unexpected popup: ${msg}`);
}
expect.popup = null;
browser.test.sendMessage("next-test");
if (expect.runNextTest) {
expect.runNextTest = false;
tests.shift()();
} else {
browser.test.sendMessage("next-test");
}
});
browser.browserAction.onClicked.addListener(() => {
@ -85,6 +102,11 @@ function* testInArea(area) {
});
browser.test.onMessage.addListener((msg) => {
if (msg == "close-popup") {
browser.runtime.sendMessage("close-popup");
return;
}
if (msg != "next-test") {
browser.test.fail("Expecting 'next-test' message");
}
@ -107,13 +129,23 @@ function* testInArea(area) {
});
let widget;
extension.onMessage("next-test", Task.async(function* () {
extension.onMessage("next-test", Task.async(function* (expecting = {}) {
if (!widget) {
widget = getBrowserActionWidget(extension);
CustomizableUI.addWidgetToArea(widget.id, area);
}
if (expecting.expectClosed) {
let panel = getBrowserActionPopup(extension);
ok(panel, "Expect panel to exist");
yield promisePopupShown(panel);
yield closeBrowserAction(extension);
extension.sendMessage("close-popup");
yield promisePopupHidden(panel);
ok(true, "Panel is closed");
} else {
yield closeBrowserAction(extension);
}
extension.sendMessage("next-test");
}));

Просмотреть файл

@ -19,6 +19,11 @@ add_task(function* testPageActionPopup() {
"popup-a.html": scriptPage("popup-a.js"),
"popup-a.js": function() {
browser.runtime.sendMessage("from-popup-a");
browser.runtime.onMessage.addListener(msg => {
if (msg == "close-popup") {
window.close();
}
});
},
"data/popup-b.html": scriptPage("popup-b.js"),
@ -55,26 +60,36 @@ add_task(function* testPageActionPopup() {
},
() => {
browser.pageAction.setPopup({tabId, popup: "/popup-a.html"});
sendClick({expectEvent: false, expectPopup: "a"});
sendClick({expectEvent: false, expectPopup: "a", runNextTest: true});
},
() => {
browser.test.sendMessage("next-test", {expectClosed: true});
},
];
let expect = {};
sendClick = ({expectEvent, expectPopup}) => {
expect = {event: expectEvent, popup: expectPopup};
sendClick = ({expectEvent, expectPopup, runNextTest}) => {
expect = {event: expectEvent, popup: expectPopup, runNextTest};
browser.test.sendMessage("send-click");
};
browser.runtime.onMessage.addListener(msg => {
if (expect.popup) {
if (msg == "close-popup") {
return;
} else if (expect.popup) {
browser.test.assertEq(msg, `from-popup-${expect.popup}`,
"expected popup opened");
} else {
browser.test.fail("unexpected popup");
browser.test.fail(`unexpected popup: ${msg}`);
}
expect.popup = null;
browser.test.sendMessage("next-test");
if (expect.runNextTest) {
expect.runNextTest = false;
tests.shift()();
} else {
browser.test.sendMessage("next-test");
}
});
browser.pageAction.onClicked.addListener(() => {
@ -89,6 +104,11 @@ add_task(function* testPageActionPopup() {
});
browser.test.onMessage.addListener((msg) => {
if (msg == "close-popup") {
browser.runtime.sendMessage("close-popup");
return;
}
if (msg != "next-test") {
browser.test.fail("Expecting 'next-test' message");
}
@ -118,12 +138,22 @@ add_task(function* testPageActionPopup() {
clickPageAction(extension);
});
extension.onMessage("next-test", Task.async(function* () {
extension.onMessage("next-test", Task.async(function* (expecting = {}) {
let panel = document.getElementById(panelId);
if (panel) {
if (expecting.expectClosed) {
ok(panel, "Expect panel to exist");
yield promisePopupShown(panel);
extension.sendMessage("close-popup");
yield promisePopupHidden(panel);
ok(true, `Panel is closed`);
} else if (panel) {
yield promisePopupShown(panel);
panel.hidePopup();
}
if (panel) {
panel = document.getElementById(panelId);
is(panel, null, "panel successfully removed from document after hiding");
}

Просмотреть файл

@ -138,6 +138,9 @@ add_task(function* testWebNavigationFrames() {
is(getAllFramesDetails.length, collectedDetails.length,
"number of frames found should equal the number onCompleted events collected");
is(getAllFramesDetails[0].frameId, 0, "the root frame has the expected frameId");
is(getAllFramesDetails[0].parentFrameId, -1, "the root frame has the expected parentFrameId");
// ordered by frameId
let sortByFrameId = (el1, el2) => {
let val1 = el1 ? el1.frameId : -1;

Просмотреть файл

@ -7,7 +7,7 @@
* clickBrowserAction clickPageAction
* getBrowserActionPopup getPageActionPopup
* closeBrowserAction closePageAction
* promisePopupShown
* promisePopupShown promisePopupHidden
*/
var {AppConstants} = Cu.import("resource://gre/modules/AppConstants.jsm");
@ -59,6 +59,16 @@ function promisePopupShown(popup) {
});
}
function promisePopupHidden(popup) {
return new Promise(resolve => {
let onPopupHidden = event => {
popup.removeEventListener("popuphidden", onPopupHidden);
resolve();
};
popup.addEventListener("popuphidden", onPopupHidden);
});
}
function getBrowserActionWidget(extension) {
return CustomizableUI.getWidget(makeWidgetId(extension.id) + "-browser-action");
}
@ -68,6 +78,8 @@ function getBrowserActionPopup(extension, win = window) {
if (group.areaType == CustomizableUI.TYPE_TOOLBAR) {
return win.document.getElementById("customizationui-widget-panel");
} else {
return win.PanelUI.panel;
}
return null;
}

Просмотреть файл

@ -46,7 +46,6 @@ skip-if = e10s # Bug ?????? - test fails - "Number of dragged items should be th
[browser_library_search.js]
[browser_library_views_liveupdate.js]
[browser_markPageAsFollowedLink.js]
skip-if = e10s # Bug 933103 - mochitest's EventUtils.synthesizeMouse functions not e10s friendly (test does EventUtils.sendMouseEvent...)
[browser_sidebarpanels_click.js]
skip-if = true # temporarily disabled for breaking the treeview - bug 658744
[browser_sort_in_library.js]

Просмотреть файл

@ -1,7 +1,3 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/
*/
/**
* Tests that visits across frames are correctly represented in the database.
*/
@ -11,76 +7,61 @@ const PAGE_URL = BASE_URL + "/framedPage.html";
const LEFT_URL = BASE_URL + "/frameLeft.html";
const RIGHT_URL = BASE_URL + "/frameRight.html";
var gTabLoaded = false;
var gLeftFrameVisited = false;
add_task(function* test() {
// We must wait for both frames to be loaded and the visits to be registered.
let deferredLeftFrameVisit = PromiseUtils.defer();
let deferredRightFrameVisit = PromiseUtils.defer();
var observer = {
observe: function(aSubject, aTopic, aData)
{
let url = aSubject.QueryInterface(Ci.nsIURI).spec;
if (url == LEFT_URL ) {
is(getTransitionForUrl(url), null,
"Embed visits should not get a database entry.");
gLeftFrameVisited = true;
maybeClickLink();
}
else if (url == RIGHT_URL ) {
is(getTransitionForUrl(url), PlacesUtils.history.TRANSITION_FRAMED_LINK,
"User activated visits should get a FRAMED_LINK transition.");
finish();
}
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver])
};
Services.obs.addObserver(observer, "uri-visit-saved", false);
Services.obs.addObserver(function observe(subject) {
Task.spawn(function* () {
let url = subject.QueryInterface(Ci.nsIURI).spec;
if (url == LEFT_URL ) {
is((yield getTransitionForUrl(url)), null,
"Embed visits should not get a database entry.");
deferredLeftFrameVisit.resolve();
}
else if (url == RIGHT_URL ) {
is((yield getTransitionForUrl(url)),
PlacesUtils.history.TRANSITION_FRAMED_LINK,
"User activated visits should get a FRAMED_LINK transition.");
Services.obs.removeObserver(observe, "uri-visit-saved");
deferredRightFrameVisit.resolve();
}
});
}, "uri-visit-saved", false);
function test()
{
waitForExplicitFinish();
gBrowser.selectedTab = gBrowser.addTab(PAGE_URL);
let frameCount = 0;
gBrowser.selectedBrowser.addEventListener("DOMContentLoaded",
function (event)
{
// Wait for all the frames.
if (frameCount++ < 2)
return;
gBrowser.selectedBrowser.removeEventListener("DOMContentLoaded", arguments.callee, false)
gTabLoaded = true;
maybeClickLink();
}, false
);
}
// Open a tab and wait for all the subframes to load.
let tab = yield BrowserTestUtils.openNewForegroundTab(gBrowser, PAGE_URL);
function maybeClickLink() {
if (gTabLoaded && gLeftFrameVisited) {
// Click on the link in the left frame to cause a page load in the
// right frame.
EventUtils.sendMouseEvent({type: "click"}, "clickme", content.frames[0]);
// Wait for the left frame visit to be registered.
info("Waiting left frame visit");
yield deferredLeftFrameVisit.promise;
// Click on the link in the left frame to cause a page load in the
// right frame.
info("Clicking link");
yield ContentTask.spawn(tab.linkedBrowser, {}, function* () {
content.frames[0].document.getElementById("clickme").click();
});
// Wait for the right frame visit to be registered.
info("Waiting right frame visit");
yield deferredRightFrameVisit.promise;
yield BrowserTestUtils.removeTab(tab);
});
function* getTransitionForUrl(url) {
// Ensure all the transactions completed.
yield PlacesTestUtils.promiseAsyncUpdates();
let db = yield PlacesUtils.promiseDBConnection();
let rows = yield db.execute(`
SELECT visit_type
FROM moz_historyvisits
WHERE place_id = (SELECT id FROM moz_places WHERE url = :url)`,
{ url });
if (rows.length) {
return rows[0].getResultByName("visit_type");
}
return null;
}
function getTransitionForUrl(aUrl)
{
let dbConn = PlacesUtils.history
.QueryInterface(Ci.nsPIPlacesDatabase).DBConnection;
let stmt = dbConn.createStatement(
"SELECT visit_type FROM moz_historyvisits WHERE place_id = " +
"(SELECT id FROM moz_places WHERE url = :page_url)");
stmt.params.page_url = aUrl;
try {
if (!stmt.executeStep()) {
return null;
}
return stmt.row.visit_type;
}
finally {
stmt.finalize();
}
}
registerCleanupFunction(function ()
{
gBrowser.removeTab(gBrowser.selectedTab);
Services.obs.removeObserver(observer, "uri-visit-saved");
})

Просмотреть файл

@ -13,6 +13,28 @@ distribution.test.string.noquotes=Test String
distribution.test.int=777
distribution.test.bool.true=true
distribution.test.bool.false=false
distribution.test.empty=
distribution.test.pref.locale="%LOCALE%"
distribution.test.pref.language.reset="Preference Set"
distribution.test.pref.locale.reset="Preference Set"
distribution.test.pref.locale.set="Preference Set"
distribution.test.pref.language.set="Preference Set"
[Preferences-en]
distribution.test.pref.language.en="en"
distribution.test.pref.language.reset=
distribution.test.pref.language.set="Language Set"
distribution.test.pref.locale.set="Language Set"
[Preferences-en-US]
distribution.test.pref.locale.en-US="en-US"
distribution.test.pref.locale.reset=
distribution.test.pref.locale.set="Locale Set"
[Preferences-de]
distribution.test.pref.language.de="de"
[LocalizablePreferences]
distribution.test.locale="%LOCALE%"
@ -33,4 +55,4 @@ distribution.test.locale.reset=
distribution.test.locale.set="Locale Set"
[LocalizablePreferences-de]
distribution.test.locale.de="de"
distribution.test.language.de="de"

Просмотреть файл

@ -71,10 +71,30 @@ add_task(function* () {
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.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(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"));
// This value was never set because of the empty language specific pref
Assert.throws(() => Services.prefs.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"));
// This value was overridden by a locale specific setting
Assert.equal(Services.prefs.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");
// Language should not override locale
Assert.notEqual(Services.prefs.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.locale.de", Ci.nsIPrefLocalizedString));
Assert.throws(() => Services.prefs.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));
// This value was never set because of the empty locale specific pref

Просмотреть файл

@ -61,6 +61,15 @@ var ContentClick = {
// Note: We don't need the sidebar code here.
// Mark the page as a user followed link. This is done so that history can
// distinguish automatic embed visits from user activated ones. For example
// pages loaded in frames are embed visits and lost with the session, while
// visits across frames should be preserved.
try {
if (!PrivateBrowsingUtils.isWindowPrivate(window))
PlacesUIUtils.markPageAsFollowedLink(json.href);
} catch (ex) { /* Skip invalid URIs. */ }
// This part is based on handleLinkClick.
var where = window.whereToOpenLink(json);
if (where == "current")
@ -73,14 +82,5 @@ var ContentClick = {
referrerPolicy: json.referrerPolicy,
noReferrer: json.noReferrer };
window.openLinkIn(json.href, where, params);
// Mark the page as a user followed link. This is done so that history can
// distinguish automatic embed visits from user activated ones. For example
// pages loaded in frames are embed visits and lost with the session, while
// visits across frames should be preserved.
try {
if (!PrivateBrowsingUtils.isWindowPrivate(window))
PlacesUIUtils.markPageAsFollowedLink(json.href);
} catch (ex) { /* Skip invalid URIs. */ }
}
};

Просмотреть файл

@ -17,6 +17,15 @@
@hudButtonFocused@
}
#identity-popup-multiView > .panel-viewcontainer > .panel-viewstack > .panel-subviews {
border-bottom-right-radius: 3.5px;
}
#identity-popup-multiView > .panel-viewcontainer > .panel-viewstack > .panel-subviews:-moz-locale-dir(rtl) {
border-bottom-right-radius: 0;
border-bottom-left-radius: 3.5px;
}
#tracking-action-block,
#tracking-action-unblock,
#tracking-action-unblock-private,

Просмотреть файл

@ -83,15 +83,9 @@
#identity-popup-multiView > .panel-viewcontainer > .panel-viewstack > .panel-subviews {
background: var(--panel-arrowcontent-background);
border-bottom-right-radius: 3.5px;
padding: 0;
}
#identity-popup-multiView > .panel-viewcontainer > .panel-viewstack > .panel-subviews:-moz-locale-dir(rtl) {
border-bottom-right-radius: 0;
border-bottom-left-radius: 3.5px;
}
.identity-popup-section:not(:first-child) {
border-top: 1px solid var(--panel-separator-color);
}
@ -100,14 +94,19 @@
#identity-popup-security-content,
#identity-popup-permissions-content,
#tracking-protection-content {
padding: 0.5em 0 1em;
-moz-padding-start: calc(2em + 24px);
-moz-padding-end: 1em;
background-repeat: no-repeat;
background-position: 1em 1em;
background-size: 24px auto;
}
#identity-popup-security-content,
#identity-popup-permissions-content,
#tracking-protection-content {
padding: 0.5em 0 1em;
-moz-padding-start: calc(2em + 24px);
-moz-padding-end: 1em;
}
#identity-popup-securityView:-moz-locale-dir(rtl),
#identity-popup-security-content:-moz-locale-dir(rtl),
#identity-popup-permissions-content:-moz-locale-dir(rtl),
@ -208,7 +207,6 @@
}
#identity-popup-securityView {
padding-bottom: 2em;
overflow: hidden;
}
@ -252,7 +250,14 @@
color: Graytext;
}
#identity-popup-securityView-header,
#identity-popup-securityView-body {
-moz-margin-start: calc(2em + 24px);
-moz-margin-end: 1em;
}
#identity-popup-securityView-header {
margin-top: 0.5em;
border-bottom: 1px solid var(--panel-separator-color);
padding-bottom: 1em;
}
@ -261,6 +266,30 @@
-moz-padding-end: 1em;
}
#identity-popup-securityView-footer {
margin-top: 1em;
background-color: hsla(210,4%,10%,.07);
}
#identity-popup-securityView-footer > button {
-moz-appearance: none;
margin: 0;
border: none;
border-top: 1px solid #ccc;
padding: 8px 20px;
color: ButtonText;
background-color: transparent;
}
#identity-popup-securityView-footer > button:hover,
#identity-popup-securityView-footer > button:focus {
background-color: hsla(210,4%,10%,.07);
}
#identity-popup-securityView-footer > button:hover:active {
background-color: hsla(210,4%,10%,.12);
}
#identity-popup-content-verifier ~ description {
margin-top: 1em;
color: Graytext;

13
devtools/bootstrap.js поставляемый
Просмотреть файл

@ -8,6 +8,13 @@ const Cu = Components.utils;
const Ci = Components.interfaces;
const {Services} = Cu.import("resource://gre/modules/Services.jsm", {});
function actionOccurred(id) {
let {require} = Cu.import("resource://devtools/shared/Loader.jsm", {});
let Telemetry = require("devtools/client/shared/telemetry");;
let telemetry = new Telemetry();
telemetry.actionOccurred(id);
}
// Helper to listen to a key on all windows
function MultiWindowKeyListener({ keyCode, ctrlKey, altKey, callback }) {
let keyListener = function (event) {
@ -150,6 +157,8 @@ function reload(event) {
gDevTools.showToolbox(target);
}, 1000);
}
actionOccurred("reloadAddonReload");
}
let listener;
@ -165,5 +174,7 @@ function shutdown() {
listener.stop();
listener = null;
}
function install() {}
function install() {
actionOccurred("reloadAddonInstalled");
}
function uninstall() {}

Просмотреть файл

@ -82,7 +82,10 @@ const EVENTS = {
OPTIONS_POPUP_HIDDEN: "Debugger:OptionsPopupHidden",
// When the widgets layout has been changed.
LAYOUT_CHANGED: "Debugger:LayoutChanged"
LAYOUT_CHANGED: "Debugger:LayoutChanged",
// When a worker has been selected.
WORKER_SELECTED: "Debugger::WorkerSelected"
};
// Descriptions for what a stack frame represents after the debugger pauses.
@ -108,7 +111,7 @@ Cu.import("resource://devtools/client/shared/widgets/ViewHelpers.jsm");
var L10N = new ViewHelpers.L10N(DBG_STRINGS_URI);
Cu.import("resource://devtools/client/shared/browser-loader.js");
const require = BrowserLoader("resource://devtools/client/debugger/", this).require;
const require = BrowserLoader("resource://devtools/client/debugger/", window).require;
XPCOMUtils.defineConstant(this, "require", require);
const { gDevTools } = require("devtools/client/framework/devtools");
@ -491,8 +494,8 @@ Workers.prototype = {
for (let workerActor in this._workerForms) {
if (!(workerActor in workerForms)) {
DebuggerView.Workers.removeWorker(this._workerForms[workerActor]);
delete this._workerForms[workerActor];
DebuggerView.Workers.removeWorker(workerActor);
}
}
@ -500,7 +503,7 @@ Workers.prototype = {
if (!(workerActor in this._workerForms)) {
let workerForm = workerForms[workerActor];
this._workerForms[workerActor] = workerForm;
DebuggerView.Workers.addWorker(workerActor, workerForm.url);
DebuggerView.Workers.addWorker(workerForm);
}
}
});
@ -510,10 +513,11 @@ Workers.prototype = {
this._updateWorkerList();
},
_onWorkerSelect: function (workerActor) {
DebuggerController.client.attachWorker(workerActor, (response, workerClient) => {
gDevTools.showToolbox(TargetFactory.forWorker(workerClient),
"jsdebugger", Toolbox.HostType.WINDOW);
_onWorkerSelect: function (workerForm) {
DebuggerController.client.attachWorker(workerForm.actor, (response, workerClient) => {
let toolbox = gDevTools.showToolbox(TargetFactory.forWorker(workerClient),
"jsdebugger", Toolbox.HostType.WINDOW);
window.emit(EVENTS.WORKER_SELECTED, toolbox);
});
}
};

Просмотреть файл

@ -45,6 +45,9 @@ support-files =
code_ugly-7.js
code_ugly-8
code_ugly-8^headers^
code_worker-source-map.coffee
code_worker-source-map.js
code_worker-source-map.js.map
code_WorkerActor.attach-worker1.js
code_WorkerActor.attach-worker2.js
code_WorkerActor.attachThread-worker.js
@ -112,6 +115,7 @@ support-files =
doc_watch-expressions.html
doc_watch-expression-button.html
doc_with-frame.html
doc_worker-source-map.html
doc_WorkerActor.attach-tab1.html
doc_WorkerActor.attach-tab2.html
doc_WorkerActor.attachThread-tab.html
@ -592,6 +596,8 @@ skip-if = e10s && debug
skip-if = e10s && debug
[browser_dbg_worker-console.js]
skip-if = e10s && debug
[browser_dbg_worker-source-map.js]
skip-if = e10s && debug
[browser_dbg_worker-window.js]
skip-if = e10s && debug
[browser_dbg_WorkerActor.attach.js]

Просмотреть файл

@ -0,0 +1,85 @@
/* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
const TAB_URL = EXAMPLE_URL + "doc_worker-source-map.html";
const WORKER_URL = "code_worker-source-map.js";
const COFFEE_URL = EXAMPLE_URL + "code_worker-source-map.coffee";
function selectWorker(aPanel, aURL) {
let panelWin = aPanel.panelWin;
let promise = waitForDebuggerEvents(aPanel, panelWin.EVENTS.WORKER_SELECTED);
let Workers = panelWin.DebuggerView.Workers;
let item = Workers.getItemForAttachment((workerForm) => {
return workerForm.url === aURL;
});
Workers.selectedItem = item;
return promise;
}
function test() {
return Task.spawn(function* () {
yield pushPrefs(["devtools.debugger.workers", true]);
let [tab,, panel] = yield initDebugger(TAB_URL);
let toolbox = yield selectWorker(panel, WORKER_URL);
let workerPanel = toolbox.getCurrentPanel();
yield waitForSourceShown(workerPanel, ".coffee");
let panelWin = workerPanel.panelWin;
let Sources = panelWin.DebuggerView.Sources;
let editor = panelWin.DebuggerView.editor;
let threadClient = panelWin.gThreadClient;
isnot(Sources.selectedItem.attachment.source.url.indexOf(".coffee"), -1,
"The debugger should show the source mapped coffee source file.");
is(Sources.selectedValue.indexOf(".js"), -1,
"The debugger should not show the generated js source file.");
is(editor.getText().indexOf("isnt"), 211,
"The debugger's editor should have the coffee source source displayed.");
is(editor.getText().indexOf("function"), -1,
"The debugger's editor should not have the JS source displayed.");
yield threadClient.interrupt();
let sourceForm = getSourceForm(Sources, COFFEE_URL);
let source = threadClient.source(sourceForm);
let response = yield source.setBreakpoint({ line: 5 });
ok(!response.error,
"Should be able to set a breakpoint in a coffee source file.");
ok(!response.actualLocation,
"Should be able to set a breakpoint on line 5.");
let promise = new Promise((resolve) => {
threadClient.addOneTimeListener("paused", (event, packet) => {
is(packet.type, "paused",
"We should now be paused again.");
is(packet.why.type, "breakpoint",
"and the reason we should be paused is because we hit a breakpoint.");
// Check that we stopped at the right place, by making sure that the
// environment is in the state that we expect.
is(packet.frame.environment.bindings.variables.start.value, 0,
"'start' is 0.");
is(packet.frame.environment.bindings.variables.stop.value.type, "undefined",
"'stop' hasn't been assigned to yet.");
is(packet.frame.environment.bindings.variables.pivot.value.type, "undefined",
"'pivot' hasn't been assigned to yet.");
waitForCaretUpdated(workerPanel, 5).then(resolve);
});
});
// This will cause the breakpoint to be hit, and put us back in the
// paused state.
yield threadClient.resume();
callInTab(tab, "binary_search", [0, 2, 3, 5, 7, 10], 5);
yield promise;
yield threadClient.resume();
yield toolbox.destroy();
yield closeDebuggerAndFinish(panel);
yield popPrefs();
});
}

Просмотреть файл

@ -0,0 +1,22 @@
# Uses a binary search algorithm to locate a value in the specified array.
binary_search = (items, value) ->
start = 0
stop = items.length - 1
pivot = Math.floor (start + stop) / 2
while items[pivot] isnt value and start < stop
# Adjust the search area.
stop = pivot - 1 if value < items[pivot]
start = pivot + 1 if value > items[pivot]
# Recalculate the pivot.
pivot = Math.floor (stop + start) / 2
# Make sure we've found the correct value.
if items[pivot] is value then pivot else -1
self.onmessage = (event) ->
data = event.data
binary_search(data.items, data.value)

Просмотреть файл

@ -0,0 +1,35 @@
// Generated by CoffeeScript 1.10.0
(function() {
var binary_search;
binary_search = function(items, value) {
var pivot, start, stop;
start = 0;
stop = items.length - 1;
pivot = Math.floor((start + stop) / 2);
while (items[pivot] !== value && start < stop) {
if (value < items[pivot]) {
stop = pivot - 1;
}
if (value > items[pivot]) {
start = pivot + 1;
}
pivot = Math.floor((stop + start) / 2);
}
if (items[pivot] === value) {
return pivot;
} else {
return -1;
}
};
self.onmessage = function(event) {
console.log("EUTA");
var data;
data = event.data;
return binary_search(data.items, data.value);
};
}).call(this);
//# sourceMappingURL=code_worker-source-map.js.map

Просмотреть файл

@ -0,0 +1,10 @@
{
"version": 3,
"file": "code_worker-source-map.js",
"sourceRoot": "",
"sources": [
"code_worker-source-map.coffee"
],
"names": [],
"mappings": ";AACA;AAAA,MAAA;;EAAA,aAAA,GAAgB,SAAC,KAAD,EAAQ,KAAR;AAEd,QAAA;IAAA,KAAA,GAAQ;IACR,IAAA,GAAQ,KAAK,CAAC,MAAN,GAAe;IACvB,KAAA,GAAQ,IAAI,CAAC,KAAL,CAAW,CAAC,KAAA,GAAQ,IAAT,CAAA,GAAiB,CAA5B;AAER,WAAM,KAAM,CAAA,KAAA,CAAN,KAAkB,KAAlB,IAA4B,KAAA,GAAQ,IAA1C;MAGE,IAAqB,KAAA,GAAQ,KAAM,CAAA,KAAA,CAAnC;QAAA,IAAA,GAAQ,KAAA,GAAQ,EAAhB;;MACA,IAAqB,KAAA,GAAQ,KAAM,CAAA,KAAA,CAAnC;QAAA,KAAA,GAAQ,KAAA,GAAQ,EAAhB;;MAGA,KAAA,GAAQ,IAAI,CAAC,KAAL,CAAW,CAAC,IAAA,GAAO,KAAR,CAAA,GAAiB,CAA5B;IAPV;IAUA,IAAG,KAAM,CAAA,KAAA,CAAN,KAAgB,KAAnB;aAA8B,MAA9B;KAAA,MAAA;aAAyC,CAAC,EAA1C;;EAhBc;;EAkBhB,IAAI,CAAC,SAAL,GAAiB,SAAC,KAAD;AACf,QAAA;IAAA,IAAA,GAAO,KAAK,CAAC;WACb,aAAA,CAAc,IAAI,CAAC,KAAnB,EAA0B,IAAI,CAAC,KAA/B;EAFe;AAlBjB"
}

Просмотреть файл

@ -0,0 +1,18 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8"/>
<script>
var worker = new Worker("code_worker-source-map.js");
function binary_search(items, value) {
worker.postMessage({
items: items,
value: value
});
}
</script>
</head>
<body>
</body>
</html>

Просмотреть файл

@ -28,22 +28,24 @@ WorkersView.prototype = Heritage.extend(WidgetMethods, {
this.widget.addEventListener("select", this._onWorkerSelect, false);
},
addWorker: function (actor, name) {
addWorker: function (workerForm) {
let element = document.createElement("label");
element.className = "plain dbg-worker-item";
element.setAttribute("value", name);
element.setAttribute("value", workerForm.url);
element.setAttribute("flex", "1");
this.push([element, actor], {});
this.push([element, workerForm.actor], {
attachment: workerForm
});
},
removeWorker: function (actor) {
this.remove(this.getItemByValue(actor));
removeWorker: function (workerForm) {
this.remove(this.getItemByValue(workerForm.actor));
},
_onWorkerSelect: function () {
if (this.selectedItem !== null) {
DebuggerController.Workers._onWorkerSelect(this.selectedItem.value);
DebuggerController.Workers._onWorkerSelect(this.selectedItem.attachment);
this.selectedItem = null;
}
}

Просмотреть файл

@ -912,6 +912,10 @@ Toolbox.prototype = {
if (!this.target.hasActor("gcli")) {
return promise.resolve();
}
// Disable gcli in browser toolbox until there is usages of it
if (this.target.chrome) {
return promise.resolve();
}
const options = {
environment: CommandUtils.createEnvironment(this, '_target')

Просмотреть файл

@ -233,7 +233,15 @@ Telemetry.prototype = {
histogram: "DEVTOOLS_CUSTOM_OPENED_COUNT",
userHistogram: "DEVTOOLS_CUSTOM_OPENED_PER_USER_FLAG",
timerHistogram: "DEVTOOLS_CUSTOM_TIME_ACTIVE_SECONDS"
}
},
reloadAddonInstalled: {
histogram: "DEVTOOLS_RELOAD_ADDON_INSTALLED_COUNT",
userHistogram: "DEVTOOLS_RELOAD_ADDON_INSTALLED_PER_USER_FLAG",
},
reloadAddonReload: {
histogram: "DEVTOOLS_RELOAD_ADDON_RELOAD_COUNT",
userHistogram: "DEVTOOLS_RELOAD_ADDON_RELOAD_PER_USER_FLAG",
},
},
/**

Просмотреть файл

@ -1150,6 +1150,8 @@ public final class BrowserDatabaseHelper extends SQLiteOpenHelper {
upgradeDatabaseFrom25to26(db);
break;
// case 27 occurs in UrlMetadataTable.onUpgrade
case 28:
upgradeDatabaseFrom27to28(db);
break;

Просмотреть файл

@ -12,6 +12,7 @@ import java.util.Map;
import org.mozilla.gecko.AboutPages;
import org.mozilla.gecko.GeckoProfile;
import org.mozilla.gecko.R;
import org.mozilla.gecko.db.BrowserContract.Bookmarks;
import org.mozilla.gecko.db.BrowserContract.Combined;
import org.mozilla.gecko.db.BrowserContract.FaviconColumns;
@ -694,21 +695,25 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
final String TABLE_TOPSITES = "topsites";
final String totalLimit = uri.getQueryParameter(BrowserContract.PARAM_LIMIT);
final String suggestedGridLimit = uri.getQueryParameter(BrowserContract.PARAM_SUGGESTEDSITES_LIMIT);
final String limitParam = uri.getQueryParameter(BrowserContract.PARAM_LIMIT);
final String gridLimitParam = uri.getQueryParameter(BrowserContract.PARAM_SUGGESTEDSITES_LIMIT);
final String[] suggestedGridLimitArgs = new String[] {
suggestedGridLimit
};
final int totalLimit;
final int suggestedGridLimit;
final String[] totalLimitArgs = new String[] {
totalLimit
};
if (limitParam == null) {
totalLimit = 50;
} else {
totalLimit = Integer.parseInt(limitParam, 10);
}
final String pinnedSitesFromClause = "FROM " + TABLE_BOOKMARKS + " WHERE " + Bookmarks.PARENT + " == ?";
final String[] pinnedSitesArgs = new String[] {
String.valueOf(Bookmarks.FIXED_PINNED_LIST_ID)
};
if (gridLimitParam == null) {
suggestedGridLimit = getContext().getResources().getInteger(R.integer.number_of_top_sites);
} else {
suggestedGridLimit = Integer.parseInt(gridLimitParam, 10);
}
final String pinnedSitesFromClause = "FROM " + TABLE_BOOKMARKS + " WHERE " + Bookmarks.PARENT + " == " + Bookmarks.FIXED_PINNED_LIST_ID;
// Ideally we'd use a recursive CTE to generate our sequence, e.g. something like this worked at one point:
// " WITH RECURSIVE" +
@ -725,12 +730,7 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
" ON numbers.position > free_ids.position" +
" GROUP BY numbers.position" +
" ORDER BY numbers.position ASC" +
" LIMIT ?";
final String[] freeIDArgs = DBUtils.concatenateSelectionArgs(
pinnedSitesArgs,
pinnedSitesArgs,
suggestedGridLimitArgs);
" LIMIT " + suggestedGridLimit;
// Filter out: unvisited pages (history_id == -1) pinned (and other special) sites, deleted sites,
// and about: pages.
@ -739,17 +739,15 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
" AND " +
Combined.URL + " NOT IN (SELECT " +
Bookmarks.URL + " FROM bookmarks WHERE " +
DBUtils.qualifyColumn("bookmarks", Bookmarks.PARENT) + " < ? AND " +
DBUtils.qualifyColumn("bookmarks", Bookmarks.PARENT) + " < " + Bookmarks.FIXED_ROOT_ID + " AND " +
DBUtils.qualifyColumn("bookmarks", Bookmarks.IS_DELETED) + " == 0)" +
" AND " +
"(" + Combined.URL + " NOT LIKE ?)";
final String[] ignoreForTopSitesArgs = new String[] {
String.valueOf(Bookmarks.FIXED_ROOT_ID),
AboutPages.URL_FILTER
};
// Stuff the suggested sites into SQL: this allows us to filter pinned and topsites out of the suggested
// sites list as part of the final query (as opposed to walking cursors in java)
final SuggestedSites suggestedSites = GeckoProfile.get(getContext(), uri.getQueryParameter(BrowserContract.PARAM_PROFILE)).getDB().getSuggestedSites();
@ -759,7 +757,7 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
// sites list, which means we'd need to process the lists within SuggestedSites in any case. If we're doing
// that processing, there is little real between us using a MatrixCursor, or a Map (or List) instead of the
// MatrixCursor.
final Cursor suggestedSitesCursor = suggestedSites.get(Integer.parseInt(suggestedGridLimit));
final Cursor suggestedSitesCursor = suggestedSites.get(suggestedGridLimit);
String[] suggestedSiteArgs = new String[0];
@ -791,11 +789,8 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
// To restrict suggested sites to the grid, we simply subtract the number of topsites (which have already had
// the pinned sites filtered out), and the number of pinned sites.
// SQLite completely ignores negative limits, hence we need to manually totalLimit to 0 in this case.
final String suggestedLimitClause = " LIMIT MAX(0, (? - (SELECT COUNT(*) FROM " + TABLE_TOPSITES + ") - (SELECT COUNT(*) " + pinnedSitesFromClause + "))) ";
final String[] suggestedLimitArgs = DBUtils.concatenateSelectionArgs(suggestedGridLimitArgs,
pinnedSitesArgs);
// SQLite completely ignores negative limits, hence we need to manually limit to 0 in this case.
final String suggestedLimitClause = " LIMIT MAX(0, (" + suggestedGridLimit + " - (SELECT COUNT(*) FROM " + TABLE_TOPSITES + ") - (SELECT COUNT(*) " + pinnedSitesFromClause + "))) ";
db.beginTransaction();
try {
@ -813,10 +808,9 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
" FROM " + Combined.VIEW_NAME +
" WHERE " + ignoreForTopSitesWhereClause +
" ORDER BY " + BrowserContract.getFrecencySortOrder(true, false) +
" LIMIT ?",
" LIMIT " + totalLimit,
DBUtils.appendSelectionArgs(ignoreForTopSitesArgs,
totalLimitArgs));
ignoreForTopSitesArgs);
if (!hasProcessedAnySuggestedSites) {
db.execSQL("INSERT INTO " + TABLE_TOPSITES +
@ -838,11 +832,17 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
Bookmarks.URL + " NOT IN (SELECT url " + pinnedSitesFromClause + ")" +
suggestedLimitClause + " )",
DBUtils.concatenateSelectionArgs(suggestedSiteArgs,
pinnedSitesArgs,
suggestedLimitArgs));
suggestedSiteArgs);
}
// If we retrieve more topsites than we have free positions for in the freeIdSubquery,
// we will have topsites that don't receive a position when joining TABLE_TOPSITES
// with freeIdSubquery. Hence we need to coalesce the position with a generated position.
// We know that the difference in positions will be at most suggestedGridLimit, hence we
// can add that to the rowid to generate a safe position.
// I.e. if we have 6 pinned sites then positions 0..5 are filled, the JOIN results in
// the first N rows having positions 6..(N+6), so row N+1 should receive a position that is at
// least N+1+6, which is equal to rowid + 6.
final SQLiteCursor c = (SQLiteCursor) db.rawQuery(
"SELECT " +
Bookmarks._ID + ", " +
@ -850,7 +850,9 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
TopSites.HISTORY_ID + ", " +
Bookmarks.URL + ", " +
Bookmarks.TITLE + ", " +
Bookmarks.POSITION + ", " +
"COALESCE(" + Bookmarks.POSITION + ", " +
DBUtils.qualifyColumn(TABLE_TOPSITES, "rowid") + " + " + suggestedGridLimit +
")" + " AS " + Bookmarks.POSITION + ", " +
Combined.HISTORY_ID + ", " +
TopSites.TYPE +
" FROM " + TABLE_TOPSITES +
@ -871,12 +873,11 @@ public class BrowserProvider extends SharedBrowserDatabaseProvider {
"NULL AS " + Combined.HISTORY_ID + ", " +
TopSites.TYPE_PINNED + " as " + TopSites.TYPE +
" FROM " + TABLE_BOOKMARKS +
" WHERE " + Bookmarks.PARENT + " == ? " +
" WHERE " + Bookmarks.PARENT + " == " + Bookmarks.FIXED_PINNED_LIST_ID +
" ORDER BY " + Bookmarks.POSITION,
DBUtils.appendSelectionArgs(freeIDArgs,
pinnedSitesArgs));
null);
c.setNotificationUri(getContext().getContentResolver(),
BrowserContract.AUTHORITY_URI);

Просмотреть файл

@ -18,6 +18,7 @@ import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.GeckoAppShell;
import org.mozilla.gecko.Telemetry;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.util.ThreadUtils;
import android.content.ContentResolver;
@ -79,7 +80,6 @@ public class LocalURLMetadata implements URLMetadata {
if (obj.has("touchIconList") &&
(icons = obj.getJSONObject("touchIconList")).length() > 0) {
int preferredSize = GeckoAppShell.getPreferredIconSize();
int bestSizeFound = -1;
Iterator<String> keys = icons.keys();
@ -88,21 +88,8 @@ public class LocalURLMetadata implements URLMetadata {
sizes.add(new Integer(keys.next()));
}
Collections.sort(sizes);
for (int size : sizes) {
if (size >= preferredSize) {
bestSizeFound = size;
break;
}
}
// If all icons are smaller than the preferred size then we don't have an icon
// selected yet (bestSizeFound == -1), therefore just take the largest (last) icon.
if (bestSizeFound == -1) {
bestSizeFound = sizes.get(sizes.size() - 1);
}
String iconURL = icons.getString(Integer.toString(bestSizeFound));
final int bestSize = Favicons.selectBestSizeFromList(sizes, preferredSize);
final String iconURL = icons.getString(Integer.toString(bestSize));
data.put(URLMetadataTable.TOUCH_ICON_COLUMN, iconURL);
}

Просмотреть файл

@ -144,6 +144,32 @@ public class Favicons {
private static FaviconCache faviconsCache;
/**
* Select the closest icon size from a list of icon sizes.
* We just find the first icon that is larger than the preferred size if available, or otherwise select the
* largest icon (if all icons are smaller than the preferred size).
*
* @return The closes icon size, or -1 if no sizes are supplied.
*/
public static int selectBestSizeFromList(final List<Integer> sizes, final int preferredSize) {
Collections.sort(sizes);
for (int size : sizes) {
if (size >= preferredSize) {
return size;
}
}
// If all icons are smaller than the preferred size then we don't have an icon
// selected yet, therefore just take the largest (last) icon.
if (sizes.size() > 0) {
return sizes.get(sizes.size() - 1);
} else {
// This isn't ideal, however current code assumes this as an error value for now.
return -1;
}
}
/**
* Returns either NOT_LOADING, or LOADED if the onFaviconLoaded call could
* be made on the main thread.
@ -584,6 +610,6 @@ public class Favicons {
*/
public static void getPreferredSizeFaviconForPage(Context context, String url, String iconURL, OnFaviconLoadedListener onFaviconLoadedListener) {
int preferredSize = GeckoAppShell.getPreferredIconSize();
loadUncachedFavicon(context, url, iconURL, 0, preferredSize, onFaviconLoadedListener);
loadUncachedFavicon(context, url, iconURL, LoadFaviconTask.FLAG_BYPASS_CACHE_WHEN_DOWNLOADING_ICONS, preferredSize, onFaviconLoadedListener);
}
}

Просмотреть файл

@ -25,12 +25,14 @@ import org.mozilla.gecko.util.IOUtils;
import org.mozilla.gecko.util.ThreadUtils;
import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.RejectedExecutionException;
import java.util.concurrent.atomic.AtomicInteger;
@ -50,6 +52,14 @@ public class LoadFaviconTask {
private static final HashMap<String, LoadFaviconTask> loadsInFlight = new HashMap<>();
public static final int FLAG_PERSIST = 1;
/**
* Bypass all caches - this is used to directly retrieve the requested icon. Without this flag,
* favicons will first be pushed into the memory cache (and possibly permanent cache if using FLAG_PERSIST),
* where they will be downscaled to the maximum cache size, before being retrieved from the cache (resulting
* in a possibly smaller icon size).
*/
public static final int FLAG_BYPASS_CACHE_WHEN_DOWNLOADING_ICONS = 2;
private static final int MAX_REDIRECTS_TO_FOLLOW = 5;
// The default size of the buffer to use for downloading Favicons in the event no size is given
// by the server.
@ -417,10 +427,13 @@ public class LoadFaviconTask {
return null;
}
LoadFaviconResult loadedBitmaps = null;
// If there are no valid bitmaps decoded, the returned LoadFaviconResult is null.
LoadFaviconResult loadedBitmaps = loadFaviconFromDb(db);
if (loadedBitmaps != null) {
return pushToCacheAndGetResult(loadedBitmaps);
if ((flags & FLAG_BYPASS_CACHE_WHEN_DOWNLOADING_ICONS) == 0) {
loadedBitmaps = loadFaviconFromDb(db);
if (loadedBitmaps != null) {
return pushToCacheAndGetResult(loadedBitmaps);
}
}
if (onlyFromLocal || isCancelled()) {
@ -445,11 +458,25 @@ public class LoadFaviconTask {
}
if (loadedBitmaps != null) {
// Fetching bytes to store can fail. saveFaviconToDb will
// do the right thing, but we still choose to cache the
// downloaded icon in memory.
saveFaviconToDb(db, loadedBitmaps.getBytesForDatabaseStorage());
return pushToCacheAndGetResult(loadedBitmaps);
if ((flags & FLAG_BYPASS_CACHE_WHEN_DOWNLOADING_ICONS) == 0) {
// Fetching bytes to store can fail. saveFaviconToDb will
// do the right thing, but we still choose to cache the
// downloaded icon in memory.
saveFaviconToDb(db, loadedBitmaps.getBytesForDatabaseStorage());
return pushToCacheAndGetResult(loadedBitmaps);
} else {
final Map<Integer, Bitmap> iconMap = new HashMap<>();
final List<Integer> sizes = new ArrayList<>();
while (loadedBitmaps.getBitmaps().hasNext()) {
final Bitmap b = loadedBitmaps.getBitmaps().next();
iconMap.put(b.getWidth(), b);
sizes.add(b.getWidth());
}
int bestSize = Favicons.selectBestSizeFromList(sizes, targetWidth);
return iconMap.get(bestSize);
}
}
if (isUsingDefaultURL) {
@ -545,11 +572,15 @@ public class LoadFaviconTask {
private void processResult(Bitmap image) {
Favicons.removeLoadTask(id);
Bitmap scaled = image;
final Bitmap scaled;
// Notify listeners, scaling if required.
if (targetWidth != -1 && image != null && image.getWidth() != targetWidth) {
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);
} else {
scaled = image;
}
Favicons.dispatchResult(pageUrl, faviconURL, scaled, listener);

Просмотреть файл

@ -25,6 +25,7 @@ import android.text.TextUtils;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
@ -35,9 +36,9 @@ public class TwoLinePageRow extends LinearLayout
private final TextView mTitle;
private final TextView mUrl;
private final ImageView mStatusIcon;
private int mSwitchToTabIconId;
private int mPageTypeIconId;
private final FaviconView mFavicon;
@ -90,11 +91,14 @@ public class TwoLinePageRow extends LinearLayout
setGravity(Gravity.CENTER_VERTICAL);
LayoutInflater.from(context).inflate(R.layout.two_line_page_row, this);
// Merge layouts lose their padding, so set it dynamically.
setPadding(0, 0, (int) getResources().getDimension(R.dimen.page_row_padding_right), 0);
mTitle = (TextView) findViewById(R.id.title);
mUrl = (TextView) findViewById(R.id.url);
mStatusIcon = (ImageView) findViewById(R.id.status_icon_bookmark);
mSwitchToTabIconId = NO_ICON;
mPageTypeIconId = NO_ICON;
mShowIcons = true;
mFavicon = (FaviconView) findViewById(R.id.icon);
@ -173,16 +177,12 @@ public class TwoLinePageRow extends LinearLayout
}
mSwitchToTabIconId = iconId;
mUrl.setCompoundDrawablesWithIntrinsicBounds(mSwitchToTabIconId, 0, mPageTypeIconId, 0);
mUrl.setCompoundDrawablesWithIntrinsicBounds(mSwitchToTabIconId, 0, 0, 0);
}
private void setPageTypeIcon(int iconId) {
if (mPageTypeIconId == iconId) {
return;
}
mPageTypeIconId = iconId;
mUrl.setCompoundDrawablesWithIntrinsicBounds(mSwitchToTabIconId, 0, mPageTypeIconId, 0);
private void showBookmarkIcon(boolean toShow) {
final int visibility = toShow ? VISIBLE : GONE;
mStatusIcon.setVisibility(visibility);
}
/**
@ -231,13 +231,10 @@ public class TwoLinePageRow extends LinearLayout
if (mShowIcons) {
// The bookmark id will be 0 (null in database) when the url
// is not a bookmark.
if (bookmarkId == 0) {
setPageTypeIcon(NO_ICON);
} else {
setPageTypeIcon(R.drawable.ic_url_bar_star);
}
final boolean isBookmark = bookmarkId != 0;
showBookmarkIcon(isBookmark);
} else {
setPageTypeIcon(NO_ICON);
showBookmarkIcon(false);
}
// Use the URL instead of an empty title for consistency with the normal URL

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 277 B

Двоичные данные
mobile/android/base/resources/drawable-nodpi/bookmarked_star.png Normal file

Двоичный файл не отображается.

После

Ширина:  |  Высота:  |  Размер: 600 B

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 368 B

Просмотреть файл

@ -14,29 +14,38 @@
android:layout_margin="16dp"
tools:background="@drawable/favicon_globe"/>
<LinearLayout android:layout_width="match_parent"
<LinearLayout android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center_vertical"
android:orientation="vertical"
android:paddingRight="25dp">
android:paddingRight="10dp"
android:orientation="vertical">
<org.mozilla.gecko.widget.FadedSingleColorTextView
android:id="@+id/title"
style="@style/Widget.TwoLinePageRow.Title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
gecko:fadeWidth="30dp"
gecko:fadeWidth="90dp"
tools:text="This is a long test title"/>
<TextView android:id="@+id/url"
<org.mozilla.gecko.widget.FadedSingleColorTextView android:id="@+id/url"
style="@style/Widget.TwoLinePageRow.Url"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="5dp"
android:maxLength="1024"
gecko:fadeWidth="90dp"
tools:text="http://test.com/test"
tools:drawableLeft="@drawable/ic_url_bar_tab"
tools:drawableRight="@drawable/ic_url_bar_star"/>
tools:drawableLeft="@drawable/ic_url_bar_tab"/>
</LinearLayout>
<ImageView android:id="@+id/status_icon_bookmark"
android:layout_width="20dp"
android:layout_height="20dp"
android:layout_gravity="center"
android:visibility="gone"
android:src="@drawable/bookmarked_star"/>
</merge>

Просмотреть файл

@ -79,6 +79,8 @@
<dimen name="site_security_unknown_inset_top">1dp</dimen>
<dimen name="site_security_unknown_inset_bottom">-1dp</dimen>
<dimen name="page_row_padding_right">15dp</dimen>
<!-- Regular page row on about:home -->
<dimen name="page_row_height">64dp</dimen>

Просмотреть файл

@ -126,7 +126,6 @@
<item name="android:textAppearance">@style/TextAppearance.Widget.Home.ItemDescription</item>
<item name="android:includeFontPadding">false</item>
<item name="android:singleLine">true</item>
<item name="android:ellipsize">middle</item>
</style>
<style name="Widget.ReadingListRow" />
@ -413,7 +412,9 @@
<item name="android:textColor">?android:attr/textColorPrimary</item>
</style>
<style name="TextAppearance.Widget.Home.ItemTitle" parent="TextAppearance.Medium"/>
<style name="TextAppearance.Widget.Home.ItemTitle" parent="TextAppearance">
<item name="android:textSize">16dp</item>
</style>
<style name="TextAppearance.Widget.Home.ItemDescription" parent="TextAppearance.Micro">
<item name="android:textColor">@color/tabs_tray_icon_grey</item>

Просмотреть файл

@ -63,8 +63,9 @@ var ActionBarHandler = {
// Else, update an open ActionBar.
if (this._selectionID) {
if ([this._targetElement, this._contentWindow] ===
[Services.focus.focusedElement, Services.focus.focusedWindow]) {
let [element, win] = this._getSelectionTargets();
if (this._targetElement === element &&
this._contentWindow === win) {
// We have the same focused window/element as before. Trigger "TextSelection:ActionbarStatus"
// message only if available actions differ from when last we checked.
this._sendActionBarActions();
@ -225,8 +226,14 @@ var ActionBarHandler = {
*/
_sendActionBarActions: function(sendAlways) {
let actions = this._getActionBarActions();
let actionCountUnchanged = this._actionBarActions &&
actions.length === this._actionBarActions.length;
let actionsMatch = actionCountUnchanged &&
this._actionBarActions.every((e,i) => {
return e.id === actions[i].id;
});
if (sendAlways || actions !== this._actionBarActions) {
if (sendAlways || !actionsMatch) {
Messaging.sendRequest({
type: "TextSelection:ActionbarStatus",
actions: actions,

Просмотреть файл

@ -208,6 +208,9 @@ let SyncedTabsInternal = {
Preferences.reset("services.sync.lastTabFetch");
Services.obs.notifyObservers(null, TOPIC_TABS_CHANGED, null);
break;
case "nsPref:changed":
Services.obs.notifyObservers(null, TOPIC_TABS_CHANGED, null);
break;
default:
break;
}
@ -232,6 +235,10 @@ let SyncedTabsInternal = {
Services.obs.addObserver(SyncedTabsInternal, "weave:engine:sync:finish", false);
Services.obs.addObserver(SyncedTabsInternal, "weave:service:start-over", false);
// Observe the pref the indicates the state of the tabs engine has changed.
// This will force consumers to re-evaluate the state of sync and update
// accordingly.
Services.prefs.addObserver("services.sync.engine.tabs", SyncedTabsInternal, false);
// The public interface.
this.SyncedTabs = {

Просмотреть файл

@ -591,6 +591,27 @@ this.BrowserTestUtils = {
});
},
/**
* Wait for a message to be fired from a particular message manager
*
* @param {nsIMessageManager} messageManager
* The message manager that should be used.
* @param {String} message
* The message we're waiting for.
* @param {Function} checkFn (optional)
* Optional function to invoke to check the message.
*/
waitForMessage(messageManager, message, checkFn) {
return new Promise(resolve => {
messageManager.addMessageListener(message, function onMessage(msg) {
if (!checkFn || checkFn(msg)) {
messageManager.removeMessageListener(message, onMessage);
resolve();
}
});
});
},
/**
* Version of synthesizeMouse that uses the center of the target as the mouse
* location. Arguments and the return value are the same.

Просмотреть файл

@ -7021,6 +7021,20 @@
"description": "Number of times a custom developer tool has been opened.",
"releaseChannelCollection": "opt-out"
},
"DEVTOOLS_RELOAD_ADDON_INSTALLED_COUNT": {
"alert_emails": ["dev-developer-tools@lists.mozilla.org"],
"expires_in_version": "55",
"kind": "count",
"description": "Number of times the reload addon has been installed.",
"bug_numbers": [1248435]
},
"DEVTOOLS_RELOAD_ADDON_RELOAD_COUNT": {
"alert_emails": ["dev-developer-tools@lists.mozilla.org"],
"expires_in_version": "55",
"kind": "count",
"description": "Number of times the tools have been reloaded by the reload addon.",
"bug_numbers": [1248435]
},
"DEVTOOLS_TOOLBOX_OPENED_PER_USER_FLAG": {
"expires_in_version": "never",
"kind": "flag",
@ -7204,6 +7218,20 @@
"kind": "flag",
"description": "Number of users that have opened a custom developer tool via the toolbox button."
},
"DEVTOOLS_RELOAD_ADDON_INSTALLED_PER_USER_FLAG": {
"alert_emails": ["dev-developer-tools@lists.mozilla.org"],
"expires_in_version": "55",
"kind": "flag",
"description": "Records once per browser version if the reload add-on is installed.",
"bug_numbers": [1248435]
},
"DEVTOOLS_RELOAD_ADDON_RELOAD_PER_USER_FLAG": {
"alert_emails": ["dev-developer-tools@lists.mozilla.org"],
"expires_in_version": "55",
"kind": "flag",
"description": "Records once per browser version if the tools have been reloaded via the reload add-on.",
"bug_numbers": [1248435]
},
"DEVTOOLS_TOOLBOX_TIME_ACTIVE_SECONDS": {
"expires_in_version": "never",
"kind": "exponential",
@ -8452,6 +8480,15 @@
"releaseChannelCollection": "opt-out",
"description": "Number of sessions where at least one chat message was exchanged"
},
"LOOP_INFOBAR_ACTION_BUTTONS": {
"alert_emails": ["firefox-dev@mozilla.org", "mbanner@mozilla.com"],
"expires_in_version": "51",
"kind": "enumerated",
"n_values": 4,
"releaseChannelCollection": "opt-out",
"bug_numbers": [1245486],
"description": "Number times info bar buttons are clicked (0=PAUSED, 1=CREATED)"
},
"E10S_STATUS": {
"alert_emails": ["firefox-dev@mozilla.org"],
"expires_in_version": "never",

Просмотреть файл

@ -0,0 +1,38 @@
/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
function doUpdate(update) {
const { classes: Cc, interfaces: Ci, results: Cr } = Components;
let listener = {
QueryInterface: function(iid)
{
if (iid.equals(Ci.nsISupports) ||
iid.equals(Ci.nsIUrlClassifierUpdateObserver))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
},
updateUrlRequested: function(url) { },
streamFinished: function(status) { },
updateError: function(errorCode) {
sendAsyncMessage("updateError", { errorCode });
},
updateSuccess: function(requestedTimeout) {
sendAsyncMessage("loadTestFrame");
}
};
let dbService = Cc["@mozilla.org/url-classifier/dbservice;1"]
.getService(Ci.nsIUrlClassifierDBService);
dbService.beginUpdate(listener, "test-malware-simple,test-unwanted-simple", "");
dbService.beginStream("", "");
dbService.updateStream(update);
dbService.finishStream();
dbService.finishUpdate();
}
addMessageListener("doUpdate", ({ testUpdate }) => {
doUpdate(testUpdate);
});

Просмотреть файл

@ -1,7 +1,8 @@
[DEFAULT]
skip-if = buildapp == 'b2g' || e10s
skip-if = buildapp == 'b2g'
support-files =
classifiedAnnotatedPBFrame.html
classifierCommon.js
classifierFrame.html
cleanWorker.js
good.js

Просмотреть файл

@ -13,8 +13,6 @@
<script class="testbody" type="text/javascript">
var Cc = SpecialPowers.Cc;
var Ci = SpecialPowers.Ci;
var firstLoad = true;
// Add some URLs to the malware database.
@ -30,51 +28,33 @@ testUpdate +=
"a:524:32:" + testData.length + "\n" +
testData;
var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"]
.getService(Ci.nsIUrlClassifierDBService);
function loadTestFrame() {
document.getElementById("testFrame").src = "classifierFrame.html";
}
function doUpdate(update) {
var listener = {
QueryInterface: SpecialPowers.wrapCallback(function(iid)
{
if (iid.equals(Ci.nsISupports) ||
iid.equals(Ci.nsIUrlClassifierUpdateObserver))
return this;
const CLASSIFIER_COMMON_URL = SimpleTest.getTestFileURL("classifierCommon.js");
let classifierCommonScript = SpecialPowers.loadChromeScript(CLASSIFIER_COMMON_URL);
throw Cr.NS_ERROR_NO_INTERFACE;
}),
updateUrlRequested: function(url) { },
streamFinished: function(status) { },
updateError: function(errorCode) {
ok(false, "Couldn't update classifier.");
// Abort test.
SimpleTest.finish();
},
updateSuccess: function(requestedTimeout) {
SpecialPowers.pushPrefEnv(
{"set" : [["browser.safebrowsing.malware.enabled", true]]},
loadTestFrame);
}
};
// Expected finish() call is in "classifierFrame.html".
SimpleTest.waitForExplicitFinish();
dbService.beginUpdate(listener, "test-malware-simple,test-unwanted-simple", "");
dbService.beginStream("", "");
dbService.updateStream(update);
dbService.finishStream();
dbService.finishUpdate();
}
classifierCommonScript.addMessageListener("loadTestFrame", () => {
SpecialPowers.pushPrefEnv(
{"set" : [["browser.safebrowsing.malware.enabled", true]]},
loadTestFrame);
});
classifierCommonScript.addMessageListener("updateError", ({ errorCode }) => {
ok(false, "Couldn't update classifier. Error code: " + errorCode);
// Abort test.
SimpleTest.finish();
});
SpecialPowers.pushPrefEnv(
{"set" : [["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple"],
["urlclassifier.phishTable", "test-phish-simple"]]},
function() { doUpdate(testUpdate); });
// Expected finish() call is in "classifierFrame.html".
SimpleTest.waitForExplicitFinish();
function() {
classifierCommonScript.sendAsyncMessage("doUpdate", { testUpdate });
});
</script>

Просмотреть файл

@ -13,9 +13,6 @@
<script class="testbody" type="text/javascript">
var Cc = SpecialPowers.Cc;
var Ci = SpecialPowers.Ci;
// Add some URLs to the malware database.
var testData = "example.com/tests/toolkit/components/url-classifier/tests/mochitest/evilWorker.js";
var testUpdate =
@ -29,42 +26,9 @@ testUpdate +=
"a:550:32:" + testData.length + "\n" +
testData;
var dbService = Cc["@mozilla.org/url-classifier/dbservice;1"]
.getService(Ci.nsIUrlClassifierDBService);
function doUpdate(update) {
var listener = {
QueryInterface: SpecialPowers.wrapCallback(function(iid)
{
if (iid.equals(Ci.nsISupports) ||
iid.equals(Ci.nsIUrlClassifierUpdateObserver))
return this;
throw Cr.NS_ERROR_NO_INTERFACE;
}),
updateUrlRequested: function(url) { },
streamFinished: function(status) { },
updateError: function(errorCode) {
ok(false, "Couldn't update classifier.");
// Abort test.
SimpleTest.finish();
},
updateSuccess: function(requestedTimeout) {
SpecialPowers.pushPrefEnv(
{"set" : [["browser.safebrowsing.malware.enabled", true]]},
function loadTestFrame() {
document.getElementById("testFrame").src =
"http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/workerFrame.html";
}
);
}
};
dbService.beginUpdate(listener, "test-malware-simple,test-unwanted-simple", "");
dbService.beginStream("", "");
dbService.updateStream(update);
dbService.finishStream();
dbService.finishUpdate();
function loadTestFrame() {
document.getElementById("testFrame").src =
"http://example.com/tests/toolkit/components/url-classifier/tests/mochitest/workerFrame.html";
}
function onmessage(event)
@ -78,10 +42,26 @@ function onmessage(event)
is(pieces[0], "success", pieces[1]);
}
const CLASSIFIER_COMMON_URL = SimpleTest.getTestFileURL("classifierCommon.js");
let classifierCommonScript = SpecialPowers.loadChromeScript(CLASSIFIER_COMMON_URL);
classifierCommonScript.addMessageListener("loadTestFrame", () => {
SpecialPowers.pushPrefEnv(
{"set" : [["browser.safebrowsing.malware.enabled", true]]},
loadTestFrame);
});
classifierCommonScript.addMessageListener("updateError", ({ errorCode }) => {
ok(false, "Couldn't update classifier. Error code: " + errorCode);
// Abort test.
SimpleTest.finish();
});
SpecialPowers.pushPrefEnv(
{"set" : [["urlclassifier.malwareTable", "test-malware-simple,test-unwanted-simple"],
["urlclassifier.phishTable", "test-phish-simple"]]},
function() { doUpdate(testUpdate); });
function() {
classifierCommonScript.sendAsyncMessage("doUpdate", { testUpdate });
});
window.addEventListener("message", onmessage, false);

Просмотреть файл

@ -3,10 +3,10 @@ skip-if = buildapp == 'b2g'
support-files =
handlerApp.xhtml
handlerApps.js
unsafeBidi_chromeScript.js
unsafeBidiFileName.sjs
[test_badMimeType.html]
[test_handlerApps.xhtml]
skip-if = (toolkit == 'android' || os == 'mac') || e10s # OS X: bug 786938
[test_unsafeBidiChars.xhtml]
skip-if = e10s

Просмотреть файл

@ -2,21 +2,22 @@
<head>
<title>Test for Handling of unsafe bidi chars</title>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SpawnTask.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="load();">
<body>
<p id="display"></p>
<iframe id="test"></iframe>
<script type="text/javascript">
<![CDATA[
var unsafeBidiChars = {
LRE: "\xe2\x80\xaa",
RLE: "\xe2\x80\xab",
PDF: "\xe2\x80\xac",
LRO: "\xe2\x80\xad",
RLO: "\xe2\x80\xae"
};
var unsafeBidiChars = [
"\xe2\x80\xaa", // LRE
"\xe2\x80\xab", // RLE
"\xe2\x80\xac", // PDF
"\xe2\x80\xad", // LRO
"\xe2\x80\xae" // RLO
];
var tests = [
"{1}.test",
@ -37,100 +38,40 @@ function sanitize(name) {
return replace(name, '_');
}
var gTests = [];
function make_test(param, expected) {
gTests.push({
param: param,
expected: expected,
add_task(function* () {
let url = SimpleTest.getTestFileURL("unsafeBidi_chromeScript.js");
let chromeScript = SpecialPowers.loadChromeScript(url);
for (let test of tests) {
for (let char of unsafeBidiChars) {
let promiseName = new Promise(function(resolve) {
chromeScript.addMessageListener("suggestedFileName",
function listener(data) {
chromeScript.removeMessageListener("suggestedFileName", listener);
resolve(data);
});
});
let name = replace(test, char);
let expected = sanitize(test);
document.getElementById("test").src =
"unsafeBidiFileName.sjs?name=" + encodeURIComponent(name);
is((yield promiseName), expected, "got the expected sanitized name");
}
}
let promise = new Promise(function(resolve) {
chromeScript.addMessageListener("unregistered", function listener() {
chromeScript.removeMessageListener("unregistered", listener);
resolve();
});
});
}
chromeScript.sendAsyncMessage("unregister");
yield promise;
SimpleTest.waitForExplicitFinish();
function load() {
var iframe = document.getElementById("test");
var gCallback = null;
function run_test(test, cb) {
var url = "unsafeBidiFileName.sjs?name=" + encodeURIComponent(test.param);
gCallback = cb;
iframe.src = url;
}
var gCounter = -1;
function run_next_test() {
if (++gCounter == gTests.length)
finish_test();
else
run_test(gTests[gCounter], run_next_test);
}
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
const HELPERAPP_DIALOG_CONTRACT = "@mozilla.org/helperapplauncherdialog;1";
const HELPERAPP_DIALOG_CID = SpecialPowers.wrap(SpecialPowers.Components).ID(SpecialPowers.Cc[HELPERAPP_DIALOG_CONTRACT].number);
const FAKE_CID = SpecialPowers.Cc["@mozilla.org/uuid-generator;1"].
getService(SpecialPowers.Ci.nsIUUIDGenerator).generateUUID();
function HelperAppLauncherDialog() {}
HelperAppLauncherDialog.prototype = {
REASON_CANTHANDLE: 0,
REASON_SERVERREQUEST: 1,
REASON_TYPESNIFFED: 2,
show: function(aLauncher, aWindowContext, aReason) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
var test = gTests[gCounter];
is(aLauncher.suggestedFileName, test.expected,
"The filename should be correctly sanitized");
gCallback();
},
QueryInterface: function(aIID) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
if (aIID.equals(SpecialPowers.Ci.nsISupports) ||
aIID.equals(SpecialPowers.Ci.nsIHelperAppLauncherDialog))
return this;
throw SpecialPowers.Cr.NS_ERROR_NO_INTERFACE;
}
};
var factory = {
createInstance: function(aOuter, aIID) {
netscape.security.PrivilegeManager.enablePrivilege('UniversalXPConnect');
if (aOuter != null)
throw SpecialPowers.Cr.NS_ERROR_NO_AGGREGATION;
return new HelperAppLauncherDialog().QueryInterface(aIID);
}
};
SpecialPowers.wrap(SpecialPowers.Components).manager
.QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar)
.registerFactory(FAKE_CID, "",
HELPERAPP_DIALOG_CONTRACT,
factory);
function finish_test() {
SpecialPowers.wrap(SpecialPowers.Components).manager
.QueryInterface(SpecialPowers.Ci.nsIComponentRegistrar)
.registerFactory(HELPERAPP_DIALOG_CID, "",
HELPERAPP_DIALOG_CONTRACT,
null);
SimpleTest.finish();
}
var i,j;
for (i = 0; i < tests.length; ++i) {
for (j in unsafeBidiChars) {
make_test(replace(tests[i], unsafeBidiChars[j]),
sanitize(tests[i]));
}
}
run_next_test();
}
chromeScript.destroy();
});
]]>
</script>
</body>
</html>

Просмотреть файл

@ -0,0 +1,28 @@
const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
const HELPERAPP_DIALOG_CONTRACT = "@mozilla.org/helperapplauncherdialog;1";
const HELPERAPP_DIALOG_CID =
Components.ID(Cc[HELPERAPP_DIALOG_CONTRACT].number);
const FAKE_CID = Cc["@mozilla.org/uuid-generator;1"].
getService(Ci.nsIUUIDGenerator).generateUUID();
function HelperAppLauncherDialog() {}
HelperAppLauncherDialog.prototype = {
show: function(aLauncher, aWindowContext, aReason) {
sendAsyncMessage("suggestedFileName", aLauncher.suggestedFileName);
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIHelperAppLauncherDialog])
};
var registrar = Components.manager.QueryInterface(Ci.nsIComponentRegistrar);
registrar.registerFactory(FAKE_CID, "", HELPERAPP_DIALOG_CONTRACT,
XPCOMUtils._getFactory(HelperAppLauncherDialog));
addMessageListener("unregister", function() {
registrar.registerFactory(HELPERAPP_DIALOG_CID, "",
HELPERAPP_DIALOG_CONTRACT, null);
sendAsyncMessage("unregistered");
});