summaryrefslogtreecommitdiffstats
path: root/application/palemoon/base/content/newtab/page.js
diff options
context:
space:
mode:
Diffstat (limited to 'application/palemoon/base/content/newtab/page.js')
-rw-r--r--application/palemoon/base/content/newtab/page.js221
1 files changed, 189 insertions, 32 deletions
diff --git a/application/palemoon/base/content/newtab/page.js b/application/palemoon/base/content/newtab/page.js
index fc836a55e..7117d4527 100644
--- a/application/palemoon/base/content/newtab/page.js
+++ b/application/palemoon/base/content/newtab/page.js
@@ -4,6 +4,9 @@
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#endif
+// The amount of time we wait while coalescing updates for hidden pages.
+const SCHEDULE_UPDATE_TIMEOUT_MS = 1000;
+
/**
* This singleton represents the whole 'New Tab Page' and takes care of
* initializing all its components.
@@ -18,10 +21,15 @@ var gPage = {
// Listen for 'unload' to unregister this page.
addEventListener("unload", this, false);
-
+
// Listen for toggle button clicks.
let button = document.getElementById("newtab-toggle");
- button.addEventListener("click", this, false);
+ button.addEventListener("click", e => this.toggleEnabled(e));
+
+ // XXX bug 991111 - Not all click events are correctly triggered when
+ // listening from xhtml nodes -- in particular middle clicks on sites, so
+ // listen from the xul window and filter then delegate
+ addEventListener("click", this, false);
// Check if the new tab feature is enabled.
let enabled = gAllPages.enabled;
@@ -34,26 +42,65 @@ var gPage = {
/**
* Listens for notifications specific to this page.
*/
- observe: function Page_observe() {
- let enabled = gAllPages.enabled;
- this._updateAttributes(enabled);
+ observe: function Page_observe(aSubject, aTopic, aData) {
+ if (aTopic == "nsPref:changed") {
+ let enabled = gAllPages.enabled;
+ this._updateAttributes(enabled);
- // Initialize the whole page if we haven't done that, yet.
- if (enabled) {
- this._init();
- } else {
- gUndoDialog.hide();
+ // Update thumbnails to the new enhanced setting
+ if (aData == "browser.newtabpage.enhanced") {
+ this.update();
+ }
+
+ // Initialize the whole page if we haven't done that, yet.
+ if (enabled) {
+ this._init();
+ } else {
+ gUndoDialog.hide();
+ }
+ } else if (aTopic == "page-thumbnail:create" && gGrid.ready) {
+ for (let site of gGrid.sites) {
+ if (site && site.url === aData) {
+ site.refreshThumbnail();
+ }
+ }
}
},
/**
- * Updates the whole page and the grid when the storage has changed.
+ * Updates the page's grid right away for visible pages. If the page is
+ * currently hidden, i.e. in a background tab or in the preloader, then we
+ * batch multiple update requests and refresh the grid once after a short
+ * delay. Accepts a single parameter the specifies the reason for requesting
+ * a page update. The page may decide to delay or prevent a requested updated
+ * based on the given reason.
*/
- update: function Page_update() {
- // The grid might not be ready yet as we initialize it asynchronously.
- if (gGrid.ready) {
- gGrid.refresh();
+ update(reason = "") {
+ // Update immediately if we're visible.
+ if (!document.hidden) {
+ // Ignore updates where reason=links-changed as those signal that the
+ // provider's set of links changed. We don't want to update visible pages
+ // in that case, it is ok to wait until the user opens the next tab.
+ if (reason != "links-changed" && gGrid.ready) {
+ gGrid.refresh();
+ }
+
+ return;
+ }
+
+ // Bail out if we scheduled before.
+ if (this._scheduleUpdateTimeout) {
+ return;
}
+
+ this._scheduleUpdateTimeout = setTimeout(() => {
+ // Refresh if the grid is ready.
+ if (gGrid.ready) {
+ gGrid.refresh();
+ }
+
+ this._scheduleUpdateTimeout = null;
+ }, SCHEDULE_UPDATE_TIMEOUT_MS);
},
/**
@@ -66,19 +113,28 @@ var gPage = {
this._initialized = true;
- gLinks.populateCache(function () {
- // Initialize and render the grid.
- gGrid.init();
+ // Set submit button label for when CSS background are disabled (e.g.
+ // high contrast mode).
+ document.getElementById("searchSubmit").value =
+ document.body.getAttribute("dir") == "ltr" ? "\u25B6" : "\u25C0";
- // Initialize the drop target shim.
- gDropTargetShim.init();
+ if (document.hidden) {
+ addEventListener("visibilitychange", this);
+ } else {
+ setTimeout(() => this.onPageFirstVisible());
+ }
+
+ // Initialize and render the grid.
+ gGrid.init();
+
+ // Initialize the drop target shim.
+ gDropTargetShim.init();
#ifdef XP_MACOSX
- // Workaround to prevent a delay on MacOSX due to a slow drop animation.
- document.addEventListener("dragover", this, false);
- document.addEventListener("drop", this, false);
+ // Workaround to prevent a delay on MacOSX due to a slow drop animation.
+ document.addEventListener("dragover", this, false);
+ document.addEventListener("drop", this, false);
#endif
- }.bind(this));
},
/**
@@ -87,7 +143,7 @@ var gPage = {
*/
_updateAttributes: function Page_updateAttributes(aValue) {
// Set the nodes' states.
- let nodeSelector = "#newtab-scrollbox, #newtab-toggle, #newtab-grid";
+ let nodeSelector = "#newtab-grid, #searchContainer";
for (let node of document.querySelectorAll(nodeSelector)) {
if (aValue)
node.removeAttribute("page-disabled");
@@ -98,15 +154,28 @@ var gPage = {
// Enables/disables the control and link elements.
let inputSelector = ".newtab-control, .newtab-link";
for (let input of document.querySelectorAll(inputSelector)) {
- if (aValue)
+ if (aValue)
input.removeAttribute("tabindex");
else
input.setAttribute("tabindex", "-1");
}
+ },
- // Update the toggle button's title.
- let toggle = document.getElementById("newtab-toggle");
- toggle.setAttribute("title", newTabString(aValue ? "hide" : "show"));
+ /**
+ * Handles unload event
+ */
+ _handleUnloadEvent: function Page_handleUnloadEvent() {
+ gAllPages.unregister(this);
+ // compute page life-span and send telemetry probe: using milli-seconds will leave
+ // many low buckets empty. Instead we use half-second precision to make low end
+ // of histogram linear and not lose the change in user attention
+ let delta = Math.round((Date.now() - this._firstVisibleTime) / 500);
+ if (this._suggestedTilePresent) {
+ Services.telemetry.getHistogramById("NEWTAB_PAGE_LIFE_SPAN_SUGGESTED").add(delta);
+ }
+ else {
+ Services.telemetry.getHistogramById("NEWTAB_PAGE_LIFE_SPAN").add(delta);
+ }
},
/**
@@ -114,11 +183,22 @@ var gPage = {
*/
handleEvent: function Page_handleEvent(aEvent) {
switch (aEvent.type) {
+ case "load":
+ this.onPageVisibleAndLoaded();
+ break;
case "unload":
- gAllPages.unregister(this);
+ this._handleUnloadEvent();
break;
case "click":
- gAllPages.enabled = !gAllPages.enabled;
+ let {button, target} = aEvent;
+ // Go up ancestors until we find a Site or not
+ while (target) {
+ if (target.hasOwnProperty("_newtabSite")) {
+ target._newtabSite.onClick(aEvent);
+ break;
+ }
+ target = target.parentNode;
+ }
break;
case "dragover":
if (gDrag.isValid(aEvent) && gDrag.draggedSite)
@@ -130,6 +210,83 @@ var gPage = {
aEvent.stopPropagation();
}
break;
+ case "visibilitychange":
+ // Cancel any delayed updates for hidden pages now that we're visible.
+ if (this._scheduleUpdateTimeout) {
+ clearTimeout(this._scheduleUpdateTimeout);
+ this._scheduleUpdateTimeout = null;
+
+ // An update was pending so force an update now.
+ this.update();
+ }
+
+ setTimeout(() => this.onPageFirstVisible());
+ removeEventListener("visibilitychange", this);
+ break;
+ }
+ },
+
+ onPageFirstVisible: function () {
+ // Record another page impression.
+ Services.telemetry.getHistogramById("NEWTAB_PAGE_SHOWN").add(true);
+
+ for (let site of gGrid.sites) {
+ if (site) {
+ // The site may need to modify and/or re-render itself if
+ // something changed after newtab was created by preloader.
+ // For example, the suggested tile endTime may have passed.
+ site.onFirstVisible();
+ }
+ }
+
+ // save timestamp to compute page life-span delta
+ this._firstVisibleTime = Date.now();
+
+ if (document.readyState == "complete") {
+ this.onPageVisibleAndLoaded();
+ } else {
+ addEventListener("load", this);
}
- }
+ },
+
+ onPageVisibleAndLoaded() {
+ // Send the index of the last visible tile.
+ this.reportLastVisibleTileIndex();
+ // Maybe tell the user they can undo an initial automigration
+ this.maybeShowAutoMigrationUndoNotification();
+ },
+
+ reportLastVisibleTileIndex() {
+ let cwu = window.QueryInterface(Ci.nsIInterfaceRequestor)
+ .getInterface(Ci.nsIDOMWindowUtils);
+
+ let rect = cwu.getBoundsWithoutFlushing(gGrid.node);
+ let nodes = cwu.nodesFromRect(rect.left, rect.top, 0, rect.width,
+ rect.height, 0, true, false);
+
+ let i = -1;
+ let lastIndex = -1;
+ let sites = gGrid.sites;
+
+ for (let node of nodes) {
+ if (node.classList && node.classList.contains("newtab-cell")) {
+ if (sites[++i]) {
+ lastIndex = i;
+ if (sites[i].link.targetedSite) {
+ // record that suggested tile is shown to use suggested-tiles-histogram
+ this._suggestedTilePresent = true;
+ }
+ }
+ }
+ }
+ },
+
+ toggleEnabled: function(aEvent) {
+ gAllPages.enabled = !gAllPages.enabled;
+ event.stopPropagation();
+ },
+
+ maybeShowAutoMigrationUndoNotification() {
+ // sendAsyncMessage("NewTab:MaybeShowAutoMigrationUndoNotification");
+ },
};