merge fx-team into m-c

This commit is contained in:
Gavin Sharp 2014-01-31 22:37:45 -08:00
commit 5f352a3725
54 changed files with 546 additions and 312 deletions

View File

@ -1343,6 +1343,10 @@ pref("browser.uiCustomization.debug", false);
// be fetched. Must use HTTPS.
pref("identity.fxaccounts.remote.uri", "https://accounts.firefox.com/?service=sync&context=fx_desktop_v1");
// The URL where remote content that forces re-authentication for Firefox Accounts
// should be fetched. Must use HTTPS.
pref("identity.fxaccounts.remote.force_auth.uri", "https://accounts.firefox.com/force_auth?service=sync&context=fx_desktop_v1");
// The URL we take the user to when they opt to "manage" their Firefox Account.
// Note that this will always need to be in the same TLD as the
// "identity.fxaccounts.remote.uri" pref.

View File

@ -58,10 +58,10 @@ function sha256(str) {
function promptForRelink(acctName) {
let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
let continueLabel = sb.GetStringFromName("continue.label");
let title = sb.GetStringFromName("relink.verify.title");
let description = sb.formatStringFromName("relink.verify.description",
let title = sb.GetStringFromName("relinkVerify.title");
let description = sb.formatStringFromName("relinkVerify.description",
[acctName], 1);
let body = sb.GetStringFromName("relink.verify.heading") +
let body = sb.GetStringFromName("relinkVerify.heading") +
"\n\n" + description;
let ps = Services.prompt;
let buttonFlags = (ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING) +
@ -76,7 +76,7 @@ function promptForRelink(acctName) {
let wrapper = {
iframe: null,
init: function () {
init: function (url=null) {
let weave = Cc["@mozilla.org/weave/service;1"]
.getService(Ci.nsISupports)
.wrappedJSObject;
@ -92,7 +92,7 @@ let wrapper = {
iframe.addEventListener("load", this);
try {
iframe.src = fxAccounts.getAccountsURI();
iframe.src = url || fxAccounts.getAccountsURI();
} catch (e) {
error("Couldn't init Firefox Account wrapper: " + e.message);
}
@ -236,11 +236,14 @@ function openPrefs() {
}
function init() {
let signinQuery = window.location.href.match(/signin=true$/);
if (signinQuery) {
if (window.location.href.contains("action=signin")) {
show("remote");
wrapper.init();
} else if (window.location.href.contains("action=reauth")) {
fxAccounts.promiseAccountsForceSigninURI().then(url => {
show("remote");
wrapper.init(url);
});
} else {
// Check if we have a local account
fxAccounts.getSignedInUser().then(user => {

View File

@ -15,7 +15,7 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>&aboutAccounts.pageTitle;</title>
<title>&syncBrand.fullName.label;</title>
<meta name="viewport" content="width=device-width"/>
@ -39,23 +39,23 @@
<div id="manage">
<header>
<h1>&syncBrand.fxa-plural.label;</h1>
<h2>&syncBrand.shortName.label;</h2>
<h1><span>&syncBrand.fullName.label;</span></h1>
<h2><span>&aboutAccounts.welcome;</span></h2>
</header>
<section>
<div class="graphic graphic-sync-intro"> </div>
<div class="button-row">
<a class="button" href="#" onclick="openPrefs()">Manage</a>
<a class="button" href="#" onclick="openPrefs()">&aboutAccountsConfig.manage.label;</a>
</div>
</section>
</div>
<div id="intro">
<header>
<h1>&aboutAccounts.pageTitle;</h1>
<h1><span>&syncBrand.fullName.label;</span></h1>
<h2><span>&aboutAccounts.welcome;</span></h2>
</header>
<section>

View File

@ -4,9 +4,7 @@
font-weight: 400;
src: local('Fira Sans'),
local('FiraSans'),
url('fonts/firasans-regular.woff') format('woff'),
/*url('/fonts/latin/firasans-regular.ttf') format('truetype'),*/
/*url('/fonts/latin/firasans-regular.svg#Fira Sans') format('svg');*/
url('fonts/firasans-regular.woff') format('woff');
}
@font-face {
font-family: 'Fira Sans';
@ -14,9 +12,7 @@
font-weight: 300;
src: local('Fira Sans Light'),
local('FiraSansLight'),
url('fonts/firasans-light.woff') format('woff'),
/*url('/fonts/latin/firasans-light.ttf') format('truetype'),*/
/*url('/fonts/latin/firasans-light.svg#Fira Sans') format('svg');*/
url('fonts/firasans-light.woff') format('woff');
}
@font-face {
font-family: 'Clear Sans';
@ -24,8 +20,6 @@
font-weight: 400;
src: local('Clear Sans'),
local('ClearSans'),
url('fonts/clearsans-regular.woff') format('woff'),
/*url('/fonts/latin/clearsans-regular.ttf') format('truetype'),*/
/*url('/fonts/latin/clearsans-regular.svg#Clear Sans') format('svg');*/
url('fonts/clearsans-regular.woff') format('woff');
}

View File

@ -100,7 +100,6 @@ let CustomizationHandler = {
if (gURLBar) {
URLBarSetURI();
XULBrowserWindow.asyncUpdateUI();
BookmarkingUI.updateStarState();
}
// Re-enable parts of the UI we disabled during the dialog

View File

@ -186,11 +186,11 @@ let gFxAccounts = {
});
},
toggle: function (event) {
onMenuPanelCommand: function (event) {
if (event.originalTarget.hasAttribute("signedin")) {
this.openPreferences();
} else {
this.openSignInPage();
this.openAccountsPage();
}
PanelUI.hide();
@ -200,7 +200,12 @@ let gFxAccounts = {
openPreferences("paneSync");
},
openSignInPage: function () {
openAccountsPage: function () {
switchToTabHavingURI("about:accounts", true);
},
openSignInAgainPage: function () {
// FIXME: This should actually show the pre-filled username version of about:accounts?
switchToTabHavingURI("about:accounts?signin=true", true);
}
};

View File

@ -362,7 +362,7 @@
openInTabs="children"
oncommand="BookmarksEventHandler.onCommand(event, this.parentNode._placesView);"
onclick="BookmarksEventHandler.onClick(event, this.parentNode._placesView);"
onpopupshowing="PlacesCommandHook.updateBookmarkAllTabsCommand();
onpopupshowing="BookmarkingUI.onMainMenuPopupShowing(event);
if (!this.parentNode._placesView)
new PlacesMenu(event, 'place:folder=BOOKMARKS_MENU');"
tooltip="bhTooltip" popupsinherittooltip="true">
@ -372,8 +372,8 @@
key="manBookmarkKb"/>
<menuseparator id="organizeBookmarksSeparator"/>
<menuitem id="menu_bookmarkThisPage"
label="&bookmarkThisPageCmd.label;"
command="Browser:AddBookmarkAs"
observes="bookmarkThisPageBroadcaster"
key="addBookmarkAsKb"/>
<menuitem id="subscribeToPageMenuitem"
#ifndef XP_MACOSX

View File

@ -959,39 +959,48 @@ let PlacesToolbarHelper = {
let BookmarkingUI = {
get button() {
delete this.button;
let widgetGroup = CustomizableUI.getWidget("bookmarks-menu-button");
if (widgetGroup.areaType == CustomizableUI.TYPE_TOOLBAR) {
return widgetGroup.forWindow(window).node;
}
return null;
return this.button = widgetGroup.forWindow(window).node;
},
/* Can't make this a self-deleting getter because it's anonymous content
* and might lose/regain bindings at some point. */
get star() {
let button = this.button;
return button && document.getAnonymousElementByAttribute(button, "anonid",
"button");
return document.getAnonymousElementByAttribute(this.button, "anonid",
"button");
},
get anchor() {
if (!this._shouldUpdateStarState()) {
return null;
}
let widget = CustomizableUI.getWidget("bookmarks-menu-button")
.forWindow(window);
if (widget.overflowed)
return widget.anchor;
let star = this.star;
return star && document.getAnonymousElementByAttribute(star, "class",
"toolbarbutton-icon");
return document.getAnonymousElementByAttribute(this.star, "class",
"toolbarbutton-icon");
},
get broadcaster() {
delete this.broadcaster;
let broadcaster = document.getElementById("bookmarkThisPageBroadcaster");
return this.broadcaster = broadcaster;
},
STATUS_UPDATING: -1,
STATUS_UNSTARRED: 0,
STATUS_STARRED: 1,
get status() {
if (!this._shouldUpdateStarState()) {
return this.STATUS_UNSTARRED;
}
if (this._pendingStmt)
return this.STATUS_UPDATING;
let button = this.button;
return button && button.hasAttribute("starred") ? this.STATUS_STARRED
: this.STATUS_UNSTARRED;
return this.button.hasAttribute("starred") ? this.STATUS_STARRED
: this.STATUS_UNSTARRED;
},
get _starredTooltip()
@ -1008,6 +1017,15 @@ let BookmarkingUI = {
gNavigatorBundle.getString("starButtonOff.tooltip");
},
/**
* The type of the area in which the button is currently located.
* When in the panel, we don't update the button's icon.
*/
_currentAreaType: null,
_shouldUpdateStarState: function() {
return this._currentAreaType == CustomizableUI.TYPE_TOOLBAR;
},
/**
* The popup contents must be updated when the user customizes the UI, or
* changes the personal toolbar collapsed status. In such a case, any needed
@ -1069,7 +1087,7 @@ let BookmarkingUI = {
* Handles star styling based on page proxy state changes.
*/
onPageProxyStateChanged: function BUI_onPageProxyStateChanged(aState) {
if (!this.star) {
if (!this._shouldUpdateStarState()) {
return;
}
@ -1080,26 +1098,31 @@ let BookmarkingUI = {
}
else {
this.star.removeAttribute("disabled");
this._updateStar();
}
this._updateToolbarStyle();
},
_updateToolbarStyle: function BUI__updateToolbarStyle() {
let button = this.button;
if (!button)
return;
_updateCustomizationState: function BUI__updateCustomizationState() {
let placement = CustomizableUI.getPlacementOfWidget("bookmarks-menu-button");
this._currentAreaType = placement && CustomizableUI.getAreaType(placement.area);
},
let personalToolbar = document.getElementById("PersonalToolbar");
let onPersonalToolbar = button.parentNode == personalToolbar ||
button.parentNode.parentNode == personalToolbar;
_updateToolbarStyle: function BUI__updateToolbarStyle() {
let onPersonalToolbar = false;
if (this._currentAreaType == CustomizableUI.TYPE_TOOLBAR) {
let personalToolbar = document.getElementById("PersonalToolbar");
onPersonalToolbar = this.button.parentNode == personalToolbar ||
this.button.parentNode.parentNode == personalToolbar;
}
if (onPersonalToolbar) {
button.classList.add("bookmark-item");
button.classList.remove("toolbarbutton-1");
this.button.classList.add("bookmark-item");
this.button.classList.remove("toolbarbutton-1");
}
else {
button.classList.remove("bookmark-item");
button.classList.add("toolbarbutton-1");
this.button.classList.remove("bookmark-item");
this.button.classList.add("toolbarbutton-1");
}
},
@ -1107,9 +1130,8 @@ let BookmarkingUI = {
// When an element with a placesView attached is removed and re-inserted,
// XBL reapplies the binding causing any kind of issues and possible leaks,
// so kill current view and let popupshowing generate a new one.
let button = this.button;
if (button && button._placesView)
button._placesView.uninit();
if (this.button._placesView)
this.button._placesView.uninit();
},
customizeStart: function BUI_customizeStart() {
@ -1117,6 +1139,11 @@ let BookmarkingUI = {
},
customizeChange: function BUI_customizeChange() {
let usedToUpdateStarState = this._shouldUpdateStarState();
this._updateCustomizationState();
if (usedToUpdateStarState != this._shouldUpdateStarState()) {
this.updateStarState();
}
this._updateToolbarStyle();
},
@ -1127,10 +1154,12 @@ let BookmarkingUI = {
init: function() {
CustomizableUI.addListener(this);
this._updateCustomizationState();
},
_hasBookmarksObserver: false,
uninit: function BUI_uninit() {
this._updateBookmarkPageMenuItem(true);
CustomizableUI.removeListener(this);
this._uninitView();
@ -1146,7 +1175,7 @@ let BookmarkingUI = {
},
updateStarState: function BUI_updateStarState() {
if (!this.button || (this._uri && gBrowser.currentURI.equals(this._uri))) {
if (this._uri && gBrowser.currentURI.equals(this._uri)) {
return;
}
@ -1195,38 +1224,57 @@ let BookmarkingUI = {
},
_updateStar: function BUI__updateStar() {
let button = this.button;
if (!button)
if (!this._shouldUpdateStarState()) {
if (this.button.hasAttribute("starred")) {
this.button.removeAttribute("starred");
this.button.removeAttribute("buttontooltiptext");
}
return;
}
if (this._itemIds.length > 0) {
button.setAttribute("starred", "true");
button.setAttribute("buttontooltiptext", this._starredTooltip);
if (this._itemIds && this._itemIds.length > 0) {
this.button.setAttribute("starred", "true");
this.button.setAttribute("buttontooltiptext", this._starredTooltip);
}
else {
button.removeAttribute("starred");
button.setAttribute("buttontooltiptext", this._unstarredTooltip);
this.button.removeAttribute("starred");
this.button.setAttribute("buttontooltiptext", this._unstarredTooltip);
}
},
/**
* forceReset is passed when we're destroyed and the label should go back
* to the default (Bookmark This Page) for OS X.
*/
_updateBookmarkPageMenuItem: function BUI__updateBookmarkPageMenuItem(forceReset) {
let isStarred = !forceReset && this._itemIds && this._itemIds.length > 0;
let label = isStarred ? "editlabel" : "bookmarklabel";
this.broadcaster.setAttribute("label", this.broadcaster.getAttribute(label));
},
onMainMenuPopupShowing: function BUI_onMainMenuPopupShowing(event) {
this._updateBookmarkPageMenuItem();
PlacesCommandHook.updateBookmarkAllTabsCommand();
},
onCommand: function BUI_onCommand(aEvent) {
if (aEvent.target != aEvent.currentTarget) {
return;
}
// Handle special case when the button is in the panel.
let widgetGroup = CustomizableUI.getWidget("bookmarks-menu-button");
let widget = widgetGroup.forWindow(window);
if (widgetGroup.areaType == CustomizableUI.TYPE_MENU_PANEL) {
let widget = CustomizableUI.getWidget("bookmarks-menu-button")
.forWindow(window);
if (this._currentAreaType == CustomizableUI.TYPE_MENU_PANEL) {
let view = document.getElementById("PanelUI-bookmarks");
view.addEventListener("ViewShowing", this.onPanelMenuViewShowing);
view.addEventListener("ViewHiding", this.onPanelMenuViewHiding);
view.addEventListener("ViewShowing", this);
view.addEventListener("ViewHiding", this);
widget.node.setAttribute("closemenu", "none");
PanelUI.showSubView("PanelUI-bookmarks", widget.node,
CustomizableUI.AREA_PANEL);
return;
}
else if (widget.overflowed) {
if (widget.overflowed) {
// Allow to close the panel if the page is already bookmarked, cause
// we are going to open the edit bookmark panel.
if (this._itemIds.length > 0)
@ -1241,7 +1289,19 @@ let BookmarkingUI = {
}
},
handleEvent: function BUI_handleEvent(aEvent) {
switch (aEvent.type) {
case "ViewShowing":
this.onPanelMenuViewShowing(aEvent);
break;
case "ViewHiding":
this.onPanelMenuViewHiding(aEvent);
break;
}
},
onPanelMenuViewShowing: function BUI_onViewShowing(aEvent) {
this._updateBookmarkPageMenuItem();
// Update checked status of the toolbar toggle.
let viewToolbar = document.getElementById("panelMenu_viewBookmarksToolbar");
let personalToolbar = document.getElementById("PersonalToolbar");
@ -1257,11 +1317,13 @@ let BookmarkingUI = {
mainLevel: "subviewbutton"
}
});
aEvent.target.removeEventListener("ViewShowing", this);
},
onPanelMenuViewHiding: function BUI_onViewHiding(aEvent) {
this._panelMenuView.uninit();
delete this._panelMenuView;
aEvent.target.removeEventListener("ViewHiding", this);
},
onPanelMenuViewCommand: function BUI_onPanelMenuViewCommand(aEvent, aView) {
@ -1278,50 +1340,50 @@ let BookmarkingUI = {
// nsINavBookmarkObserver
onItemAdded: function BUI_onItemAdded(aItemId, aParentId, aIndex, aItemType,
aURI) {
if (!this.button) {
return;
}
if (aURI && aURI.equals(this._uri)) {
// If a new bookmark has been added to the tracked uri, register it.
if (this._itemIds.indexOf(aItemId) == -1) {
this._itemIds.push(aItemId);
this._updateStar();
// Only need to update the UI if it wasn't marked as starred before:
if (this._itemIds.length == 1) {
this._updateStar();
}
}
}
},
onItemRemoved: function BUI_onItemRemoved(aItemId) {
if (!this.button) {
return;
}
let index = this._itemIds.indexOf(aItemId);
// If one of the tracked bookmarks has been removed, unregister it.
if (index != -1) {
this._itemIds.splice(index, 1);
this._updateStar();
// Only need to update the UI if the page is no longer starred
if (this._itemIds.length == 0) {
this._updateStar();
}
}
},
onItemChanged: function BUI_onItemChanged(aItemId, aProperty,
aIsAnnotationProperty, aNewValue) {
if (!this.button) {
return;
}
if (aProperty == "uri") {
let index = this._itemIds.indexOf(aItemId);
// If the changed bookmark was tracked, check if it is now pointing to
// a different uri and unregister it.
if (index != -1 && aNewValue != this._uri.spec) {
this._itemIds.splice(index, 1);
this._updateStar();
// Only need to update the UI if the page is no longer starred
if (this._itemIds.length == 0) {
this._updateStar();
}
}
// If another bookmark is now pointing to the tracked uri, register it.
else if (index == -1 && aNewValue == this._uri.spec) {
this._itemIds.push(aItemId);
this._updateStar();
// Only need to update the UI if it wasn't marked as starred before:
if (this._itemIds.length == 1) {
this._updateStar();
}
}
}
},
@ -1334,23 +1396,30 @@ let BookmarkingUI = {
// CustomizableUI events:
_starButtonLabel: null,
_starButtonOverflowedLabel: null,
get _starButtonOverflowedLabel() {
delete this._starButtonOverflowedLabel;
this._starButtonOverflowedLabel =
gNavigatorBundle.getString("starButtonOverflowed.label");
},
get _starButtonOverflowedStarredLabel() {
delete this._starButtonOverflowedStarredLabel;
this._starButtonOverflowedStarredLabel =
gNavigatorBundle.getString("starButtonOverflowedStarred.label");
},
onWidgetOverflow: function(aNode, aContainer) {
let win = aNode.ownerDocument.defaultView;
if (aNode.id != "bookmarks-menu-button" || win != window)
return;
if (!this._starButtonOverflowedLabel) {
this._starButtonOverflowedLabel = gNavigatorBundle.getString(
"starButtonOverflowed.label");
}
let currentLabel = aNode.getAttribute("label");
if (!this._starButtonLabel)
this._starButtonLabel = currentLabel;
if (currentLabel == this._starButtonLabel)
aNode.setAttribute("label", this._starButtonOverflowedLabel);
if (currentLabel == this._starButtonLabel) {
let desiredLabel = this._itemIds.length > 0 ? this._starButtonOverflowedStarredLabel
: this._starButtonOverflowedLabel;
aNode.setAttribute("label", desiredLabel);
}
},
onWidgetUnderflow: function(aNode, aContainer) {
@ -1360,10 +1429,10 @@ let BookmarkingUI = {
// If the button hasn't been in the overflow panel before, we may ignore
// this event.
if (!this._starButtonOverflowedLabel || !this._starButtonLabel)
if (!this._starButtonLabel)
return;
if (aNode.getAttribute("label") == this._starButtonOverflowedLabel)
if (aNode.getAttribute("label") != this._starButtonLabel)
aNode.setAttribute("label", this._starButtonLabel);
},

View File

@ -140,6 +140,11 @@
type="checkbox" group="sidebar" sidebarurl="chrome://browser/content/web-panels.xul"
oncommand="toggleSidebar('viewWebPanelsSidebar');"/>
<broadcaster id="bookmarkThisPageBroadcaster"
label="&bookmarkThisPageCmd.label;"
bookmarklabel="&bookmarkThisPageCmd.label;"
editlabel="&editThisBookmarkCmd.label;"/>
<!-- popup blocking menu items -->
<broadcaster id="blockedPopupAllowSite"
accesskey="&allowPopups.accesskey;"

View File

@ -389,12 +389,13 @@
<!-- Sync Panel -->
<panel id="sync-start-panel" class="sync-panel" type="arrow" hidden="true"
noautofocus="true" level="top" onclick="this.hidePopup();">
noautofocus="true" level="top" onclick="this.hidePopup();"
flip="slide">
<hbox class="sync-panel-outer">
<image class="sync-panel-icon"/>
<vbox class="sync-panel-inner">
<description id="sync-start-panel-title"
value="&syncStartPanel.title;"/>
value="&syncStartPanel.heading;"/>
<description id="sync-start-panel-subtitle">
#ifdef XP_UNIX
&syncStartPanel.subTitleUnix;
@ -408,29 +409,21 @@
<!-- Sync Error Panel -->
<panel id="sync-error-panel" class="sync-panel" type="arrow" hidden="true"
noautofocus="true" level="top" onclick="this.hidePopup();">
noautofocus="true" level="top" onclick="this.hidePopup();"
flip="slide">
<hbox class="sync-panel-outer">
<image class="sync-panel-icon"/>
<vbox class="sync-panel-inner">
<description id="sync-error-panel-title"
value="&syncErrorPanel.title;"/>
value="&syncErrorPanel.heading;"/>
<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();"/>
onclick="gFxAccounts.openSignInAgainPage();"/>
</hbox>
</vbox>
</hbox>

View File

@ -32,9 +32,9 @@
<label id="sync-customize-title" value="&syncCustomize.title;"/>
<description id="sync-customize-subtitle"
#ifdef XP_UNIX
value="&syncCustomize.subTitleUnix;"
value="&syncCustomizeUnix.description;"
#else
value="&syncCustomize.subTitle;"
value="&syncCustomize.description;"
#endif
/>

View File

@ -11,7 +11,6 @@ XPCOMUtils.defineLazyModuleGetter(this, "fxAccounts",
registerCleanupFunction(function() {
// Ensure we don't pollute prefs for next tests.
Services.prefs.clearUserPref("identity.fxaccounts.enabled");
Services.prefs.clearUserPref("identity.fxaccounts.remote.uri");
});
@ -21,7 +20,6 @@ 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");
},

View File

@ -50,15 +50,18 @@ browser.jar:
content/browser/abouthealthreport/abouthealth.js (content/abouthealthreport/abouthealth.js)
content/browser/abouthealthreport/abouthealth.css (content/abouthealthreport/abouthealth.css)
#endif
content/browser/aboutaccounts/aboutaccounts.xhtml (content/aboutaccounts/aboutaccounts.xhtml)
content/browser/aboutaccounts/aboutaccounts.js (content/aboutaccounts/aboutaccounts.js)
content/browser/aboutaccounts/aboutaccounts.css (content/aboutaccounts/aboutaccounts.css)
content/browser/aboutaccounts/main.css (content/aboutaccounts/main.css)
content/browser/aboutaccounts/normalize.css (content/aboutaccounts/normalize.css)
content/browser/aboutaccounts/fonts.css (content/aboutaccounts/fonts.css)
content/browser/aboutaccounts/images/fox.png (content/aboutaccounts/images/fox.png)
content/browser/aboutaccounts/images/graphic_sync_intro.png (content/aboutaccounts/images/graphic_sync_intro.png)
content/browser/aboutaccounts/images/graphic_sync_intro@2x.png (content/aboutaccounts/images/graphic_sync_intro@2x.png)
content/browser/aboutaccounts/aboutaccounts.xhtml (content/aboutaccounts/aboutaccounts.xhtml)
content/browser/aboutaccounts/aboutaccounts.js (content/aboutaccounts/aboutaccounts.js)
content/browser/aboutaccounts/aboutaccounts.css (content/aboutaccounts/aboutaccounts.css)
content/browser/aboutaccounts/main.css (content/aboutaccounts/main.css)
content/browser/aboutaccounts/normalize.css (content/aboutaccounts/normalize.css)
content/browser/aboutaccounts/fonts.css (content/aboutaccounts/fonts.css)
content/browser/aboutaccounts/fonts/clearsans-regular.woff (content/aboutaccounts/fonts/clearsans-regular.woff)
content/browser/aboutaccounts/fonts/firasans-light.woff (content/aboutaccounts/fonts/firasans-light.woff)
content/browser/aboutaccounts/fonts/firasans-regular.woff (content/aboutaccounts/fonts/firasans-regular.woff)
content/browser/aboutaccounts/images/fox.png (content/aboutaccounts/images/fox.png)
content/browser/aboutaccounts/images/graphic_sync_intro.png (content/aboutaccounts/images/graphic_sync_intro.png)
content/browser/aboutaccounts/images/graphic_sync_intro@2x.png (content/aboutaccounts/images/graphic_sync_intro@2x.png)
content/browser/aboutRobots-icon.png (content/aboutRobots-icon.png)
content/browser/aboutRobots-widget-left.png (content/aboutRobots-widget-left.png)

View File

@ -18,7 +18,7 @@
<toolbarbutton id="PanelUI-fxa-status"
defaultlabel="&fxaSignIn.label;"
errorlabel="&fxaSignInError.label;"
oncommand="gFxAccounts.toggle(event);"
oncommand="gFxAccounts.onMenuPanelCommand(event);"
hidden="true"/>
<hbox id="PanelUI-footer-inner">
@ -88,8 +88,8 @@
<panelview id="PanelUI-bookmarks" flex="1" class="PanelUI-subView">
<label value="&bookmarksMenu.label;" class="panel-subview-header"/>
<toolbarbutton id="panelMenuBookmarkThisPage"
label="&bookmarkThisPageCmd.label;"
class="subviewbutton"
observes="bookmarkThisPageBroadcaster"
command="Browser:AddBookmarkAs"
onclick="PanelUI.hide();"/>
<toolbarseparator/>

View File

@ -256,8 +256,12 @@ let gSyncPane = {
window.close();
},
signIn: function() {
this.openContentInBrowser("about:accounts?action=signin");
},
reSignIn: function() {
this.openContentInBrowser("about:accounts");
this.openContentInBrowser("about:accounts?action=reauth");
},
manageFirefoxAccount: function() {
@ -290,17 +294,19 @@ let gSyncPane = {
// We use a string bundle shared with aboutAccounts.
let sb = Services.strings.createBundle("chrome://browser/locale/syncSetup.properties");
let continueLabel = sb.GetStringFromName("continue.label");
let title = sb.GetStringFromName("unlink.verify.title");
let body = sb.GetStringFromName("unlink.verify.heading") +
let title = sb.GetStringFromName("disconnect.verify.title");
let brandBundle = Services.strings.createBundle("chrome://branding/locale/brand.properties");
let brandShortName = brandBundle.GetStringFromName("brandShortName");
let body = sb.GetStringFromName("disconnect.verify.heading") +
"\n\n" +
sb.GetStringFromName("unlink.verify.description");
sb.formatStringFromName("disconnect.verify.description",
[brandShortName], 1);
let ps = Services.prompt;
let buttonFlags = (ps.BUTTON_POS_0 * ps.BUTTON_TITLE_IS_STRING) +
(ps.BUTTON_POS_1 * ps.BUTTON_TITLE_CANCEL) +
ps.BUTTON_POS_1_DEFAULT;
let pressed = Services.prompt.confirmEx(window, title, body, buttonFlags,
continueLabel, null, null, null,
{});
continueLabel, null, null, null, {});
if (pressed != 0) { // 0 is the "continue" button
return;
}

View File

@ -188,8 +188,11 @@
<vbox id="noFxaAccount">
<description>&welcome.description;</description>
<label class="text-link"
onclick="gSyncPane.openContentInBrowser('about:accounts?signin=true'); return false;"
value="&welcome.startButton.label;"/>
onclick="gSyncPane.signIn(); return false;"
value="&welcome.createAccount.label;"/>
<label class="text-link"
onclick="gSyncPane.signIn(); return false;"
value="&welcome.signIn.label;"/>
<spacer flex="1"/>
<label class="text-link"
onclick="gSyncPane.openOldSyncSupportPage(); return false;"
@ -199,7 +202,7 @@
<vbox id="hasFxaAccount">
<groupbox id="fxaGroup">
<caption label="&syncBrand.fxa-singular.label;"/>
<caption label="&syncBrand.fxAccount.label;"/>
<deck id="fxaLoginStatus">
@ -212,7 +215,7 @@
<spacer flex="1"/>
<vbox align="end">
<button onclick="gSyncPane.unlinkFirefoxAccount(true);"
label="&unlink.label;" />
label="&disconnect.label;" />
</vbox>
</hbox>

View File

@ -43,6 +43,22 @@ function setupAutoCompletion(ctx, walker) {
return;
}
return win.CodeMirror.Pass;
},
"Up": cm => {
if (popup && popup.isOpen) {
cycleSuggestions(ed, true);
return;
}
return win.CodeMirror.Pass;
},
"Down": cm => {
if (popup && popup.isOpen) {
cycleSuggestions(ed);
return;
}
return win.CodeMirror.Pass;
},
};
@ -147,8 +163,9 @@ function cycleSuggestions(ed, reverse) {
function onEditorKeypress(ed, event) {
let private = privates.get(ed);
switch (event.keyCode) {
case event.DOM_VK_UP:
case event.DOM_VK_DOWN:
case event.DOM_VK_ESCAPE:
if (private.popup.isOpen)
event.preventDefault();
case event.DOM_VK_LEFT:
case event.DOM_VK_RIGHT:
case event.DOM_VK_HOME:
@ -157,7 +174,6 @@ function onEditorKeypress(ed, event) {
case event.DOM_VK_DELETE:
case event.DOM_VK_ENTER:
case event.DOM_VK_RETURN:
case event.DOM_VK_ESCAPE:
private.doNotAutocomplete = true;
private.popup.hidePopup();
break;

View File

@ -22,6 +22,8 @@ let TEST_CASES = [
['VK_RIGHT', -1],
['VK_RIGHT', -1],
[-1, 1, 0],
['VK_LEFT', -1],
['VK_RIGHT', -1],
['VK_DOWN', -1],
['VK_RIGHT', -1],
['VK_RIGHT', -1],
@ -31,14 +33,18 @@ let TEST_CASES = [
['VK_RETURN', -1],
['b', MAX_SUGGESTIONS, 0],
['a', 11, 0],
['VK_TAB', 11, 0, 1],
['VK_DOWN', 11, 0, 1],
['VK_TAB', 11, 1, 1],
[':', -1],
['b', 9, 0],
['l', 4, 0],
['VK_TAB', 4, 0, 1],
['VK_DOWN', 4, 1, 1],
['VK_UP', 4, 0, 1],
['VK_TAB', 4, 1, 1],
['VK_TAB', 4, 2, 1],
['VK_LEFT', -1],
['VK_RIGHT', -1],
['VK_DOWN', -1],
['VK_RETURN', -1],
['b', 2, 0],
@ -102,12 +108,13 @@ function testState() {
key = " ";
mods.accelKey = true;
}
else if (/(down|left|right|return|home|end)/ig.test(key)) {
else if (/(left|right|return|home|end)/ig.test(key) ||
(key == "VK_DOWN" && !gPopup.isOpen)) {
info("pressing key " + key + " to get result: [" + TEST_CASES[index] +
"] for index " + index);
gEditor.once("cursorActivity", checkState);
}
else if (key == "VK_TAB") {
else if (key == "VK_TAB" || key == "VK_UP" || key == "VK_DOWN") {
info("pressing key " + key + " to get result: [" + TEST_CASES[index] +
"] for index " + index);
gEditor.once("suggestion-entered", checkState);
@ -139,8 +146,7 @@ function checkState() {
}
}
else {
ok(gPopup._panel.state != "open" && gPopup._panel.state != "showing",
"Popup is closed for index " + index);
ok(!gPopup.isOpen, "Popup is closed for index " + index);
}
index++;
testState();

View File

@ -2,8 +2,9 @@
- 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 aboutAccounts.pageTitle "Welcome to Sync">
<!ENTITY aboutAccounts.welcome "Welcome to &syncBrand.shortName.label;">
<!ENTITY aboutAccountsConfig.description "Sign in to sync your tabs, bookmarks, passwords &amp; more.">
<!ENTITY aboutAccountsConfig.startButton.label "Get started">
<!ENTITY aboutAccountsConfig.useOldSync.label "Using an older version of Sync?">
<!ENTITY aboutAccountsConfig.manage.label "Manage">

View File

@ -98,15 +98,11 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY fxaSignIn.label "Sign in to &syncBrand.shortName.label;">
<!ENTITY fxaSignInError.label "Reconnect to &syncBrand.shortName.label;">
<!ENTITY syncStartPanel.title "&brandShortName; is now syncing.">
<!ENTITY syncStartPanel.heading "&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.heading "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">
@ -131,6 +127,7 @@ These should match what Safari and other Apple applications use on OS X Lion. --
<!ENTITY bookmarksMenu.label "Bookmarks">
<!ENTITY bookmarksMenu.accesskey "B">
<!ENTITY bookmarkThisPageCmd.label "Bookmark This Page">
<!ENTITY editThisBookmarkCmd.label "Edit This Bookmark">
<!ENTITY bookmarkThisPageCmd.commandkey "d">
<!ENTITY markPageCmd.commandkey "l">
<!ENTITY sharePageCmd.label "Share This Page">

View File

@ -240,6 +240,7 @@ bookmarksMenuButton.tooltip=Show your bookmarks (%S)
starButtonOn.tooltip=Edit this bookmark
starButtonOff.tooltip=Bookmark this page
starButtonOverflowed.label=Bookmark This Page
starButtonOverflowedStarred.label=Edit This Bookmark
# Print button tooltip on OS X
# LOCALIZATION NOTE (printButton.tooltip):

View File

@ -140,4 +140,4 @@ shouldRestartTitle=Restart %S
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.
firefoxAccountVerificationSentDescription=Please check your email and click the link to begin syncing.

View File

@ -48,7 +48,7 @@
<!-- Firefox Accounts stuff -->
<!ENTITY fxaPrivacyNotice.link.label "Privacy Notice">
<!ENTITY determiningStatus.label "Determining your Firefox Account status…">
<!ENTITY determiningStatus.label "Determining your Firefox Account status…">
<!ENTITY signedInUnverified.beforename.label "">
<!ENTITY signedInUnverified.aftername.label "is not verified.">
@ -58,10 +58,12 @@
<!ENTITY notSignedIn.label "You are not signed in.">
<!ENTITY signIn.label "Sign in">
<!ENTITY manage.label "Manage">
<!ENTITY unlink.label "Unlink this Browser…">
<!ENTITY disconnect.label "Disconnect…">
<!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.signIn.label "Sign In">
<!ENTITY welcome.createAccount.label "Create Account">
<!ENTITY welcome.useOldSync.label "Using an older version of Sync?">

View File

@ -4,5 +4,4 @@
<!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">
<!ENTITY syncBrand.fxAccount.label "Firefox Account">

View File

@ -6,8 +6,8 @@
<!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.">
<!ENTITY syncCustomize.description "You can change this selection in Options.">
<!ENTITY syncCustomizeUnix.description "You can change this selection in Preferences.">
<!--
These engine names are the same as in browser/preferences/sync.dtd except

View File

@ -52,14 +52,15 @@ existingAccount.change.label = You can change this preference by selecting Sync
# Firefox Accounts based setup.
continue.label = Continue
unlink.verify.title = Unlink Browser
unlink.verify.heading = Are you sure?
unlink.verify.description = This browser will stop syncing with your other computers, but won't delete any of your local browsing data.
relink.verify.title = Merge Warning
relink.verify.heading = Are you sure you want to sign in to Sync?
# LOCALIZATION NOTE (relink.verify.description): Email address of a user previously signed into sync.
relink.verify.description = A different user was previously signed in to Sync on this device. Signing in will merge this browser's bookmarks, passwords and other settings with %S
disconnect.verify.title = Disconnect
disconnect.verify.heading = Are you sure?
# LOCALIZATION NOTE (disconnect.verify.description): %S will be replaced with
# brandShortName
disconnect.verify.description = %S will stop syncing with your account, but wont delete any of your browsing data on this computer.
relinkVerify.title = Merge Warning
relinkVerify.heading = Are you sure you want to sign in to Sync?
# LOCALIZATION NOTE (relinkVerify.description): Email address of a user previously signed into sync.
relinkVerify.description = A different user was previously signed in to Sync on this computer. Signing in will merge this browsers bookmarks, passwords and other settings with %S
manage.pageTitle = Manage Sync

View File

@ -20,9 +20,10 @@
<hbox id="start-container">
<box id="instruction-topsites-box" insertafter="start-topsites" class="firstrun">
<box id="instruction-topsites">
<vbox align="center">
<vbox class="instruction-content-container">
<image class="instruction-arrow arrow-left-straight" />
<image class="instruction-arrow arrow-left" />
<label class="instruction-label" value="&firstRunTopSites.label;"/>
<label class="instruction-label">&firstRunTopSites.label;</label>
</vbox>
</box>
</box>
@ -35,32 +36,32 @@
<box insertafter="start-bookmarks" class="firstrun">
<box id="instruction-bookmarks">
<vbox align="center">
<vbox class="instruction-content-container" align="center">
<image class="instruction-arrow arrow-right" />
<label class="instruction-label" value="&firstRunBookmarks.label;"/>
<label class="instruction-label">&firstRunBookmarks.label;</label>
</vbox>
</box>
</box>
<box id="instruction-history-container" insertafter="start-history" class="firstrun">
<box id="instruction-history">
<vbox align="center">
<vbox class="instruction-content-container" align="center">
<image class="instruction-arrow arrow-right" />
<label class="instruction-label" value="&firstRunHistory.label;"/>
<label class="instruction-label">&firstRunHistory.label;</label>
</vbox>
</box>
</box>
<box id="instruction-tabs" class="firstrun">
<vbox align="center">
<vbox class="instruction-content-container" align="center">
<image class="instruction-arrow arrow-top" />
<label class="instruction-label" value="&firstRunTabs.label;"/>
<label class="instruction-label">&firstRunTabs.label;</label>
</vbox>
</box>
<box id="instruction-menu" class="firstrun">
<hbox>
<label class="instruction-label" value="&firstRunMenu.label;"/>
<hbox class="instruction-content-container" align="right">
<label class="instruction-label">&firstRunMenu.label;</label>
<image class="instruction-arrow arrow-down" />
</hbox>
</box>

View File

@ -73,6 +73,10 @@ below instruction */
/* Instructions ---------------------- */
.instruction-content-container {
width: 380px;
}
.instruction-label {
font-size: 16px;
color: #808080;
@ -94,7 +98,9 @@ below instruction */
}
.instruction-arrow.arrow-left,
.instruction-arrow.arrow-right {
.instruction-arrow.arrow-right,
.instruction-arrow.arrow-left-straight,
.instruction-arrow.arrow-right-straight {
background-image: url("chrome://browser/skin/images/arrow-left.png");
}
@ -102,6 +108,14 @@ below instruction */
transform: rotate(180deg) scaleY(-1);
}
.instruction-arrow.arrow-left-straight {
transform: rotate(220deg) scaleX(-1);
}
.instruction-arrow.arrow-right-straight {
transform: rotate(-220deg);
}
#instruction-tabs {
position: absolute;
top: 10px;
@ -114,13 +128,35 @@ below instruction */
#start-container[viewstate="landscape"] #instruction-topsites {
position: absolute;
bottom: 20px;
transform: translateX(-50%);
margin-left: 20px;
bottom: 15%;
}
#start-container[viewstate="portrait"] #instruction-topsites {
transform: translateX(calc(150px + 50%)) translateY(-60px);
#start-container[viewstate="landscape"] #instruction-topsites .instruction-content-container {
-moz-box-align: start;
}
#start-container[viewstate="portrait"] #instruction-topsites-box {
margin-bottom: 20px;
}
#start-container[viewstate="portrait"] #instruction-topsites .instruction-content-container {
-moz-box-align: center;
}
#start-container[viewstate="portrait"] #instruction-topsites .arrow-left-straight {
display: none;
}
#start-container[viewstate="portrait"] #instruction-topsites .arrow-left {
display: block;
}
#start-container[viewstate="landscape"] #instruction-topsites .arrow-left-straight {
display: block;
}
#start-container[viewstate="landscape"] #instruction-topsites .arrow-left {
display: none;
}
#start-container[viewstate="landscape"] #instruction-history,
@ -131,7 +167,8 @@ below instruction */
}
#start-container[viewstate="portrait"] #instruction-history,
#start-container[viewstate="portrait"] #instruction-bookmarks {
#start-container[viewstate="portrait"] #instruction-bookmarks,
#start-container[viewstate="portrait"] #instruction-topsites {
transform: translateX(-55px);
}
@ -147,6 +184,10 @@ below instruction */
transform: translateY(40px);
}
#start-container #instruction-menu .instruction-label {
max-width: 300px;
}
#start-container[viewstate="landscape"] #start-history {
padding-left: 50px;
}

View File

@ -1682,10 +1682,6 @@ toolbarbutton.chevron > .toolbarbutton-icon {
/* Sync Panel */
.sync-panel {
padding: 10px;
}
.sync-panel-icon {
width: 32px;
background: url("chrome://browser/content/abouthome/sync.png") top left no-repeat;
@ -1700,7 +1696,8 @@ toolbarbutton.chevron > .toolbarbutton-icon {
margin-top: 1em;
}
#sync-error-panel-title {
#sync-error-panel-title,
#sync-start-panel-title {
font-weight: bold;
}

View File

@ -962,10 +962,6 @@ toolbar .toolbarbutton-1:not([type="menu-button"]),
-moz-image-region: rect(0px, 320px, 64px, 256px);
}
#bookmarks-menu-button[starred][cui-areatype="menu-panel"] {
-moz-image-region: rect(0px, 384px, 64px, 320px);
}
#history-panelmenu[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #history-panelmenu {
-moz-image-region: rect(0px, 448px, 64px, 384px);
@ -3601,10 +3597,6 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
/* Sync Panels */
.sync-panel {
padding: 10px;
}
.sync-panel-icon {
width: 32px;
background: url("chrome://browser/content/abouthome/sync.png") top left no-repeat;
@ -3641,7 +3633,8 @@ toolbarbutton.chevron > .toolbarbutton-menu-dropmarker {
@hudButtonFocused@
}
#sync-error-panel-title {
#sync-error-panel-title,
#sync-start-panel-title {
font-weight: bold;
}

View File

@ -15,10 +15,6 @@ toolbarpaletteitem[place="palette"] > #bookmarks-menu-button {
-moz-image-region: rect(0px, 160px, 32px, 128px);
}
#bookmarks-menu-button[starred][cui-areatype="menu-panel"] {
-moz-image-region: rect(0px, 192px, 32px, 160px);
}
#history-panelmenu[cui-areatype="menu-panel"],
toolbarpaletteitem[place="palette"] > #history-panelmenu {
-moz-image-region: rect(0px, 224px, 32px, 192px);

View File

@ -2144,10 +2144,6 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
/* Sync Panel */
.sync-panel {
padding: 10px;
}
.sync-panel-icon {
width: 32px;
background: url("chrome://browser/content/abouthome/sync.png") top left no-repeat;
@ -2162,7 +2158,8 @@ toolbarbutton.bookmark-item[dragover="true"][open="true"] {
margin-top: 1em;
}
#sync-error-panel-title {
#sync-error-panel-title,
#sync-start-panel-title {
font-weight: bold;
}

View File

@ -403,7 +403,7 @@ CSPRep.fromString = function(aStr, self, reportOnly, docRequest, csp) {
// relative to "self", but we should let devs know if the scheme is
// abnormal and may fail a POST.
if (!uri.schemeIs("http") && !uri.schemeIs("https")) {
cspWarn(aCSPR, CSPLocalizer.getFormatStr("reportURInotHttpsOrHttp",
cspWarn(aCSPR, CSPLocalizer.getFormatStr("reportURInotHttpsOrHttp2",
[uri.asciiSpec]));
}
} catch(e) {
@ -651,7 +651,7 @@ CSPRep.fromStringSpecCompliant = function(aStr, self, reportOnly, docRequest, cs
// relative to "self", but we should let devs know if the scheme is
// abnormal and may fail a POST.
if (!uri.schemeIs("http") && !uri.schemeIs("https")) {
cspWarn(aCSPR, CSPLocalizer.getFormatStr("reportURInotHttpsOrHttp",
cspWarn(aCSPR, CSPLocalizer.getFormatStr("reportURInotHttpsOrHttp2",
[uri.asciiSpec]));
}
} catch(e) {

View File

@ -25,9 +25,9 @@ couldNotProcessUnknownDirective = Couldn't process unknown directive '%1$S'
# LOCALIZATION NOTE (ignoringUnknownOption):
# %1$S is the option that could not be understood
ignoringUnknownOption = Ignoring unknown option %1$S
# LOCALIZATION NOTE (reportURInotHttpsOrHttp):
# LOCALIZATION NOTE (reportURInotHttpsOrHttp2):
# %1$S is the ETLD of the report URI that is not HTTP or HTTPS
reportURInotHttpsOrHttp = The report URI (%1$) should be an HTTP or HTTPS URI.
reportURInotHttpsOrHttp2 = The report URI (%1$S) should be an HTTP or HTTPS URI.
# LOCALIZATION NOTE (pageCannotSendReportsTo):
# %1$S is the URI of the page with the policy
# %2$S is the report URI that could not be used

View File

@ -2482,7 +2482,11 @@ abstract public class BrowserApp extends GeckoApp
// HomePager.OnUrlOpenListener
@Override
public void onUrlOpen(String url, EnumSet<OnUrlOpenListener.Flags> flags) {
if (!maybeSwitchToTab(url, flags)) {
if (flags.contains(OnUrlOpenListener.Flags.OPEN_WITH_INTENT)) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse(url));
startActivity(intent);
} else if (!maybeSwitchToTab(url, flags)) {
openUrlAndStopEditing(url);
}
}

View File

@ -106,7 +106,7 @@ public class DynamicPanel extends HomeFragment {
switch(mPanelConfig.getLayoutType()) {
case FRAME:
final PanelDatasetHandler datasetHandler = new PanelDatasetHandler();
mLayout = new FramePanelLayout(getActivity(), mPanelConfig, datasetHandler);
mLayout = new FramePanelLayout(getActivity(), mPanelConfig, datasetHandler, mUrlOpenListener);
break;
default:

View File

@ -5,6 +5,7 @@
package org.mozilla.gecko.home;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import org.mozilla.gecko.home.HomeConfig.PanelConfig;
import org.mozilla.gecko.home.HomeConfig.ViewConfig;
@ -18,8 +19,8 @@ class FramePanelLayout extends PanelLayout {
private final View mChildView;
private final ViewConfig mChildConfig;
public FramePanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler) {
super(context, panelConfig, datasetHandler);
public FramePanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler, OnUrlOpenListener urlOpenListener) {
super(context, panelConfig, datasetHandler, urlOpenListener);
// This layout can only hold one view so we simply
// take the first defined view from PanelConfig.

View File

@ -30,7 +30,7 @@ public class HomeListView extends ListView
private HomeContextMenuInfo mContextMenuInfo;
// On URL open listener
private OnUrlOpenListener mUrlOpenListener;
protected OnUrlOpenListener mUrlOpenListener;
// Top divider
private boolean mShowTopDivider;

View File

@ -68,7 +68,8 @@ public class HomePager extends ViewPager {
public interface OnUrlOpenListener {
public enum Flags {
ALLOW_SWITCH_TO_TAB
ALLOW_SWITCH_TO_TAB,
OPEN_WITH_INTENT
}
public void onUrlOpen(String url, EnumSet<Flags> flags);

View File

@ -5,6 +5,7 @@
package org.mozilla.gecko.home;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import org.mozilla.gecko.home.HomeConfig.PanelConfig;
import org.mozilla.gecko.home.HomeConfig.ViewConfig;
@ -58,6 +59,7 @@ abstract class PanelLayout extends FrameLayout {
private final List<ViewEntry> mViewEntries;
private final DatasetHandler mDatasetHandler;
private final OnUrlOpenListener mUrlOpenListener;
/**
* To be used by panel views to express that they are
@ -86,10 +88,15 @@ abstract class PanelLayout extends FrameLayout {
public void resetDataset(String datasetId);
}
public PanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler) {
public interface PanelView {
public void setOnUrlOpenListener(OnUrlOpenListener listener);
}
public PanelLayout(Context context, PanelConfig panelConfig, DatasetHandler datasetHandler, OnUrlOpenListener urlOpenListener) {
super(context);
mViewEntries = new ArrayList<ViewEntry>();
mDatasetHandler = datasetHandler;
mUrlOpenListener = urlOpenListener;
}
/**
@ -155,6 +162,8 @@ abstract class PanelLayout extends FrameLayout {
final ViewEntry entry = new ViewEntry(view, viewConfig);
mViewEntries.add(entry);
((PanelView) view).setOnUrlOpenListener(mUrlOpenListener);
return view;
}
@ -228,4 +237,4 @@ abstract class PanelLayout extends FrameLayout {
return mViewConfig.getDatasetId();
}
}
}
}

View File

@ -7,7 +7,10 @@ package org.mozilla.gecko.home;
import org.mozilla.gecko.R;
import org.mozilla.gecko.home.HomeConfig.ViewConfig;
import org.mozilla.gecko.home.HomePager.OnUrlOpenListener;
import org.mozilla.gecko.home.PanelLayout.DatasetBacked;
import org.mozilla.gecko.home.PanelLayout.PanelView;
import org.mozilla.gecko.db.BrowserContract.HomeItems;
import android.content.Context;
import android.database.Cursor;
@ -16,9 +19,12 @@ import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import java.util.EnumSet;
public class PanelListView extends HomeListView
implements DatasetBacked {
implements DatasetBacked, PanelView {
private static final String LOGTAG = "GeckoPanelListView";
@ -30,6 +36,7 @@ public class PanelListView extends HomeListView
mViewConfig = viewConfig;
mAdapter = new PanelListAdapter(context);
setAdapter(mAdapter);
setOnItemClickListener(new PanelListItemClickListener());
}
@Override
@ -54,4 +61,19 @@ public class PanelListView extends HomeListView
return LayoutInflater.from(parent.getContext()).inflate(R.layout.panel_list_row, parent, false);
}
}
}
private class PanelListItemClickListener implements AdapterView.OnItemClickListener {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Cursor cursor = mAdapter.getCursor();
if (cursor == null || !cursor.moveToPosition(position)) {
throw new IllegalStateException("Couldn't move cursor to position " + position);
}
int urlIndex = cursor.getColumnIndexOrThrow(HomeItems.URL);
final String url = cursor.getString(urlIndex);
mUrlOpenListener.onUrlOpen(url, EnumSet.of(OnUrlOpenListener.Flags.OPEN_WITH_INTENT));
}
}
}

View File

@ -66,7 +66,7 @@
<!ENTITY settings_title "Settings">
<!ENTITY pref_category_advanced "Advanced">
<!ENTITY pref_category_customize "Customize">
<!ENTITY pref_category_search2 "Search settings">
<!ENTITY pref_category_search3 "Search">
<!ENTITY pref_category_display "Display">
<!ENTITY pref_category_privacy_short "Privacy">
<!ENTITY pref_category_vendor "&vendorShortName;">

View File

@ -9,18 +9,24 @@
xmlns:gecko="http://schemas.android.com/apk/res-auto"
android:enabled="false">
<PreferenceScreen android:title="@string/pref_category_home"
android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
<extra android:name="resource"
android:value="preferences_home" />
</PreferenceScreen>
<PreferenceScreen android:title="@string/pref_category_search"
android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
<extra android:name="resource"
android:value="preferences_search"/>
</PreferenceScreen>
<PreferenceScreen android:title="@string/pref_category_home"
android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
<extra android:name="resource"
android:value="preferences_home" />
</PreferenceScreen>
<ListPreference android:key="android.not_a_preference.restoreSession3"
android:title="@string/pref_restore"
android:defaultValue="quit"
android:entries="@array/pref_restore_entries"
android:entryValues="@array/pref_restore_values"
android:persistent="true" />
<org.mozilla.gecko.preferences.AndroidImportPreference
android:key="android.not_a_preference.import_android"
@ -32,13 +38,6 @@
android:negativeButtonText="@string/button_cancel"
android:persistent="false" />
<ListPreference android:key="android.not_a_preference.restoreSession3"
android:title="@string/pref_restore"
android:defaultValue="quit"
android:entries="@array/pref_restore_entries"
android:entryValues="@array/pref_restore_values"
android:persistent="true" />
<ListPreference android:key="app.update.autodownload"
android:title="@string/pref_update_autodownload"
android:entries="@array/pref_update_autodownload_entries"

View File

@ -16,17 +16,24 @@
android:title="@string/pref_sync"
android:persistent="false" />
<PreferenceScreen android:title="@string/pref_category_home"
android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
<extra android:name="resource"
android:value="preferences_home" />
</PreferenceScreen>
<PreferenceScreen android:title="@string/pref_category_search"
android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
<extra android:name="resource"
android:value="preferences_search"/>
</PreferenceScreen>
<PreferenceScreen android:title="@string/pref_category_home"
android:fragment="org.mozilla.gecko.preferences.GeckoPreferenceFragment" >
<extra android:name="resource"
android:value="preferences_home" />
</PreferenceScreen>
<ListPreference android:key="android.not_a_preference.restoreSession3"
android:title="@string/pref_restore"
android:defaultValue="quit"
android:entries="@array/pref_restore_entries"
android:entryValues="@array/pref_restore_values"
android:persistent="true" />
<org.mozilla.gecko.preferences.AndroidImportPreference
android:key="android.not_a_preference.import_android"
@ -38,13 +45,6 @@
android:negativeButtonText="@string/button_cancel"
android:persistent="false" />
<ListPreference android:key="android.not_a_preference.restoreSession3"
android:title="@string/pref_restore"
android:defaultValue="quit"
android:entries="@array/pref_restore_entries"
android:entryValues="@array/pref_restore_values"
android:persistent="true" />
<ListPreference android:key="app.update.autodownload"
android:title="@string/pref_update_autodownload"
android:entries="@array/pref_update_autodownload_entries"

View File

@ -7,16 +7,6 @@
xmlns:gecko="http://schemas.android.com/apk/res-auto"
android:enabled="false">
<PreferenceScreen android:title="@string/pref_category_search" >
<intent android:action="android.intent.action.VIEW"
android:targetPackage="@string/android_package_name"
android:targetClass="org.mozilla.gecko.preferences.GeckoPreferences" >
<extra
android:name="resource"
android:value="preferences_search" />
</intent>
</PreferenceScreen>
<PreferenceScreen android:title="@string/pref_category_home" >
<intent android:action="android.intent.action.VIEW"
android:targetPackage="@string/android_package_name"
@ -27,6 +17,24 @@
</intent>
</PreferenceScreen>
<PreferenceScreen android:title="@string/pref_category_search" >
<intent android:action="android.intent.action.VIEW"
android:targetPackage="@string/android_package_name"
android:targetClass="org.mozilla.gecko.preferences.GeckoPreferences" >
<extra
android:name="resource"
android:value="preferences_search" />
</intent>
</PreferenceScreen>
<ListPreference android:key="android.not_a_preference.restoreSession3"
android:title="@string/pref_restore"
android:defaultValue="quit"
android:entries="@array/pref_restore_entries"
android:entryValues="@array/pref_restore_values"
android:persistent="true" />
<org.mozilla.gecko.preferences.AndroidImportPreference
android:key="android.not_a_preference.import_android"
gecko:entries="@array/pref_import_android_entries"
@ -37,12 +45,6 @@
android:negativeButtonText="@string/button_cancel"
android:persistent="false" />
<ListPreference android:key="android.not_a_preference.restoreSession3"
android:title="@string/pref_restore"
android:defaultValue="quit"
android:entries="@array/pref_restore_entries"
android:entryValues="@array/pref_restore_values"
android:persistent="true" />
<ListPreference android:key="app.update.autodownload"
android:title="@string/pref_update_autodownload"

View File

@ -93,7 +93,7 @@
<string name="settings_title">&settings_title;</string>
<string name="pref_category_advanced">&pref_category_advanced;</string>
<string name="pref_category_customize">&pref_category_customize;</string>
<string name="pref_category_search">&pref_category_search2;</string>
<string name="pref_category_search">&pref_category_search3;</string>
<string name="pref_category_display">&pref_category_display;</string>
<string name="pref_category_privacy_short">&pref_category_privacy_short;</string>
<string name="pref_category_vendor">&pref_category_vendor;</string>

View File

@ -31,10 +31,10 @@ public class testSettingsMenuItems extends PixelTest {
// Customize menu items.
String[][] OPTIONS_CUSTOMIZE = {
{ "Search settings", "", "Show search suggestions", "Installed search engines"},
{ "Home", "", "Panels" },
{ "Import from Android", "", "Bookmarks", "History", "Import" },
{ "Search", "", "Show search suggestions", "Installed search engines"},
{ "Tabs", "Don't restore after quitting " + BRAND_NAME, "Always restore", "Don't restore after quitting " + BRAND_NAME },
{ "Import from Android", "", "Bookmarks", "History", "Import" },
};
// Display menu items.

View File

@ -194,7 +194,11 @@ HawkClient.prototype = {
};
let request = new HAWKAuthenticatedRESTRequest(uri, credentials, extra);
request[method](payloadObj, onComplete);
if (method == "post" || method == "put") {
request[method](payloadObj, onComplete);
} else {
request[method](onComplete);
}
return deferred.promise;
}

View File

@ -763,19 +763,25 @@ HAWKAuthenticatedRESTRequest.prototype = {
__proto__: RESTRequest.prototype,
dispatch: function dispatch(method, data, onComplete, onProgress) {
let contentType = "text/plain";
if (method == "POST" || method == "PUT") {
contentType = "application/json";
}
if (this.credentials) {
let options = {
now: this.now,
localtimeOffsetMsec: this.localtimeOffsetMsec,
credentials: this.credentials,
payload: data && JSON.stringify(data) || "",
contentType: "application/json; charset=utf-8",
contentType: contentType,
};
let header = CryptoUtils.computeHAWK(this.uri, method, options);
this.setHeader("Authorization", header.field);
this._log.trace("hawk auth header: " + header.field);
}
this.setHeader("Content-Type", contentType);
return RESTRequest.prototype.dispatch.call(
this, method, data, onComplete, onProgress
);

View File

@ -178,7 +178,6 @@ function uninstallFakePAC() {
// 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);
Services.prefs.setBoolPref("services.sync.fxaccounts.enabled", false);
}
setDefaultIdentityConfig();

View File

@ -385,15 +385,6 @@ InternalMethods.prototype = {
Services.obs.notifyObservers(null, topic, null);
},
/**
* Give xpcshell tests an override point for duration testing. This is
* necessary because the tests need to manipulate the date in order to
* simulate certificate expiration.
*/
now: function() {
return Date.now();
},
pollEmailStatus: function pollEmailStatus(sessionToken, why) {
let myGenerationCount = this.generationCount;
log.debug("entering pollEmailStatus: " + why + " " + myGenerationCount);
@ -489,6 +480,20 @@ this.FxAccounts = function(mockInternal) {
this.FxAccounts.prototype = Object.freeze({
version: DATA_FORMAT_VERSION,
now: function() {
if (this.internal) {
return this.internal.now();
}
return internal.now();
},
get localtimeOffsetMsec() {
if (this.internal) {
return this.internal.localtimeOffsetMsec;
}
return internal.localtimeOffsetMsec;
},
// set() makes sure that polling is happening, if necessary.
// get() does not wait for verification, and returns an object even if
// unverified. The caller of get() must check .verified .
@ -620,7 +625,6 @@ this.FxAccounts.prototype = Object.freeze({
return internal.whenVerified(userData);
},
/**
* Sign the current user out.
*
@ -638,7 +642,26 @@ this.FxAccounts.prototype = Object.freeze({
throw new Error("Firefox Accounts server must use HTTPS");
}
return url;
},
// Returns a promise that resolves with the URL to use to force a re-signin
// of the current account.
promiseAccountsForceSigninURI: function() {
let url = Services.urlFormatter.formatURLPref("identity.fxaccounts.remote.force_auth.uri");
if (!/^https:/.test(url)) { // Comment to un-break emacs js-mode highlighting
throw new Error("Firefox Accounts server must use HTTPS");
}
// but we need to append the email address onto a query string.
return this.getSignedInUser().then(accountData => {
if (!accountData) {
return null;
}
let newQueryPortion = url.indexOf("?") == -1 ? "?" : "&";
newQueryPortion += "email=" + encodeURIComponent(accountData.email);
return url + newQueryPortion;
});
}
});
/**

View File

@ -66,24 +66,6 @@ WeaveService.prototype = {
},
get fxAccountsEnabled() {
// 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
// (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");
} catch (_) {
}
if (!fxAccountsAvailable) {
// Currently we don't support toggling this pref after initialization, so
// inject the pref value as a regular boolean.
delete this.fxAccountsEnabled;
this.fxAccountsEnabled = false;
return false;
}
// work out what identity manager to use. This is stored in a preference;
// if the preference exists, we trust it.
let fxAccountsEnabled;

View File

@ -46,6 +46,15 @@ function deriveKeyBundle(kB) {
return bundle;
}
/*
General authentication error for abstracting authentication
errors from multiple sources (e.g., from FxAccounts, TokenServer)
'message' is a string with a description of the error
*/
function AuthenticationError(message) {
this.message = message || "";
}
this.BrowserIDManager = function BrowserIDManager() {
this._fxaService = fxAccounts;
this._tokenServerClient = new TokenServerClient();
@ -86,6 +95,7 @@ this.BrowserIDManager.prototype = {
initialize: function() {
Services.obs.addObserver(this, fxAccountsCommon.ONLOGIN_NOTIFICATION, false);
Services.obs.addObserver(this, fxAccountsCommon.ONLOGOUT_NOTIFICATION, false);
Services.obs.addObserver(this, "weave:service:logout:finish", false);
return this.initializeWithCurrentIdentity();
},
@ -145,8 +155,7 @@ this.BrowserIDManager.prototype = {
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;
this._log.error("Background fetch for key bundle failed: " + err.message);
});
// and we are done - the fetch continues on in the background...
}).then(null, err => {
@ -168,6 +177,14 @@ this.BrowserIDManager.prototype = {
this._account = null;
Weave.Service.logout();
break;
case "weave:service:logout:finish":
// This signals an auth error with the storage server,
// or that the user unlinked her account from the browser.
// Either way, we clear our auth token. In the case of an
// auth error, this will force the fetch of a new one.
this._token = null;
break;
}
},
@ -194,11 +211,11 @@ this.BrowserIDManager.prototype = {
* Provide override point for testing token expiration.
*/
_now: function() {
return this._fxaService.internal.now()
return this._fxaService.now()
},
get _localtimeOffsetMsec() {
return this._fxaService.internal.localtimeOffsetMsec;
return this._fxaService.localtimeOffsetMsec;
},
get account() {
@ -364,10 +381,12 @@ this.BrowserIDManager.prototype = {
_fetchSyncKeyBundle: function() {
// Fetch a sync token for the logged in user from the token server.
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.");
// Unlikely, but if the logged in user somehow changed between these
// calls we better fail. TODO: add tests for these
if (!userData) {
throw new AuthenticationError("No userData in _fetchSyncKeyBundle");
} else if (userData.email !== this.account) {
throw new AuthenticationError("Unexpected user change in _fetchSyncKeyBundle");
}
return this._fetchTokenForUser(userData).then(token => {
this._token = token;
@ -397,7 +416,7 @@ this.BrowserIDManager.prototype = {
let cb = function (err, token) {
if (err) {
log.info("TokenServerClient.getTokenFromBrowserIDAssertion() failed with: " + err.message);
return deferred.reject(err);
return deferred.reject(new AuthenticationError(err.message));
} else {
return deferred.resolve(token);
}
@ -407,19 +426,44 @@ this.BrowserIDManager.prototype = {
return deferred.promise;
}
let audience = Services.io.newURI(tokenServerURI, null, null).prePath;
function getAssertion() {
let audience = Services.io.newURI(tokenServerURI, null, null).prePath;
return fxAccounts.getAssertion(audience).then(null, err => {
if (err.code === 401) {
throw new AuthenticationError("Unable to get assertion for user");
} else {
throw err;
}
});
};
// wait until the account email is verified and we know that
// getAssertion() will return a real assertion (not null).
return this._fxaService.whenVerified(userData)
.then(() => this._fxaService.getAssertion(audience))
.then(() => getAssertion())
.then(assertion => getToken(tokenServerURI, assertion))
.then(token => {
token.expiration = this._now() + (token.duration * 1000);
// TODO: Make it be only 80% of the duration, so refresh the token
// before it actually expires. This is to avoid sync storage errors
// otherwise, we get a nasty notification bar briefly. Bug 966568.
token.expiration = this._now() + (token.duration * 1000) * 0.80;
return token;
})
.then(null, err => {
Cu.reportError("Failed to fetch token: " + err);
// XXX - TODO - work out how to set sync to an error state.
// TODO: write tests to make sure that different auth error cases are handled here
// properly: auth error getting assertion, auth error getting token (invalid generation
// and client-state error)
if (err instanceof AuthenticationError) {
this._log.error("Authentication error in _fetchTokenForUser: " + err.message);
// Drop the sync key bundle, but still expect to have one.
// This will arrange for us to be in the right 'currentAuthState'
// such that UI will show the right error.
this._shouldHaveSyncKeyBundle = true;
this._syncKeyBundle = null;
Weave.Status.login = this.currentAuthState;
Services.obs.notifyObservers(null, "weave:service:login:error", null);
}
throw err;
});
},

View File

@ -136,13 +136,21 @@ add_test(function test_resourceAuthenticatorSkew() {
do_check_eq(fxa.internal.now(), now);
do_check_eq(fxa.internal.localtimeOffsetMsec, localtimeOffsetMsec);
do_check_eq(fxa.now(), now);
do_check_eq(fxa.localtimeOffsetMsec, localtimeOffsetMsec);
// Mocks within mocks...
configureFxAccountIdentity(browseridManager, identityConfig);
browseridManager._fxaService = fxa;
do_check_eq(browseridManager._fxaService.internal.now(), now);
do_check_eq(browseridManager._fxaService.internal.localtimeOffsetMsec,
localtimeOffsetMsec);
do_check_eq(browseridManager._fxaService.now(), now);
do_check_eq(browseridManager._fxaService.localtimeOffsetMsec,
localtimeOffsetMsec);
let request = new SyncStorageRequest("https://example.net/i/like/pie/");
let authenticator = browseridManager.getResourceAuthenticator();
let output = authenticator(request, 'GET');