зеркало из https://github.com/mozilla/gecko-dev.git
Merge latest green inbound changeset and mozilla-central
This commit is contained in:
Коммит
2a9e32f301
|
@ -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']
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"title": "jetpack mochitests",
|
||||
"dependencies": {
|
||||
"underscore": "1.0.0"
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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]
|
||||
|
|
Загрузка…
Ссылка в новой задаче