Merge latest green inbound changeset and mozilla-central

This commit is contained in:
Ed Morley 2014-01-29 10:32:34 +00:00
Родитель cc60a4deef 5695dd3da3
Коммит 2a9e32f301
76 изменённых файлов: 1518 добавлений и 545 удалений

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

@ -4,3 +4,4 @@
# 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/.
BROWSER_CHROME_MANIFESTS += ['test/browser.ini']

11
addon-sdk/test/Math.jsm Normal file
Просмотреть файл

@ -0,0 +1,11 @@
/* 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";
this.EXPORTED_SYMBOLS = ["Math"];
this.Math = {
square: function (x) { return x * x; }
};

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

@ -0,0 +1,14 @@
[DEFAULT]
support-files =
head.js
Math.jsm
math.js
data.json
invalid.json
[browser_sdk_loader_sdk_modules.js]
[browser_sdk_loader_sdk_gui_modules.js]
[browser_sdk_loader_jsm_modules.js]
[browser_sdk_loader_js_modules.js]
[browser_sdk_loader_json.js]
[browser_sdk_loader_chrome.js]
[browser_sdk_loader_chrome_in_sdk.js]

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

@ -0,0 +1,24 @@
function test () {
let loader = makeLoader();
let module = Module("./main", gTestPath);
let require = Require(loader, module);
const { Ci, Cc, Cu, components } = require("chrome");
let { generateUUID } = Cc["@mozilla.org/uuid-generator;1"]
.getService(Ci.nsIUUIDGenerator);
ok(isUUID(generateUUID()), "chrome.Cc and chrome.Ci works");
let { ID: parseUUID } = components;
let uuidString = "00001111-2222-3333-4444-555566667777";
let parsed = parseUUID(uuidString);
is(parsed, "{" + uuidString + "}", "chrome.components works");
const { defer } = Cu.import("resource://gre/modules/Promise.jsm").Promise;
let { promise, resolve } = defer();
resolve(5);
promise.then(val => {
is(val, 5, "chrome.Cu works");
finish();
});
}

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

@ -0,0 +1,19 @@
function test () {
let loader = makeLoader();
let module = Module("./main", gTestPath);
let require = Require(loader, module);
// sdk/util/uuid uses Cc, Ci, components
const { uuid } = require("sdk/util/uuid");
ok(isUUID(uuid()), "chrome.Cc and chrome.Ci works in SDK includes");
let uuidString = '00001111-2222-3333-4444-555566667777';
let parsed = uuid(uuidString);
is(parsed, '{' + uuidString + '}', "chrome.components works in SDK includes");
// sdk/base64 uses Cu
const { encode } = require("sdk/base64");
is(encode("hello"), "aGVsbG8=", "chrome.Cu works in SDK includes");
finish();
}

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

@ -0,0 +1,33 @@
function test () {
let loader = makeLoader();
let module = Module("./main", gTestPath);
let require = Require(loader, module);
try {
let Model = require("./cant-find-me");
ok(false, "requiring a JS module that doesn't exist should throw");
}
catch (e) {
ok(e, "requiring a JS module that doesn't exist should throw");
}
/*
* Relative resource:// URI of JS
*/
let { square } = require("./math");
is(square(5), 25, "loads relative URI of JS");
/*
* Absolute resource:// URI of JS
*/
let { has } = require("resource://gre/modules/commonjs/sdk/util/array");
let testArray = ['rock', 'paper', 'scissors'];
ok(has(testArray, 'rock'), "loads absolute resource:// URI of JS");
ok(!has(testArray, 'dragon'), "loads absolute resource:// URI of JS");
finish();
}

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

@ -0,0 +1,31 @@
function test () {
let loader = makeLoader();
let module = Module("./main", gTestPath);
let require = Require(loader, module);
try {
let Model = require("resource://gre/modules/BlinkTag.jsm");
ok(false, "requiring a JS module that doesn't exist should throw");
}
catch (e) {
ok(e, "requiring a JS module that doesn't exist should throw");
}
/*
* Relative resource:// URI of JSM
*/
let { square } = require("./Math.jsm").Math;
is(square(5), 25, "loads relative URI of JSM");
/*
* Absolute resource:// URI of JSM
*/
let { defer } = require("resource://gre/modules/Promise.jsm").Promise;
let { resolve, promise } = defer();
resolve(5);
promise.then(val => {
is(val, 5, "loads absolute resource:// URI of JSM");
}).then(finish);
}

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

@ -0,0 +1,23 @@
function test () {
let loader = makeLoader();
let module = Module("./main", gTestPath);
let require = Require(loader, module);
/*
* Relative resource:// URI of json
*/
let data = require("./data.json");
is(data.title, "jetpack mochitests", "loads relative JSON");
is(data.dependencies.underscore, "1.0.0", "loads relative JSON");
try {
let data = require("./invalid.json");
ok(false, "parsing an invalid JSON should throw");
}
catch (e) {
ok(e, "parsing an invalid JSON should throw");
}
finish();
}

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

@ -0,0 +1,18 @@
function test () {
// Load `constructor` as global since tabs uses `traits`
// that use this module
let loader = makeLoader({ globals: constructor });
let module = Module("./main", "scratchpad://");
let require = Require(loader, module);
let tabs = require("sdk/tabs");
tabs.open({
url: "about:blank",
onReady: function (tab) {
is(tab.url, "about:blank", "correct uri for tab");
is(tabs.activeTab, tab, "correctly active tab");
tab.close(finish);
}
});
}

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

@ -0,0 +1,22 @@
function test () {
let loader = makeLoader();
let module = Module("./main", "scratchpad://");
let require = Require(loader, module);
let { has } = require("sdk/util/array");
let { extend } = require("sdk/util/object");
let testArray = [1, 'hello', 2, 'hi'];
ok(has(testArray, 1), 'loader loading simple array utils');
ok(has(testArray, 'hello'), 'loader loading simple array utils');
ok(!has(testArray, 4), 'loader loading simple array utils');
let testObj1 = { strings: 6, prop: [] };
let testObj2 = { name: 'Tosin Abasi', strings: 8 };
let extended = extend(testObj1, testObj2);
is(extended.name, 'Tosin Abasi', 'loader loading simple object utils');
is(extended.strings, 8, 'loader loading simple object utils');
is(extended.prop, testObj1.prop, 'loader loading simple object utils');
finish();
}

6
addon-sdk/test/data.json Normal file
Просмотреть файл

@ -0,0 +1,6 @@
{
"title": "jetpack mochitests",
"dependencies": {
"underscore": "1.0.0"
}
}

79
addon-sdk/test/head.js Normal file
Просмотреть файл

@ -0,0 +1,79 @@
/* 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/. */
const { utils: Cu } = Components;
const { Services } = Cu.import("resource://gre/modules/Services.jsm", {});
const LoaderModule = Cu.import("resource://gre/modules/commonjs/toolkit/loader.js", {}).Loader;
const { console } = Cu.import("resource://gre/modules/devtools/Console.jsm", {});
let {
Loader, main, Module, Require, unload
} = LoaderModule;
let CURRENT_DIR = gTestPath.replace(/\/[^\/]*\.js$/,'/');
let loaders = [];
// All tests are asynchronous.
waitForExplicitFinish();
let gEnableLogging = Services.prefs.getBoolPref("devtools.debugger.log");
Services.prefs.setBoolPref("devtools.debugger.log", true);
registerCleanupFunction(() => {
info("finish() was called, cleaning up...");
loaders.forEach(unload);
Services.prefs.setBoolPref("devtools.debugger.log", gEnableLogging);
});
function makePaths (root) {
return {
'./': CURRENT_DIR,
'': 'resource://gre/modules/commonjs/'
};
}
function makeLoader (options) {
let { paths, globals } = options || {};
// We have to have `console` as a global, otherwise
// many SDK modules will fail
// bug 961252
let globalDefaults = {
console: console
};
let loader = Loader({
paths: paths || makePaths(),
globals: extend({}, globalDefaults, globals) || null,
modules: {
// Needed because sdk/ modules reference utilities in
// `toolkit/loader`, until Bug 961194 is completed
'toolkit/loader': LoaderModule
},
// We need rootURI due to `sdk/self` (or are using native loader)
// which overloads with pseudo modules
// bug 961245
rootURI: CURRENT_DIR,
// We also need metadata dummy object
// bug 961245
metadata: {}
});
loaders.push(loader);
return loader;
}
function isUUID (string) {
return /^\{[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\}$/.test(string);
}
function extend (...objs) {
if (objs.length === 0 || objs.length === 1)
return objs[0] || {};
for (let i = objs.length; i > 1; i--) {
for (var prop in objs[i - 1])
objs[0][prop] = objs[i - 1][prop];
}
return objs[0];
}

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

@ -0,0 +1 @@
this isnt json

7
addon-sdk/test/math.js Normal file
Просмотреть файл

@ -0,0 +1,7 @@
/* 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";
exports.square = function square (x) { return x * x; }

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

@ -21,6 +21,16 @@ let wrapper = {
iframe: null,
init: function () {
let weave = Cc["@mozilla.org/weave/service;1"]
.getService(Ci.nsISupports)
.wrappedJSObject;
// Don't show about:accounts with FxA disabled.
if (!weave.fxAccountsEnabled) {
document.body.remove();
return;
}
let iframe = document.getElementById("remote");
this.iframe = iframe;
iframe.addEventListener("load", this);
@ -53,6 +63,11 @@ let wrapper = {
onLogin: function (accountData) {
log("Received: 'login'. Data:" + JSON.stringify(accountData));
if (accountData.customizeSync) {
Services.prefs.setBoolPref("services.sync.needsCustomization", true);
delete accountData.customizeSync;
}
fxAccounts.setSignedInUser(accountData).then(
() => {
this.injectData("message", { status: "login" });
@ -133,7 +148,7 @@ let wrapper = {
// Button onclick handlers
function handleOldSync() {
// we just want to navigate the current tab to the new location...
window.location = "https://services.mozilla.com/legacysync";
window.location = Services.urlFormatter.formatURLPref("app.support.baseURL") + "old-sync";
}
function getStarted() {

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

@ -9,14 +9,38 @@ XPCOMUtils.defineLazyGetter(this, "FxAccountsCommon", function () {
let gFxAccounts = {
_initialized: false,
_originalLabel: null,
_inCustomizationMode: false,
get weave() {
delete this.weave;
return this.weave = Cc["@mozilla.org/weave/service;1"]
.getService(Ci.nsISupports)
.wrappedJSObject;
},
get topics() {
delete this.topics;
return this.topics = [
FxAccountsCommon.ONVERIFIED_NOTIFICATION
FxAccountsCommon.ONLOGIN_NOTIFICATION,
FxAccountsCommon.ONVERIFIED_NOTIFICATION,
FxAccountsCommon.ONLOGOUT_NOTIFICATION
];
},
get button() {
delete this.button;
return this.button = document.getElementById("PanelUI-fxa-status");
},
get syncNeedsCustomization() {
try {
return Services.prefs.getBoolPref("services.sync.needsCustomization");
} catch (e) {
return false;
}
},
init: function () {
if (this._initialized) {
return;
@ -26,7 +50,15 @@ let gFxAccounts = {
Services.obs.addObserver(this, topic, false);
}
gNavToolbox.addEventListener("customizationstarting", this);
gNavToolbox.addEventListener("customizationending", this);
// Save the button's original label so that
// we can restore it if overridden later.
this._originalLabel = this.button.getAttribute("label");
this._initialized = true;
this.updateUI();
},
uninit: function () {
@ -42,11 +74,20 @@ let gFxAccounts = {
},
observe: function (subject, topic) {
this.showDoorhanger();
if (topic != FxAccountsCommon.ONVERIFIED_NOTIFICATION) {
this.updateUI();
} else if (!this.syncNeedsCustomization) {
this.showSyncStartedDoorhanger();
}
},
showDoorhanger: function () {
let panel = document.getElementById("sync-popup");
handleEvent: function (event) {
this._inCustomizationMode = event.type == "customizationstarting";
this.updateUI();
},
showDoorhanger: function (id) {
let panel = document.getElementById(id);
let anchor = document.getElementById("PanelUI-menu-button");
let iconAnchor =
@ -55,5 +96,62 @@ let gFxAccounts = {
panel.hidden = false;
panel.openPopup(iconAnchor || anchor, "bottomcenter topright");
},
showSyncStartedDoorhanger: function () {
this.showDoorhanger("sync-start-panel");
},
showSyncFailedDoorhanger: function () {
this.showDoorhanger("sync-error-panel");
},
updateUI: function () {
// Bail out if FxA is disabled.
if (!this.weave.fxAccountsEnabled) {
return;
}
// FxA is enabled, show the widget.
this.button.removeAttribute("hidden");
// Make sure the button is disabled in customization mode.
if (this._inCustomizationMode) {
this.button.setAttribute("disabled", "true");
} else {
this.button.removeAttribute("disabled");
}
// If the user is signed into their Firefox account and we are not
// currently in customization mode, show their email address.
fxAccounts.getSignedInUser().then(userData => {
if (userData && !this._inCustomizationMode) {
this.button.setAttribute("signedin", "true");
this.button.setAttribute("label", userData.email);
this.button.setAttribute("tooltiptext", userData.email);
} else {
this.button.removeAttribute("signedin");
this.button.setAttribute("label", this._originalLabel);
this.button.removeAttribute("tooltiptext");
}
});
},
toggle: function (event) {
if (event.originalTarget.hasAttribute("signedin")) {
this.openPreferences();
} else {
this.openSignInPage();
}
PanelUI.hide();
},
openPreferences: function () {
openPreferences("paneSync");
},
openSignInPage: function () {
switchToTabHavingURI("about:accounts", true);
}
};

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

@ -383,13 +383,50 @@
</panel>
<!-- Sync Panel -->
<panel id="sync-popup" type="arrow" hidden="true" noautofocus="true"
level="top" onclick="this.hidePopup();">
<hbox id="sync-popup-outer">
<image id="sync-popup-icon"/>
<vbox id="sync-popup-inner">
<description id="sync-popup-desc-syncing" value="&syncPanel.descriptionSyncing;"/>
<description id="sync-popup-desc-prefs">&syncPanel.descriptionPrefs;</description>
<panel id="sync-start-panel" class="sync-panel" type="arrow" hidden="true"
noautofocus="true" level="top" onclick="this.hidePopup();">
<hbox class="sync-panel-outer">
<image class="sync-panel-icon"/>
<vbox class="sync-panel-inner">
<description id="sync-start-panel-title"
value="&syncStartPanel.title;"/>
<description id="sync-start-panel-subtitle">
#ifdef XP_UNIX
&syncStartPanel.subTitleUnix;
#else
&syncStartPanel.subTitle;
#endif
</description>
</vbox>
</hbox>
</panel>
<!-- Sync Error Panel -->
<panel id="sync-error-panel" class="sync-panel" type="arrow" hidden="true"
noautofocus="true" level="top" onclick="this.hidePopup();">
<hbox class="sync-panel-outer">
<image class="sync-panel-icon"/>
<vbox class="sync-panel-inner">
<description id="sync-error-panel-title"
value="&syncErrorPanel.title;"/>
<description id="sync-error-panel-subtitle"
value="&syncErrorPanel.subTitle;"/>
<hbox class="sync-panel-button-box">
<button class="sync-panel-button"
#ifdef XP_UNIX
label="&syncErrorPanel.prefButtonUnix.label;"
accesskey="&syncErrorPanel.prefButtonUnix.accesskey;"
#else
label="&syncErrorPanel.prefButton.label;"
accesskey="&syncErrorPanel.prefButton.accesskey;"
#endif
onclick="gFxAccounts.openPreferences();"/>
<spacer flex="1"/>
<button class="sync-panel-button"
label="&syncErrorPanel.signInButton.label;"
accesskey="&syncErrorPanel.signInButton.accesskey;"
onclick="gFxAccounts.openSignInPage();"/>
</hbox>
</vbox>
</hbox>
</panel>

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

@ -0,0 +1,28 @@
/* 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/. */
:root {
font-size: 80%;
}
#sync-customize-pane {
-moz-padding-start: 74px;
background: top left url(chrome://browser/skin/sync-128.png) no-repeat;
background-size: 64px;
}
#sync-customize-title {
-moz-margin-start: 0;
padding-bottom: 0.5em;
font-weight: bold;
}
#sync-customize-subtitle {
font-size: 90%;
}
checkbox {
margin: 0;
padding: 0.5em 0 0;
}

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

@ -0,0 +1,9 @@
/* 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";
addEventListener("dialogaccept", function () {
window.arguments[0].accepted = true;
});

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

@ -0,0 +1,64 @@
<?xml version="1.0"?>
<!-- This Source Code Form is subject to the terms of the Mozilla Public
- License, v. 2.0. If a copy of the MPL was not distributed with this
- file, You can obtain one at http://mozilla.org/MPL/2.0/. -->
<?xml-stylesheet href="chrome://global/skin/" type="text/css"?>
<?xml-stylesheet href="chrome://browser/content/sync/customize.css" type="text/css"?>
<!DOCTYPE dialog [
<!ENTITY % syncCustomizeDTD SYSTEM "chrome://browser/locale/syncCustomize.dtd">
%syncCustomizeDTD;
]>
<dialog id="sync-customize"
windowtype="Sync:Customize"
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul"
xmlns:html="http://www.w3.org/1999/xhtml"
title="&syncCustomize.dialog.title;"
buttonlabelaccept="&syncCustomize.acceptButton.label;"
buttons="accept">
<prefpane id="sync-customize-pane">
<preferences>
<preference id="engine.bookmarks" name="services.sync.engine.bookmarks" type="bool"/>
<preference id="engine.history" name="services.sync.engine.history" type="bool"/>
<preference id="engine.tabs" name="services.sync.engine.tabs" type="bool"/>
<preference id="engine.passwords" name="services.sync.engine.passwords" type="bool"/>
<preference id="engine.addons" name="services.sync.engine.addons" type="bool"/>
<preference id="engine.prefs" name="services.sync.engine.prefs" type="bool"/>
</preferences>
<label id="sync-customize-title" value="&syncCustomize.title;"/>
<description id="sync-customize-subtitle"
#ifdef XP_UNIX
value="&syncCustomize.subTitleUnix;"
#else
value="&syncCustomize.subTitle;"
#endif
/>
<checkbox label="&engine.bookmarks.label;"
accesskey="&engine.bookmarks.accesskey;"
preference="engine.bookmarks"/>
<checkbox label="&engine.history.label;"
accesskey="&engine.history.accesskey;"
preference="engine.history"/>
<checkbox label="&engine.tabs.label;"
accesskey="&engine.tabs.accesskey;"
preference="engine.tabs"/>
<checkbox label="&engine.passwords.label;"
accesskey="&engine.passwords.accesskey;"
preference="engine.passwords"/>
<checkbox label="&engine.addons.label;"
accesskey="&engine.addons.accesskey;"
preference="engine.addons"/>
<checkbox label="&engine.prefs.label;"
accesskey="&engine.prefs.accesskey;"
preference="engine.prefs"/>
</prefpane>
<script type="application/javascript"
src="chrome://browser/content/sync/customize.js" />
</dialog>

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

@ -14,6 +14,13 @@ let gSyncUtils = {
return this.bundle = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
},
get fxAccountsEnabled() {
let service = Components.classes["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
return service.fxAccountsEnabled;
},
// opens in a new window if we're in a modal prefwindow world, in a new tab otherwise
_openLink: function (url) {
let thisDocEl = document.documentElement,
@ -71,11 +78,13 @@ let gSyncUtils = {
},
openToS: function () {
this._openLink(Weave.Svc.Prefs.get("termsURL"));
let root = this.fxAccountsEnabled ? "fxa." : "";
this._openLink(Weave.Svc.Prefs.get(root + "termsURL"));
},
openPrivacyPolicy: function () {
this._openLink(Weave.Svc.Prefs.get("privacyURL"));
let root = this.fxAccountsEnabled ? "fxa." : "";
this._openLink(Weave.Svc.Prefs.get(root + "privacyURL"));
},
openFirstSyncProgressPage: function () {

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

@ -9,6 +9,7 @@ XPCOMUtils.defineLazyModuleGetter(this, "Task",
registerCleanupFunction(function() {
// Ensure we don't pollute prefs for next tests.
Services.prefs.clearUserPref("identity.fxaccounts.enabled");
Services.prefs.clearUserPref("identity.fxaccounts.remote.uri");
});
@ -18,6 +19,7 @@ let gTests = [
desc: "Test the remote commands",
setup: function ()
{
Services.prefs.setBoolPref("identity.fxaccounts.enabled", true);
Services.prefs.setCharPref("identity.fxaccounts.remote.uri",
"https://example.com/browser/browser/base/content/test/general/accounts_testRemoteCommands.html");
},

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

@ -101,6 +101,9 @@ browser.jar:
content/browser/sync/utils.js (content/sync/utils.js)
content/browser/sync/progress.js (content/sync/progress.js)
content/browser/sync/progress.xhtml (content/sync/progress.xhtml)
* content/browser/sync/customize.xul (content/sync/customize.xul)
content/browser/sync/customize.js (content/sync/customize.js)
content/browser/sync/customize.css (content/sync/customize.css)
#endif
content/browser/safeMode.css (content/safeMode.css)
content/browser/safeMode.js (content/safeMode.js)

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

@ -15,25 +15,29 @@
</vbox>
<footer id="PanelUI-footer">
<!-- The parentNode is used so that the footer is presented as the anchor
instead of just the button being the anchor. -->
<toolbarbutton id="PanelUI-customize" label="&appMenuCustomize.label;"
exitLabel="&appMenuCustomizeExit.label;"
oncommand="gCustomizeMode.toggle();"/>
<toolbarseparator/>
<toolbarbutton id="PanelUI-help" label="&helpMenu.label;"
tooltiptext="&helpMenu.label;"
oncommand="PanelUI.showHelpView(this.parentNode);"/>
<toolbarseparator/>
<toolbarbutton id="PanelUI-quit"
<toolbarbutton id="PanelUI-fxa-status" label="&fxaSignIn.label;"
oncommand="gFxAccounts.toggle(event);"
hidden="true"/>
<hbox id="PanelUI-footer-inner">
<toolbarbutton id="PanelUI-customize" label="&appMenuCustomize.label;"
exitLabel="&appMenuCustomizeExit.label;"
oncommand="gCustomizeMode.toggle();"/>
<toolbarseparator/>
<toolbarbutton id="PanelUI-help" label="&helpMenu.label;"
tooltiptext="&helpMenu.label;"
oncommand="PanelUI.showHelpView(this.parentNode);"/>
<toolbarseparator/>
<toolbarbutton id="PanelUI-quit"
#ifdef XP_WIN
label="&quitApplicationCmdWin.label;"
tooltiptext="&quitApplicationCmdWin.label;"
label="&quitApplicationCmdWin.label;"
tooltiptext="&quitApplicationCmdWin.label;"
#else
label="&quitApplicationCmd.label;"
tooltiptext="&quitApplicationCmd.label;"
label="&quitApplicationCmd.label;"
tooltiptext="&quitApplicationCmd.label;"
#endif
command="cmd_quitApplication"/>
command="cmd_quitApplication"/>
</hbox>
</footer>
</panelview>

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

@ -113,9 +113,9 @@
<children />
<xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label"/>
<xul:label class="toolbarbutton-text" crop="right" flex="1"
xbl:inherits="value=label,accesskey,crop"/>
xbl:inherits="value=label,accesskey,crop,wrap"/>
<xul:label class="toolbarbutton-multiline-text" flex="1"
xbl:inherits="xbl:text=label,accesskey"/>
xbl:inherits="xbl:text=label,accesskey,wrap"/>
</content>
</binding>
</bindings>

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

@ -8,6 +8,17 @@ Components.utils.import("resource://gre/modules/Services.jsm");
const PAGE_NO_ACCOUNT = 0;
const PAGE_HAS_ACCOUNT = 1;
const PAGE_NEEDS_UPDATE = 2;
const PAGE_PLEASE_WAIT = 3;
const FXA_PAGE_LOGGED_OUT = 4;
const FXA_PAGE_LOGGED_IN = 5;
// Indexes into the "login status" deck.
// We are in a successful verified state - everything should work!
const FXA_LOGIN_VERIFIED = 0;
// We have logged in to an unverified account.
const FXA_LOGIN_UNVERIFIED = 1;
// We are logged in locally, but the server rejected our credentials.
const FXA_LOGIN_FAILED = 2;
let gSyncPane = {
_stringBundle: null,
@ -44,6 +55,10 @@ let gSyncPane = {
return;
}
// it may take some time before we can determine what provider to use
// and the state of that provider, so show the "please wait" page.
this.page = PAGE_PLEASE_WAIT;
let onUnload = function () {
window.removeEventListener("unload", onUnload, false);
try {
@ -88,16 +103,59 @@ let gSyncPane = {
},
updateWeavePrefs: function () {
if (Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED ||
Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
let service = Components.classes["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
// service.fxAccountsEnabled is false iff sync is already configured for
// the legacy provider.
if (service.fxAccountsEnabled) {
// determine the fxa status...
this.page = PAGE_PLEASE_WAIT;
Components.utils.import("resource://gre/modules/FxAccounts.jsm");
fxAccounts.getSignedInUser().then(data => {
if (!data) {
this.page = FXA_PAGE_LOGGED_OUT;
return;
}
this.page = FXA_PAGE_LOGGED_IN;
// We are logged in locally, but maybe we are in a state where the
// server rejected our credentials (eg, password changed on the server)
let fxaLoginStatus = document.getElementById("fxaLoginStatus");
let enginesListDisabled;
// Not Verfied implies login error state, so check that first.
if (!data.verified) {
fxaLoginStatus.selectedIndex = FXA_LOGIN_UNVERIFIED;
enginesListDisabled = true;
// So we think we are logged in, so login problems are next.
} else if (Weave.Status.login != Weave.LOGIN_SUCCEEDED) {
fxaLoginStatus.selectedIndex = FXA_LOGIN_FAILED;
enginesListDisabled = true;
// Else we must be golden!
} else {
fxaLoginStatus.selectedIndex = FXA_LOGIN_VERIFIED;
enginesListDisabled = false;
}
document.getElementById("fxaEmailAddress1").textContent = data.email;
document.getElementById("fxaEmailAddress2").textContent = data.email;
document.getElementById("fxaEmailAddress3").textContent = data.email;
document.getElementById("fxaSyncComputerName").value = Weave.Service.clientsEngine.localName;
let enginesList = document.getElementById("fxaSyncEnginesList")
enginesList.disabled = enginesListDisabled;
// *sigh* - disabling the <richlistbox> draws each item as if it is disabled,
// but doesn't disable the checkboxes.
for (let checkbox of enginesList.querySelectorAll("checkbox")) {
checkbox.disabled = enginesListDisabled;
}
});
// If fxAccountEnabled is false and we are in a "not configured" state,
// then fxAccounts is probably fully disabled rather than just unconfigured,
// so handle this case. This block can be removed once we remove support
// for fxAccounts being disabled.
} else if (Weave.Status.service == Weave.CLIENT_NOT_CONFIGURED ||
Weave.Svc.Prefs.get("firstSync", "") == "notReady") {
this.page = PAGE_NO_ACCOUNT;
let service = Components.classes["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
// no concept of "pair" in an fxAccounts world.
if (service.fxAccountsEnabled) {
document.getElementById("pairDevice").hidden = true;
}
// else: sync was previously configured for the legacy provider, so we
// make the "old" panels available.
} else if (Weave.Status.login == Weave.LOGIN_FAILED_INVALID_PASSPHRASE ||
Weave.Status.login == Weave.LOGIN_FAILED_LOGIN_REJECTED) {
this.needsUpdate();
@ -163,10 +221,7 @@ let gSyncPane = {
.wrappedJSObject;
if (service.fxAccountsEnabled) {
let win = Services.wm.getMostRecentWindow("navigator:browser");
win.switchToTabHavingURI("about:accounts", true);
// seeing as we are doing this in a tab we close the prefs dialog.
window.close();
this.openContentInBrowser("about:accounts");
} else {
let win = Services.wm.getMostRecentWindow("Weave:AccountSetup");
if (win) {
@ -179,6 +234,56 @@ let gSyncPane = {
}
},
openContentInBrowser: function(url) {
let win = Services.wm.getMostRecentWindow("navigator:browser");
if (!win) {
// no window to use, so use _openLink to create a new one. We don't
// always use that as it prefers to open a new window rather than use
// an existing one.
gSyncUtils._openLink(url);
return;
}
win.switchToTabHavingURI(url, true);
// seeing as we are doing this in a tab we close the prefs dialog.
window.close();
},
reSignIn: function() {
this.openContentInBrowser("about:accounts");
},
manageFirefoxAccount: function() {
let url = Services.prefs.getCharPref("identity.fxaccounts.settings.uri");
this.openContentInBrowser(url);
},
verifyFirefoxAccount: function() {
Components.utils.import("resource://gre/modules/FxAccounts.jsm");
fxAccounts.resendVerificationEmail().then(() => {
fxAccounts.getSignedInUser().then(data => {
let sb = this._stringBundle;
let title = sb.GetStringFromName("firefoxAccountsVerificationSentTitle");
let heading = sb.formatStringFromName("firefoxAccountsVerificationSentHeading",
[data.email], 1);
let description = sb.GetStringFromName("firefoxAccountVerificationSentDescription");
Services.prompt.alert(window, title, heading + "\n\n" + description);
});
});
},
openOldSyncSupportPage: function() {
let url = Services.urlFormatter.formatURLPref('app.support.baseURL') + "old-sync"
this.openContentInBrowser(url);
},
unlinkFirefoxAccount: function(confirm) {
Components.utils.import('resource://gre/modules/FxAccounts.jsm');
fxAccounts.signOut().then(() => {
this.updateWeavePrefs();
});
},
openQuotaDialog: function () {
let win = Services.wm.getMostRecentWindow("Sync:ViewQuota");
if (win) {

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

@ -38,6 +38,8 @@
<deck id="weavePrefsDeck">
<!-- These panels are for the "legacy" sync provider -->
<vbox id="noAccount" align="center">
<spacer flex="1"/>
<description id="syncDesc">
@ -175,6 +177,137 @@
onclick="gSyncPane.startOver(true); return false;"
value="&unlinkDevice.label;"/>
</vbox>
<!-- These panels are for the Firefox Accounts identity provider -->
<vbox id="fxaDeterminingStatus" align="center">
<spacer flex="1"/>
<p>&determiningStatus.label;</p>
<spacer flex="1"/>
</vbox>
<vbox id="noFxaAccount">
<description>&welcome.description;</description>
<label class="text-link"
onclick="gSyncPane.openContentInBrowser('about:accounts?signin=true'); return false;"
value="&welcome.startButton.label;"/>
<spacer flex="1"/>
<label class="text-link"
onclick="gSyncPane.openOldSyncSupportPage(); return false;"
value="&welcome.useOldSync.label;"/>
<spacer flex="10"/>
</vbox>
<vbox id="hasFxaAccount">
<groupbox id="fxaGroup">
<caption label="&syncBrand.fxa-singular.label;"/>
<deck id="fxaLoginStatus">
<!-- logged in and verified and all is good -->
<hbox flex="1">
<label id="fxaEmailAddress1"/>
<label class="text-link"
onclick="gSyncPane.manageFirefoxAccount();"
value="&manage.label;"/>
<spacer flex="1"/>
<vbox align="end">
<button onclick="gSyncPane.unlinkFirefoxAccount(true);"
label="&unlink.label;" />
</vbox>
</hbox>
<!-- logged in to an unverified account -->
<hbox flex="1">
<description>
&signedInUnverified.beforename.label;
<span id="fxaEmailAddress2"></span>
&signedInUnverified.aftername.label;
</description>
<spacer flex="1"/>
<vbox align="end">
<button onclick="gSyncPane.verifyFirefoxAccount();"
caption="&verify.label;"/>
<label class="text-link"
onclick="/* no warning as account can't have previously synced */ gSyncPane.unlinkFirefoxAccount(false);"
value="&forget.label;"/>
</vbox>
</hbox>
<!-- logged in locally but server rejected credentials -->
<hbox flex="1">
<description>
&signedInLoginFailure.beforename.label;
<span id="fxaEmailAddress3"></span>
&signedInLoginFailure.aftername.label;
</description>
<spacer flex="1"/>
<vbox align="end">
<button onclick="gSyncPane.reSignIn();"
label="&signIn.label;"/>
<label class="text-link"
onclick="gSyncPane.unlinkFirefoxAccount(true);"
value="&forget.label;"/>
</vbox>
</hbox>
</deck>
</groupbox>
<groupbox id="syncOptions">
<caption label="&syncBrand.fullName.label;"/>
<vbox>
<label value="&syncMy.label;" />
<richlistbox id="fxaSyncEnginesList"
orient="vertical"
onselect="if (this.selectedCount) this.clearSelection();">
<richlistitem>
<checkbox label="&engine.addons.label;"
accesskey="&engine.addons.accesskey;"
preference="engine.addons"/>
</richlistitem>
<richlistitem>
<checkbox label="&engine.bookmarks.label;"
accesskey="&engine.bookmarks.accesskey;"
preference="engine.bookmarks"/>
</richlistitem>
<richlistitem>
<checkbox label="&engine.passwords.label;"
accesskey="&engine.passwords.accesskey;"
preference="engine.passwords"/>
</richlistitem>
<richlistitem>
<checkbox label="&engine.prefs.label;"
accesskey="&engine.prefs.accesskey;"
preference="engine.prefs"/>
</richlistitem>
<richlistitem>
<checkbox label="&engine.history.label;"
accesskey="&engine.history.accesskey;"
preference="engine.history"/>
</richlistitem>
<richlistitem>
<checkbox label="&engine.tabs.label;"
accesskey="&engine.tabs.accesskey;"
preference="engine.tabs"/>
</richlistitem>
</richlistbox>
</vbox>
</groupbox>
<vbox>
<label value="&syncDeviceName.label;"
accesskey="&syncDeviceName.accesskey;"
control="syncComputerName"/>
<textbox id="fxaSyncComputerName"
onchange="gSyncUtils.changeName(this)"/>
</vbox>
<hbox id="tosPP" pack="center">
<label class="text-link"
onclick="event.stopPropagation();gSyncUtils.openToS();"
value="&prefs.tosLink.label;"/>
<label class="text-link"
onclick="event.stopPropagation();gSyncUtils.openPrivacyPolicy();"
value="&fxaPrivacyNotice.link.label;"/>
</hbox>
</vbox>
</deck>
</prefpane>
</overlay>

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

@ -50,7 +50,6 @@ function SelectorSearch(aInspector, aContentDocument, aInputNode) {
let options = {
panelId: "inspector-searchbox-panel",
listBoxId: "searchbox-panel-listbox",
fixedWidth: true,
autoSelect: true,
position: "before_start",
direction: "ltr",

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

@ -76,7 +76,6 @@ function MarkupView(aInspector, aFrame, aControllerWindow) {
// Creating the popup to be used to show CSS suggestions.
let options = {
fixedWidth: true,
autoSelect: true,
theme: "auto"
};

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

@ -25,7 +25,6 @@ loader.lazyImporter(this, "gDevTools", "resource:///modules/devtools/gDevTools.j
* - theme {String} String related to the theme of the popup.
* - autoSelect {Boolean} Boolean to allow the first entry of the popup
* panel to be automatically selected when the popup shows.
* - fixedWidth {Boolean} Boolean to control dynamic width of the popup.
* - direction {String} The direction of the text in the panel. rtl or ltr
* - onSelect {String} The select event handler for the richlistbox
* - onClick {String} The click event handler for the richlistbox.
@ -35,7 +34,6 @@ function AutocompletePopup(aDocument, aOptions = {})
{
this._document = aDocument;
this.fixedWidth = aOptions.fixedWidth || false;
this.autoSelect = aOptions.autoSelect || false;
this.position = aOptions.position || "after_start";
this.direction = aOptions.direction || "ltr";
@ -75,7 +73,6 @@ function AutocompletePopup(aDocument, aOptions = {})
else {
this._document.documentElement.appendChild(this._panel);
}
this._list = null;
}
else {
this._list = this._panel.firstChild;
@ -137,14 +134,13 @@ AutocompletePopup.prototype = {
*/
openPopup: function AP_openPopup(aAnchor, aXOffset = 0, aYOffset = 0)
{
this.__maxLabelLength = -1;
this._updateSize();
this._panel.openPopup(aAnchor, this.position, aXOffset, aYOffset);
if (this.autoSelect) {
this.selectFirstItem();
}
if (!this.fixedWidth) {
this._updateSize();
}
},
/**
@ -241,9 +237,7 @@ AutocompletePopup.prototype = {
if (this.autoSelect) {
this.selectFirstItem();
}
if (!this.fixedWidth) {
this._updateSize();
}
this._updateSize();
}
},
@ -260,6 +254,28 @@ AutocompletePopup.prototype = {
else {
this.selectedIndex = 0;
}
this._list.ensureIndexIsVisible(this._list.selectedIndex);
},
__maxLabelLength: -1,
get _maxLabelLength() {
if (this.__maxLabelLength != -1) {
return this.__maxLabelLength;
}
let max = 0;
for (let i = 0; i < this._list.childNodes.length; i++) {
let item = this._list.childNodes[i]._autocompleteItem;
let str = item.label;
if (item.count) {
str += (item.count + "");
}
max = Math.max(str.length, max);
}
this.__maxLabelLength = max;
return this.__maxLabelLength;
},
/**
@ -272,24 +288,8 @@ AutocompletePopup.prototype = {
if (!this._panel) {
return;
}
// Flush the layout so that we get the latest height.
this._panel.boxObject.height;
let height = {};
this._list.scrollBoxObject.getScrolledSize({}, height);
// Change the width of the popup only if the scrollbar is visible.
if (height.value > this._panel.clientHeight) {
this._list.width = this._panel.clientWidth + this._scrollbarWidth;
}
// Height change is required, otherwise the panel is drawn at an offset
// the first time.
this._list.height = this._list.clientHeight;
// This brings the panel back at right position.
this._list.top = 0;
// Move the panel to -1,-1 to realign the popup with its anchor node when
// decreasing the panel height.
this._panel.moveTo(-1, -1);
// Changing panel height might make the selected item out of view, so
// bring it back to view.
this._list.style.width = (this._maxLabelLength + 3) +"ch";
this._list.ensureIndexIsVisible(this._list.selectedIndex);
},
@ -305,16 +305,17 @@ AutocompletePopup.prototype = {
this._list.removeChild(this._list.firstChild);
}
if (!this.fixedWidth) {
// Reset the panel and list dimensions. New dimensions are calculated when
// a new set of items is added to the autocomplete popup.
this._list.width = "";
this._list.height = "";
this._panel.width = "";
this._panel.height = "";
this._panel.top = "";
this._panel.left = "";
}
this.__maxLabelLength = -1;
// Reset the panel and list dimensions. New dimensions are calculated when
// a new set of items is added to the autocomplete popup.
this._list.width = "";
this._list.style.width = "";
this._list.height = "";
this._panel.width = "";
this._panel.height = "";
this._panel.top = "";
this._panel.left = "";
},
/**
@ -496,38 +497,6 @@ AutocompletePopup.prototype = {
this._list.focus();
},
/**
* Determine the scrollbar width in the current document.
*
* @private
*/
get _scrollbarWidth()
{
if (this.__scrollbarWidth !== null) {
return this.__scrollbarWidth;
}
let doc = this._document;
if (doc.defaultView.matchMedia("(-moz-overlay-scrollbars)").matches) {
// This is for the Mac's floating scrollbar, which actually is drawn over
// the content, thus taking no extra width.
return (this.__scrollbarWidth = 0);
}
let hbox = doc.createElementNS(XUL_NS, "hbox");
hbox.setAttribute("style", "height: 0%; overflow: hidden");
let scrollbar = doc.createElementNS(XUL_NS, "scrollbar");
scrollbar.setAttribute("orient", "vertical");
hbox.appendChild(scrollbar);
doc.documentElement.appendChild(hbox);
this.__scrollbarWidth = scrollbar.clientWidth;
doc.documentElement.removeChild(hbox);
return this.__scrollbarWidth;
},
/**
* Manages theme switching for the popup based on the devtools.theme pref.
*

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

@ -1089,7 +1089,6 @@ function CssRuleView(aInspector, aDoc, aStore, aPageStyle)
this._prefObserver.on(PREF_ORIG_SOURCES, this._onSourcePrefChanged);
let options = {
fixedWidth: true,
autoSelect: true,
theme: "auto"
};

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

@ -96,8 +96,19 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY showAllTabsCmd.label "Show All Tabs">
<!ENTITY showAllTabsCmd.accesskey "A">
<!ENTITY syncPanel.descriptionSyncing "&brandShortName; is now syncing.">
<!ENTITY syncPanel.descriptionPrefs "You can manage Sync from your browser's Preferences.">
<!ENTITY fxaSignIn.label "Sign in to &syncBrand.shortName.label;">
<!ENTITY syncStartPanel.title "&brandShortName; is now syncing.">
<!ENTITY syncStartPanel.subTitle "You can manage &syncBrand.shortName.label; in Options.">
<!ENTITY syncStartPanel.subTitleUnix "You can manage &syncBrand.shortName.label; in Preferences.">
<!ENTITY syncErrorPanel.title "Cannot connect to &syncBrand.shortName.label;">
<!ENTITY syncErrorPanel.subTitle "Please sign in to resume syncing.">
<!ENTITY syncErrorPanel.prefButton.label "Options">
<!ENTITY syncErrorPanel.prefButton.accesskey "O">
<!ENTITY syncErrorPanel.prefButtonUnix.label "Preferences">
<!ENTITY syncErrorPanel.prefButtonUnix.accesskey "P">
<!ENTITY syncErrorPanel.signInButton.label "Sign In">
<!ENTITY syncErrorPanel.signInButton.accesskey "S">
<!ENTITY fullScreenMinimize.tooltip "Minimize">
<!ENTITY fullScreenRestore.tooltip "Restore">

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

@ -135,3 +135,9 @@ syncUnlinkConfirm.label=Unlink
featureEnableRequiresRestart=%S must restart to enable this feature.
featureDisableRequiresRestart=%S must restart to disable this feature.
shouldRestartTitle=Restart %S
###Preferences::Sync::Firefox Accounts
firefoxAccountsVerificationSentTitle=Verification Sent
# LOCALIZATION NOTE: %S = user's email address.
firefoxAccountsVerificationSentHeading=A verification link has been sent to %S
firefoxAccountVerificationSentDescription=Please check your email and click the verification link to begin syncing.

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

@ -45,3 +45,23 @@
<!-- Footer stuff -->
<!ENTITY prefs.tosLink.label "Terms of Service">
<!ENTITY prefs.ppLink.label "Privacy Policy">
<!-- Firefox Accounts stuff -->
<!ENTITY fxaPrivacyNotice.link.label "Privacy Notice">
<!ENTITY determiningStatus.label "Determining your Firefox Account status…">
<!ENTITY signedInUnverified.beforename.label "">
<!ENTITY signedInUnverified.aftername.label "is not verified.">
<!ENTITY signedInLoginFailure.beforename.label "Please sign in to reconnect">
<!ENTITY signedInLoginFailure.aftername.label "">
<!ENTITY notSignedIn.label "You are not signed in.">
<!ENTITY signIn.label "Sign in">
<!ENTITY manage.label "Manage">
<!ENTITY unlink.label "Unlink This Browser…">
<!ENTITY verify.label "Verify Email">
<!ENTITY forget.label "Forget this Email">
<!ENTITY welcome.description "Access your tabs, bookmarks, passwords and more wherever you use &brandShortName;.">
<!ENTITY welcome.startButton.label "Sign in or Create an Account">
<!ENTITY welcome.useOldSync.label "Using an older version of Sync?">

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

@ -4,3 +4,5 @@
<!ENTITY syncBrand.shortName.label "Sync">
<!ENTITY syncBrand.fullName.label "Firefox Sync">
<!ENTITY syncBrand.fxa-singular.label "Firefox Account">
<!ENTITY syncBrand.fxa-plural.label "Firefox Accounts">

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

@ -0,0 +1,27 @@
<!-- 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/. -->
<!ENTITY syncCustomize.dialog.title "Sync Selection">
<!ENTITY syncCustomize.acceptButton.label "Start">
<!ENTITY syncCustomize.title "What would you like to sync?">
<!ENTITY syncCustomize.subTitle "You can manage this selection in Options.">
<!ENTITY syncCustomize.subTitleUnix "You can manage this selection in Preferences.">
<!--
These engine names are the same as in browser/preferences/sync.dtd except
for the last two that are marked as being specific to Desktop browsers.
-->
<!ENTITY engine.bookmarks.label "Bookmarks">
<!ENTITY engine.bookmarks.accesskey "m">
<!ENTITY engine.history.label "History">
<!ENTITY engine.history.accesskey "r">
<!ENTITY engine.tabs.label "Tabs">
<!ENTITY engine.tabs.accesskey "T">
<!ENTITY engine.passwords.label "Passwords">
<!ENTITY engine.passwords.accesskey "P">
<!ENTITY engine.addons.label "Desktop Add-ons">
<!ENTITY engine.addons.accesskey "A">
<!ENTITY engine.prefs.label "Desktop Preferences">
<!ENTITY engine.prefs.accesskey "S">

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

@ -18,6 +18,7 @@
locale/browser/aboutSessionRestore.dtd (%chrome/browser/aboutSessionRestore.dtd)
#ifdef MOZ_SERVICES_SYNC
locale/browser/syncProgress.dtd (%chrome/browser/syncProgress.dtd)
locale/browser/syncCustomize.dtd (%chrome/browser/syncCustomize.dtd)
locale/browser/aboutSyncTabs.dtd (%chrome/browser/aboutSyncTabs.dtd)
#endif
locale/browser/browser.dtd (%chrome/browser/browser.dtd)

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

@ -245,7 +245,7 @@ gTests.push({
run: function () {
let mozTab = yield addTab("about:mozilla");
// addTab will dismiss navbar, but lets check anyway.
yield hideNavBar();
ok(!ContextUI.navbarVisible, "navbar dismissed");
BrowserUI.doCommand("cmd_newTab");

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

@ -0,0 +1,55 @@
/* vim: set 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/. */
"use strict";
var gSessionStore = Cc["@mozilla.org/browser/sessionstore;1"]
.getService(Ci.nsISessionStore);
function test() {
runTests();
}
function getState() {
return JSON.parse(gSessionStore.getBrowserState());
}
function getTabData() {
return getState().windows[0].tabs;
}
function isValidTabData(aData) {
return aData && aData.entries && aData.entries.length &&
typeof aData.index == "number";
}
gTests.push({
desc: "getBrowserState tests",
run: function() {
// Wait for Session Manager to be initialized.
yield waitForCondition(() => window.__SSID);
info(window.__SSID);
let tabData1 = getTabData();
ok(tabData1.every(isValidTabData), "Tab data starts out valid");
// Open a tab.
let tab = Browser.addTab("about:mozilla");
let tabData2 = getTabData();
is(tabData2.length, tabData1.length, "New tab not added yet.");
// Wait for the tab's session data to be initialized.
yield waitForMessage("Content:SessionHistory", tab.browser.messageManager);
yield waitForMs(0);
let tabData3 = getTabData();
is(tabData3.length, tabData1.length + 1, "New tab added.");
ok(tabData3.every(isValidTabData), "Tab data still valid");
// Close the tab.
Browser.closeTab(tab, { forceClose: true } );
let tabData4 = getTabData();
is(tabData4.length, tabData1.length, "Closed tab removed.");
ok(tabData4.every(isValidTabData), "Tab data valid again");
}
});

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

@ -56,6 +56,7 @@ support-files =
[browser_private_browsing.js]
[browser_prompt.js]
[browser_remotetabs.js]
[browser_sessionstore.js]
[browser_snappedState.js]
[browser_tabs.js]
[browser_tabs_container.js]

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

@ -338,6 +338,7 @@ SessionStore.prototype = {
// Assign it a unique identifier and create its data object
aWindow.__SSID = "window" + gUUIDGenerator.generateUUID().toString();
this._windows[aWindow.__SSID] = { tabs: [], selected: 0, _closedTabs: [] };
this._orderedWindows.push(aWindow.__SSID);
// Perform additional initialization when the first window is loading
if (this._loadState == STATE_STOPPED) {
@ -348,9 +349,6 @@ SessionStore.prototype = {
if (!this.shouldRestore()) {
this._clearCache();
Services.obs.notifyObservers(null, "sessionstore-windows-restored", "");
// If nothing is being restored, we only have our single Metro window.
this._orderedWindows.push(aWindow.__SSID);
}
}
@ -554,15 +552,13 @@ SessionStore.prototype = {
_getTabData: function(aWindow) {
return aWindow.Browser.tabs
.filter(tab => !tab.isPrivate)
.filter(tab => !tab.isPrivate && tab.browser.__SS_data)
.map(tab => {
let browser = tab.browser;
if (browser.__SS_data) {
let tabData = browser.__SS_data;
if (browser.__SS_extdata)
tabData.extData = browser.__SS_extdata;
return tabData;
}
let tabData = browser.__SS_data;
if (browser.__SS_extdata)
tabData.extData = browser.__SS_extdata;
return tabData;
});
},
@ -801,6 +797,7 @@ SessionStore.prototype = {
} catch (ex) { /* currentGroupId is undefined if user has no tab groups */ }
// Move all window data from sessionstore.js to this._windows.
this._orderedWindows = [];
for (let i = 0; i < data.windows.length; i++) {
let SSID;
if (i != windowIndex) {

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

@ -1682,21 +1682,30 @@ toolbarbutton.chevron > .toolbarbutton-icon {
/* Sync Panel */
#sync-popup {
.sync-panel {
padding: 10px;
}
#sync-popup-icon {
.sync-panel-icon {
width: 32px;
background: url("chrome://browser/content/abouthome/sync.png") center center no-repeat;
background: url("chrome://browser/content/abouthome/sync.png") top left no-repeat;
}
#sync-popup-inner {
.sync-panel-inner {
width: 0;
padding-left: 10px;
}
#sync-popup-desc-prefs {
.sync-panel-button-box {
margin-top: 1em;
}
#sync-error-panel-title {
font-weight: bold;
}
#sync-start-panel-subtitle,
#sync-error-panel-subtitle {
margin: 0;
}

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

@ -3596,30 +3596,54 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
margin-top: .5em;
}
/* Sync Panel */
/* Sync Panels */
#sync-popup {
.sync-panel {
padding: 10px;
}
#sync-popup-icon {
.sync-panel-icon {
width: 32px;
background: url("chrome://browser/content/abouthome/sync.png") center center no-repeat;
background: url("chrome://browser/content/abouthome/sync.png") top left no-repeat;
}
@media (min-resolution: 2dppx) {
#sync-popup-icon {
background: url("chrome://browser/content/abouthome/sync@2x.png") center center no-repeat;
.sync-panel-icon {
background: url("chrome://browser/content/abouthome/sync@2x.png") top left no-repeat;
background-size: 32px 32px;
}
}
#sync-popup-inner {
.sync-panel-inner {
width: 0;
padding-left: 10px;
}
#sync-popup-desc-prefs {
.sync-panel-button-box {
margin-top: 1em;
}
.sync-panel-button {
@hudButton@
margin: 0;
min-width: 72px;
min-height: 22px;
}
.sync-panel-button:hover:active {
@hudButtonPressed@
}
.sync-panel-button:-moz-focusring {
@hudButtonFocused@
}
#sync-error-panel-title {
font-weight: bold;
}
#sync-start-panel-subtitle,
#sync-error-panel-subtitle {
margin: 0;
}

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

@ -6,8 +6,8 @@
%define menuPanelWidth 22.35em
%define exitSubviewGutterWidth 38px
%define buttonStateHover :not(:-moz-any([disabled],[checked="true"],[open],:active)):hover
%define buttonStateActive :not([disabled]):-moz-any([open],[checked="true"],:hover:active)
%define buttonStateHover :not(:-moz-any([disabled],[open],[checked="true"],:active)):-moz-any(:hover,[_moz-menuactive])
%define buttonStateActive :not([disabled]):-moz-any([open],[checked="true"],:hover:active,[_moz-menuactive]:active)
%include ../browser.inc
@ -216,11 +216,16 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
#PanelUI-footer {
display: flex;
flex-direction: column;
background-color: rgba(0, 0, 0, 0.05);
box-shadow: 0 -1px 0 rgba(0,0,0,.15);
padding: 0;
margin: 0;
min-height: 4em;
}
#PanelUI-footer-inner {
display: flex;
box-shadow: 0 -1px 0 rgba(0,0,0,.15);
}
#PanelUI-footer > toolbarseparator {
@ -234,10 +239,12 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
}
#PanelUI-help,
#PanelUI-fxa-status,
#PanelUI-customize,
#PanelUI-quit {
margin: 0;
padding: 10px 0;
min-height: 2em;
-moz-appearance: none;
box-shadow: none;
background-image: none;
@ -248,11 +255,21 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
-moz-box-orient: horizontal;
}
#PanelUI-fxa-status {
width: calc(@menuPanelWidth@ + 20px);
border-bottom-style: solid;
}
#PanelUI-fxa-status[signedin] {
font-weight: bold;
}
#PanelUI-help,
#PanelUI-quit {
min-width: 46px;
}
#PanelUI-fxa-status > .toolbarbutton-text,
#PanelUI-customize > .toolbarbutton-text {
text-align: start;
}
@ -267,6 +284,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
-moz-margin-end: 0;
}
#PanelUI-fxa-status,
#PanelUI-customize {
flex: 1;
-moz-padding-start: 15px;
@ -283,6 +301,7 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
list-style-image: url(chrome://browser/skin/menuPanel-exit.png);
}
#PanelUI-fxa-status,
#PanelUI-customize,
#PanelUI-help,
#PanelUI-quit {
@ -314,6 +333,12 @@ toolbarpaletteitem[place="palette"] > toolbaritem > toolbarbutton {
box-shadow: none;
}
#PanelUI-fxa-status:not([disabled]):hover {
background-color: rgba(0,0,0,0.1);
border-bottom-color: rgba(0,0,0,0.1);
box-shadow: 0 -1px 0 rgba(0,0,0,0.2);
}
#PanelUI-quit:not([disabled]):hover {
background-color: #d94141;
outline-color: #c23a3a;

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

@ -2141,21 +2141,30 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
/* Sync Panel */
#sync-popup {
.sync-panel {
padding: 10px;
}
#sync-popup-icon {
.sync-panel-icon {
width: 32px;
background: url("chrome://browser/content/abouthome/sync.png") center center no-repeat;
background: url("chrome://browser/content/abouthome/sync.png") top left no-repeat;
}
#sync-popup-inner {
.sync-panel-inner {
width: 0;
padding-left: 10px;
}
#sync-popup-desc-prefs {
.sync-panel-button-box {
margin-top: 1em;
}
#sync-error-panel-title {
font-weight: bold;
}
#sync-start-panel-subtitle,
#sync-error-panel-subtitle {
margin: 0;
}

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

@ -301,7 +301,7 @@
android:permission="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER"/>
<provider android:name="org.mozilla.gecko.db.HomeProvider"
android:authorities="@ANDROID_PACKAGE_NAME@.db.homelists"
android:authorities="@ANDROID_PACKAGE_NAME@.db.home"
android:permission="@ANDROID_PACKAGE_NAME@.permissions.BROWSER_PROVIDER"/>
<service

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

@ -1362,15 +1362,15 @@ abstract public class BrowserApp extends GeckoApp
}
final Tabs tabs = Tabs.getInstance();
final int tabId = tabs.getTabIdForUrl(url, tabs.getSelectedTab().isPrivate());
if (tabId < 0) {
final Tab tab = tabs.getFirstTabForUrl(url, tabs.getSelectedTab().isPrivate());
if (tab == null) {
return false;
}
// Set the target tab to null so it does not get selected (on editing
// mode exit) in lieu of the tab we are about to select.
mTargetTabForEditingMode = null;
Tabs.getInstance().selectTab(tabId);
tabs.selectTab(tab.getId());
mBrowserToolbar.cancelEdit();

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

@ -163,7 +163,7 @@ public class Tabs implements GeckoEventListener {
return count;
}
public synchronized int isOpen(String url) {
public int isOpen(String url) {
for (Tab tab : mOrder) {
if (tab.getURL().equals(url)) {
return tab.getId();
@ -645,32 +645,44 @@ public class Tabs implements GeckoEventListener {
/**
* Looks for an open tab with the given URL.
* @param url the URL of the tab we're looking for
*
* @return first Tab with the given URL, or null if there is no such tab.
*/
public Tab getFirstTabForUrl(String url) {
return getFirstTabForUrlHelper(url, null);
}
/**
* Looks for an open tab with the given URL and private state.
* @param url the URL of the tab we're looking for
* @param isPrivate if true, only look for tabs that are private. if false,
* only look for tabs that are non-private.
*
* @return id of an open tab with the given URL; -1 if the tab doesn't exist.
* @return first Tab with the given URL, or null if there is no such tab.
*/
public int getTabIdForUrl(String url, boolean isPrivate) {
public Tab getFirstTabForUrl(String url, boolean isPrivate) {
return getFirstTabForUrlHelper(url, isPrivate);
}
private Tab getFirstTabForUrlHelper(String url, Boolean isPrivate) {
if (url == null) {
return null;
}
for (Tab tab : mOrder) {
if (isPrivate != null && isPrivate != tab.isPrivate()) {
continue;
}
String tabUrl = tab.getURL();
if (AboutPages.isAboutReader(tabUrl)) {
tabUrl = ReaderModeUtils.getUrlFromAboutReader(tabUrl);
}
if (TextUtils.equals(tabUrl, url) && isPrivate == tab.isPrivate()) {
return tab.getId();
if (url.equals(tabUrl)) {
return tab;
}
}
return -1;
}
public int getTabIdForUrl(String url) {
return getTabIdForUrl(url, Tabs.getInstance().getSelectedTab().isPrivate());
}
public synchronized Tab getTabForUrl(String url) {
int tabId = getTabIdForUrl(url);
return getTab(tabId);
return null;
}
/**

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

@ -239,7 +239,7 @@ public class Favicons {
// Attempt to determine the Favicon URL from the Tabs datastructure. Can dodge having to use
// the database sometimes by doing this.
String targetURL;
Tab theTab = Tabs.getInstance().getTabForUrl(pageURL);
Tab theTab = Tabs.getInstance().getFirstTabForUrl(pageURL);
if (theTab != null) {
targetURL = theTab.getFaviconURL();
if (targetURL != null) {

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

@ -219,16 +219,11 @@ public class DynamicPanel extends HomeFragment {
public Cursor loadCursor() {
final ContentResolver cr = getContext().getContentResolver();
// XXX: Use the test URI for static fake data
final Uri fakeItemsUri = HomeItems.CONTENT_FAKE_URI.buildUpon().
appendQueryParameter(BrowserContract.PARAM_PROFILE, "default").build();
final String selection = HomeItems.DATASET_ID + " = ?";
final String[] selectionArgs = new String[] { mDatasetId };
Log.i(LOGTAG, "Loading fake data for list provider: " + mDatasetId);
return cr.query(fakeItemsUri, null, selection, selectionArgs, null);
// XXX: You can use CONTENT_FAKE_URI for development to pull items from fake_home_items.json.
return cr.query(HomeItems.CONTENT_URI, null, selection, selectionArgs, null);
}
}

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

@ -120,8 +120,8 @@ public class TwoLinePageRow extends TwoLineRow
*/
private void updateDisplayedUrl() {
boolean isPrivate = Tabs.getInstance().getSelectedTab().isPrivate();
int tabId = Tabs.getInstance().getTabIdForUrl(mPageUrl, isPrivate);
if (!mShowIcons || tabId < 0) {
Tab tab = Tabs.getInstance().getFirstTabForUrl(mPageUrl, isPrivate);
if (!mShowIcons || tab == null) {
setSecondaryText(mPageUrl);
setSecondaryIcon(NO_ICON);
} else {

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

@ -638,7 +638,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
}
// Set TabCounter based on visibility
if (isVisible() && ViewHelper.getAlpha(mTabsCounter) != 0) {
if (isVisible() && ViewHelper.getAlpha(mTabsCounter) != 0 && !isEditing()) {
mTabsCounter.setCountWithAnimation(count);
} else {
mTabsCounter.setCount(count);
@ -863,6 +863,10 @@ public class BrowserToolbar extends GeckoRelativeLayout
// in setButtonEnabled().
final float alpha = (enabled ? 1.0f : 0.24f);
if (!enabled) {
mTabsCounter.onEnterEditingMode();
}
mTabs.setEnabled(enabled);
ViewHelper.setAlpha(mTabsCounter, alpha);
mMenu.setEnabled(enabled);

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

@ -102,6 +102,16 @@ public class TabCounter extends GeckoTextSwitcher
mCount = count;
}
// Alpha animations in editing mode cause action bar corruption on the
// Nexus 7 (bug 961749). As a workaround, skip these animations in editing
// mode.
void onEnterEditingMode() {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
getChildAt(i).clearAnimation();
}
}
private AnimationSet createAnimation(float startAngle, float endAngle,
FadeMode fadeMode,
float zEnd, boolean reverse) {

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

@ -30,11 +30,6 @@ import android.text.TextUtils;
import android.util.Log;
import android.util.Xml;
/**
* Mozilla: Extra imports.
*/
import android.content.pm.ApplicationInfo;
/**
* Mozilla: Unused import.
*/
@ -262,11 +257,6 @@ public class ActivityChooserModel extends DataSetObservable {
*/
//private final PackageMonitor mPackageMonitor = new DataModelPackageMonitor();
/**
* Mozilla: Count to monitor added and removed packages.
*/
private int mApplicationsCount;
/**
* Context for accessing resources.
*/
@ -742,15 +732,6 @@ public class ActivityChooserModel extends DataSetObservable {
* @return Whether loading was performed.
*/
private boolean loadActivitiesIfNeeded() {
/**
* Mozilla: Hack to find change in the installed/uninstalled applications.
*/
List<ApplicationInfo> applications = mContext.getPackageManager().getInstalledApplications(0);
if (applications != null && applications.size() != mApplicationsCount) {
mApplicationsCount = applications.size();
mReloadActivities = true;
}
if (mReloadActivities && mIntent != null) {
mReloadActivities = false;
mActivities.clear();

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

@ -560,6 +560,23 @@ var SelectionHandler = {
this.startSelection(aElement, { mode : this.SELECT_ALL });
},
/*
* Helper function for moving the selection inside an editable element.
*
* @param aAnchorX the stationary handle's x-coordinate in client coordinates
* @param aX the moved handle's x-coordinate in client coordinates
* @param aCaretPos the current position of the caret
*/
_moveSelectionInEditable: function sh_moveSelectionInEditable(aAnchorX, aX, aCaretPos) {
let anchorOffset = aX < aAnchorX ? this._targetElement.selectionEnd
: this._targetElement.selectionStart;
let newOffset = aCaretPos.offset;
let [start, end] = anchorOffset <= newOffset ?
[anchorOffset, newOffset] :
[newOffset, anchorOffset];
this._targetElement.setSelectionRange(start, end);
},
/*
* Moves the selection as the user drags a selection handle.
*
@ -597,8 +614,8 @@ var SelectionHandler = {
// are reversed, so we need to reverse the logic to extend the selection.
if ((aIsStartHandle && !this._isRTL) || (!aIsStartHandle && this._isRTL)) {
if (targetIsEditable) {
// XXX This will just collapse the selection if the start handle goes past the end handle.
this._targetElement.selectionStart = caretPos.offset;
let anchorX = this._isRTL ? this._cache.start.x : this._cache.end.x;
this._moveSelectionInEditable(anchorX, aX, caretPos);
} else {
let focusNode = selection.focusNode;
let focusOffset = selection.focusOffset;
@ -607,8 +624,8 @@ var SelectionHandler = {
}
} else {
if (targetIsEditable) {
// XXX This will just collapse the selection if the end handle goes past the start handle.
this._targetElement.selectionEnd = caretPos.offset;
let anchorX = this._isRTL ? this._cache.end.x : this._cache.start.x;
this._moveSelectionInEditable(anchorX, aX, caretPos);
} else {
selection.extend(caretPos.offsetNode, caretPos.offset);
}

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

@ -28,7 +28,6 @@ function dump(a) {
const STATE_STOPPED = 0;
const STATE_RUNNING = 1;
const STATE_QUITTING = -1;
function SessionStore() { }
@ -76,11 +75,7 @@ SessionStore.prototype = {
observerService.addObserver(this, "final-ui-startup", true);
observerService.addObserver(this, "domwindowopened", true);
observerService.addObserver(this, "domwindowclosed", true);
observerService.addObserver(this, "browser-lastwindow-close-granted", true);
observerService.addObserver(this, "browser:purge-session-history", true);
observerService.addObserver(this, "quit-application-requested", true);
observerService.addObserver(this, "quit-application-granted", true);
observerService.addObserver(this, "quit-application", true);
observerService.addObserver(this, "Session:Restore", true);
break;
case "final-ui-startup":
@ -98,60 +93,9 @@ SessionStore.prototype = {
case "domwindowclosed": // catch closed windows
this.onWindowClose(aSubject);
break;
case "browser-lastwindow-close-granted":
// If a save has been queued, kill the timer and save state now
if (this._saveTimer) {
this._saveTimer.cancel();
this._saveTimer = null;
this.saveState();
}
// Freeze the data at what we've got (ignoring closing windows)
this._loadState = STATE_QUITTING;
break;
case "quit-application-requested":
// Get a current snapshot of all windows
this._forEachBrowserWindow(function(aWindow) {
self._collectWindowData(aWindow);
});
break;
case "quit-application-granted":
// Get a current snapshot of all windows
this._forEachBrowserWindow(function(aWindow) {
self._collectWindowData(aWindow);
});
// Freeze the data at what we've got (ignoring closing windows)
this._loadState = STATE_QUITTING;
break;
case "quit-application":
// Freeze the data at what we've got (ignoring closing windows)
this._loadState = STATE_QUITTING;
observerService.removeObserver(this, "domwindowopened");
observerService.removeObserver(this, "domwindowclosed");
observerService.removeObserver(this, "browser-lastwindow-close-granted");
observerService.removeObserver(this, "quit-application-requested");
observerService.removeObserver(this, "quit-application-granted");
observerService.removeObserver(this, "quit-application");
observerService.removeObserver(this, "Session:Restore");
// If a save has been queued, kill the timer and save state now
if (this._saveTimer) {
this._saveTimer.cancel();
this._saveTimer = null;
this.saveState();
}
break;
case "browser:purge-session-history": // catch sanitization
this._clearDisk();
// If the browser is shutting down, simply return after clearing the
// session data on disk as this notification fires after the
// quit-application notification so the browser is about to exit.
if (this._loadState == STATE_QUITTING)
return;
// Clear all data about closed tabs
for (let [ssid, win] in Iterator(this._windows))
win.closedTabs = [];
@ -237,7 +181,7 @@ SessionStore.prototype = {
return;
// Ignore non-browser windows and windows opened while shutting down
if (aWindow.document.documentElement.getAttribute("windowtype") != "navigator:browser" || this._loadState == STATE_QUITTING)
if (aWindow.document.documentElement.getAttribute("windowtype") != "navigator:browser")
return;
// Assign it a unique identifier (timestamp) and create its data object

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

@ -217,7 +217,7 @@ let HomePanels = {
sendMessageToJava({
type: "HomePanels:Remove",
id: panel.id
id: id
});
},

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

@ -24,7 +24,7 @@ const DB_PATH = OS.Path.join(OS.Constants.Path.profileDir, "home.sqlite");
const SQL = {
createItemsTable:
"CREATE TABLE items (" +
"id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"_id INTEGER PRIMARY KEY AUTOINCREMENT, " +
"dataset_id TEXT NOT NULL, " +
"url TEXT," +
"title TEXT," +
@ -77,6 +77,7 @@ HomeStorage.prototype = {
// XXX: Factor this out to some migration path.
if (!(yield db.tableExists("items"))) {
yield db.execute(SQL.createItemsTable);
yield db.setSchemaVersion(SCHEMA_VERSION);
}
// Insert data into DB.

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

@ -172,3 +172,13 @@ function uninstallFakePAC() {
let CID = PACSystemSettings.CID;
Cm.nsIComponentRegistrar.unregisterFactory(CID, PACSystemSettings);
}
// We want to ensure the legacy provider is used for most of these tests; the
// tests that know how to deal with the Firefox Accounts identity hack things
// to ensure that still works.
function setDefaultIdentityConfig() {
Cu.import("resource://gre/modules/Services.jsm");
Services.prefs.setBoolPref("identity.fxaccounts.enabled", false);
// Services.prefs.setBoolPref("services.sync.fxaccounts.enabled", false);
}
setDefaultIdentityConfig();

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

@ -69,7 +69,9 @@ WeaveService.prototype = {
// first check if Firefox accounts is available at all. This is so we can
// get this landed without forcing Fxa to be used (and require nightly
// testers to manually set this pref)
// Once we decide we want Fxa to be available, we just remove this block.
// Once we decide we want Fxa to be available, we just remove this block
// (although a fly in this ointment is tests - it might be that we must
// just set this as a pref with a default of true)
let fxAccountsAvailable;
try {
fxAccountsAvailable = Services.prefs.getBoolPref("identity.fxaccounts.enabled");
@ -101,58 +103,12 @@ WeaveService.prototype = {
return this.fxAccountsEnabled = fxAccountsEnabled;
},
maybeInitWithFxAccountsAndEnsureLoaded: function() {
Components.utils.import("resource://services-sync/main.js");
// FxAccounts imports lots of stuff, so only do this as we need it
Cu.import("resource://gre/modules/FxAccounts.jsm");
// This isn't quite sufficient here to handle all the cases. Cases
// we need to handle:
// - User is signed in to FxAccounts, btu hasn't set up sync.
return fxAccounts.getSignedInUser().then(
(accountData) => {
if (accountData) {
Cu.import("resource://services-sync/browserid_identity.js");
// The Sync Identity module needs to be set in both these places if
// it's swapped out as we are doing here. When Weave.Service initializes
// it grabs a reference to Weave.Status._authManager, and for references
// to Weave.Service.identity to resolve correctly, we also need to reset
// Weave.Service.identity as well.
Weave.Service.identity = Weave.Status._authManager = new BrowserIDManager(),
// Init the identity module with any account data from
// firefox accounts. The Identity module will fetch the signed in
// user from fxAccounts directly.
Weave.Service.identity.initWithLoggedInUser().then(function () {
// Set the cluster data that we got from the token
Weave.Service.clusterURL = Weave.Service.identity.clusterURL;
// checkSetup() will check the auth state of the identity module
// and records that status in Weave.Status
if (Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED) {
// This makes sure that Weave.Service is loaded
Svc.Obs.notify("weave:service:setup-complete");
// TODO: this shouldn't be here. It should be at the end
// of the promise chain of the 'fxaccounts:onverified' handler.
Weave.Utils.nextTick(Weave.Service.sync, Weave.Service);
this.ensureLoaded();
}
}.bind(this));
} else if (Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED) {
// This makes sure that Weave.Service is loaded
this.ensureLoaded();
}
},
(err) => {dump("err in getting logged in account "+err.message)}
).then(null, (err) => {dump("err in processing logged in account "+err.message)});
},
observe: function (subject, topic, data) {
switch (topic) {
case "app-startup":
let os = Cc["@mozilla.org/observer-service;1"].
getService(Ci.nsIObserverService);
os.addObserver(this, "final-ui-startup", true);
os.addObserver(this, "fxaccounts:onverified", true);
os.addObserver(this, "fxaccounts:onlogout", true);
break;
case "final-ui-startup":
@ -160,53 +116,24 @@ WeaveService.prototype = {
this.timer = Cc["@mozilla.org/timer;1"].createInstance(Ci.nsITimer);
this.timer.initWithCallback({
notify: function() {
if (this.fxAccountsEnabled) {
// init the fxAccounts identity manager.
this.maybeInitWithFxAccountsAndEnsureLoaded();
} else {
// init the "old" style, sync-specific identity manager.
// We only load more if it looks like Sync is configured.
let prefs = Services.prefs.getBranch(SYNC_PREFS_BRANCH);
if (!prefs.prefHasUserValue("username")) {
return;
}
// We only load more if it looks like Sync is configured.
let prefs = Services.prefs.getBranch(SYNC_PREFS_BRANCH);
if (!prefs.prefHasUserValue("username")) {
return;
}
// We have a username. So, do a more thorough check. This will
// import a number of modules and thus increase memory
// accordingly. We could potentially copy code performed by
// this check into this file if our above code is yielding too
// many false positives.
Components.utils.import("resource://services-sync/main.js");
if (Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED) {
this.ensureLoaded();
}
// We have a username. So, do a more thorough check. This will
// import a number of modules and thus increase memory
// accordingly. We could potentially copy code performed by
// this check into this file if our above code is yielding too
// many false positives.
Components.utils.import("resource://services-sync/main.js");
if (Weave.Status.checkSetup() != Weave.CLIENT_NOT_CONFIGURED) {
this.ensureLoaded();
}
}.bind(this)
}, 10000, Ci.nsITimer.TYPE_ONE_SHOT);
break;
case 'fxaccounts:onverified':
// Tell sync that if this is a first sync, it should try and sync the
// server data with what is on the client - despite the name implying
// otherwise, this is what "resetClient" does.
// TOOD: This implicitly assumes we're in the CLIENT_NOT_CONFIGURED state, and
// if we're not, we should handle it here.
Components.utils.import("resource://services-sync/main.js"); // ensure 'Weave' exists
Weave.Svc.Prefs.set("firstSync", "resetClient");
this.maybeInitWithFxAccountsAndEnsureLoaded().then(() => {
// and off we go...
// TODO: I have this being done in maybeInitWithFxAccountsAndEnsureLoaded
// because I had a bug in the promise chains that was triggering this
// too early. This should be fixed.
//Weave.Utils.nextTick(Weave.Service.sync, Weave.Service);
});
break;
case 'fxaccounts:onlogout':
Components.utils.import("resource://services-sync/main.js"); // ensure 'Weave' exists
// startOver is throwing some errors and we can't re-log in in this
// session - so for now, we don't do this!
//Weave.Service.startOver();
break;
}
}
};

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

@ -150,7 +150,10 @@ this.configureIdentity = function(identityOverrides) {
if (ns.Service.identity instanceof BrowserIDManager) {
// do the FxAccounts thang...
configureFxAccountIdentity(ns.Service.identity, config);
return ns.Service.identity.initWithLoggedInUser();
return ns.Service.identity.initializeWithCurrentIdentity().then(() => {
// need to wait until this identity manager is readyToAuthenticate.
return ns.Service.identity.whenReadyToAuthenticate.promise;
});
}
// old style identity provider.
setBasicCredentials(config.username, config.sync.password, config.sync.syncKey);
@ -172,7 +175,9 @@ this.SyncTestingInfrastructure = function (server, username, password, syncKey)
config.sync.password = password;
if (syncKey)
config.sync.syncKey = syncKey;
configureIdentity(config);
let cb = Async.makeSpinningCallback();
configureIdentity(config).then(cb, cb);
cb.wait();
let i = server.identity;
let uri = i.primaryScheme + "://" + i.primaryHost + ":" +
@ -224,16 +229,16 @@ this.add_identity_test = function(test, testFunction) {
test.add_task(function() {
note("sync");
let oldIdentity = Status._authManager;
Status._authManager = ns.Service.identity = new IdentityManager();
Status.__authManager = ns.Service.identity = new IdentityManager();
yield testFunction();
Status._authManager = ns.Service.identity = oldIdentity;
Status.__authManager = ns.Service.identity = oldIdentity;
});
// another task for the FxAccounts identity manager.
test.add_task(function() {
note("FxAccounts");
let oldIdentity = Status._authManager;
Status._authManager = ns.Service.identity = new BrowserIDManager();
Status.__authManager = ns.Service.identity = new BrowserIDManager();
yield testFunction();
Status._authManager = ns.Service.identity = oldIdentity;
Status.__authManager = ns.Service.identity = oldIdentity;
});
}

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

@ -18,6 +18,7 @@ Cu.import("resource://services-common/tokenserverclient.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://services-sync/stages/cluster.js");
// Lazy imports to prevent unnecessary load on startup.
XPCOMUtils.defineLazyModuleGetter(this, "BulkKeyBundle",
@ -26,6 +27,12 @@ XPCOMUtils.defineLazyModuleGetter(this, "BulkKeyBundle",
XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
"resource://gre/modules/FxAccounts.jsm");
XPCOMUtils.defineLazyGetter(this, 'fxAccountsCommon', function() {
let ob = {};
Cu.import("resource://gre/modules/FxAccountsCommon.js", ob);
return ob;
});
function deriveKeyBundle(kB) {
let out = CryptoUtils.hkdf(kB, undefined,
"identity.mozilla.com/picl/v1/oldsync", 2*32);
@ -39,6 +46,8 @@ function deriveKeyBundle(kB) {
this.BrowserIDManager = function BrowserIDManager() {
this._fxaService = fxAccounts;
this._tokenServerClient = new TokenServerClient();
// will be a promise that resolves when we are ready to authenticate
this.whenReadyToAuthenticate = null;
this._log = Log.repository.getLogger("Sync.BrowserIDManager");
this._log.Level = Log.Level[Svc.Prefs.get("log.logger.identity")];
@ -53,6 +62,107 @@ this.BrowserIDManager.prototype = {
_token: null,
_account: null,
// it takes some time to fetch a sync key bundle, so until this flag is set,
// we don't consider the lack of a keybundle as a failure state.
_shouldHaveSyncKeyBundle: false,
get readyToAuthenticate() {
// We are finished initializing when we *should* have a sync key bundle,
// although we might not actually have one due to auth failures etc.
return this._shouldHaveSyncKeyBundle;
},
get needsCustomization() {
try {
return Services.prefs.getBoolPref("services.sync.needsCustomization");
} catch (e) {
return false;
}
},
initialize: function() {
Services.obs.addObserver(this, fxAccountsCommon.ONVERIFIED_NOTIFICATION, false);
Services.obs.addObserver(this, fxAccountsCommon.ONLOGOUT_NOTIFICATION, false);
return this.initializeWithCurrentIdentity();
},
initializeWithCurrentIdentity: function() {
this._log.trace("initializeWithCurrentIdentity");
Components.utils.import("resource://services-sync/main.js");
// Reset the world before we do anything async.
this.whenReadyToAuthenticate = Promise.defer();
this._shouldHaveSyncKeyBundle = false;
this.username = ""; // this calls resetCredentials which drops the key bundle.
return fxAccounts.getSignedInUser().then(accountData => {
if (!accountData) {
this._log.info("initializeWithCurrentIdentity has no user logged in");
this._account = null;
return;
}
if (this.needsCustomization) {
// If the user chose to "Customize sync options" when signing
// up with Firefox Accounts, ask them to choose what to sync.
const url = "chrome://browser/content/sync/customize.xul";
const features = "centerscreen,chrome,modal,dialog,resizable=no";
let win = Services.wm.getMostRecentWindow("navigator:browser");
let data = {accepted: false};
win.openDialog(url, "_blank", features, data);
if (data.accepted) {
Services.prefs.clearUserPref("services.sync.needsCustomization");
} else {
// Log out if the user canceled the dialog.
return fxAccounts.signOut();
}
}
this._account = accountData.email;
// We start a background keybundle fetch...
this._log.info("Starting background fetch for key bundle.");
this._fetchSyncKeyBundle().then(() => {
this._shouldHaveSyncKeyBundle = true; // and we should actually have one...
this.whenReadyToAuthenticate.resolve();
this._log.info("Background fetch for key bundle done");
}).then(null, err => {
this._shouldHaveSyncKeyBundle = true; // but we probably don't have one...
this.whenReadyToAuthenticate.reject(err);
// report what failed...
this._log.error("Background fetch for key bundle failed: " + err);
throw err;
});
// and we are done - the fetch continues on in the background...
}).then(null, err => {
dump("err in processing logged in account "+err.message);
});
},
observe: function (subject, topic, data) {
switch (topic) {
case fxAccountsCommon.ONVERIFIED_NOTIFICATION:
case fxAccountsCommon.ONLOGIN_NOTIFICATION:
// For now, we just assume it's the same user logging back in.
// Bug 958927 exists to work out what to do if that's not true. It might
// be that the :onlogout observer does a .startOver (or maybe not - TBD)
// But for now, do nothing, and sync will just start re-synching in its
// own sweet time...
this.initializeWithCurrentIdentity();
break;
case fxAccountsCommon.ONLOGOUT_NOTIFICATION:
Components.utils.import("resource://services-sync/main.js");
// Setting .username calls resetCredentials which drops the key bundle
// and resets _shouldHaveSyncKeyBundle.
this.username = "";
this._account = null;
Weave.Service.logout();
break;
}
},
/**
* Provide override point for testing token expiration.
*/
@ -60,8 +170,6 @@ this.BrowserIDManager.prototype = {
return Date.now();
},
clusterURL: null,
get account() {
return this._account;
},
@ -148,6 +256,7 @@ this.BrowserIDManager.prototype = {
this._syncKey = null;
this._syncKeyBundle = null;
this._syncKeyUpdated = true;
this._shouldHaveSyncKeyBundle = false;
},
/**
@ -166,8 +275,8 @@ this.BrowserIDManager.prototype = {
// No need to check this.syncKey as our getter for that attribute
// uses this.syncKeyBundle
// If bundle creation failed.
if (!this.syncKeyBundle) {
// If bundle creation started, but failed.
if (this._shouldHaveSyncKeyBundle && !this.syncKeyBundle) {
return LOGIN_FAILED_NO_PASSPHRASE;
}
@ -221,45 +330,24 @@ this.BrowserIDManager.prototype = {
return userData;
},
// initWithLoggedInUser will fetch the logged in user from firefox accounts,
// and if such a logged in user exists, will use that user to initialize
// the identity module. Returns a Promise.
initWithLoggedInUser: function() {
// Get the signed in user from FxAccounts.
return this._fxaService.getSignedInUser()
.then(userData => {
if (!userData) {
this._log.warn("initWithLoggedInUser found no logged in user");
throw new Error("initWithLoggedInUser found no logged in user");
}
// Make a note of the last logged in user.
this._account = userData.email;
// Fetch a sync token for the logged in user from the token server.
return this._refreshTokenForLoggedInUser();
})
.then(token => {
this._token = token;
// Set the username to be the uid returned by the token server.
// TODO: check here to see if the uid is different that the current
// this.username. If so, we may need to reinit sync, detect if the new
// user has sync set up, etc
this.username = this._token.uid.toString();
return this._fxaService.getKeys();
})
.then(userData => {
// both Jelly and FxAccounts give us kA/kB as hex.
let kB = Utils.hexToBytes(userData.kB);
this._syncKeyBundle = deriveKeyBundle(kB);
// Set the clusterURI for this user based on the endpoint in the
// token. This is a bit of a hack, and we should figure out a better
// way of distributing it to components that need it.
let clusterURI = Services.io.newURI(this._token.endpoint, null, null);
clusterURI.path = "/";
this.clusterURL = clusterURI.spec;
this._log.info("initWithLoggedUser has username " + this.username + ", endpoint is " + this.clusterURL);
});
_fetchSyncKeyBundle: function() {
// Fetch a sync token for the logged in user from the token server.
return this._refreshTokenForLoggedInUser(
).then(token => {
this._token = token;
return this._fxaService.getKeys();
}).then(userData => {
// unlikely, but if the logged in user somehow changed between these
// calls we better fail.
if (!userData || userData.email !== this.account) {
throw new Error("The currently logged-in user has changed.");
}
// Set the username to be the uid returned by the token server.
this.username = this._token.uid.toString();
// both Jelly and FxAccounts give us kA/kB as hex.
let kB = Utils.hexToBytes(userData.kB);
this._syncKeyBundle = deriveKeyBundle(kB);
});
},
// Refresh the sync token for the currently logged in Firefox Accounts user.
@ -368,5 +456,45 @@ this.BrowserIDManager.prototype = {
}
request.setHeader("authorization", header.headers.authorization);
return request;
},
createClusterManager: function(service) {
return new BrowserIDClusterManager(service);
}
};
/* An implementation of the ClusterManager for this identity
*/
function BrowserIDClusterManager(service) {
ClusterManager.call(this, service);
}
BrowserIDClusterManager.prototype = {
__proto__: ClusterManager.prototype,
_findCluster: function() {
let promiseClusterURL = function() {
return fxAccounts.getSignedInUser().then(userData => {
return this.identity._fetchTokenForUser(userData).then(token => {
// Set the clusterURI for this user based on the endpoint in the
// token. This is a bit of a hack, and we should figure out a better
// way of distributing it to components that need it.
let clusterURI = Services.io.newURI(token.endpoint, null, null);
clusterURI.path = "/";
return clusterURI.spec;
});
});
}.bind(this);
let cb = Async.makeSpinningCallback();
promiseClusterURL().then(function (clusterURL) {
cb(null, clusterURL);
},
function (err) {
cb(err);
});
return cb.wait();
},
}

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

@ -119,6 +119,7 @@ LOGIN_FAILED_NETWORK_ERROR: "error.login.reason.network",
LOGIN_FAILED_SERVER_ERROR: "error.login.reason.server",
LOGIN_FAILED_INVALID_PASSPHRASE: "error.login.reason.recoverykey",
LOGIN_FAILED_LOGIN_REJECTED: "error.login.reason.account",
LOGIN_FAILED_NOT_READY: "error.login.reason.initializing",
// sync failure status codes
METARECORD_DOWNLOAD_FAIL: "error.sync.reason.metarecord_download_fail",

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

@ -9,6 +9,7 @@ this.EXPORTED_SYMBOLS = ["IdentityManager"];
const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Promise.jsm");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://services-sync/util.js");
@ -21,7 +22,8 @@ for (let symbol of ["BulkKeyBundle", "SyncKeyBundle"]) {
}
/**
* Manages identity and authentication for Sync.
* Manages "legacy" identity and authentication for Sync.
* See browserid_identity for the Firefox Accounts based identity manager.
*
* The following entities are managed:
*
@ -81,6 +83,24 @@ IdentityManager.prototype = {
_syncKeyBundle: null,
/**
* Initialize the identity provider. Returns a promise that is resolved
* when initialization is complete and the provider can be queried for
* its state
*/
initialize: function() {
// nothing to do for this identity provider
return Promise.resolve();
},
/**
* Indicates if the identity manager is still initializing
*/
get readyToAuthenticate() {
// We initialize in a fully sync manner, so we are always finished.
return true;
},
get account() {
return Svc.Prefs.get("account", this.username);
},
@ -505,5 +525,10 @@ IdentityManager.prototype = {
onRESTRequestBasic: function onRESTRequestBasic(request) {
let up = this.username + ":" + this.basicPassword;
request.setHeader("authorization", "Basic " + btoa(up));
},
createClusterManager: function(service) {
Cu.import("resource://services-sync/stages/cluster.js");
return new ClusterManager(service);
}
};

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

@ -12,9 +12,11 @@ const {classes: Cc, interfaces: Ci, utils: Cu, results: Cr} = Components;
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://services-sync/engines.js");
Cu.import("resource://services-sync/status.js");
Cu.import("resource://services-sync/util.js");
XPCOMUtils.defineLazyModuleGetter(this, "Status",
"resource://services-sync/status.js");
this.SyncScheduler = function SyncScheduler(service) {
this.service = service;
this.init();

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

@ -30,7 +30,6 @@ Cu.import("resource://services-sync/policies.js");
Cu.import("resource://services-sync/record.js");
Cu.import("resource://services-sync/resource.js");
Cu.import("resource://services-sync/rest.js");
Cu.import("resource://services-sync/stages/cluster.js");
Cu.import("resource://services-sync/stages/enginesync.js");
Cu.import("resource://services-sync/status.js");
Cu.import("resource://services-sync/userapi.js");
@ -323,7 +322,7 @@ Sync11Service.prototype = {
this._log.info("Loading Weave " + WEAVE_VERSION);
this._clusterManager = new ClusterManager(this);
this._clusterManager = this.identity.createClusterManager(this);
this.recordManager = new RecordManager(this);
this.enabled = true;
@ -649,6 +648,13 @@ Sync11Service.prototype = {
},
verifyLogin: function verifyLogin() {
// If the identity isn't ready it might not know the username...
if (!this.identity.readyToAuthenticate) {
this._log.info("Not ready to authenticate in verifyLogin.");
this.status.login = LOGIN_FAILED_NOT_READY;
return false;
}
if (!this.identity.username) {
this._log.warn("No username in verifyLogin.");
this.status.login = LOGIN_FAILED_NO_USERNAME;

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

@ -12,13 +12,31 @@ const Cu = Components.utils;
Cu.import("resource://services-sync/constants.js");
Cu.import("resource://gre/modules/Log.jsm");
Cu.import("resource://services-sync/identity.js");
Cu.import("resource://services-sync/browserid_identity.js");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://services-common/async.js");
this.Status = {
_log: Log.repository.getLogger("Sync.Status"),
_authManager: new IdentityManager(),
__authManager: null,
ready: false,
get _authManager() {
if (this.__authManager) {
return this.__authManager;
}
let service = Components.classes["@mozilla.org/weave/service;1"]
.getService(Components.interfaces.nsISupports)
.wrappedJSObject;
let idClass = service.fxAccountsEnabled ? BrowserIDManager : IdentityManager;
this.__authManager = new idClass();
// .initialize returns a promise, so we need to spin until it resolves.
let cb = Async.makeSpinningCallback();
this.__authManager.initialize().then(cb, cb);
cb.wait();
return this.__authManager;
},
get service() {
return this._service;
},

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

@ -73,3 +73,6 @@ pref("services.sync.log.logger.userapi", "Debug");
pref("services.sync.log.cryptoDebug", false);
pref("services.sync.tokenServerURI", "http://auth.oldsync.dev.lcip.org/1.0/sync/1.1");
pref("services.sync.fxa.termsURL", "https://accounts.firefox.com/legal/terms");
pref("services.sync.fxa.privacyURL", "https://accounts.firefox.com/legal/privacy");

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

@ -330,31 +330,21 @@ function checkItem(aExpected, aNode)
aExpected.lastModified);
break;
case "url":
yield function() {
let deferred = Promise.defer();
PlacesUtils.livemarks.getLivemark(
{ id: id },
function (aStatus, aLivemark) {
if (!Components.isSuccessCode(aStatus)) {
do_check_eq(aNode.uri, aExpected.url);
}
deferred.resolve();
}
);
return deferred.promise; }();
if (!("feedUrl" in aExpected))
do_check_eq(aNode.uri, aExpected.url)
break;
case "icon":
yield function() {
let deferred = Promise.defer();
let (deferred = Promise.defer(), data) {
PlacesUtils.favicons.getFaviconDataForPage(
NetUtil.newURI(aExpected.url),
function (aURI, aDataLen, aData, aMimeType) {
let base64Icon = "data:image/png;base64," +
base64EncodeString(String.fromCharCode.apply(String, aData));
do_check_true(base64Icon == aExpected.icon);
deferred.resolve();
});
return deferred.promise; }();
deferred.resolve(aData);
});
data = yield deferred.promise;
let base64Icon = "data:image/png;base64," +
base64EncodeString(String.fromCharCode.apply(String, data));
do_check_true(base64Icon == aExpected.icon);
}
break;
case "keyword":
break;
@ -373,18 +363,13 @@ function checkItem(aExpected, aNode)
do_check_eq((yield PlacesUtils.getCharsetForURI(testURI)), aExpected.charset);
break;
case "feedUrl":
yield function() {
let deferred = Promise.defer();
PlacesUtils.livemarks.getLivemark(
{ id: id },
function (aStatus, aLivemark) {
do_check_true(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark.siteURI.spec, aExpected.url);
do_check_eq(aLivemark.feedURI.spec, aExpected.feedUrl);
deferred.resolve();
}
);
return deferred.promise; }();
yield PlacesUtils.livemarks.getLivemark(
{ id: id },
(aStatus, aLivemark) => {
do_check_true(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark.siteURI.spec, aExpected.url);
do_check_eq(aLivemark.feedURI.spec, aExpected.feedUrl);
});
break;
case "children":
let folder = aNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);

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

@ -161,30 +161,21 @@ function checkItem(aExpected, aNode) {
aExpected.lastModified);
break;
case "url":
yield function() {
let deferred = Promise.defer();
PlacesUtils.livemarks.getLivemark(
{ id: id },
function (aStatus, aLivemark) {
if (!Components.isSuccessCode(aStatus)) {
do_check_eq(aNode.uri, aExpected.url);
}
deferred.resolve();
});
return deferred.promise; }();
if (!("feedUrl" in aExpected))
do_check_eq(aNode.uri, aExpected.url);
break;
case "icon":
yield function() {
let deferred = Promise.defer();
let (deferred = Promise.defer(), data) {
PlacesUtils.favicons.getFaviconDataForPage(
NetUtil.newURI(aExpected.url),
function (aURI, aDataLen, aData, aMimeType) {
let base64Icon = "data:image/png;base64," +
base64EncodeString(String.fromCharCode.apply(String, aData));
do_check_true(base64Icon == aExpected.icon);
deferred.resolve();
deferred.resolve(aData);
});
return deferred.promise; }();
data = yield deferred.promise;
let base64Icon = "data:image/png;base64," +
base64EncodeString(String.fromCharCode.apply(String, data));
do_check_true(base64Icon == aExpected.icon);
}
break;
case "keyword":
break;
@ -201,17 +192,13 @@ function checkItem(aExpected, aNode) {
do_check_eq((yield PlacesUtils.getCharsetForURI(testURI)), aExpected.charset);
break;
case "feedUrl":
yield function() {
let deferred = Promise.defer();
PlacesUtils.livemarks.getLivemark(
{ id: id },
function (aStatus, aLivemark) {
do_check_true(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark.siteURI.spec, aExpected.url);
do_check_eq(aLivemark.feedURI.spec, aExpected.feedUrl);
deferred.resolve();
});
return deferred.promise; }();
yield PlacesUtils.livemarks.getLivemark(
{ id: id },
(aStatus, aLivemark) => {
do_check_true(Components.isSuccessCode(aStatus));
do_check_eq(aLivemark.siteURI.spec, aExpected.url);
do_check_eq(aLivemark.feedURI.spec, aExpected.feedUrl);
});
break;
case "children":
let folder = aNode.QueryInterface(Ci.nsINavHistoryContainerResultNode);

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

@ -19,9 +19,9 @@
<children includes="observes|template|menupopup|panel|tooltip"/>
<xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label"/>
<xul:label class="toolbarbutton-text" crop="right" flex="1"
xbl:inherits="value=label,accesskey,crop"/>
xbl:inherits="value=label,accesskey,crop,wrap"/>
<xul:label class="toolbarbutton-multiline-text" flex="1"
xbl:inherits="xbl:text=label,accesskey"/>
xbl:inherits="xbl:text=label,accesskey,wrap"/>
</content>
</binding>
@ -31,9 +31,9 @@
<children includes="observes|template|menupopup|panel|tooltip"/>
<xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label,type"/>
<xul:label class="toolbarbutton-text" crop="right" flex="1"
xbl:inherits="value=label,accesskey,crop,dragover-top"/>
xbl:inherits="value=label,accesskey,crop,dragover-top,wrap"/>
<xul:label class="toolbarbutton-multiline-text" flex="1"
xbl:inherits="xbl:text=label,accesskey"/>
xbl:inherits="xbl:text=label,accesskey,wrap"/>
<xul:dropmarker anonid="dropmarker" type="menu"
class="toolbarbutton-menu-dropmarker" xbl:inherits="disabled,label"/>
</content>
@ -47,9 +47,9 @@
<xul:vbox flex="1" align="center">
<xul:image class="toolbarbutton-icon" xbl:inherits="validate,src=image,label"/>
<xul:label class="toolbarbutton-text" crop="right" flex="1"
xbl:inherits="value=label,accesskey,crop,dragover-top"/>
xbl:inherits="value=label,accesskey,crop,dragover-top,wrap"/>
<xul:label class="toolbarbutton-multiline-text" flex="1"
xbl:inherits="xbl:text=label,accesskey"/>
xbl:inherits="xbl:text=label,accesskey,wrap"/>
</xul:vbox>
<xul:dropmarker anonid="dropmarker" type="menu"
class="toolbarbutton-menu-dropmarker" xbl:inherits="disabled,label"/>

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

@ -158,12 +158,13 @@ toolbarbutton[type="menu-button"] {
}
toolbar[mode="icons"] .toolbarbutton-text,
toolbar[mode="icons"] .toolbarbutton-multiline-text,
toolbar[mode="text"] .toolbarbutton-icon {
display: none;
}
toolbarbutton:not([wrap="true"]) > .toolbarbutton-multiline-text,
toolbarbutton[wrap="true"] > .toolbarbutton-text {
.toolbarbutton-multiline-text:not([wrap="true"]),
.toolbarbutton-text[wrap="true"] {
display: none;
}

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

@ -58,16 +58,14 @@ const XRE_UPDATE_ROOT_DIR = "UpdRootD";
const CRC_ERROR = 4;
const WRITE_ERROR = 7;
const DIR_PATCH = "0";
const DIR_UPDATES = "updates";
const DIR_PATCH = "0";
const DIR_UPDATES = "updates";
#ifdef XP_MACOSX
const DIR_APP_REL_PATH = "/Contents/MacOS/";
const DIR_APP_SUFFIX = ".app";
const DIR_UPDATED = "Updated.app";
const DIR_BIN_REL_PATH = "Contents/MacOS/";
const DIR_UPDATED = "Updated.app";
#else
const DIR_APP_REL_PATH = "/appdir/";
const DIR_APP_SUFFIX = "";
const DIR_UPDATED = "updated";
const DIR_BIN_REL_PATH = "";
const DIR_UPDATED = "updated";
#endif
const FILE_BACKUP_LOG = "backup-update.log";

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

@ -415,9 +415,6 @@ function setupTestCommon() {
gAppDirOrig = getAppBaseDir();
let applyDir = getApplyDirFile(null, true).parent;
if (IS_MACOSX) {
applyDir = applyDir.parent;
}
// Try to remove the directory used to apply updates and the updates directory
// on platforms other than Windows. Since the test hasn't ran yet and the
@ -540,9 +537,6 @@ function cleanupTestCommon() {
}
let applyDir = getApplyDirFile(null, true).parent;
if (IS_MACOSX) {
applyDir = applyDir.parent;
}
// Try to remove the directory used to apply updates. Since the test has
// already finished this is non-fatal for the test.
@ -674,25 +668,18 @@ function getAppVersion() {
/**
* Helper function for getting the relative path to the directory where the
* application binary is located.
* For Mac OS X the path will be:
* <test_file_leafname>.app/Contents/MacOS/
* For other platforms the path will be:
* <test_file_leafname>/appdir/
* application binary is located (e.g. <test_file_leafname>/dir.app/).
*
* Note: The appdir subdirectory is needed for platforms other than Mac OS X so
* the tests can run in parallel due to update staging creating a lock
* file named moz_update_in_progress.lock in the parent directory of the
* installation directory.
* Note: The dir.app subdirectory under <test_file_leafname> is needed for
* platforms other than Mac OS X so the tests can run in parallel due to
* update staging creating a lock file named moz_update_in_progress.lock in
* the parent directory of the installation directory.
*
* @return The relative path to the directory where application binary is
* located.
*/
function getApplyDirPath() {
if (IS_MACOSX) {
return gTestID + DIR_APP_SUFFIX + DIR_APP_REL_PATH;
}
return gTestID + DIR_APP_REL_PATH;
return gTestID + "/dir.app/";
}
/**
@ -752,21 +739,7 @@ function getTestDirFile(aRelPath) {
* that contains the staged update.
*/
function getUpdatedDirPath() {
let updatedDirPath = gTestID;
if (IS_MACOSX) {
updatedDirPath += DIR_APP_SUFFIX;
} else {
// The appdir subdirectory is needed so the tests can run in parallel due to
// update staging creating a lock file named moz_update_in_progress.lock in
// the parent directory of the installation directory.
updatedDirPath += DIR_APP_REL_PATH;
}
if (gStageUpdate) {
updatedDirPath += DIR_UPDATED + "/";
} else if (IS_MACOSX) {
updatedDirPath += DIR_APP_REL_PATH;
}
return updatedDirPath;
return getApplyDirPath() + (gStageUpdate ? DIR_UPDATED + "/" : "");
}
/**
@ -777,13 +750,7 @@ function getUpdatedDirPath() {
* modified by the simple.mar update file.
*/
function getUpdateTestDir() {
let updateTestDir = getApplyDirFile(null, true);
if (IS_MACOSX) {
updateTestDir = updateTestDir.parent.parent;
}
updateTestDir.append("update_test");
return updateTestDir;
return getApplyDirFile("update_test", true);
}
/**
@ -793,13 +760,7 @@ function getUpdateTestDir() {
* @return nsIFile for the directory for the updating directory.
*/
function getUpdatingDir() {
let updatingDir = getApplyDirFile(null, true);
if (IS_MACOSX) {
updatingDir = updatingDir.parent.parent;
}
updatingDir.append("updating");
return updatingDir;
return getApplyDirFile("updating", true);
}
#ifdef XP_WIN
@ -941,7 +902,7 @@ function getMockUpdRootD() {
* when running a test that launches the application.
*/
function getMockUpdRootD() {
return getApplyDirFile(null, true);
return getApplyDirFile(DIR_BIN_REL_PATH, true);
}
#endif
@ -1051,6 +1012,7 @@ function runUpdate(aExpectedExitValue, aExpectedStatus, aCallback) {
if (gStageUpdate || gSwitchApp) {
applyToDirPath += "/" + DIR_UPDATED + "/";
}
if (IS_WIN) {
// Convert to native path
applyToDirPath = applyToDirPath.replace(/\//g, "\\");
@ -1341,16 +1303,17 @@ function copyFileToTestAppDir(aFileRelPath) {
if (IS_MACOSX && !srcFile.exists()) {
logTestInfo("unable to copy file since it doesn't exist! Checking if " +
fileRelPath + DIR_APP_SUFFIX + " exists. Path: " +
fileRelPath + ".app exists. Path: " +
srcFile.path);
srcFile = gGREDirOrig.clone();
for (let i = 0; i < pathParts.length; i++) {
if (pathParts[i]) {
srcFile.append(pathParts[i] + (pathParts.length - 1 == i ? DIR_APP_SUFFIX : ""));
srcFile.append(pathParts[i] + (pathParts.length - 1 == i ? ".app" : ""));
}
}
fileRelPath = fileRelPath + DIR_APP_SUFFIX;
fileRelPath = fileRelPath + ".app";
}
if (!srcFile.exists()) {
do_throw("Unable to copy file since it doesn't exist! Path: " +
srcFile.path);
@ -1361,7 +1324,7 @@ function copyFileToTestAppDir(aFileRelPath) {
let shouldSymlink = (pathParts[pathParts.length - 1] == "XUL" ||
fileRelPath.substr(fileRelPath.length - 3) == ".so" ||
fileRelPath.substr(fileRelPath.length - 6) == ".dylib");
let destFile = getApplyDirFile(fileRelPath, true);
let destFile = getApplyDirFile(DIR_BIN_REL_PATH + fileRelPath, true);
if (!shouldSymlink) {
if (!destFile.exists()) {
try {
@ -2591,7 +2554,7 @@ function getProcessArgs(aExtraArgs) {
aExtraArgs = [];
}
let appBinPath = getApplyDirFile(FILE_APP_BIN, false).path;
let appBinPath = getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, false).path;
if (/ /.test(appBinPath)) {
appBinPath = '"' + appBinPath + '"';
}
@ -2661,12 +2624,12 @@ function adjustGeneralPaths() {
switch (aProp) {
case NS_GRE_DIR:
if (gUseTestAppDir) {
return getApplyDirFile(null, true);
return getApplyDirFile(DIR_BIN_REL_PATH, true);
}
break;
case XRE_EXECUTABLE_FILE:
if (gUseTestAppDir) {
return getApplyDirFile(FILE_APP_BIN, true);
return getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, true);
}
break;
case XRE_UPDATE_ROOT_DIR:
@ -2744,7 +2707,7 @@ function adjustGeneralPaths() {
function launchAppToApplyUpdate() {
logTestInfo("start - launching application to apply update");
let appBin = getApplyDirFile(FILE_APP_BIN, false);
let appBin = getApplyDirFile(DIR_BIN_REL_PATH + FILE_APP_BIN, false);
if (typeof(customLaunchAppToApplyUpdate) == typeof(Function)) {
customLaunchAppToApplyUpdate();

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

@ -242,6 +242,8 @@ function createSymlink() {
let args = ["setup-symlink", "moz-foo", "moz-bar", "target",
getApplyDirFile().path + "/a/b/link"];
runHelperProcess(args);
getApplyDirFile("a/b/link", false).permissions = 0o666;
args = ["setup-symlink", "moz-foo2", "moz-bar2", "target2",
getApplyDirFile().path + "/a/b/link2", "change-perm"];
runHelperProcess(args);
@ -288,8 +290,8 @@ function run_test() {
compareContents : "test",
originalFile : null,
compareFile : null,
originalPerms : 0o664,
comparePerms : 0o664
originalPerms : 0o666,
comparePerms : 0o666
});
}

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

@ -15,10 +15,9 @@ generated-files = head_update.js
[marSuccessPartial.js]
[marFailurePartial.js]
[marStageSuccessComplete.js]
skip-if = os == 'mac' || toolkit == 'gonk'
skip-if = toolkit == 'gonk'
reason = bug 820380
[marStageSuccessPartial.js]
skip-if = os == 'mac'
[marVersionDowngrade.js]
run-if = os == 'win'
[marWrongChannel.js]
@ -35,7 +34,7 @@ run-if = os == 'win'
[marAppInUseSuccessComplete.js]
skip-if = toolkit == 'gonk'
[marAppInUseStageSuccessComplete_unix.js]
run-if = os == 'linux' || os == 'sunos'
run-if = os == 'linux' || os == 'sunos' || os == 'mac'
[marAppInUseStageFailureComplete_win.js]
run-if = os == 'win'
[marAppInUseFallbackStageFailureComplete_win.js]