зеркало из https://github.com/mozilla/gecko-dev.git
merge fx-team to mozilla-central
This commit is contained in:
Коммит
c9cd70cfdf
|
@ -4,6 +4,20 @@
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
|
const {classes: Cc, interfaces: Ci, utils: Cu} = Components;
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/Services.jsm");
|
||||||
|
|
||||||
|
let service = Cc["@mozilla.org/weave/service;1"]
|
||||||
|
.getService(Ci.nsISupports)
|
||||||
|
.wrappedJSObject;
|
||||||
|
|
||||||
|
if (!service.allowPasswordsEngine) {
|
||||||
|
let checkbox = document.getElementById("fxa-pweng-chk");
|
||||||
|
checkbox.checked = false;
|
||||||
|
checkbox.disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
addEventListener("dialogaccept", function () {
|
addEventListener("dialogaccept", function () {
|
||||||
window.arguments[0].accepted = true;
|
window.arguments[0].accepted = true;
|
||||||
});
|
});
|
||||||
|
|
|
@ -44,7 +44,8 @@
|
||||||
<checkbox label="&engine.bookmarks.label;"
|
<checkbox label="&engine.bookmarks.label;"
|
||||||
accesskey="&engine.bookmarks.accesskey;"
|
accesskey="&engine.bookmarks.accesskey;"
|
||||||
preference="engine.bookmarks"/>
|
preference="engine.bookmarks"/>
|
||||||
<checkbox label="&engine.passwords.label;"
|
<checkbox id="fxa-pweng-chk"
|
||||||
|
label="&engine.passwords.label;"
|
||||||
accesskey="&engine.passwords.accesskey;"
|
accesskey="&engine.passwords.accesskey;"
|
||||||
preference="engine.passwords"/>
|
preference="engine.passwords"/>
|
||||||
<checkbox label="&engine.history.label;"
|
<checkbox label="&engine.history.label;"
|
||||||
|
|
|
@ -87,6 +87,12 @@ let gSyncUtils = {
|
||||||
this._openLink(Weave.Svc.Prefs.get(root + "privacyURL"));
|
this._openLink(Weave.Svc.Prefs.get(root + "privacyURL"));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
openMPInfoPage: function (event) {
|
||||||
|
event.stopPropagation();
|
||||||
|
let baseURL = Services.urlFormatter.formatURLPref("app.support.baseURL");
|
||||||
|
this._openLink(baseURL + "sync-master-password");
|
||||||
|
},
|
||||||
|
|
||||||
openFirstSyncProgressPage: function () {
|
openFirstSyncProgressPage: function () {
|
||||||
this._openLink("about:sync-progress");
|
this._openLink("about:sync-progress");
|
||||||
},
|
},
|
||||||
|
|
|
@ -173,6 +173,9 @@ var gSecurityPane = {
|
||||||
this.changeMasterPassword();
|
this.changeMasterPassword();
|
||||||
|
|
||||||
this._initMasterPasswordUI();
|
this._initMasterPasswordUI();
|
||||||
|
|
||||||
|
// We might want to hide sync's password engine.
|
||||||
|
gSyncPane.updateWeavePrefs();
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -154,6 +154,17 @@ let gSyncPane = {
|
||||||
for (let checkbox of engines.querySelectorAll("checkbox")) {
|
for (let checkbox of engines.querySelectorAll("checkbox")) {
|
||||||
checkbox.disabled = enginesListDisabled;
|
checkbox.disabled = enginesListDisabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let checkbox = document.getElementById("fxa-pweng-chk");
|
||||||
|
let help = document.getElementById("fxa-pweng-help");
|
||||||
|
let allowPasswordsEngine = service.allowPasswordsEngine;
|
||||||
|
|
||||||
|
if (!allowPasswordsEngine) {
|
||||||
|
checkbox.checked = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
checkbox.disabled = !allowPasswordsEngine;
|
||||||
|
help.hidden = allowPasswordsEngine;
|
||||||
});
|
});
|
||||||
// If fxAccountEnabled is false and we are in a "not configured" state,
|
// If fxAccountEnabled is false and we are in a "not configured" state,
|
||||||
// then fxAccounts is probably fully disabled rather than just unconfigured,
|
// then fxAccounts is probably fully disabled rather than just unconfigured,
|
||||||
|
|
|
@ -267,9 +267,23 @@
|
||||||
<checkbox label="&engine.bookmarks.label;"
|
<checkbox label="&engine.bookmarks.label;"
|
||||||
accesskey="&engine.bookmarks.accesskey;"
|
accesskey="&engine.bookmarks.accesskey;"
|
||||||
preference="engine.bookmarks"/>
|
preference="engine.bookmarks"/>
|
||||||
<checkbox label="&engine.passwords.label;"
|
<hbox>
|
||||||
accesskey="&engine.passwords.accesskey;"
|
<checkbox id="fxa-pweng-chk"
|
||||||
preference="engine.passwords"/>
|
label="&engine.passwords.label;"
|
||||||
|
accesskey="&engine.passwords.accesskey;"
|
||||||
|
preference="engine.passwords"/>
|
||||||
|
|
||||||
|
<vbox id="fxa-pweng-help">
|
||||||
|
<spacer flex="1"/>
|
||||||
|
<hbox id="fxa-pweng-help-link">
|
||||||
|
<label value=" ["/>
|
||||||
|
<label class="text-link" value="?"
|
||||||
|
onclick="gSyncUtils.openMPInfoPage(event);"/>
|
||||||
|
<label value="]"/>
|
||||||
|
</hbox>
|
||||||
|
<spacer flex="1"/>
|
||||||
|
</vbox>
|
||||||
|
</hbox>
|
||||||
<checkbox label="&engine.history.label;"
|
<checkbox label="&engine.history.label;"
|
||||||
accesskey="&engine.history.accesskey;"
|
accesskey="&engine.history.accesskey;"
|
||||||
preference="engine.history"/>
|
preference="engine.history"/>
|
||||||
|
|
|
@ -215,10 +215,20 @@ let SyncHandler = {
|
||||||
*/
|
*/
|
||||||
let SessionHistoryListener = {
|
let SessionHistoryListener = {
|
||||||
init: function () {
|
init: function () {
|
||||||
|
// The frame tree observer is needed to handle navigating away from
|
||||||
|
// an about page. Currently nsISHistoryListener does not have
|
||||||
|
// OnHistoryNewEntry() called for about pages because the history entry is
|
||||||
|
// modified to point at the new page. Once Bug 981900 lands the frame tree
|
||||||
|
// observer can be removed.
|
||||||
gFrameTree.addObserver(this);
|
gFrameTree.addObserver(this);
|
||||||
addEventListener("load", this, true);
|
|
||||||
addEventListener("hashchange", this, true);
|
// By adding the SHistoryListener immediately, we will unfortunately be
|
||||||
Services.obs.addObserver(this, "browser:purge-session-history", false);
|
// notified of every history entry as the tab is restored. We don't bother
|
||||||
|
// waiting to add the listener later because these notifications are cheap.
|
||||||
|
// We will likely only collect once since we are batching collection on
|
||||||
|
// a delay.
|
||||||
|
docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory.
|
||||||
|
addSHistoryListener(this);
|
||||||
|
|
||||||
// Collect data if we start with a non-empty shistory.
|
// Collect data if we start with a non-empty shistory.
|
||||||
if (!SessionHistory.isEmpty(docShell)) {
|
if (!SessionHistory.isEmpty(docShell)) {
|
||||||
|
@ -227,22 +237,9 @@ let SessionHistoryListener = {
|
||||||
},
|
},
|
||||||
|
|
||||||
uninit: function () {
|
uninit: function () {
|
||||||
Services.obs.removeObserver(this, "browser:purge-session-history");
|
let sessionHistory = docShell.QueryInterface(Ci.nsIWebNavigation).sessionHistory;
|
||||||
},
|
if (sessionHistory) {
|
||||||
|
sessionHistory.removeSHistoryListener(this);
|
||||||
observe: function () {
|
|
||||||
// We need to use setTimeout() here because we listen for
|
|
||||||
// "browser:purge-session-history". When that is fired all observers are
|
|
||||||
// expected to purge their data. We can't expect to be called *after* the
|
|
||||||
// observer in browser.xml that clears session history so we need to wait
|
|
||||||
// a tick before actually collecting data.
|
|
||||||
setTimeout(() => this.collect(), 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
handleEvent: function (event) {
|
|
||||||
// We are only interested in "load" events from subframes.
|
|
||||||
if (event.type == "hashchange" || event.target != content.document) {
|
|
||||||
this.collect();
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -258,7 +255,41 @@ let SessionHistoryListener = {
|
||||||
|
|
||||||
onFrameTreeReset: function () {
|
onFrameTreeReset: function () {
|
||||||
this.collect();
|
this.collect();
|
||||||
}
|
},
|
||||||
|
|
||||||
|
OnHistoryNewEntry: function (newURI) {
|
||||||
|
this.collect();
|
||||||
|
},
|
||||||
|
|
||||||
|
OnHistoryGoBack: function (backURI) {
|
||||||
|
this.collect();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
OnHistoryGoForward: function (forwardURI) {
|
||||||
|
this.collect();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
OnHistoryGotoIndex: function (index, gotoURI) {
|
||||||
|
this.collect();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
OnHistoryPurge: function (numEntries) {
|
||||||
|
this.collect();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
OnHistoryReload: function (reloadURI, reloadFlags) {
|
||||||
|
this.collect();
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
QueryInterface: XPCOMUtils.generateQI([
|
||||||
|
Ci.nsISHistoryListener,
|
||||||
|
Ci.nsISupportsWeakReference
|
||||||
|
])
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -165,3 +165,71 @@ add_task(function test_subframes() {
|
||||||
// Cleanup.
|
// Cleanup.
|
||||||
gBrowser.removeTab(tab);
|
gBrowser.removeTab(tab);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that navigating from an about page invalidates shistory.
|
||||||
|
*/
|
||||||
|
add_task(function test_about_page_navigate() {
|
||||||
|
// Create a new tab.
|
||||||
|
let tab = gBrowser.addTab("about:blank");
|
||||||
|
let browser = tab.linkedBrowser;
|
||||||
|
yield promiseBrowserLoaded(browser);
|
||||||
|
|
||||||
|
// Check that we have a single shistory entry.
|
||||||
|
SyncHandlers.get(browser).flush();
|
||||||
|
let {entries} = JSON.parse(ss.getTabState(tab));
|
||||||
|
is(entries.length, 1, "there is one shistory entry");
|
||||||
|
is(entries[0].url, "about:blank", "url is correct");
|
||||||
|
|
||||||
|
browser.loadURI("about:robots");
|
||||||
|
yield promiseBrowserLoaded(browser);
|
||||||
|
|
||||||
|
// Check that we have changed the history entry.
|
||||||
|
SyncHandlers.get(browser).flush();
|
||||||
|
let {entries} = JSON.parse(ss.getTabState(tab));
|
||||||
|
is(entries.length, 1, "there is one shistory entry");
|
||||||
|
is(entries[0].url, "about:robots", "url is correct");
|
||||||
|
|
||||||
|
// Cleanup.
|
||||||
|
gBrowser.removeTab(tab);
|
||||||
|
});
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ensure that history.pushState and history.replaceState invalidate shistory.
|
||||||
|
*/
|
||||||
|
add_task(function test_pushstate_replacestate() {
|
||||||
|
// Create a new tab.
|
||||||
|
let tab = gBrowser.addTab("http://example.com/1");
|
||||||
|
let browser = tab.linkedBrowser;
|
||||||
|
yield promiseBrowserLoaded(browser);
|
||||||
|
|
||||||
|
// Check that we have a single shistory entry.
|
||||||
|
SyncHandlers.get(browser).flush();
|
||||||
|
let {entries} = JSON.parse(ss.getTabState(tab));
|
||||||
|
is(entries.length, 1, "there is one shistory entry");
|
||||||
|
is(entries[0].url, "http://example.com/1", "url is correct");
|
||||||
|
|
||||||
|
browser.messageManager.
|
||||||
|
sendAsyncMessage("ss-test:historyPushState", {url: 'test-entry/'});
|
||||||
|
yield promiseContentMessage(browser, "ss-test:historyPushState");
|
||||||
|
|
||||||
|
// Check that we have added the history entry.
|
||||||
|
SyncHandlers.get(browser).flush();
|
||||||
|
let {entries} = JSON.parse(ss.getTabState(tab));
|
||||||
|
is(entries.length, 2, "there is another shistory entry");
|
||||||
|
is(entries[1].url, "http://example.com/test-entry/", "url is correct");
|
||||||
|
|
||||||
|
// Disabled until replaceState invalidation is supported. See Bug 967028.
|
||||||
|
// browser.messageManager.
|
||||||
|
// sendAsyncMessage("ss-test:historyReplaceState", {url: 'test-entry2/'});
|
||||||
|
// yield promiseContentMessage(browser, "ss-test:historyReplaceState");
|
||||||
|
|
||||||
|
// // Check that we have modified the history entry.
|
||||||
|
// SyncHandlers.get(browser).flush();
|
||||||
|
// let {entries} = JSON.parse(ss.getTabState(tab));
|
||||||
|
// is(entries.length, 2, "there is still two shistory entries");
|
||||||
|
// is(entries[1].url, "http://example.com/test-entry/test-entry2/", "url is correct");
|
||||||
|
|
||||||
|
// Cleanup.
|
||||||
|
gBrowser.removeTab(tab);
|
||||||
|
});
|
||||||
|
|
|
@ -163,3 +163,17 @@ addMessageListener("ss-test:click", function ({data}) {
|
||||||
content.document.getElementById(data.id).click();
|
content.document.getElementById(data.id).click();
|
||||||
sendAsyncMessage("ss-test:click");
|
sendAsyncMessage("ss-test:click");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
addMessageListener("ss-test:historyPushState", function ({data}) {
|
||||||
|
content.window.history.
|
||||||
|
pushState(data.stateObj || {}, data.title || "", data.url);
|
||||||
|
|
||||||
|
sendAsyncMessage("ss-test:historyPushState");
|
||||||
|
});
|
||||||
|
|
||||||
|
addMessageListener("ss-test:historyReplaceState", function ({data}) {
|
||||||
|
content.window.history.
|
||||||
|
replaceState(data.stateObj || {}, data.title || "", data.url);
|
||||||
|
|
||||||
|
sendAsyncMessage("ss-test:historyReplaceState");
|
||||||
|
});
|
||||||
|
|
|
@ -165,4 +165,8 @@ label.small {
|
||||||
margin-bottom: 0.6em;
|
margin-bottom: 0.6em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#fxa-pweng-help-link > label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
%endif
|
%endif
|
||||||
|
|
|
@ -230,4 +230,8 @@ html|a.inline-link:-moz-focusring {
|
||||||
margin-bottom: 0.6em;
|
margin-bottom: 0.6em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#fxa-pweng-help-link > label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
%endif
|
%endif
|
||||||
|
|
|
@ -233,7 +233,7 @@ toolbarpaletteitem[place="toolbar"] {
|
||||||
max-width: 24px;
|
max-width: 24px;
|
||||||
min-width: 24px;
|
min-width: 24px;
|
||||||
max-height: 24px;
|
max-height: 24px;
|
||||||
height: 24px;
|
min-height: 24px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -855,11 +855,16 @@ toolbarpaletteitem[place="palette"] > #search-container {
|
||||||
border-color: hsla(210,4%,10%,0);
|
border-color: hsla(210,4%,10%,0);
|
||||||
border-bottom-color: hsla(210,4%,10%,.1);
|
border-bottom-color: hsla(210,4%,10%,.1);
|
||||||
padding: 0;
|
padding: 0;
|
||||||
margin-bottom: -1px;
|
|
||||||
transition-property: background-color, border-color;
|
transition-property: background-color, border-color;
|
||||||
transition-duration: 150ms;
|
transition-duration: 150ms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Make direct siblings overlap borders: */
|
||||||
|
.toolbaritem-combined-buttons + .toolbaritem-combined-buttons@inAnyPanel@ {
|
||||||
|
margin-top: -1px;
|
||||||
|
border-top-color: transparent !important;
|
||||||
|
}
|
||||||
|
|
||||||
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton {
|
.toolbaritem-combined-buttons@inAnyPanel@ > toolbarbutton {
|
||||||
border: 0;
|
border: 0;
|
||||||
padding: .5em;
|
padding: .5em;
|
||||||
|
|
|
@ -1681,7 +1681,7 @@ toolbarbutton[type="socialmark"] > .toolbarbutton-icon {
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-promo-box {
|
.panel-promo-box {
|
||||||
margin: 10px -4px -4px;
|
margin: 10px -10px -10px;
|
||||||
padding: 8px 10px;
|
padding: 8px 10px;
|
||||||
border-top: 1px solid ThreeDShadow;
|
border-top: 1px solid ThreeDShadow;
|
||||||
background-image: linear-gradient(hsla(0,0%,0%,.15), hsla(0,0%,0%,.08) 6px);
|
background-image: linear-gradient(hsla(0,0%,0%,.15), hsla(0,0%,0%,.08) 6px);
|
||||||
|
@ -2186,19 +2186,6 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Notification icon box */
|
/* Notification icon box */
|
||||||
#notification-popup > .panel-arrowcontainer > .panel-arrowcontent {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#notification-popup .popup-notification-closebutton {
|
|
||||||
-moz-margin-end: -14px;
|
|
||||||
margin-top: -10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#notification-popup .panel-promo-box {
|
|
||||||
margin: 10px -10px -10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#notification-popup-box {
|
#notification-popup-box {
|
||||||
position: relative;
|
position: relative;
|
||||||
background-color: #fff;
|
background-color: #fff;
|
||||||
|
|
Двоичные данные
browser/themes/windows/menuPanel-aero.png
Двоичные данные
browser/themes/windows/menuPanel-aero.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 28 KiB После Ширина: | Высота: | Размер: 28 KiB |
Двоичные данные
browser/themes/windows/menuPanel.png
Двоичные данные
browser/themes/windows/menuPanel.png
Двоичный файл не отображается.
До Ширина: | Высота: | Размер: 14 KiB После Ширина: | Высота: | Размер: 14 KiB |
|
@ -155,4 +155,8 @@ label.small {
|
||||||
margin-bottom: 0.6em;
|
margin-bottom: 0.6em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#fxa-pweng-help-link > label {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
|
||||||
%endif
|
%endif
|
||||||
|
|
|
@ -19,6 +19,7 @@ import org.mozilla.gecko.fxa.login.State;
|
||||||
import org.mozilla.gecko.fxa.login.State.StateLabel;
|
import org.mozilla.gecko.fxa.login.State.StateLabel;
|
||||||
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
|
import org.mozilla.gecko.sync.setup.activities.ActivityUtils;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
@ -63,6 +64,8 @@ public class FxAccountConfirmAccountActivity extends FxAccountAbstractActivity i
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
ActivityUtils.openURLInFennec(v.getContext(), null);
|
ActivityUtils.openURLInFennec(v.getContext(), null);
|
||||||
|
setResult(Activity.RESULT_OK);
|
||||||
|
finish();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -37,6 +37,7 @@
|
||||||
<activity
|
<activity
|
||||||
android:theme="@style/FxAccountTheme"
|
android:theme="@style/FxAccountTheme"
|
||||||
android:name="org.mozilla.gecko.fxa.activities.FxAccountConfirmAccountActivity"
|
android:name="org.mozilla.gecko.fxa.activities.FxAccountConfirmAccountActivity"
|
||||||
|
android:noHistory="true"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
@ -49,6 +50,7 @@
|
||||||
<activity
|
<activity
|
||||||
android:theme="@style/FxAccountTheme"
|
android:theme="@style/FxAccountTheme"
|
||||||
android:name="org.mozilla.gecko.fxa.activities.FxAccountVerifiedAccountActivity"
|
android:name="org.mozilla.gecko.fxa.activities.FxAccountVerifiedAccountActivity"
|
||||||
|
android:noHistory="true"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
@ -61,5 +63,6 @@
|
||||||
<activity
|
<activity
|
||||||
android:theme="@style/FxAccountTheme"
|
android:theme="@style/FxAccountTheme"
|
||||||
android:name="org.mozilla.gecko.fxa.activities.FxAccountCreateAccountNotAllowedActivity"
|
android:name="org.mozilla.gecko.fxa.activities.FxAccountCreateAccountNotAllowedActivity"
|
||||||
|
android:noHistory="true"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
|
@ -117,6 +117,16 @@ WeaveService.prototype = {
|
||||||
return fxAccountsEnabled;
|
return fxAccountsEnabled;
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the password engine is allowed. We explicitly disallow
|
||||||
|
* the password engine when a master password is used to ensure those can't
|
||||||
|
* be accessed without the master key.
|
||||||
|
*/
|
||||||
|
get allowPasswordsEngine() {
|
||||||
|
// This doesn't apply to old-style sync, it's only an issue for FxA.
|
||||||
|
return !this.fxAccountsEnabled || !Utils.mpEnabled();
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Whether Sync appears to be enabled.
|
* Whether Sync appears to be enabled.
|
||||||
*
|
*
|
||||||
|
|
|
@ -36,6 +36,28 @@ PasswordEngine.prototype = {
|
||||||
_recordObj: LoginRec,
|
_recordObj: LoginRec,
|
||||||
applyIncomingBatchSize: PASSWORDS_STORE_BATCH_SIZE,
|
applyIncomingBatchSize: PASSWORDS_STORE_BATCH_SIZE,
|
||||||
|
|
||||||
|
get isAllowed() {
|
||||||
|
return Cc["@mozilla.org/weave/service;1"]
|
||||||
|
.getService(Ci.nsISupports)
|
||||||
|
.wrappedJSObject
|
||||||
|
.allowPasswordsEngine;
|
||||||
|
},
|
||||||
|
|
||||||
|
get enabled() {
|
||||||
|
// If we are disabled due to !isAllowed(), we must take care to ensure the
|
||||||
|
// engine has actually had the enabled setter called which reflects this state.
|
||||||
|
let prefVal = SyncEngine.prototype.__lookupGetter__("enabled").call(this);
|
||||||
|
let newVal = this.isAllowed && prefVal;
|
||||||
|
if (newVal != prefVal) {
|
||||||
|
this.enabled = newVal;
|
||||||
|
}
|
||||||
|
return newVal;
|
||||||
|
},
|
||||||
|
|
||||||
|
set enabled(val) {
|
||||||
|
SyncEngine.prototype.__lookupSetter__("enabled").call(this, this.isAllowed && val);
|
||||||
|
},
|
||||||
|
|
||||||
_syncFinish: function _syncFinish() {
|
_syncFinish: function _syncFinish() {
|
||||||
SyncEngine.prototype._syncFinish.call(this);
|
SyncEngine.prototype._syncFinish.call(this);
|
||||||
|
|
||||||
|
|
|
@ -254,25 +254,37 @@ EngineSynchronizer.prototype = {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (Svc.Prefs.get("engineStatusChanged." + engine.prefName, false)) {
|
let attemptedEnable = false;
|
||||||
// The engine was disabled locally. Wipe server data and
|
// If the engine was enabled remotely, enable it locally.
|
||||||
// disable it everywhere.
|
if (!Svc.Prefs.get("engineStatusChanged." + engine.prefName, false)) {
|
||||||
this._log.trace("Wiping data for " + engineName + " engine.");
|
|
||||||
engine.wipeServer();
|
|
||||||
delete meta.payload.engines[engineName];
|
|
||||||
meta.changed = true; // TODO: Should we still do this?
|
|
||||||
|
|
||||||
// We also here mark the engine as declined, because the pref
|
|
||||||
// was explicitly changed to false.
|
|
||||||
// This will be reflected in meta/global in the next stage.
|
|
||||||
this._log.trace("Engine " + engineName + " was disabled locally. Marking as declined.");
|
|
||||||
toDecline.add(engineName);
|
|
||||||
} else {
|
|
||||||
// The engine was enabled remotely. Enable it locally.
|
|
||||||
this._log.trace("Engine " + engineName + " was enabled. Marking as non-declined.");
|
this._log.trace("Engine " + engineName + " was enabled. Marking as non-declined.");
|
||||||
toUndecline.add(engineName);
|
toUndecline.add(engineName);
|
||||||
this._log.trace(engineName + " engine was enabled remotely.");
|
this._log.trace(engineName + " engine was enabled remotely.");
|
||||||
engine.enabled = true;
|
engine.enabled = true;
|
||||||
|
// Note that setting engine.enabled to true might not have worked for
|
||||||
|
// the password engine if a master-password is enabled. However, it's
|
||||||
|
// still OK that we added it to undeclined - the user *tried* to enable
|
||||||
|
// it remotely - so it still winds up as not being flagged as declined
|
||||||
|
// even though it's disabled remotely.
|
||||||
|
attemptedEnable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If either the engine was disabled locally or enabling the engine
|
||||||
|
// failed (see above re master-password) then wipe server data and
|
||||||
|
// disable it everywhere.
|
||||||
|
if (!engine.enabled) {
|
||||||
|
this._log.trace("Wiping data for " + engineName + " engine.");
|
||||||
|
engine.wipeServer();
|
||||||
|
delete meta.payload.engines[engineName];
|
||||||
|
meta.changed = true; // the new enabled state must propagate
|
||||||
|
// We also here mark the engine as declined, because the pref
|
||||||
|
// was explicitly changed to false - unless we tried, and failed,
|
||||||
|
// to enable it - in which case we leave the declined state alone.
|
||||||
|
if (!attemptedEnable) {
|
||||||
|
// This will be reflected in meta/global in the next stage.
|
||||||
|
this._log.trace("Engine " + engineName + " was disabled locally. Marking as declined.");
|
||||||
|
toDecline.add(engineName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -290,8 +302,8 @@ EngineSynchronizer.prototype = {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.service.engineManager.decline(toDecline);
|
engineManager.decline(toDecline);
|
||||||
this.service.engineManager.undecline(toUndecline);
|
engineManager.undecline(toUndecline);
|
||||||
|
|
||||||
Svc.Prefs.resetBranch("engineStatusChanged.");
|
Svc.Prefs.resetBranch("engineStatusChanged.");
|
||||||
this.service._ignorePrefObserver = false;
|
this.service._ignorePrefObserver = false;
|
||||||
|
|
|
@ -555,6 +555,22 @@ this.Utils = {
|
||||||
return function innerBind() { return method.apply(object, arguments); };
|
return function innerBind() { return method.apply(object, arguments); };
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is there a master password configured, regardless of current lock state?
|
||||||
|
*/
|
||||||
|
mpEnabled: function mpEnabled() {
|
||||||
|
let modules = Cc["@mozilla.org/security/pkcs11moduledb;1"]
|
||||||
|
.getService(Ci.nsIPKCS11ModuleDB);
|
||||||
|
let sdrSlot = modules.findSlotByName("");
|
||||||
|
let status = sdrSlot.status;
|
||||||
|
let slots = Ci.nsIPKCS11Slot;
|
||||||
|
|
||||||
|
return status != slots.SLOT_UNINITIALIZED && status != slots.SLOT_READY;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Is there a master password configured and currently locked?
|
||||||
|
*/
|
||||||
mpLocked: function mpLocked() {
|
mpLocked: function mpLocked() {
|
||||||
let modules = Cc["@mozilla.org/security/pkcs11moduledb;1"]
|
let modules = Cc["@mozilla.org/security/pkcs11moduledb;1"]
|
||||||
.getService(Ci.nsIPKCS11ModuleDB);
|
.getService(Ci.nsIPKCS11ModuleDB);
|
||||||
|
|
|
@ -0,0 +1,130 @@
|
||||||
|
/* Any copyright is dedicated to the Public Domain.
|
||||||
|
http://creativecommons.org/publicdomain/zero/1.0/ */
|
||||||
|
|
||||||
|
Cu.import("resource://gre/modules/Log.jsm");
|
||||||
|
Cu.import("resource://services-sync/stages/enginesync.js");
|
||||||
|
Cu.import("resource://services-sync/util.js");
|
||||||
|
Cu.import("resource://services-sync/engines/passwords.js");
|
||||||
|
Cu.import("resource://services-sync/service.js");
|
||||||
|
Cu.import("resource://testing-common/services/sync/utils.js");
|
||||||
|
|
||||||
|
function run_test() {
|
||||||
|
initTestLogging("Trace");
|
||||||
|
run_next_test();
|
||||||
|
}
|
||||||
|
|
||||||
|
add_test(function test_simple() {
|
||||||
|
Services.prefs.setBoolPref("services.sync.fxaccounts.enabled", true);
|
||||||
|
// Stub mpEnabled.
|
||||||
|
let mpEnabledF = Utils.mpEnabled;
|
||||||
|
let mpEnabled = false;
|
||||||
|
Utils.mpEnabled = function() mpEnabled;
|
||||||
|
|
||||||
|
let manager = Service.engineManager;
|
||||||
|
|
||||||
|
Service.engineManager.register(PasswordEngine);
|
||||||
|
let engine = Service.engineManager.get("passwords");
|
||||||
|
let wipeCount = 0;
|
||||||
|
let engineWipeServerF = engine.wipeServer;
|
||||||
|
engine.wipeServer = function() {
|
||||||
|
++wipeCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
// A server for the metadata.
|
||||||
|
let server = new SyncServer();
|
||||||
|
let johndoe = server.registerUser("johndoe", "password");
|
||||||
|
johndoe.createContents({
|
||||||
|
meta: {global: {engines: {passwords: {version: engine.version,
|
||||||
|
syncID: engine.syncID}}}},
|
||||||
|
crypto: {},
|
||||||
|
clients: {}
|
||||||
|
});
|
||||||
|
server.start();
|
||||||
|
setBasicCredentials("johndoe", "password", "abcdeabcdeabcdeabcdeabcdea");
|
||||||
|
Service.serverURL = server.baseURI;
|
||||||
|
Service.clusterURL = server.baseURI;
|
||||||
|
|
||||||
|
let engineSync = new EngineSynchronizer(Service);
|
||||||
|
engineSync._log.level = Log.Level.Trace;
|
||||||
|
|
||||||
|
function assertEnabled(expected, message) {
|
||||||
|
Assert.strictEqual(engine.enabled, expected, message);
|
||||||
|
// The preference *must* reflect the actual state.
|
||||||
|
Assert.strictEqual(Svc.Prefs.get("engine." + engine.prefName), expected,
|
||||||
|
message + " (pref should match enabled state)");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
assertEnabled(true, "password engine should be enabled by default")
|
||||||
|
let engineMeta = Service.recordManager.get(engine.metaURL);
|
||||||
|
// This engine should be in the meta/global
|
||||||
|
Assert.notStrictEqual(engineMeta.payload.engines[engine.name], undefined,
|
||||||
|
"The engine should appear in the metadata");
|
||||||
|
Assert.ok(!engineMeta.changed, "the metadata for the password engine hasn't changed");
|
||||||
|
|
||||||
|
// (pretend to) enable a master-password
|
||||||
|
mpEnabled = true;
|
||||||
|
// The password engine should be locally disabled...
|
||||||
|
assertEnabled(false, "if mp is locked the engine should be disabled");
|
||||||
|
// ...but not declined.
|
||||||
|
Assert.ok(!manager.isDeclined("passwords"), "password engine is not declined");
|
||||||
|
// Next time a sync would happen, we call _updateEnabledEngines(), which
|
||||||
|
// would remove the engine from the metadata - call that now.
|
||||||
|
engineSync._updateEnabledEngines();
|
||||||
|
// The global meta should no longer list the engine.
|
||||||
|
engineMeta = Service.recordManager.get(engine.metaURL);
|
||||||
|
Assert.strictEqual(engineMeta.payload.engines[engine.name], undefined,
|
||||||
|
"The engine should have vanished");
|
||||||
|
// And we should have wiped the server data.
|
||||||
|
Assert.strictEqual(wipeCount, 1, "wipeServer should have been called");
|
||||||
|
|
||||||
|
// Now simulate an incoming meta/global indicating the engine should be
|
||||||
|
// enabled. We should fail to actually enable it - the pref should remain
|
||||||
|
// false and we wipe the server for anything another device might have
|
||||||
|
// stored.
|
||||||
|
let meta = {
|
||||||
|
payload: {
|
||||||
|
engines: {
|
||||||
|
"passwords": {"version":1,"syncID":"yfBi2v7PpFO2"},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
engineSync._updateEnabledFromMeta(meta, 3, manager);
|
||||||
|
Assert.strictEqual(wipeCount, 2, "wipeServer should have been called");
|
||||||
|
Assert.ok(!manager.isDeclined("passwords"), "password engine is not declined");
|
||||||
|
assertEnabled(false, "engine still not enabled locally");
|
||||||
|
|
||||||
|
// Let's turn the MP off - but *not* re-enable it locally.
|
||||||
|
mpEnabled = false;
|
||||||
|
// Just disabling the MP isn't enough to force it back to enabled.
|
||||||
|
assertEnabled(false, "engine still not enabled locally");
|
||||||
|
// Another incoming metadata record with the engine enabled should cause
|
||||||
|
// it to be enabled locally.
|
||||||
|
meta = {
|
||||||
|
payload: {
|
||||||
|
engines: {
|
||||||
|
"passwords": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
engineSync._updateEnabledFromMeta(meta, 3, manager);
|
||||||
|
Assert.strictEqual(wipeCount, 2, "wipeServer should *not* have been called again");
|
||||||
|
Assert.ok(!manager.isDeclined("passwords"), "password engine is not declined");
|
||||||
|
// It should be enabled locally.
|
||||||
|
assertEnabled(true, "engine now enabled locally");
|
||||||
|
// Next time a sync starts it should magically re-appear in our meta/global
|
||||||
|
engine._syncStartup();
|
||||||
|
//engineSync._updateEnabledEngines();
|
||||||
|
engineMeta = Service.recordManager.get(engine.metaURL);
|
||||||
|
Assert.equal(engineMeta.payload.engines[engine.name].version, engine.version,
|
||||||
|
"The engine should re-appear in the metadata");
|
||||||
|
} finally {
|
||||||
|
// restore the damage we did above...
|
||||||
|
engine.wipeServer = engineWipeServerF;
|
||||||
|
engine._store.wipe();
|
||||||
|
// Un-stub mpEnabled.
|
||||||
|
Utils.mpEnabled = mpEnabledF;
|
||||||
|
Services.prefs.clearUserPref("services.sync.fxaccounts.enabled");
|
||||||
|
server.stop(run_next_test);
|
||||||
|
}
|
||||||
|
});
|
|
@ -169,3 +169,5 @@ skip-if = debug
|
||||||
|
|
||||||
[test_healthreport.js]
|
[test_healthreport.js]
|
||||||
skip-if = ! healthreport
|
skip-if = ! healthreport
|
||||||
|
|
||||||
|
[test_password_mpenabled.js]
|
||||||
|
|
|
@ -167,6 +167,6 @@ XXX: apply styles to all themes until bug 509642 is fixed
|
||||||
%endif
|
%endif
|
||||||
|
|
||||||
.popup-notification-closebutton {
|
.popup-notification-closebutton {
|
||||||
-moz-margin-end: -8px;
|
-moz-margin-end: -14px;
|
||||||
margin-top: -4px;
|
margin-top: -10px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ panel[type="arrow"][side="right"] {
|
||||||
}
|
}
|
||||||
|
|
||||||
.panel-arrowcontent {
|
.panel-arrowcontent {
|
||||||
padding: 4px;
|
padding: 10px;
|
||||||
color: -moz-FieldText;
|
color: -moz-FieldText;
|
||||||
background: -moz-field;
|
background: -moz-field;
|
||||||
background-clip: padding-box;
|
background-clip: padding-box;
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
|
|
||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
let promise = Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js", {}).Promise;
|
const { Promise } = Cu.import("resource://gre/modules/Promise.jsm", {});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* WebappRT-specific actors.
|
* WebappRT-specific actors.
|
||||||
|
@ -90,7 +90,7 @@ WebappTabList.prototype.getList = function() {
|
||||||
this._mustNotify = true;
|
this._mustNotify = true;
|
||||||
this._checkListening();
|
this._checkListening();
|
||||||
|
|
||||||
return promise.resolve([actor for ([_, actor] of this._actorByBrowser)]);
|
return Promise.resolve([actor for ([_, actor] of this._actorByBrowser)]);
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
Загрузка…
Ссылка в новой задаче