author | Wes Kocher <wkocher@mozilla.com> |
Mon, 06 Oct 2014 19:29:41 -0700 | |
changeset 232287 | 9ee9e193fc48b0c7e6f27337c64fe4780842ea87 |
parent 232256 | 0208159db84df29d10dd18705a53c6d5334db613 (current diff) |
parent 232286 | 650603771acde4563e4b8b20d3efd213c746d9cb (diff) |
child 232306 | f65726bd726555bd7cab954c3347931f4c9d4c00 |
child 232311 | 896e255af48ebaf6fc0b5581865fcdbd53e07733 |
child 232319 | be8ee7774bb95b714ec5880adf7bfb6224374e29 |
push id | 4187 |
push user | bhearsum@mozilla.com |
push date | Fri, 28 Nov 2014 15:29:12 +0000 |
treeherder | mozilla-beta@f23cc6a30c11 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | merge |
milestone | 35.0a1 |
first release with | nightly linux32
9ee9e193fc48
/
35.0a1
/
20141007030202
/
files
nightly linux64
9ee9e193fc48
/
35.0a1
/
20141007030202
/
files
nightly mac
9ee9e193fc48
/
35.0a1
/
20141007030202
/
files
nightly win32
9ee9e193fc48
/
35.0a1
/
20141007030202
/
files
nightly win64
9ee9e193fc48
/
35.0a1
/
20141007030202
/
files
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
releases | nightly linux32
35.0a1
/
20141007030202
/
pushlog to previous
nightly linux64
35.0a1
/
20141007030202
/
pushlog to previous
nightly mac
35.0a1
/
20141007030202
/
pushlog to previous
nightly win32
35.0a1
/
20141007030202
/
pushlog to previous
nightly win64
35.0a1
/
20141007030202
/
pushlog to previous
|
--- a/addon-sdk/source/lib/sdk/content/sandbox.js +++ b/addon-sdk/source/lib/sdk/content/sandbox.js @@ -69,17 +69,20 @@ const WorkerSandbox = Class({ /** * Synchronous version of `emit`. * /!\ Should only be used when it is strictly mandatory /!\ * Doesn't ensure passing only JSON values. * Mainly used by context-menu in order to avoid breaking it. */ emitSync: function emitSync(...args) { - return emitToContent(this, args); + // because the arguments could be also non JSONable values, + // we need to ensure the array instance is created from + // the content's sandbox + return emitToContent(this, new modelFor(this).sandbox.Array(...args)); }, /** * Configures sandbox and loads content scripts into it. * @param {Worker} worker * content worker */ initialize: function WorkerSandbox(worker, window) {
--- a/addon-sdk/source/lib/sdk/panel/window.js +++ b/addon-sdk/source/lib/sdk/panel/window.js @@ -30,20 +30,24 @@ function getWindow(anchor) { for (let enumWindow of windows) { // Check if the anchor is in this browser window. if (enumWindow == anchorWindow) { window = anchorWindow; break; } // Check if the anchor is in a browser tab in this browser window. - let browser = enumWindow.gBrowser.getBrowserForDocument(anchorDocument); - if (browser) { - window = enumWindow; - break; + try { + let browser = enumWindow.gBrowser.getBrowserForDocument(anchorDocument); + if (browser) { + window = enumWindow; + break; + } + } + catch (e) { } // Look in other subdocuments (sidebar, etc.)? } } // If we didn't find the anchor's window (or we have no anchor), // return the most recent browser window.
--- a/browser/components/customizableui/test/browser.ini +++ b/browser/components/customizableui/test/browser.ini @@ -130,16 +130,17 @@ skip-if = os == "linux" [browser_985815_propagate_setToolbarVisibility.js] [browser_981305_separator_insertion.js] [browser_988072_sidebar_events.js] [browser_989338_saved_placements_not_resaved.js] [browser_989751_subviewbutton_class.js] [browser_987177_destroyWidget_xul.js] [browser_987177_xul_wrapper_updating.js] +[browser_987185_syncButton.js] [browser_987492_window_api.js] [browser_987640_charEncoding.js] [browser_992747_toggle_noncustomizable_toolbar.js] [browser_993322_widget_notoolbar.js] [browser_995164_registerArea_during_customize_mode.js] [browser_996364_registerArea_different_properties.js] [browser_996635_remove_non_widgets.js] [browser_1003588_no_specials_in_panel.js]
new file mode 100755 --- /dev/null +++ b/browser/components/customizableui/test/browser_987185_syncButton.js @@ -0,0 +1,69 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ +"use strict"; + +let syncService = {}; +Components.utils.import("resource://services-sync/service.js", syncService); + +let needsSetup; +let originalSync; +let service = syncService.Service; +let syncWasCalled = false; + +add_task(function* testSyncButtonFunctionality() { + info("Check Sync button functionality"); + storeInitialValues(); + mockFunctions(); + + // add the Sync button to the panel + CustomizableUI.addWidgetToArea("sync-button", CustomizableUI.AREA_PANEL); + + // check the button's functionality + yield PanelUI.show(); + info("The panel menu was opened"); + + let syncButton = document.getElementById("sync-button"); + ok(syncButton, "The Sync button was added to the Panel Menu"); + syncButton.click(); + info("The sync button was clicked"); + + yield waitForCondition(() => syncWasCalled); +}); + +add_task(function* asyncCleanup() { + // reset the panel UI to the default state + yield resetCustomization(); + ok(CustomizableUI.inDefaultState, "The panel UI is in default state again."); + + if (isPanelUIOpen()) { + let panelHidePromise = promisePanelHidden(window); + PanelUI.hide(); + yield panelHidePromise; + } + + restoreValues(); +}); + +function mockFunctions() { + // mock needsSetup + gSyncUI._needsSetup = function() false; + + // mock service.errorHandler.syncAndReportErrors() + service.errorHandler.syncAndReportErrors = mocked_syncAndReportErrors; +} + +function mocked_syncAndReportErrors() { + syncWasCalled = true; +} + +function restoreValues() { + gSyncUI._needsSetup = needsSetup; + service.sync = originalSync; +} + +function storeInitialValues() { + needsSetup = gSyncUI._needsSetup; + originalSync = service.sync; +}
--- a/browser/components/loop/content/js/panel.js +++ b/browser/components/loop/content/js/panel.js @@ -50,17 +50,19 @@ loop.panel = (function(_, mozL10n) { tabs.push( React.DOM.div({key: i, className: cx({tab: true, selected: isSelected})}, tab.props.children ) ); }, this); return ( React.DOM.div({className: "tab-view-container"}, - React.DOM.ul({className: "tab-view"}, tabButtons), + !this.props.buttonsHidden + ? React.DOM.ul({className: "tab-view"}, tabButtons) + : null, tabs ) ); } }); var Tab = React.createClass({displayName: 'Tab', render: function() { @@ -434,16 +436,17 @@ loop.panel = (function(_, mozL10n) { */ var PanelView = React.createClass({displayName: 'PanelView', propTypes: { notifications: React.PropTypes.object.isRequired, client: React.PropTypes.object.isRequired, // Mostly used for UI components showcase and unit tests callUrl: React.PropTypes.string, userProfile: React.PropTypes.object, + showTabButtons: React.PropTypes.bool, }, getInitialState: function() { return { userProfile: this.props.userProfile || navigator.mozLoop.userProfile, }; }, @@ -470,17 +473,22 @@ loop.panel = (function(_, mozL10n) { detailsButtonLabel: serviceError.error.friendlyDetailsButtonLabel, }); } else { this.props.notifications.remove(this.props.notifications.get("service-error")); } }, _onStatusChanged: function() { - this.setState({userProfile: navigator.mozLoop.userProfile}); + var profile = navigator.mozLoop.userProfile; + if (profile != this.state.userProfile) { + // On profile change (login, logout), switch back to the default tab. + this.selectTab("call"); + } + this.setState({userProfile: profile}); this.updateServiceErrors(); }, startForm: function(name, contact) { this.refs[name].initForm(contact); this.selectTab(name); }, @@ -503,17 +511,17 @@ loop.panel = (function(_, mozL10n) { render: function() { var NotificationListView = sharedViews.NotificationListView; var displayName = this.state.userProfile && this.state.userProfile.email || __("display_name_guest"); return ( React.DOM.div(null, NotificationListView({notifications: this.props.notifications, clearOnDocumentHidden: true}), - TabView({ref: "tabView"}, + TabView({ref: "tabView", buttonsHidden: !this.state.userProfile && !this.props.showTabButtons}, Tab({name: "call"}, React.DOM.div({className: "content-area"}, CallUrlResult({client: this.props.client, notifications: this.props.notifications, callUrl: this.props.callUrl}), ToSView(null) ) ),
--- a/browser/components/loop/content/js/panel.jsx +++ b/browser/components/loop/content/js/panel.jsx @@ -50,17 +50,19 @@ loop.panel = (function(_, mozL10n) { tabs.push( <div key={i} className={cx({tab: true, selected: isSelected})}> {tab.props.children} </div> ); }, this); return ( <div className="tab-view-container"> - <ul className="tab-view">{tabButtons}</ul> + {!this.props.buttonsHidden + ? <ul className="tab-view">{tabButtons}</ul> + : null} {tabs} </div> ); } }); var Tab = React.createClass({ render: function() { @@ -434,16 +436,17 @@ loop.panel = (function(_, mozL10n) { */ var PanelView = React.createClass({ propTypes: { notifications: React.PropTypes.object.isRequired, client: React.PropTypes.object.isRequired, // Mostly used for UI components showcase and unit tests callUrl: React.PropTypes.string, userProfile: React.PropTypes.object, + showTabButtons: React.PropTypes.bool, }, getInitialState: function() { return { userProfile: this.props.userProfile || navigator.mozLoop.userProfile, }; }, @@ -470,17 +473,22 @@ loop.panel = (function(_, mozL10n) { detailsButtonLabel: serviceError.error.friendlyDetailsButtonLabel, }); } else { this.props.notifications.remove(this.props.notifications.get("service-error")); } }, _onStatusChanged: function() { - this.setState({userProfile: navigator.mozLoop.userProfile}); + var profile = navigator.mozLoop.userProfile; + if (profile != this.state.userProfile) { + // On profile change (login, logout), switch back to the default tab. + this.selectTab("call"); + } + this.setState({userProfile: profile}); this.updateServiceErrors(); }, startForm: function(name, contact) { this.refs[name].initForm(contact); this.selectTab(name); }, @@ -503,17 +511,17 @@ loop.panel = (function(_, mozL10n) { render: function() { var NotificationListView = sharedViews.NotificationListView; var displayName = this.state.userProfile && this.state.userProfile.email || __("display_name_guest"); return ( <div> <NotificationListView notifications={this.props.notifications} clearOnDocumentHidden={true} /> - <TabView ref="tabView"> + <TabView ref="tabView" buttonsHidden={!this.state.userProfile && !this.props.showTabButtons}> <Tab name="call"> <div className="content-area"> <CallUrlResult client={this.props.client} notifications={this.props.notifications} callUrl={this.props.callUrl} /> <ToSView /> </div> </Tab>
--- a/browser/components/loop/test/desktop-local/panel_test.js +++ b/browser/components/loop/test/desktop-local/panel_test.js @@ -135,17 +135,18 @@ describe("loop.panel", function() { fakeClient = { requestCallUrl: function(_, cb) { cb(null, callUrlData); } }; view = TestUtils.renderIntoDocument(loop.panel.PanelView({ notifications: notifications, - client: fakeClient + client: fakeClient, + showTabButtons: true, })); [callTab, contactsTab] = TestUtils.scryRenderedDOMComponentsWithClass(view, "tab"); }); describe('TabView', function() { it("should select contacts tab when clicking tab button", function() {
--- a/browser/components/translation/Translation.jsm +++ b/browser/components/translation/Translation.jsm @@ -27,18 +27,18 @@ this.Translation = { STATE_OFFER: 0, STATE_TRANSLATING: 1, STATE_TRANSLATED: 2, STATE_ERROR: 3, STATE_UNAVAILABLE: 4, serviceUnavailable: false, - supportedSourceLanguages: ["de", "en", "es", "fr", "ja", "ko", "pt", "ru", "zh"], - supportedTargetLanguages: ["de", "en", "es", "fr", "ja", "ko", "pl", "pt", "ru", "tr", "vi", "zh"], + supportedSourceLanguages: ["bg", "cs", "de", "en", "es", "fr", "ja", "ko", "nl", "no", "pl", "pt", "ru", "tr", "vi", "zh"], + supportedTargetLanguages: ["bg", "cs", "de", "en", "es", "fr", "ja", "ko", "nl", "no", "pl", "pt", "ru", "tr", "vi", "zh"], _defaultTargetLanguage: "", get defaultTargetLanguage() { if (!this._defaultTargetLanguage) { this._defaultTargetLanguage = Cc["@mozilla.org/chrome/chrome-registry;1"] .getService(Ci.nsIXULChromeRegistry) .getSelectedLocale("global") .split("-")[0];
--- a/browser/components/translation/translation-infobar.xml +++ b/browser/components/translation/translation-infobar.xml @@ -18,16 +18,31 @@ <resources> <stylesheet src="chrome://global/skin/notification.css"/> </resources> <content> <xul:hbox class="notification-inner outset" flex="1" xbl:inherits="type"> <xul:hbox anonid="details" align="center" flex="1"> <xul:image class="translate-infobar-element messageImage" anonid="messageImage"/> + <xul:panel anonid="welcomePanel" class="translation-welcome-panel" + type="arrow" align="start"> + <xul:image class="translation-welcome-logo"/> + <xul:vbox flex="1" class="translation-welcome-content"> + <xul:description class="translation-welcome-headline" + anonid="welcomeHeadline"/> + <xul:description class="translation-welcome-body" anonid="welcomeBody"/> + <xul:hbox align="center"> + <xul:label anonid="learnMore" class="plain text-link"/> + <xul:spacer flex="1"/> + <xul:button class="translate-infobar-element" anonid="thanksButton" + onclick="this.parentNode.parentNode.parentNode.hidePopup();"/> + </xul:hbox> + </xul:vbox> + </xul:panel> <xul:deck anonid="translationStates" selectedIndex="0"> <!-- offer to translate --> <xul:hbox class="translate-offer-box" align="center"> <xul:label class="translate-infobar-element" value="&translation.thisPageIsIn.label;"/> <xul:menulist class="translate-infobar-element" anonid="detectedLanguage"> <xul:menupopup/> </xul:menulist> @@ -193,16 +208,51 @@ for (let [code, name] of targetLanguages) toLanguage.appendItem(name, code); if (aTranslation.translatedTo) toLanguage.value = aTranslation.translatedTo; if (aTranslation.state) this.state = aTranslation.state; + + const kWelcomePref = "browser.translation.ui.welcomeMessageShown"; + if (Services.prefs.prefHasUserValue(kWelcomePref)) + return; + + this.addEventListener("transitionend", function onShown() { + this.removeEventListener("transitionend", onShown); + + // These strings are hardcoded because they need to reach beta + // without riding the trains. + let localizedStrings = { + en: ["Hey look! It's something new!", + "Now the Web is even more accessible with our new in-page translation feature. Click the translate button to try it!", + "Learn more.", + "Thanks"] + }; + + let locale = Cc["@mozilla.org/chrome/chrome-registry;1"] + .getService(Ci.nsIXULChromeRegistry) + .getSelectedLocale("browser"); + if (!(locale in localizedStrings)) + locale = "en"; + let strings = localizedStrings[locale]; + + this._getAnonElt("welcomeHeadline").setAttribute("value", strings[0]); + this._getAnonElt("welcomeBody").textContent = strings[1]; + this._getAnonElt("learnMore").setAttribute("value", strings[2]); + this._getAnonElt("thanksButton").setAttribute("label", strings[3]); + + let panel = this._getAnonElt("welcomePanel"); + panel.openPopup(this._getAnonElt("messageImage"), + "bottomcenter topleft"); + + Services.prefs.setBoolPref(kWelcomePref, true); + }); ]]> </body> </method> <method name="_getAnonElt"> <parameter name="aAnonId"/> <body> return document.getAnonymousElementByAttribute(this, "anonid", aAnonId);
--- a/browser/locales/en-US/chrome/browser/devtools/webide.properties +++ b/browser/locales/en-US/chrome/browser/devtools/webide.properties @@ -14,17 +14,17 @@ local_runtime=Local Runtime remote_runtime=Remote Runtime remote_runtime_promptTitle=Remote Runtime remote_runtime_promptMessage=hostname:port importPackagedApp_title=Select Directory importHostedApp_title=Open Hosted App importHostedApp_header=Enter Manifest URL -notification_showTroubleShooting_label=troubleshooting +notification_showTroubleShooting_label=Troubleshooting notification_showTroubleShooting_accesskey=t # LOCALIZATION NOTE (project_tab_loading): This is shown as a temporary tab # title for browser tab projects when the tab is still loading. project_tab_loading=Loading… # These messages appear in a notification box when an error occur.
--- a/browser/themes/shared/translation/infobar.inc.css +++ b/browser/themes/shared/translation/infobar.inc.css @@ -59,8 +59,33 @@ notification[value="translation"] menuli .translation-attribution > label { margin-bottom: 0; } .translation-attribution > image { width: 70px; } + +.translation-welcome-panel { + width: 305px; +} + +.translation-welcome-logo { + height: 32px; + width: 32px; + list-style-image: url(chrome://browser/skin/translation-16@2x.png); + -moz-image-region: rect(0, 64px, 32px, 32px); +} + +.translation-welcome-content { + -moz-margin-start: 16px; +} + +.translation-welcome-headline { + font-size: larger; + font-weight: bold; +} + +.translation-welcome-body { + padding: 1em 0; + margin: 0 0; +}
--- a/mobile/android/base/AndroidManifest.xml.in +++ b/mobile/android/base/AndroidManifest.xml.in @@ -18,20 +18,18 @@ #include ../services/manifests/FxAccountAndroidManifest_permissions.xml.in #include ../services/manifests/HealthReportAndroidManifest_permissions.xml.in #include ../services/manifests/SyncAndroidManifest_permissions.xml.in #ifdef MOZ_ANDROID_SEARCH_ACTIVITY #include ../search/manifests/SearchAndroidManifest_permissions.xml.in #endif -#ifndef RELEASE_BUILD <uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/> -#endif <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/> <uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT"/> <uses-permission android:name="com.android.browser.permission.READ_HISTORY_BOOKMARKS"/>
--- a/mobile/android/base/home/TwoLinePageRow.java +++ b/mobile/android/base/home/TwoLinePageRow.java @@ -2,17 +2,19 @@ * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.home; import java.lang.ref.WeakReference; +import org.mozilla.gecko.AboutPages; import org.mozilla.gecko.R; +import org.mozilla.gecko.ReaderModeUtils; import org.mozilla.gecko.Tab; import org.mozilla.gecko.Tabs; import org.mozilla.gecko.db.BrowserContract.Combined; import org.mozilla.gecko.db.BrowserContract.URLColumns; import org.mozilla.gecko.favicons.Favicons; import org.mozilla.gecko.favicons.OnFaviconLoadedListener; import org.mozilla.gecko.widget.FaviconView; @@ -243,17 +245,22 @@ public class TwoLinePageRow extends Line // No point updating the below things if URL has not changed. Prevents evil Favicon flicker. if (url.equals(mPageUrl)) { return; } // Blank the Favicon, so we don't show the wrong Favicon if we scroll and miss DB. mFavicon.clearImage(); Favicons.cancelFaviconLoad(mLoadFaviconJobId); - mLoadFaviconJobId = Favicons.getSizedFaviconForPageFromLocal(getContext(), url, mFaviconListener); + + // Displayed RecentTabsPanel urls may refer to pages openned in readermode, so we + // remove the about:reader prefix to ensure the Favicon loads properly. + final String pageURL = AboutPages.isAboutReader(url) ? + ReaderModeUtils.getUrlFromAboutReader(url) : url; + mLoadFaviconJobId = Favicons.getSizedFaviconForPageFromLocal(getContext(), pageURL, mFaviconListener); updateDisplayedUrl(url); } /** * Update the data displayed by this row. * <p> * This method must be invoked on the UI thread.
--- a/mobile/android/base/locales/Makefile.in +++ b/mobile/android/base/locales/Makefile.in @@ -110,21 +110,23 @@ endef # L10NBASEDIR is not defined for en-US. l10n-srcdir := $(if $(filter en-US,$(AB_CD)),,$(or $(realpath $(L10NBASEDIR)),$(abspath $(L10NBASEDIR)))/$(AB_CD)/mobile/chrome) $(eval $(call generated_file_template,suggestedsites,suggestedsites.json)) $(suggestedsites-dstdir-raw)/suggestedsites.json: FORCE $(call py_action,generate_suggestedsites, \ + --verbose \ --android-package-name=$(ANDROID_PACKAGE_NAME) \ --resources=$(srcdir)/../resources \ $(if $(filter en-US,$(AB_CD)),,--srcdir=$(l10n-srcdir)) \ --srcdir=$(topsrcdir)/mobile/locales/en-US/chrome \ $@) $(eval $(call generated_file_template,browsersearch,browsersearch.json)) $(browsersearch-dstdir-raw)/browsersearch.json: FORCE $(call py_action,generate_browsersearch, \ + --verbose \ $(if $(filter en-US,$(AB_CD)),,--srcdir=$(l10n-srcdir)) \ --srcdir=$(topsrcdir)/mobile/locales/en-US/chrome \ $@)
--- a/mobile/android/chrome/content/aboutAddons.js +++ b/mobile/android/chrome/content/aboutAddons.js @@ -522,16 +522,31 @@ var Addons = { element = this._createItemForAddon(aAddon); list.insertBefore(element, list.firstElementChild); } if (needsRestart) element.setAttribute("opType", "needs-restart"); }, + onInstalled: function(aAddon) { + let list = document.getElementById("addons-list"); + let element = this._getElementForAddon(aAddon.id); + if (!element) { + element = this._createItemForAddon(aAddon); + list.insertBefore(element, list.firstElementChild); + } + }, + + onUninstalled: function(aAddon) { + let list = document.getElementById("addons-list"); + let element = this._getElementForAddon(aAddon.id); + list.removeChild(element); + }, + onInstallFailed: function(aInstall) { }, onDownloadProgress: function xpidm_onDownloadProgress(aInstall) { }, onDownloadFailed: function(aInstall) { },
--- a/mobile/android/chrome/content/aboutApps.xhtml +++ b/mobile/android/chrome/content/aboutApps.xhtml @@ -30,17 +30,16 @@ <body dir="&locale.dir;"> <menu type="context" id="appmenu"> <menuitem id="uninstallLabel" label="&aboutApps.uninstall;"></menuitem> </menu> <div class="header"> <div>&aboutApps.header;</div> - <div id="header-button" role="button" aria-label="&aboutApps.browseMarketplace;" pref="app.marketplaceURL"/> </div> <div id="main-container" class="hidden"> <div> <div class="spacer" id="spacer1"> </div> <div id="appgrid"/> <div class="spacer" id="spacer1"> </div> </div>
--- a/mobile/android/chrome/content/browser.js +++ b/mobile/android/chrome/content/browser.js @@ -824,16 +824,23 @@ var BrowserApp = { Services.obs.notifyObservers(null, "Passwords:Init", ""); // Migrate user-set "plugins.click_to_play" pref. See bug 884694. // Because the default value is true, a user-set pref means that the pref was set to false. if (Services.prefs.prefHasUserValue("plugins.click_to_play")) { Services.prefs.setIntPref("plugin.default.state", Ci.nsIPluginTag.STATE_ENABLED); Services.prefs.clearUserPref("plugins.click_to_play"); } + + // Set the search activity default pref on app upgrade if it has not been set already. + if (this._startupStatus === "upgrade" && + !Services.prefs.prefHasUserValue("searchActivity.default.migrated")) { + Services.prefs.setBoolPref("searchActivity.default.migrated", true); + SearchEngines.migrateSearchActivityDefaultPref(); + } }, shutdown: function shutdown() { NativeWindow.uninit(); LightWeightThemeWebInstaller.uninit(); FormAssistant.uninit(); IndexedDB.uninit(); ViewportHandler.uninit(); @@ -6904,16 +6911,20 @@ var SearchEngines = { } break; default: dump("Unexpected message type observed: " + aTopic); break; } }, + migrateSearchActivityDefaultPref: function migrateSearchActivityDefaultPref() { + Services.search.init(() => this._setSearchActivityDefaultPref(Services.search.defaultEngine)); + }, + // Updates the search activity pref when the default engine changes. _setSearchActivityDefaultPref: function _setSearchActivityDefaultPref(engine) { // Helper function copied from nsSearchService.js. This is the logic that is used // to create file names for search plugin XML serialized to disk. function sanitizeName(aName) { const maxLength = 60; const minLength = 1; let name = aName.toLowerCase();
--- a/mobile/android/themes/core/aboutApps.css +++ b/mobile/android/themes/core/aboutApps.css @@ -1,23 +1,16 @@ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ .app:active { background-color: #febc2b; } -#header-button { - background-image: url("chrome://browser/skin/images/marketplace-logo.png"), url("chrome://browser/skin/images/chevron.png"); - background-size: 32px 32px, 8px 20px; - background-position: left, right 0.5em center; - -moz-padding-start: 2.5em; -} - #main-container { padding: 2em; background-color: #EEF2F5; border-bottom: 1px solid #BAC2AC; } .hidden { display: none;
--- a/python/moz.build +++ b/python/moz.build @@ -18,16 +18,17 @@ PYTHON_UNIT_TESTS += [ 'mach/mach/test/test_config.py', 'mach/mach/test/test_entry_point.py', 'mach/mach/test/test_error_output.py', 'mach/mach/test/test_logger.py', 'mozbuild/dumbmake/test/__init__.py', 'mozbuild/dumbmake/test/test_dumbmake.py', 'mozbuild/mozbuild/test/__init__.py', 'mozbuild/mozbuild/test/action/test_buildlist.py', + 'mozbuild/mozbuild/test/action/test_generate_browsersearch.py', 'mozbuild/mozbuild/test/backend/__init__.py', 'mozbuild/mozbuild/test/backend/common.py', 'mozbuild/mozbuild/test/backend/test_android_eclipse.py', 'mozbuild/mozbuild/test/backend/test_configenvironment.py', 'mozbuild/mozbuild/test/backend/test_recursivemake.py', 'mozbuild/mozbuild/test/backend/test_visualstudio.py', 'mozbuild/mozbuild/test/common.py', 'mozbuild/mozbuild/test/compilation/__init__.py',
--- a/python/mozbuild/mozbuild/action/generate_browsersearch.py +++ b/python/mozbuild/mozbuild/action/generate_browsersearch.py @@ -17,19 +17,23 @@ 2. Read the default search plugin from t 3. Read the list of search plugins from the 'browser.search.order.INDEX' properties with values identifying particular search plugins by name. 4. Generate a JSON representation of 2. and 3., and write the result to browsersearch.json in the locale-specific raw resource directory e.g. raw/browsersearch.json, raw-pt-rBR/browsersearch.json. ''' -from __future__ import print_function +from __future__ import ( + print_function, + unicode_literals, +) import argparse +import codecs import json import re import sys import os from mozbuild.dotproperties import ( DotProperties, ) @@ -67,18 +71,19 @@ def main(args): # Use reversed order so that the first srcdir has higher priority to override keys. properties = merge_properties('region.properties', reversed(opts.srcdir)) default = properties.get('browser.search.defaultenginename') engines = properties.get_list('browser.search.order') if opts.verbose: - print('Read {len} engines: {engines}'.format(len=len(engines), engines=engines)) - print("Default engine is '{default}'.".format(default=default)) + writer = codecs.getwriter('utf-8')(sys.stdout) + print('Read {len} engines: {engines}'.format(len=len(engines), engines=engines), file=writer) + print("Default engine is '{default}'.".format(default=default), file=writer) browsersearch = {} browsersearch['default'] = default browsersearch['engines'] = engines # FileAvoidWrite creates its parent directories. output = os.path.abspath(opts.output) fh = FileAvoidWrite(output)
new file mode 100644 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/invalid/region.properties @@ -0,0 +1,12 @@ +# A region.properties file with invalid unicode byte sequences. The +# sequences were cribbed from Markus Kuhn's "UTF-8 decoder capability +# and stress test", available at +# http://www.cl.cam.ac.uk/~mgk25/ucs/examples/UTF-8-test.txt + +# 3.5 Impossible bytes | +# | +# The following two bytes cannot appear in a correct UTF-8 string | +# | +# 3.5.1 fe = "þ" | +# 3.5.2 ff = "ÿ" | +# 3.5.3 fe fe ff ff = "þþÿÿ" |
new file mode 100644 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/data/valid-zh-CN/region.properties @@ -0,0 +1,37 @@ +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. + +# Default search engine +browser.search.defaultenginename=百度 + +# Search engine order (order displayed in the search bar dropdown)s +browser.search.order.1=百度 +browser.search.order.2=Google + +# This is the default set of web based feed handlers shown in the reader +# selection UI +browser.contentHandlers.types.0.title=Bloglines +browser.contentHandlers.types.0.uri=http://www.bloglines.com/login?r=/sub/%s + +# increment this number when anything gets changed in the list below. This will +# cause Firefox to re-read these prefs and inject any new handlers into the +# profile database. Note that "new" is defined as "has a different URL"; this +# means that it's not possible to update the name of existing handler, so +# don't make any spelling errors here. +gecko.handlerService.defaultHandlersVersion=3 + +# The default set of protocol handlers for webcal: +gecko.handlerService.schemes.webcal.0.name=30 Boxes +gecko.handlerService.schemes.webcal.0.uriTemplate=https://30boxes.com/external/widget?refer=ff&url=%s + +# The default set of protocol handlers for mailto: +gecko.handlerService.schemes.mailto.0.name=Yahoo! 邮件 +gecko.handlerService.schemes.mailto.0.uriTemplate=https://compose.mail.yahoo.com/?To=%s +gecko.handlerService.schemes.mailto.1.name=Gmail +gecko.handlerService.schemes.mailto.1.uriTemplate=https://mail.google.com/mail/?extsrc=mailto&url=%s + +# This is the default set of web based feed handlers shown in the reader +# selection UI +browser.contentHandlers.types.0.title=My Yahoo! +browser.contentHandlers.types.0.uri=http://www.bloglines.com/login?r=/sub/%s
new file mode 100644 --- /dev/null +++ b/python/mozbuild/mozbuild/test/action/test_generate_browsersearch.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- + +# Any copyright is dedicated to the Public Domain. +# http://creativecommons.org/publicdomain/zero/1.0/ + +from __future__ import unicode_literals + +import json +import os +import unittest + +import mozunit + +import mozbuild.action.generate_browsersearch as generate_browsersearch + +from mozfile.mozfile import ( + NamedTemporaryFile, + TemporaryDirectory, +) + +import mozpack.path as mozpath + + +test_data_path = mozpath.abspath(mozpath.dirname(__file__)) +test_data_path = mozpath.join(test_data_path, 'data') + + +class TestGenerateBrowserSearch(unittest.TestCase): + """ + Unit tests for generate_browsersearch.py. + """ + + def _test_one(self, name): + with TemporaryDirectory() as tmpdir: + with NamedTemporaryFile(mode='r+') as temp: + srcdir = os.path.join(test_data_path, name) + + generate_browsersearch.main([ + '--verbose', + '--srcdir', srcdir, + temp.name]) + return json.load(temp) + + def test_valid_unicode(self): + o = self._test_one('valid-zh-CN') + self.assertEquals(o['default'], '百度') + self.assertEquals(o['engines'], ['百度', 'Google']) + + def test_invalid_unicode(self): + with self.assertRaises(UnicodeDecodeError): + self._test_one('invalid') + + +if __name__ == '__main__': + mozunit.main()
--- a/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm +++ b/toolkit/components/places/PlacesSearchAutocompleteProvider.jsm @@ -59,16 +59,17 @@ const SearchAutocompleteProviderInternal initialized: false, observe: function (subject, topic, data) { switch (data) { case "engine-added": case "engine-changed": case "engine-removed": + case "engine-current": this._refresh(); } }, _refresh: function () { this.priorityMatches = []; this.aliasMatches = []; this.defaultMatch = null;
--- a/toolkit/components/places/tests/unifiedcomplete/test_searchEngine_current.js +++ b/toolkit/components/places/tests/unifiedcomplete/test_searchEngine_current.js @@ -26,10 +26,22 @@ add_task(function*() { do_log_info("search engine, multiple words"); yield check_autocomplete({ search: "mozzarella cheese", searchParam: "enable-actions", matches: [ { uri: makeActionURI("searchengine", {engineName: "MozSearch", input: "mozzarella cheese", searchQuery: "mozzarella cheese"}), title: "MozSearch" }, ] }); + do_log_info("search engine, after current engine has changed"); + Services.search.addEngineWithDetails("MozSearch2", "", "", "", "GET", + "http://s.example.com/search2"); + engine = Services.search.getEngineByName("MozSearch2"); + notEqual(Services.search.currentEngine, engine, "New engine shouldn't be the current engine yet"); + Services.search.currentEngine = engine; + yield check_autocomplete({ + search: "mozilla", + searchParam: "enable-actions", + matches: [ { uri: makeActionURI("searchengine", {engineName: "MozSearch2", input: "mozilla", searchQuery: "mozilla"}), title: "MozSearch2" }, ] + }); + yield cleanup(); }); \ No newline at end of file