This commit is contained in:
Phil Ringnalda 2015-06-13 18:32:23 -07:00
Родитель 8e54f9dc78 978033514e
Коммит 2f4d09b07a
108 изменённых файлов: 1340 добавлений и 480 удалений

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

@ -347,6 +347,7 @@ pref("browser.urlbar.match.url", "@");
pref("browser.urlbar.suggest.history", true); pref("browser.urlbar.suggest.history", true);
pref("browser.urlbar.suggest.bookmark", true); pref("browser.urlbar.suggest.bookmark", true);
pref("browser.urlbar.suggest.openpage", true); pref("browser.urlbar.suggest.openpage", true);
pref("browser.urlbar.suggest.searches", true);
// Restrictions to current suggestions can also be applied (intersection). // Restrictions to current suggestions can also be applied (intersection).
// Typed suggestion works only if history is set to true. // Typed suggestion works only if history is set to true.
@ -357,8 +358,8 @@ pref("browser.urlbar.trimURLs", true);
pref("browser.altClickSave", false); pref("browser.altClickSave", false);
// Enable logging downloads operations to the Error Console. // Enable logging downloads operations to the Console.
pref("browser.download.debug", false); pref("browser.download.loglevel", "Error");
// Number of milliseconds to wait for the http headers (and thus // Number of milliseconds to wait for the http headers (and thus
// the Content-Disposition filename) before giving up and falling back to // the Content-Disposition filename) before giving up and falling back to

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

@ -32,7 +32,7 @@
command="Browser:Stop"/> command="Browser:Stop"/>
<menuitem id="context-bookmarkpage" <menuitem id="context-bookmarkpage"
class="menuitem-iconic" class="menuitem-iconic"
tooltiptext="&bookmarkPageCmd2.label;" observes="bookmarkThisPageBroadcaster"
aria-label="&bookmarkPageCmd2.label;" aria-label="&bookmarkPageCmd2.label;"
oncommand="gContextMenu.bookmarkThisPage();"/> oncommand="gContextMenu.bookmarkThisPage();"/>
</menugroup> </menugroup>

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

@ -1344,11 +1344,13 @@ let BookmarkingUI = {
if (aState == "invalid") { if (aState == "invalid") {
this.star.setAttribute("disabled", "true"); this.star.setAttribute("disabled", "true");
this.button.removeAttribute("starred"); this.broadcaster.setAttribute("stardisabled", "true");
this.button.setAttribute("buttontooltiptext", ""); this.broadcaster.removeAttribute("starred");
this.broadcaster.setAttribute("buttontooltiptext", "");
} }
else { else {
this.star.removeAttribute("disabled"); this.star.removeAttribute("disabled");
this.broadcaster.removeAttribute("stardisabled");
this._updateStar(); this._updateStar();
} }
this._updateToolbarStyle(); this._updateToolbarStyle();
@ -1523,23 +1525,23 @@ let BookmarkingUI = {
_updateStar: function BUI__updateStar() { _updateStar: function BUI__updateStar() {
if (!this._shouldUpdateStarState()) { if (!this._shouldUpdateStarState()) {
if (this.button.hasAttribute("starred")) { if (this.broadcaster.hasAttribute("starred")) {
this.button.removeAttribute("starred"); this.broadcaster.removeAttribute("starred");
this.button.removeAttribute("buttontooltiptext"); this.broadcaster.removeAttribute("buttontooltiptext");
} }
return; return;
} }
if (this._itemIds.length > 0) { if (this._itemIds.length > 0) {
this.button.setAttribute("starred", "true"); this.broadcaster.setAttribute("starred", "true");
this.button.setAttribute("buttontooltiptext", this._starredTooltip); this.broadcaster.setAttribute("buttontooltiptext", this._starredTooltip);
if (this.button.getAttribute("overflowedItem") == "true") { if (this.button.getAttribute("overflowedItem") == "true") {
this.button.setAttribute("label", this._starButtonOverflowedStarredLabel); this.button.setAttribute("label", this._starButtonOverflowedStarredLabel);
} }
} }
else { else {
this.button.removeAttribute("starred"); this.broadcaster.removeAttribute("starred");
this.button.setAttribute("buttontooltiptext", this._unstarredTooltip); this.broadcaster.setAttribute("buttontooltiptext", this._unstarredTooltip);
if (this.button.getAttribute("overflowedItem") == "true") { if (this.button.getAttribute("overflowedItem") == "true") {
this.button.setAttribute("label", this._starButtonOverflowedLabel); this.button.setAttribute("label", this._starButtonOverflowedLabel);
} }
@ -1553,7 +1555,9 @@ let BookmarkingUI = {
_updateBookmarkPageMenuItem: function BUI__updateBookmarkPageMenuItem(forceReset) { _updateBookmarkPageMenuItem: function BUI__updateBookmarkPageMenuItem(forceReset) {
let isStarred = !forceReset && this._itemIds.length > 0; let isStarred = !forceReset && this._itemIds.length > 0;
let label = isStarred ? "editlabel" : "bookmarklabel"; let label = isStarred ? "editlabel" : "bookmarklabel";
this.broadcaster.setAttribute("label", this.broadcaster.getAttribute(label)); if (this.broadcaster) {
this.broadcaster.setAttribute("label", this.broadcaster.getAttribute(label));
}
}, },
onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) { onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) {
@ -1671,6 +1675,10 @@ let BookmarkingUI = {
} }
}, },
onCurrentPageContextPopupShowing() {
this._updateBookmarkPageMenuItem();
},
handleEvent: function BUI_handleEvent(aEvent) { handleEvent: function BUI_handleEvent(aEvent) {
switch (aEvent.type) { switch (aEvent.type) {
case "ViewShowing": case "ViewShowing":

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

@ -1328,3 +1328,28 @@ toolbarpaletteitem[place="palette"][hidden] {
#login-fill-doorhanger:not([inDetailView]) > #login-fill-clickcapturer { #login-fill-doorhanger:not([inDetailView]) > #login-fill-clickcapturer {
pointer-events: none; pointer-events: none;
} }
.popup-notification-invalid-input {
box-shadow: 0 0 1.5px 1px red;
}
.popup-notification-invalid-input[focused] {
box-shadow: 0 0 2px 2px rgba(255,0,0,0.4);
}
#password-notification-password::after {
color: hsl(0,0%,60%);
content: attr(show-content);
pointer-events: none;
position: absolute;
right: 0;
transition: color 250ms;
}
#password-notification-password:hover::after {
color: hsl(210,100%,50%);
}
#password-notification-password[focused]::after {
content: none;
}

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

@ -7189,15 +7189,18 @@ let gPrivateBrowsingUI = {
} }
} }
if (gURLBar && if (gURLBar) {
!PrivateBrowsingUtils.permanentPrivateBrowsing) {
// Disable switch to tab autocompletion for private windows.
// We leave it enabled for permanent private browsing mode though.
let value = gURLBar.getAttribute("autocompletesearchparam") || ""; let value = gURLBar.getAttribute("autocompletesearchparam") || "";
if (!value.includes("disable-private-actions")) { if (!PrivateBrowsingUtils.permanentPrivateBrowsing &&
gURLBar.setAttribute("autocompletesearchparam", !value.includes("disable-private-actions")) {
value + " disable-private-actions"); // Disable switch to tab autocompletion for private windows.
// We leave it enabled for permanent private browsing mode though.
value += " disable-private-actions";
} }
if (!value.includes("private-window")) {
value += " private-window";
}
gURLBar.setAttribute("autocompletesearchparam", value);
} }
} }
}; };

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

@ -821,6 +821,8 @@
ondrop="PlacesMenuDNDHandler.onDrop(event);" ondrop="PlacesMenuDNDHandler.onDrop(event);"
cui-areatype="toolbar" cui-areatype="toolbar"
oncommand="BookmarkingUI.onCommand(event);"> oncommand="BookmarkingUI.onCommand(event);">
<observes element="bookmarkThisPageBroadcaster" attribute="starred"/>
<observes element="bookmarkThisPageBroadcaster" attribute="buttontooltiptext"/>
<menupopup id="BMB_bookmarksPopup" <menupopup id="BMB_bookmarksPopup"
class="cui-widget-panel cui-widget-panelview cui-widget-panelWithFooter PanelUI-subView" class="cui-widget-panel cui-widget-panelview cui-widget-panelWithFooter PanelUI-subView"
placespopup="true" placespopup="true"

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

@ -501,7 +501,8 @@ input[type=button] {
.newtab-search-panel-engine, .newtab-search-panel-engine,
#newtab-search-manage { #newtab-search-manage {
line-height: 25px; line-height: 25px;
padding: 15px 15px 15px 40px; padding: 15px;
-moz-padding-start: 40px;
font-size: 14px; font-size: 14px;
cursor: pointer; cursor: pointer;
} }
@ -532,11 +533,20 @@ input[type=button] {
.newtab-customize-complex-option { .newtab-customize-complex-option {
width: 100%; width: 100%;
display: block; display: block;
text-align: left; text-align: start;
max-width: 300px; max-width: 300px;
background-color: #F9F9F9; background-color: #F9F9F9;
} }
.newtab-customize-panel-item[selected]:-moz-locale-dir(rtl) {
background-position: right 15px center;
}
.newtab-customize-complex-option:hover > .selectable:not([selected]):-moz-locale-dir(rtl),
.selectable:not([selected]):hover:-moz-locale-dir(rtl) {
background-position: right 15px center;
}
.newtab-intro-image-customize .newtab-customize-panel-item, .newtab-intro-image-customize .newtab-customize-panel-item,
.newtab-intro-image-customize .newtab-customize-complex-option { .newtab-intro-image-customize .newtab-customize-complex-option {
max-width: 100%; max-width: 100%;
@ -589,12 +599,13 @@ input[type=button] {
.newtab-customize-panel-subitem { .newtab-customize-panel-subitem {
font-size: 12px; font-size: 12px;
padding: 0px 15px 15px 40px; padding: 0px 15px 15px 15px;
-moz-padding-start: 40px;
display: block; display: block;
} }
.newtab-customize-panel-subitem > label { .newtab-customize-panel-subitem > label {
padding-left: 10px; padding: 0px 10px;
line-height: 20px; line-height: 20px;
vertical-align: middle; vertical-align: middle;
max-width: 225px; max-width: 225px;
@ -603,7 +614,8 @@ input[type=button] {
.newtab-customize-panel-superitem { .newtab-customize-panel-superitem {
line-height: 20px; line-height: 20px;
border-bottom: medium none !important; border-bottom: medium none !important;
padding: 15px 15px 10px 40px; padding: 15px 15px 10px 15px;
-moz-padding-start: 40px;
border-top: 1px solid threedshadow; border-top: 1px solid threedshadow;
} }

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

@ -51,6 +51,10 @@ nsContextMenu.prototype = {
this.isContentSelected = !this.selectionInfo.docSelectionIsCollapsed; this.isContentSelected = !this.selectionInfo.docSelectionIsCollapsed;
this.onPlainTextLink = false; this.onPlainTextLink = false;
let bookmarkPage = document.getElementById("context-bookmarkpage");
if (bookmarkPage)
BookmarkingUI.onCurrentPageContextPopupShowing();
// Initialize (disable/remove) menu items. // Initialize (disable/remove) menu items.
this.initItems(); this.initItems();
@ -269,10 +273,14 @@ nsContextMenu.prototype = {
initMiscItems: function CM_initMiscItems() { initMiscItems: function CM_initMiscItems() {
// Use "Bookmark This Link" if on a link. // Use "Bookmark This Link" if on a link.
this.showItem("context-bookmarkpage", let bookmarkPage = document.getElementById("context-bookmarkpage");
this.showItem(bookmarkPage,
!(this.isContentSelected || this.onTextInput || this.onLink || !(this.isContentSelected || this.onTextInput || this.onLink ||
this.onImage || this.onVideo || this.onAudio || this.onSocial || this.onImage || this.onVideo || this.onAudio || this.onSocial ||
this.onCanvas)); this.onCanvas));
bookmarkPage.setAttribute("tooltiptext", bookmarkPage.getAttribute("buttontooltiptext"));
bookmarkPage.disabled = bookmarkPage.hasAttribute("stardisabled");
this.showItem("context-bookmarklink", (this.onLink && !this.onMailtoLink && this.showItem("context-bookmarklink", (this.onLink && !this.onMailtoLink &&
!this.onSocial) || this.onPlainTextLink); !this.onSocial) || this.onPlainTextLink);
this.showItem("context-keywordfield", this.showItem("context-keywordfield",

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

@ -57,8 +57,7 @@
<popupnotification id="password-notification" hidden="true"> <popupnotification id="password-notification" hidden="true">
<popupnotificationcontent orient="vertical"> <popupnotificationcontent orient="vertical">
<textbox id="password-notification-username"/> <textbox id="password-notification-username"/>
<textbox id="password-notification-password" type="password" <textbox id="password-notification-password" type="password" show-content=""/>
disabled="true"/>
</popupnotificationcontent> </popupnotificationcontent>
</popupnotification> </popupnotification>

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

@ -33,10 +33,7 @@ this.EXPORTED_SYMBOLS = [
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////
//// Globals //// Globals
const Cc = Components.classes; const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
@ -65,8 +62,15 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm"); "resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task", XPCOMUtils.defineLazyModuleGetter(this, "Task",
"resource://gre/modules/Task.jsm"); "resource://gre/modules/Task.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadsLogger",
"resource:///modules/DownloadsLogger.jsm"); XPCOMUtils.defineLazyGetter(this, "DownloadsLogger", () => {
let { ConsoleAPI } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
let consoleOptions = {
maxLogLevelPref: "browser.download.loglevel",
prefix: "Downloads"
};
return new ConsoleAPI(consoleOptions);
});
const nsIDM = Ci.nsIDownloadManager; const nsIDM = Ci.nsIDownloadManager;
@ -124,7 +128,6 @@ let PrefObserver = {
PrefObserver.register({ PrefObserver.register({
// prefName: defaultValue // prefName: defaultValue
debug: false,
animateNotifications: true animateNotifications: true
}); });
@ -144,20 +147,6 @@ this.DownloadsCommon = {
BLOCK_VERDICT_POTENTIALLY_UNWANTED: "PotentiallyUnwanted", BLOCK_VERDICT_POTENTIALLY_UNWANTED: "PotentiallyUnwanted",
BLOCK_VERDICT_UNCOMMON: "Uncommon", BLOCK_VERDICT_UNCOMMON: "Uncommon",
log(...aMessageArgs) {
if (!PrefObserver.debug) {
return;
}
DownloadsLogger.log(...aMessageArgs);
},
error(...aMessageArgs) {
if (!PrefObserver.debug) {
return;
}
DownloadsLogger.reportError(...aMessageArgs);
},
/** /**
* Returns an object whose keys are the string names from the downloads string * Returns an object whose keys are the string names from the downloads string
* bundle, and whose values are either the translated strings or functions * bundle, and whose values are either the translated strings or functions
@ -601,6 +590,13 @@ this.DownloadsCommon = {
}), }),
}; };
XPCOMUtils.defineLazyGetter(this.DownloadsCommon, "log", () => {
return DownloadsLogger.log.bind(DownloadsLogger);
});
XPCOMUtils.defineLazyGetter(this.DownloadsCommon, "error", () => {
return DownloadsLogger.error.bind(DownloadsLogger);
});
/** /**
* Returns true if we are executing on Windows Vista or a later version. * Returns true if we are executing on Windows Vista or a later version.
*/ */

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

@ -1,75 +0,0 @@
/* -*- js-indent-level: 2; indent-tabs-mode: nil -*- */
/* vim: set ft=javascript ts=2 et sw=2 tw=80: */
/* 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/. */
/**
* The contents of this file were copied almost entirely from
* toolkit/identity/LogUtils.jsm. Until we've got a more generalized logging
* mechanism for toolkit, I think this is going to be how we roll.
*/
"use strict";
this.EXPORTED_SYMBOLS = ["DownloadsLogger"];
const PREF_DEBUG = "browser.download.debug";
const Cu = Components.utils;
const Ci = Components.interfaces;
const Cc = Components.classes;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
this.DownloadsLogger = {
_generateLogMessage(args) {
// create a string representation of a list of arbitrary things
let strings = [];
for (let arg of args) {
if (typeof arg === 'string') {
strings.push(arg);
} else if (arg === undefined) {
strings.push('undefined');
} else if (arg === null) {
strings.push('null');
} else {
try {
strings.push(JSON.stringify(arg, null, 2));
} catch(err) {
strings.push("<<something>>");
}
}
};
return 'Downloads: ' + strings.join(' ');
},
/**
* log() - utility function to print a list of arbitrary things
*
* Enable with about:config pref browser.download.debug
*/
log(...args) {
let output = this._generateLogMessage(args);
dump(output + "\n");
// Additionally, make the output visible in the Error Console
Services.console.logStringMessage(output);
},
/**
* reportError() - report an error through component utils as well as
* our log function
*/
reportError(...aArgs) {
// Report the error in the browser
let output = this._generateLogMessage(aArgs);
Cu.reportError(output);
dump("ERROR:" + output + "\n");
for (let frame = Components.stack.caller; frame; frame = frame.caller) {
dump("\t" + frame + "\n");
}
},
};

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

@ -14,7 +14,6 @@ JAR_MANIFESTS += ['jar.mn']
EXTRA_JS_MODULES += [ EXTRA_JS_MODULES += [
'DownloadsCommon.jsm', 'DownloadsCommon.jsm',
'DownloadsLogger.jsm',
'DownloadsTaskbar.jsm', 'DownloadsTaskbar.jsm',
'DownloadsViewUI.jsm', 'DownloadsViewUI.jsm',
] ]

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

@ -0,0 +1,260 @@
/* 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/. */
"use strict";
const { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/FileUtils.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource:///modules/MigrationUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "PlacesUtils",
"resource://gre/modules/PlacesUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Sqlite",
"resource://gre/modules/Sqlite.jsm");
function parseINIStrings(file) {
let factory = Cc["@mozilla.org/xpcom/ini-parser-factory;1"].
getService(Ci.nsIINIParserFactory);
let parser = factory.createINIParser(file);
let obj = {};
let sections = parser.getSections();
while (sections.hasMore()) {
let section = sections.getNext();
obj[section] = {};
let keys = parser.getKeys(section);
while (keys.hasMore()) {
let key = keys.getNext();
obj[section][key] = parser.getString(section, key);
}
}
return obj;
}
function getHash(aStr) {
// return the two-digit hexadecimal code for a byte
function toHexString(charCode)
("0" + charCode.toString(16)).slice(-2);
let hasher = Cc["@mozilla.org/security/hash;1"].
createInstance(Ci.nsICryptoHash);
hasher.init(Ci.nsICryptoHash.MD5);
let stringStream = Cc["@mozilla.org/io/string-input-stream;1"].
createInstance(Ci.nsIStringInputStream);
stringStream.data = aStr;
hasher.updateFromStream(stringStream, -1);
// convert the binary hash data to a hex string.
let binary = hasher.finish(false);
return [toHexString(binary.charCodeAt(i)) for (i in binary)].join("").toLowerCase();
}
function Bookmarks(aProfileFolder) {
let file = aProfileFolder.clone();
file.append("360sefav.db");
this._file = file;
}
Bookmarks.prototype = {
type: MigrationUtils.resourceTypes.BOOKMARKS,
get exists() {
return this._file.exists() && this._file.isReadable();
},
migrate: function (aCallback) {
return Task.spawn(function* () {
let idToGuid = new Map();
let folderGuid = PlacesUtils.bookmarks.toolbarGuid;
if (!MigrationUtils.isStartupMigration) {
folderGuid =
yield MigrationUtils.createImportedBookmarksFolder("360se", folderGuid);
}
idToGuid.set(0, folderGuid);
let connection = yield Sqlite.openConnection({
path: this._file.path
});
try {
let rows = yield connection.execute(
`WITH RECURSIVE
bookmark(id, parent_id, is_folder, title, url, pos) AS (
VALUES(0, -1, 1, '', '', 0)
UNION
SELECT f.id, f.parent_id, f.is_folder, f.title, f.url, f.pos
FROM tb_fav AS f
JOIN bookmark AS b ON f.parent_id = b.id
ORDER BY f.pos ASC
)
SELECT id, parent_id, is_folder, title, url FROM bookmark WHERE id`);
for (let row of rows) {
let id = parseInt(row.getResultByName("id"), 10),
parent_id = parseInt(row.getResultByName("parent_id"), 10),
is_folder = parseInt(row.getResultByName("is_folder"), 10),
title = row.getResultByName("title"),
url = row.getResultByName("url");
let parentGuid = idToGuid.get(parent_id) || idToGuid.get("fallback");
if (!parentGuid) {
parentGuid = PlacesUtils.bookmarks.unfiledGuid;
if (!MigrationUtils.isStartupMigration) {
parentGuid =
yield MigrationUtils.createImportedBookmarksFolder("360se", parentGuid);
}
idToGuid.set("fallback", parentGuid);
}
try {
if (is_folder == 1) {
let newFolderGuid = (yield PlacesUtils.bookmarks.insert({
parentGuid,
type: PlacesUtils.bookmarks.TYPE_FOLDER,
title
})).guid;
idToGuid.set(id, newFolderGuid);
} else {
yield PlacesUtils.bookmarks.insert({
parentGuid,
url,
title
});
}
} catch (ex) {
Cu.reportError(ex);
}
}
} finally {
yield connection.close();
}
}.bind(this)).then(() => aCallback(true),
e => { Cu.reportError(e); aCallback(false) });
}
};
function Qihoo360seProfileMigrator() {
let paths = [
// for v6 and above
{
users: ["360se6", "apps", "data", "users"],
defaultUser: "default"
},
// for earlier versions
{
users: ["360se"],
defaultUser: "data"
}
];
this._usersDir = null;
this._defaultUserPath = null;
for (let path of paths) {
let usersDir = FileUtils.getDir("AppData", path.users, false);
if (usersDir.exists()) {
this._usersDir = usersDir;
this._defaultUserPath = path.defaultUser;
break;
}
}
}
Qihoo360seProfileMigrator.prototype = Object.create(MigratorPrototype);
Object.defineProperty(Qihoo360seProfileMigrator.prototype, "sourceProfiles", {
get: function() {
if ("__sourceProfiles" in this)
return this.__sourceProfiles;
if (!this._usersDir)
return this.__sourceProfiles = [];
let profiles = [];
let noLoggedInUser = true;
try {
let loginIni = this._usersDir.clone();
loginIni.append("login.ini");
if (!loginIni.exists()) {
throw new Error("360 Secure Browser's 'login.ini' does not exist.");
}
if (!loginIni.isReadable()) {
throw new Error("360 Secure Browser's 'login.ini' file could not be read.");
}
let loginIniObj = parseINIStrings(loginIni);
let nowLoginEmail = loginIniObj.NowLogin && loginIniObj.NowLogin.email;
/*
* NowLogin section may:
* 1. be missing or without email, before any user logs in.
* 2. represents the current logged in user
* 3. represents the most recent logged in user
*
* In the second case, user represented by NowLogin should be the first
* profile; otherwise the default user should be selected by default.
*/
if (nowLoginEmail) {
if (loginIniObj.NowLogin.IsLogined === "1") {
noLoggedInUser = false;
}
profiles.push({
id: this._getIdFromConfig(loginIniObj.NowLogin),
name: nowLoginEmail,
});
}
for (let section in loginIniObj) {
if (!loginIniObj[section].email ||
(nowLoginEmail && loginIniObj[section].email == nowLoginEmail)) {
continue;
}
profiles.push({
id: this._getIdFromConfig(loginIniObj[section]),
name: loginIniObj[section].email,
});
}
} catch (e) {
Cu.reportError("Error detecting 360 Secure Browser profiles: " + e);
} finally {
profiles[noLoggedInUser ? "unshift" : "push"]({
id: this._defaultUserPath,
name: "Default",
});
}
return this.__sourceProfiles = profiles.filter(profile => {
let resources = this.getResources(profile);
return resources && resources.length > 0;
});
}
});
Qihoo360seProfileMigrator.prototype._getIdFromConfig = function(aConfig) {
return aConfig.UserMd5 || getHash(aConfig.email);
};
Qihoo360seProfileMigrator.prototype.getResources = function(aProfile) {
let profileFolder = this._usersDir.clone();
profileFolder.append(aProfile.id);
if (!profileFolder.exists()) {
return [];
}
let resources = [
new Bookmarks(profileFolder)
];
return [r for each (r in resources) if (r.exists)];
};
Qihoo360seProfileMigrator.prototype.classDescription = "360 Secure Browser Profile Migrator";
Qihoo360seProfileMigrator.prototype.contractID = "@mozilla.org/profile/migrator;1?app=browser&type=360se";
Qihoo360seProfileMigrator.prototype.classID = Components.ID("{d0037b95-296a-4a4e-94b2-c3d075d20ab1}");
this.NSGetFactory = XPCOMUtils.generateNSGetFactory([Qihoo360seProfileMigrator]);

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

@ -12,3 +12,7 @@ contract @mozilla.org/profile/migrator;1?app=browser&type=ie {3d2532e3-4932-4774
component {4b609ecf-60b2-4655-9df4-dc149e474da1} SafariProfileMigrator.js component {4b609ecf-60b2-4655-9df4-dc149e474da1} SafariProfileMigrator.js
contract @mozilla.org/profile/migrator;1?app=browser&type=safari {4b609ecf-60b2-4655-9df4-dc149e474da1} contract @mozilla.org/profile/migrator;1?app=browser&type=safari {4b609ecf-60b2-4655-9df4-dc149e474da1}
#endif #endif
#ifdef HAS_360SE_MIGRATOR
component {d0037b95-296a-4a4e-94b2-c3d075d20ab1} 360seProfileMigrator.js
contract @mozilla.org/profile/migrator;1?app=browser&type=360se {d0037b95-296a-4a4e-94b2-c3d075d20ab1}
#endif

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

@ -43,11 +43,12 @@ function getMigrationBundle() {
*/ */
function getMigratorKeyForDefaultBrowser() { function getMigratorKeyForDefaultBrowser() {
const APP_DESC_TO_KEY = { const APP_DESC_TO_KEY = {
"Internet Explorer": "ie", "Internet Explorer": "ie",
"Safari": "safari", "Safari": "safari",
"Firefox": "firefox", "Firefox": "firefox",
"Google Chrome": "chrome", // Windows, Linux "Google Chrome": "chrome", // Windows, Linux
"Chrome": "chrome", // OS X "Chrome": "chrome", // OS X
"360\u5b89\u5168\u6d4f\u89c8\u5668": "360se",
}; };
let browserDesc = ""; let browserDesc = "";
@ -450,6 +451,7 @@ this.MigrationUtils = Object.freeze({
* Supported values: ie (windows), * Supported values: ie (windows),
* safari (mac/windows), * safari (mac/windows),
* chrome (mac/windows/linux), * chrome (mac/windows/linux),
* 360se (windows),
* firefox. * firefox.
* *
* If null is returned, either no data can be imported * If null is returned, either no data can be imported
@ -482,7 +484,7 @@ this.MigrationUtils = Object.freeze({
get migrators() { get migrators() {
let migratorKeysOrdered = [ let migratorKeysOrdered = [
#ifdef XP_WIN #ifdef XP_WIN
"firefox", "ie", "chrome", "safari" "firefox", "ie", "chrome", "safari", "360se"
#elifdef XP_MACOSX #elifdef XP_MACOSX
"firefox", "safari", "chrome" "firefox", "safari", "chrome"
#elifdef XP_UNIX #elifdef XP_UNIX

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

@ -292,6 +292,9 @@ var MigrationWizard = {
case "firefox": case "firefox":
source = "sourceNameFirefox"; source = "sourceNameFirefox";
break; break;
case "360se":
source = "sourceName360se";
break;
} }
// semi-wallpaper for crash when multiple profiles exist, since we haven't initialized mSourceProfile in places // semi-wallpaper for crash when multiple profiles exist, since we haven't initialized mSourceProfile in places

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

@ -37,6 +37,7 @@
<radio id="ie" label="&importFromIE.label;" accesskey="&importFromIE.accesskey;"/> <radio id="ie" label="&importFromIE.label;" accesskey="&importFromIE.accesskey;"/>
<radio id="chrome" label="&importFromChrome.label;" accesskey="&importFromChrome.accesskey;"/> <radio id="chrome" label="&importFromChrome.label;" accesskey="&importFromChrome.accesskey;"/>
<radio id="safari" label="&importFromSafari.label;" accesskey="&importFromSafari.accesskey;"/> <radio id="safari" label="&importFromSafari.label;" accesskey="&importFromSafari.accesskey;"/>
<radio id="360se" label="&importFrom360se.label;" accesskey="&importFrom360se.accesskey;"/>
#elifdef XP_MACOSX #elifdef XP_MACOSX
<radio id="safari" label="&importFromSafari.label;" accesskey="&importFromSafari.accesskey;"/> <radio id="safari" label="&importFromSafari.label;" accesskey="&importFromSafari.accesskey;"/>
<radio id="chrome" label="&importFromChrome.label;" accesskey="&importFromChrome.accesskey;"/> <radio id="chrome" label="&importFromChrome.label;" accesskey="&importFromChrome.accesskey;"/>

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

@ -26,8 +26,10 @@ EXTRA_COMPONENTS += [
if CONFIG['OS_ARCH'] == 'WINNT': if CONFIG['OS_ARCH'] == 'WINNT':
EXTRA_COMPONENTS += [ EXTRA_COMPONENTS += [
'360seProfileMigrator.js',
'IEProfileMigrator.js', 'IEProfileMigrator.js',
] ]
DEFINES['HAS_360SE_MIGRATOR'] = True
DEFINES['HAS_IE_MIGRATOR'] = True DEFINES['HAS_IE_MIGRATOR'] = True
EXTRA_PP_COMPONENTS += [ EXTRA_PP_COMPONENTS += [

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

@ -67,6 +67,7 @@
<menuitem value="ko" label="&font.langGroup.korean;"/> <menuitem value="ko" label="&font.langGroup.korean;"/>
<menuitem value="x-western" label="&font.langGroup.latin;"/> <menuitem value="x-western" label="&font.langGroup.latin;"/>
<menuitem value="x-mlym" label="&font.langGroup.malayalam;"/> <menuitem value="x-mlym" label="&font.langGroup.malayalam;"/>
<menuitem value="x-math" label="&font.langGroup.math;"/>
<menuitem value="x-orya" label="&font.langGroup.oriya;"/> <menuitem value="x-orya" label="&font.langGroup.oriya;"/>
<menuitem value="x-sinh" label="&font.langGroup.sinhala;"/> <menuitem value="x-sinh" label="&font.langGroup.sinhala;"/>
<menuitem value="x-tamil" label="&font.langGroup.tamil;"/> <menuitem value="x-tamil" label="&font.langGroup.tamil;"/>

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

@ -358,12 +358,12 @@ var gPrivacyPane = {
* Update browser.urlbar.autocomplete.enabled when a * Update browser.urlbar.autocomplete.enabled when a
* browser.urlbar.suggest.* pref is changed from the ui. * browser.urlbar.suggest.* pref is changed from the ui.
*/ */
writeSuggestionPref: function () { writeSuggestionPref() {
let getVal = (aPref) => { let getVal = (aPref) => {
return document.getElementById("browser.urlbar.suggest." + aPref).value; return document.getElementById("browser.urlbar.suggest." + aPref).value;
} }
// autocomplete.enabled is true if any of the suggestions is true // autocomplete.enabled is true if any of the suggestions is true
let enabled = ["history", "bookmark", "openpage"].map(getVal).some(v => v); let enabled = ["history", "bookmark", "openpage", "searches"].map(getVal).some(v => v);
Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", enabled); Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", enabled);
}, },

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

@ -38,6 +38,9 @@
<preference id="browser.urlbar.suggest.openpage" <preference id="browser.urlbar.suggest.openpage"
name="browser.urlbar.suggest.openpage" name="browser.urlbar.suggest.openpage"
type="bool"/> type="bool"/>
<preference id="browser.urlbar.suggest.searches"
name="browser.urlbar.suggest.searches"
type="bool"/>
<!-- History --> <!-- History -->
<preference id="places.history.enabled" <preference id="places.history.enabled"
@ -254,4 +257,8 @@
onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();" onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
accesskey="&locbar.openpage.accesskey;" accesskey="&locbar.openpage.accesskey;"
preference="browser.urlbar.suggest.openpage"/> preference="browser.urlbar.suggest.openpage"/>
<checkbox id="searchesSuggestion" label="&locbar.searches.label;"
onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
accesskey="&locbar.searches.accesskey;"
preference="browser.urlbar.suggest.searches"/>
</groupbox> </groupbox>

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

@ -15,6 +15,7 @@ function test() {
run_test_subset([ run_test_subset([
test_locbar_suggestion_retention("history", true), test_locbar_suggestion_retention("history", true),
test_locbar_suggestion_retention("bookmark", true), test_locbar_suggestion_retention("bookmark", true),
test_locbar_suggestion_retention("searches", true),
test_locbar_suggestion_retention("openpage", false), test_locbar_suggestion_retention("openpage", false),
test_locbar_suggestion_retention("history", true), test_locbar_suggestion_retention("history", true),
test_locbar_suggestion_retention("history", false), test_locbar_suggestion_retention("history", false),
@ -22,4 +23,4 @@ function test() {
// reset all preferences to their default values once we're done // reset all preferences to their default values once we're done
reset_preferences reset_preferences
]); ]);
} }

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

@ -325,12 +325,12 @@ var gPrivacyPane = {
* Update browser.urlbar.autocomplete.enabled when a * Update browser.urlbar.autocomplete.enabled when a
* browser.urlbar.suggest.* pref is changed from the ui. * browser.urlbar.suggest.* pref is changed from the ui.
*/ */
writeSuggestionPref: function PPP_writeSuggestionPref() { writeSuggestionPref() {
let getVal = (aPref) => { let getVal = (aPref) => {
return document.getElementById("browser.urlbar.suggest." + aPref).value; return document.getElementById("browser.urlbar.suggest." + aPref).value;
} }
// autocomplete.enabled is true if any of the suggestions is true // autocomplete.enabled is true if any of the suggestions is true
let enabled = ["history", "bookmark", "openpage"].map(getVal).some(v => v); let enabled = ["history", "bookmark", "openpage", "searches"].map(getVal).some(v => v);
Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", enabled); Services.prefs.setBoolPref("browser.urlbar.autocomplete.enabled", enabled);
}, },

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

@ -51,6 +51,9 @@
<preference id="browser.urlbar.suggest.openpage" <preference id="browser.urlbar.suggest.openpage"
name="browser.urlbar.suggest.openpage" name="browser.urlbar.suggest.openpage"
type="bool"/> type="bool"/>
<preference id="browser.urlbar.suggest.searches"
name="browser.urlbar.suggest.searches"
type="bool"/>
<!-- History --> <!-- History -->
<preference id="places.history.enabled" <preference id="places.history.enabled"
@ -280,6 +283,10 @@
onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();" onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
accesskey="&locbar.openpage.accesskey;" accesskey="&locbar.openpage.accesskey;"
preference="browser.urlbar.suggest.openpage"/> preference="browser.urlbar.suggest.openpage"/>
<checkbox id="searchesSuggestion" label="&locbar.searches.label;"
onsyncfrompreference="return gPrivacyPane.writeSuggestionPref();"
accesskey="&locbar.searches.accesskey;"
preference="browser.urlbar.suggest.searches"/>
</vbox> </vbox>
</groupbox> </groupbox>

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

@ -15,6 +15,7 @@ function test() {
run_test_subset([ run_test_subset([
test_locbar_suggestion_retention("history", true), test_locbar_suggestion_retention("history", true),
test_locbar_suggestion_retention("bookmark", true), test_locbar_suggestion_retention("bookmark", true),
test_locbar_suggestion_retention("searches", true),
test_locbar_suggestion_retention("openpage", false), test_locbar_suggestion_retention("openpage", false),
test_locbar_suggestion_retention("history", true), test_locbar_suggestion_retention("history", true),
test_locbar_suggestion_retention("history", false), test_locbar_suggestion_retention("history", false),
@ -22,4 +23,4 @@ function test() {
// reset all preferences to their default values once we're done // reset all preferences to their default values once we're done
reset_preferences reset_preferences
]); ]);
} }

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

@ -839,17 +839,12 @@ let SessionStoreInternal = {
}, },
/** /**
* If it's the first window load since app start... * Registers and tracks a given window.
* - determine if we're reloading after a crash or a forced-restart *
* - restore window state
* - restart downloads
* Set up event listeners for this window's tabs
* @param aWindow * @param aWindow
* Window reference * Window reference
* @param aInitialState
* The initial state to be loaded after startup (optional)
*/ */
onLoad: function ssi_onLoad(aWindow, aInitialState = null) { onLoad(aWindow) {
// return if window has already been initialized // return if window has already been initialized
if (aWindow && aWindow.__SSi && this._windows[aWindow.__SSi]) if (aWindow && aWindow.__SSi && this._windows[aWindow.__SSi])
return; return;
@ -874,14 +869,43 @@ let SessionStoreInternal = {
// and create its data object // and create its data object
this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [], busy: false }; this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [], busy: false };
let isPrivateWindow = false;
if (PrivateBrowsingUtils.isWindowPrivate(aWindow)) if (PrivateBrowsingUtils.isWindowPrivate(aWindow))
this._windows[aWindow.__SSi].isPrivate = isPrivateWindow = true; this._windows[aWindow.__SSi].isPrivate = true;
if (!this._isWindowLoaded(aWindow)) if (!this._isWindowLoaded(aWindow))
this._windows[aWindow.__SSi]._restoring = true; this._windows[aWindow.__SSi]._restoring = true;
if (!aWindow.toolbar.visible) if (!aWindow.toolbar.visible)
this._windows[aWindow.__SSi].isPopup = true; this._windows[aWindow.__SSi].isPopup = true;
let tabbrowser = aWindow.gBrowser;
// add tab change listeners to all already existing tabs
for (let i = 0; i < tabbrowser.tabs.length; i++) {
this.onTabAdd(aWindow, tabbrowser.tabs[i], true);
}
// notification of tab add/remove/selection/show/hide
TAB_EVENTS.forEach(function(aEvent) {
tabbrowser.tabContainer.addEventListener(aEvent, this, true);
}, this);
// Keep track of a browser's latest frameLoader.
aWindow.gBrowser.addEventListener("XULFrameLoaderCreated", this);
},
/**
* Initializes a given window.
*
* Windows are registered as soon as they are created but we need to wait for
* the session file to load, and the initial window's delayed startup to
* finish before initializing a window, i.e. restoring data into it.
*
* @param aWindow
* Window reference
* @param aInitialState
* The initial state to be loaded after startup (optional)
*/
initializeWindow(aWindow, aInitialState = null) {
let isPrivateWindow = PrivateBrowsingUtils.isWindowPrivate(aWindow);
// perform additional initialization when the first window is loading // perform additional initialization when the first window is loading
if (RunState.isStopped) { if (RunState.isStopped) {
RunState.setRunning(); RunState.setRunning();
@ -1007,20 +1031,6 @@ let SessionStoreInternal = {
// undoCloseWindow was executed. // undoCloseWindow was executed.
this._restoreLastWindow = false; this._restoreLastWindow = false;
} }
var tabbrowser = aWindow.gBrowser;
// add tab change listeners to all already existing tabs
for (let i = 0; i < tabbrowser.tabs.length; i++) {
this.onTabAdd(aWindow, tabbrowser.tabs[i], true);
}
// notification of tab add/remove/selection/show/hide
TAB_EVENTS.forEach(function(aEvent) {
tabbrowser.tabContainer.addEventListener(aEvent, this, true);
}, this);
// Keep track of a browser's latest frameLoader.
aWindow.gBrowser.addEventListener("XULFrameLoaderCreated", this);
}, },
/** /**
@ -1029,9 +1039,12 @@ let SessionStoreInternal = {
* Window reference * Window reference
*/ */
onBeforeBrowserWindowShown: function (aWindow) { onBeforeBrowserWindowShown: function (aWindow) {
// Just call onLoad() directly if we're initialized already. // Register the window.
this.onLoad(aWindow);
// Just call initializeWindow() directly if we're initialized already.
if (this._sessionInitialized) { if (this._sessionInitialized) {
this.onLoad(aWindow); this.initializeWindow(aWindow);
return; return;
} }
@ -1067,13 +1080,13 @@ let SessionStoreInternal = {
} }
if (this._sessionInitialized) { if (this._sessionInitialized) {
this.onLoad(aWindow); this.initializeWindow(aWindow);
} else { } else {
let initialState = this.initSession(); let initialState = this.initSession();
this._sessionInitialized = true; this._sessionInitialized = true;
TelemetryStopwatch.start("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS"); TelemetryStopwatch.start("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS");
this.onLoad(aWindow, initialState); this.initializeWindow(aWindow, initialState);
TelemetryStopwatch.finish("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS"); TelemetryStopwatch.finish("FX_SESSION_RESTORE_STARTUP_ONLOAD_INITIAL_WINDOW_MS");
// Let everyone know we're done. // Let everyone know we're done.
@ -2173,6 +2186,7 @@ let SessionStoreInternal = {
*/ */
navigateAndRestore(tab, loadArguments, historyIndex) { navigateAndRestore(tab, loadArguments, historyIndex) {
let window = tab.ownerDocument.defaultView; let window = tab.ownerDocument.defaultView;
NS_ASSERT(window.__SSi, "tab's window must be tracked");
let browser = tab.linkedBrowser; let browser = tab.linkedBrowser;
// Set tab title to "Connecting..." and start the throbber to pretend we're // Set tab title to "Connecting..." and start the throbber to pretend we're

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

@ -30,6 +30,16 @@ add_task(function*() {
ok(parseInt(toolbox._host.frame.style.marginBottom, 10) == 0, ok(parseInt(toolbox._host.frame.style.marginBottom, 10) == 0,
"The toolbox host is shown again"); "The toolbox host is shown again");
info("Try to minimize again using the keyboard shortcut");
yield minimizeWithShortcut(toolbox);
ok(parseInt(toolbox._host.frame.style.marginBottom, 10) < 0,
"The toolbox host has been hidden away with a negative-margin");
info("Try to maximize again using the keyboard shortcut");
yield maximizeWithShortcut(toolbox);
ok(parseInt(toolbox._host.frame.style.marginBottom, 10) == 0,
"The toolbox host is shown again");
info("Minimize again and switch to another tool"); info("Minimize again and switch to another tool");
yield minimize(toolbox); yield minimize(toolbox);
let onMaximized = toolbox._host.once("maximized"); let onMaximized = toolbox._host.once("maximized");
@ -67,9 +77,27 @@ function* minimize(toolbox) {
yield onMinimized; yield onMinimized;
} }
function* minimizeWithShortcut(toolbox) {
let key = toolbox.doc.getElementById("toolbox-minimize-key")
.getAttribute("key");
let onMinimized = toolbox._host.once("minimized");
EventUtils.synthesizeKey(key, {accelKey: true, shiftKey: true},
toolbox.doc.defaultView);
yield onMinimized;
}
function* maximize(toolbox) { function* maximize(toolbox) {
let button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize"); let button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
let onMaximized = toolbox._host.once("maximized"); let onMaximized = toolbox._host.once("maximized");
EventUtils.synthesizeMouseAtCenter(button, {}, toolbox.doc.defaultView); EventUtils.synthesizeMouseAtCenter(button, {}, toolbox.doc.defaultView);
yield onMaximized; yield onMaximized;
} }
function* maximizeWithShortcut(toolbox) {
let key = toolbox.doc.getElementById("toolbox-minimize-key")
.getAttribute("key");
let onMaximized = toolbox._host.once("maximized");
EventUtils.synthesizeKey(key, {accelKey: true, shiftKey: true},
toolbox.doc.defaultView);
yield onMaximized;
}

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

@ -2,8 +2,8 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this * 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/. */ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* globals gDevTools, DOMHelpers, toolboxStrings, InspectorFront, Selection, /* globals gDevTools, DOMHelpers, toolboxStrings, InspectorFront, Selection,
getPerformanceActorsConnection, CommandUtils, DevToolsUtils, screenManager, CommandUtils, DevToolsUtils, screenManager, oscpu, Hosts, is64Bit,
oscpu, Hosts, is64Bit */ osString, showDoorhanger, getHighlighterUtils, getPerformanceFront */
"use strict"; "use strict";
@ -21,24 +21,20 @@ let {Cc, Ci, Cu} = require("chrome");
let {Promise: promise} = require("resource://gre/modules/Promise.jsm"); let {Promise: promise} = require("resource://gre/modules/Promise.jsm");
let EventEmitter = require("devtools/toolkit/event-emitter"); let EventEmitter = require("devtools/toolkit/event-emitter");
let Telemetry = require("devtools/shared/telemetry"); let Telemetry = require("devtools/shared/telemetry");
let {getHighlighterUtils} = require("devtools/framework/toolbox-highlighter-utils");
let HUDService = require("devtools/webconsole/hudservice"); let HUDService = require("devtools/webconsole/hudservice");
let {showDoorhanger} = require("devtools/shared/doorhanger");
let sourceUtils = require("devtools/shared/source-utils"); let sourceUtils = require("devtools/shared/source-utils");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/devtools/gDevTools.jsm"); Cu.import("resource:///modules/devtools/gDevTools.jsm");
Cu.import("resource:///modules/devtools/scratchpad-manager.jsm"); Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
Cu.import("resource:///modules/devtools/DOMHelpers.jsm"); Cu.import("resource:///modules/devtools/DOMHelpers.jsm");
Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/Task.jsm");
loader.lazyGetter(this, "Hosts", () => require("devtools/framework/toolbox-hosts").Hosts); loader.lazyImporter(this, "CommandUtils",
"resource:///modules/devtools/DeveloperToolbar.jsm");
loader.lazyImporter(this, "CommandUtils", "resource:///modules/devtools/DeveloperToolbar.jsm");
loader.lazyGetter(this, "toolboxStrings", () => { loader.lazyGetter(this, "toolboxStrings", () => {
let bundle = Services.strings.createBundle("chrome://browser/locale/devtools/toolbox.properties"); const properties = "chrome://browser/locale/devtools/toolbox.properties";
const bundle = Services.strings.createBundle(properties);
return (name, ...args) => { return (name, ...args) => {
try { try {
if (!args.length) { if (!args.length) {
@ -51,22 +47,31 @@ loader.lazyGetter(this, "toolboxStrings", () => {
} }
}; };
}); });
loader.lazyRequireGetter(this, "getHighlighterUtils",
loader.lazyGetter(this, "Selection", () => require("devtools/framework/selection").Selection); "devtools/framework/toolbox-highlighter-utils", true);
loader.lazyGetter(this, "InspectorFront", () => require("devtools/server/actors/inspector").InspectorFront); loader.lazyRequireGetter(this, "Hosts",
loader.lazyRequireGetter(this, "DevToolsUtils", "devtools/toolkit/DevToolsUtils"); "devtools/framework/toolbox-hosts", true);
loader.lazyRequireGetter(this, "getPerformanceFront", "devtools/performance/front", true); loader.lazyRequireGetter(this, "Selection",
"devtools/framework/selection", true);
XPCOMUtils.defineLazyGetter(this, "screenManager", () => { loader.lazyRequireGetter(this, "InspectorFront",
"devtools/server/actors/inspector", true);
loader.lazyRequireGetter(this, "DevToolsUtils",
"devtools/toolkit/DevToolsUtils");
loader.lazyRequireGetter(this, "showDoorhanger",
"devtools/shared/doorhanger", true);
loader.lazyRequireGetter(this, "getPerformanceFront",
"devtools/performance/front", true);
loader.lazyGetter(this, "osString", () => {
return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULRuntime).OS;
});
loader.lazyGetter(this, "screenManager", () => {
return Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager); return Cc["@mozilla.org/gfx/screenmanager;1"].getService(Ci.nsIScreenManager);
}); });
loader.lazyGetter(this, "oscpu", () => {
XPCOMUtils.defineLazyGetter(this, "oscpu", () => {
return Cc["@mozilla.org/network/protocol;1?name=http"] return Cc["@mozilla.org/network/protocol;1?name=http"]
.getService(Ci.nsIHttpProtocolHandler).oscpu; .getService(Ci.nsIHttpProtocolHandler).oscpu;
}); });
loader.lazyGetter(this, "is64Bit", () => {
XPCOMUtils.defineLazyGetter(this, "is64Bit", () => {
return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).is64Bit; return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).is64Bit;
}); });
@ -79,8 +84,9 @@ const ToolboxButtons = exports.ToolboxButtons = [
target.getTrait("highlightable") target.getTrait("highlightable")
}, },
{ id: "command-button-frames", { id: "command-button-frames",
isTargetSupported: target => isTargetSupported: target => {
( target.activeTab && target.activeTab.traits.frames ) return target.activeTab && target.activeTab.traits.frames;
}
}, },
{ id: "command-button-splitconsole", { id: "command-button-splitconsole",
isTargetSupported: target => !target.isAddon }, isTargetSupported: target => !target.isAddon },
@ -135,6 +141,7 @@ function Toolbox(target, selectedTool, hostType, hostOptions) {
this._onBottomHostMaximized = this._onBottomHostMaximized.bind(this); this._onBottomHostMaximized = this._onBottomHostMaximized.bind(this);
this._onToolSelectWhileMinimized = this._onToolSelectWhileMinimized.bind(this); this._onToolSelectWhileMinimized = this._onToolSelectWhileMinimized.bind(this);
this._onBottomHostWillChange = this._onBottomHostWillChange.bind(this); this._onBottomHostWillChange = this._onBottomHostWillChange.bind(this);
this._toggleMinimizeMode = this._toggleMinimizeMode.bind(this);
this._target.on("close", this.destroy); this._target.on("close", this.destroy);
@ -481,7 +488,7 @@ Toolbox.prototype = {
["toolbox-force-reload-key", true], ["toolbox-force-reload-key", true],
["toolbox-force-reload-key2", true] ["toolbox-force-reload-key2", true]
].forEach(([id, force]) => { ].forEach(([id, force]) => {
this.doc.getElementById(id).addEventListener("command", (event) => { this.doc.getElementById(id).addEventListener("command", () => {
this.reloadTarget(force); this.reloadTarget(force);
}, true); }, true);
}); });
@ -493,6 +500,9 @@ Toolbox.prototype = {
let prevKey = this.doc.getElementById("toolbox-previous-tool-key"); let prevKey = this.doc.getElementById("toolbox-previous-tool-key");
prevKey.addEventListener("command", this.selectPreviousTool.bind(this), true); prevKey.addEventListener("command", this.selectPreviousTool.bind(this), true);
let minimizeKey = this.doc.getElementById("toolbox-minimize-key");
minimizeKey.addEventListener("command", this._toggleMinimizeMode, true);
// Split console uses keypress instead of command so the event can be // Split console uses keypress instead of command so the event can be
// cancelled with stopPropagation on the keypress, and not preventDefault. // cancelled with stopPropagation on the keypress, and not preventDefault.
this.doc.addEventListener("keypress", this._splitConsoleOnKeypress, false); this.doc.addEventListener("keypress", this._splitConsoleOnKeypress, false);
@ -703,18 +713,11 @@ Toolbox.prototype = {
if (this.hostType == Toolbox.HostType.BOTTOM) { if (this.hostType == Toolbox.HostType.BOTTOM) {
let minimizeBtn = this.doc.createElement("toolbarbutton"); let minimizeBtn = this.doc.createElement("toolbarbutton");
minimizeBtn.id = "toolbox-dock-bottom-minimize"; minimizeBtn.id = "toolbox-dock-bottom-minimize";
minimizeBtn.className = "maximized";
minimizeBtn.setAttribute("tooltiptext", minimizeBtn.addEventListener("command", this._toggleMinimizeMode);
toolboxStrings("toolboxDockButtons.bottom.minimize"));
// Calculate the height to which the host should be minimized so the
// tabbar is still visible.
let toolbarHeight = this.doc.querySelector(".devtools-tabbar")
.getBoxQuads({box: "content"})[0]
.bounds.height;
minimizeBtn.addEventListener("command", () => {
this._host.toggleMinimizeMode(toolbarHeight);
});
dockBox.appendChild(minimizeBtn); dockBox.appendChild(minimizeBtn);
// Show the button in its maximized state.
this._onBottomHostMaximized();
// Update the label and icon when the state changes. // Update the label and icon when the state changes.
this._host.on("minimized", this._onBottomHostMinimized); this._host.on("minimized", this._onBottomHostMinimized);
@ -754,18 +757,29 @@ Toolbox.prototype = {
} }
}, },
_getMinimizeButtonShortcutTooltip: function() {
let key = this.doc.getElementById("toolbox-minimize-key")
.getAttribute("key");
return "(" + (osString == "Darwin" ? "Cmd+Shift+" : "Ctrl+Shift+") +
key.toUpperCase() + ")";
},
_onBottomHostMinimized: function() { _onBottomHostMinimized: function() {
let btn = this.doc.querySelector("#toolbox-dock-bottom-minimize"); let btn = this.doc.querySelector("#toolbox-dock-bottom-minimize");
btn.className = "minimized"; btn.className = "minimized";
btn.setAttribute("tooltiptext", btn.setAttribute("tooltiptext",
toolboxStrings("toolboxDockButtons.bottom.maximize")); toolboxStrings("toolboxDockButtons.bottom.maximize") + " " +
this._getMinimizeButtonShortcutTooltip());
}, },
_onBottomHostMaximized: function() { _onBottomHostMaximized: function() {
let btn = this.doc.querySelector("#toolbox-dock-bottom-minimize"); let btn = this.doc.querySelector("#toolbox-dock-bottom-minimize");
btn.className = "maximized"; btn.className = "maximized";
btn.setAttribute("tooltiptext", btn.setAttribute("tooltiptext",
toolboxStrings("toolboxDockButtons.bottom.minimize")); toolboxStrings("toolboxDockButtons.bottom.minimize") + " " +
this._getMinimizeButtonShortcutTooltip());
}, },
_onToolSelectWhileMinimized: function() { _onToolSelectWhileMinimized: function() {
@ -780,6 +794,19 @@ Toolbox.prototype = {
this.off("before-select", this._onToolSelectWhileMinimized); this.off("before-select", this._onToolSelectWhileMinimized);
}, },
_toggleMinimizeMode: function() {
if (this.hostType !== Toolbox.HostType.BOTTOM) {
return;
}
// Calculate the height to which the host should be minimized so the
// tabbar is still visible.
let toolbarHeight = this.doc.querySelector(".devtools-tabbar")
.getBoxQuads({box: "content"})[0]
.bounds.height;
this._host.toggleMinimizeMode(toolbarHeight);
},
/** /**
* Add tabs to the toolbox UI for registered tools * Add tabs to the toolbox UI for registered tools
*/ */

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

@ -70,6 +70,10 @@
keycode="VK_F5" keycode="VK_F5"
oncommand="void(0);" oncommand="void(0);"
modifiers="accel"/> modifiers="accel"/>
<key id="toolbox-minimize-key"
key="&toolboxToggleMinimize.key;"
oncommand="void(0);"
modifiers="shift, accel"/>
</keyset> </keyset>
<popupset> <popupset>

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

@ -11,7 +11,7 @@
const TEST_URL = TEST_URL_ROOT + "doc_markup_search.html"; const TEST_URL = TEST_URL_ROOT + "doc_markup_search.html";
add_task(function*() { add_task(function*() {
let {inspector, toolbox} = yield addTab(TEST_URL).then(openInspector); let {inspector} = yield addTab(TEST_URL).then(openInspector);
let container = yield getContainerForSelector("em", inspector); let container = yield getContainerForSelector("em", inspector);
ok(!container, "The <em> tag isn't present yet in the markup-view"); ok(!container, "The <em> tag isn't present yet in the markup-view");
@ -19,11 +19,10 @@ add_task(function*() {
// Searching for the innermost element first makes sure that the inspector // Searching for the innermost element first makes sure that the inspector
// back-end is able to attach the resulting node to the tree it knows at the // back-end is able to attach the resulting node to the tree it knows at the
// moment. When the inspector is started, the <body> is the default selected // moment. When the inspector is started, the <body> is the default selected
// node, and only the parents up to the ROOT are known, and its direct children // node, and only the parents up to the ROOT are known, and its direct
// children.
info("searching for the innermost child: <em>"); info("searching for the innermost child: <em>");
let updated = inspector.once("inspector-updated"); yield searchFor("em", inspector);
searchUsingSelectorSearch("em", inspector);
yield updated;
container = yield getContainerForSelector("em", inspector); container = yield getContainerForSelector("em", inspector);
ok(container, "The <em> tag is now imported in the markup-view"); ok(container, "The <em> tag is now imported in the markup-view");
@ -34,12 +33,19 @@ add_task(function*() {
info("searching for other nodes too"); info("searching for other nodes too");
for (let node of ["span", "li", "ul"]) { for (let node of ["span", "li", "ul"]) {
let updated = inspector.once("inspector-updated"); yield searchFor(node, inspector);
searchUsingSelectorSearch(node, inspector);
yield updated;
nodeFront = yield getNodeFront(node, inspector); nodeFront = yield getNodeFront(node, inspector);
is(inspector.selection.nodeFront, nodeFront, is(inspector.selection.nodeFront, nodeFront,
"The <" + node + "> tag is the currently selected node"); "The <" + node + "> tag is the currently selected node");
} }
}); });
function* searchFor(selector, inspector) {
let onNewNodeFront = inspector.selection.once("new-node-front");
searchUsingSelectorSearch(selector, inspector);
yield onNewNodeFront;
yield inspector.once("inspector-updated");
}

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

@ -23,6 +23,9 @@ const EVENTS = {
// more information about what each packet is supposed to deliver. // more information about what each packet is supposed to deliver.
NETWORK_EVENT: "NetMonitor:NetworkEvent", NETWORK_EVENT: "NetMonitor:NetworkEvent",
// When a network event is added to the view
REQUEST_ADDED: "NetMonitor:RequestAdded",
// When request headers begin and finish receiving. // When request headers begin and finish receiving.
UPDATING_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdating:RequestHeaders", UPDATING_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdating:RequestHeaders",
RECEIVED_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdated:RequestHeaders", RECEIVED_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdated:RequestHeaders",
@ -528,34 +531,33 @@ NetworkEventsHandler.prototype = {
*/ */
_onNetworkEventUpdate: function(type, { packet, networkInfo }) { _onNetworkEventUpdate: function(type, { packet, networkInfo }) {
let { actor, request: { url } } = networkInfo; let { actor, request: { url } } = networkInfo;
switch (packet.updateType) { switch (packet.updateType) {
case "requestHeaders": case "requestHeaders":
this.webConsoleClient.getRequestHeaders(actor, this._onRequestHeaders); this.webConsoleClient.getRequestHeaders(actor, this._onRequestHeaders);
window.emit(EVENTS.UPDATING_REQUEST_HEADERS, [actor, url]); window.emit(EVENTS.UPDATING_REQUEST_HEADERS, actor);
break; break;
case "requestCookies": case "requestCookies":
this.webConsoleClient.getRequestCookies(actor, this._onRequestCookies); this.webConsoleClient.getRequestCookies(actor, this._onRequestCookies);
window.emit(EVENTS.UPDATING_REQUEST_COOKIES, [actor, url]); window.emit(EVENTS.UPDATING_REQUEST_COOKIES, actor);
break; break;
case "requestPostData": case "requestPostData":
this.webConsoleClient.getRequestPostData(actor, this._onRequestPostData); this.webConsoleClient.getRequestPostData(actor, this._onRequestPostData);
window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, [actor, url]); window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
break; break;
case "securityInfo": case "securityInfo":
NetMonitorView.RequestsMenu.updateRequest(actor, { NetMonitorView.RequestsMenu.updateRequest(actor, {
securityState: networkInfo.securityInfo, securityState: networkInfo.securityInfo,
}); });
this.webConsoleClient.getSecurityInfo(actor, this._onSecurityInfo); this.webConsoleClient.getSecurityInfo(actor, this._onSecurityInfo);
window.emit(EVENTS.UPDATING_SECURITY_INFO, [actor, url]); window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
break; break;
case "responseHeaders": case "responseHeaders":
this.webConsoleClient.getResponseHeaders(actor, this._onResponseHeaders); this.webConsoleClient.getResponseHeaders(actor, this._onResponseHeaders);
window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, [actor, url]); window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
break; break;
case "responseCookies": case "responseCookies":
this.webConsoleClient.getResponseCookies(actor, this._onResponseCookies); this.webConsoleClient.getResponseCookies(actor, this._onResponseCookies);
window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, [actor, url]); window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, actor);
break; break;
case "responseStart": case "responseStart":
NetMonitorView.RequestsMenu.updateRequest(actor, { NetMonitorView.RequestsMenu.updateRequest(actor, {
@ -566,7 +568,7 @@ NetworkEventsHandler.prototype = {
statusText: networkInfo.response.statusText, statusText: networkInfo.response.statusText,
headersSize: networkInfo.response.headersSize headersSize: networkInfo.response.headersSize
}); });
window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, [actor, url]); window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
break; break;
case "responseContent": case "responseContent":
NetMonitorView.RequestsMenu.updateRequest(actor, { NetMonitorView.RequestsMenu.updateRequest(actor, {
@ -575,14 +577,14 @@ NetworkEventsHandler.prototype = {
mimeType: networkInfo.response.content.mimeType mimeType: networkInfo.response.content.mimeType
}); });
this.webConsoleClient.getResponseContent(actor, this._onResponseContent); this.webConsoleClient.getResponseContent(actor, this._onResponseContent);
window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, [actor, url]); window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
break; break;
case "eventTimings": case "eventTimings":
NetMonitorView.RequestsMenu.updateRequest(actor, { NetMonitorView.RequestsMenu.updateRequest(actor, {
totalTime: networkInfo.totalTime totalTime: networkInfo.totalTime
}); });
this.webConsoleClient.getEventTimings(actor, this._onEventTimings); this.webConsoleClient.getEventTimings(actor, this._onEventTimings);
window.emit(EVENTS.UPDATING_EVENT_TIMINGS, [actor, url]); window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
break; break;
} }
}, },
@ -596,8 +598,9 @@ NetworkEventsHandler.prototype = {
_onRequestHeaders: function(aResponse) { _onRequestHeaders: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
requestHeaders: aResponse requestHeaders: aResponse
}, () => {
window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, aResponse.from);
}); });
window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, aResponse.from);
}, },
/** /**
@ -609,8 +612,9 @@ NetworkEventsHandler.prototype = {
_onRequestCookies: function(aResponse) { _onRequestCookies: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
requestCookies: aResponse requestCookies: aResponse
}, () => {
window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, aResponse.from);
}); });
window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, aResponse.from);
}, },
/** /**
@ -622,8 +626,9 @@ NetworkEventsHandler.prototype = {
_onRequestPostData: function(aResponse) { _onRequestPostData: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
requestPostData: aResponse requestPostData: aResponse
}, () => {
window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, aResponse.from);
}); });
window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, aResponse.from);
}, },
/** /**
@ -635,9 +640,9 @@ NetworkEventsHandler.prototype = {
_onSecurityInfo: function(aResponse) { _onSecurityInfo: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
securityInfo: aResponse.securityInfo securityInfo: aResponse.securityInfo
}, () => {
window.emit(EVENTS.RECEIVED_SECURITY_INFO, aResponse.from);
}); });
window.emit(EVENTS.RECEIVED_SECURITY_INFO, aResponse.from);
}, },
/** /**
@ -649,8 +654,9 @@ NetworkEventsHandler.prototype = {
_onResponseHeaders: function(aResponse) { _onResponseHeaders: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
responseHeaders: aResponse responseHeaders: aResponse
}, () => {
window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, aResponse.from);
}); });
window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, aResponse.from);
}, },
/** /**
@ -662,8 +668,9 @@ NetworkEventsHandler.prototype = {
_onResponseCookies: function(aResponse) { _onResponseCookies: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
responseCookies: aResponse responseCookies: aResponse
}, () => {
window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, aResponse.from);
}); });
window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, aResponse.from);
}, },
/** /**
@ -675,8 +682,9 @@ NetworkEventsHandler.prototype = {
_onResponseContent: function(aResponse) { _onResponseContent: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
responseContent: aResponse responseContent: aResponse
}, () => {
window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, aResponse.from);
}); });
window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, aResponse.from);
}, },
/** /**
@ -688,8 +696,9 @@ NetworkEventsHandler.prototype = {
_onEventTimings: function(aResponse) { _onEventTimings: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, { NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
eventTimings: aResponse eventTimings: aResponse
}, () => {
window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, aResponse.from);
}); });
window.emit(EVENTS.RECEIVED_EVENT_TIMINGS, aResponse.from);
}, },
/** /**

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

@ -75,6 +75,8 @@ const GENERIC_VARIABLES_VIEW_SETTINGS = {
const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200; // px const NETWORK_ANALYSIS_PIE_CHART_DIAMETER = 200; // px
const FREETEXT_FILTER_SEARCH_DELAY = 200; // ms const FREETEXT_FILTER_SEARCH_DELAY = 200; // ms
const {DeferredTask} = Cu.import("resource://gre/modules/DeferredTask.jsm", {});
/** /**
* Object defining the network monitor view components. * Object defining the network monitor view components.
*/ */
@ -95,6 +97,7 @@ let NetMonitorView = {
* Destroys the network monitor view. * Destroys the network monitor view.
*/ */
destroy: function() { destroy: function() {
this._isDestroyed = true;
this.Toolbar.destroy(); this.Toolbar.destroy();
this.RequestsMenu.destroy(); this.RequestsMenu.destroy();
this.NetworkDetails.destroy(); this.NetworkDetails.destroy();
@ -375,7 +378,6 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
this.allowFocusOnRightClick = true; this.allowFocusOnRightClick = true;
this.maintainSelectionVisible = true; this.maintainSelectionVisible = true;
this.widget.autoscrollWithAppendedItems = true;
this.widget.addEventListener("select", this._onSelect, false); this.widget.addEventListener("select", this._onSelect, false);
this.widget.addEventListener("swap", this._onSwap, false); this.widget.addEventListener("swap", this._onSwap, false);
@ -394,6 +396,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
this._onContextToggleRawHeadersCommand = this.toggleRawHeaders.bind(this); this._onContextToggleRawHeadersCommand = this.toggleRawHeaders.bind(this);
this._onContextPerfCommand = () => NetMonitorView.toggleFrontendMode(); this._onContextPerfCommand = () => NetMonitorView.toggleFrontendMode();
this._onReloadCommand = () => NetMonitorView.reloadPage(); this._onReloadCommand = () => NetMonitorView.reloadPage();
this._flushRequestsTask = new DeferredTask(this._flushRequests, REQUESTS_REFRESH_RATE);
this.sendCustomRequestEvent = this.sendCustomRequest.bind(this); this.sendCustomRequestEvent = this.sendCustomRequest.bind(this);
this.closeCustomRequestEvent = this.closeCustomRequest.bind(this); this.closeCustomRequestEvent = this.closeCustomRequest.bind(this);
@ -470,7 +473,10 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
$("#requests-menu-clear-button").removeEventListener("click", this.reqeustsMenuClearEvent, false); $("#requests-menu-clear-button").removeEventListener("click", this.reqeustsMenuClearEvent, false);
this.freetextFilterBox.removeEventListener("input", this.requestsFreetextFilterEvent, false); this.freetextFilterBox.removeEventListener("input", this.requestsFreetextFilterEvent, false);
this.freetextFilterBox.removeEventListener("command", this.requestsFreetextFilterEvent, false); this.freetextFilterBox.removeEventListener("command", this.requestsFreetextFilterEvent, false);
this.userInputTimer.cancel(); this.userInputTimer.cancel();
this._flushRequestsTask.disarm();
$("#network-request-popup").removeEventListener("popupshowing", this._onContextShowing, false); $("#network-request-popup").removeEventListener("popupshowing", this._onContextShowing, false);
$("#request-menu-context-newtab").removeEventListener("command", this._onContextNewTabCommand, false); $("#request-menu-context-newtab").removeEventListener("command", this._onContextNewTabCommand, false);
$("#request-menu-context-copy-url").removeEventListener("command", this._onContextCopyUrlCommand, false); $("#request-menu-context-copy-url").removeEventListener("command", this._onContextCopyUrlCommand, false);
@ -496,6 +502,8 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
*/ */
reset: function() { reset: function() {
this.empty(); this.empty();
this._addQueue = [];
this._updateQueue = [];
this._firstRequestStartedMillis = -1; this._firstRequestStartedMillis = -1;
this._lastRequestEndedMillis = -1; this._lastRequestEndedMillis = -1;
}, },
@ -503,7 +511,18 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
/** /**
* Specifies if this view may be updated lazily. * Specifies if this view may be updated lazily.
*/ */
lazyUpdate: true, _lazyUpdate: true,
get lazyUpdate() {
return this._lazyUpdate;
},
set lazyUpdate(value) {
this._lazyUpdate = value;
if (!value) {
this._flushRequests();
}
},
/** /**
* Adds a network request to this container. * Adds a network request to this container.
@ -523,47 +542,14 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
* Indicates if the result came from the browser cache * Indicates if the result came from the browser cache
*/ */
addRequest: function(aId, aStartedDateTime, aMethod, aUrl, aIsXHR, aFromCache) { addRequest: function(aId, aStartedDateTime, aMethod, aUrl, aIsXHR, aFromCache) {
// Convert the received date/time string to a unix timestamp. this._addQueue.push([aId, aStartedDateTime, aMethod, aUrl, aIsXHR, aFromCache]);
let unixTime = Date.parse(aStartedDateTime);
// Create the element node for the network request item. // Lazy updating is disabled in some tests.
let menuView = this._createMenuView(aMethod, aUrl); if (!this.lazyUpdate) {
return void this._flushRequests();
// Remember the first and last event boundaries.
this._registerFirstRequestStart(unixTime);
this._registerLastRequestEnd(unixTime);
// Append a network request item to this container.
let requestItem = this.push([menuView, aId], {
attachment: {
startedDeltaMillis: unixTime - this._firstRequestStartedMillis,
startedMillis: unixTime,
method: aMethod,
url: aUrl,
isXHR: aIsXHR,
fromCache: aFromCache
}
});
// Create a tooltip for the newly appended network request item.
let requestTooltip = requestItem.attachment.tooltip = new Tooltip(document, {
closeOnEvents: [{
emitter: $("#requests-menu-contents"),
event: "scroll",
useCapture: true
}]
});
$("#details-pane-toggle").disabled = false;
$("#requests-menu-empty-notice").hidden = true;
this.refreshSummary();
this.refreshZebra();
this.refreshTooltip(requestItem);
if (aId == this._preferredItemId) {
this.selectedItem = requestItem;
} }
this._flushRequestsTask.arm();
}, },
/** /**
@ -1326,30 +1312,80 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
* @param object aData * @param object aData
* An object containing several { key: value } tuples of network info. * An object containing several { key: value } tuples of network info.
* Supported keys are "httpVersion", "status", "statusText" etc. * Supported keys are "httpVersion", "status", "statusText" etc.
* @param function aCallback
* A function to call once the request has been updated in the view.
*/ */
updateRequest: function(aId, aData) { updateRequest: function(aId, aData, aCallback) {
// Prevent interference from zombie updates received after target closed. this._updateQueue.push([aId, aData, aCallback]);
if (NetMonitorView._isDestroyed) {
return;
}
this._updateQueue.push([aId, aData]);
// Lazy updating is disabled in some tests. // Lazy updating is disabled in some tests.
if (!this.lazyUpdate) { if (!this.lazyUpdate) {
return void this._flushRequests(); return void this._flushRequests();
} }
// Allow requests to settle down first.
setNamedTimeout( this._flushRequestsTask.arm();
"update-requests", REQUESTS_REFRESH_RATE, () => this._flushRequests());
}, },
/** /**
* Starts adding all queued additional information about network requests. * Starts adding all queued additional information about network requests.
*/ */
_flushRequests: function() { _flushRequests: function() {
// Prevent displaying any updates received after the target closed.
if (NetMonitorView._isDestroyed) {
return;
}
let widget = NetMonitorView.RequestsMenu.widget;
let isScrolledToBottom = widget.isScrolledToBottom();
for (let [id, startedDateTime, method, url, isXHR, fromCache] of this._addQueue) {
// Convert the received date/time string to a unix timestamp.
let unixTime = Date.parse(startedDateTime);
// Create the element node for the network request item.
let menuView = this._createMenuView(method, url);
// Remember the first and last event boundaries.
this._registerFirstRequestStart(unixTime);
this._registerLastRequestEnd(unixTime);
// Append a network request item to this container.
let requestItem = this.push([menuView, id], {
attachment: {
startedDeltaMillis: unixTime - this._firstRequestStartedMillis,
startedMillis: unixTime,
method: method,
url: url,
isXHR: isXHR,
fromCache: fromCache
}
});
// Create a tooltip for the newly appended network request item.
let requestTooltip = requestItem.attachment.tooltip = new Tooltip(document, {
closeOnEvents: [{
emitter: $("#requests-menu-contents"),
event: "scroll",
useCapture: true
}]
});
this.refreshTooltip(requestItem);
if (id == this._preferredItemId) {
this.selectedItem = requestItem;
}
window.emit(EVENTS.REQUEST_ADDED, id);
}
if (isScrolledToBottom && this._addQueue.length) {
widget.scrollToBottom();
}
// For each queued additional information packet, get the corresponding // For each queued additional information packet, get the corresponding
// request item in the view and update it based on the specified data. // request item in the view and update it based on the specified data.
for (let [id, data] of this._updateQueue) { for (let [id, data, callback] of this._updateQueue) {
let requestItem = this.getItemByValue(id); let requestItem = this.getItemByValue(id);
if (!requestItem) { if (!requestItem) {
// Packet corresponds to a dead request item, target navigated. // Packet corresponds to a dead request item, target navigated.
@ -1482,6 +1518,10 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
} }
} }
refreshNetworkDetailsPaneIfNecessary(requestItem); refreshNetworkDetailsPaneIfNecessary(requestItem);
if (callback) {
callback();
}
} }
/** /**
@ -1502,6 +1542,10 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
// We're done flushing all the requests, clear the update queue. // We're done flushing all the requests, clear the update queue.
this._updateQueue = []; this._updateQueue = [];
this._addQueue = [];
$("#details-pane-toggle").disabled = !this.itemCount;
$("#requests-menu-empty-notice").hidden = !!this.itemCount;
// Make sure all the requests are sorted and filtered. // Make sure all the requests are sorted and filtered.
// Freshly added requests may not yet contain all the information required // Freshly added requests may not yet contain all the information required
@ -1650,9 +1694,9 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
} }
case "responseContent": { case "responseContent": {
let { mimeType } = aItem.attachment; let { mimeType } = aItem.attachment;
let { text, encoding } = aValue.content;
if (mimeType.includes("image/")) { if (mimeType.includes("image/")) {
let { text, encoding } = aValue.content;
let responseBody = yield gNetwork.getString(text); let responseBody = yield gNetwork.getString(text);
let node = $(".requests-menu-icon", aItem.target); let node = $(".requests-menu-icon", aItem.target);
node.src = "data:" + mimeType + ";" + encoding + "," + responseBody; node.src = "data:" + mimeType + ";" + encoding + "," + responseBody;
@ -2158,6 +2202,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
_firstRequestStartedMillis: -1, _firstRequestStartedMillis: -1,
_lastRequestEndedMillis: -1, _lastRequestEndedMillis: -1,
_updateQueue: [], _updateQueue: [],
_addQueue: [],
_updateTimeout: null, _updateTimeout: null,
_resizeTimeout: null, _resizeTimeout: null,
_activeFilters: ["all"], _activeFilters: ["all"],

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

@ -95,6 +95,11 @@ let test = Task.async(function* () {
info("Waiting for NETWORK_EVENT"); info("Waiting for NETWORK_EVENT");
yield onNetworkEvent; yield onNetworkEvent;
if (!RequestsMenu.getItemAtIndex(0)) {
info("Waiting for the request to be added to the view")
yield monitor.panelWin.once(monitor.panelWin.EVENTS.REQUEST_ADDED);
}
ok(true, "Received NETWORK_EVENT. Selecting the item."); ok(true, "Received NETWORK_EVENT. Selecting the item.");
let item = RequestsMenu.getItemAtIndex(0); let item = RequestsMenu.getItemAtIndex(0);
RequestsMenu.selectedItem = item; RequestsMenu.selectedItem = item;

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

@ -240,20 +240,24 @@ function waitForNetworkEvents(aMonitor, aGetRequests, aPostRequests = 0) {
maybeResolve(event, actor); maybeResolve(event, actor);
} }
function maybeResolve(event, [actor, url]) { function maybeResolve(event, actor) {
info("> Network events progress: " + info("> Network events progress: " +
genericEvents + "/" + ((aGetRequests + aPostRequests) * 13) + ", " + genericEvents + "/" + ((aGetRequests + aPostRequests) * 13) + ", " +
postEvents + "/" + (aPostRequests * 2) + ", " + postEvents + "/" + (aPostRequests * 2) + ", " +
"got " + event + " for " + actor); "got " + event + " for " + actor);
let networkInfo =
panel.NetMonitorController.webConsoleClient.getNetworkRequest(actor)
let url = networkInfo.request.url;
updateProgressForURL(url, event); updateProgressForURL(url, event);
info("> Current state: " + JSON.stringify(progress, null, 2)); info("> Current state: " + JSON.stringify(progress, null, 2));
// There are 15 updates which need to be fired for a request to be // There are 15 updates which need to be fired for a request to be
// considered finished. The "requestPostData" packet isn't fired for // considered finished. The "requestPostData" packet isn't fired for
// non-POST requests. // non-POST requests.
if (genericEvents == (aGetRequests + aPostRequests) * 13 && if (genericEvents >= (aGetRequests + aPostRequests) * 13 &&
postEvents == aPostRequests * 2) { postEvents >= aPostRequests * 2) {
awaitedEventsToListeners.forEach(([e, l]) => panel.off(events[e], l)); awaitedEventsToListeners.forEach(([e, l]) => panel.off(events[e], l));
executeSoon(deferred.resolve); executeSoon(deferred.resolve);

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

@ -5,6 +5,9 @@
* Tests that the js call tree view renders the correct columns. * Tests that the js call tree view renders the correct columns.
*/ */
function* spawnTest() { function* spawnTest() {
// This test seems to take a long time to cleanup on Ubuntu VMs.
requestLongerTimeout(2);
let { panel } = yield initPerformance(SIMPLE_URL); let { panel } = yield initPerformance(SIMPLE_URL);
let { EVENTS, $, $$, DetailsView, JsCallTreeView } = panel.panelWin; let { EVENTS, $, $$, DetailsView, JsCallTreeView } = panel.panelWin;

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

@ -35,6 +35,7 @@ function* spawnTest() {
busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity
yield front.stopRecording(firstRecording); yield front.stopRecording(firstRecording);
info("The first recording is " + firstRecording.getDuration() + "ms long.");
is(firstRecordingStartTime, 0, is(firstRecordingStartTime, 0,
"The profiling start time should be 0 for the first recording."); "The profiling start time should be 0 for the first recording.");
@ -50,6 +51,8 @@ function* spawnTest() {
busyWait(WAIT_TIME); // allow the profiler module to sample more cpu activity busyWait(WAIT_TIME); // allow the profiler module to sample more cpu activity
yield front.stopRecording(secondRecording); yield front.stopRecording(secondRecording);
info("The second recording is " + secondRecording.getDuration() + "ms long.");
let secondRecordingProfile = secondRecording.getProfile(); let secondRecordingProfile = secondRecording.getProfile();
let secondRecordingSamples = secondRecordingProfile.threads[0].samples.data; let secondRecordingSamples = secondRecordingProfile.threads[0].samples.data;

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

@ -24,64 +24,63 @@ function testRecordings (win, expected) {
function* spawnTest() { function* spawnTest() {
loadFrameScripts(); loadFrameScripts();
let { target, toolbox, panel } = yield initPerformance(SIMPLE_URL); let { target, toolbox, panel } = yield initPerformance(SIMPLE_URL);
let win = panel.panelWin; let { EVENTS, PerformanceController, OverviewView, RecordingsView, WaterfallView } = panel.panelWin;
let { $, EVENTS, gFront, PerformanceController, OverviewView, RecordingsView, WaterfallView } = win;
info("Starting console.profile()..."); info("Starting console.profile()...");
yield consoleProfile(win); yield consoleProfile(panel.panelWin);
testRecordings(win, [C+S+R]); testRecordings(panel.panelWin, [C+S+R]);
info("Starting manual recording..."); info("Starting manual recording...");
yield startRecording(panel); yield startRecording(panel);
testRecordings(win, [C+R, R+S]); testRecordings(panel.panelWin, [C+R, R+S]);
info("Starting console.profile(\"3\")..."); info("Starting console.profile(\"3\")...");
yield consoleProfile(win, "3"); yield consoleProfile(panel.panelWin, "3");
testRecordings(win, [C+R, R+S, C+R]); testRecordings(panel.panelWin, [C+R, R+S, C+R]);
info("Starting console.profile(\"3\")..."); info("Starting console.profile(\"3\")...");
yield consoleProfile(win, "4"); yield consoleProfile(panel.panelWin, "4");
testRecordings(win, [C+R, R+S, C+R, C+R]); testRecordings(panel.panelWin, [C+R, R+S, C+R, C+R]);
info("Ending console.profileEnd()..."); info("Ending console.profileEnd()...");
yield consoleProfileEnd(win); yield consoleProfileEnd(panel.panelWin);
testRecordings(win, [C+R, R+S, C+R, C]); testRecordings(panel.panelWin, [C+R, R+S, C+R, C]);
ok(OverviewView.isRendering(), "still rendering overview with manual recorded selected."); ok(OverviewView.isRendering(), "still rendering overview with manual recorded selected.");
let onSelected = once(WaterfallView, EVENTS.WATERFALL_RENDERED); let onSelected = once(WaterfallView, EVENTS.WATERFALL_RENDERED);
info("Select last recording..."); info("Select last recording...");
RecordingsView.selectedIndex = 3; RecordingsView.selectedIndex = 3;
yield onSelected; yield onSelected;
testRecordings(win, [C+R, R, C+R, C+S]); testRecordings(panel.panelWin, [C+R, R, C+R, C+S]);
ok(!OverviewView.isRendering(), "stop rendering overview when selected completed recording."); ok(!OverviewView.isRendering(), "stop rendering overview when selected completed recording.");
info("Manually stop manual recording..."); info("Manually stop manual recording...");
yield stopRecording(panel); yield stopRecording(panel);
testRecordings(win, [C+R, S, C+R, C]); testRecordings(panel.panelWin, [C+R, S, C+R, C]);
ok(!OverviewView.isRendering(), "stop rendering overview when selected completed recording."); ok(!OverviewView.isRendering(), "stop rendering overview when selected completed recording.");
onSelected = once(PerformanceController, EVENTS.RECORDING_SELECTED); onSelected = once(PerformanceController, EVENTS.RECORDING_SELECTED);
info("Select first recording..."); info("Select first recording...");
RecordingsView.selectedIndex = 0; RecordingsView.selectedIndex = 0;
yield onSelected; yield onSelected;
testRecordings(win, [C+R+S, 0, C+R, C]); testRecordings(panel.panelWin, [C+R+S, 0, C+R, C]);
yield once(OverviewView, EVENTS.OVERVIEW_RENDERED); yield once(OverviewView, EVENTS.OVERVIEW_RENDERED);
ok(OverviewView.isRendering(), "should be rendering overview when selected recording in progress."); ok(OverviewView.isRendering(), "should be rendering overview when selected recording in progress.");
info("Ending console.profileEnd()..."); info("Ending console.profileEnd()...");
yield consoleProfileEnd(win); yield consoleProfileEnd(panel.panelWin);
testRecordings(win, [C+R+S, 0, C, C]); testRecordings(panel.panelWin, [C+R+S, 0, C, C]);
ok(OverviewView.isRendering(), "should still be rendering overview when selected recording in progress."); ok(OverviewView.isRendering(), "should still be rendering overview when selected recording in progress.");
info("Start one more manual recording..."); info("Start one more manual recording...");
yield startRecording(panel); yield startRecording(panel);
testRecordings(win, [C+R, 0, C, C, R+S]); testRecordings(panel.panelWin, [C+R, 0, C, C, R+S]);
ok(OverviewView.isRendering(), "should be rendering overview when selected recording in progress."); ok(OverviewView.isRendering(), "should be rendering overview when selected recording in progress.");
info("Stop manual recording..."); info("Stop manual recording...");
yield stopRecording(panel); yield stopRecording(panel);
testRecordings(win, [C+R, 0, C, C, S]); testRecordings(panel.panelWin, [C+R, 0, C, C, S]);
ok(!OverviewView.isRendering(), "stop rendering overview when selected completed recording."); ok(!OverviewView.isRendering(), "stop rendering overview when selected completed recording.");
info("Ending console.profileEnd()..."); info("Ending console.profileEnd()...");
yield consoleProfileEnd(win); yield consoleProfileEnd(panel.panelWin);
testRecordings(win, [C, 0, C, C, S]); testRecordings(panel.panelWin, [C, 0, C, C, S]);
ok(!OverviewView.isRendering(), "stop rendering overview when selected completed recording."); ok(!OverviewView.isRendering(), "stop rendering overview when selected completed recording.");
yield teardown(panel); yield teardown(panel);

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

@ -7,9 +7,8 @@
function* spawnTest() { function* spawnTest() {
let { target, panel } = yield initPerformance(SIMPLE_URL); let { target, panel } = yield initPerformance(SIMPLE_URL);
let { $, $$, EVENTS, PerformanceController, OverviewView, WaterfallView } = panel.panelWin; let { $, $$, PerformanceController, WaterfallView } = panel.panelWin;
let { L10N } = devtools.require("devtools/performance/global"); let { L10N } = devtools.require("devtools/performance/global");
let { TIMELINE_BLUEPRINT } = devtools.require("devtools/performance/markers");
let { getMarkerLabel } = devtools.require("devtools/performance/marker-utils"); let { getMarkerLabel } = devtools.require("devtools/performance/marker-utils");
// Hijack the markers massaging part of creating the waterfall view, // Hijack the markers massaging part of creating the waterfall view,
@ -34,38 +33,35 @@ function* spawnTest() {
yield stopRecording(panel); yield stopRecording(panel);
ok(true, "Recording has ended."); ok(true, "Recording has ended.");
// Select everything
let timeline = OverviewView.graphs.get("timeline");
let rerendered = WaterfallView.once(EVENTS.WATERFALL_RENDERED);
timeline.setSelection({ start: 0, end: timeline.width })
yield rerendered;
let bars = $$(".waterfall-marker-bar"); let bars = $$(".waterfall-marker-bar");
let markers = PerformanceController.getCurrentRecording().getMarkers(); let markers = PerformanceController.getCurrentRecording().getMarkers();
ok(bars.length > 2, "Got at least 3 markers (1)"); ok(bars.length > 2, "Got at least 3 markers (1)");
ok(markers.length > 2, "Got at least 3 markers (2)"); ok(markers.length > 2, "Got at least 3 markers (2)");
let toMs = ms => L10N.getFormatStrWithNumbers("timeline.tick", ms);
for (let i = 0; i < bars.length; i++) { for (let i = 0; i < bars.length; i++) {
let bar = bars[i]; let bar = bars[i];
let m = markers[i]; let mkr = markers[i];
EventUtils.sendMouseEvent({ type: "mousedown" }, bar); EventUtils.sendMouseEvent({ type: "mousedown" }, bar);
is($("#waterfall-details .marker-details-type").getAttribute("value"), getMarkerLabel(m), let type = $(".marker-details-type").getAttribute("value");
"Sidebar title matches markers name.");
let tooltip = $(".marker-details-duration").getAttribute("tooltiptext"); let tooltip = $(".marker-details-duration").getAttribute("tooltiptext");
let duration = $(".marker-details-duration .marker-details-labelvalue").getAttribute("value"); let duration = $(".marker-details-duration .marker-details-labelvalue").getAttribute("value");
let toMs = ms => L10N.getFormatStrWithNumbers("timeline.tick", ms); info("Current marker data: " + mkr.toSource());
info("Current marker output: " + $("#waterfall-details").innerHTML);
is(type, getMarkerLabel(mkr), "Sidebar title matches markers name.");
// Values are rounded. We don't use a strict equality. // Values are rounded. We don't use a strict equality.
is(toMs(m.end - m.start), duration, "Sidebar duration is valid."); is(toMs(mkr.end - mkr.start), duration, "Sidebar duration is valid.");
// For some reason, anything that creates "→" here turns it into a "â" for some reason. // For some reason, anything that creates "→" here turns it into a "â" for some reason.
// So just check that start and end time are in there somewhere. // So just check that start and end time are in there somewhere.
ok(tooltip.indexOf(toMs(m.start)) !== -1, "Tooltip has start time."); ok(tooltip.indexOf(toMs(mkr.start)) !== -1, "Tooltip has start time.");
ok(tooltip.indexOf(toMs(m.end)) !== -1, "Tooltip has end time."); ok(tooltip.indexOf(toMs(mkr.end)) !== -1, "Tooltip has end time.");
} }
yield teardown(panel); yield teardown(panel);

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

@ -10,12 +10,9 @@ const Cu = Components.utils;
Cu.import("resource:///modules/devtools/ViewHelpers.jsm"); Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
Cu.import("resource://gre/modules/devtools/event-emitter.js"); Cu.import("resource://gre/modules/devtools/event-emitter.js");
const {DeferredTask} = Cu.import("resource://gre/modules/DeferredTask.jsm", {});
this.EXPORTED_SYMBOLS = ["SideMenuWidget"]; this.EXPORTED_SYMBOLS = ["SideMenuWidget"];
const SCROLL_FREQUENCY = 16;
/** /**
* A simple side menu, with the ability of grouping menu items. * A simple side menu, with the ability of grouping menu items.
* *
@ -81,14 +78,6 @@ SideMenuWidget.prototype = {
*/ */
groupSortPredicate: function(a, b) a.localeCompare(b), groupSortPredicate: function(a, b) a.localeCompare(b),
/**
* Specifies that the container viewport should be "stuck" to the
* bottom. That is, the container is automatically scrolled down to
* keep appended items visible, but only when the scroll position is
* already at the bottom.
*/
autoscrollWithAppendedItems: false,
/** /**
* Inserts an item in this container at the specified index, optionally * Inserts an item in this container at the specified index, optionally
* grouping by name. * grouping by name.
@ -106,29 +95,10 @@ SideMenuWidget.prototype = {
* The element associated with the displayed item. * The element associated with the displayed item.
*/ */
insertItemAt: function(aIndex, aContents, aAttachment={}) { insertItemAt: function(aIndex, aContents, aAttachment={}) {
// Maintaining scroll position at the bottom when a new item is inserted
// depends on several factors (the order of testing is important to avoid
// needlessly expensive operations that may cause reflows):
let maintainScrollAtBottom =
// 1. The behavior should be enabled,
this.autoscrollWithAppendedItems &&
// 2. There shouldn't currently be any selected item in the list.
!this._selectedItem &&
// 3. The new item should be appended at the end of the list.
(aIndex < 0 || aIndex >= this._orderedMenuElementsArray.length) &&
// 4. We aren't waiting for a scroll to happen.
(!this._scrollToBottomTask || !this._scrollToBottomTask.isArmed) &&
// 5. The list should already be scrolled at the bottom.
this.isScrolledToBottom();
let group = this._getMenuGroupForName(aAttachment.group); let group = this._getMenuGroupForName(aAttachment.group);
let item = this._getMenuItemForGroup(group, aContents, aAttachment); let item = this._getMenuItemForGroup(group, aContents, aAttachment);
let element = item.insertSelfAt(aIndex); let element = item.insertSelfAt(aIndex);
if (maintainScrollAtBottom) {
this.scrollToBottom();
}
return element; return element;
}, },
@ -158,30 +128,8 @@ SideMenuWidget.prototype = {
* If the user scrolls in the meantime, cancel this operation. * If the user scrolls in the meantime, cancel this operation.
*/ */
scrollToBottom: function() { scrollToBottom: function() {
// Lazily attach this functionality to the object, so it won't get this._list.scrollTop = this._list.scrollHeight;
// created unless if this scrollToBottom behavior is needed. this.emit("scroll-to-bottom");
if (!this._scrollToBottomTask) {
// The scroll event fires asynchronously, so we need to keep a bit to
// distinguish between user-initiated events and scrollTop assignment.
let ignoreNextScroll = false;
this._scrollToBottomTask = new DeferredTask(() => {
ignoreNextScroll = true;
this._list.scrollTop = this._list.scrollHeight;
this.emit("scroll-to-bottom");
}, SCROLL_FREQUENCY);
// On a user scroll, cancel any pending calls to the scroll function.
this._list.addEventListener("scroll", () => {
if (!ignoreNextScroll && this._scrollToBottomTask.isArmed &&
!this.isScrolledToBottom()) {
this._scrollToBottomTask.disarm();
}
ignoreNextScroll = false;
}, true);
}
this._scrollToBottomTask.arm();
}, },
/** /**

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

@ -17,6 +17,7 @@ add_task(function*() {
let target = TargetFactory.forTab(tab); let target = TargetFactory.forTab(tab);
let toolbox = yield gDevTools.showToolbox(target, "netmonitor"); let toolbox = yield gDevTools.showToolbox(target, "netmonitor");
let netmonitor = toolbox.getPanel("netmonitor"); let netmonitor = toolbox.getPanel("netmonitor");
netmonitor._view.RequestsMenu.lazyUpdate = false;
info("Navigating to test page"); info("Navigating to test page");
yield navigateTo(TEST_URL); yield navigateTo(TEST_URL);

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

@ -47,7 +47,7 @@
</div> </div>
<!-- TODO : Bug 1165122 : Show this button by default --> <!-- TODO : Bug 1165122 : Show this button by default -->
<button hidden="true" id="ruleview-add-rule-button" title="&addRuleButtonTooltip;" class="devtools-button"></button> <button hidden="true" id="ruleview-add-rule-button" title="&addRuleButtonTooltip;" class="devtools-button"></button>
<button id="pseudo-class-panel-toggle" class="devtools-button"></button> <button id="pseudo-class-panel-toggle" title="&togglePseudoClassPanel;" class="devtools-button"></button>
</div> </div>
<div id="pseudo-class-panel" class="devtools-toolbar" hidden="true"> <div id="pseudo-class-panel" class="devtools-toolbar" hidden="true">
<label><input id="pseudo-hover-toggle" type="checkbox" value=":hover" />:hover</label> <label><input id="pseudo-hover-toggle" type="checkbox" value=":hover" />:hover</label>

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

@ -63,6 +63,8 @@ function loadDocument(browser) {
function testNetmonitor(toolbox) { function testNetmonitor(toolbox) {
let monitor = toolbox.getCurrentPanel(); let monitor = toolbox.getCurrentPanel();
let { RequestsMenu } = monitor.panelWin.NetMonitorView; let { RequestsMenu } = monitor.panelWin.NetMonitorView;
RequestsMenu.lazyUpdate = false;
is(RequestsMenu.itemCount, 1, "Network request appears in the network panel"); is(RequestsMenu.itemCount, 1, "Network request appears in the network panel");
let item = RequestsMenu.getItemAtIndex(0); let item = RequestsMenu.getItemAtIndex(0);

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

@ -63,6 +63,7 @@ function loadDocument(browser) {
function testNetmonitor(toolbox) { function testNetmonitor(toolbox) {
let monitor = toolbox.getCurrentPanel(); let monitor = toolbox.getCurrentPanel();
let { RequestsMenu } = monitor.panelWin.NetMonitorView; let { RequestsMenu } = monitor.panelWin.NetMonitorView;
RequestsMenu.lazyUpdate = false;
is(RequestsMenu.itemCount, 1, "Network request appears in the network panel"); is(RequestsMenu.itemCount, 1, "Network request appears in the network panel");
let item = RequestsMenu.getItemAtIndex(0); let item = RequestsMenu.getItemAtIndex(0);

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

@ -489,6 +489,7 @@
@RESPATH@/browser/components/ChromeProfileMigrator.js @RESPATH@/browser/components/ChromeProfileMigrator.js
@RESPATH@/browser/components/FirefoxProfileMigrator.js @RESPATH@/browser/components/FirefoxProfileMigrator.js
#ifdef XP_WIN #ifdef XP_WIN
@RESPATH@/browser/components/360seProfileMigrator.js
@RESPATH@/browser/components/IEProfileMigrator.js @RESPATH@/browser/components/IEProfileMigrator.js
@RESPATH@/browser/components/SafariProfileMigrator.js @RESPATH@/browser/components/SafariProfileMigrator.js
#endif #endif

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

@ -27,6 +27,11 @@
- tree. --> - tree. -->
<!ENTITY selectedElementLabel "Selected element:"> <!ENTITY selectedElementLabel "Selected element:">
<!-- LOCALIZATION NOTE (togglePseudoClassPanel): This is the tooltip
- shown when hovering over the `Toggle Pseudo Class Panel` button in the
- rule view toolbar. -->
<!ENTITY togglePseudoClassPanel "Toggle pseudo-classes">
<!-- LOCALIZATION NOTE (noPropertiesFound): In the case where there are no CSS <!-- LOCALIZATION NOTE (noPropertiesFound): In the case where there are no CSS
- properties to display e.g. due to search criteria this message is - properties to display e.g. due to search criteria this message is
- displayed. --> - displayed. -->

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

@ -21,6 +21,9 @@
<!ENTITY toolboxZoomReset.key "0"> <!ENTITY toolboxZoomReset.key "0">
<!ENTITY toolboxReload.key "r"> <!ENTITY toolboxReload.key "r">
<!-- This key is used with the accel+shift modifiers to minimize the toolbox -->
<!ENTITY toolboxToggleMinimize.key "U">
<!-- LOCALIZATION NOTE (toolboxFramesButton): This is the label for <!-- LOCALIZATION NOTE (toolboxFramesButton): This is the label for
- the iframes menu list that appears only when the document has some. - the iframes menu list that appears only when the document has some.
- It allows you to switch the context of the whole toolbox. --> - It allows you to switch the context of the whole toolbox. -->

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

@ -19,6 +19,8 @@
<!ENTITY importFromChrome.accesskey "C"> <!ENTITY importFromChrome.accesskey "C">
<!ENTITY importFromFirefox.label "Firefox"> <!ENTITY importFromFirefox.label "Firefox">
<!ENTITY importFromFirefox.accesskey "X"> <!ENTITY importFromFirefox.accesskey "X">
<!ENTITY importFrom360se.label "360 Secure Browser">
<!ENTITY importFrom360se.accesskey "3">
<!ENTITY noMigrationSources.label "No programs that contain bookmarks, history or password data could be found."> <!ENTITY noMigrationSources.label "No programs that contain bookmarks, history or password data could be found.">

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

@ -9,6 +9,7 @@ sourceNameIE=Internet Explorer
sourceNameSafari=Safari sourceNameSafari=Safari
sourceNameChrome=Google Chrome sourceNameChrome=Google Chrome
sourceNameFirefox=Mozilla Firefox sourceNameFirefox=Mozilla Firefox
sourceName360se=360 Secure Browser
importedBookmarksFolder=From %S importedBookmarksFolder=From %S
@ -20,34 +21,41 @@ importedSafariReadingList=Reading List (From Safari)
1_ie=Internet Options 1_ie=Internet Options
1_safari=Preferences 1_safari=Preferences
1_chrome=Preferences 1_chrome=Preferences
1_360se=Preferences
2_ie=Cookies 2_ie=Cookies
2_safari=Cookies 2_safari=Cookies
2_chrome=Cookies 2_chrome=Cookies
2_firefox=Cookies 2_firefox=Cookies
2_360se=Cookies
4_ie=Browsing History 4_ie=Browsing History
4_safari=Browsing History 4_safari=Browsing History
4_chrome=Browsing History 4_chrome=Browsing History
4_firefox_history_and_bookmarks=Browsing History and Bookmarks 4_firefox_history_and_bookmarks=Browsing History and Bookmarks
4_360se=Browsing History
8_ie=Saved Form History 8_ie=Saved Form History
8_safari=Saved Form History 8_safari=Saved Form History
8_chrome=Saved Form History 8_chrome=Saved Form History
8_firefox=Saved Form History 8_firefox=Saved Form History
8_360se=Saved Form History
16_ie=Saved Passwords 16_ie=Saved Passwords
16_safari=Saved Passwords 16_safari=Saved Passwords
16_chrome=Saved Passwords 16_chrome=Saved Passwords
16_firefox=Saved Passwords 16_firefox=Saved Passwords
16_360se=Saved Passwords
32_ie=Favorites 32_ie=Favorites
32_safari=Bookmarks 32_safari=Bookmarks
32_chrome=Bookmarks 32_chrome=Bookmarks
32_360se=Bookmarks
64_ie=Other Data 64_ie=Other Data
64_safari=Other Data 64_safari=Other Data
64_chrome=Other Data 64_chrome=Other Data
64_firefox_other=Other Data 64_firefox_other=Other Data
64_360se=Other Data
128_firefox=Windows and Tabs 128_firefox=Windows and Tabs

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

@ -46,6 +46,7 @@
<!ENTITY font.langGroup.gurmukhi "Gurmukhi"> <!ENTITY font.langGroup.gurmukhi "Gurmukhi">
<!ENTITY font.langGroup.khmer "Khmer"> <!ENTITY font.langGroup.khmer "Khmer">
<!ENTITY font.langGroup.malayalam "Malayalam"> <!ENTITY font.langGroup.malayalam "Malayalam">
<!ENTITY font.langGroup.math "Mathematics">
<!ENTITY font.langGroup.oriya "Oriya"> <!ENTITY font.langGroup.oriya "Oriya">
<!ENTITY font.langGroup.telugu "Telugu"> <!ENTITY font.langGroup.telugu "Telugu">
<!ENTITY font.langGroup.kannada "Kannada"> <!ENTITY font.langGroup.kannada "Kannada">

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

@ -22,6 +22,8 @@
<!ENTITY locbar.bookmarks.accesskey "k"> <!ENTITY locbar.bookmarks.accesskey "k">
<!ENTITY locbar.openpage.label "Open tabs"> <!ENTITY locbar.openpage.label "Open tabs">
<!ENTITY locbar.openpage.accesskey "O"> <!ENTITY locbar.openpage.accesskey "O">
<!ENTITY locbar.searches.label "Related searches from the default search engine">
<!ENTITY locbar.searches.accesskey "d">
<!ENTITY acceptCookies.label "Accept cookies from sites"> <!ENTITY acceptCookies.label "Accept cookies from sites">
<!ENTITY acceptCookies.accesskey "A"> <!ENTITY acceptCookies.accesskey "A">

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

@ -121,6 +121,11 @@
margin-top: @windowButtonMarginTop@; margin-top: @windowButtonMarginTop@;
} }
#main-window:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-buttonbox-container:-moz-lwtheme,
#main-window:not([tabsintitlebar]) > #titlebar > #titlebar-content > #titlebar-secondary-buttonbox > #titlebar-fullscreen-button:-moz-lwtheme {
margin-top: 3px;
}
#main-window[customize-entered] > #titlebar { #main-window[customize-entered] > #titlebar {
-moz-appearance: none; -moz-appearance: none;
} }

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

@ -68,6 +68,18 @@
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#bookmark-disabled"); list-style-image: url("chrome://browser/skin/content-contextmenu.svg#bookmark-disabled");
} }
#context-bookmarkpage[starred=true] {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#bookmarked");
}
#context-bookmarkpage[starred=true][_moz-menuactive=true] {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#bookmarked-active");
}
#context-bookmarkpage[starred=true][disabled=true] {
list-style-image: url("chrome://browser/skin/content-contextmenu.svg#bookmarked-disabled");
}
#context-back:-moz-locale-dir(rtl), #context-back:-moz-locale-dir(rtl),
#context-forward:-moz-locale-dir(rtl), #context-forward:-moz-locale-dir(rtl),
#context-reload:-moz-locale-dir(rtl) { #context-reload:-moz-locale-dir(rtl) {

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

@ -384,7 +384,8 @@ static const char kPermissionChangeNotification[] = PERM_CHANGE_NOTIFICATION;
NS_IMPL_ISUPPORTS(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference) NS_IMPL_ISUPPORTS(nsPermissionManager, nsIPermissionManager, nsIObserver, nsISupportsWeakReference)
nsPermissionManager::nsPermissionManager() nsPermissionManager::nsPermissionManager()
: mLargestID(0) : mMemoryOnlyDB(false)
, mLargestID(0)
, mIsShuttingDown(false) , mIsShuttingDown(false)
{ {
} }
@ -426,6 +427,10 @@ nsPermissionManager::Init()
{ {
nsresult rv; nsresult rv;
// If the 'permissions.memory_only' pref is set to true, then don't write any
// permission settings to disk, but keep them in a memory-only database.
mMemoryOnlyDB = mozilla::Preferences::GetBool("permissions.memory_only", false);
mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv); mObserverService = do_GetService("@mozilla.org/observer-service;1", &rv);
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
mObserverService->AddObserver(this, "profile-before-change", true); mObserverService->AddObserver(this, "profile-before-change", true);
@ -457,6 +462,23 @@ nsPermissionManager::RefreshPermission() {
return NS_OK; return NS_OK;
} }
nsresult
nsPermissionManager::OpenDatabase(nsIFile* aPermissionsFile)
{
nsresult rv;
nsCOMPtr<mozIStorageService> storage = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
if (!storage) {
return NS_ERROR_UNEXPECTED;
}
// cache a connection to the hosts database
if (mMemoryOnlyDB) {
rv = storage->OpenSpecialDatabase("memory", getter_AddRefs(mDBConn));
} else {
rv = storage->OpenDatabase(aPermissionsFile, getter_AddRefs(mDBConn));
}
return rv;
}
nsresult nsresult
nsPermissionManager::InitDB(bool aRemoveFile) nsPermissionManager::InitDB(bool aRemoveFile)
{ {
@ -480,13 +502,10 @@ nsPermissionManager::InitDB(bool aRemoveFile)
} }
} }
nsCOMPtr<mozIStorageService> storage = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID); rv = OpenDatabase(permissionsFile);
if (!storage) if (rv == NS_ERROR_UNEXPECTED) {
return NS_ERROR_UNEXPECTED; return rv;
} else if (rv == NS_ERROR_FILE_CORRUPTED) {
// cache a connection to the hosts database
rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
if (rv == NS_ERROR_FILE_CORRUPTED) {
LogToConsole(NS_LITERAL_STRING("permissions.sqlite is corrupted! Try again!")); LogToConsole(NS_LITERAL_STRING("permissions.sqlite is corrupted! Try again!"));
// Add telemetry probe // Add telemetry probe
@ -497,7 +516,7 @@ nsPermissionManager::InitDB(bool aRemoveFile)
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
LogToConsole(NS_LITERAL_STRING("Corrupted permissions.sqlite has been removed.")); LogToConsole(NS_LITERAL_STRING("Corrupted permissions.sqlite has been removed."));
rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn)); rv = OpenDatabase(permissionsFile);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
LogToConsole(NS_LITERAL_STRING("OpenDatabase to permissions.sqlite is successful!")); LogToConsole(NS_LITERAL_STRING("OpenDatabase to permissions.sqlite is successful!"));
} }
@ -515,7 +534,7 @@ nsPermissionManager::InitDB(bool aRemoveFile)
// Add telemetry probe // Add telemetry probe
mozilla::Telemetry::Accumulate(mozilla::Telemetry::DEFECTIVE_PERMISSIONS_SQL_REMOVED, 1); mozilla::Telemetry::Accumulate(mozilla::Telemetry::DEFECTIVE_PERMISSIONS_SQL_REMOVED, 1);
rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn)); rv = OpenDatabase(permissionsFile);
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);
LogToConsole(NS_LITERAL_STRING("OpenDatabase to permissions.sqlite is successful!")); LogToConsole(NS_LITERAL_STRING("OpenDatabase to permissions.sqlite is successful!"));

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

@ -234,6 +234,7 @@ private:
bool aExactHostMatch, bool aExactHostMatch,
bool aIncludingSession); bool aIncludingSession);
nsresult OpenDatabase(nsIFile* permissionsFile);
nsresult InitDB(bool aRemoveFile); nsresult InitDB(bool aRemoveFile);
nsresult CreateTable(); nsresult CreateTable();
nsresult Import(); nsresult Import();
@ -326,6 +327,8 @@ private:
nsCOMPtr<mozIStorageAsyncStatement> mStmtDelete; nsCOMPtr<mozIStorageAsyncStatement> mStmtDelete;
nsCOMPtr<mozIStorageAsyncStatement> mStmtUpdate; nsCOMPtr<mozIStorageAsyncStatement> mStmtUpdate;
bool mMemoryOnlyDB;
nsTHashtable<PermissionHashKey> mPermissionTable; nsTHashtable<PermissionHashKey> mPermissionTable;
// a unique, monotonically increasing id used to identify each database entry // a unique, monotonically increasing id used to identify each database entry
int64_t mLargestID; int64_t mLargestID;

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

@ -221,6 +221,9 @@ class RefTest(object):
# And for about:newtab content fetch and pings. # And for about:newtab content fetch and pings.
prefs['browser.newtabpage.directory.source'] = 'data:application/json,{"reftest":1}' prefs['browser.newtabpage.directory.source'] = 'data:application/json,{"reftest":1}'
prefs['browser.newtabpage.directory.ping'] = '' prefs['browser.newtabpage.directory.ping'] = ''
# Only allow add-ons from the profile and app and allow foreign injection
prefs["extensions.enabledScopes"] = 5;
prefs["extensions.autoDisableScopes"] = 0;
# Allow unsigned add-ons # Allow unsigned add-ons
prefs['xpinstall.signatures.required'] = False prefs['xpinstall.signatures.required'] = False

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

@ -1773,7 +1773,7 @@ public class GeckoAppShell
String[] split = output.split("\\s+"); String[] split = output.split("\\s+");
if (split.length <= pidColumn || split.length <= nameColumn) if (split.length <= pidColumn || split.length <= nameColumn)
continue; continue;
Integer pid = new Integer(split[pidColumn]); final Integer pid = Integer.valueOf(split[pidColumn]);
String name = pidNameMap.get(pid); String name = pidNameMap.get(pid);
if (name == null) { if (name == null) {
name = getAppNameByPID(pid.intValue()); name = getAppNameByPID(pid.intValue());

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

@ -5,11 +5,13 @@
package org.mozilla.gecko; package org.mozilla.gecko;
import android.util.Log;
import org.json.JSONObject; import org.json.JSONObject;
import android.text.TextUtils; import android.text.TextUtils;
public class SiteIdentity { public class SiteIdentity {
private final String LOGTAG = "GeckoSiteIdentity";
private SecurityMode mSecurityMode; private SecurityMode mSecurityMode;
private MixedMode mMixedMode; private MixedMode mMixedMode;
private TrackingMode mTrackingMode; private TrackingMode mTrackingMode;
@ -17,7 +19,8 @@ public class SiteIdentity {
private String mOwner; private String mOwner;
private String mSupplemental; private String mSupplemental;
private String mVerifier; private String mVerifier;
private String mEncrypted; private boolean mEncrypted;
private String mOrigin;
// The order of the items here relate to image levels in // The order of the items here relate to image levels in
// site_security_level.xml // site_security_level.xml
@ -124,11 +127,12 @@ public class SiteIdentity {
public void resetIdentity() { public void resetIdentity() {
mSecurityMode = SecurityMode.UNKNOWN; mSecurityMode = SecurityMode.UNKNOWN;
mOrigin = null;
mHost = null; mHost = null;
mOwner = null; mOwner = null;
mSupplemental = null; mSupplemental = null;
mVerifier = null; mVerifier = null;
mEncrypted = null; mEncrypted = false;
} }
public void reset() { public void reset() {
@ -166,12 +170,14 @@ public class SiteIdentity {
} }
try { try {
mOrigin = identityData.getString("origin");
mHost = identityData.getString("host"); mHost = identityData.getString("host");
mOwner = identityData.optString("owner", null); mOwner = identityData.optString("owner", null);
mSupplemental = identityData.optString("supplemental", null); mSupplemental = identityData.optString("supplemental", null);
mVerifier = identityData.getString("verifier"); mVerifier = identityData.getString("verifier");
mEncrypted = identityData.getString("encrypted"); mEncrypted = identityData.optBoolean("encrypted", false);
} catch (Exception e) { } catch (Exception e) {
Log.e(LOGTAG, "Error fetching Site identity host info", e);
resetIdentity(); resetIdentity();
} }
} catch (Exception e) { } catch (Exception e) {
@ -183,6 +189,10 @@ public class SiteIdentity {
return mSecurityMode; return mSecurityMode;
} }
public String getOrigin() {
return mOrigin;
}
public String getHost() { public String getHost() {
return mHost; return mHost;
} }
@ -199,7 +209,7 @@ public class SiteIdentity {
return mVerifier; return mVerifier;
} }
public String getEncrypted() { public boolean getEncrypted() {
return mEncrypted; return mEncrypted;
} }

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

@ -452,7 +452,6 @@ size. -->
<!ENTITY button_clear_data "Clear data"> <!ENTITY button_clear_data "Clear data">
<!ENTITY button_set "Set"> <!ENTITY button_set "Set">
<!ENTITY button_clear "Clear"> <!ENTITY button_clear "Clear">
<!ENTITY button_remember "Remember">
<!ENTITY button_copy "Copy"> <!ENTITY button_copy "Copy">
<!ENTITY firstrun_panel_title_welcome "Welcome"> <!ENTITY firstrun_panel_title_welcome "Welcome">
@ -548,6 +547,7 @@ just addresses the organization to follow, e.g. "This site is run by " -->
<!ENTITY identity_run_by "which is run by"> <!ENTITY identity_run_by "which is run by">
<!ENTITY identity_no_info "This website does not supply identity information."> <!ENTITY identity_no_info "This website does not supply identity information.">
<!ENTITY identity_not_encrypted "Your connection to this website is not encrypted."> <!ENTITY identity_not_encrypted "Your connection to this website is not encrypted.">
<!ENTITY identity_encrypted "Encrypted">
<!-- Mixed content notifications in site identity popup --> <!-- Mixed content notifications in site identity popup -->
<!ENTITY loaded_mixed_content_message "This page is displaying content that isn\'t secure."> <!ENTITY loaded_mixed_content_message "This page is displaying content that isn\'t secure.">

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

До

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

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

До

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

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

До

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

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

До

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

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

До

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

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

До

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

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

До

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

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

До

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

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

До

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

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

До

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

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

До

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

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

До

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

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

До

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

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

До

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

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

До

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

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

До

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

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

@ -14,8 +14,8 @@
android:padding="@dimen/doorhanger_padding"> android:padding="@dimen/doorhanger_padding">
<ImageView android:id="@+id/larry" <ImageView android:id="@+id/larry"
android:layout_width="wrap_content" android:layout_width="@dimen/doorhanger_icon_size"
android:layout_height="wrap_content" android:layout_height="@dimen/doorhanger_icon_size"
android:src="@drawable/larry" android:src="@drawable/larry"
android:paddingRight="@dimen/doorhanger_padding"/> android:paddingRight="@dimen/doorhanger_padding"/>
@ -32,6 +32,20 @@
android:visibility="gone" android:visibility="gone"
android:orientation="vertical"> android:orientation="vertical">
<TextView android:id="@+id/site_identity_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/doorhanger_section_padding_small"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light"/>
<TextView android:id="@+id/site_identity_encrypted"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/doorhanger_section_padding_small"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Bold"
android:textColor="@color/affirmative_green"
android:text="@string/identity_encrypted"/>
<TextView android:layout_width="wrap_content" <TextView android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light" android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light"
@ -70,7 +84,8 @@
android:textColor="@color/link_blue" android:textColor="@color/link_blue"
android:layout_marginTop="@dimen/doorhanger_section_padding_large" android:layout_marginTop="@dimen/doorhanger_section_padding_large"
android:layout_marginBottom="@dimen/doorhanger_padding" android:layout_marginBottom="@dimen/doorhanger_padding"
android:text="@string/contextmenu_site_settings"/> android:text="@string/contextmenu_site_settings"
android:visibility="gone"/>
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

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

@ -6,6 +6,7 @@
<resources> <resources>
<!-- Fennec color palette (bug 1127517) --> <!-- Fennec color palette (bug 1127517) -->
<color name="fennec_ui_orange">#FF9500</color> <color name="fennec_ui_orange">#FF9500</color>
<color name="affirmative_green">#6FBE4A</color>
<color name="action_orange">#E66000</color> <color name="action_orange">#E66000</color>
<color name="action_orange_pressed">#DC5600</color> <color name="action_orange_pressed">#DC5600</color>
<color name="link_blue">#0096DD</color> <color name="link_blue">#0096DD</color>

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

@ -427,6 +427,10 @@
<item name="android:textSize">16dp</item> <item name="android:textSize">16dp</item>
</style> </style>
<style name="TextAppearance.DoorHanger.Medium.Bold">
<item name="android:fontFamily">sans-serif-medium</item>
</style>
<style name="TextAppearance.DoorHanger.Medium.Light"> <style name="TextAppearance.DoorHanger.Medium.Light">
<item name="android:fontFamily">sans-serif-light</item> <item name="android:fontFamily">sans-serif-light</item>
</style> </style>

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

@ -385,7 +385,6 @@
<string name="button_clear">&button_clear;</string> <string name="button_clear">&button_clear;</string>
<string name="button_yes">&button_yes;</string> <string name="button_yes">&button_yes;</string>
<string name="button_no">&button_no;</string> <string name="button_no">&button_no;</string>
<string name="button_remember">&button_remember;</string>
<string name="button_copy">&button_copy;</string> <string name="button_copy">&button_copy;</string>
<string name="firstrun_panel_title_welcome">&firstrun_panel_title_welcome;</string> <string name="firstrun_panel_title_welcome">&firstrun_panel_title_welcome;</string>
@ -458,6 +457,7 @@
<string name="identity_run_by">&identity_run_by;</string> <string name="identity_run_by">&identity_run_by;</string>
<string name="identity_no_info">&identity_no_info;</string> <string name="identity_no_info">&identity_no_info;</string>
<string name="identity_not_encrypted">&identity_not_encrypted;</string> <string name="identity_not_encrypted">&identity_not_encrypted;</string>
<string name="identity_encrypted">&identity_encrypted;</string>
<string name="loaded_mixed_content_message">&loaded_mixed_content_message;</string> <string name="loaded_mixed_content_message">&loaded_mixed_content_message;</string>
<string name="blocked_mixed_content_message_top">&blocked_mixed_content_message_top;</string> <string name="blocked_mixed_content_message_top">&blocked_mixed_content_message_top;</string>
<string name="blocked_mixed_content_message_bottom">&blocked_mixed_content_message_bottom;</string> <string name="blocked_mixed_content_message_bottom">&blocked_mixed_content_message_bottom;</string>

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

@ -102,8 +102,8 @@ public class AccountPickler {
final SyncAccountParameters params, final boolean syncAutomatically) { final SyncAccountParameters params, final boolean syncAutomatically) {
final ExtendedJSONObject o = params.asJSON(); final ExtendedJSONObject o = params.asJSON();
o.put(Constants.JSON_KEY_SYNC_AUTOMATICALLY, Boolean.valueOf(syncAutomatically)); o.put(Constants.JSON_KEY_SYNC_AUTOMATICALLY, Boolean.valueOf(syncAutomatically));
o.put(Constants.JSON_KEY_VERSION, new Long(VERSION)); o.put(Constants.JSON_KEY_VERSION, Long.valueOf(VERSION));
o.put(Constants.JSON_KEY_TIMESTAMP, new Long(System.currentTimeMillis())); o.put(Constants.JSON_KEY_TIMESTAMP, Long.valueOf(System.currentTimeMillis()));
PrintStream ps = null; PrintStream ps = null;
try { try {

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

@ -6,6 +6,8 @@ package org.mozilla.gecko.toolbar;
import android.content.ClipData; import android.content.ClipData;
import android.content.ClipboardManager; import android.content.ClipboardManager;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.widget.Toast; import android.widget.Toast;
import org.json.JSONException; import org.json.JSONException;
import org.json.JSONArray; import org.json.JSONArray;
@ -21,6 +23,8 @@ import org.mozilla.gecko.SiteIdentity.MixedMode;
import org.mozilla.gecko.SiteIdentity.TrackingMode; import org.mozilla.gecko.SiteIdentity.TrackingMode;
import org.mozilla.gecko.Tab; import org.mozilla.gecko.Tab;
import org.mozilla.gecko.Tabs; import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.favicons.Favicons;
import org.mozilla.gecko.favicons.OnFaviconLoadedListener;
import org.mozilla.gecko.util.GeckoEventListener; import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.widget.AnchoredPopup; import org.mozilla.gecko.widget.AnchoredPopup;
@ -64,10 +68,13 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
private LinearLayout mIdentityKnownContainer; private LinearLayout mIdentityKnownContainer;
private LinearLayout mIdentityUnknownContainer; private LinearLayout mIdentityUnknownContainer;
private TextView mTitle;
private TextView mEncrypted;
private TextView mHost; private TextView mHost;
private TextView mOwnerLabel; private TextView mOwnerLabel;
private TextView mOwner; private TextView mOwner;
private TextView mVerifier; private TextView mVerifier;
private TextView mSiteSettingsLink;
private View mDivider; private View mDivider;
@ -82,6 +89,7 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
mContentButtonClickListener = new ContentNotificationButtonListener(); mContentButtonClickListener = new ContentNotificationButtonListener();
EventDispatcher.getInstance().registerGeckoThreadListener(this, "Doorhanger:Logins"); EventDispatcher.getInstance().registerGeckoThreadListener(this, "Doorhanger:Logins");
EventDispatcher.getInstance().registerGeckoThreadListener(this, "Permissions:CheckResult");
} }
@Override @Override
@ -101,20 +109,16 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
mIdentityUnknownContainer = mIdentityUnknownContainer =
(LinearLayout) mIdentity.findViewById(R.id.site_identity_unknown_container); (LinearLayout) mIdentity.findViewById(R.id.site_identity_unknown_container);
mTitle = (TextView) mIdentityKnownContainer.findViewById(R.id.site_identity_title);
mEncrypted = (TextView) mIdentityKnownContainer.findViewById(R.id.site_identity_encrypted);
mHost = (TextView) mIdentityKnownContainer.findViewById(R.id.host); mHost = (TextView) mIdentityKnownContainer.findViewById(R.id.host);
mOwnerLabel = (TextView) mIdentityKnownContainer.findViewById(R.id.owner_label); mOwnerLabel = (TextView) mIdentityKnownContainer.findViewById(R.id.owner_label);
mOwner = (TextView) mIdentityKnownContainer.findViewById(R.id.owner); mOwner = (TextView) mIdentityKnownContainer.findViewById(R.id.owner);
mVerifier = (TextView) mIdentityKnownContainer.findViewById(R.id.verifier); mVerifier = (TextView) mIdentityKnownContainer.findViewById(R.id.verifier);
mDivider = mIdentity.findViewById(R.id.divider_doorhanger); mDivider = mIdentity.findViewById(R.id.divider_doorhanger);
final TextView siteSettingsLink = (TextView) mIdentity.findViewById(R.id.site_settings_link); mSiteSettingsLink = (TextView) mIdentity.findViewById(R.id.site_settings_link);
siteSettingsLink.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Permissions:Get", null));
dismiss();
}
});
} }
private void updateIdentity(final SiteIdentity siteIdentity) { private void updateIdentity(final SiteIdentity siteIdentity) {
@ -128,6 +132,9 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
if (isIdentityKnown) { if (isIdentityKnown) {
updateIdentityInformation(siteIdentity); updateIdentityInformation(siteIdentity);
} }
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(
"Permissions:Check", null));
} }
@Override @Override
@ -145,6 +152,24 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
} catch (JSONException e) { } catch (JSONException e) {
Log.e(LOGTAG, "Error accessing logins in Doorhanger:Logins message", e); Log.e(LOGTAG, "Error accessing logins in Doorhanger:Logins message", e);
} }
} else if ("Permissions:CheckResult".equals(event)) {
final boolean hasPermissions = geckoObject.optBoolean("hasPermissions", false);
if (hasPermissions) {
mSiteSettingsLink.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Permissions:Get", null));
dismiss();
}
});
}
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
mSiteSettingsLink.setVisibility(hasPermissions ? View.VISIBLE : View.GONE);
}
});
} }
} }
@ -268,6 +293,25 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
} }
private void updateIdentityInformation(final SiteIdentity siteIdentity) { private void updateIdentityInformation(final SiteIdentity siteIdentity) {
final String host = siteIdentity.getHost();
mTitle.setText(host);
final String hostUrl = siteIdentity.getOrigin();
Favicons.getSizedFaviconForPageFromLocal(mContext, hostUrl, 32, new OnFaviconLoadedListener() {
@Override
public void onFaviconLoaded(String url, String faviconURL, Bitmap favicon) {
if (favicon == null) {
// If there is no favicon, clear the compound drawable.
mTitle.setCompoundDrawablesWithIntrinsicBounds(null, null, null, null);
} else {
mTitle.setCompoundDrawablesWithIntrinsicBounds(new BitmapDrawable(mContext.getResources(), favicon), null, null, null);
mTitle.setCompoundDrawablePadding((int) mContext.getResources().getDimension(R.dimen.doorhanger_drawable_padding));
}
}
});
mEncrypted.setVisibility(siteIdentity.getEncrypted() ? View.VISIBLE : View.GONE);
mHost.setText(siteIdentity.getHost()); mHost.setText(siteIdentity.getHost());
String owner = siteIdentity.getOwner(); String owner = siteIdentity.getOwner();
@ -287,8 +331,7 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
} }
final String verifier = siteIdentity.getVerifier(); final String verifier = siteIdentity.getVerifier();
final String encrypted = siteIdentity.getEncrypted(); mVerifier.setText(verifier);
mVerifier.setText(verifier + "\n" + encrypted);
} }
private void addMixedContentNotification(boolean blocked) { private void addMixedContentNotification(boolean blocked) {
@ -439,6 +482,7 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
void destroy() { void destroy() {
EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "Doorhanger:Logins"); EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "Doorhanger:Logins");
EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "Permissions:CheckResult");
} }
@Override @Override

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

@ -38,7 +38,7 @@ public class LoginDoorHanger extends DoorHanger {
private final TextView mTitle; private final TextView mTitle;
private final TextView mMessage; private final TextView mMessage;
private final TextView mLink; private final TextView mLink;
private int mCallbackID; private final DoorhangerConfig.ButtonConfig mButtonConfig;
public LoginDoorHanger(Context context, DoorhangerConfig config) { public LoginDoorHanger(Context context, DoorhangerConfig config) {
super(context, config, Type.LOGIN); super(context, config, Type.LOGIN);
@ -49,6 +49,8 @@ public class LoginDoorHanger extends DoorHanger {
mIcon.setImageResource(R.drawable.icon_key); mIcon.setImageResource(R.drawable.icon_key);
mIcon.setVisibility(View.VISIBLE); mIcon.setVisibility(View.VISIBLE);
mButtonConfig = config.getPositiveButtonConfig();
loadConfig(config); loadConfig(config);
} }
@ -62,7 +64,6 @@ public class LoginDoorHanger extends DoorHanger {
setOptions(config.getOptions()); setOptions(config.getOptions());
setMessage(config.getMessage()); setMessage(config.getMessage());
// Store the positive callback id for nested dialogs that need the same callback id. // Store the positive callback id for nested dialogs that need the same callback id.
mCallbackID = config.getPositiveButtonConfig().callback;
addButtonsToLayout(config); addButtonsToLayout(config);
} }
@ -160,12 +161,12 @@ public class LoginDoorHanger extends DoorHanger {
}); });
builder.setView(view); builder.setView(view);
builder.setPositiveButton(R.string.button_remember, new DialogInterface.OnClickListener() { builder.setPositiveButton(mButtonConfig.label, new DialogInterface.OnClickListener() {
@Override @Override
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
JSONObject response = new JSONObject(); JSONObject response = new JSONObject();
try { try {
response.put("callback", mCallbackID); response.put("callback", mButtonConfig.callback);
final JSONObject inputs = new JSONObject(); final JSONObject inputs = new JSONObject();
inputs.put("username", username.getText()); inputs.put("username", username.getText());
inputs.put("password", password.getText()); inputs.put("password", password.getText());
@ -214,7 +215,7 @@ public class LoginDoorHanger extends DoorHanger {
public void onClick(DialogInterface dialog, int which) { public void onClick(DialogInterface dialog, int which) {
final JSONObject response = new JSONObject(); final JSONObject response = new JSONObject();
try { try {
response.put("callback", mCallbackID); response.put("callback", mButtonConfig.callback);
response.put("password", passwords[which]); response.put("password", passwords[which]);
} catch (JSONException e) { } catch (JSONException e) {
Log.e(LOGTAG, "Error making login select dialog JSON", e); Log.e(LOGTAG, "Error making login select dialog JSON", e);

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

@ -51,8 +51,11 @@ var PermissionsHelper = {
observe: function observe(aSubject, aTopic, aData) { observe: function observe(aSubject, aTopic, aData) {
let uri = BrowserApp.selectedBrowser.currentURI; let uri = BrowserApp.selectedBrowser.currentURI;
let check = false;
switch (aTopic) { switch (aTopic) {
case "Permissions:Check":
check = true;
case "Permissions:Get": case "Permissions:Get":
let permissions = []; let permissions = [];
for (let i = 0; i < this._permissonTypes.length; i++) { for (let i = 0; i < this._permissonTypes.length; i++) {
@ -63,6 +66,13 @@ var PermissionsHelper = {
if (value == Services.perms.UNKNOWN_ACTION) if (value == Services.perms.UNKNOWN_ACTION)
continue; continue;
if (check) {
Messaging.sendRequest({
type: "Permissions:CheckResult",
hasPermissions: true
});
return;
}
// Get the strings that correspond to the permission type // Get the strings that correspond to the permission type
let typeStrings = this._permissionStrings[type]; let typeStrings = this._permissionStrings[type];
let label = Strings.browser.GetStringFromName(typeStrings["label"]); let label = Strings.browser.GetStringFromName(typeStrings["label"]);
@ -79,6 +89,14 @@ var PermissionsHelper = {
}); });
} }
if (check) {
Messaging.sendRequest({
type: "Permissions:CheckResult",
hasPermissions: false
});
return;
}
// Keep track of permissions, so we know which ones to clear // Keep track of permissions, so we know which ones to clear
this._currentPermissions = permissions; this._currentPermissions = permissions;

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

@ -148,7 +148,7 @@ let lazilyLoadedObserverScripts = [
["MemoryObserver", ["memory-pressure", "Memory:Dump"], "chrome://browser/content/MemoryObserver.js"], ["MemoryObserver", ["memory-pressure", "Memory:Dump"], "chrome://browser/content/MemoryObserver.js"],
["ConsoleAPI", ["console-api-log-event"], "chrome://browser/content/ConsoleAPI.js"], ["ConsoleAPI", ["console-api-log-event"], "chrome://browser/content/ConsoleAPI.js"],
["FindHelper", ["FindInPage:Opened", "FindInPage:Closed", "Tab:Selected"], "chrome://browser/content/FindHelper.js"], ["FindHelper", ["FindInPage:Opened", "FindInPage:Closed", "Tab:Selected"], "chrome://browser/content/FindHelper.js"],
["PermissionsHelper", ["Permissions:Get", "Permissions:Clear"], "chrome://browser/content/PermissionsHelper.js"], ["PermissionsHelper", ["Permissions:Check", "Permissions:Get", "Permissions:Clear"], "chrome://browser/content/PermissionsHelper.js"],
["FeedHandler", ["Feeds:Subscribe"], "chrome://browser/content/FeedHandler.js"], ["FeedHandler", ["Feeds:Subscribe"], "chrome://browser/content/FeedHandler.js"],
["Feedback", ["Feedback:Show"], "chrome://browser/content/Feedback.js"], ["Feedback", ["Feedback:Show"], "chrome://browser/content/Feedback.js"],
["SelectionHandler", ["TextSelection:Get"], "chrome://browser/content/SelectionHandler.js"], ["SelectionHandler", ["TextSelection:Get"], "chrome://browser/content/SelectionHandler.js"],
@ -7028,6 +7028,7 @@ var IdentityHandler = {
locationObj.host = location.host; locationObj.host = location.host;
locationObj.hostname = location.hostname; locationObj.hostname = location.hostname;
locationObj.port = location.port; locationObj.port = location.port;
locationObj.origin = location.origin;
} catch (ex) { } catch (ex) {
// Can sometimes throw if the URL being visited has no host/hostname, // Can sometimes throw if the URL being visited has no host/hostname,
// e.g. about:blank. The _state for these pages means we won't need these // e.g. about:blank. The _state for these pages means we won't need these
@ -7039,6 +7040,7 @@ var IdentityHandler = {
let mixedMode = this.getMixedMode(aState); let mixedMode = this.getMixedMode(aState);
let trackingMode = this.getTrackingMode(aState); let trackingMode = this.getTrackingMode(aState);
let result = { let result = {
origin: locationObj.origin,
mode: { mode: {
identity: identityMode, identity: identityMode,
mixed: mixedMode, mixed: mixedMode,
@ -7052,8 +7054,7 @@ var IdentityHandler = {
return result; return result;
} }
// Ideally we'd just make this a Java string result.encrypted = true;
result.encrypted = Strings.browser.GetStringFromName("identity.encrypted2");
result.host = this.getEffectiveHost(); result.host = this.getEffectiveHost();
let iData = this.getIdentityData(); let iData = this.getIdentityData();

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

@ -146,16 +146,24 @@ LoginManagerPrompter.prototype = {
* String message to be displayed in the doorhanger * String message to be displayed in the doorhanger
* @param aButtons * @param aButtons
* Buttons to display with the doorhanger * Buttons to display with the doorhanger
* @param aActionText * @param aUsername
* Object with text to be displayed as clickable, along with a bundle to create an action * Username string used in creating a doorhanger action
* * @param aPassword
* Password string used in creating a doorhanger action
*/ */
_showLoginNotification : function (aTitle, aBody, aButtons, aActionText) { _showLoginNotification : function (aTitle, aBody, aButtons, aUsername, aPassword) {
let notifyWin = this._window.top; let notifyWin = this._window.top;
let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject; let chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject;
let browser = chromeWin.BrowserApp.getBrowserForWindow(notifyWin); let browser = chromeWin.BrowserApp.getBrowserForWindow(notifyWin);
let tabID = chromeWin.BrowserApp.getTabForBrowser(browser).id; let tabID = chromeWin.BrowserApp.getTabForBrowser(browser).id;
let actionText = {
text: aUsername,
type: "EDIT",
bundle: { username: aUsername,
password: aPassword }
};
// The page we're going to hasn't loaded yet, so we want to persist // The page we're going to hasn't loaded yet, so we want to persist
// across the first location change. // across the first location change.
@ -163,12 +171,11 @@ LoginManagerPrompter.prototype = {
// at the post-authentication page. I don't see a good way to // at the post-authentication page. I don't see a good way to
// heuristically determine when to ignore such location changes, so // heuristically determine when to ignore such location changes, so
// we'll try ignoring location changes based on a time interval. // we'll try ignoring location changes based on a time interval.
let options = { let options = {
persistWhileVisible: true, persistWhileVisible: true,
timeout: Date.now() + 10000, timeout: Date.now() + 10000,
title: aTitle, title: aTitle,
actionText: aActionText actionText: actionText
} }
var nativeWindow = this._getNativeWindow(); var nativeWindow = this._getNativeWindow();
@ -194,13 +201,6 @@ LoginManagerPrompter.prototype = {
let username = aLogin.username ? this._sanitizeUsername(aLogin.username) : ""; let username = aLogin.username ? this._sanitizeUsername(aLogin.username) : "";
let actionText = {
text: username,
type: "EDIT",
bundle: { username: username,
password: aLogin.password }
};
// The callbacks in |buttons| have a closure to access the variables // The callbacks in |buttons| have a closure to access the variables
// in scope here; set one to |this._pwmgr| so we can get back to pwmgr // in scope here; set one to |this._pwmgr| so we can get back to pwmgr
// without a getService() call. // without a getService() call.
@ -230,7 +230,7 @@ LoginManagerPrompter.prototype = {
} }
]; ];
this._showLoginNotification(title, notificationText, buttons, actionText); this._showLoginNotification(title, notificationText, buttons, aLogin.username, aLogin.password);
}, },
/* /*
@ -280,15 +280,17 @@ LoginManagerPrompter.prototype = {
}, },
{ {
label: this._getLocalizedString("updateButton"), label: this._getLocalizedString("updateButton"),
callback: function() { callback: function(checked, response) {
self._updateLogin(aOldLogin, aNewPassword); let password = response ? response["password"] : aNewPassword;
promptHistogram.add(PROMPT_UPDATE); self._updateLogin(aOldLogin, password);
promptHistogram.add(PROMPT_UPDATE);
}, },
positive: true positive: true
} }
]; ];
this._showLoginNotification(title, notificationText, buttons); this._showLoginNotification(title, notificationText, buttons, aOldLogin.username, aNewPassword);
}, },

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

@ -98,7 +98,6 @@ identity.identified.verifier=Verified by: %S
identity.identified.verified_by_you=You have added a security exception for this site identity.identified.verified_by_you=You have added a security exception for this site
identity.identified.state_and_country=%S, %S identity.identified.state_and_country=%S, %S
identity.identified.title_with_country=%S (%S) identity.identified.title_with_country=%S (%S)
identity.encrypted2=Encrypted
# Geolocation UI # Geolocation UI
geolocation.allow=Share geolocation.allow=Share

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

@ -193,6 +193,7 @@ public class StringHelper {
public final String SETTINGS_LABEL; public final String SETTINGS_LABEL;
public final String GUEST_MODE_LABEL; public final String GUEST_MODE_LABEL;
public final String TAB_QUEUE_LABEL; public final String TAB_QUEUE_LABEL;
public final String TAB_QUEUE_SUMMARY;
// Android 3.0+ // Android 3.0+
public final String TOOLS_LABEL; public final String TOOLS_LABEL;
@ -367,6 +368,7 @@ public class StringHelper {
SETTINGS_LABEL = res.getString(R.string.settings); SETTINGS_LABEL = res.getString(R.string.settings);
GUEST_MODE_LABEL = res.getString(R.string.new_guest_session); GUEST_MODE_LABEL = res.getString(R.string.new_guest_session);
TAB_QUEUE_LABEL = res.getString(R.string.pref_tab_queue_title); TAB_QUEUE_LABEL = res.getString(R.string.pref_tab_queue_title);
TAB_QUEUE_SUMMARY = res.getString(R.string.pref_tab_queue_summary);
// Android 3.0+ // Android 3.0+
TOOLS_LABEL = res.getString(R.string.tools); TOOLS_LABEL = res.getString(R.string.tools);

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

@ -206,8 +206,7 @@ public class testSettingsMenuItems extends PixelTest {
// Tab Queue // Tab Queue
if (AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_ANDROID_TAB_QUEUE) { if (AppConstants.NIGHTLY_BUILD && AppConstants.MOZ_ANDROID_TAB_QUEUE) {
final String expected = "Queue links for later instead of switching to " + mStringHelper.BRAND_NAME + " each time"; final String[] tabQueue = { mStringHelper.TAB_QUEUE_LABEL, mStringHelper.TAB_QUEUE_SUMMARY };
String[] tabQueue = { mStringHelper.TAB_QUEUE_LABEL, expected };
settingsMap.get(PATH_CUSTOMIZE).add(tabQueue); settingsMap.get(PATH_CUSTOMIZE).add(tabQueue);
} }

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

@ -67,6 +67,7 @@ user_pref("experiments.manifest.uri", "http://%(server)s/experiments-dummy/manif
// Only load extensions from the application and user profile // Only load extensions from the application and user profile
// AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION // AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
user_pref("extensions.enabledScopes", 5); user_pref("extensions.enabledScopes", 5);
user_pref("extensions.autoDisableScopes", 0);
// Disable metadata caching for installed add-ons by default // Disable metadata caching for installed add-ons by default
user_pref("extensions.getAddons.cache.enabled", false); user_pref("extensions.getAddons.cache.enabled", false);
// Disable intalling any distribution add-ons // Disable intalling any distribution add-ons
@ -325,3 +326,7 @@ user_pref("dom.serviceWorkers.periodic-updates.enabled", false);
// Enable speech synth test service, and disable built in platform services. // Enable speech synth test service, and disable built in platform services.
user_pref("media.webspeech.synth.test", true); user_pref("media.webspeech.synth.test", true);
// Turn off search suggestions in the location bar so as not to trigger network
// connections.
user_pref("browser.urlbar.suggest.searches", false);

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

@ -5,7 +5,7 @@
}, },
"global": { "global": {
"talos_repo": "https://hg.mozilla.org/build/talos", "talos_repo": "https://hg.mozilla.org/build/talos",
"talos_revision": "e04283e4f2b2" "talos_revision": "4a8d22dd38c4"
}, },
"extra_options": { "extra_options": {
"android": [ "--apkPath=%(apk_path)s" ] "android": [ "--apkPath=%(apk_path)s" ]

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

@ -800,6 +800,20 @@ LoginManagerPrompter.prototype = {
let currentNotification; let currentNotification;
let updateButtonStatus = (element) => {
let mainActionButton = chromeDoc.getAnonymousElementByAttribute(element.button, "anonid", "button");
// Disable the main button inside the menu-button if the password field is empty.
if (login.password.length == 0) {
mainActionButton.setAttribute("disabled", true);
chromeDoc.getElementById("password-notification-password")
.classList.add("popup-notification-invalid-input");
} else {
mainActionButton.removeAttribute("disabled");
chromeDoc.getElementById("password-notification-password")
.classList.remove("popup-notification-invalid-input");
}
};
let updateButtonLabel = () => { let updateButtonLabel = () => {
let foundLogins = Services.logins.findLogins({}, login.hostname, let foundLogins = Services.logins.findLogins({}, login.hostname,
login.formSubmitURL, login.formSubmitURL,
@ -821,6 +835,7 @@ LoginManagerPrompter.prototype = {
if (element) { if (element) {
element.setAttribute("buttonlabel", label); element.setAttribute("buttonlabel", label);
element.setAttribute("buttonaccesskey", accessKey); element.setAttribute("buttonaccesskey", accessKey);
updateButtonStatus(element);
} }
}; };
@ -831,6 +846,8 @@ LoginManagerPrompter.prototype = {
.setAttribute("value", login.username); .setAttribute("value", login.username);
chromeDoc.getElementById("password-notification-password") chromeDoc.getElementById("password-notification-password")
.setAttribute("value", login.password); .setAttribute("value", login.password);
chromeDoc.getElementById("password-notification-password")
.setAttribute("show-content", showPasswordPlaceholder);
updateButtonLabel(); updateButtonLabel();
}; };
@ -841,11 +858,19 @@ LoginManagerPrompter.prototype = {
chromeDoc.getElementById("password-notification-password").value; chromeDoc.getElementById("password-notification-password").value;
}; };
let onUsernameInput = () => { let onInput = () => {
readDataFromUI(); readDataFromUI();
updateButtonLabel(); updateButtonLabel();
}; };
let onPasswordFocus = () => {
chromeDoc.getElementById("password-notification-password").type = "";
};
let onPasswordBlur = () => {
chromeDoc.getElementById("password-notification-password").type = "password";
};
let persistData = () => { let persistData = () => {
let foundLogins = Services.logins.findLogins({}, login.hostname, let foundLogins = Services.logins.findLogins({}, login.hostname,
login.formSubmitURL, login.formSubmitURL,
@ -862,7 +887,12 @@ LoginManagerPrompter.prototype = {
login.usernameField, login.usernameField,
login.passwordField)); login.passwordField));
} else if (logins.length == 1) { } else if (logins.length == 1) {
this._updateLogin(logins[0], login.password); if (logins[0].password == login.password) {
// We only want to touch the login's use count and last used time.
this._updateLogin(logins[0], null);
} else {
this._updateLogin(logins[0], login.password);
}
} else { } else {
Cu.reportError("Unexpected match of multiple logins."); Cu.reportError("Unexpected match of multiple logins.");
} }
@ -896,6 +926,7 @@ LoginManagerPrompter.prototype = {
}] : null; }] : null;
let usernamePlaceholder = this._getLocalizedString("noUsernamePlaceholder"); let usernamePlaceholder = this._getLocalizedString("noUsernamePlaceholder");
let showPasswordPlaceholder = this._getLocalizedString("showPasswordPlaceholder");
let displayHost = this._getShortDisplayHost(login.hostname); let displayHost = this._getShortDisplayHost(login.hostname);
this._getPopupNote().show( this._getPopupNote().show(
@ -913,9 +944,17 @@ LoginManagerPrompter.prototype = {
switch (topic) { switch (topic) {
case "showing": case "showing":
currentNotification = this; currentNotification = this;
writeDataToUI();
chromeDoc.getElementById("password-notification-username") chromeDoc.getElementById("password-notification-username")
.addEventListener("input", onUsernameInput); .addEventListener("input", onInput);
chromeDoc.getElementById("password-notification-password")
.addEventListener("input", onInput);
chromeDoc.getElementById("password-notification-password")
.addEventListener("focus", onPasswordFocus);
chromeDoc.getElementById("password-notification-password")
.addEventListener("blur", onPasswordBlur);
break;
case "shown":
writeDataToUI();
break; break;
case "dismissed": case "dismissed":
readDataFromUI(); readDataFromUI();
@ -923,7 +962,13 @@ LoginManagerPrompter.prototype = {
case "removed": case "removed":
currentNotification = null; currentNotification = null;
chromeDoc.getElementById("password-notification-username") chromeDoc.getElementById("password-notification-username")
.removeEventListener("input", onUsernameInput); .removeEventListener("input", onInput);
chromeDoc.getElementById("password-notification-password")
.removeEventListener("input", onInput);
chromeDoc.getElementById("password-notification-password")
.removeEventListener("focus", onPasswordFocus);
chromeDoc.getElementById("password-notification-password")
.removeEventListener("blur", onPasswordBlur);
break; break;
} }
return false; return false;

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

@ -635,9 +635,21 @@ AsyncFetchAndSetIconFromNetwork::OnStopRequest(nsIRequest* aRequest,
return NS_OK; return NS_OK;
} }
NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, aRequest, nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
TO_INTBUFFER(mIcon.data), mIcon.data.Length(), // aRequest should always QI to nsIChannel.
mIcon.mimeType); // See AsyncFetchAndSetIconFromNetwork::Run()
MOZ_ASSERT(channel);
nsAutoCString contentType;
channel->GetContentType(contentType);
// Bug 366324 - can't sniff SVG yet, so rely on server-specified type
if (contentType.EqualsLiteral("image/svg+xml")) {
mIcon.mimeType.AssignLiteral("image/svg+xml");
} else {
NS_SniffContent(NS_DATA_SNIFFER_CATEGORY, aRequest,
TO_INTBUFFER(mIcon.data), mIcon.data.Length(),
mIcon.mimeType);
}
// If the icon does not have a valid MIME type, add it to the failed cache. // If the icon does not have a valid MIME type, add it to the failed cache.
if (mIcon.mimeType.IsEmpty()) { if (mIcon.mimeType.IsEmpty()) {
@ -649,10 +661,6 @@ AsyncFetchAndSetIconFromNetwork::OnStopRequest(nsIRequest* aRequest,
return NS_OK; return NS_OK;
} }
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
// aRequest should always QI to nsIChannel.
// See AsyncFetchAndSetIconFromNetwork::Run()
MOZ_ASSERT(channel);
mIcon.expiration = GetExpirationTimeFromChannel(channel); mIcon.expiration = GetExpirationTimeFromChannel(channel);
// Telemetry probes to measure the favicon file sizes for each different file type. // Telemetry probes to measure the favicon file sizes for each different file type.

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

@ -17,6 +17,9 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Task.jsm"); Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm"); Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SearchSuggestionController",
"resource://gre/modules/SearchSuggestionController.jsm");
const SEARCH_ENGINE_TOPIC = "browser-search-engine-modified"; const SEARCH_ENGINE_TOPIC = "browser-search-engine-modified";
const SearchAutocompleteProviderInternal = { const SearchAutocompleteProviderInternal = {
@ -110,10 +113,65 @@ const SearchAutocompleteProviderInternal = {
} }
}, },
getSuggestionController(searchToken, inPrivateContext, maxResults) {
let engine = Services.search.currentEngine;
if (!engine) {
return null;
}
return new SearchSuggestionControllerWrapper(engine, searchToken,
inPrivateContext, maxResults);
},
QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver, QueryInterface: XPCOMUtils.generateQI([Ci.nsIObserver,
Ci.nsISupportsWeakReference]), Ci.nsISupportsWeakReference]),
} }
function SearchSuggestionControllerWrapper(engine, searchToken,
inPrivateContext, maxResults) {
this._controller = new SearchSuggestionController();
this._controller.maxLocalResults = 0;
this._controller.maxRemoteResults = maxResults;
let promise = this._controller.fetch(searchToken, inPrivateContext, engine);
this._suggestions = [];
this._promise = promise.then(results => {
this._suggestions = (results ? results.remote : null) || [];
}).catch(err => {
// fetch() rejects its promise if there's a pending request.
});
}
SearchSuggestionControllerWrapper.prototype = {
/**
* Resolved when all suggestions have been fetched.
*/
get fetchCompletePromise() {
return this._promise;
},
/**
* Returns one suggestion, if any are available. The returned value is an
* array [match, suggestion]. If none are available, returns [null, null].
* Note that there are two reasons that suggestions might not be available:
* all suggestions may have been fetched and consumed, or the fetch may not
* have completed yet.
*
* @return An array [match, suggestion].
*/
consume() {
return !this._suggestions.length ? [null, null] :
[SearchAutocompleteProviderInternal.defaultMatch,
this._suggestions.shift()];
},
/**
* Stops the fetch.
*/
stop() {
this._controller.stop();
},
};
let gInitializationPromise = null; let gInitializationPromise = null;
this.PlacesSearchAutocompleteProvider = Object.freeze({ this.PlacesSearchAutocompleteProvider = Object.freeze({
@ -214,4 +272,12 @@ this.PlacesSearchAutocompleteProvider = Object.freeze({
terms: parseUrlResult.terms, terms: parseUrlResult.terms,
}; };
}, },
getSuggestionController(searchToken, inPrivateContext, maxResults) {
if (!SearchAutocompleteProviderInternal.initialized) {
throw new Error("The component has not been initialized.");
}
return SearchAutocompleteProviderInternal.getSuggestionController(
searchToken, inPrivateContext, maxResults);
},
}); });

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

@ -35,6 +35,7 @@ const PREF_SUGGEST_HISTORY = [ "suggest.history", true ];
const PREF_SUGGEST_BOOKMARK = [ "suggest.bookmark", true ]; const PREF_SUGGEST_BOOKMARK = [ "suggest.bookmark", true ];
const PREF_SUGGEST_OPENPAGE = [ "suggest.openpage", true ]; const PREF_SUGGEST_OPENPAGE = [ "suggest.openpage", true ];
const PREF_SUGGEST_HISTORY_ONLYTYPED = [ "suggest.history.onlyTyped", false ]; const PREF_SUGGEST_HISTORY_ONLYTYPED = [ "suggest.history.onlyTyped", false ];
const PREF_SUGGEST_SEARCHES = [ "suggest.searches", true ];
// Match type constants. // Match type constants.
// These indicate what type of search function we should be using. // These indicate what type of search function we should be using.
@ -65,6 +66,13 @@ const TELEMETRY_6_FIRST_RESULTS = "PLACES_AUTOCOMPLETE_6_FIRST_RESULTS_TIME_MS";
// The default frecency value used when inserting matches with unknown frecency. // The default frecency value used when inserting matches with unknown frecency.
const FRECENCY_DEFAULT = 1000; const FRECENCY_DEFAULT = 1000;
// Search suggestion results are mixed in with all other results after they
// become available, but they're only inserted once every N results whose
// frecencies are less than FRECENCY_DEFAULT. In other words, for every N
// results that fall below that frecency threshold, one search suggestion is
// inserted. This value = N.
const SEARCH_SUGGESTION_INSERT_INTERVAL = 2;
// A regex that matches "single word" hostnames for whitelisting purposes. // A regex that matches "single word" hostnames for whitelisting purposes.
// The hostname will already have been checked for general validity, so we // The hostname will already have been checked for general validity, so we
// don't need to be exhaustive here, so allow dashes anywhere. // don't need to be exhaustive here, so allow dashes anywhere.
@ -344,10 +352,15 @@ XPCOMUtils.defineLazyGetter(this, "SwitchToTabStorage", () => Object.seal({
*/ */
XPCOMUtils.defineLazyGetter(this, "Prefs", () => { XPCOMUtils.defineLazyGetter(this, "Prefs", () => {
let prefs = new Preferences(PREF_BRANCH); let prefs = new Preferences(PREF_BRANCH);
let types = ["History", "Bookmark", "Openpage", "Typed"]; let types = ["History", "Bookmark", "Openpage", "Typed", "Searches"];
function syncEnabledPref(init = false) { function syncEnabledPref(init = false) {
let suggestPrefs = [PREF_SUGGEST_HISTORY, PREF_SUGGEST_BOOKMARK, PREF_SUGGEST_OPENPAGE]; let suggestPrefs = [
PREF_SUGGEST_HISTORY,
PREF_SUGGEST_BOOKMARK,
PREF_SUGGEST_OPENPAGE,
PREF_SUGGEST_SEARCHES,
];
if (init) { if (init) {
// Make sure to initialize the properties when first called with init = true. // Make sure to initialize the properties when first called with init = true.
@ -356,6 +369,7 @@ XPCOMUtils.defineLazyGetter(this, "Prefs", () => {
store.suggestBookmark = prefs.get(...PREF_SUGGEST_BOOKMARK); store.suggestBookmark = prefs.get(...PREF_SUGGEST_BOOKMARK);
store.suggestOpenpage = prefs.get(...PREF_SUGGEST_OPENPAGE); store.suggestOpenpage = prefs.get(...PREF_SUGGEST_OPENPAGE);
store.suggestTyped = prefs.get(...PREF_SUGGEST_HISTORY_ONLYTYPED); store.suggestTyped = prefs.get(...PREF_SUGGEST_HISTORY_ONLYTYPED);
store.suggestSearches = prefs.get(...PREF_SUGGEST_SEARCHES);
} }
if (store.enabled) { if (store.enabled) {
@ -395,6 +409,7 @@ XPCOMUtils.defineLazyGetter(this, "Prefs", () => {
store.suggestBookmark = prefs.get(...PREF_SUGGEST_BOOKMARK); store.suggestBookmark = prefs.get(...PREF_SUGGEST_BOOKMARK);
store.suggestOpenpage = prefs.get(...PREF_SUGGEST_OPENPAGE); store.suggestOpenpage = prefs.get(...PREF_SUGGEST_OPENPAGE);
store.suggestTyped = prefs.get(...PREF_SUGGEST_HISTORY_ONLYTYPED); store.suggestTyped = prefs.get(...PREF_SUGGEST_HISTORY_ONLYTYPED);
store.suggestSearches = prefs.get(...PREF_SUGGEST_SEARCHES);
// If history is not set, onlyTyped value should be ignored. // If history is not set, onlyTyped value should be ignored.
if (!store.suggestHistory) { if (!store.suggestHistory) {
@ -566,6 +581,7 @@ function Search(searchString, searchParam, autocompleteListener,
let params = new Set(searchParam.split(" ")); let params = new Set(searchParam.split(" "));
this._enableActions = params.has("enable-actions"); this._enableActions = params.has("enable-actions");
this._disablePrivateActions = params.has("disable-private-actions"); this._disablePrivateActions = params.has("disable-private-actions");
this._inPrivateWindow = params.has("private-window");
this._searchTokens = this._searchTokens =
this.filterTokens(getUnfilteredSearchTokens(this._searchString)); this.filterTokens(getUnfilteredSearchTokens(this._searchString));
@ -599,6 +615,8 @@ function Search(searchString, searchParam, autocompleteListener,
// These are used to avoid adding duplicate entries to the results. // These are used to avoid adding duplicate entries to the results.
this._usedURLs = new Set(); this._usedURLs = new Set();
this._usedPlaceIds = new Set(); this._usedPlaceIds = new Set();
this._searchSuggestionInsertCounter = 0;
} }
Search.prototype = { Search.prototype = {
@ -704,6 +722,10 @@ Search.prototype = {
this._sleepDeferred.resolve(); this._sleepDeferred.resolve();
this._sleepDeferred = null; this._sleepDeferred = null;
} }
if (this._searchSuggestionController) {
this._searchSuggestionController.stop();
this._searchSuggestionController = null;
}
this.pending = false; this.pending = false;
}, },
@ -806,7 +828,22 @@ Search.prototype = {
// IMPORTANT: No other first result heuristics should run after // IMPORTANT: No other first result heuristics should run after
// _matchHeuristicFallback(). // _matchHeuristicFallback().
yield this._sleep(Prefs.delay); yield this._sleep(Math.round(Prefs.delay / 2));
if (!this.pending)
return;
// Start fetching search suggestions a little earlier than Prefs.delay since
// they're remote and will probably take longer to arrive.
if (this.hasBehavior("searches")) {
this._searchSuggestionController =
PlacesSearchAutocompleteProvider.getSuggestionController(
this._originalSearchString,
this._inPrivateWindow,
Prefs.maxRichResults
);
}
yield this._sleep(Math.round(Prefs.delay / 2));
if (!this.pending) if (!this.pending)
return; return;
@ -837,6 +874,13 @@ Search.prototype = {
return; return;
} }
} }
// If we still don't have enough results, fill the remaining space with
// search suggestions.
if (this._searchSuggestionController && this.pending) {
yield this._searchSuggestionController.fetchCompletePromise;
while (this.pending && this._maybeAddSearchSuggestionMatch());
}
}), }),
_matchKnownUrl: function* (conn, queries) { _matchKnownUrl: function* (conn, queries) {
@ -966,7 +1010,7 @@ Search.prototype = {
match.engineAlias = alias; match.engineAlias = alias;
let query = this._searchTokens.slice(1).join(" "); let query = this._searchTokens.slice(1).join(" ");
yield this._addSearchEngineMatch(match, query); this._addSearchEngineMatch(match, query);
return true; return true;
}, },
@ -976,16 +1020,17 @@ Search.prototype = {
return; return;
let query = this._originalSearchString; let query = this._originalSearchString;
this._addSearchEngineMatch(match, query);
yield this._addSearchEngineMatch(match, query);
}, },
_addSearchEngineMatch: function* (match, query) { _addSearchEngineMatch(match, query, suggestion) {
let actionURLParams = { let actionURLParams = {
engineName: match.engineName, engineName: match.engineName,
input: this._originalSearchString, input: this._originalSearchString,
searchQuery: query, searchQuery: query,
}; };
if (suggestion)
actionURLParams.searchSuggestion = suggestion;
if (match.engineAlias) { if (match.engineAlias) {
actionURLParams.alias = match.engineAlias; actionURLParams.alias = match.engineAlias;
} }
@ -1129,12 +1174,43 @@ Search.prototype = {
parseResult.engineName; parseResult.engineName;
}, },
_maybeAddSearchSuggestionMatch() {
if (this._searchSuggestionController) {
let [match, suggestion] = this._searchSuggestionController.consume();
if (suggestion) {
this._addSearchEngineMatch(match, this._originalSearchString,
suggestion);
return true;
}
}
return false;
},
_addMatch: function (match) { _addMatch: function (match) {
// A search could be canceled between a query start and its completion, // A search could be canceled between a query start and its completion,
// in such a case ensure we won't notify any result for it. // in such a case ensure we won't notify any result for it.
if (!this.pending) if (!this.pending)
return; return;
// Mix in search suggestions. Insert one suggestion every N non-suggestion
// matches that fall below the default frecency, and start inserting them as
// soon as they become available. N = SEARCH_SUGGESTION_INSERT_INTERVAL.
if (match.frecency < FRECENCY_DEFAULT) {
if (this._searchSuggestionInsertCounter %
SEARCH_SUGGESTION_INSERT_INTERVAL == 0) {
// Search engine matches are created with FRECENCY_DEFAULT, so there's
// no danger of infinite indirect recursion.
if (this._maybeAddSearchSuggestionMatch()) {
if (!this.pending) {
return;
}
this._searchSuggestionInsertCounter++;
}
} else {
this._searchSuggestionInsertCounter++;
}
}
let notifyResults = false; let notifyResults = false;
// Must check both id and url, cause keywords dinamically modify the url. // Must check both id and url, cause keywords dinamically modify the url.

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

@ -13,7 +13,7 @@ interface nsIURI;
* search provider as well as methods to track opened pages for AutoComplete * search provider as well as methods to track opened pages for AutoComplete
* purposes. * purposes.
*/ */
[scriptable, uuid(6e252399-77f9-4f11-98cc-6fa9fab96f92)] [scriptable, uuid(61b6348a-09e1-4810-8057-f8cb3cec6ef8)]
interface mozIPlacesAutoComplete : nsISupports interface mozIPlacesAutoComplete : nsISupports
{ {
////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////
@ -102,6 +102,11 @@ interface mozIPlacesAutoComplete : nsISupports
*/ */
const long BEHAVIOR_RESTRICT = 1 << 8; const long BEHAVIOR_RESTRICT = 1 << 8;
/**
* Include search suggestions from the currently selected search provider.
*/
const long BEHAVIOR_SEARCHES = 1 << 9;
/** /**
* Mark a page as being currently open. * Mark a page as being currently open.
* *

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

@ -57,6 +57,16 @@ XPCOMUtils.defineLazyGetter(this, "SMALLPNG_DATA_URI", function() {
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAA" + "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAA" +
"AAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg=="); "AAAA6fptVAAAACklEQVQI12NgAAAAAgAB4iG8MwAAAABJRU5ErkJggg==");
}); });
XPCOMUtils.defineLazyGetter(this, "SMALLSVG_DATA_URI", function() {
return NetUtil.newURI(
"data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy5" +
"3My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAxMDAgMTAwIiBmaWxs" +
"PSIjNDI0ZTVhIj4NCiAgPGNpcmNsZSBjeD0iNTAiIGN5PSI1MCIgcj0iN" +
"DQiIHN0cm9rZT0iIzQyNGU1YSIgc3Ryb2tlLXdpZHRoPSIxMSIgZmlsbD" +
"0ibm9uZSIvPg0KICA8Y2lyY2xlIGN4PSI1MCIgY3k9IjI0LjYiIHI9IjY" +
"uNCIvPg0KICA8cmVjdCB4PSI0NSIgeT0iMzkuOSIgd2lkdGg9IjEwLjEi" +
"IGhlaWdodD0iNDEuOCIvPg0KPC9zdmc%2BDQo%3D");
});
let gTestDir = do_get_cwd(); let gTestDir = do_get_cwd();

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

@ -0,0 +1,30 @@
const PAGEURI = NetUtil.newURI("http://deliciousbacon.com/");
add_task(function* () {
// First, add a history entry or else Places can't save a favicon.
yield PlacesTestUtils.addVisits({
uri: PAGEURI,
transition: TRANSITION_LINK,
visitDate: Date.now() * 1000
});
yield new Promise(resolve => {
function onSetComplete(aURI, aDataLen, aData, aMimeType) {
equal(aURI.spec, SMALLSVG_DATA_URI.spec, "setFavicon aURI check");
equal(aDataLen, 263, "setFavicon aDataLen check");
equal(aMimeType, "image/svg+xml", "setFavicon aMimeType check");
resolve();
}
PlacesUtils.favicons.setAndFetchFaviconForPage(PAGEURI, SMALLSVG_DATA_URI,
false,
PlacesUtils.favicons.FAVICON_LOAD_NON_PRIVATE,
onSetComplete);
});
let data = yield PlacesUtils.promiseFaviconData(PAGEURI.spec);
equal(data.uri.spec, SMALLSVG_DATA_URI.spec, "getFavicon aURI check");
equal(data.dataLen, 263, "getFavicon aDataLen check");
equal(data.mimeType, "image/svg+xml", "getFavicon aMimeType check");
});

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

@ -138,6 +138,7 @@ skip-if = os == "android"
[test_resolveNullBookmarkTitles.js] [test_resolveNullBookmarkTitles.js]
[test_result_sort.js] [test_result_sort.js]
[test_sql_guid_functions.js] [test_sql_guid_functions.js]
[test_svg_favicon.js]
[test_tag_autocomplete_search.js] [test_tag_autocomplete_search.js]
[test_tagging.js] [test_tagging.js]
[test_telemetry.js] [test_telemetry.js]

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

@ -11,3 +11,7 @@
image-rendering: -moz-crisp-edges; image-rendering: -moz-crisp-edges;
} }
} }
richlistitem > .ac-title-box > .ac-title > .ac-comment:not([selected]) > html|span.ac-selected-text {
display: none;
}

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

@ -1539,11 +1539,10 @@ extends="chrome://global/content/bindings/popup.xml#popup">
let index = (match[1] || 1) - 1; let index = (match[1] || 1) - 1;
if (index >= 0 && index < aReplacements.length) { if (index >= 0 && index < aReplacements.length) {
let replacement = aReplacements[index]; pairs.push([...aReplacements[index]]);
pairs.push([...replacement]);
} }
} else { } else {
pairs.push([part, false]); pairs.push([part]);
} }
} }
@ -1572,8 +1571,15 @@ extends="chrome://global/content/bindings/popup.xml#popup">
if (emphasise) { if (emphasise) {
let span = aDescriptionElement.appendChild( let span = aDescriptionElement.appendChild(
document.createElementNS("http://www.w3.org/1999/xhtml", "span")); document.createElementNS("http://www.w3.org/1999/xhtml", "span"));
span.className = "ac-emphasize-text";
span.textContent = text; span.textContent = text;
switch(emphasise) {
case "match":
span.className = "ac-emphasize-text";
break;
case "selected":
span.className = "ac-selected-text";
break;
}
} else { } else {
aDescriptionElement.appendChild(document.createTextNode(text)); aDescriptionElement.appendChild(document.createTextNode(text));
} }
@ -1629,10 +1635,22 @@ extends="chrome://global/content/bindings/popup.xml#popup">
} else if (action.type == "searchengine") { } else if (action.type == "searchengine") {
emphasiseUrl = false; emphasiseUrl = false;
let sourceStr = this._stringBundle.GetStringFromName("searchWithEngineForQuery"); // The order here is not localizable, we default to appending
title = this._generateEmphasisPairs(sourceStr, [ // "- Search with Engine" to the search string, to be able to
[action.params.engineName, false], // properly generate emphasis pairs. That said, no localization
[action.params.searchQuery, true] // changed the order while it was possible, so doesn't look like
// there's a strong need for that.
let {engineName, searchSuggestion, searchQuery} = action.params;
let engineStr = "- " +
this._stringBundle.formatStringFromName("searchWithEngine",
[engineName], 1);
let suggestedPart = "";
if (searchSuggestion) {
suggestedPart = searchSuggestion.substr(searchQuery.length);
}
title = this._generateEmphasisPairs(`%1$S${suggestedPart} %2$S`, [
[searchQuery, "match"],
[engineStr, "selected"],
]); ]);
// If this is a default search match, we remove the image so we // If this is a default search match, we remove the image so we
// can style it ourselves with a generic search icon. // can style it ourselves with a generic search icon.
@ -1649,7 +1667,7 @@ extends="chrome://global/content/bindings/popup.xml#popup">
let sourceStr = this._stringBundle.GetStringFromName("visitURL"); let sourceStr = this._stringBundle.GetStringFromName("visitURL");
title = this._generateEmphasisPairs(sourceStr, [ title = this._generateEmphasisPairs(sourceStr, [
[displayUrl, true], [displayUrl, "match"],
]); ]);
} }
@ -1679,7 +1697,7 @@ extends="chrome://global/content/bindings/popup.xml#popup">
let sourceStr = this._stringBundle.GetStringFromName("visitURL"); let sourceStr = this._stringBundle.GetStringFromName("visitURL");
title = this._generateEmphasisPairs(sourceStr, [ title = this._generateEmphasisPairs(sourceStr, [
[displayUrl, true], [displayUrl, "match"],
]); ]);
types.delete("autofill"); types.delete("autofill");

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

@ -38,7 +38,8 @@ exports.safeErrorString = function safeErrorString(aError) {
} }
} catch (ee) { } } catch (ee) { }
return "<failed trying to find error description>"; // We failed to find a good error description, so do the next best thing.
return Object.prototype.toString.call(aError);
} }
/** /**

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше