From 5ef00624f9fc93f80931e0a9b5e109b139158110 Mon Sep 17 00:00:00 2001 From: Tim Taubert Date: Mon, 17 Feb 2014 13:45:15 +0100 Subject: [PATCH 01/44] Bug 973559 - Fix baseURI serialization for srcdoc frames r=billm --- browser/components/sessionstore/src/SessionHistory.jsm | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/components/sessionstore/src/SessionHistory.jsm b/browser/components/sessionstore/src/SessionHistory.jsm index 334c572be92e..2de460bde7e3 100644 --- a/browser/components/sessionstore/src/SessionHistory.jsm +++ b/browser/components/sessionstore/src/SessionHistory.jsm @@ -185,7 +185,7 @@ let SessionHistoryInternal = { entry.isSrcdocEntry = shEntry.isSrcdocEntry; if (shEntry.baseURI) - entry.baseURI = shEntry.baseURI; + entry.baseURI = shEntry.baseURI.spec; if (shEntry.contentType) entry.contentType = shEntry.contentType; @@ -332,7 +332,7 @@ let SessionHistoryInternal = { if (entry.isSrcdocEntry) shEntry.srcdocData = entry.srcdocData; if (entry.baseURI) - shEntry.baseURI = entry.baseURI; + shEntry.baseURI = Utils.makeURI(entry.baseURI); if (entry.cacheKey) { var cacheKey = Cc["@mozilla.org/supports-PRUint32;1"]. From d83030d79ec9544dc4a51b13fd73aa32bdefd57e Mon Sep 17 00:00:00 2001 From: Tim Taubert Date: Sun, 16 Feb 2014 19:32:32 +0100 Subject: [PATCH 02/44] Bug 863514 - Don't disable gestures in e10s even if they're partially broken r=billm --- browser/base/content/browser-gestureSupport.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/browser/base/content/browser-gestureSupport.js b/browser/base/content/browser-gestureSupport.js index 1f405e607055..b4fed8080c65 100644 --- a/browser/base/content/browser-gestureSupport.js +++ b/browser/base/content/browser-gestureSupport.js @@ -27,10 +27,6 @@ let gGestureSupport = { * True to add/init listeners and false to remove/uninit */ init: function GS_init(aAddListener) { - // Bug 863514 - Make gesture support work in electrolysis - if (gMultiProcessBrowser) - return; - const gestureEvents = ["SwipeGestureStart", "SwipeGestureUpdate", "SwipeGestureEnd", "SwipeGesture", "MagnifyGestureStart", "MagnifyGestureUpdate", "MagnifyGesture", From a682dda2c287642add6a3f9ed046fc6c85ca1bfb Mon Sep 17 00:00:00 2001 From: Tim Taubert Date: Tue, 21 Jan 2014 00:55:24 +0100 Subject: [PATCH 03/44] Bug 895359 - Switch about:newtab to Promise.jsm and remove remaining callback r=jaws --- browser/base/content/newtab/newTab.js | 2 +- browser/base/content/newtab/sites.js | 5 +- .../base/content/newtab/transformations.js | 141 +++++++----------- browser/base/content/newtab/updater.js | 93 +++++------- 4 files changed, 90 insertions(+), 151 deletions(-) diff --git a/browser/base/content/newtab/newTab.js b/browser/base/content/newtab/newTab.js index 2ca2ff433c01..ebe043794830 100644 --- a/browser/base/content/newtab/newTab.js +++ b/browser/base/content/newtab/newTab.js @@ -12,7 +12,7 @@ Cu.import("resource://gre/modules/Services.jsm"); Cu.import("resource://gre/modules/PageThumbs.jsm"); Cu.import("resource://gre/modules/BackgroundPageThumbs.jsm"); Cu.import("resource://gre/modules/NewTabUtils.jsm"); -Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js"); +Cu.import("resource://gre/modules/Promise.jsm"); XPCOMUtils.defineLazyModuleGetter(this, "Rect", "resource://gre/modules/Geometry.jsm"); diff --git a/browser/base/content/newtab/sites.js b/browser/base/content/newtab/sites.js index 156ea91baab4..3ec4f956d23f 100644 --- a/browser/base/content/newtab/sites.js +++ b/browser/base/content/newtab/sites.js @@ -60,7 +60,7 @@ Site.prototype = { }, /** - * Unpins the site and calls the given callback when done. + * Unpins the site. */ unpin: function Site_unpin() { if (this.isPinned()) { @@ -79,8 +79,7 @@ Site.prototype = { }, /** - * Blocks the site (removes it from the grid) and calls the given callback - * when done. + * Blocks the site (removes it from the grid). */ block: function Site_block() { if (!gBlockedLinks.isBlocked(this._link)) { diff --git a/browser/base/content/newtab/transformations.js b/browser/base/content/newtab/transformations.js index 7976b12f9620..3e711c90e307 100644 --- a/browser/base/content/newtab/transformations.js +++ b/browser/base/content/newtab/transformations.js @@ -38,46 +38,24 @@ let gTransformation = { return new Rect(left + scrollX, top + scrollY, width, height); }, - /** - * Fades a given node from zero to full opacity. - * @param aNode The node to fade. - * @param aCallback The callback to call when finished. - */ - fadeNodeIn: function Transformation_fadeNodeIn(aNode, aCallback) { - this._setNodeOpacity(aNode, 1, function () { - // Clear the style property. - aNode.style.opacity = ""; - - if (aCallback) - aCallback(); - }); - }, - - /** - * Fades a given node from full to zero opacity. - * @param aNode The node to fade. - * @param aCallback The callback to call when finished. - */ - fadeNodeOut: function Transformation_fadeNodeOut(aNode, aCallback) { - this._setNodeOpacity(aNode, 0, aCallback); - }, - /** * Fades a given site from zero to full opacity. * @param aSite The site to fade. - * @param aCallback The callback to call when finished. */ - showSite: function Transformation_showSite(aSite, aCallback) { - this.fadeNodeIn(aSite.node, aCallback); + showSite: function (aSite) { + let node = aSite.node; + return this._setNodeOpacity(node, 1).then(() => { + // Clear the style property. + node.style.opacity = ""; + }); }, /** * Fades a given site from full to zero opacity. * @param aSite The site to fade. - * @param aCallback The callback to call when finished. */ - hideSite: function Transformation_hideSite(aSite, aCallback) { - this.fadeNodeOut(aSite.node, aCallback); + hideSite: function (aSite) { + return this._setNodeOpacity(aSite.node, 0); }, /** @@ -129,22 +107,11 @@ let gTransformation = { * @param aTarget The slide target. * @param aOptions Set of options (see below). * unfreeze - unfreeze the site after sliding - * callback - the callback to call when finished */ - slideSiteTo: function Transformation_slideSiteTo(aSite, aTarget, aOptions) { + slideSiteTo: function (aSite, aTarget, aOptions) { let currentPosition = this.getNodePosition(aSite.node); let targetPosition = this.getNodePosition(aTarget.node) - let callback = aOptions && aOptions.callback; - - let self = this; - - function finish() { - if (aOptions && aOptions.unfreeze) - self.unfreezeSitePosition(aSite); - - if (callback) - callback(); - } + let promise; // We need to take the width of a cell's border into account. targetPosition.left += this._cellBorderWidths.left; @@ -153,11 +120,17 @@ let gTransformation = { // Nothing to do here if the positions already match. if (currentPosition.left == targetPosition.left && currentPosition.top == targetPosition.top) { - finish(); + promise = Promise.resolve(); } else { this.setSitePosition(aSite, targetPosition); - this._whenTransitionEnded(aSite.node, ["left", "top"], finish); + promise = this._whenTransitionEnded(aSite.node, ["left", "top"]); } + + if (aOptions && aOptions.unfreeze) { + promise = promise.then(() => this.unfreezeSitePosition(aSite)); + } + + return promise; }, /** @@ -166,55 +139,50 @@ let gTransformation = { * @param aSites An array of sites to rearrange. * @param aOptions Set of options (see below). * unfreeze - unfreeze the site after rearranging - * callback - the callback to call when finished */ - rearrangeSites: function Transformation_rearrangeSites(aSites, aOptions) { - let batch = []; + rearrangeSites: function (aSites, aOptions) { + let self = this; let cells = gGrid.cells; - let callback = aOptions && aOptions.callback; let unfreeze = aOptions && aOptions.unfreeze; - aSites.forEach(function (aSite, aIndex) { - // Do not re-arrange empty cells or the dragged site. - if (!aSite || aSite == gDrag.draggedSite) - return; + function* promises() { + let index = 0; - let deferred = Promise.defer(); - batch.push(deferred.promise); - let cb = function () deferred.resolve(); + for (let site of aSites) { + if (site && site !== gDrag.draggedSite) { + if (!cells[index]) { + // The site disappeared from the grid, hide it. + yield self.hideSite(site); + } else if (self._getNodeOpacity(site.node) != 1) { + // The site disappeared before but is now back, show it. + yield self.showSite(site); + } else { + // The site's position has changed, move it around. + yield self._moveSite(site, index, {unfreeze: unfreeze}); + } + } + index++; + } + } - if (!cells[aIndex]) - // The site disappeared from the grid, hide it. - this.hideSite(aSite, cb); - else if (this._getNodeOpacity(aSite.node) != 1) - // The site disappeared before but is now back, show it. - this.showSite(aSite, cb); - else - // The site's position has changed, move it around. - this._moveSite(aSite, aIndex, {unfreeze: unfreeze, callback: cb}); - }, this); - - let wait = Promise.promised(function () callback && callback()); - wait.apply(null, batch); + return Promise.all([p for (p of promises())]); }, /** - * Listens for the 'transitionend' event on a given node and calls the given - * callback. + * Listens for the 'transitionend' event on a given node. * @param aNode The node that is transitioned. * @param aProperties The properties we'll wait to be transitioned. - * @param aCallback The callback to call when finished. */ - _whenTransitionEnded: - function Transformation_whenTransitionEnded(aNode, aProperties, aCallback) { - + _whenTransitionEnded: function (aNode, aProperties) { + let deferred = Promise.defer(); let props = new Set(aProperties); aNode.addEventListener("transitionend", function onEnd(e) { if (props.has(e.propertyName)) { aNode.removeEventListener("transitionend", onEnd); - aCallback(); + deferred.resolve(); } }); + return deferred.promise; }, /** @@ -231,21 +199,14 @@ let gTransformation = { * Sets a given node's opacity. * @param aNode The node to set the opacity value for. * @param aOpacity The opacity value to set. - * @param aCallback The callback to call when finished. */ - _setNodeOpacity: - function Transformation_setNodeOpacity(aNode, aOpacity, aCallback) { - + _setNodeOpacity: function (aNode, aOpacity) { if (this._getNodeOpacity(aNode) == aOpacity) { - if (aCallback) - aCallback(); - } else { - if (aCallback) { - this._whenTransitionEnded(aNode, ["opacity"], aCallback); - } - - aNode.style.opacity = aOpacity; + return Promise.resolve(); } + + aNode.style.opacity = aOpacity; + return this._whenTransitionEnded(aNode, ["opacity"]); }, /** @@ -254,9 +215,9 @@ let gTransformation = { * @param aIndex The target cell's index. * @param aOptions Options that are directly passed to slideSiteTo(). */ - _moveSite: function Transformation_moveSite(aSite, aIndex, aOptions) { + _moveSite: function (aSite, aIndex, aOptions) { this.freezeSitePosition(aSite); - this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions); + return this.slideSiteTo(aSite, gGrid.cells[aIndex], aOptions); }, /** diff --git a/browser/base/content/newtab/updater.js b/browser/base/content/newtab/updater.js index 7b483e037f2c..8225906d1b4f 100644 --- a/browser/base/content/newtab/updater.js +++ b/browser/base/content/newtab/updater.js @@ -12,32 +12,29 @@ let gUpdater = { /** * Updates the current grid according to its pinned and blocked sites. * This removes old, moves existing and creates new sites to fill gaps. - * @param aCallback The callback to call when finished. */ - updateGrid: function Updater_updateGrid(aCallback) { + updateGrid: function Updater_updateGrid() { let links = gLinks.getLinks().slice(0, gGrid.cells.length); // Find all sites that remain in the grid. let sites = this._findRemainingSites(links); - let self = this; - // Remove sites that are no longer in the grid. - this._removeLegacySites(sites, function () { + this._removeLegacySites(sites).then(() => { // Freeze all site positions so that we can move their DOM nodes around // without any visual impact. - self._freezeSitePositions(sites); + this._freezeSitePositions(sites); // Move the sites' DOM nodes to their new position in the DOM. This will // have no visual effect as all the sites have been frozen and will // remain in their current position. - self._moveSiteNodes(sites); + this._moveSiteNodes(sites); // Now it's time to animate the sites actually moving to their new // positions. - self._rearrangeSites(sites, function () { + this._rearrangeSites(sites).then(() => { // Try to fill empty cells and finish. - self._fillEmptyCells(links, aCallback); + this._fillEmptyCells(links); // Update other pages that might be open to keep them synced. gAllPages.update(gPage); @@ -112,75 +109,57 @@ let gUpdater = { /** * Rearranges the given sites and slides them to their new positions. * @param aSites The array of sites to re-arrange. - * @param aCallback The callback to call when finished. */ - _rearrangeSites: function Updater_rearrangeSites(aSites, aCallback) { - let options = {callback: aCallback, unfreeze: true}; - gTransformation.rearrangeSites(aSites, options); + _rearrangeSites: function (aSites) { + return gTransformation.rearrangeSites(aSites, {unfreeze: true}); }, /** * Removes all sites from the grid that are not in the given links array or * exceed the grid. * @param aSites The array of sites remaining in the grid. - * @param aCallback The callback to call when finished. */ - _removeLegacySites: function Updater_removeLegacySites(aSites, aCallback) { - let batch = []; + _removeLegacySites: function (aSites) { + let remainingSites = new Set(aSites); - // Delete sites that were removed from the grid. - gGrid.sites.forEach(function (aSite) { - // The site must be valid and not in the current grid. - if (!aSite || aSites.indexOf(aSite) != -1) - return; + function* promises() { + for (let site of gGrid.sites) { + // The site must be valid and not in the current grid. + if (site && !remainingSites.has(site)) { + // Hide the site and remove it from the DOM. + let remove = site.node.remove.bind(site.node); + yield gTransformation.hideSite(site).then(remove); + } + } + } - let deferred = Promise.defer(); - batch.push(deferred.promise); - - // Fade out the to-be-removed site. - gTransformation.hideSite(aSite, function () { - let node = aSite.node; - - // Remove the site from the DOM. - node.parentNode.removeChild(node); - deferred.resolve(); - }); - }); - - let wait = Promise.promised(aCallback); - wait.apply(null, batch); + return Promise.all([p for (p of promises())]); }, /** * Tries to fill empty cells with new links if available. * @param aLinks The array of links. - * @param aCallback The callback to call when finished. */ - _fillEmptyCells: function Updater_fillEmptyCells(aLinks, aCallback) { + _fillEmptyCells: function (aLinks) { let {cells, sites} = gGrid; - let batch = []; + let index = 0; // Find empty cells and fill them. - sites.forEach(function (aSite, aIndex) { - if (aSite || !aLinks[aIndex]) - return; + for (let site of sites) { + if (!site && aLinks[index]) { + // Create the new site and fade it in. + site = gGrid.createSite(aLinks[index], cells[index]); - let deferred = Promise.defer(); - batch.push(deferred.promise); + // Set the site's initial opacity to zero. + site.node.style.opacity = 0; - // Create the new site and fade it in. - let site = gGrid.createSite(aLinks[aIndex], cells[aIndex]); + // Flush all style changes for the dynamically inserted site to make + // the fade-in transition work. + window.getComputedStyle(site.node).opacity; + gTransformation.showSite(site); + } - // Set the site's initial opacity to zero. - site.node.style.opacity = 0; - - // Flush all style changes for the dynamically inserted site to make - // the fade-in transition work. - window.getComputedStyle(site.node).opacity; - gTransformation.showSite(site, function () deferred.resolve()); - }); - - let wait = Promise.promised(aCallback); - wait.apply(null, batch); + index++; + } } }; From ede1c3e8e1705dc6e91c16ae635e182a0db1468a Mon Sep 17 00:00:00 2001 From: Tim Taubert Date: Fri, 14 Feb 2014 11:15:51 +0100 Subject: [PATCH 04/44] Bug 895359 - Pass draggedSite to updateGrid() to avoid intermittent Windows failures r=jaws --- browser/base/content/newtab/drop.js | 4 ++-- browser/base/content/newtab/transformations.js | 5 +++-- browser/base/content/newtab/updater.js | 13 +++---------- 3 files changed, 8 insertions(+), 14 deletions(-) diff --git a/browser/base/content/newtab/drop.js b/browser/base/content/newtab/drop.js index 7363396e81e4..57e2d75af10c 100644 --- a/browser/base/content/newtab/drop.js +++ b/browser/base/content/newtab/drop.js @@ -57,7 +57,7 @@ let gDrop = { this._cancelDelayedArrange(); // Update the grid and move all sites to their new places. - gUpdater.updateGrid(); + gUpdater.updateGrid(gDrag.draggedSite); }, /** @@ -145,6 +145,6 @@ let gDrop = { if (aCell) sites = gDropPreview.rearrange(aCell); - gTransformation.rearrangeSites(sites, {unfreeze: !aCell}); + gTransformation.rearrangeSites(sites, gDrag.draggedSite, {unfreeze: !aCell}); } }; diff --git a/browser/base/content/newtab/transformations.js b/browser/base/content/newtab/transformations.js index 3e711c90e307..8a4eb951c4b9 100644 --- a/browser/base/content/newtab/transformations.js +++ b/browser/base/content/newtab/transformations.js @@ -137,10 +137,11 @@ let gTransformation = { * Rearranges a given array of sites and moves them to their new positions or * fades in/out new/removed sites. * @param aSites An array of sites to rearrange. + * @param aDraggedSite The currently dragged site, may be null. * @param aOptions Set of options (see below). * unfreeze - unfreeze the site after rearranging */ - rearrangeSites: function (aSites, aOptions) { + rearrangeSites: function (aSites, aDraggedSite, aOptions) { let self = this; let cells = gGrid.cells; let unfreeze = aOptions && aOptions.unfreeze; @@ -149,7 +150,7 @@ let gTransformation = { let index = 0; for (let site of aSites) { - if (site && site !== gDrag.draggedSite) { + if (site && site !== aDraggedSite) { if (!cells[index]) { // The site disappeared from the grid, hide it. yield self.hideSite(site); diff --git a/browser/base/content/newtab/updater.js b/browser/base/content/newtab/updater.js index 8225906d1b4f..563384f8f63d 100644 --- a/browser/base/content/newtab/updater.js +++ b/browser/base/content/newtab/updater.js @@ -13,7 +13,7 @@ let gUpdater = { * Updates the current grid according to its pinned and blocked sites. * This removes old, moves existing and creates new sites to fill gaps. */ - updateGrid: function Updater_updateGrid() { + updateGrid: function Updater_updateGrid(draggedSite = null) { let links = gLinks.getLinks().slice(0, gGrid.cells.length); // Find all sites that remain in the grid. @@ -32,7 +32,8 @@ let gUpdater = { // Now it's time to animate the sites actually moving to their new // positions. - this._rearrangeSites(sites).then(() => { + let opts = {unfreeze: true}; + gTransformation.rearrangeSites(sites, draggedSite, opts).then(() => { // Try to fill empty cells and finish. this._fillEmptyCells(links); @@ -106,14 +107,6 @@ let gUpdater = { }, this); }, - /** - * Rearranges the given sites and slides them to their new positions. - * @param aSites The array of sites to re-arrange. - */ - _rearrangeSites: function (aSites) { - return gTransformation.rearrangeSites(aSites, {unfreeze: true}); - }, - /** * Removes all sites from the grid that are not in the given links array or * exceed the grid. From d6c43911f6902815744f119c5c9c8533be856f85 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 21 Feb 2014 10:33:58 +0000 Subject: [PATCH 05/44] Bug 972306 - Rename HomePanels:Remove to HomePanels:Uninstall (r=margaret) --- .../base/home/HomeConfigInvalidator.java | 20 +++++++++---------- mobile/android/modules/Home.jsm | 2 +- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/mobile/android/base/home/HomeConfigInvalidator.java b/mobile/android/base/home/HomeConfigInvalidator.java index c8512b3acf32..49c0baa79d1f 100644 --- a/mobile/android/base/home/HomeConfigInvalidator.java +++ b/mobile/android/base/home/HomeConfigInvalidator.java @@ -38,13 +38,13 @@ public class HomeConfigInvalidator implements GeckoEventListener { private static final int PANEL_INFO_TIMEOUT_MSEC = 1000; private static final String EVENT_HOMEPANELS_INSTALL = "HomePanels:Install"; - private static final String EVENT_HOMEPANELS_REMOVE = "HomePanels:Remove"; + private static final String EVENT_HOMEPANELS_UNINSTALL = "HomePanels:Uninstall"; private static final String EVENT_HOMEPANELS_REFRESH = "HomePanels:Refresh"; private static final String JSON_KEY_PANEL = "panel"; private enum ChangeType { - REMOVE, + UNINSTALL, INSTALL, REFRESH } @@ -74,7 +74,7 @@ public class HomeConfigInvalidator implements GeckoEventListener { mHomeConfig = HomeConfig.getDefault(context); GeckoAppShell.getEventDispatcher().registerEventListener(EVENT_HOMEPANELS_INSTALL, this); - GeckoAppShell.getEventDispatcher().registerEventListener(EVENT_HOMEPANELS_REMOVE, this); + GeckoAppShell.getEventDispatcher().registerEventListener(EVENT_HOMEPANELS_UNINSTALL, this); GeckoAppShell.getEventDispatcher().registerEventListener(EVENT_HOMEPANELS_REFRESH, this); } @@ -91,9 +91,9 @@ public class HomeConfigInvalidator implements GeckoEventListener { if (event.equals(EVENT_HOMEPANELS_INSTALL)) { Log.d(LOGTAG, EVENT_HOMEPANELS_INSTALL); handlePanelInstall(panelConfig); - } else if (event.equals(EVENT_HOMEPANELS_REMOVE)) { - Log.d(LOGTAG, EVENT_HOMEPANELS_REMOVE); - handlePanelRemove(panelConfig); + } else if (event.equals(EVENT_HOMEPANELS_UNINSTALL)) { + Log.d(LOGTAG, EVENT_HOMEPANELS_UNINSTALL); + handlePanelUninstall(panelConfig); } else if (event.equals(EVENT_HOMEPANELS_REFRESH)) { Log.d(LOGTAG, EVENT_HOMEPANELS_REFRESH); handlePanelRefresh(panelConfig); @@ -116,9 +116,9 @@ public class HomeConfigInvalidator implements GeckoEventListener { /** * Runs in the gecko thread. */ - private void handlePanelRemove(PanelConfig panelConfig) { - mPendingChanges.offer(new ConfigChange(ChangeType.REMOVE, panelConfig)); - Log.d(LOGTAG, "handlePanelRemove: " + mPendingChanges.size()); + private void handlePanelUninstall(PanelConfig panelConfig) { + mPendingChanges.offer(new ConfigChange(ChangeType.UNINSTALL, panelConfig)); + Log.d(LOGTAG, "handlePanelUninstall: " + mPendingChanges.size()); scheduleInvalidation(); } @@ -175,7 +175,7 @@ public class HomeConfigInvalidator implements GeckoEventListener { final PanelConfig panelConfig = pendingChange.target; switch (pendingChange.type) { - case REMOVE: + case UNINSTALL: if (panelConfigs.remove(panelConfig)) { Log.d(LOGTAG, "executePendingChanges: removed panel " + panelConfig.getId()); } diff --git a/mobile/android/modules/Home.jsm b/mobile/android/modules/Home.jsm index cdf730189b01..0ca6f9a4cd34 100644 --- a/mobile/android/modules/Home.jsm +++ b/mobile/android/modules/Home.jsm @@ -321,7 +321,7 @@ let HomePanels = (function () { delete _panels[id]; sendMessageToJava({ - type: "HomePanels:Remove", + type: "HomePanels:Uninstall", panel: _panelToJSON(panel) }); } From 1d4fe2879ceaf9aed3b59bc3f8665398a058c778 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 21 Feb 2014 10:33:59 +0000 Subject: [PATCH 06/44] Bug 972306 - Change HomePanels:Uninstall to take an id (r=margaret) --- .../base/home/HomeConfigInvalidator.java | 50 +++++++++++++------ mobile/android/modules/Home.jsm | 3 +- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/mobile/android/base/home/HomeConfigInvalidator.java b/mobile/android/base/home/HomeConfigInvalidator.java index 49c0baa79d1f..c2e3d652d299 100644 --- a/mobile/android/base/home/HomeConfigInvalidator.java +++ b/mobile/android/base/home/HomeConfigInvalidator.java @@ -42,6 +42,7 @@ public class HomeConfigInvalidator implements GeckoEventListener { private static final String EVENT_HOMEPANELS_REFRESH = "HomePanels:Refresh"; private static final String JSON_KEY_PANEL = "panel"; + private static final String JSON_KEY_PANEL_ID = "id"; private enum ChangeType { UNINSTALL, @@ -51,9 +52,9 @@ public class HomeConfigInvalidator implements GeckoEventListener { private static class ConfigChange { private final ChangeType type; - private final PanelConfig target; + private final Object target; - public ConfigChange(ChangeType type, PanelConfig target) { + public ConfigChange(ChangeType type, Object target) { this.type = type; this.target = target; } @@ -85,24 +86,27 @@ public class HomeConfigInvalidator implements GeckoEventListener { @Override public void handleMessage(String event, JSONObject message) { try { - final JSONObject json = message.getJSONObject(JSON_KEY_PANEL); - final PanelConfig panelConfig = new PanelConfig(json); - if (event.equals(EVENT_HOMEPANELS_INSTALL)) { Log.d(LOGTAG, EVENT_HOMEPANELS_INSTALL); - handlePanelInstall(panelConfig); + handlePanelInstall(createPanelConfigFromMessage(message)); } else if (event.equals(EVENT_HOMEPANELS_UNINSTALL)) { Log.d(LOGTAG, EVENT_HOMEPANELS_UNINSTALL); - handlePanelUninstall(panelConfig); + final String panelId = message.getString(JSON_KEY_PANEL_ID); + handlePanelUninstall(panelId); } else if (event.equals(EVENT_HOMEPANELS_REFRESH)) { Log.d(LOGTAG, EVENT_HOMEPANELS_REFRESH); - handlePanelRefresh(panelConfig); + handlePanelRefresh(createPanelConfigFromMessage(message)); } } catch (Exception e) { Log.e(LOGTAG, "Failed to handle event " + event, e); } } + private PanelConfig createPanelConfigFromMessage(JSONObject message) throws JSONException { + final JSONObject json = message.getJSONObject(JSON_KEY_PANEL); + return new PanelConfig(json); + } + /** * Runs in the gecko thread. */ @@ -116,8 +120,8 @@ public class HomeConfigInvalidator implements GeckoEventListener { /** * Runs in the gecko thread. */ - private void handlePanelUninstall(PanelConfig panelConfig) { - mPendingChanges.offer(new ConfigChange(ChangeType.UNINSTALL, panelConfig)); + private void handlePanelUninstall(String panelId) { + mPendingChanges.offer(new ConfigChange(ChangeType.UNINSTALL, panelId)); Log.d(LOGTAG, "handlePanelUninstall: " + mPendingChanges.size()); scheduleInvalidation(); @@ -164,6 +168,16 @@ public class HomeConfigInvalidator implements GeckoEventListener { return false; } + private PanelConfig findPanelConfigWithId(List panelConfigs, String panelId) { + for (PanelConfig panelConfig : panelConfigs) { + if (panelConfig.getId().equals(panelId)) { + return panelConfig; + } + } + + return null; + } + /** * Runs in the background thread. */ @@ -172,23 +186,28 @@ public class HomeConfigInvalidator implements GeckoEventListener { while (!mPendingChanges.isEmpty()) { final ConfigChange pendingChange = mPendingChanges.poll(); - final PanelConfig panelConfig = pendingChange.target; switch (pendingChange.type) { - case UNINSTALL: - if (panelConfigs.remove(panelConfig)) { + case UNINSTALL: { + final String panelId = (String) pendingChange.target; + final PanelConfig panelConfig = findPanelConfigWithId(panelConfigs, panelId); + if (panelConfig != null && panelConfigs.remove(panelConfig)) { Log.d(LOGTAG, "executePendingChanges: removed panel " + panelConfig.getId()); } break; + } - case INSTALL: + case INSTALL: { + final PanelConfig panelConfig = (PanelConfig) pendingChange.target; if (!replacePanelConfig(panelConfigs, panelConfig)) { panelConfigs.add(panelConfig); Log.d(LOGTAG, "executePendingChanges: added panel " + panelConfig.getId()); } break; + } - case REFRESH: + case REFRESH: { + final PanelConfig panelConfig = (PanelConfig) pendingChange.target; if (panelConfig != null) { if (!replacePanelConfig(panelConfigs, panelConfig)) { Log.w(LOGTAG, "Tried to refresh non-existing panel " + panelConfig.getId()); @@ -197,6 +216,7 @@ public class HomeConfigInvalidator implements GeckoEventListener { shouldRefreshAll = true; } break; + } } } diff --git a/mobile/android/modules/Home.jsm b/mobile/android/modules/Home.jsm index 0ca6f9a4cd34..290daca02142 100644 --- a/mobile/android/modules/Home.jsm +++ b/mobile/android/modules/Home.jsm @@ -317,12 +317,11 @@ let HomePanels = (function () { throw "Home.panels: Panel doesn't exist: id = " + id; } - let panel = _panels[id]; delete _panels[id]; sendMessageToJava({ type: "HomePanels:Uninstall", - panel: _panelToJSON(panel) + id: id }); } }); From 06b64c8310b6f86f498f5929548554decca0e601 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 21 Feb 2014 10:33:59 +0000 Subject: [PATCH 07/44] Bug 972306 - Rename HomePanels:Refresh to HomePanels:Update (r=margaret) --- .../base/home/HomeConfigInvalidator.java | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/mobile/android/base/home/HomeConfigInvalidator.java b/mobile/android/base/home/HomeConfigInvalidator.java index c2e3d652d299..4d4d86365691 100644 --- a/mobile/android/base/home/HomeConfigInvalidator.java +++ b/mobile/android/base/home/HomeConfigInvalidator.java @@ -39,7 +39,7 @@ public class HomeConfigInvalidator implements GeckoEventListener { private static final String EVENT_HOMEPANELS_INSTALL = "HomePanels:Install"; private static final String EVENT_HOMEPANELS_UNINSTALL = "HomePanels:Uninstall"; - private static final String EVENT_HOMEPANELS_REFRESH = "HomePanels:Refresh"; + private static final String EVENT_HOMEPANELS_UPDATE = "HomePanels:Update"; private static final String JSON_KEY_PANEL = "panel"; private static final String JSON_KEY_PANEL_ID = "id"; @@ -47,7 +47,7 @@ public class HomeConfigInvalidator implements GeckoEventListener { private enum ChangeType { UNINSTALL, INSTALL, - REFRESH + UPDATE } private static class ConfigChange { @@ -76,11 +76,11 @@ public class HomeConfigInvalidator implements GeckoEventListener { GeckoAppShell.getEventDispatcher().registerEventListener(EVENT_HOMEPANELS_INSTALL, this); GeckoAppShell.getEventDispatcher().registerEventListener(EVENT_HOMEPANELS_UNINSTALL, this); - GeckoAppShell.getEventDispatcher().registerEventListener(EVENT_HOMEPANELS_REFRESH, this); + GeckoAppShell.getEventDispatcher().registerEventListener(EVENT_HOMEPANELS_UPDATE, this); } public void refreshAll() { - handlePanelRefresh(null); + handlePanelUpdate(null); } @Override @@ -93,9 +93,9 @@ public class HomeConfigInvalidator implements GeckoEventListener { Log.d(LOGTAG, EVENT_HOMEPANELS_UNINSTALL); final String panelId = message.getString(JSON_KEY_PANEL_ID); handlePanelUninstall(panelId); - } else if (event.equals(EVENT_HOMEPANELS_REFRESH)) { - Log.d(LOGTAG, EVENT_HOMEPANELS_REFRESH); - handlePanelRefresh(createPanelConfigFromMessage(message)); + } else if (event.equals(EVENT_HOMEPANELS_UPDATE)) { + Log.d(LOGTAG, EVENT_HOMEPANELS_UPDATE); + handlePanelUpdate(createPanelConfigFromMessage(message)); } } catch (Exception e) { Log.e(LOGTAG, "Failed to handle event " + event, e); @@ -128,14 +128,14 @@ public class HomeConfigInvalidator implements GeckoEventListener { } /** - * Schedules a panel refresh in HomeConfig. Runs in the gecko thread. + * Schedules a panel update in HomeConfig. Runs in the gecko thread. * * @param panelConfig the target PanelConfig instance or NULL to refresh * all HomeConfig entries. */ - private void handlePanelRefresh(PanelConfig panelConfig) { - mPendingChanges.offer(new ConfigChange(ChangeType.REFRESH, panelConfig)); - Log.d(LOGTAG, "handlePanelRefresh: " + mPendingChanges.size()); + private void handlePanelUpdate(PanelConfig panelConfig) { + mPendingChanges.offer(new ConfigChange(ChangeType.UPDATE, panelConfig)); + Log.d(LOGTAG, "handlePanelUpdate: " + mPendingChanges.size()); scheduleInvalidation(); } @@ -206,11 +206,11 @@ public class HomeConfigInvalidator implements GeckoEventListener { break; } - case REFRESH: { + case UPDATE: { final PanelConfig panelConfig = (PanelConfig) pendingChange.target; if (panelConfig != null) { if (!replacePanelConfig(panelConfigs, panelConfig)) { - Log.w(LOGTAG, "Tried to refresh non-existing panel " + panelConfig.getId()); + Log.w(LOGTAG, "Tried to update non-existing panel " + panelConfig.getId()); } } else { shouldRefreshAll = true; From 2a9f9f127337300b7ae4703b445378f7f3b0682f Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 21 Feb 2014 10:33:59 +0000 Subject: [PATCH 08/44] Bug 972306 - Restructure the Home.panels API for clarity (r=margaret) --- mobile/android/modules/Home.jsm | 78 ++++++++++++++++----------------- 1 file changed, 37 insertions(+), 41 deletions(-) diff --git a/mobile/android/modules/Home.jsm b/mobile/android/modules/Home.jsm index 290daca02142..1b2fc0d20104 100644 --- a/mobile/android/modules/Home.jsm +++ b/mobile/android/modules/Home.jsm @@ -210,6 +210,12 @@ let HomePanels = (function () { return false; }; + let _assertPanelExists = function(id) { + if (!(id in _panels)) { + throw "Home.panels: Panel doesn't exist: id = " + id; + } + }; + return Object.freeze({ // Valid layouts for a panel. Layout: Object.freeze({ @@ -222,12 +228,6 @@ let HomePanels = (function () { GRID: "grid" }), - // Valid actions for a panel. - Action: Object.freeze({ - INSTALL: "install", - REFRESH: "refresh" - }), - // Valid item types for a panel view. Item: Object.freeze({ ARTICLE: "article", @@ -240,18 +240,16 @@ let HomePanels = (function () { INTENT: "intent" }), - add: function(options) { + register: function(options) { let panel = new Panel(options); - if (!panel.id || !panel.title) { - throw "Home.panels: Can't create a home panel without an id and title!"; + + // Bail if the panel already exists + if (panel.id in _panels) { + throw "Home.panels: Panel already exists: id = " + panel.id; } - let action = options.action; - - // Bail if the panel already exists, except when we're refreshing - // an existing panel instance. - if (panel.id in _panels && action != this.Action.REFRESH) { - throw "Home.panels: Panel already exists: id = " + panel.id; + if (!panel.id || !panel.title) { + throw "Home.panels: Can't create a home panel without an id and title!"; } if (!_valueExists(this.Layout, panel.layout)) { @@ -288,41 +286,39 @@ let HomePanels = (function () { } _panels[panel.id] = panel; - - if (action) { - let messageType; - - switch(action) { - case this.Action.INSTALL: - messageType = "HomePanels:Install"; - break; - - case this.Action.REFRESH: - messageType = "HomePanels:Refresh"; - break; - - default: - throw "Home.panels: Invalid action for panel: panel.id = " + panel.id + ", action = " + action; - } - - sendMessageToJava({ - type: messageType, - panel: _panelToJSON(panel) - }); - } }, - remove: function(id) { - if (!(id in _panels)) { - throw "Home.panels: Panel doesn't exist: id = " + id; - } + unregister: function(id) { + _assertPanelExists(id); delete _panels[id]; + }, + + install: function(id) { + _assertPanelExists(id); + + sendMessageToJava({ + type: "HomePanels:Install", + panel: _panelToJSON(_panels[id]) + }); + }, + + uninstall: function(id) { + _assertPanelExists(id); sendMessageToJava({ type: "HomePanels:Uninstall", id: id }); + }, + + update: function(id) { + _assertPanelExists(id); + + sendMessageToJava({ + type: "HomePanels:Update", + panel: _panelToJSON(_panels[id]) + }); } }); })(); From 95ae663b053ddfdfce415825df14d91c24d543f1 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 21 Feb 2014 10:47:44 +0000 Subject: [PATCH 09/44] Bug 968172 - Don't invalidate HomePager in onLocaleReady (r=margaret) --- mobile/android/base/BrowserApp.java | 4 --- mobile/android/base/home/HomePager.java | 41 ++----------------------- 2 files changed, 2 insertions(+), 43 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 992a7b7964c6..10e1dc01b8f7 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -1641,10 +1641,6 @@ abstract public class BrowserApp extends GeckoApp public void onLocaleReady(final String locale) { super.onLocaleReady(locale); - if (mHomePager != null) { - mHomePager.invalidate(getSupportLoaderManager(), getSupportFragmentManager()); - } - if (mMenu != null) { mMenu.clear(); onCreateOptionsMenu(mMenu); diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index 39d108ecfb5c..8b4e79173237 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -49,9 +49,6 @@ public class HomePager extends ViewPager { private String mInitialPanelId; - // Whether or not we need to restart the loader when we show the HomePager. - private boolean mRestartLoader; - // Cached original ViewPager background. private final Drawable mOriginalBackground; @@ -165,35 +162,6 @@ public class HomePager extends ViewPager { super.addView(child, index, params); } - /** - * Invalidates the current configuration, redisplaying the HomePager if necessary. - */ - public void invalidate(LoaderManager lm, FragmentManager fm) { - // We need to restart the loader to load the new strings. - mRestartLoader = true; - - // If the HomePager is currently visible, redisplay it with the new strings. - if (isVisible()) { - redisplay(lm, fm); - } - } - - private void redisplay(LoaderManager lm, FragmentManager fm) { - final HomeAdapter adapter = (HomeAdapter) getAdapter(); - - // If mInitialPanelId is non-null, this means the HomePager hasn't - // finished loading its config yet. Simply re-show() with the - // current target panel. - final String currentPanelId; - if (mInitialPanelId != null) { - currentPanelId = mInitialPanelId; - } else { - currentPanelId = adapter.getPanelIdAtPosition(getCurrentItem()); - } - - show(lm, fm, currentPanelId, null); - } - /** * Loads and initializes the pager. * @@ -217,13 +185,8 @@ public class HomePager extends ViewPager { // list of panels in place. mTabStrip.setVisibility(View.INVISIBLE); - // Load list of panels from configuration. Restart the loader if necessary. - if (mRestartLoader) { - lm.restartLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks); - mRestartLoader = false; - } else { - lm.initLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks); - } + // Load list of panels from configuration + lm.initLoader(LOADER_ID_CONFIG, null, mConfigLoaderCallbacks); if (shouldAnimate) { animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() { From 9924fe2b2cbda7c4a8f2a853fc645b86dbab6a90 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 21 Feb 2014 10:47:44 +0000 Subject: [PATCH 10/44] Bug 968172 - Rename PREFS_KEY constant to PREFS_CONFIG_KEY (r=rnewman) --- mobile/android/base/home/HomeConfigPrefsBackend.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/mobile/android/base/home/HomeConfigPrefsBackend.java b/mobile/android/base/home/HomeConfigPrefsBackend.java index eeb8dc08347f..1e7c3b05d9bd 100644 --- a/mobile/android/base/home/HomeConfigPrefsBackend.java +++ b/mobile/android/base/home/HomeConfigPrefsBackend.java @@ -33,7 +33,7 @@ import java.util.List; class HomeConfigPrefsBackend implements HomeConfigBackend { private static final String LOGTAG = "GeckoHomeConfigBackend"; - private static final String PREFS_KEY = "home_panels"; + private static final String PREFS_CONFIG_KEY = "home_panels"; private final Context mContext; private PrefsListener mPrefsListener; @@ -104,7 +104,7 @@ class HomeConfigPrefsBackend implements HomeConfigBackend { @Override public List load() { final SharedPreferences prefs = getSharedPreferences(); - final String jsonString = prefs.getString(PREFS_KEY, null); + final String jsonString = prefs.getString(PREFS_CONFIG_KEY, null); final List panelConfigs; if (TextUtils.isEmpty(jsonString)) { @@ -135,7 +135,7 @@ class HomeConfigPrefsBackend implements HomeConfigBackend { final SharedPreferences.Editor editor = prefs.edit(); final String jsonString = jsonPanelConfigs.toString(); - editor.putString(PREFS_KEY, jsonString); + editor.putString(PREFS_CONFIG_KEY, jsonString); editor.commit(); } @@ -159,7 +159,7 @@ class HomeConfigPrefsBackend implements HomeConfigBackend { private class PrefsListener implements OnSharedPreferenceChangeListener { @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { - if (TextUtils.equals(key, PREFS_KEY)) { + if (TextUtils.equals(key, PREFS_CONFIG_KEY)) { mChangeListener.onChange(); } } From 448f168e5ae1401a8a76b6d93b59522f4919065f Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 21 Feb 2014 10:47:44 +0000 Subject: [PATCH 11/44] Bug 968172 - Ensure consistent locale string on onLocaleReady() calls (r=rnewman) --- mobile/android/base/LocaleManager.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/LocaleManager.java b/mobile/android/base/LocaleManager.java index 9cc57abbc53b..9ca7d085b4d9 100644 --- a/mobile/android/base/LocaleManager.java +++ b/mobile/android/base/LocaleManager.java @@ -235,11 +235,11 @@ public class LocaleManager { // Note that we don't tell Gecko about this. We notify Gecko when the // locale is set, not when we update Java. - updateLocale(localeCode); + final String resultant = updateLocale(localeCode); final long t2 = android.os.SystemClock.uptimeMillis(); Log.i(LOG_TAG, "Locale read and update took: " + (t2 - t1) + "ms."); - return localeCode; + return resultant; } /** From 379bbdc198b7f2b15fde3fdbdc9d07c90fcfaa65 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 21 Feb 2014 10:47:45 +0000 Subject: [PATCH 12/44] Bug 968172 - Trigger a full refresh when locale changes (r=rnewman) --- mobile/android/base/BrowserApp.java | 3 ++ mobile/android/base/home/HomeConfig.java | 5 ++++ .../base/home/HomeConfigInvalidator.java | 16 +++++++++-- .../base/home/HomeConfigPrefsBackend.java | 28 +++++++++++++++++++ 4 files changed, 49 insertions(+), 3 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 10e1dc01b8f7..f9ea47ae4e95 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -24,6 +24,7 @@ import org.mozilla.gecko.health.BrowserHealthReporter; import org.mozilla.gecko.health.HealthRecorder; import org.mozilla.gecko.health.SessionInformation; import org.mozilla.gecko.home.BrowserSearch; +import org.mozilla.gecko.home.HomeConfigInvalidator; import org.mozilla.gecko.home.HomePager; import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; import org.mozilla.gecko.home.SearchEngine; @@ -1641,6 +1642,8 @@ abstract public class BrowserApp extends GeckoApp public void onLocaleReady(final String locale) { super.onLocaleReady(locale); + HomeConfigInvalidator.getInstance().onLocaleReady(locale); + if (mMenu != null) { mMenu.clear(); onCreateOptionsMenu(mMenu); diff --git a/mobile/android/base/home/HomeConfig.java b/mobile/android/base/home/HomeConfig.java index 8a82bd9ea1db..14ff45b94e87 100644 --- a/mobile/android/base/home/HomeConfig.java +++ b/mobile/android/base/home/HomeConfig.java @@ -699,6 +699,7 @@ public final class HomeConfig { public interface HomeConfigBackend { public List load(); public void save(List entries); + public String getLocale(); public void setOnChangeListener(OnChangeListener listener); } @@ -718,6 +719,10 @@ public final class HomeConfig { return mBackend.load(); } + public String getLocale() { + return mBackend.getLocale(); + } + public void save(List panelConfigs) { mBackend.save(panelConfigs); } diff --git a/mobile/android/base/home/HomeConfigInvalidator.java b/mobile/android/base/home/HomeConfigInvalidator.java index 4d4d86365691..aad5bb0d30ac 100644 --- a/mobile/android/base/home/HomeConfigInvalidator.java +++ b/mobile/android/base/home/HomeConfigInvalidator.java @@ -17,6 +17,7 @@ import static org.mozilla.gecko.home.HomeConfig.createBuiltinPanelConfig; import android.content.Context; import android.os.Handler; +import android.text.TextUtils; import android.util.Log; import org.json.JSONException; @@ -79,8 +80,16 @@ public class HomeConfigInvalidator implements GeckoEventListener { GeckoAppShell.getEventDispatcher().registerEventListener(EVENT_HOMEPANELS_UPDATE, this); } - public void refreshAll() { - handlePanelUpdate(null); + public void onLocaleReady(final String locale) { + ThreadUtils.getBackgroundHandler().post(new Runnable() { + @Override + public void run() { + final String configLocale = mHomeConfig.getLocale(); + if (configLocale == null || !configLocale.equals(locale)) { + handlePanelUpdate(null); + } + } + }); } @Override @@ -128,7 +137,8 @@ public class HomeConfigInvalidator implements GeckoEventListener { } /** - * Schedules a panel update in HomeConfig. Runs in the gecko thread. + * Schedules a panel update in HomeConfig. Runs in the gecko or + * background thread. * * @param panelConfig the target PanelConfig instance or NULL to refresh * all HomeConfig entries. diff --git a/mobile/android/base/home/HomeConfigPrefsBackend.java b/mobile/android/base/home/HomeConfigPrefsBackend.java index 1e7c3b05d9bd..428f28980e99 100644 --- a/mobile/android/base/home/HomeConfigPrefsBackend.java +++ b/mobile/android/base/home/HomeConfigPrefsBackend.java @@ -29,11 +29,13 @@ import android.util.Log; import java.util.ArrayList; import java.util.EnumSet; import java.util.List; +import java.util.Locale; class HomeConfigPrefsBackend implements HomeConfigBackend { private static final String LOGTAG = "GeckoHomeConfigBackend"; private static final String PREFS_CONFIG_KEY = "home_panels"; + private static final String PREFS_LOCALE_KEY = "home_locale"; private final Context mContext; private PrefsListener mPrefsListener; @@ -136,9 +138,35 @@ class HomeConfigPrefsBackend implements HomeConfigBackend { final String jsonString = jsonPanelConfigs.toString(); editor.putString(PREFS_CONFIG_KEY, jsonString); + editor.putString(PREFS_LOCALE_KEY, Locale.getDefault().toString()); editor.commit(); } + @Override + public String getLocale() { + final SharedPreferences prefs = getSharedPreferences(); + + String locale = prefs.getString(PREFS_LOCALE_KEY, null); + if (locale == null) { + // Initialize config with the current locale + final String currentLocale = Locale.getDefault().toString(); + + final SharedPreferences.Editor editor = prefs.edit(); + editor.putString(PREFS_LOCALE_KEY, currentLocale); + editor.commit(); + + // If the user has saved HomeConfig before, return null this + // one time to trigger a refresh and ensure we use the + // correct locale for the saved state. For more context, + // see HomeConfigInvalidator.onLocaleReady(). + if (!prefs.contains(PREFS_CONFIG_KEY)) { + locale = currentLocale; + } + } + + return locale; + } + @Override public void setOnChangeListener(OnChangeListener listener) { final SharedPreferences prefs = getSharedPreferences(); From 4fc7019c193c2932d7284a36ab24d80569244ef5 Mon Sep 17 00:00:00 2001 From: Lucas Rocha Date: Fri, 21 Feb 2014 10:50:15 +0000 Subject: [PATCH 13/44] Bug 974598 - Handle locale changes immediately in HomeConfigInvalidator (r=margaret) --- .../base/home/HomeConfigInvalidator.java | 63 ++++++++++++------- 1 file changed, 42 insertions(+), 21 deletions(-) diff --git a/mobile/android/base/home/HomeConfigInvalidator.java b/mobile/android/base/home/HomeConfigInvalidator.java index aad5bb0d30ac..c95b2a05784a 100644 --- a/mobile/android/base/home/HomeConfigInvalidator.java +++ b/mobile/android/base/home/HomeConfigInvalidator.java @@ -48,13 +48,23 @@ public class HomeConfigInvalidator implements GeckoEventListener { private enum ChangeType { UNINSTALL, INSTALL, - UPDATE + UPDATE, + REFRESH + } + + private enum InvalidationMode { + DELAYED, + IMMEDIATE } private static class ConfigChange { private final ChangeType type; private final Object target; + public ConfigChange(ChangeType type) { + this(type, null); + } + public ConfigChange(ChangeType type, Object target) { this.type = type; this.target = target; @@ -86,7 +96,7 @@ public class HomeConfigInvalidator implements GeckoEventListener { public void run() { final String configLocale = mHomeConfig.getLocale(); if (configLocale == null || !configLocale.equals(locale)) { - handlePanelUpdate(null); + handleLocaleChange(); } } }); @@ -123,7 +133,7 @@ public class HomeConfigInvalidator implements GeckoEventListener { mPendingChanges.offer(new ConfigChange(ChangeType.INSTALL, panelConfig)); Log.d(LOGTAG, "handlePanelInstall: " + mPendingChanges.size()); - scheduleInvalidation(); + scheduleInvalidation(InvalidationMode.DELAYED); } /** @@ -133,33 +143,44 @@ public class HomeConfigInvalidator implements GeckoEventListener { mPendingChanges.offer(new ConfigChange(ChangeType.UNINSTALL, panelId)); Log.d(LOGTAG, "handlePanelUninstall: " + mPendingChanges.size()); - scheduleInvalidation(); + scheduleInvalidation(InvalidationMode.DELAYED); } /** - * Schedules a panel update in HomeConfig. Runs in the gecko or - * background thread. - * - * @param panelConfig the target PanelConfig instance or NULL to refresh - * all HomeConfig entries. + * Runs in the gecko thread. */ private void handlePanelUpdate(PanelConfig panelConfig) { mPendingChanges.offer(new ConfigChange(ChangeType.UPDATE, panelConfig)); Log.d(LOGTAG, "handlePanelUpdate: " + mPendingChanges.size()); - scheduleInvalidation(); + scheduleInvalidation(InvalidationMode.DELAYED); + } + + /** + * Runs in the background thread. + */ + private void handleLocaleChange() { + mPendingChanges.offer(new ConfigChange(ChangeType.REFRESH)); + Log.d(LOGTAG, "handleLocaleChange: " + mPendingChanges.size()); + + scheduleInvalidation(InvalidationMode.IMMEDIATE); } /** * Runs in the gecko or main thread. */ - private void scheduleInvalidation() { + private void scheduleInvalidation(InvalidationMode mode) { final Handler handler = ThreadUtils.getBackgroundHandler(); handler.removeCallbacks(mInvalidationRunnable); - handler.postDelayed(mInvalidationRunnable, INVALIDATION_DELAY_MSEC); - Log.d(LOGTAG, "scheduleInvalidation: scheduled new invalidation"); + if (mode == InvalidationMode.IMMEDIATE) { + handler.post(mInvalidationRunnable); + } else { + handler.postDelayed(mInvalidationRunnable, INVALIDATION_DELAY_MSEC); + } + + Log.d(LOGTAG, "scheduleInvalidation: scheduled new invalidation: " + mode); } /** @@ -192,7 +213,7 @@ public class HomeConfigInvalidator implements GeckoEventListener { * Runs in the background thread. */ private List executePendingChanges(List panelConfigs) { - boolean shouldRefreshAll = false; + boolean shouldRefresh = false; while (!mPendingChanges.isEmpty()) { final ConfigChange pendingChange = mPendingChanges.poll(); @@ -218,19 +239,19 @@ public class HomeConfigInvalidator implements GeckoEventListener { case UPDATE: { final PanelConfig panelConfig = (PanelConfig) pendingChange.target; - if (panelConfig != null) { - if (!replacePanelConfig(panelConfigs, panelConfig)) { - Log.w(LOGTAG, "Tried to update non-existing panel " + panelConfig.getId()); - } - } else { - shouldRefreshAll = true; + if (!replacePanelConfig(panelConfigs, panelConfig)) { + Log.w(LOGTAG, "Tried to update non-existing panel " + panelConfig.getId()); } break; } + + case REFRESH: { + shouldRefresh = true; + } } } - if (shouldRefreshAll) { + if (shouldRefresh) { return executeRefresh(panelConfigs); } else { return panelConfigs; From 81e7fb1f02c86b43573bf7a3e17b066b344f038f Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Wed, 19 Feb 2014 19:44:02 +0000 Subject: [PATCH 14/44] Bug 972267 - adjust Australis' dispatching of customization events so customizationchange fires on all windows, r=Unfocused --- .../customizableui/content/toolbar.xml | 11 +++-- .../customizableui/src/CustomizableUI.jsm | 31 ++++++++++++ .../customizableui/src/CustomizeMode.jsm | 28 +++++------ .../customizableui/test/browser.ini | 1 + ...owser_972267_customizationchange_events.js | 47 +++++++++++++++++++ 5 files changed, 97 insertions(+), 21 deletions(-) create mode 100644 browser/components/customizableui/test/browser_972267_customizationchange_events.js diff --git a/browser/components/customizableui/content/toolbar.xml b/browser/components/customizableui/content/toolbar.xml index f2596d032584..55c0a12ef2b1 100644 --- a/browser/components/customizableui/content/toolbar.xml +++ b/browser/components/customizableui/content/toolbar.xml @@ -532,11 +532,14 @@ this.evictNode(node); this._isModifying = false; this._updateMigratedSet(); - // We will now have moved stuff around; kick off an aftercustomization event + // We will now have moved stuff around; kick off some events // so add-ons know we've just moved their stuff: - if (window.gCustomizeMode) { - window.gCustomizeMode.dispatchToolboxEvent("aftercustomization"); - } + // XXXgijs: only in this window. It's hard to know for sure what's the right + // thing to do here - typically insertItem is used on each window, so + // this seems to make the most sense, even if some of the effects of + // evictNode might affect multiple windows. + CustomizableUI.dispatchToolboxEvent("customizationchange", {}, window); + CustomizableUI.dispatchToolboxEvent("aftercustomization", {}, window); return node; ]]> diff --git a/browser/components/customizableui/src/CustomizableUI.jsm b/browser/components/customizableui/src/CustomizableUI.jsm index 172646741fe4..fbafaf7283db 100644 --- a/browser/components/customizableui/src/CustomizableUI.jsm +++ b/browser/components/customizableui/src/CustomizableUI.jsm @@ -1758,6 +1758,24 @@ let CustomizableUIInternal = { } }, + _dispatchToolboxEventToWindow: function(aEventType, aDetails, aWindow) { + let evt = new aWindow.CustomEvent(aEventType, { + bubbles: true, + cancelable: true, + detail: aDetails + }); + aWindow.gNavToolbox.dispatchEvent(evt); + }, + + dispatchToolboxEvent: function(aEventType, aDetails={}, aWindow=null) { + if (aWindow) { + return this._dispatchToolboxEventToWindow(aEventType, aDetails, aWindow); + } + for (let [win, ] of gBuildWindows) { + this._dispatchToolboxEventToWindow(aEventType, aDetails, win); + } + }, + createWidget: function(aProperties) { let widget = this.normalizeWidget(aProperties, CustomizableUI.SOURCE_EXTERNAL); //XXXunf This should probably throw. @@ -3080,6 +3098,19 @@ this.CustomizableUI = { notifyEndCustomizing: function(aWindow) { CustomizableUIInternal.notifyListeners("onCustomizeEnd", aWindow); }, + + /** + * Notify toolbox(es) of a particular event. If you don't pass aWindow, + * all toolboxes will be notified. For use from Customize Mode only, + * do not use otherwise. + * @param aEvent the name of the event to send. + * @param aDetails optional, the details of the event. + * @param aWindow optional, the window in which to send the event. + */ + dispatchToolboxEvent: function(aEvent, aDetails={}, aWindow=null) { + CustomizableUIInternal.dispatchToolboxEvent(aEvent, aDetails, aWindow); + }, + /** * Check whether an area is overflowable. * diff --git a/browser/components/customizableui/src/CustomizeMode.jsm b/browser/components/customizableui/src/CustomizeMode.jsm index ff13ac89bce6..652fa59f35e4 100644 --- a/browser/components/customizableui/src/CustomizeMode.jsm +++ b/browser/components/customizableui/src/CustomizeMode.jsm @@ -170,7 +170,7 @@ CustomizeMode.prototype = { if (this.document.documentElement._lightweightTheme) this.document.documentElement._lightweightTheme.disable(); - this.dispatchToolboxEvent("beforecustomization"); + CustomizableUI.dispatchToolboxEvent("beforecustomization", {}, window); CustomizableUI.notifyStartCustomizing(this.window); // Add a keypress listener to the document so that we can quickly exit @@ -220,7 +220,7 @@ CustomizeMode.prototype = { yield this._doTransition(true); // Let everybody in this window know that we're about to customize. - this.dispatchToolboxEvent("customizationstarting"); + CustomizableUI.dispatchToolboxEvent("customizationstarting", {}, window); this._mainViewContext = mainView.getAttribute("context"); if (this._mainViewContext) { @@ -266,7 +266,7 @@ CustomizeMode.prototype = { this._handler.isEnteringCustomizeMode = false; panelContents.removeAttribute("customize-transitioning"); - this.dispatchToolboxEvent("customizationready"); + CustomizableUI.dispatchToolboxEvent("customizationready", {}, window); if (!this._wantToBeInCustomizeMode) { this.exit(); } @@ -359,7 +359,7 @@ CustomizeMode.prototype = { // Let everybody in this window know that we're starting to // exit customization mode. - this.dispatchToolboxEvent("customizationending"); + CustomizableUI.dispatchToolboxEvent("customizationending", {}, window); window.PanelUI.setMainView(window.PanelUI.mainView); window.PanelUI.menuButton.disabled = false; @@ -424,8 +424,8 @@ CustomizeMode.prototype = { this._changed = false; this._transitioning = false; this._handler.isExitingCustomizeMode = false; - this.dispatchToolboxEvent("aftercustomization"); - CustomizableUI.notifyEndCustomizing(this.window); + CustomizableUI.dispatchToolboxEvent("aftercustomization", {}, window); + CustomizableUI.notifyEndCustomizing(window); if (this._wantToBeInCustomizeMode) { this.enter(); @@ -477,7 +477,7 @@ CustomizeMode.prototype = { this.document.documentElement.setAttribute("customize-entered", true); this.document.documentElement.removeAttribute("customize-entering"); } - this.dispatchToolboxEvent("customization-transitionend", aEntering); + CustomizableUI.dispatchToolboxEvent("customization-transitionend", aEntering, this.window); deferred.resolve(); }.bind(this), 0); @@ -500,12 +500,6 @@ CustomizeMode.prototype = { return deferred.promise; }, - dispatchToolboxEvent: function(aEventType, aDetails={}) { - let evt = this.document.createEvent("CustomEvent"); - evt.initCustomEvent(aEventType, true, true, {changed: this._changed}); - let result = this.window.gNavToolbox.dispatchEvent(evt); - }, - _getCustomizableChildForNode: function(aNode) { // NB: adjusted from _getCustomizableParent to keep that method fast // (it's used during drags), and avoid multiple DOM loops @@ -544,7 +538,7 @@ CustomizeMode.prototype = { } CustomizableUI.addWidgetToArea(aNode.id, CustomizableUI.AREA_NAVBAR); if (!this._customizing) { - this.dispatchToolboxEvent("customizationchange"); + CustomizableUI.dispatchToolboxEvent("customizationchange"); } }, @@ -555,7 +549,7 @@ CustomizeMode.prototype = { } CustomizableUI.addWidgetToArea(aNode.id, CustomizableUI.AREA_PANEL); if (!this._customizing) { - this.dispatchToolboxEvent("customizationchange"); + CustomizableUI.dispatchToolboxEvent("customizationchange"); } }, @@ -566,7 +560,7 @@ CustomizeMode.prototype = { } CustomizableUI.removeWidgetFromArea(aNode.id); if (!this._customizing) { - this.dispatchToolboxEvent("customizationchange"); + CustomizableUI.dispatchToolboxEvent("customizationchange"); } }, @@ -1017,7 +1011,7 @@ CustomizeMode.prototype = { this._updateUndoResetButton(); this._updateEmptyPaletteNotice(); } - this.dispatchToolboxEvent("customizationchange"); + CustomizableUI.dispatchToolboxEvent("customizationchange"); }, _updateEmptyPaletteNotice: function() { diff --git a/browser/components/customizableui/test/browser.ini b/browser/components/customizableui/test/browser.ini index b35cb8a3e18e..0312cf9572c3 100644 --- a/browser/components/customizableui/test/browser.ini +++ b/browser/components/customizableui/test/browser.ini @@ -68,4 +68,5 @@ skip-if = os == "linux" [browser_969427_recreate_destroyed_widget_after_reset.js] [browser_969661_character_encoding_navbar_disabled.js] [browser_970511_undo_restore_default.js] +[browser_972267_customizationchange_events.js] [browser_panel_toggle.js] diff --git a/browser/components/customizableui/test/browser_972267_customizationchange_events.js b/browser/components/customizableui/test/browser_972267_customizationchange_events.js new file mode 100644 index 000000000000..96c5f924624d --- /dev/null +++ b/browser/components/customizableui/test/browser_972267_customizationchange_events.js @@ -0,0 +1,47 @@ +/* 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"; + +// Create a new window, then move the home button to the menu and check both windows have +// customizationchange events fire on the toolbox: +add_task(function() { + let newWindow = yield openAndLoadWindow(); + let otherToolbox = newWindow.gNavToolbox; + + let handlerCalledCount = 0; + let handler = (ev) => { + handlerCalledCount++; + }; + + let homeButton = document.getElementById("home-button"); + + gNavToolbox.addEventListener("customizationchange", handler); + otherToolbox.addEventListener("customizationchange", handler); + + gCustomizeMode.addToPanel(homeButton); + + is(handlerCalledCount, 2, "Should be called for both windows."); + + // If the test is run in isolation and the panel has never been open, + // the button will be in the palette. Deal with this case: + if (homeButton.parentNode.id == "BrowserToolbarPalette") { + yield PanelUI.ensureReady(); + isnot(homeButton.parentNode.id, "BrowserToolbarPalette", "Home button should now be in panel"); + } + + handlerCalledCount = 0; + gCustomizeMode.addToToolbar(homeButton); + is(handlerCalledCount, 2, "Should be called for both windows."); + + gNavToolbox.removeEventListener("customizationchange", handler); + otherToolbox.removeEventListener("customizationchange", handler); + + newWindow.close(); +}); + +add_task(function asyncCleanup() { + yield resetCustomization(); +}); + From ceae8db36f1ef6e3738aa0b219c10fe79e407f96 Mon Sep 17 00:00:00 2001 From: Gijs Kruitbosch Date: Fri, 21 Feb 2014 12:29:01 +0000 Subject: [PATCH 15/44] Bug 965209 - workaround Australis panel issues with sdk iframe widgets, r=MattN --HG-- extra : rebase_source : c82f7b61812dbdfb0976fb66822f63f3fcecf823 --- .../shared/customizableui/panelUIOverlay.inc.css | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/browser/themes/shared/customizableui/panelUIOverlay.inc.css b/browser/themes/shared/customizableui/panelUIOverlay.inc.css index 9ce0a0805cf2..5bc23a9112a2 100644 --- a/browser/themes/shared/customizableui/panelUIOverlay.inc.css +++ b/browser/themes/shared/customizableui/panelUIOverlay.inc.css @@ -250,12 +250,12 @@ toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-it opacity: .5; } -toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"] { +toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-item) { width: calc(@menuPanelButtonWidth@); margin: 0 !important; } -toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-widget) { +toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-item) { -moz-box-align: center; -moz-box-pack: center; } @@ -264,6 +264,18 @@ toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"] > iframe { margin: 4px auto; } +/* + * XXXgijs: this is a workaround for a layout issue that was caused by these iframes, + * which was affecting subview display. Because of this, we're hiding the iframe *only* + * when displaying a subview. The discerning user might notice this, but it's not nearly + * as bad as the brokenness. + * This hack should be removed once https://bugzilla.mozilla.org/show_bug.cgi?id=975375 + * is addressed. + */ +#PanelUI-multiView[viewtype="subview"] toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-item) > iframe { + visibility: hidden; +} + toolbaritem[cui-areatype="menu-panel"][sdkstylewidget="true"]:not(.panel-wide-item) > .toolbarbutton-text { text-align: center; } From 56fb063a43a13d5ecf12d2719283ed6e2c7502c4 Mon Sep 17 00:00:00 2001 From: Rodrigo Silveira Date: Wed, 19 Feb 2014 13:25:17 -0800 Subject: [PATCH 16/44] Bug 969356 - When mouse clicking on a top site tile, the watermark shifts r=jimm --HG-- extra : rebase_source : cdfe6847b51ff24ebf46beecff66e16ec241dde3 --- browser/metro/base/content/WebProgress.js | 1 + browser/metro/base/content/browser-ui.js | 1 - 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/metro/base/content/WebProgress.js b/browser/metro/base/content/WebProgress.js index 90b55341aaa0..7c9c55575081 100644 --- a/browser/metro/base/content/WebProgress.js +++ b/browser/metro/base/content/WebProgress.js @@ -101,6 +101,7 @@ const WebProgress = { if (aTab == Browser.selectedTab) { BrowserUI.updateURI(); BrowserUI.update(); + BrowserUI.updateStartURIAttributes(aJson.location); } let locationHasChanged = (location != aTab.browser.lastLocation); diff --git a/browser/metro/base/content/browser-ui.js b/browser/metro/base/content/browser-ui.js index 5152755d11a3..31e65574a138 100644 --- a/browser/metro/base/content/browser-ui.js +++ b/browser/metro/base/content/browser-ui.js @@ -262,7 +262,6 @@ var BrowserUI = { }, showContent: function showContent(aURI) { - this.updateStartURIAttributes(aURI); ContextUI.dismissTabs(); ContextUI.dismissContextAppbar(); FlyoutPanelsUI.hide(); From bde1d58d2d58f58e3ecddb9a982fc72ac21c032e Mon Sep 17 00:00:00 2001 From: Margaret Leibovic Date: Fri, 21 Feb 2014 11:20:37 -0800 Subject: [PATCH 17/44] Bug 839885 - Update the selected tab style before calling setSelection. r=lucasr --- mobile/android/base/TabsTray.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/TabsTray.java b/mobile/android/base/TabsTray.java index 9f4ccfb52d86..1bed298fceba 100644 --- a/mobile/android/base/TabsTray.java +++ b/mobile/android/base/TabsTray.java @@ -202,11 +202,11 @@ public class TabsTray extends TwoWayView // Updates the selected position in the list so that it will be scrolled to the right place. private void updateSelectedPosition() { int selected = getPositionForTab(Tabs.getInstance().getSelectedTab()); + updateSelectedStyle(selected); + if (selected != -1) { TabsTray.this.setSelection(selected); } - - updateSelectedStyle(selected); } /** From 7ef9b14f04f2c600988aefd362f3b7cbf130faab Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Thu, 13 Feb 2014 15:41:08 -0800 Subject: [PATCH 18/44] Bug 960359: (1/4) Remove HomeBanner from TopSitesPanel. r=margaret --- mobile/android/base/home/TopSitesPanel.java | 72 ------------------- .../resources/layout/home_top_sites_panel.xml | 28 ++------ 2 files changed, 5 insertions(+), 95 deletions(-) diff --git a/mobile/android/base/home/TopSitesPanel.java b/mobile/android/base/home/TopSitesPanel.java index a15794d41bb0..e0c08e20a50a 100644 --- a/mobile/android/base/home/TopSitesPanel.java +++ b/mobile/android/base/home/TopSitesPanel.java @@ -87,15 +87,6 @@ public class TopSitesPanel extends HomeFragment { // Grid of top sites private TopSitesGridView mGrid; - // Banner to show snippets. - private HomeBanner mBanner; - - // Raw Y value of the last event that happened on the list view. - private float mListTouchY = -1; - - // Scrolling direction of the banner. - private boolean mSnapBannerToTop; - // Callbacks used for the search and favicon cursor loaders private CursorLoaderCallbacks mCursorLoaderCallbacks; @@ -226,15 +217,6 @@ public class TopSitesPanel extends HomeFragment { registerForContextMenu(mList); registerForContextMenu(mGrid); - - mBanner = (HomeBanner) view.findViewById(R.id.home_banner); - mList.setOnTouchListener(new OnTouchListener() { - @Override - public boolean onTouch(View v, MotionEvent event) { - TopSitesPanel.this.handleListTouchEvent(event); - return false; - } - }); } @Override @@ -473,60 +455,6 @@ public class TopSitesPanel extends HomeFragment { } } - private void handleListTouchEvent(MotionEvent event) { - // Ignore the event if the banner is hidden for this session. - if (mBanner.isDismissed()) { - return; - } - - switch (event.getActionMasked()) { - case MotionEvent.ACTION_DOWN: { - mListTouchY = event.getRawY(); - break; - } - - case MotionEvent.ACTION_MOVE: { - // There is a chance that we won't receive ACTION_DOWN, if the touch event - // actually started on the Grid instead of the List. Treat this as first event. - if (mListTouchY == -1) { - mListTouchY = event.getRawY(); - return; - } - - final float curY = event.getRawY(); - final float delta = mListTouchY - curY; - mSnapBannerToTop = (delta > 0.0f) ? false : true; - - final float height = mBanner.getHeight(); - float newTranslationY = ViewHelper.getTranslationY(mBanner) + delta; - - // Clamp the values to be between 0 and height. - if (newTranslationY < 0.0f) { - newTranslationY = 0.0f; - } else if (newTranslationY > height) { - newTranslationY = height; - } - - ViewHelper.setTranslationY(mBanner, newTranslationY); - mListTouchY = curY; - break; - } - - case MotionEvent.ACTION_UP: - case MotionEvent.ACTION_CANCEL: { - mListTouchY = -1; - final float y = ViewHelper.getTranslationY(mBanner); - final float height = mBanner.getHeight(); - if (y > 0.0f && y < height) { - final PropertyAnimator animator = new PropertyAnimator(100); - animator.attach(mBanner, Property.TRANSLATION_Y, mSnapBannerToTop ? 0 : height); - animator.start(); - } - break; - } - } - } - private void updateUiFromCursor(Cursor c) { mList.setHeaderDividersEnabled(c != null && c.getCount() > mMaxGridEntries); } diff --git a/mobile/android/base/resources/layout/home_top_sites_panel.xml b/mobile/android/base/resources/layout/home_top_sites_panel.xml index 4230b277ebc4..1f1a0d31c4c7 100644 --- a/mobile/android/base/resources/layout/home_top_sites_panel.xml +++ b/mobile/android/base/resources/layout/home_top_sites_panel.xml @@ -3,26 +3,8 @@ - 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/. --> - - - - - - - + From 2c1e3e5ab92583a992189f454bd9c1d4005a4fce Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Thu, 13 Feb 2014 15:43:48 -0800 Subject: [PATCH 19/44] Bug 960359: (2/4) Control HomeBanner visibility from home_pager_container. r=margaret --- mobile/android/base/BrowserApp.java | 9 ++++++--- mobile/android/base/home/HomePager.java | 17 +++++++---------- .../resources/layout-large-v11/home_pager.xml | 3 +-- .../android/base/resources/layout/gecko_app.xml | 10 ++++++++++ .../base/resources/layout/home_pager.xml | 3 +-- 5 files changed, 25 insertions(+), 17 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index f9ea47ae4e95..c8d882837739 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -1422,7 +1422,8 @@ abstract public class BrowserApp extends GeckoApp } private boolean isHomePagerVisible() { - return (mHomePager != null && mHomePager.isVisible()); + return (mHomePager != null && mHomePager.isLoaded() + && mHomePagerContainer != null && mHomePagerContainer.getVisibility() == View.VISIBLE); } /* Favicon stuff. */ @@ -1679,7 +1680,8 @@ abstract public class BrowserApp extends GeckoApp mHomePager = (HomePager) homePagerStub.inflate(); } - mHomePager.show(getSupportLoaderManager(), + mHomePagerContainer.setVisibility(View.VISIBLE); + mHomePager.load(getSupportLoaderManager(), getSupportFragmentManager(), pageId, animator); @@ -1739,9 +1741,10 @@ abstract public class BrowserApp extends GeckoApp // Display the previously hidden web content (which prevented screen reader access). mLayerView.setVisibility(View.VISIBLE); + mHomePagerContainer.setVisibility(View.GONE); if (mHomePager != null) { - mHomePager.hide(); + mHomePager.unload(); } mBrowserToolbar.setNextFocusDownId(R.id.layer_view); diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index 8b4e79173237..8a467e398957 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -167,7 +167,7 @@ public class HomePager extends ViewPager { * * @param fm FragmentManager for the adapter */ - public void show(LoaderManager lm, FragmentManager fm, String panelId, PropertyAnimator animator) { + public void load(LoaderManager lm, FragmentManager fm, String panelId, PropertyAnimator animator) { mLoaded = true; mInitialPanelId = panelId; @@ -179,8 +179,6 @@ public class HomePager extends ViewPager { adapter.setCanLoadHint(!shouldAnimate); setAdapter(adapter); - setVisibility(VISIBLE); - // Don't show the tabs strip until we have the // list of panels in place. mTabStrip.setVisibility(View.INVISIBLE); @@ -211,23 +209,22 @@ public class HomePager extends ViewPager { } /** - * Hides the pager and removes all child fragments. + * Removes all child fragments to free memory. */ - public void hide() { + public void unload() { mLoaded = false; - setVisibility(GONE); setAdapter(null); } /** - * Determines whether the pager is visible. + * Determines whether the pager is loaded. * * Unlike getVisibility(), this method does not need to be called on the UI * thread. * - * @return Whether the pager and its fragments are being displayed + * @return Whether the pager and its fragments are loaded */ - public boolean isVisible() { + public boolean isLoaded() { return mLoaded; } @@ -296,7 +293,7 @@ public class HomePager extends ViewPager { setAdapter(adapter); // Use the default panel as defined in the HomePager's configuration - // if the initial panel wasn't explicitly set by the show() caller, + // if the initial panel wasn't explicitly set by the load() caller, // or if the initial panel is not found in the adapter. final int itemPosition = (mInitialPanelId == null) ? -1 : adapter.getItemPosition(mInitialPanelId); if (itemPosition > -1) { diff --git a/mobile/android/base/resources/layout-large-v11/home_pager.xml b/mobile/android/base/resources/layout-large-v11/home_pager.xml index b3b40d0bf8fa..61e6a9b07e50 100644 --- a/mobile/android/base/resources/layout-large-v11/home_pager.xml +++ b/mobile/android/base/resources/layout-large-v11/home_pager.xml @@ -11,8 +11,7 @@ android:id="@+id/home_pager" android:layout_width="fill_parent" android:layout_height="fill_parent" - android:background="@android:color/white" - android:visibility="gone"> + android:background="@android:color/white"> + diff --git a/mobile/android/base/resources/layout/home_pager.xml b/mobile/android/base/resources/layout/home_pager.xml index 25fb53b14289..ac2ca74b4960 100644 --- a/mobile/android/base/resources/layout/home_pager.xml +++ b/mobile/android/base/resources/layout/home_pager.xml @@ -11,8 +11,7 @@ android:id="@+id/home_pager" android:layout_width="fill_parent" android:layout_height="fill_parent" - android:background="@android:color/white" - android:visibility="gone"> + android:background="@android:color/white"> Date: Thu, 13 Feb 2014 15:47:17 -0800 Subject: [PATCH 20/44] Bug 960359: (3/4) Display HomeBanner on default page and animate. r=margaret --- mobile/android/base/BrowserApp.java | 4 + mobile/android/base/home/HomeBanner.java | 128 +++++++++++++++++- mobile/android/base/home/HomePager.java | 59 ++++++-- .../base/resources/layout/gecko_app.xml | 3 +- 4 files changed, 174 insertions(+), 20 deletions(-) diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index c8d882837739..34b564c0c3d0 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -24,6 +24,7 @@ import org.mozilla.gecko.health.BrowserHealthReporter; import org.mozilla.gecko.health.HealthRecorder; import org.mozilla.gecko.health.SessionInformation; import org.mozilla.gecko.home.BrowserSearch; +import org.mozilla.gecko.home.HomeBanner; import org.mozilla.gecko.home.HomeConfigInvalidator; import org.mozilla.gecko.home.HomePager; import org.mozilla.gecko.home.HomePager.OnUrlOpenListener; @@ -1678,6 +1679,9 @@ abstract public class BrowserApp extends GeckoApp if (mHomePager == null) { final ViewStub homePagerStub = (ViewStub) findViewById(R.id.home_pager_stub); mHomePager = (HomePager) homePagerStub.inflate(); + + HomeBanner homeBanner = (HomeBanner) findViewById(R.id.home_banner); + mHomePager.setBanner(homeBanner); } mHomePagerContainer.setVisibility(View.VISIBLE); diff --git a/mobile/android/base/home/HomeBanner.java b/mobile/android/base/home/HomeBanner.java index fa429536e78b..7d08b0667b48 100644 --- a/mobile/android/base/home/HomeBanner.java +++ b/mobile/android/base/home/HomeBanner.java @@ -5,6 +5,10 @@ package org.mozilla.gecko.home; +import org.mozilla.gecko.animation.PropertyAnimator; +import org.mozilla.gecko.animation.PropertyAnimator.Property; +import org.mozilla.gecko.animation.PropertyAnimator.PropertyAnimationListener; +import org.mozilla.gecko.animation.ViewHelper; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoEvent; import org.mozilla.gecko.R; @@ -23,6 +27,7 @@ import android.text.TextUtils; import android.util.AttributeSet; import android.util.Log; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.widget.ImageButton; import android.widget.ImageView; @@ -33,6 +38,22 @@ public class HomeBanner extends LinearLayout implements GeckoEventListener { private static final String LOGTAG = "GeckoHomeBanner"; + // Used for tracking scroll length + private float mTouchY = -1; + + // Used to detect for upwards scroll to push banner all the way up + private boolean mSnapBannerToTop; + + // Tracks if the banner has been enabled by HomePager to avoid race conditions. + private boolean mEnabled = false; + + // The user is currently swiping between HomePager pages + private boolean mScrollingPages = false; + + // Tracks whether the user swiped the banner down, preventing us from autoshowing when the user + // switches back to the default page. + private boolean mUserSwipedDown = false; + public HomeBanner(Context context) { this(context, null); } @@ -82,9 +103,9 @@ public class HomeBanner extends LinearLayout GeckoAppShell.getEventDispatcher().unregisterEventListener("HomeBanner:Data", this); } - public boolean isDismissed() { - return (getVisibility() == View.GONE); - } + public void setScrollingPages(boolean scrollingPages) { + mScrollingPages = scrollingPages; + } @Override public void handleMessage(String event, JSONObject message) { @@ -101,7 +122,8 @@ public class HomeBanner extends LinearLayout @Override public void run() { textView.setText(text); - setVisibility(View.VISIBLE); + setVisibility(VISIBLE); + animateUp(); } }); } catch (JSONException e) { @@ -137,4 +159,102 @@ public class HomeBanner extends LinearLayout } }); } + + public void setEnabled(boolean enabled) { + // No need to animate if not changing + if (mEnabled == enabled) { + return; + } + + mEnabled = enabled; + if (enabled) { + animateUp(); + } else { + animateDown(); + } + } + + private void animateUp() { + // Check to make sure that message has been received and the banner has been enabled. + // Necessary to avoid race conditions between show() and handleMessage() calls. + TextView textView = (TextView) findViewById(R.id.text); + if (!mEnabled || TextUtils.isEmpty(textView.getText()) || mUserSwipedDown) { + return; + } + + // No need to animate if already translated. + if (ViewHelper.getTranslationY(this) == 0) { + return; + } + + final PropertyAnimator animator = new PropertyAnimator(100); + animator.attach(this, Property.TRANSLATION_Y, 0); + animator.start(); + } + + private void animateDown() { + // No need to animate if already translated or gone. + if (ViewHelper.getTranslationY(this) == getHeight()) { + return; + } + + final PropertyAnimator animator = new PropertyAnimator(100); + animator.attach(this, Property.TRANSLATION_Y, getHeight()); + animator.start(); + } + + public void handleHomeTouch(MotionEvent event) { + if (!mEnabled || getVisibility() == GONE || mScrollingPages) { + return; + } + + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: { + // Track the beginning of the touch + mTouchY = event.getRawY(); + break; + } + + case MotionEvent.ACTION_MOVE: { + final float curY = event.getRawY(); + final float delta = mTouchY - curY; + mSnapBannerToTop = delta <= 0.0f; + + final float height = getHeight(); + float newTranslationY = ViewHelper.getTranslationY(this) + delta; + + // Clamp the values to be between 0 and height. + if (newTranslationY < 0.0f) { + newTranslationY = 0.0f; + } else if (newTranslationY > height) { + newTranslationY = height; + } + + // Don't change this value if it wasn't a significant movement + if (delta >= 10 || delta <= -10) { + mUserSwipedDown = newTranslationY == height; + } + + ViewHelper.setTranslationY(this, newTranslationY); + mTouchY = curY; + break; + } + + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: { + mTouchY = -1; + final float y = ViewHelper.getTranslationY(this); + final float height = getHeight(); + if (y > 0.0f && y < height) { + if (mSnapBannerToTop) { + animateUp(); + } else { + animateDown(); + mUserSwipedDown = true; + } + } + break; + } + } + } } diff --git a/mobile/android/base/home/HomePager.java b/mobile/android/base/home/HomePager.java index 8a467e398957..5d78c2f4ceff 100644 --- a/mobile/android/base/home/HomePager.java +++ b/mobile/android/base/home/HomePager.java @@ -41,6 +41,8 @@ public class HomePager extends ViewPager { private volatile boolean mLoaded; private Decor mDecor; private View mTabStrip; + private HomeBanner mHomeBanner; + private int mDefaultPageIndex = -1; private final OnAddPanelListener mAddPanelListener; @@ -125,6 +127,7 @@ public class HomePager extends ViewPager { setFocusableInTouchMode(true); mOriginalBackground = getBackground(); + setOnPageChangeListener(new PageChangeListener()); } @Override @@ -140,21 +143,6 @@ public class HomePager extends ViewPager { setCurrentItem(index, true); } }); - - setOnPageChangeListener(new ViewPager.OnPageChangeListener() { - @Override - public void onPageSelected(int position) { - mDecor.onPageSelected(position); - } - - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - mDecor.onPageScrolled(position, positionOffset, positionOffsetPixels); - } - - @Override - public void onPageScrollStateChanged(int state) { } - }); } else if (child instanceof HomePagerTabStrip) { mTabStrip = child; } @@ -247,6 +235,19 @@ public class HomePager extends ViewPager { return super.onInterceptTouchEvent(event); } + public void setBanner(HomeBanner banner) { + mHomeBanner = banner; + } + + @Override + public boolean dispatchTouchEvent(MotionEvent event) { + if (mHomeBanner != null) { + mHomeBanner.handleHomeTouch(event); + } + + return super.dispatchTouchEvent(event); + } + private void updateUiFromPanelConfigs(List panelConfigs) { // We only care about the adapter if HomePager is currently // loaded, which means it's visible in the activity. @@ -303,6 +304,7 @@ public class HomePager extends ViewPager { for (int i = 0; i < count; i++) { final PanelConfig panelConfig = enabledPanels.get(i); if (panelConfig.isDefault()) { + mDefaultPageIndex = i; setCurrentItem(i, false); break; } @@ -325,4 +327,31 @@ public class HomePager extends ViewPager { public void onLoaderReset(Loader> loader) { } } + + private class PageChangeListener implements ViewPager.OnPageChangeListener { + @Override + public void onPageSelected(int position) { + if (mDecor != null) { + mDecor.onPageSelected(position); + } + + if (mHomeBanner != null) { + mHomeBanner.setEnabled(position == mDefaultPageIndex); + } + } + + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + if (mDecor != null) { + mDecor.onPageScrolled(position, positionOffset, positionOffsetPixels); + } + + if (mHomeBanner != null) { + mHomeBanner.setScrollingPages(positionOffsetPixels != 0); + } + } + + @Override + public void onPageScrollStateChanged(int state) { } + } } diff --git a/mobile/android/base/resources/layout/gecko_app.xml b/mobile/android/base/resources/layout/gecko_app.xml index 12b00283d440..c77a2a7b9482 100644 --- a/mobile/android/base/resources/layout/gecko_app.xml +++ b/mobile/android/base/resources/layout/gecko_app.xml @@ -44,7 +44,8 @@ android:gravity="center_vertical" android:visibility="gone" android:clickable="true" - android:focusable="true"/> + android:focusable="true" + android:translationY="@dimen/home_banner_height"/> From 8aea92c4e5e13d6d3f79c1b7c2f8553ef7071497 Mon Sep 17 00:00:00 2001 From: Josh Dover Date: Thu, 20 Feb 2014 16:12:04 -0800 Subject: [PATCH 21/44] Bug 960359: (4/4) Update HomeBanner test for new implementation. r=margaret --- mobile/android/base/tests/BaseTest.java | 4 +-- .../tests/components/AboutHomeComponent.java | 26 +++++++++++++------ mobile/android/base/tests/testHomeBanner.java | 12 ++++----- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/mobile/android/base/tests/BaseTest.java b/mobile/android/base/tests/BaseTest.java index 8d5f57c6d138..439b44938521 100644 --- a/mobile/android/base/tests/BaseTest.java +++ b/mobile/android/base/tests/BaseTest.java @@ -483,12 +483,12 @@ abstract class BaseTest extends ActivityInstrumentationTestCase2 { } public final void verifyHomePagerHidden() { - final View homePagerView = mSolo.getView(R.id.home_pager); + final View homePagerContainer = mSolo.getView(R.id.home_pager_container); boolean rc = waitForCondition(new Condition() { @Override public boolean isSatisfied() { - return homePagerView.getVisibility() != View.VISIBLE; + return homePagerContainer.getVisibility() != View.VISIBLE; } }, MAX_WAIT_HOME_PAGER_HIDDEN_MS); diff --git a/mobile/android/base/tests/components/AboutHomeComponent.java b/mobile/android/base/tests/components/AboutHomeComponent.java index a17653188a89..da28d9ad612b 100644 --- a/mobile/android/base/tests/components/AboutHomeComponent.java +++ b/mobile/android/base/tests/components/AboutHomeComponent.java @@ -59,6 +59,10 @@ public class AboutHomeComponent extends BaseComponent { super(testContext); } + private View getHomePagerContainer() { + return mSolo.getView(R.id.home_pager_container); + } + private ViewPager getHomePagerView() { return (ViewPager) mSolo.getView(R.id.home_pager); } @@ -77,26 +81,32 @@ public class AboutHomeComponent extends BaseComponent { } public AboutHomeComponent assertNotVisible() { - assertFalse("The HomePager is not visible", - getHomePagerView().getVisibility() == View.VISIBLE); + assertTrue("The HomePager is not visible", + getHomePagerContainer().getVisibility() != View.VISIBLE || + getHomePagerView().getVisibility() != View.VISIBLE); return this; } public AboutHomeComponent assertVisible() { - assertEquals("The HomePager is visible", - View.VISIBLE, getHomePagerView().getVisibility()); + assertTrue("The HomePager is visible", + getHomePagerContainer().getVisibility() == View.VISIBLE && + getHomePagerView().getVisibility() == View.VISIBLE); return this; } public AboutHomeComponent assertBannerNotVisible() { - assertFalse("The HomeBanner is not visible", - getHomeBannerView().getVisibility() == View.VISIBLE); + View banner = getHomeBannerView(); + assertTrue("The HomeBanner is not visible", + getHomePagerContainer().getVisibility() != View.VISIBLE || + banner.getVisibility() != View.VISIBLE || + banner.getTranslationY() == banner.getHeight()); return this; } public AboutHomeComponent assertBannerVisible() { - assertEquals("The HomeBanner is visible", - View.VISIBLE, getHomeBannerView().getVisibility()); + assertTrue("The HomeBanner is visible", + getHomePagerContainer().getVisibility() == View.VISIBLE && + getHomeBannerView().getVisibility() == View.VISIBLE); return this; } diff --git a/mobile/android/base/tests/testHomeBanner.java b/mobile/android/base/tests/testHomeBanner.java index 4e521db5846c..d2ec48a523b1 100644 --- a/mobile/android/base/tests/testHomeBanner.java +++ b/mobile/android/base/tests/testHomeBanner.java @@ -22,7 +22,9 @@ public class testHomeBanner extends UITest { // These test methods depend on being run in this order. addBannerTest(); - removeBannerTest(); + // TODO: API doesn't actually support this but it used to work due to how the banner was + // part of TopSitesPanel's lifecycle + // removeBannerTest(); // Make sure to test dismissing the banner after everything else, since dismissing // the banner will prevent it from showing up again. @@ -41,7 +43,8 @@ public class testHomeBanner extends UITest { // Load about:home again, and make sure the onshown handler is called. Actions.EventExpecter eventExpecter = getActions().expectGeckoEvent("TestHomeBanner:MessageShown"); NavigationHelper.enterAndLoadUrl("about:home"); - eventExpecter.blockForEvent(); + // TODO: Add shown event passing from Java: bug 974723 + // eventExpecter.blockForEvent(); // Verify that the banner is visible with the correct text. mAboutHome.assertBannerText(TEXT); @@ -54,10 +57,7 @@ public class testHomeBanner extends UITest { // Verify that the banner isn't visible after navigating away from about:home. NavigationHelper.enterAndLoadUrl("about:firefox"); - // AboutHomeComponent calls mSolo.getView, which will fail an assertion if the - // view is not present, so we need to use findViewById in this case. - final View banner = getActivity().findViewById(R.id.home_banner); - assertTrue("The HomeBanner is not visible", banner == null || banner.getVisibility() != View.VISIBLE); + mAboutHome.assertBannerNotVisible(); } From e2f93e65340b282d14333c696440510ca1446d85 Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Fri, 21 Feb 2014 13:56:39 -0800 Subject: [PATCH 22/44] Bug 911904 - Avoid a circular dependency finding html file handlers. r=margaret --- mobile/android/modules/HelperApps.jsm | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mobile/android/modules/HelperApps.jsm b/mobile/android/modules/HelperApps.jsm index 4db5139f7a7f..427f427ce938 100644 --- a/mobile/android/modules/HelperApps.jsm +++ b/mobile/android/modules/HelperApps.jsm @@ -42,9 +42,11 @@ var HelperApps = { get defaultHtmlHandlers() { delete this.defaultHtmlHandlers; - let handlers = this.getAppsForUri(Services.io.newURI("http://www.example.com/index.html", null, null)); - this.defaultHtmlHandlers = {}; + let handlers = this.getAppsForUri(Services.io.newURI("http://www.example.com/index.html", null, null), { + filterHtml: false + }); + handlers.forEach(function(app) { this.defaultHtmlHandlers[app.name] = app; }, this); From 08ad809725d4995fe1dd84797f34b2d4c021b89c Mon Sep 17 00:00:00 2001 From: Wes Johnston Date: Fri, 21 Feb 2014 13:56:40 -0800 Subject: [PATCH 23/44] Bug 975525 - Refresh action mode buttons when activities are uninstalled. r=mfinkle --- mobile/android/base/GeckoApp.java | 6 +++++- mobile/android/base/menu/GeckoMenu.java | 13 +++++++++++++ mobile/android/base/menu/GeckoMenuItem.java | 11 +++++++++++ .../android/base/widget/ActivityChooserModel.java | 9 ++++++++- mobile/android/base/widget/GeckoActionProvider.java | 9 +++++++++ 5 files changed, 46 insertions(+), 2 deletions(-) diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 53a2fc91de59..479043411576 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -425,8 +425,12 @@ public abstract class GeckoApp @Override public boolean onPreparePanel(int featureId, View view, Menu menu) { - if (Build.VERSION.SDK_INT >= 11 && featureId == Window.FEATURE_OPTIONS_PANEL) + if (Build.VERSION.SDK_INT >= 11 && featureId == Window.FEATURE_OPTIONS_PANEL) { + if (menu instanceof GeckoMenu) { + ((GeckoMenu) menu).refresh(); + } return onPrepareOptionsMenu(menu); + } return super.onPreparePanel(featureId, view, menu); } diff --git a/mobile/android/base/menu/GeckoMenu.java b/mobile/android/base/menu/GeckoMenu.java index e90679972a3f..a0ae54448501 100644 --- a/mobile/android/base/menu/GeckoMenu.java +++ b/mobile/android/base/menu/GeckoMenu.java @@ -26,6 +26,7 @@ import android.widget.ListView; import java.util.ArrayList; import java.util.HashMap; +import java.util.Iterator; import java.util.List; import java.util.Map; @@ -636,6 +637,18 @@ public class GeckoMenu extends ListView } } + public void refresh() { + for (Iterator i = mPrimaryActionItems.keySet().iterator(); i.hasNext();) { + GeckoMenuItem item = i.next(); + item.refreshIfChanged(); + } + + for (Iterator i = mSecondaryActionItems.keySet().iterator(); i.hasNext();) { + GeckoMenuItem item = i.next(); + item.refreshIfChanged(); + } + } + // Adapter to bind menu items to the list. private class MenuItemsAdapter extends BaseAdapter { private static final int VIEW_TYPE_DEFAULT = 0; diff --git a/mobile/android/base/menu/GeckoMenuItem.java b/mobile/android/base/menu/GeckoMenuItem.java index b244b18f26ff..88df93c8a398 100644 --- a/mobile/android/base/menu/GeckoMenuItem.java +++ b/mobile/android/base/menu/GeckoMenuItem.java @@ -222,6 +222,17 @@ public class GeckoMenuItem implements MenuItem { return this; } + public void refreshIfChanged() { + if (mActionProvider == null) + return; + + if (mActionProvider instanceof GeckoActionProvider) { + if (((GeckoActionProvider) mActionProvider).hasChanged()) { + mShowAsActionChangedListener.onShowAsActionChanged(GeckoMenuItem.this); + } + } + } + @Override public MenuItem setActionView(int resId) { return this; diff --git a/mobile/android/base/widget/ActivityChooserModel.java b/mobile/android/base/widget/ActivityChooserModel.java index 47ddc0175c37..9edfa36a791a 100644 --- a/mobile/android/base/widget/ActivityChooserModel.java +++ b/mobile/android/base/widget/ActivityChooserModel.java @@ -319,6 +319,8 @@ public class ActivityChooserModel extends DataSetObservable { */ private boolean mReloadActivities = false; + private long mLastChanged = 0; + /** * Policy for controlling how the model handles chosen activities. */ @@ -745,6 +747,7 @@ public class ActivityChooserModel extends DataSetObservable { ResolveInfo resolveInfo = resolveInfos.get(i); mActivities.add(new ActivityResolveInfo(resolveInfo)); } + mLastChanged = System.currentTimeMillis(); return true; } return false; @@ -1220,7 +1223,11 @@ public class ActivityChooserModel extends DataSetObservable { } mReloadActivities = true; + mLastChanged = System.currentTimeMillis(); } } -} + public long getLastChanged() { + return mLastChanged; + } +} diff --git a/mobile/android/base/widget/GeckoActionProvider.java b/mobile/android/base/widget/GeckoActionProvider.java index 7eb8ff953408..5037730f7100 100644 --- a/mobile/android/base/widget/GeckoActionProvider.java +++ b/mobile/android/base/widget/GeckoActionProvider.java @@ -21,6 +21,7 @@ import android.view.View.OnClickListener; public class GeckoActionProvider extends ActionProvider { private static int MAX_HISTORY_SIZE = 2; + private long mLastChanged = 0; /** * A listener to know when a target was selected. @@ -79,6 +80,14 @@ public class GeckoActionProvider extends ActionProvider { return onCreateActionView(); } + public boolean hasChanged() { + ActivityChooserModel dataModel = ActivityChooserModel.get(mContext, mHistoryFileName); + long lastChanged = dataModel.getLastChanged(); + boolean ret = lastChanged != mLastChanged; + mLastChanged = lastChanged; + return ret; + } + @Override public boolean hasSubMenu() { return true; From f793e1cd917865ecf439d57ba07fd9977b6ec92b Mon Sep 17 00:00:00 2001 From: Matthew Noorenberghe Date: Fri, 21 Feb 2014 14:42:40 -0800 Subject: [PATCH 24/44] Bug 974804 - UITour: Calling recreatePopup on the menu panel before it's ready leads to a computed width of 0 for the panel scroller. r=adw --- browser/modules/UITour.jsm | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/browser/modules/UITour.jsm b/browser/modules/UITour.jsm index 62b18a935147..9c8f67335395 100644 --- a/browser/modules/UITour.jsm +++ b/browser/modules/UITour.jsm @@ -927,6 +927,12 @@ this.UITour = { recreatePopup: function(aPanel) { // After changing popup attributes that relate to how the native widget is created // (e.g. @noautohide) we need to re-create the frame/widget for it to take effect. + if (aPanel.hidden) { + // If the panel is already hidden, we don't need to recreate it but flush + // in case someone just hid it. + aPanel.clientWidth; // flush + return; + } aPanel.hidden = true; aPanel.clientWidth; // flush aPanel.hidden = false; From 8d4410ac9e5110a2bacf1e7a44eb4b9bc2e7d3e0 Mon Sep 17 00:00:00 2001 From: Eugen Sawin Date: Thu, 20 Feb 2014 18:50:18 +0100 Subject: [PATCH 25/44] Bug 971012 - Use onConfigurationChange for screen orientation change. r=snorp --- mobile/android/base/BrowserApp.java | 2 +- mobile/android/base/GeckoApp.java | 25 +- mobile/android/base/GeckoAppShell.java | 10 +- .../android/base/GeckoScreenOrientation.java | 376 ++++++++++++++++++ .../base/GeckoScreenOrientationListener.java | 229 ----------- mobile/android/base/moz.build | 2 +- 6 files changed, 395 insertions(+), 249 deletions(-) create mode 100644 mobile/android/base/GeckoScreenOrientation.java delete mode 100644 mobile/android/base/GeckoScreenOrientationListener.java diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java index 34b564c0c3d0..5337260c1cd6 100644 --- a/mobile/android/base/BrowserApp.java +++ b/mobile/android/base/BrowserApp.java @@ -1014,7 +1014,7 @@ abstract public class BrowserApp extends GeckoApp if (mMainLayoutAnimator != null) mMainLayoutAnimator.stop(); - boolean isSideBar = (HardwareUtils.isTablet() && mOrientation == Configuration.ORIENTATION_LANDSCAPE); + boolean isSideBar = (HardwareUtils.isTablet() && getOrientation() == Configuration.ORIENTATION_LANDSCAPE); final int sidebarWidth = getResources().getDimensionPixelSize(R.dimen.tabs_sidebar_width); ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) mTabsPanel.getLayoutParams(); diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 479043411576..7c5c98d88ebd 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -183,7 +183,6 @@ public abstract class GeckoApp protected MenuPanel mMenuPanel; protected Menu mMenu; protected GeckoProfile mProfile; - public static int mOrientation; protected boolean mIsRestoringActivity; private ContactService mContactService; @@ -941,7 +940,7 @@ public abstract class GeckoApp mFullScreenPluginView = null; - GeckoScreenOrientationListener.getInstance().unlockScreenOrientation(); + GeckoScreenOrientation.getInstance().unlock(); setFullScreen(false); } @@ -1252,7 +1251,7 @@ public abstract class GeckoApp super.onCreate(savedInstanceState); - mOrientation = getResources().getConfiguration().orientation; + GeckoScreenOrientation.getInstance().update(getResources().getConfiguration().orientation); setContentView(getLayout()); @@ -1924,6 +1923,10 @@ public abstract class GeckoApp return uri; } + protected int getOrientation() { + return GeckoScreenOrientation.getInstance().getAndroidOrientation(); + } + @Override public void onResume() { @@ -1932,14 +1935,10 @@ public abstract class GeckoApp super.onResume(); int newOrientation = getResources().getConfiguration().orientation; - - if (mOrientation != newOrientation) { - mOrientation = newOrientation; + if (GeckoScreenOrientation.getInstance().update(newOrientation)) { refreshChrome(); } - GeckoScreenOrientationListener.getInstance().start(); - // User may have enabled/disabled accessibility. GeckoAccessibility.updateAccessibilitySettings(this); @@ -2015,8 +2014,6 @@ public abstract class GeckoApp } }); - GeckoScreenOrientationListener.getInstance().stop(); - if (mAppStateListeners != null) { for(GeckoAppShell.AppStateListener listener: mAppStateListeners) { listener.onPause(); @@ -2155,14 +2152,16 @@ public abstract class GeckoApp public void onConfigurationChanged(Configuration newConfig) { Log.d(LOGTAG, "onConfigurationChanged: " + newConfig.locale); LocaleManager.correctLocale(getResources(), newConfig); - super.onConfigurationChanged(newConfig); - if (mOrientation != newConfig.orientation) { - mOrientation = newConfig.orientation; + // onConfigurationChanged is not called for 180 degree orientation changes, + // we will miss such rotations and the screen orientation will not be + // updated. + if (GeckoScreenOrientation.getInstance().update(newConfig.orientation)) { if (mFormAssistPopup != null) mFormAssistPopup.hide(); refreshChrome(); } + super.onConfigurationChanged(newConfig); } public String getContentProcessName() { diff --git a/mobile/android/base/GeckoAppShell.java b/mobile/android/base/GeckoAppShell.java index df9b9710fa17..59ca9072c6c4 100644 --- a/mobile/android/base/GeckoAppShell.java +++ b/mobile/android/base/GeckoAppShell.java @@ -2571,27 +2571,27 @@ public class GeckoAppShell @WrapElementForJNI(stubName = "GetScreenOrientationWrapper") public static short getScreenOrientation() { - return GeckoScreenOrientationListener.getInstance().getScreenOrientation(); + return GeckoScreenOrientation.getInstance().getScreenOrientation().value; } @WrapElementForJNI public static void enableScreenOrientationNotifications() { - GeckoScreenOrientationListener.getInstance().enableNotifications(); + GeckoScreenOrientation.getInstance().enableNotifications(); } @WrapElementForJNI public static void disableScreenOrientationNotifications() { - GeckoScreenOrientationListener.getInstance().disableNotifications(); + GeckoScreenOrientation.getInstance().disableNotifications(); } @WrapElementForJNI public static void lockScreenOrientation(int aOrientation) { - GeckoScreenOrientationListener.getInstance().lockScreenOrientation(aOrientation); + GeckoScreenOrientation.getInstance().lock(aOrientation); } @WrapElementForJNI public static void unlockScreenOrientation() { - GeckoScreenOrientationListener.getInstance().unlockScreenOrientation(); + GeckoScreenOrientation.getInstance().unlock(); } @WrapElementForJNI diff --git a/mobile/android/base/GeckoScreenOrientation.java b/mobile/android/base/GeckoScreenOrientation.java new file mode 100644 index 000000000000..05a918c04043 --- /dev/null +++ b/mobile/android/base/GeckoScreenOrientation.java @@ -0,0 +1,376 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * 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; + +import android.content.Context; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.util.Log; +import android.view.Surface; +import android.app.Activity; + +import java.util.Arrays; +import java.util.List; + +/* + * Updates, locks and unlocks the screen orientation. + * + * Note: Replaces the OnOrientationChangeListener to avoid redundant rotation + * event handling. + */ +public class GeckoScreenOrientation { + private static final String LOGTAG = "GeckoScreenOrientation"; + + // Make sure that any change in dom/base/ScreenOrientation.h happens here too. + public enum ScreenOrientation { + NONE(0), + PORTRAIT_PRIMARY(1 << 0), + PORTRAIT_SECONDARY(1 << 1), + LANDSCAPE_PRIMARY(1 << 2), + LANDSCAPE_SECONDARY(1 << 3), + DEFAULT(1 << 4); + + public final short value; + + private ScreenOrientation(int value) { + this.value = (short)value; + } + + public static ScreenOrientation get(short value) { + switch (value) { + case (1 << 0): return PORTRAIT_PRIMARY; + case (1 << 1): return PORTRAIT_SECONDARY; + case (1 << 2): return LANDSCAPE_PRIMARY; + case (1 << 3): return LANDSCAPE_SECONDARY; + case (1 << 4): return DEFAULT; + default: return NONE; + } + } + } + + // Singleton instance. + private static GeckoScreenOrientation sInstance = null; + // Default screen orientation, used for initialization and unlocking. + private static final ScreenOrientation DEFAULT_SCREEN_ORIENTATION = ScreenOrientation.DEFAULT; + // Default rotation, used when device rotation is unknown. + private static final int DEFAULT_ROTATION = Surface.ROTATION_0; + // Default orientation, used if screen orientation is unspecified. + private ScreenOrientation mDefaultScreenOrientation; + // Last updated screen orientation. + private ScreenOrientation mScreenOrientation; + // Whether the update should notify Gecko about screen orientation changes. + private boolean mShouldNotify = true; + // Configuration screen orientation preference path. + private static final String DEFAULT_SCREEN_ORIENTATION_PREF = "app.orientation.default"; + + public GeckoScreenOrientation() { + PrefsHelper.getPref(DEFAULT_SCREEN_ORIENTATION_PREF, new PrefsHelper.PrefHandlerBase() { + @Override public void prefValue(String pref, String value) { + // Read and update the configuration default preference. + mDefaultScreenOrientation = screenOrientationFromArrayString(value); + setRequestedOrientation(mDefaultScreenOrientation); + } + }); + + mDefaultScreenOrientation = DEFAULT_SCREEN_ORIENTATION; + update(); + } + + public static GeckoScreenOrientation getInstance() { + if (sInstance == null) { + sInstance = new GeckoScreenOrientation(); + } + return sInstance; + } + + /* + * Enable Gecko screen orientation events on update. + */ + public void enableNotifications() { + update(); + mShouldNotify = true; + } + + /* + * Disable Gecko screen orientation events on update. + */ + public void disableNotifications() { + mShouldNotify = false; + } + + /* + * Update screen orientation. + * Retrieve orientation and rotation via GeckoAppShell. + * + * @return Whether the screen orientation has changed. + */ + public boolean update() { + Activity activity = GeckoAppShell.getGeckoInterface().getActivity(); + if (activity == null) { + return false; + } + Configuration config = activity.getResources().getConfiguration(); + return update(config.orientation); + } + + /* + * Update screen orientation given the android orientation. + * Retrieve rotation via GeckoAppShell. + * + * @param aAndroidOrientation + * Android screen orientation from Configuration.orientation. + * + * @return Whether the screen orientation has changed. + */ + public boolean update(int aAndroidOrientation) { + return update(getScreenOrientation(aAndroidOrientation, getRotation())); + } + + /* + * Update screen orientation given the screen orientation. + * + * @param aScreenOrientation + * Gecko screen orientation based on android orientation and rotation. + * + * @return Whether the screen orientation has changed. + */ + public boolean update(ScreenOrientation aScreenOrientation) { + if (mScreenOrientation == aScreenOrientation) { + return false; + } + mScreenOrientation = aScreenOrientation; + Log.d(LOGTAG, "updating to new orientation " + mScreenOrientation); + if (mShouldNotify) { + GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenOrientationEvent(mScreenOrientation.value)); + } + return true; + } + + /* + * @return The Android orientation (Configuration.orientation). + */ + public int getAndroidOrientation() { + return screenOrientationToAndroidOrientation(getScreenOrientation()); + } + + /* + * @return The Gecko screen orientation derived from Android orientation and + * rotation. + */ + public ScreenOrientation getScreenOrientation() { + return mScreenOrientation; + } + + /* + * Lock screen orientation given the Android orientation. + * Retrieve rotation via GeckoAppShell. + * + * @param aAndroidOrientation + * The Android orientation provided by Configuration.orientation. + */ + public void lock(int aAndroidOrientation) { + lock(getScreenOrientation(aAndroidOrientation, getRotation())); + } + + /* + * Lock screen orientation given the Gecko screen orientation. + * Retrieve rotation via GeckoAppShell. + * + * @param aScreenOrientation + * Gecko screen orientation derived from Android orientation and + * rotation. + * + * @return Whether the locking was successful. + */ + public boolean lock(ScreenOrientation aScreenOrientation) { + Log.d(LOGTAG, "locking to " + aScreenOrientation); + update(aScreenOrientation); + return setRequestedOrientation(aScreenOrientation); + } + + /* + * Unlock and update screen orientation. + * + * @return Whether the unlocking was successful. + */ + public boolean unlock() { + Log.d(LOGTAG, "unlocking"); + setRequestedOrientation(mDefaultScreenOrientation); + return update(); + } + + /* + * Set the given requested orientation for the current activity. + * This is essentially an unlock without an update. + * + * @param aScreenOrientation + * Gecko screen orientation. + * + * @return Whether the requested orientation was set. This can only fail if + * the current activity cannot be retrieved vie GeckoAppShell. + * + */ + private boolean setRequestedOrientation(ScreenOrientation aScreenOrientation) { + int activityOrientation = screenOrientationToActivityInfoOrientation(aScreenOrientation); + Activity activity = GeckoAppShell.getGeckoInterface().getActivity(); + if (activity == null) { + Log.w(LOGTAG, "setRequestOrientation: failed to get activity"); + } + if (activity.getRequestedOrientation() == activityOrientation) { + return false; + } + activity.setRequestedOrientation(activityOrientation); + return true; + } + + /* + * Combine the Android orientation and rotation to the Gecko orientation. + * + * @param aAndroidOrientation + * Android orientation from Configuration.orientation. + * @param aRotation + * Device rotation from Display.getRotation(). + * + * @return Gecko screen orientation. + */ + private ScreenOrientation getScreenOrientation(int aAndroidOrientation, int aRotation) { + boolean isPrimary = aRotation == Surface.ROTATION_0 || aRotation == Surface.ROTATION_90; + if (aAndroidOrientation == Configuration.ORIENTATION_PORTRAIT) { + if (isPrimary) { + // Non-rotated portrait device or landscape device rotated + // to primary portrait mode counter-clockwise. + return ScreenOrientation.PORTRAIT_PRIMARY; + } + return ScreenOrientation.PORTRAIT_SECONDARY; + } + if (aAndroidOrientation == Configuration.ORIENTATION_LANDSCAPE) { + if (isPrimary) { + // Non-rotated landscape device or portrait device rotated + // to primary landscape mode counter-clockwise. + return ScreenOrientation.LANDSCAPE_PRIMARY; + } + return ScreenOrientation.LANDSCAPE_SECONDARY; + } + return ScreenOrientation.NONE; + } + + /* + * @return Device rotation from Display.getRotation(). + */ + private int getRotation() { + Activity activity = GeckoAppShell.getGeckoInterface().getActivity(); + if (activity == null) { + Log.w(LOGTAG, "getRotation: failed to get activity"); + return DEFAULT_ROTATION; + } + return activity.getWindowManager().getDefaultDisplay().getRotation(); + } + + /* + * Retrieve the screen orientation from an array string. + * + * @param aArray + * String containing comma-delimited strings. + * + * @return First parsed Gecko screen orientation. + */ + public static ScreenOrientation screenOrientationFromArrayString(String aArray) { + List orientations = Arrays.asList(aArray.split(",")); + if (orientations.size() == 0) { + // If nothing is listed, return default. + Log.w(LOGTAG, "screenOrientationFromArrayString: no orientation in string"); + return DEFAULT_SCREEN_ORIENTATION; + } + + // We don't support multiple orientations yet. To avoid developer + // confusion, just take the first one listed. + return screenOrientationFromString(orientations.get(0)); + } + + /* + * Retrieve the scren orientation from a string. + * + * @param aStr + * String hopefully containing a screen orientation name. + * @return Gecko screen orientation if matched, DEFAULT_SCREEN_ORIENTATION + * otherwise. + */ + public static ScreenOrientation screenOrientationFromString(String aStr) { + if ("portrait".equals(aStr)) { + return ScreenOrientation.PORTRAIT_PRIMARY; + } + else if ("landscape".equals(aStr)) { + return ScreenOrientation.LANDSCAPE_PRIMARY; + } + else if ("portrait-primary".equals(aStr)) { + return ScreenOrientation.PORTRAIT_PRIMARY; + } + else if ("portrait-secondary".equals(aStr)) { + return ScreenOrientation.PORTRAIT_SECONDARY; + } + else if ("landscape-primary".equals(aStr)) { + return ScreenOrientation.LANDSCAPE_PRIMARY; + } + else if ("landscape-secondary".equals(aStr)) { + return ScreenOrientation.LANDSCAPE_SECONDARY; + } + Log.w(LOGTAG, "screenOrientationFromString: unknown orientation string"); + return DEFAULT_SCREEN_ORIENTATION; + } + + /* + * Convert Gecko screen orientation to Android orientation. + * + * @param aScreenOrientation + * Gecko screen orientation. + * @return Android orientation. This conversion is lossy, the Android + * orientation does not differentiate between primary and secondary + * orientations. + */ + public static int screenOrientationToAndroidOrientation(ScreenOrientation aScreenOrientation) { + switch (aScreenOrientation) { + case PORTRAIT_PRIMARY: + case PORTRAIT_SECONDARY: + return Configuration.ORIENTATION_PORTRAIT; + case LANDSCAPE_PRIMARY: + case LANDSCAPE_SECONDARY: + return Configuration.ORIENTATION_LANDSCAPE; + case NONE: + case DEFAULT: + default: + return Configuration.ORIENTATION_UNDEFINED; + } + } + + + /* + * Convert Gecko screen orientation to Android ActivityInfo orientation. + * This is yet another orientation used by Android, but it's more detailed + * than the Android orientation. + * It is required for screen orientation locking and unlocking. + * + * @param aScreenOrientation + * Gecko screen orientation. + * @return Android ActivityInfo orientation. + */ + public static int screenOrientationToActivityInfoOrientation(ScreenOrientation aScreenOrientation) { + switch (aScreenOrientation) { + case PORTRAIT_PRIMARY: + return ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; + case PORTRAIT_SECONDARY: + return ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; + case LANDSCAPE_PRIMARY: + return ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; + case LANDSCAPE_SECONDARY: + return ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; + case DEFAULT: + case NONE: + return ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; + default: + return ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; + } + } +} diff --git a/mobile/android/base/GeckoScreenOrientationListener.java b/mobile/android/base/GeckoScreenOrientationListener.java deleted file mode 100644 index 0764ece5387f..000000000000 --- a/mobile/android/base/GeckoScreenOrientationListener.java +++ /dev/null @@ -1,229 +0,0 @@ -/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- - * 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; - -import android.content.Context; -import android.content.pm.ActivityInfo; -import android.util.Log; -import android.view.OrientationEventListener; -import android.view.Surface; - -import java.util.Arrays; -import java.util.List; - -import android.app.Activity; - -public class GeckoScreenOrientationListener { - private static final String LOGTAG = "GeckoScreenOrientationListener"; - - static class OrientationEventListenerImpl extends OrientationEventListener { - public OrientationEventListenerImpl(Context c) { - super(c); - } - - @Override - public void onOrientationChanged(int aOrientation) { - GeckoScreenOrientationListener.getInstance().updateScreenOrientation(aOrientation); - } - } - - static private GeckoScreenOrientationListener sInstance = null; - - // Make sure that any change in dom/base/ScreenOrientation.h happens here too. - static public final short eScreenOrientation_None = 0; - static public final short eScreenOrientation_PortraitPrimary = 1; // PR_BIT(0) - static public final short eScreenOrientation_PortraitSecondary = 2; // PR_BIT(1) - static public final short eScreenOrientation_LandscapePrimary = 4; // PR_BIT(2) - static public final short eScreenOrientation_LandscapeSecondary = 8; // PR_BIT(3) - static public final short eScreenOrientation_Default = 16;// PR_BIT(4) - - static private final short DEFAULT_ORIENTATION = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED; - - private short mOrientation; - private OrientationEventListenerImpl mListener = null; - - // Whether the listener should be listening to changes. - private boolean mShouldBeListening = false; - // Whether the listener should notify Gecko that a change happened. - private boolean mShouldNotify = false; - // The default orientation to use if nothing is specified - private short mDefaultOrientation; - - private static final String DEFAULT_ORIENTATION_PREF = "app.orientation.default"; - - private GeckoScreenOrientationListener() { - mListener = new OrientationEventListenerImpl(GeckoAppShell.getContext()); - - PrefsHelper.getPref(DEFAULT_ORIENTATION_PREF, new PrefsHelper.PrefHandlerBase() { - @Override public void prefValue(String pref, String value) { - mDefaultOrientation = orientationFromStringArray(value); - unlockScreenOrientation(); - } - - @Override - public boolean isObserver() { - return true; - } - }); - - mDefaultOrientation = DEFAULT_ORIENTATION; - } - - public static GeckoScreenOrientationListener getInstance() { - if (sInstance == null) { - sInstance = new GeckoScreenOrientationListener(); - } - - return sInstance; - } - - public void start() { - mShouldBeListening = true; - updateScreenOrientation(); - - if (mShouldNotify) { - startListening(); - } - } - - public void stop() { - mShouldBeListening = false; - - if (mShouldNotify) { - stopListening(); - } - } - - public void enableNotifications() { - updateScreenOrientation(); - mShouldNotify = true; - - if (mShouldBeListening) { - startListening(); - } - } - - public void disableNotifications() { - mShouldNotify = false; - - if (mShouldBeListening) { - stopListening(); - } - } - - private void startListening() { - mListener.enable(); - } - - private void stopListening() { - mListener.disable(); - } - - private short orientationFromStringArray(String val) { - List orientations = Arrays.asList(val.split(",")); - // if nothing is listed, return unspecified - if (orientations.size() == 0) - return DEFAULT_ORIENTATION; - - // we dont' support multiple orientations yet. To avoid developer confusion, - // just take the first one listed - return orientationFromString(orientations.get(0)); - } - - private short orientationFromString(String val) { - if ("portrait".equals(val)) - return (short)ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; - else if ("landscape".equals(val)) - return (short)ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; - else if ("portrait-primary".equals(val)) - return (short)ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; - else if ("portrait-secondary".equals(val)) - return (short)ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; - else if ("landscape-primary".equals(val)) - return (short)ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - else if ("landscape-secondary".equals(val)) - return (short)ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; - return DEFAULT_ORIENTATION; - } - - private void updateScreenOrientation() { - Context context = GeckoAppShell.getContext(); - int rotation = mDefaultOrientation; - if (context instanceof Activity) { - rotation = ((Activity)context).getWindowManager().getDefaultDisplay().getRotation(); - } - updateScreenOrientation(rotation * 90); - } - - private void updateScreenOrientation(int aOrientation) { - short previousOrientation = mOrientation; - - if (aOrientation >= 315 || aOrientation < 45) { - mOrientation = eScreenOrientation_PortraitPrimary; - } else if (aOrientation >= 45 && aOrientation < 135) { - mOrientation = eScreenOrientation_LandscapePrimary; - } else if (aOrientation >= 135 && aOrientation < 225) { - mOrientation = eScreenOrientation_PortraitSecondary; - } else if (aOrientation >= 225 && aOrientation < 315) { - mOrientation = eScreenOrientation_LandscapeSecondary; - } else { - Log.e(LOGTAG, "Unexpected value received! (" + aOrientation + ")"); - return; - } - - if (mShouldNotify && mOrientation != previousOrientation) { - GeckoAppShell.sendEventToGecko(GeckoEvent.createScreenOrientationEvent(mOrientation)); - } - } - - public short getScreenOrientation() { - return mOrientation; - } - - public void lockScreenOrientation(int aOrientation) { - int orientation = 0; - - switch (aOrientation) { - case eScreenOrientation_PortraitPrimary: - orientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT; - break; - case eScreenOrientation_PortraitSecondary: - orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT; - break; - case eScreenOrientation_PortraitPrimary | eScreenOrientation_PortraitSecondary: - orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT; - break; - case eScreenOrientation_LandscapePrimary: - orientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE; - break; - case eScreenOrientation_LandscapeSecondary: - orientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE; - break; - case eScreenOrientation_LandscapePrimary | eScreenOrientation_LandscapeSecondary: - orientation = ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE; - break; - case eScreenOrientation_Default: - orientation = ActivityInfo.SCREEN_ORIENTATION_NOSENSOR; - break; - default: - Log.e(LOGTAG, "Unexpected value received! (" + aOrientation + ")"); - return; - } - if (GeckoAppShell.getContext() instanceof Activity) - ((Activity)GeckoAppShell.getContext()).setRequestedOrientation(orientation); - updateScreenOrientation(); - } - - public void unlockScreenOrientation() { - if (!(GeckoAppShell.getContext() instanceof Activity)) - return; - if (((Activity)GeckoAppShell.getContext()).getRequestedOrientation() == mDefaultOrientation) - return; - - ((Activity)GeckoAppShell.getContext()).setRequestedOrientation(mDefaultOrientation); - updateScreenOrientation(); - } -} diff --git a/mobile/android/base/moz.build b/mobile/android/base/moz.build index e36221a37f35..b9343f21ee5a 100644 --- a/mobile/android/base/moz.build +++ b/mobile/android/base/moz.build @@ -154,7 +154,7 @@ gbjar.sources += [ 'GeckoMessageReceiver.java', 'GeckoNetworkManager.java', 'GeckoProfile.java', - 'GeckoScreenOrientationListener.java', + 'GeckoScreenOrientation.java', 'GeckoSmsManager.java', 'GeckoThread.java', 'GeckoUpdateReceiver.java', From fed985cff41e3ac9d9beb14e38d79eebf74feab5 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 09:00:22 -0800 Subject: [PATCH 26/44] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/6c139b2c645b Author: evanxd Desc: Merge pull request #16435 from evanxd/bug-973876 Bug 973876 - make test-integration shouldn't ask for admin password ======== https://hg.mozilla.org/integration/gaia-central/rev/4b4f3cdd35f5 Author: Evan Xd Desc: Bug 973876 - Remove the admin permission request. --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 9978a936f70b..59de8b9d0f41 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "5b157e823d42532a365d33cfe3a8bd9ba4b71152", + "revision": "6c139b2c645ba55e5413b066a216cf6a293106c2", "repo_path": "/integration/gaia-central" } From 26ac0338fe4c29a36ff3b61f83c213c00a7625ad Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 09:06:09 -0800 Subject: [PATCH 27/44] Bumping manifests a=b2g-bump --- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/inari/sources.xml | 2 +- b2g/config/leo/sources.xml | 2 +- b2g/config/mako/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 304d9e7bf9be..0943723cf2fa 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index a16169e66133..30a703353855 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 304d9e7bf9be..0943723cf2fa 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index dbe4566ade6e..8c1fec93ffcc 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 3f722ace9077..68409bb5861d 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -10,7 +10,7 @@ - + diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml index 22392746507e..20a41a01efeb 100644 --- a/b2g/config/inari/sources.xml +++ b/b2g/config/inari/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml index f5e0cdc7dc0a..1a4f5a3715cb 100644 --- a/b2g/config/leo/sources.xml +++ b/b2g/config/leo/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml index 75be018d3350..b453e76c8f9f 100644 --- a/b2g/config/mako/sources.xml +++ b/b2g/config/mako/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 9de0d0100e21..b66b66e1a126 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -11,7 +11,7 @@ - + From 48706284ec628ecb39ba7fc508d2574caa808685 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 09:25:26 -0800 Subject: [PATCH 28/44] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/d1db45a074b9 Author: Mason Chang Desc: Merge pull request #16500 from changm/bug960325 Bug 960325 - Move Homescreen Background into Homescreen App. r=alive, crdlc ======== https://hg.mozilla.org/integration/gaia-central/rev/a463921aeee5 Author: Mason Chang Desc: Bug 960325 - Move Homescreen Background into Homescreen App. r=alive, crdlc --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index 59de8b9d0f41..bca5a12ddfc0 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "6c139b2c645ba55e5413b066a216cf6a293106c2", + "revision": "d1db45a074b94f211b45bcae759f92d631bd607a", "repo_path": "/integration/gaia-central" } From dddae810754bf1801e9b6fb0ce9deb1314ec45f7 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 09:30:47 -0800 Subject: [PATCH 29/44] Bumping manifests a=b2g-bump --- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/inari/sources.xml | 2 +- b2g/config/leo/sources.xml | 2 +- b2g/config/mako/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 0943723cf2fa..2688677e192e 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 30a703353855..48a22cecc3bd 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 0943723cf2fa..2688677e192e 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 8c1fec93ffcc..9adb0842fa2b 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 68409bb5861d..a741787fdc81 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -10,7 +10,7 @@ - + diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml index 20a41a01efeb..05d98000f5b5 100644 --- a/b2g/config/inari/sources.xml +++ b/b2g/config/inari/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml index 1a4f5a3715cb..66f264a86385 100644 --- a/b2g/config/leo/sources.xml +++ b/b2g/config/leo/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml index b453e76c8f9f..1f3d070d283b 100644 --- a/b2g/config/mako/sources.xml +++ b/b2g/config/mako/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index b66b66e1a126..c27b27d8367e 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -11,7 +11,7 @@ - + From faf48739187aa131a27af4170b8263fd9bf4b506 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 09:35:21 -0800 Subject: [PATCH 30/44] Bumping gaia.json for 1 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/65124b4dde97 Author: Zac Campbell Desc: Revert "Bug 974906 - Xfail test_camera_capture_video.py because of Bug 974851." This reverts commit 6773129d877db48f3183782a1aefea4091393f1a. --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index bca5a12ddfc0..a267adaaecd9 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "d1db45a074b94f211b45bcae759f92d631bd607a", + "revision": "65124b4dde97934efea25fb4f603bcfe65d756db", "repo_path": "/integration/gaia-central" } From 8a5b7d8ecb9e28e2747aea083d50fc2e890dcdf3 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 09:36:26 -0800 Subject: [PATCH 31/44] Bumping manifests a=b2g-bump --- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/inari/sources.xml | 2 +- b2g/config/leo/sources.xml | 2 +- b2g/config/mako/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 2688677e192e..1c6c3a456769 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 48a22cecc3bd..1399f018c878 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 2688677e192e..1c6c3a456769 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 9adb0842fa2b..844c188a5f86 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index a741787fdc81..10ffefbacc8a 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -10,7 +10,7 @@ - + diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml index 05d98000f5b5..e8471ba26d4f 100644 --- a/b2g/config/inari/sources.xml +++ b/b2g/config/inari/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml index 66f264a86385..1ee2259cfe0e 100644 --- a/b2g/config/leo/sources.xml +++ b/b2g/config/leo/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml index 1f3d070d283b..d541da2117ca 100644 --- a/b2g/config/mako/sources.xml +++ b/b2g/config/mako/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index c27b27d8367e..d6d4a1ef9de4 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -11,7 +11,7 @@ - + From 63e0bf861e36aee0dd875071e69a9e163893c0cf Mon Sep 17 00:00:00 2001 From: Mike Habicher Date: Fri, 21 Feb 2014 13:14:52 -0500 Subject: [PATCH 32/44] Bug 965533 - fix the zoom attribute, r=dhylands --- dom/camera/GonkCameraParameters.cpp | 85 ++++++++++++++++++++++++----- dom/camera/GonkCameraParameters.h | 1 + dom/camera/test/test_camera.html | 40 +++++++++++++- 3 files changed, 111 insertions(+), 15 deletions(-) diff --git a/dom/camera/GonkCameraParameters.cpp b/dom/camera/GonkCameraParameters.cpp index 6023487fa4b3..5989f67152bf 100644 --- a/dom/camera/GonkCameraParameters.cpp +++ b/dom/camera/GonkCameraParameters.cpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2013 Mozilla Foundation + * Copyright (C) 2013-2014 Mozilla Foundation * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -153,6 +153,10 @@ GonkCameraParameters::Initialize() if (NS_FAILED(rv)) { return rv; } + rv = GetListAsArray(CAMERA_PARAM_SUPPORTED_ZOOMRATIOS, mZoomRatios); + if (NS_FAILED(rv)) { + return rv; + } mInitialized = true; return NS_OK; @@ -401,16 +405,53 @@ GonkCameraParameters::GetTranslated(uint32_t aKey, int64_t& aValue) nsresult GonkCameraParameters::SetTranslated(uint32_t aKey, const double& aValue) { - if (aKey == CAMERA_PARAM_EXPOSURECOMPENSATION) { - /** - * Convert from real value to a Gonk index, round - * to the nearest step; index is 1-based. - */ - int index = - (aValue - mExposureCompensationMin + mExposureCompensationStep / 2) / - mExposureCompensationStep + 1; - DOM_CAMERA_LOGI("Exposure compensation = %f --> index = %d\n", aValue, index); - return SetImpl(CAMERA_PARAM_EXPOSURECOMPENSATION, index); + int index; + int value; + + switch (aKey) { + case CAMERA_PARAM_EXPOSURECOMPENSATION: + /** + * Convert from real value to a Gonk index, round + * to the nearest step; index is 1-based. + */ + index = + (aValue - mExposureCompensationMin + mExposureCompensationStep / 2) / + mExposureCompensationStep + 1; + DOM_CAMERA_LOGI("Exposure compensation = %f --> index = %d\n", aValue, index); + return SetImpl(CAMERA_PARAM_EXPOSURECOMPENSATION, index); + + case CAMERA_PARAM_ZOOM: + { + /** + * Convert from a real zoom multipler (e.g. 2.5x) to + * the index of the nearest supported value. + */ + value = aValue * 100.0; + + // mZoomRatios is sorted, so we can binary search it + unsigned int bottom = 0; + unsigned int top = mZoomRatios.Length() - 1; + unsigned int middle; + + while (bottom != top) { + middle = (top + bottom) / 2; + if (value == mZoomRatios[middle]) { + // exact match + break; + } + if (value > mZoomRatios[middle] && value < mZoomRatios[middle + 1]) { + // the specified zoom value lies in this interval + break; + } + if (value > mZoomRatios[middle]) { + bottom = middle + 1; + } else { + top = middle - 1; + } + } + index = middle; + } + return SetImpl(CAMERA_PARAM_ZOOM, index); } return SetImpl(aKey, aValue); @@ -427,9 +468,9 @@ GonkCameraParameters::GetTranslated(uint32_t aKey, double& aValue) switch (aKey) { case CAMERA_PARAM_ZOOM: - rv = GetImpl(CAMERA_PARAM_ZOOM, val); + rv = GetImpl(CAMERA_PARAM_ZOOM, index); if (NS_SUCCEEDED(rv)) { - val /= 100.0; + val = mZoomRatios[index] / 100.0; } else { // return 1x when zooming is not supported val = 1.0; @@ -557,6 +598,16 @@ ParseItem(const char* aStart, const char* aEnd, double* aItem) return NS_ERROR_FAILURE; } +nsresult +ParseItem(const char* aStart, const char* aEnd, int* aItem) +{ + if (sscanf(aStart, "%d", aItem) == 1) { + return NS_OK; + } + + return NS_ERROR_FAILURE; +} + template nsresult GonkCameraParameters::GetListAsArray(uint32_t aKey, nsTArray& aArray) { @@ -609,6 +660,14 @@ GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray& aValues) nsresult GonkCameraParameters::GetTranslated(uint32_t aKey, nsTArray& aValues) { + if (aKey == CAMERA_PARAM_SUPPORTED_ZOOMRATIOS) { + aValues.Clear(); + for (int i = 0; i < mZoomRatios.Length(); ++i) { + *aValues.AppendElement() = mZoomRatios[i] / 100.0; + } + return NS_OK; + } + return GetListAsArray(aKey, aValues); } diff --git a/dom/camera/GonkCameraParameters.h b/dom/camera/GonkCameraParameters.h index 396f47925d0e..69b472b8f205 100644 --- a/dom/camera/GonkCameraParameters.h +++ b/dom/camera/GonkCameraParameters.h @@ -94,6 +94,7 @@ protected: // Required internal properties double mExposureCompensationMin; double mExposureCompensationStep; + nsTArray mZoomRatios; // This subclass of android::CameraParameters just gives // all of the AOSP getters and setters the same signature. diff --git a/dom/camera/test/test_camera.html b/dom/camera/test/test_camera.html index 37a1615ad88d..fbecf7e5deab 100644 --- a/dom/camera/test/test_camera.html +++ b/dom/camera/test/test_camera.html @@ -33,8 +33,8 @@ function onError(e) { } var capabilities = [ 'previewSizes', 'pictureSizes', 'fileFormats', 'maxFocusAreas', 'minExposureCompensation', - 'maxExposureCompensation', 'stepExposureCompensation', 'maxMeteringAreas', 'videoSizes', - 'recorderProfiles']; + 'maxExposureCompensation', 'stepExposureCompensation', 'maxMeteringAreas', 'videoSizes', + 'recorderProfiles', 'zoomRatios']; var Camera = { cameraObj: null, @@ -49,6 +49,7 @@ var Camera = { _zoomRatios: null, _sceneModes: null, _focusModes: null, + _zoomRatios: null, _testsCompleted: 0, _shutter: 0, _config: { @@ -67,6 +68,12 @@ var Camera = { setFocus: function camera_setfocus(mode) { this.cameraObj.focus = mode; }, + setZoom: function camera_setZoom(zoom) { + this.cameraObj.zoom = zoom; + }, + getZoom: function camera_getZoom() { + return this.cameraObj.zoom; + }, getFileFormats: function camera_formats() { this._fileFormats = this.cameraObj.capabilities.fileFormats; }, @@ -91,6 +98,9 @@ var Camera = { getPreviewSizes: function camera_preview() { this._previewSizes = this.cameraObj.capabilities.previewSizes; }, + getZoomRatios: function camera_preview() { + this._zoomRatios = this.cameraObj.capabilities.zoomRatios; + }, takePictureSuccess: function taken_foto(blob) { var img = new Image(); var test = this._currentTest; @@ -137,9 +147,35 @@ var Camera = { Camera.getPreviewSizes(); Camera.getFileFormats(); Camera.getFocusModes(); + Camera.getZoomRatios(); ok(Camera._previewSizes.length > 0, "previewSizes length = " + Camera._previewSizes.length); ok(Camera._pictureSizes.length > 0, "picturesizes length = " + Camera._pictureSizes.length); ok(Camera._fileFormats.length > 0, "file formats length = " + Camera._fileFormats.length); + info("zoom ratios length = " + Camera._zoomRatios.length); + + if (Camera._zoomRatios.length > 0) { + Camera._zoomRatios.forEach(function(element, index) { + info("zoom[" + index + "] = " + element + "x"); + Camera.setZoom(element); + ok(Camera.getZoom() === element, "zoom[" + index + "] = " + element + "x"); + }); + + var zoom = Camera._zoomRatios[0] - 0.1; + Camera.setZoom(zoom); + ok(Camera.getZoom() === Camera._zoomRatios[0], + zoom + "x zoom clamps to minimum: " + Camera._zoomRatios[0]); + zoom = Camera._zoomRatios.slice(-1)[0] + 1.0; + Camera.setZoom(zoom); + ok(Camera.getZoom() === Camera._zoomRatios[0], + zoom + "x zoom clamps to maximum: " + Camera._zoomRatios.slice(-1)[0]); + if (Camera._zoomRatios.length > 1) { + zoom = (Camera._zoomRatios[0] + Camera._zoomRatios[1]) / 2; + Camera.setZoom(zoom); + ok(Camera.getZoom() === Camera._zoomRatios[0], + zoom + "x zoom rounded down to maximum: " + Camera._zoomRatios.slice[0]); + } + } + Camera._tests = new Array(); for (var i in Camera._pictureSizes) { for (var l in Camera._fileFormats) { From 9d604d3f4fe4a0e7dd72223d9838d946cc8f53ac Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 10:50:21 -0800 Subject: [PATCH 33/44] Bumping gaia.json for 4 gaia revision(s) a=gaia-bump MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ======== https://hg.mozilla.org/integration/gaia-central/rev/cc70d60b7bdc Author: Francisco Jordano Desc: Merge pull request #16522 from arcturus/bug-973661 Bug 973661 - [FTE] In FTE, when skipping PIN screen, there is no option to get that screen again when pressing back ======== https://hg.mozilla.org/integration/gaia-central/rev/dd2c575455e1 Author: Francisco Jordano Desc: Bug 973661 - [FTE] In FTE, when skipping PIN screen, there is no option to get that screen again when pressing back ======== https://hg.mozilla.org/integration/gaia-central/rev/96fd0c5057b6 Author: Hubert Figuière Desc: Merge pull request #16451 from stasm/974361-arr-undefined Bug 974361 - Memory average assumes the array of measurements always exists r=hub ======== https://hg.mozilla.org/integration/gaia-central/rev/e14022e2d19f Author: Staś Małolepszy Desc: Bug 974361 - Memory average assumes the array of measurements always exists --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index a267adaaecd9..aa98b265e16d 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "65124b4dde97934efea25fb4f603bcfe65d756db", + "revision": "cc70d60b7bdc9a25162598732b30b0e25213c167", "repo_path": "/integration/gaia-central" } From b01154e1ab56ced6dc7cbfb5b8f16d665915c3c2 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 10:56:08 -0800 Subject: [PATCH 34/44] Bumping manifests a=b2g-bump --- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/inari/sources.xml | 2 +- b2g/config/leo/sources.xml | 2 +- b2g/config/mako/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 1c6c3a456769..6fc821ec525a 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 1399f018c878..a91e6ca2d720 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 1c6c3a456769..6fc821ec525a 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 844c188a5f86..e9f7caee5b91 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 10ffefbacc8a..3ca1dd21adc5 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -10,7 +10,7 @@ - + diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml index e8471ba26d4f..a3814961b4f9 100644 --- a/b2g/config/inari/sources.xml +++ b/b2g/config/inari/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml index 1ee2259cfe0e..87fd57cafc82 100644 --- a/b2g/config/leo/sources.xml +++ b/b2g/config/leo/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml index d541da2117ca..d5f8fed184ee 100644 --- a/b2g/config/mako/sources.xml +++ b/b2g/config/mako/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index d6d4a1ef9de4..ef1e9f3b14d6 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -11,7 +11,7 @@ - + From 72b0e2b4e2f3db53391eff154c2ea8bcd6ae4dda Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 11:05:22 -0800 Subject: [PATCH 35/44] Bumping gaia.json for 1 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/09d19920c1c4 Author: sasikala.paruchuri Desc: Bug 929027 - [Messages] Multiple image sharing from Gallery to Messages r=schung Updated review comments --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index aa98b265e16d..a2a1a9e79bfc 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "cc70d60b7bdc9a25162598732b30b0e25213c167", + "revision": "09d19920c1c438f5c9aaaaed016b433fd4095a08", "repo_path": "/integration/gaia-central" } From 570b69baa3f117d43e72a9b4239c94940fce6f28 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 11:11:13 -0800 Subject: [PATCH 36/44] Bumping manifests a=b2g-bump --- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/inari/sources.xml | 2 +- b2g/config/leo/sources.xml | 2 +- b2g/config/mako/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 6fc821ec525a..a88be18fa525 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index a91e6ca2d720..eaeabfc8890e 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 6fc821ec525a..a88be18fa525 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index e9f7caee5b91..38f01e9b1f16 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 3ca1dd21adc5..99a855b5b202 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -10,7 +10,7 @@ - + diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml index a3814961b4f9..790dc4641e7e 100644 --- a/b2g/config/inari/sources.xml +++ b/b2g/config/inari/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml index 87fd57cafc82..ae0bf13fdb41 100644 --- a/b2g/config/leo/sources.xml +++ b/b2g/config/leo/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml index d5f8fed184ee..39b858da2c19 100644 --- a/b2g/config/mako/sources.xml +++ b/b2g/config/mako/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index ef1e9f3b14d6..9ccc879b32cf 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -11,7 +11,7 @@ - + From 17711c187edbe5002e4dcf8b8458bb14e44b9d1d Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 11:30:22 -0800 Subject: [PATCH 37/44] Bumping gaia.json for 3 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/2dce8ae3d194 Author: Rick Waldron Desc: Revert "Bug 929027 - [Messages] Multiple image sharing from Gallery to Messages r=schung" This reverts commit fdc8e4beba5c8279f32083bc4e702348ef22f211. ======== https://hg.mozilla.org/integration/gaia-central/rev/230677ee9536 Author: Marcus Cavanaugh Desc: Merge pull request #16455 from JonHylands/jhylands-947617-clock-power-usage Bug 947617 - fixed clock hand transform to invoke the GPU. r=mcav ======== https://hg.mozilla.org/integration/gaia-central/rev/b910d53b03da Author: Jon Hylands Desc: Bug 947617 - fixed clock hand transform to invoke the GPU --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index a2a1a9e79bfc..d004898229f0 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "09d19920c1c438f5c9aaaaed016b433fd4095a08", + "revision": "2dce8ae3d194ba25e825b0be0071afd0db65e20e", "repo_path": "/integration/gaia-central" } From 78294b4488182c5b67c6a1ba0843553c8c5d285b Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 11:36:12 -0800 Subject: [PATCH 38/44] Bumping manifests a=b2g-bump --- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/inari/sources.xml | 2 +- b2g/config/leo/sources.xml | 2 +- b2g/config/mako/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index a88be18fa525..2a31f02bc99f 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index eaeabfc8890e..425696795453 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index a88be18fa525..2a31f02bc99f 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 38f01e9b1f16..148a2232f6ef 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 99a855b5b202..68cee9344552 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -10,7 +10,7 @@ - + diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml index 790dc4641e7e..5e99247958a2 100644 --- a/b2g/config/inari/sources.xml +++ b/b2g/config/inari/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml index ae0bf13fdb41..a9cd3df4a14f 100644 --- a/b2g/config/leo/sources.xml +++ b/b2g/config/leo/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml index 39b858da2c19..72a162289f3b 100644 --- a/b2g/config/mako/sources.xml +++ b/b2g/config/mako/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 9ccc879b32cf..917ccde02feb 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -11,7 +11,7 @@ - + From 833eb191038870cff2611641f4c1dcb03be0c105 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 11:45:24 -0800 Subject: [PATCH 39/44] Bumping gaia.json for 2 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/4283fcd52197 Author: Ghislain 'Aus' Lacroix Desc: Merge pull request #15914 from nullaus/task-switcher Cards View Goodness! ======== https://hg.mozilla.org/integration/gaia-central/rev/7759d7419f23 Author: Ghislain 'Aus' Lacroix Desc: bug 939809 * added StackManager.snapshot which gets a copy of the array of applications in the stack * updated CardsView to get the list of and current app from StackManager * made StackManager ignore the System application explicitly * minor changes to help with jshint later * added 'position' getter on stack manager * added several tests to ensure that the position is current after stack has changed * moved apps used in cards view test into a variable used to populate running apps as well as the stack * removed old user ordering code (web os model task manager...) since it's incompatible with our new model and the code does not have any tests and it hasn't been enabled in a long time. We're moving on! * detect cardsview being dismissed and re-enable edge gestures * created a local help function to enable/disable edge gestures based on events received * select currently active application card in cards view when cards view is shown if no active application, then default to stack position. * cards view uses 'position' in stack internally, updated code to stop using old 'origin' name to avoid confusion. * cards view now broadcasts 'newStackPosition' when it's closed and the stack position has changed. newStackPosition contains the position of the application that will be in the top of the stack. * made edge swipe detector listen to 'cardviewclosed' with new fancy newStackPosition, this also prevents edge gestures from being re-enabled when we go back to the homescreen. * made stack manager use newStackPosition to move application that was displayed from cards view to top of stack. * fixed cards view unit tests * prevent system app from appearing in the application history stack. * updated 'runningApps' var name to 'stack' in cards view for clarity * updated 'displayedApp' var name to 'currentPosition' in cards view for clarity * added getter/setter for position in stack manager, with tests * added getter/setter for lifecycleEnabled in edge gestures as well as tests for it's usage * added test to stack manager to ensure that system app does not show up in stack * alignCurrentCard after we remove one. * fix gaia ui test. * use 'position' getter/setter everywhere in StackManager. * added test to ensure that internal _current position in StackManager doesn't accidentily become a string. r=etienne --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index d004898229f0..c9eed0cd56b8 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "2dce8ae3d194ba25e825b0be0071afd0db65e20e", + "revision": "4283fcd52197a7cb733676b7f1a1c3109702ae8e", "repo_path": "/integration/gaia-central" } From 36d11271061a0a4f5c2660e8b93d941e189372df Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 11:51:12 -0800 Subject: [PATCH 40/44] Bumping manifests a=b2g-bump --- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/inari/sources.xml | 2 +- b2g/config/leo/sources.xml | 2 +- b2g/config/mako/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index 2a31f02bc99f..cc3a46359615 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 425696795453..07059f01eceb 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index 2a31f02bc99f..cc3a46359615 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index 148a2232f6ef..ffdb14d363dc 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 68cee9344552..152bb410d472 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -10,7 +10,7 @@ - + diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml index 5e99247958a2..b6c50e7b0f08 100644 --- a/b2g/config/inari/sources.xml +++ b/b2g/config/inari/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml index a9cd3df4a14f..110c845a5daf 100644 --- a/b2g/config/leo/sources.xml +++ b/b2g/config/leo/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml index 72a162289f3b..8397db1ffc03 100644 --- a/b2g/config/mako/sources.xml +++ b/b2g/config/mako/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 917ccde02feb..1b4804c54a66 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -11,7 +11,7 @@ - + From d949be0ef11ac808606befeadbc491b7e33f4b69 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fabrice=20Desr=C3=A9?= Date: Fri, 21 Feb 2014 11:54:03 -0800 Subject: [PATCH 41/44] Backout 96eaa0f376ab (bug 961282) for smoketest regression. --- dom/apps/src/Webapps.jsm | 96 ++++++++++------------- toolkit/devtools/server/actors/webapps.js | 12 --- 2 files changed, 42 insertions(+), 66 deletions(-) diff --git a/dom/apps/src/Webapps.jsm b/dom/apps/src/Webapps.jsm index f6f8b490928c..d7aac84d7aca 100755 --- a/dom/apps/src/Webapps.jsm +++ b/dom/apps/src/Webapps.jsm @@ -110,14 +110,14 @@ XPCOMUtils.defineLazyGetter(this, "updateSvc", function() { }); #ifdef MOZ_WIDGET_GONK - const DIRECTORY_KEY = "webappsDir"; + const DIRECTORY_NAME = "webappsDir"; #elifdef ANDROID - const DIRECTORY_KEY = "webappsDir"; + const DIRECTORY_NAME = "webappsDir"; #else // If we're executing in the context of the webapp runtime, the data files // are in a different directory (currently the Firefox profile that installed // the webapp); otherwise, they're in the current profile. - const DIRECTORY_KEY = WEBAPP_RUNTIME ? "WebappRegD" : "ProfD"; + const DIRECTORY_NAME = WEBAPP_RUNTIME ? "WebappRegD" : "ProfD"; #endif // We'll use this to identify privileged apps that have been preinstalled @@ -161,8 +161,8 @@ this.DOMApplicationRegistry = { AppDownloadManager.registerCancelFunction(this.cancelDownload.bind(this)); - this.appsFile = OS.Path.join(Services.dirsvc.get(DIRECTORY_KEY, Ci.nsIFile).path, - "webapps", "webapps.json"); + this.appsFile = FileUtils.getFile(DIRECTORY_NAME, + ["webapps", "webapps.json"], true).path; this.loadAndUpdateApps(); }, @@ -171,10 +171,7 @@ this.DOMApplicationRegistry = { loadCurrentRegistry: function() { return this._loadJSONAsync(this.appsFile).then((aData) => { if (!aData) { - // If _loadJSONAsync returns null, we're probably in the firstrun case - // so we may need to create the "webapps" directory. - return OS.File.makeDir(OS.Path.dirname(this.appsFile), - { ignoreExisting: true }); + return; } this.webapps = aData; @@ -410,13 +407,8 @@ this.DOMApplicationRegistry = { debug("Installing 3rd party app : " + aId + " from " + baseDir.path); - // We copy this app to DIRECTORY_KEY/$aId, and set the base path as needed. - let destDir = this._getAppDir(aId); - try { - destDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); - } catch (ex if ex.result == Cr.NS_ERROR_FILE_ALREADY_EXISTS) { - // Ignore the exception if the directory already exists. - } + // We copy this app to DIRECTORY_NAME/$aId, and set the base path as needed. + let destDir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", aId], true, true); filesToMove.forEach(function(aFile) { let file = baseDir.clone(); @@ -1205,7 +1197,7 @@ this.DOMApplicationRegistry = { }, _getAppDir: function(aId) { - return FileUtils.getDir(DIRECTORY_KEY, ["webapps", aId], false, true); + return FileUtils.getDir(DIRECTORY_NAME, ["webapps", aId], true, true); }, _writeFile: function(aPath, aData) { @@ -1345,8 +1337,11 @@ this.DOMApplicationRegistry = { // We need to get the update manifest here, not the webapp manifest. // If this is an update, the update manifest is staged. - let file = this._getAppDir(id); - file.append(isUpdate ? "staged-update.webapp" : "update.webapp"); + let file = FileUtils.getFile(DIRECTORY_NAME, + ["webapps", id, + isUpdate ? "staged-update.webapp" + : "update.webapp"], + true); if (!file.exists()) { // This is a hosted app, let's check if it has an appcache @@ -1436,13 +1431,13 @@ this.DOMApplicationRegistry = { // We need to get the old manifest to unregister web activities. this.getManifestFor(aManifestURL).then((aOldManifest) => { // Move the application.zip and manifest.webapp files out of TmpD - let tmpDir = FileUtils.getDir("TmpD", ["webapps", id], false, true); + let tmpDir = FileUtils.getDir("TmpD", ["webapps", id], true, true); let manFile = tmpDir.clone(); manFile.append("manifest.webapp"); let appFile = tmpDir.clone(); appFile.append("application.zip"); - let dir = this._getAppDir(id); + let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps", id], true, true); appFile.moveTo(dir, "application.zip"); manFile.moveTo(dir, "manifest.webapp"); @@ -2205,14 +2200,16 @@ this.DOMApplicationRegistry = { }, denyInstall: function(aData) { - Task.spawn(function*() { - let packageId = aData.app.packageId; - if (packageId) { - let dir = OS.Path.join(OS.Constants.Path.tmpDir, "webapps", packageId); - yield OS.File.removeDir(dir, { ignoreAbsent: true }); + let packageId = aData.app.packageId; + if (packageId) { + let dir = FileUtils.getDir("TmpD", ["webapps", packageId], + true, true); + try { + dir.remove(true); + } catch(e) { } - aData.mm.sendAsyncMessage("Webapps:Install:Return:KO", aData); - }).then(null, Cu.reportError); + } + aData.mm.sendAsyncMessage("Webapps:Install:Return:KO", aData); }, // This function is called after we called the onsuccess callback on the @@ -2314,13 +2311,14 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL, return appObject; }, - _writeManifestFile: function(aDir, aIsPackage, aJsonManifest) { + _writeManifestFile: function(aId, aIsPackage, aJsonManifest) { debug("_writeManifestFile"); // For packaged apps, keep the update manifest distinct from the app manifest. let manifestName = aIsPackage ? "update.webapp" : "manifest.webapp"; - let manFile = OS.Path.join(aDir, manifestName); + let dir = this._getAppDir(aId).path; + let manFile = OS.Path.join(dir, manifestName); this._writeFile(manFile, JSON.stringify(aJsonManifest)); }, @@ -2346,18 +2344,10 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL, localId = this._nextLocalId(); } - // Create the app directory - let dir = this._getAppDir(id); - try { - dir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); - } catch (ex if ex.result == Cr.NS_ERROR_FILE_ALREADY_EXISTS) { - // Ignore the exception if the directory already exists. - } - let app = this._setupApp(aData, id); let jsonManifest = aData.isPackage ? app.updateManifest : app.manifest; - this._writeManifestFile(dir.path, aData.isPackage, jsonManifest); + this._writeManifestFile(id, aData.isPackage, jsonManifest); debug("app.origin: " + app.origin); let manifest = new ManifestHelper(jsonManifest, app.origin); @@ -2477,14 +2467,11 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL, debug("_onDownloadPackage"); // Success! Move the zip out of TmpD. let app = this.webapps[aId]; - - let tmpDir = FileUtils.getDir("TmpD", ["webapps", aId], false, true); - let zipFile = tmpDir.clone(); - zipFile.append("application.zip"); - + let zipFile = + FileUtils.getFile("TmpD", ["webapps", aId, "application.zip"], true); let dir = this._getAppDir(aId); zipFile.moveTo(dir, "application.zip"); - + let tmpDir = FileUtils.getDir("TmpD", ["webapps", aId], true, true); try { tmpDir.remove(true); } catch(e) { } @@ -2577,7 +2564,7 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL, if (!this._manifestCache[id]) { // the manifest file used to be named manifest.json, so fallback on this. let baseDir = this.webapps[id].basePath == this.getCoreAppsBasePath() - ? "coreAppsDir" : DIRECTORY_KEY; + ? "coreAppsDir" : DIRECTORY_NAME; let dir = FileUtils.getDir(baseDir, ["webapps", id], false, true); @@ -2705,7 +2692,7 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL, debug("No deviceStorage"); // deviceStorage isn't available, so use FileUtils to find the size of // available storage. - let dir = FileUtils.getDir(DIRECTORY_KEY, ["webapps"], false, true); + let dir = FileUtils.getDir(DIRECTORY_NAME, ["webapps"], true, true); try { let sufficientStorage = this._checkDownloadSize(dir.diskSpaceAvailable, aNewApp); @@ -2962,9 +2949,10 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL, eventType: ["downloadsuccess", "downloadapplied"] }); }); - - let appDir = OS.Path.join(OS.Constants.Path.tmpDir, "webapps", aId); - OS.File.removeDir(appDir, { ignoreAbsent: true }); + let file = FileUtils.getFile("TmpD", ["webapps", aId], false); + if (file && file.exists()) { + file.remove(true); + } }, _openAndReadPackage: function(aZipFile, aOldApp, aNewApp, aIsLocalFileInstall, @@ -3229,9 +3217,9 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL, this.webapps[newId] = aOldApp; delete this.webapps[oldId]; // Rename the directories where the files are installed. - [DIRECTORY_KEY, "TmpD"].forEach(function(aDir) { - let parent = FileUtils.getDir(aDir, ["webapps"], false, true); - let dir = FileUtils.getDir(aDir, ["webapps", oldId], false, true); + [DIRECTORY_NAME, "TmpD"].forEach(function(aDir) { + let parent = FileUtils.getDir(aDir, ["webapps"], true, true); + let dir = FileUtils.getDir(aDir, ["webapps", oldId], true, true); dir.moveTo(parent, newId); }); // Signals that we need to swap the old id with the new app. @@ -3308,7 +3296,7 @@ onInstallSuccessAck: function onInstallSuccessAck(aManifestURL, // Removes the directory we created, and sends an error to the DOM side. _revertDownloadPackage: function(aId, aOldApp, aNewApp, aIsUpdate, aError) { debug("Cleanup: " + aError + "\n" + aError.stack); - let dir = FileUtils.getDir("TmpD", ["webapps", aId], false, true); + let dir = FileUtils.getDir("TmpD", ["webapps", aId], true, true); try { dir.remove(true); } catch (e) { } diff --git a/toolkit/devtools/server/actors/webapps.js b/toolkit/devtools/server/actors/webapps.js index c71bba8b6139..2e837bf0f757 100644 --- a/toolkit/devtools/server/actors/webapps.js +++ b/toolkit/devtools/server/actors/webapps.js @@ -275,12 +275,6 @@ WebappsActor.prototype = { // Move manifest.webapp to the destination directory. // The destination directory for this app. let installDir = DOMApplicationRegistry._getAppDir(aId); - try { - installDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); - } catch (ex if ex.result == Cr.NS_ERROR_FILE_ALREADY_EXISTS) { - // Ignore the exception if the directory already exists. - } - if (aManifest) { let manFile = OS.Path.join(installDir.path, "manifest.webapp"); return DOMApplicationRegistry._writeFile(manFile, JSON.stringify(aManifest)).then(() => { @@ -400,12 +394,6 @@ WebappsActor.prototype = { // we can move application.zip to the destination directory, and // extract manifest.webapp there. let installDir = DOMApplicationRegistry._getAppDir(id); - try { - installDir.create(Ci.nsIFile.DIRECTORY_TYPE, FileUtils.PERMS_DIRECTORY); - } catch (ex if ex.result == Cr.NS_ERROR_FILE_ALREADY_EXISTS) { - // Ignore the exception if the directory already exists. - } - let manFile = installDir.clone(); manFile.append("manifest.webapp"); zipReader.extract("manifest.webapp", manFile); From dc4498390b7c53a1476e7ac872a38ff4233dd79b Mon Sep 17 00:00:00 2001 From: Reuben Morais Date: Fri, 21 Feb 2014 16:56:19 -0300 Subject: [PATCH 42/44] Bug 951785 - For browser content, unless explicitly denied for the app, consider permissions for the requesting origin, not the requesting app. r=sicking --HG-- extra : rebase_source : 83d71d004f193c597f19a3cead1682698579d3d5 --- dom/ipc/AppProcessChecker.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/dom/ipc/AppProcessChecker.cpp b/dom/ipc/AppProcessChecker.cpp index 9393ed1159ee..ecb214dcf2a4 100644 --- a/dom/ipc/AppProcessChecker.cpp +++ b/dom/ipc/AppProcessChecker.cpp @@ -228,6 +228,7 @@ CheckPermission(PContentParent* aActor, uint32_t appPerm = nsIPermissionManager::UNKNOWN_ACTION; nsresult rv = pm->TestExactPermissionFromPrincipal(appPrincipal, aPermission, &appPerm); NS_ENSURE_SUCCESS(rv, nsIPermissionManager::UNKNOWN_ACTION); + // Setting to "deny" in the settings UI should deny everywhere. if (appPerm == nsIPermissionManager::UNKNOWN_ACTION || appPerm == nsIPermissionManager::DENY_ACTION) { return appPerm; @@ -241,6 +242,15 @@ CheckPermission(PContentParent* aActor, return permission; } + // For browser content (and if the app hasn't explicitly denied this), + // consider the requesting origin, not the app. + if (appPerm == nsIPermissionManager::PROMPT_ACTION && + aPrincipal->GetIsInBrowserElement()) { + return permission; + } + + // Setting to "prompt" in the settings UI should prompt everywhere in + // non-browser content. if (appPerm == nsIPermissionManager::PROMPT_ACTION || permission == nsIPermissionManager::PROMPT_ACTION) { return nsIPermissionManager::PROMPT_ACTION; From 0f671f581bd4a488f5f93230984fcb985ddf635e Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 12:07:49 -0800 Subject: [PATCH 43/44] Bumping gaia.json for 1 gaia revision(s) a=gaia-bump ======== https://hg.mozilla.org/integration/gaia-central/rev/2741c63d7513 Author: Marcus Cavanaugh Desc: Revert "Merge pull request #16455 from JonHylands/jhylands-947617-clock-power-usage" This reverts commit 7e1c255c20b64d09ef0e021b8b9d7377840d4061, reversing changes made to fdc8e4beba5c8279f32083bc4e702348ef22f211. --- b2g/config/gaia.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index c9eed0cd56b8..665dd0399483 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "4283fcd52197a7cb733676b7f1a1c3109702ae8e", + "revision": "2741c63d7513126ea0a7f033266a4230334dfc53", "repo_path": "/integration/gaia-central" } From 6aed01878cce48391e26c5e405f1f5a22530d762 Mon Sep 17 00:00:00 2001 From: B2G Bumper Bot Date: Fri, 21 Feb 2014 12:07:58 -0800 Subject: [PATCH 44/44] Bumping manifests a=b2g-bump --- b2g/config/emulator-ics/sources.xml | 2 +- b2g/config/emulator-jb/sources.xml | 2 +- b2g/config/emulator/sources.xml | 2 +- b2g/config/hamachi/sources.xml | 2 +- b2g/config/helix/sources.xml | 2 +- b2g/config/inari/sources.xml | 2 +- b2g/config/leo/sources.xml | 2 +- b2g/config/mako/sources.xml | 2 +- b2g/config/wasabi/sources.xml | 2 +- 9 files changed, 9 insertions(+), 9 deletions(-) diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml index cc3a46359615..223bdc6f087e 100644 --- a/b2g/config/emulator-ics/sources.xml +++ b/b2g/config/emulator-ics/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml index 07059f01eceb..2ef27e485e60 100644 --- a/b2g/config/emulator-jb/sources.xml +++ b/b2g/config/emulator-jb/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml index cc3a46359615..223bdc6f087e 100644 --- a/b2g/config/emulator/sources.xml +++ b/b2g/config/emulator/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml index ffdb14d363dc..fba31f092eb8 100644 --- a/b2g/config/hamachi/sources.xml +++ b/b2g/config/hamachi/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml index 152bb410d472..60c5956520b0 100644 --- a/b2g/config/helix/sources.xml +++ b/b2g/config/helix/sources.xml @@ -10,7 +10,7 @@ - + diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml index b6c50e7b0f08..319b81114e7a 100644 --- a/b2g/config/inari/sources.xml +++ b/b2g/config/inari/sources.xml @@ -12,7 +12,7 @@ - + diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml index 110c845a5daf..7156b440282c 100644 --- a/b2g/config/leo/sources.xml +++ b/b2g/config/leo/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml index 8397db1ffc03..77258c7d1e4f 100644 --- a/b2g/config/mako/sources.xml +++ b/b2g/config/mako/sources.xml @@ -11,7 +11,7 @@ - + diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml index 1b4804c54a66..c66309ea90d6 100644 --- a/b2g/config/wasabi/sources.xml +++ b/b2g/config/wasabi/sources.xml @@ -11,7 +11,7 @@ - +