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

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

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

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

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

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

@ -1328,3 +1328,28 @@ toolbarpaletteitem[place="palette"][hidden] {
#login-fill-doorhanger:not([inDetailView]) > #login-fill-clickcapturer {
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 &&
!PrivateBrowsingUtils.permanentPrivateBrowsing) {
if (gURLBar) {
let value = gURLBar.getAttribute("autocompletesearchparam") || "";
if (!PrivateBrowsingUtils.permanentPrivateBrowsing &&
!value.includes("disable-private-actions")) {
// Disable switch to tab autocompletion for private windows.
// We leave it enabled for permanent private browsing mode though.
let value = gURLBar.getAttribute("autocompletesearchparam") || "";
if (!value.includes("disable-private-actions")) {
gURLBar.setAttribute("autocompletesearchparam",
value + " disable-private-actions");
value += " disable-private-actions";
}
if (!value.includes("private-window")) {
value += " private-window";
}
gURLBar.setAttribute("autocompletesearchparam", value);
}
}
};

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

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

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

@ -501,7 +501,8 @@ input[type=button] {
.newtab-search-panel-engine,
#newtab-search-manage {
line-height: 25px;
padding: 15px 15px 15px 40px;
padding: 15px;
-moz-padding-start: 40px;
font-size: 14px;
cursor: pointer;
}
@ -532,11 +533,20 @@ input[type=button] {
.newtab-customize-complex-option {
width: 100%;
display: block;
text-align: left;
text-align: start;
max-width: 300px;
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-complex-option {
max-width: 100%;
@ -589,12 +599,13 @@ input[type=button] {
.newtab-customize-panel-subitem {
font-size: 12px;
padding: 0px 15px 15px 40px;
padding: 0px 15px 15px 15px;
-moz-padding-start: 40px;
display: block;
}
.newtab-customize-panel-subitem > label {
padding-left: 10px;
padding: 0px 10px;
line-height: 20px;
vertical-align: middle;
max-width: 225px;
@ -603,7 +614,8 @@ input[type=button] {
.newtab-customize-panel-superitem {
line-height: 20px;
border-bottom: medium none !important;
padding: 15px 15px 10px 40px;
padding: 15px 15px 10px 15px;
-moz-padding-start: 40px;
border-top: 1px solid threedshadow;
}

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

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

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

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

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

@ -33,10 +33,7 @@ this.EXPORTED_SYMBOLS = [
////////////////////////////////////////////////////////////////////////////////
//// Globals
const Cc = Components.classes;
const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
const { classes: Cc, interfaces: Ci, utils: Cu, results: Cr } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
@ -65,8 +62,15 @@ XPCOMUtils.defineLazyModuleGetter(this, "Promise",
"resource://gre/modules/Promise.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task",
"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;
@ -124,7 +128,6 @@ let PrefObserver = {
PrefObserver.register({
// prefName: defaultValue
debug: false,
animateNotifications: true
});
@ -144,20 +147,6 @@ this.DownloadsCommon = {
BLOCK_VERDICT_POTENTIALLY_UNWANTED: "PotentiallyUnwanted",
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
* 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.
*/

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

@ -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 += [
'DownloadsCommon.jsm',
'DownloadsLogger.jsm',
'DownloadsTaskbar.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
contract @mozilla.org/profile/migrator;1?app=browser&type=safari {4b609ecf-60b2-4655-9df4-dc149e474da1}
#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

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

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

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

@ -292,6 +292,9 @@ var MigrationWizard = {
case "firefox":
source = "sourceNameFirefox";
break;
case "360se":
source = "sourceName360se";
break;
}
// 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="chrome" label="&importFromChrome.label;" accesskey="&importFromChrome.accesskey;"/>
<radio id="safari" label="&importFromSafari.label;" accesskey="&importFromSafari.accesskey;"/>
<radio id="360se" label="&importFrom360se.label;" accesskey="&importFrom360se.accesskey;"/>
#elifdef XP_MACOSX
<radio id="safari" label="&importFromSafari.label;" accesskey="&importFromSafari.accesskey;"/>
<radio id="chrome" label="&importFromChrome.label;" accesskey="&importFromChrome.accesskey;"/>

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

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

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

@ -67,6 +67,7 @@
<menuitem value="ko" label="&font.langGroup.korean;"/>
<menuitem value="x-western" label="&font.langGroup.latin;"/>
<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-sinh" label="&font.langGroup.sinhala;"/>
<menuitem value="x-tamil" label="&font.langGroup.tamil;"/>

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

@ -358,12 +358,12 @@ var gPrivacyPane = {
* Update browser.urlbar.autocomplete.enabled when a
* browser.urlbar.suggest.* pref is changed from the ui.
*/
writeSuggestionPref: function () {
writeSuggestionPref() {
let getVal = (aPref) => {
return document.getElementById("browser.urlbar.suggest." + aPref).value;
}
// 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);
},

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

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

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

@ -15,6 +15,7 @@ function test() {
run_test_subset([
test_locbar_suggestion_retention("history", true),
test_locbar_suggestion_retention("bookmark", true),
test_locbar_suggestion_retention("searches", true),
test_locbar_suggestion_retention("openpage", false),
test_locbar_suggestion_retention("history", true),
test_locbar_suggestion_retention("history", false),

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

@ -325,12 +325,12 @@ var gPrivacyPane = {
* Update browser.urlbar.autocomplete.enabled when a
* browser.urlbar.suggest.* pref is changed from the ui.
*/
writeSuggestionPref: function PPP_writeSuggestionPref() {
writeSuggestionPref() {
let getVal = (aPref) => {
return document.getElementById("browser.urlbar.suggest." + aPref).value;
}
// 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);
},

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

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

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

@ -15,6 +15,7 @@ function test() {
run_test_subset([
test_locbar_suggestion_retention("history", true),
test_locbar_suggestion_retention("bookmark", true),
test_locbar_suggestion_retention("searches", true),
test_locbar_suggestion_retention("openpage", false),
test_locbar_suggestion_retention("history", true),
test_locbar_suggestion_retention("history", false),

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

@ -839,17 +839,12 @@ let SessionStoreInternal = {
},
/**
* If it's the first window load since app start...
* - 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
* Registers and tracks a given window.
*
* @param aWindow
* 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
if (aWindow && aWindow.__SSi && this._windows[aWindow.__SSi])
return;
@ -874,14 +869,43 @@ let SessionStoreInternal = {
// and create its data object
this._windows[aWindow.__SSi] = { tabs: [], selected: 0, _closedTabs: [], busy: false };
let isPrivateWindow = false;
if (PrivateBrowsingUtils.isWindowPrivate(aWindow))
this._windows[aWindow.__SSi].isPrivate = isPrivateWindow = true;
this._windows[aWindow.__SSi].isPrivate = true;
if (!this._isWindowLoaded(aWindow))
this._windows[aWindow.__SSi]._restoring = true;
if (!aWindow.toolbar.visible)
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
if (RunState.isStopped) {
RunState.setRunning();
@ -1007,20 +1031,6 @@ let SessionStoreInternal = {
// undoCloseWindow was executed.
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
*/
onBeforeBrowserWindowShown: function (aWindow) {
// Just call onLoad() directly if we're initialized already.
if (this._sessionInitialized) {
// Register the window.
this.onLoad(aWindow);
// Just call initializeWindow() directly if we're initialized already.
if (this._sessionInitialized) {
this.initializeWindow(aWindow);
return;
}
@ -1067,13 +1080,13 @@ let SessionStoreInternal = {
}
if (this._sessionInitialized) {
this.onLoad(aWindow);
this.initializeWindow(aWindow);
} else {
let initialState = this.initSession();
this._sessionInitialized = true;
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");
// Let everyone know we're done.
@ -2173,6 +2186,7 @@ let SessionStoreInternal = {
*/
navigateAndRestore(tab, loadArguments, historyIndex) {
let window = tab.ownerDocument.defaultView;
NS_ASSERT(window.__SSi, "tab's window must be tracked");
let browser = tab.linkedBrowser;
// 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,
"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");
yield minimize(toolbox);
let onMaximized = toolbox._host.once("maximized");
@ -67,9 +77,27 @@ function* minimize(toolbox) {
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) {
let button = toolbox.doc.querySelector("#toolbox-dock-bottom-minimize");
let onMaximized = toolbox._host.once("maximized");
EventUtils.synthesizeMouseAtCenter(button, {}, toolbox.doc.defaultView);
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
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* globals gDevTools, DOMHelpers, toolboxStrings, InspectorFront, Selection,
getPerformanceActorsConnection, CommandUtils, DevToolsUtils, screenManager,
oscpu, Hosts, is64Bit */
CommandUtils, DevToolsUtils, screenManager, oscpu, Hosts, is64Bit,
osString, showDoorhanger, getHighlighterUtils, getPerformanceFront */
"use strict";
@ -21,24 +21,20 @@ let {Cc, Ci, Cu} = require("chrome");
let {Promise: promise} = require("resource://gre/modules/Promise.jsm");
let EventEmitter = require("devtools/toolkit/event-emitter");
let Telemetry = require("devtools/shared/telemetry");
let {getHighlighterUtils} = require("devtools/framework/toolbox-highlighter-utils");
let HUDService = require("devtools/webconsole/hudservice");
let {showDoorhanger} = require("devtools/shared/doorhanger");
let sourceUtils = require("devtools/shared/source-utils");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource:///modules/devtools/gDevTools.jsm");
Cu.import("resource:///modules/devtools/scratchpad-manager.jsm");
Cu.import("resource:///modules/devtools/DOMHelpers.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", () => {
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) => {
try {
if (!args.length) {
@ -51,22 +47,31 @@ loader.lazyGetter(this, "toolboxStrings", () => {
}
};
});
loader.lazyGetter(this, "Selection", () => require("devtools/framework/selection").Selection);
loader.lazyGetter(this, "InspectorFront", () => require("devtools/server/actors/inspector").InspectorFront);
loader.lazyRequireGetter(this, "DevToolsUtils", "devtools/toolkit/DevToolsUtils");
loader.lazyRequireGetter(this, "getPerformanceFront", "devtools/performance/front", true);
XPCOMUtils.defineLazyGetter(this, "screenManager", () => {
loader.lazyRequireGetter(this, "getHighlighterUtils",
"devtools/framework/toolbox-highlighter-utils", true);
loader.lazyRequireGetter(this, "Hosts",
"devtools/framework/toolbox-hosts", true);
loader.lazyRequireGetter(this, "Selection",
"devtools/framework/selection", true);
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);
});
XPCOMUtils.defineLazyGetter(this, "oscpu", () => {
loader.lazyGetter(this, "oscpu", () => {
return Cc["@mozilla.org/network/protocol;1?name=http"]
.getService(Ci.nsIHttpProtocolHandler).oscpu;
});
XPCOMUtils.defineLazyGetter(this, "is64Bit", () => {
loader.lazyGetter(this, "is64Bit", () => {
return Cc["@mozilla.org/xre/app-info;1"].getService(Ci.nsIXULAppInfo).is64Bit;
});
@ -79,8 +84,9 @@ const ToolboxButtons = exports.ToolboxButtons = [
target.getTrait("highlightable")
},
{ id: "command-button-frames",
isTargetSupported: target =>
( target.activeTab && target.activeTab.traits.frames )
isTargetSupported: target => {
return target.activeTab && target.activeTab.traits.frames;
}
},
{ id: "command-button-splitconsole",
isTargetSupported: target => !target.isAddon },
@ -135,6 +141,7 @@ function Toolbox(target, selectedTool, hostType, hostOptions) {
this._onBottomHostMaximized = this._onBottomHostMaximized.bind(this);
this._onToolSelectWhileMinimized = this._onToolSelectWhileMinimized.bind(this);
this._onBottomHostWillChange = this._onBottomHostWillChange.bind(this);
this._toggleMinimizeMode = this._toggleMinimizeMode.bind(this);
this._target.on("close", this.destroy);
@ -481,7 +488,7 @@ Toolbox.prototype = {
["toolbox-force-reload-key", true],
["toolbox-force-reload-key2", true]
].forEach(([id, force]) => {
this.doc.getElementById(id).addEventListener("command", (event) => {
this.doc.getElementById(id).addEventListener("command", () => {
this.reloadTarget(force);
}, true);
});
@ -493,6 +500,9 @@ Toolbox.prototype = {
let prevKey = this.doc.getElementById("toolbox-previous-tool-key");
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
// cancelled with stopPropagation on the keypress, and not preventDefault.
this.doc.addEventListener("keypress", this._splitConsoleOnKeypress, false);
@ -703,18 +713,11 @@ Toolbox.prototype = {
if (this.hostType == Toolbox.HostType.BOTTOM) {
let minimizeBtn = this.doc.createElement("toolbarbutton");
minimizeBtn.id = "toolbox-dock-bottom-minimize";
minimizeBtn.className = "maximized";
minimizeBtn.setAttribute("tooltiptext",
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);
});
minimizeBtn.addEventListener("command", this._toggleMinimizeMode);
dockBox.appendChild(minimizeBtn);
// Show the button in its maximized state.
this._onBottomHostMaximized();
// Update the label and icon when the state changes.
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() {
let btn = this.doc.querySelector("#toolbox-dock-bottom-minimize");
btn.className = "minimized";
btn.setAttribute("tooltiptext",
toolboxStrings("toolboxDockButtons.bottom.maximize"));
toolboxStrings("toolboxDockButtons.bottom.maximize") + " " +
this._getMinimizeButtonShortcutTooltip());
},
_onBottomHostMaximized: function() {
let btn = this.doc.querySelector("#toolbox-dock-bottom-minimize");
btn.className = "maximized";
btn.setAttribute("tooltiptext",
toolboxStrings("toolboxDockButtons.bottom.minimize"));
toolboxStrings("toolboxDockButtons.bottom.minimize") + " " +
this._getMinimizeButtonShortcutTooltip());
},
_onToolSelectWhileMinimized: function() {
@ -780,6 +794,19 @@ Toolbox.prototype = {
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
*/

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

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

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

@ -11,7 +11,7 @@
const TEST_URL = TEST_URL_ROOT + "doc_markup_search.html";
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);
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
// 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
// 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>");
let updated = inspector.once("inspector-updated");
searchUsingSelectorSearch("em", inspector);
yield updated;
yield searchFor("em", inspector);
container = yield getContainerForSelector("em", inspector);
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");
for (let node of ["span", "li", "ul"]) {
let updated = inspector.once("inspector-updated");
searchUsingSelectorSearch(node, inspector);
yield updated;
yield searchFor(node, inspector);
nodeFront = yield getNodeFront(node, inspector);
is(inspector.selection.nodeFront, nodeFront,
"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.
NETWORK_EVENT: "NetMonitor:NetworkEvent",
// When a network event is added to the view
REQUEST_ADDED: "NetMonitor:RequestAdded",
// When request headers begin and finish receiving.
UPDATING_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdating:RequestHeaders",
RECEIVED_REQUEST_HEADERS: "NetMonitor:NetworkEventUpdated:RequestHeaders",
@ -528,34 +531,33 @@ NetworkEventsHandler.prototype = {
*/
_onNetworkEventUpdate: function(type, { packet, networkInfo }) {
let { actor, request: { url } } = networkInfo;
switch (packet.updateType) {
case "requestHeaders":
this.webConsoleClient.getRequestHeaders(actor, this._onRequestHeaders);
window.emit(EVENTS.UPDATING_REQUEST_HEADERS, [actor, url]);
window.emit(EVENTS.UPDATING_REQUEST_HEADERS, actor);
break;
case "requestCookies":
this.webConsoleClient.getRequestCookies(actor, this._onRequestCookies);
window.emit(EVENTS.UPDATING_REQUEST_COOKIES, [actor, url]);
window.emit(EVENTS.UPDATING_REQUEST_COOKIES, actor);
break;
case "requestPostData":
this.webConsoleClient.getRequestPostData(actor, this._onRequestPostData);
window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, [actor, url]);
window.emit(EVENTS.UPDATING_REQUEST_POST_DATA, actor);
break;
case "securityInfo":
NetMonitorView.RequestsMenu.updateRequest(actor, {
securityState: networkInfo.securityInfo,
});
this.webConsoleClient.getSecurityInfo(actor, this._onSecurityInfo);
window.emit(EVENTS.UPDATING_SECURITY_INFO, [actor, url]);
window.emit(EVENTS.UPDATING_SECURITY_INFO, actor);
break;
case "responseHeaders":
this.webConsoleClient.getResponseHeaders(actor, this._onResponseHeaders);
window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, [actor, url]);
window.emit(EVENTS.UPDATING_RESPONSE_HEADERS, actor);
break;
case "responseCookies":
this.webConsoleClient.getResponseCookies(actor, this._onResponseCookies);
window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, [actor, url]);
window.emit(EVENTS.UPDATING_RESPONSE_COOKIES, actor);
break;
case "responseStart":
NetMonitorView.RequestsMenu.updateRequest(actor, {
@ -566,7 +568,7 @@ NetworkEventsHandler.prototype = {
statusText: networkInfo.response.statusText,
headersSize: networkInfo.response.headersSize
});
window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, [actor, url]);
window.emit(EVENTS.STARTED_RECEIVING_RESPONSE, actor);
break;
case "responseContent":
NetMonitorView.RequestsMenu.updateRequest(actor, {
@ -575,14 +577,14 @@ NetworkEventsHandler.prototype = {
mimeType: networkInfo.response.content.mimeType
});
this.webConsoleClient.getResponseContent(actor, this._onResponseContent);
window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, [actor, url]);
window.emit(EVENTS.UPDATING_RESPONSE_CONTENT, actor);
break;
case "eventTimings":
NetMonitorView.RequestsMenu.updateRequest(actor, {
totalTime: networkInfo.totalTime
});
this.webConsoleClient.getEventTimings(actor, this._onEventTimings);
window.emit(EVENTS.UPDATING_EVENT_TIMINGS, [actor, url]);
window.emit(EVENTS.UPDATING_EVENT_TIMINGS, actor);
break;
}
},
@ -596,8 +598,9 @@ NetworkEventsHandler.prototype = {
_onRequestHeaders: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
requestHeaders: aResponse
});
}, () => {
window.emit(EVENTS.RECEIVED_REQUEST_HEADERS, aResponse.from);
});
},
/**
@ -609,8 +612,9 @@ NetworkEventsHandler.prototype = {
_onRequestCookies: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
requestCookies: aResponse
});
}, () => {
window.emit(EVENTS.RECEIVED_REQUEST_COOKIES, aResponse.from);
});
},
/**
@ -622,8 +626,9 @@ NetworkEventsHandler.prototype = {
_onRequestPostData: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
requestPostData: aResponse
});
}, () => {
window.emit(EVENTS.RECEIVED_REQUEST_POST_DATA, aResponse.from);
});
},
/**
@ -635,9 +640,9 @@ NetworkEventsHandler.prototype = {
_onSecurityInfo: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
securityInfo: aResponse.securityInfo
});
}, () => {
window.emit(EVENTS.RECEIVED_SECURITY_INFO, aResponse.from);
});
},
/**
@ -649,8 +654,9 @@ NetworkEventsHandler.prototype = {
_onResponseHeaders: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
responseHeaders: aResponse
});
}, () => {
window.emit(EVENTS.RECEIVED_RESPONSE_HEADERS, aResponse.from);
});
},
/**
@ -662,8 +668,9 @@ NetworkEventsHandler.prototype = {
_onResponseCookies: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
responseCookies: aResponse
});
}, () => {
window.emit(EVENTS.RECEIVED_RESPONSE_COOKIES, aResponse.from);
});
},
/**
@ -675,8 +682,9 @@ NetworkEventsHandler.prototype = {
_onResponseContent: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
responseContent: aResponse
});
}, () => {
window.emit(EVENTS.RECEIVED_RESPONSE_CONTENT, aResponse.from);
});
},
/**
@ -688,8 +696,9 @@ NetworkEventsHandler.prototype = {
_onEventTimings: function(aResponse) {
NetMonitorView.RequestsMenu.updateRequest(aResponse.from, {
eventTimings: aResponse
});
}, () => {
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 FREETEXT_FILTER_SEARCH_DELAY = 200; // ms
const {DeferredTask} = Cu.import("resource://gre/modules/DeferredTask.jsm", {});
/**
* Object defining the network monitor view components.
*/
@ -95,6 +97,7 @@ let NetMonitorView = {
* Destroys the network monitor view.
*/
destroy: function() {
this._isDestroyed = true;
this.Toolbar.destroy();
this.RequestsMenu.destroy();
this.NetworkDetails.destroy();
@ -375,7 +378,6 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
this.allowFocusOnRightClick = true;
this.maintainSelectionVisible = true;
this.widget.autoscrollWithAppendedItems = true;
this.widget.addEventListener("select", this._onSelect, false);
this.widget.addEventListener("swap", this._onSwap, false);
@ -394,6 +396,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
this._onContextToggleRawHeadersCommand = this.toggleRawHeaders.bind(this);
this._onContextPerfCommand = () => NetMonitorView.toggleFrontendMode();
this._onReloadCommand = () => NetMonitorView.reloadPage();
this._flushRequestsTask = new DeferredTask(this._flushRequests, REQUESTS_REFRESH_RATE);
this.sendCustomRequestEvent = this.sendCustomRequest.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);
this.freetextFilterBox.removeEventListener("input", this.requestsFreetextFilterEvent, false);
this.freetextFilterBox.removeEventListener("command", this.requestsFreetextFilterEvent, false);
this.userInputTimer.cancel();
this._flushRequestsTask.disarm();
$("#network-request-popup").removeEventListener("popupshowing", this._onContextShowing, false);
$("#request-menu-context-newtab").removeEventListener("command", this._onContextNewTabCommand, false);
$("#request-menu-context-copy-url").removeEventListener("command", this._onContextCopyUrlCommand, false);
@ -496,6 +502,8 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
*/
reset: function() {
this.empty();
this._addQueue = [];
this._updateQueue = [];
this._firstRequestStartedMillis = -1;
this._lastRequestEndedMillis = -1;
},
@ -503,7 +511,18 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
/**
* 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.
@ -523,47 +542,14 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
* Indicates if the result came from the browser cache
*/
addRequest: function(aId, aStartedDateTime, aMethod, aUrl, aIsXHR, aFromCache) {
// Convert the received date/time string to a unix timestamp.
let unixTime = Date.parse(aStartedDateTime);
this._addQueue.push([aId, aStartedDateTime, aMethod, aUrl, aIsXHR, aFromCache]);
// Create the element node for the network request item.
let menuView = this._createMenuView(aMethod, aUrl);
// 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
// Lazy updating is disabled in some tests.
if (!this.lazyUpdate) {
return void this._flushRequests();
}
});
// 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
* An object containing several { key: value } tuples of network info.
* 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) {
// Prevent interference from zombie updates received after target closed.
if (NetMonitorView._isDestroyed) {
return;
}
this._updateQueue.push([aId, aData]);
updateRequest: function(aId, aData, aCallback) {
this._updateQueue.push([aId, aData, aCallback]);
// Lazy updating is disabled in some tests.
if (!this.lazyUpdate) {
return void this._flushRequests();
}
// Allow requests to settle down first.
setNamedTimeout(
"update-requests", REQUESTS_REFRESH_RATE, () => this._flushRequests());
this._flushRequestsTask.arm();
},
/**
* Starts adding all queued additional information about network requests.
*/
_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
// 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);
if (!requestItem) {
// Packet corresponds to a dead request item, target navigated.
@ -1482,6 +1518,10 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
}
}
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.
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.
// Freshly added requests may not yet contain all the information required
@ -1650,9 +1694,9 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
}
case "responseContent": {
let { mimeType } = aItem.attachment;
let { text, encoding } = aValue.content;
if (mimeType.includes("image/")) {
let { text, encoding } = aValue.content;
let responseBody = yield gNetwork.getString(text);
let node = $(".requests-menu-icon", aItem.target);
node.src = "data:" + mimeType + ";" + encoding + "," + responseBody;
@ -2158,6 +2202,7 @@ RequestsMenuView.prototype = Heritage.extend(WidgetMethods, {
_firstRequestStartedMillis: -1,
_lastRequestEndedMillis: -1,
_updateQueue: [],
_addQueue: [],
_updateTimeout: null,
_resizeTimeout: null,
_activeFilters: ["all"],

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

@ -95,6 +95,11 @@ let test = Task.async(function* () {
info("Waiting for NETWORK_EVENT");
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.");
let item = RequestsMenu.getItemAtIndex(0);
RequestsMenu.selectedItem = item;

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

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

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

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

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

@ -35,6 +35,7 @@ function* spawnTest() {
busyWait(WAIT_TIME); // allow the profiler module to sample some cpu activity
yield front.stopRecording(firstRecording);
info("The first recording is " + firstRecording.getDuration() + "ms long.");
is(firstRecordingStartTime, 0,
"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
yield front.stopRecording(secondRecording);
info("The second recording is " + secondRecording.getDuration() + "ms long.");
let secondRecordingProfile = secondRecording.getProfile();
let secondRecordingSamples = secondRecordingProfile.threads[0].samples.data;

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

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

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

@ -7,9 +7,8 @@
function* spawnTest() {
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 { TIMELINE_BLUEPRINT } = devtools.require("devtools/performance/markers");
let { getMarkerLabel } = devtools.require("devtools/performance/marker-utils");
// Hijack the markers massaging part of creating the waterfall view,
@ -34,38 +33,35 @@ function* spawnTest() {
yield stopRecording(panel);
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 markers = PerformanceController.getCurrentRecording().getMarkers();
ok(bars.length > 2, "Got at least 3 markers (1)");
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++) {
let bar = bars[i];
let m = markers[i];
let mkr = markers[i];
EventUtils.sendMouseEvent({ type: "mousedown" }, bar);
is($("#waterfall-details .marker-details-type").getAttribute("value"), getMarkerLabel(m),
"Sidebar title matches markers name.");
let type = $(".marker-details-type").getAttribute("value");
let tooltip = $(".marker-details-duration").getAttribute("tooltiptext");
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.
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.
// 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(m.end)) !== -1, "Tooltip has end time.");
ok(tooltip.indexOf(toMs(mkr.start)) !== -1, "Tooltip has start time.");
ok(tooltip.indexOf(toMs(mkr.end)) !== -1, "Tooltip has end time.");
}
yield teardown(panel);

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

@ -10,12 +10,9 @@ const Cu = Components.utils;
Cu.import("resource:///modules/devtools/ViewHelpers.jsm");
Cu.import("resource://gre/modules/devtools/event-emitter.js");
const {DeferredTask} = Cu.import("resource://gre/modules/DeferredTask.jsm", {});
this.EXPORTED_SYMBOLS = ["SideMenuWidget"];
const SCROLL_FREQUENCY = 16;
/**
* A simple side menu, with the ability of grouping menu items.
*
@ -81,14 +78,6 @@ SideMenuWidget.prototype = {
*/
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
* grouping by name.
@ -106,29 +95,10 @@ SideMenuWidget.prototype = {
* The element associated with the displayed item.
*/
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 item = this._getMenuItemForGroup(group, aContents, aAttachment);
let element = item.insertSelfAt(aIndex);
if (maintainScrollAtBottom) {
this.scrollToBottom();
}
return element;
},
@ -158,30 +128,8 @@ SideMenuWidget.prototype = {
* If the user scrolls in the meantime, cancel this operation.
*/
scrollToBottom: function() {
// Lazily attach this functionality to the object, so it won't get
// created unless if this scrollToBottom behavior is needed.
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 toolbox = yield gDevTools.showToolbox(target, "netmonitor");
let netmonitor = toolbox.getPanel("netmonitor");
netmonitor._view.RequestsMenu.lazyUpdate = false;
info("Navigating to test page");
yield navigateTo(TEST_URL);

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

@ -47,7 +47,7 @@
</div>
<!-- TODO : Bug 1165122 : Show this button by default -->
<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 id="pseudo-class-panel" class="devtools-toolbar" hidden="true">
<label><input id="pseudo-hover-toggle" type="checkbox" value=":hover" />:hover</label>

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

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

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

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

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

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

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

@ -27,6 +27,11 @@
- tree. -->
<!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
- properties to display e.g. due to search criteria this message is
- displayed. -->

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

@ -21,6 +21,9 @@
<!ENTITY toolboxZoomReset.key "0">
<!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
- the iframes menu list that appears only when the document has some.
- It allows you to switch the context of the whole toolbox. -->

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

@ -19,6 +19,8 @@
<!ENTITY importFromChrome.accesskey "C">
<!ENTITY importFromFirefox.label "Firefox">
<!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.">

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

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

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

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

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

@ -22,6 +22,8 @@
<!ENTITY locbar.bookmarks.accesskey "k">
<!ENTITY locbar.openpage.label "Open tabs">
<!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.accesskey "A">

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

@ -121,6 +121,11 @@
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 {
-moz-appearance: none;
}

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

@ -68,6 +68,18 @@
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-forward:-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)
nsPermissionManager::nsPermissionManager()
: mLargestID(0)
: mMemoryOnlyDB(false)
, mLargestID(0)
, mIsShuttingDown(false)
{
}
@ -426,6 +427,10 @@ nsPermissionManager::Init()
{
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);
if (NS_SUCCEEDED(rv)) {
mObserverService->AddObserver(this, "profile-before-change", true);
@ -457,6 +462,23 @@ nsPermissionManager::RefreshPermission() {
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
nsPermissionManager::InitDB(bool aRemoveFile)
{
@ -480,13 +502,10 @@ nsPermissionManager::InitDB(bool aRemoveFile)
}
}
nsCOMPtr<mozIStorageService> storage = do_GetService(MOZ_STORAGE_SERVICE_CONTRACTID);
if (!storage)
return NS_ERROR_UNEXPECTED;
// cache a connection to the hosts database
rv = storage->OpenDatabase(permissionsFile, getter_AddRefs(mDBConn));
if (rv == NS_ERROR_FILE_CORRUPTED) {
rv = OpenDatabase(permissionsFile);
if (rv == NS_ERROR_UNEXPECTED) {
return rv;
} else if (rv == NS_ERROR_FILE_CORRUPTED) {
LogToConsole(NS_LITERAL_STRING("permissions.sqlite is corrupted! Try again!"));
// Add telemetry probe
@ -497,7 +516,7 @@ nsPermissionManager::InitDB(bool aRemoveFile)
NS_ENSURE_SUCCESS(rv, rv);
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);
LogToConsole(NS_LITERAL_STRING("OpenDatabase to permissions.sqlite is successful!"));
}
@ -515,7 +534,7 @@ nsPermissionManager::InitDB(bool aRemoveFile)
// Add telemetry probe
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);
LogToConsole(NS_LITERAL_STRING("OpenDatabase to permissions.sqlite is successful!"));

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

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

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

@ -221,6 +221,9 @@ class RefTest(object):
# And for about:newtab content fetch and pings.
prefs['browser.newtabpage.directory.source'] = 'data:application/json,{"reftest":1}'
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
prefs['xpinstall.signatures.required'] = False

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

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

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

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

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

@ -452,7 +452,6 @@ size. -->
<!ENTITY button_clear_data "Clear data">
<!ENTITY button_set "Set">
<!ENTITY button_clear "Clear">
<!ENTITY button_remember "Remember">
<!ENTITY button_copy "Copy">
<!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_no_info "This website does not supply identity information.">
<!ENTITY identity_not_encrypted "Your connection to this website is not encrypted.">
<!ENTITY identity_encrypted "Encrypted">
<!-- Mixed content notifications in site identity popup -->
<!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">
<ImageView android:id="@+id/larry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_width="@dimen/doorhanger_icon_size"
android:layout_height="@dimen/doorhanger_icon_size"
android:src="@drawable/larry"
android:paddingRight="@dimen/doorhanger_padding"/>
@ -32,6 +32,20 @@
android:visibility="gone"
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"
android:layout_height="wrap_content"
android:textAppearance="@style/TextAppearance.DoorHanger.Medium.Light"
@ -70,7 +84,8 @@
android:textColor="@color/link_blue"
android:layout_marginTop="@dimen/doorhanger_section_padding_large"
android:layout_marginBottom="@dimen/doorhanger_padding"
android:text="@string/contextmenu_site_settings"/>
android:text="@string/contextmenu_site_settings"
android:visibility="gone"/>
</LinearLayout>
</LinearLayout>

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

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

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

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

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

@ -385,7 +385,6 @@
<string name="button_clear">&button_clear;</string>
<string name="button_yes">&button_yes;</string>
<string name="button_no">&button_no;</string>
<string name="button_remember">&button_remember;</string>
<string name="button_copy">&button_copy;</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_no_info">&identity_no_info;</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="blocked_mixed_content_message_top">&blocked_mixed_content_message_top;</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 ExtendedJSONObject o = params.asJSON();
o.put(Constants.JSON_KEY_SYNC_AUTOMATICALLY, Boolean.valueOf(syncAutomatically));
o.put(Constants.JSON_KEY_VERSION, new Long(VERSION));
o.put(Constants.JSON_KEY_TIMESTAMP, new Long(System.currentTimeMillis()));
o.put(Constants.JSON_KEY_VERSION, Long.valueOf(VERSION));
o.put(Constants.JSON_KEY_TIMESTAMP, Long.valueOf(System.currentTimeMillis()));
PrintStream ps = null;
try {

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

@ -6,6 +6,8 @@ package org.mozilla.gecko.toolbar;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.widget.Toast;
import org.json.JSONException;
import org.json.JSONArray;
@ -21,6 +23,8 @@ import org.mozilla.gecko.SiteIdentity.MixedMode;
import org.mozilla.gecko.SiteIdentity.TrackingMode;
import org.mozilla.gecko.Tab;
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.ThreadUtils;
import org.mozilla.gecko.widget.AnchoredPopup;
@ -64,10 +68,13 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
private LinearLayout mIdentityKnownContainer;
private LinearLayout mIdentityUnknownContainer;
private TextView mTitle;
private TextView mEncrypted;
private TextView mHost;
private TextView mOwnerLabel;
private TextView mOwner;
private TextView mVerifier;
private TextView mSiteSettingsLink;
private View mDivider;
@ -82,6 +89,7 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
mContentButtonClickListener = new ContentNotificationButtonListener();
EventDispatcher.getInstance().registerGeckoThreadListener(this, "Doorhanger:Logins");
EventDispatcher.getInstance().registerGeckoThreadListener(this, "Permissions:CheckResult");
}
@Override
@ -101,20 +109,16 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
mIdentityUnknownContainer =
(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);
mOwnerLabel = (TextView) mIdentityKnownContainer.findViewById(R.id.owner_label);
mOwner = (TextView) mIdentityKnownContainer.findViewById(R.id.owner);
mVerifier = (TextView) mIdentityKnownContainer.findViewById(R.id.verifier);
mDivider = mIdentity.findViewById(R.id.divider_doorhanger);
final TextView siteSettingsLink = (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();
}
});
mSiteSettingsLink = (TextView) mIdentity.findViewById(R.id.site_settings_link);
}
private void updateIdentity(final SiteIdentity siteIdentity) {
@ -128,6 +132,9 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
if (isIdentityKnown) {
updateIdentityInformation(siteIdentity);
}
GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent(
"Permissions:Check", null));
}
@Override
@ -145,6 +152,24 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
} catch (JSONException 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) {
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());
String owner = siteIdentity.getOwner();
@ -287,8 +331,7 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
}
final String verifier = siteIdentity.getVerifier();
final String encrypted = siteIdentity.getEncrypted();
mVerifier.setText(verifier + "\n" + encrypted);
mVerifier.setText(verifier);
}
private void addMixedContentNotification(boolean blocked) {
@ -439,6 +482,7 @@ public class SiteIdentityPopup extends AnchoredPopup implements GeckoEventListen
void destroy() {
EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "Doorhanger:Logins");
EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "Permissions:CheckResult");
}
@Override

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

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

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

@ -51,8 +51,11 @@ var PermissionsHelper = {
observe: function observe(aSubject, aTopic, aData) {
let uri = BrowserApp.selectedBrowser.currentURI;
let check = false;
switch (aTopic) {
case "Permissions:Check":
check = true;
case "Permissions:Get":
let permissions = [];
for (let i = 0; i < this._permissonTypes.length; i++) {
@ -63,6 +66,13 @@ var PermissionsHelper = {
if (value == Services.perms.UNKNOWN_ACTION)
continue;
if (check) {
Messaging.sendRequest({
type: "Permissions:CheckResult",
hasPermissions: true
});
return;
}
// Get the strings that correspond to the permission type
let typeStrings = this._permissionStrings[type];
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
this._currentPermissions = permissions;

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

@ -148,7 +148,7 @@ let lazilyLoadedObserverScripts = [
["MemoryObserver", ["memory-pressure", "Memory:Dump"], "chrome://browser/content/MemoryObserver.js"],
["ConsoleAPI", ["console-api-log-event"], "chrome://browser/content/ConsoleAPI.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"],
["Feedback", ["Feedback:Show"], "chrome://browser/content/Feedback.js"],
["SelectionHandler", ["TextSelection:Get"], "chrome://browser/content/SelectionHandler.js"],
@ -7028,6 +7028,7 @@ var IdentityHandler = {
locationObj.host = location.host;
locationObj.hostname = location.hostname;
locationObj.port = location.port;
locationObj.origin = location.origin;
} catch (ex) {
// 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
@ -7039,6 +7040,7 @@ var IdentityHandler = {
let mixedMode = this.getMixedMode(aState);
let trackingMode = this.getTrackingMode(aState);
let result = {
origin: locationObj.origin,
mode: {
identity: identityMode,
mixed: mixedMode,
@ -7052,8 +7054,7 @@ var IdentityHandler = {
return result;
}
// Ideally we'd just make this a Java string
result.encrypted = Strings.browser.GetStringFromName("identity.encrypted2");
result.encrypted = true;
result.host = this.getEffectiveHost();
let iData = this.getIdentityData();

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

@ -146,16 +146,24 @@ LoginManagerPrompter.prototype = {
* String message to be displayed in the doorhanger
* @param aButtons
* Buttons to display with the doorhanger
* @param aActionText
* Object with text to be displayed as clickable, along with a bundle to create an action
*
* @param aUsername
* 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 chromeWin = this._getChromeWindow(notifyWin).wrappedJSObject;
let browser = chromeWin.BrowserApp.getBrowserForWindow(notifyWin);
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
// across the first location change.
@ -163,12 +171,11 @@ LoginManagerPrompter.prototype = {
// at the post-authentication page. I don't see a good way to
// heuristically determine when to ignore such location changes, so
// we'll try ignoring location changes based on a time interval.
let options = {
persistWhileVisible: true,
timeout: Date.now() + 10000,
title: aTitle,
actionText: aActionText
actionText: actionText
}
var nativeWindow = this._getNativeWindow();
@ -194,13 +201,6 @@ LoginManagerPrompter.prototype = {
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
// in scope here; set one to |this._pwmgr| so we can get back to pwmgr
// 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"),
callback: function() {
self._updateLogin(aOldLogin, aNewPassword);
callback: function(checked, response) {
let password = response ? response["password"] : aNewPassword;
self._updateLogin(aOldLogin, password);
promptHistogram.add(PROMPT_UPDATE);
},
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.state_and_country=%S, %S
identity.identified.title_with_country=%S (%S)
identity.encrypted2=Encrypted
# Geolocation UI
geolocation.allow=Share

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

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

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

@ -206,8 +206,7 @@ public class testSettingsMenuItems extends PixelTest {
// 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";
String[] tabQueue = { mStringHelper.TAB_QUEUE_LABEL, expected };
final String[] tabQueue = { mStringHelper.TAB_QUEUE_LABEL, mStringHelper.TAB_QUEUE_SUMMARY };
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
// AddonManager.SCOPE_PROFILE + AddonManager.SCOPE_APPLICATION
user_pref("extensions.enabledScopes", 5);
user_pref("extensions.autoDisableScopes", 0);
// Disable metadata caching for installed add-ons by default
user_pref("extensions.getAddons.cache.enabled", false);
// 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.
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": {
"talos_repo": "https://hg.mozilla.org/build/talos",
"talos_revision": "e04283e4f2b2"
"talos_revision": "4a8d22dd38c4"
},
"extra_options": {
"android": [ "--apkPath=%(apk_path)s" ]

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

@ -800,6 +800,20 @@ LoginManagerPrompter.prototype = {
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 foundLogins = Services.logins.findLogins({}, login.hostname,
login.formSubmitURL,
@ -821,6 +835,7 @@ LoginManagerPrompter.prototype = {
if (element) {
element.setAttribute("buttonlabel", label);
element.setAttribute("buttonaccesskey", accessKey);
updateButtonStatus(element);
}
};
@ -831,6 +846,8 @@ LoginManagerPrompter.prototype = {
.setAttribute("value", login.username);
chromeDoc.getElementById("password-notification-password")
.setAttribute("value", login.password);
chromeDoc.getElementById("password-notification-password")
.setAttribute("show-content", showPasswordPlaceholder);
updateButtonLabel();
};
@ -841,11 +858,19 @@ LoginManagerPrompter.prototype = {
chromeDoc.getElementById("password-notification-password").value;
};
let onUsernameInput = () => {
let onInput = () => {
readDataFromUI();
updateButtonLabel();
};
let onPasswordFocus = () => {
chromeDoc.getElementById("password-notification-password").type = "";
};
let onPasswordBlur = () => {
chromeDoc.getElementById("password-notification-password").type = "password";
};
let persistData = () => {
let foundLogins = Services.logins.findLogins({}, login.hostname,
login.formSubmitURL,
@ -862,7 +887,12 @@ LoginManagerPrompter.prototype = {
login.usernameField,
login.passwordField));
} else if (logins.length == 1) {
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 {
Cu.reportError("Unexpected match of multiple logins.");
}
@ -896,6 +926,7 @@ LoginManagerPrompter.prototype = {
}] : null;
let usernamePlaceholder = this._getLocalizedString("noUsernamePlaceholder");
let showPasswordPlaceholder = this._getLocalizedString("showPasswordPlaceholder");
let displayHost = this._getShortDisplayHost(login.hostname);
this._getPopupNote().show(
@ -913,9 +944,17 @@ LoginManagerPrompter.prototype = {
switch (topic) {
case "showing":
currentNotification = this;
writeDataToUI();
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;
case "dismissed":
readDataFromUI();
@ -923,7 +962,13 @@ LoginManagerPrompter.prototype = {
case "removed":
currentNotification = null;
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;
}
return false;

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

@ -635,9 +635,21 @@ AsyncFetchAndSetIconFromNetwork::OnStopRequest(nsIRequest* aRequest,
return NS_OK;
}
nsCOMPtr<nsIChannel> channel = do_QueryInterface(aRequest);
// aRequest should always QI to nsIChannel.
// 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 (mIcon.mimeType.IsEmpty()) {
@ -649,10 +661,6 @@ AsyncFetchAndSetIconFromNetwork::OnStopRequest(nsIRequest* aRequest,
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);
// 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/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "SearchSuggestionController",
"resource://gre/modules/SearchSuggestionController.jsm");
const SEARCH_ENGINE_TOPIC = "browser-search-engine-modified";
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,
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;
this.PlacesSearchAutocompleteProvider = Object.freeze({
@ -214,4 +272,12 @@ this.PlacesSearchAutocompleteProvider = Object.freeze({
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_OPENPAGE = [ "suggest.openpage", true ];
const PREF_SUGGEST_HISTORY_ONLYTYPED = [ "suggest.history.onlyTyped", false ];
const PREF_SUGGEST_SEARCHES = [ "suggest.searches", true ];
// Match type constants.
// 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.
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.
// The hostname will already have been checked for general validity, so we
// 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", () => {
let prefs = new Preferences(PREF_BRANCH);
let types = ["History", "Bookmark", "Openpage", "Typed"];
let types = ["History", "Bookmark", "Openpage", "Typed", "Searches"];
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) {
// 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.suggestOpenpage = prefs.get(...PREF_SUGGEST_OPENPAGE);
store.suggestTyped = prefs.get(...PREF_SUGGEST_HISTORY_ONLYTYPED);
store.suggestSearches = prefs.get(...PREF_SUGGEST_SEARCHES);
}
if (store.enabled) {
@ -395,6 +409,7 @@ XPCOMUtils.defineLazyGetter(this, "Prefs", () => {
store.suggestBookmark = prefs.get(...PREF_SUGGEST_BOOKMARK);
store.suggestOpenpage = prefs.get(...PREF_SUGGEST_OPENPAGE);
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 (!store.suggestHistory) {
@ -566,6 +581,7 @@ function Search(searchString, searchParam, autocompleteListener,
let params = new Set(searchParam.split(" "));
this._enableActions = params.has("enable-actions");
this._disablePrivateActions = params.has("disable-private-actions");
this._inPrivateWindow = params.has("private-window");
this._searchTokens =
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.
this._usedURLs = new Set();
this._usedPlaceIds = new Set();
this._searchSuggestionInsertCounter = 0;
}
Search.prototype = {
@ -704,6 +722,10 @@ Search.prototype = {
this._sleepDeferred.resolve();
this._sleepDeferred = null;
}
if (this._searchSuggestionController) {
this._searchSuggestionController.stop();
this._searchSuggestionController = null;
}
this.pending = false;
},
@ -806,7 +828,22 @@ Search.prototype = {
// IMPORTANT: No other first result heuristics should run after
// _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)
return;
@ -837,6 +874,13 @@ Search.prototype = {
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) {
@ -966,7 +1010,7 @@ Search.prototype = {
match.engineAlias = alias;
let query = this._searchTokens.slice(1).join(" ");
yield this._addSearchEngineMatch(match, query);
this._addSearchEngineMatch(match, query);
return true;
},
@ -976,16 +1020,17 @@ Search.prototype = {
return;
let query = this._originalSearchString;
yield this._addSearchEngineMatch(match, query);
this._addSearchEngineMatch(match, query);
},
_addSearchEngineMatch: function* (match, query) {
_addSearchEngineMatch(match, query, suggestion) {
let actionURLParams = {
engineName: match.engineName,
input: this._originalSearchString,
searchQuery: query,
};
if (suggestion)
actionURLParams.searchSuggestion = suggestion;
if (match.engineAlias) {
actionURLParams.alias = match.engineAlias;
}
@ -1129,12 +1174,43 @@ Search.prototype = {
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) {
// 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.
if (!this.pending)
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;
// 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
* purposes.
*/
[scriptable, uuid(6e252399-77f9-4f11-98cc-6fa9fab96f92)]
[scriptable, uuid(61b6348a-09e1-4810-8057-f8cb3cec6ef8)]
interface mozIPlacesAutoComplete : nsISupports
{
//////////////////////////////////////////////////////////////////////////////
@ -102,6 +102,11 @@ interface mozIPlacesAutoComplete : nsISupports
*/
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.
*

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

@ -57,6 +57,16 @@ XPCOMUtils.defineLazyGetter(this, "SMALLPNG_DATA_URI", function() {
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAA" +
"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();

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

@ -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_result_sort.js]
[test_sql_guid_functions.js]
[test_svg_favicon.js]
[test_tag_autocomplete_search.js]
[test_tagging.js]
[test_telemetry.js]

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

@ -11,3 +11,7 @@
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;
if (index >= 0 && index < aReplacements.length) {
let replacement = aReplacements[index];
pairs.push([...replacement]);
pairs.push([...aReplacements[index]]);
}
} else {
pairs.push([part, false]);
pairs.push([part]);
}
}
@ -1572,8 +1571,15 @@ extends="chrome://global/content/bindings/popup.xml#popup">
if (emphasise) {
let span = aDescriptionElement.appendChild(
document.createElementNS("http://www.w3.org/1999/xhtml", "span"));
span.className = "ac-emphasize-text";
span.textContent = text;
switch(emphasise) {
case "match":
span.className = "ac-emphasize-text";
break;
case "selected":
span.className = "ac-selected-text";
break;
}
} else {
aDescriptionElement.appendChild(document.createTextNode(text));
}
@ -1629,10 +1635,22 @@ extends="chrome://global/content/bindings/popup.xml#popup">
} else if (action.type == "searchengine") {
emphasiseUrl = false;
let sourceStr = this._stringBundle.GetStringFromName("searchWithEngineForQuery");
title = this._generateEmphasisPairs(sourceStr, [
[action.params.engineName, false],
[action.params.searchQuery, true]
// The order here is not localizable, we default to appending
// "- Search with Engine" to the search string, to be able to
// properly generate emphasis pairs. That said, no localization
// 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
// 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");
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");
title = this._generateEmphasisPairs(sourceStr, [
[displayUrl, true],
[displayUrl, "match"],
]);
types.delete("autofill");

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

@ -38,7 +38,8 @@ exports.safeErrorString = function safeErrorString(aError) {
}
} 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);
}
/**

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