diff --git a/CLOBBER b/CLOBBER
index 4bb16a505313..45331ae0d3b9 100644
--- a/CLOBBER
+++ b/CLOBBER
@@ -22,4 +22,4 @@
# changes to stick? As of bug 928195, this shouldn't be necessary! Please
# don't change CLOBBER for WebIDL changes any more.
-Bug 957865 - Non-clobbered ASAN builds were failing all mochitests after the clang upgrade
+Bug 989137 - /experiments needed clobber to build on OSX
diff --git a/b2g/config/emulator-ics/sources.xml b/b2g/config/emulator-ics/sources.xml
index f7b8cee5db43..b7c296e7b253 100644
--- a/b2g/config/emulator-ics/sources.xml
+++ b/b2g/config/emulator-ics/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/emulator-jb/sources.xml b/b2g/config/emulator-jb/sources.xml
index e2443e11c4b6..f2d9c10e89dd 100644
--- a/b2g/config/emulator-jb/sources.xml
+++ b/b2g/config/emulator-jb/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/emulator-kk/sources.xml b/b2g/config/emulator-kk/sources.xml
index 972ec30fa4d2..2a7578e28989 100644
--- a/b2g/config/emulator-kk/sources.xml
+++ b/b2g/config/emulator-kk/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/emulator/sources.xml b/b2g/config/emulator/sources.xml
index f7b8cee5db43..b7c296e7b253 100644
--- a/b2g/config/emulator/sources.xml
+++ b/b2g/config/emulator/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json
index 36530b90a5f8..ddd5c1a005a3 100644
--- a/b2g/config/gaia.json
+++ b/b2g/config/gaia.json
@@ -4,6 +4,6 @@
"remote": "",
"branch": ""
},
- "revision": "6c593455e3d1292120a6f3d41ec5d06bc91019f1",
+ "revision": "f3575a1613e6c94fbc6b2ae01fd00130ee1b3f8a",
"repo_path": "/integration/gaia-central"
}
diff --git a/b2g/config/hamachi/sources.xml b/b2g/config/hamachi/sources.xml
index e9ccd1391aa1..a46e93397af2 100644
--- a/b2g/config/hamachi/sources.xml
+++ b/b2g/config/hamachi/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/helix/sources.xml b/b2g/config/helix/sources.xml
index 9bf5e0fbd710..88c7a676f985 100644
--- a/b2g/config/helix/sources.xml
+++ b/b2g/config/helix/sources.xml
@@ -15,7 +15,7 @@
-
+
diff --git a/b2g/config/inari/sources.xml b/b2g/config/inari/sources.xml
index 307d4393cf9e..fbdb0d184b9c 100644
--- a/b2g/config/inari/sources.xml
+++ b/b2g/config/inari/sources.xml
@@ -19,7 +19,7 @@
-
+
diff --git a/b2g/config/leo/sources.xml b/b2g/config/leo/sources.xml
index 7b27d398a221..f319bfd38c4e 100644
--- a/b2g/config/leo/sources.xml
+++ b/b2g/config/leo/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/mako/sources.xml b/b2g/config/mako/sources.xml
index fb6187a4609c..69308a7d3f6d 100644
--- a/b2g/config/mako/sources.xml
+++ b/b2g/config/mako/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/b2g/config/wasabi/sources.xml b/b2g/config/wasabi/sources.xml
index b2c86ac773eb..998b1fa121fc 100644
--- a/b2g/config/wasabi/sources.xml
+++ b/b2g/config/wasabi/sources.xml
@@ -17,7 +17,7 @@
-
+
diff --git a/browser/base/content/browser.js b/browser/base/content/browser.js
index 27b6b036ed17..33c37b23f78a 100644
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -1187,22 +1187,20 @@ var gBrowserInit = {
let windows8WindowFrameColor = Cu.import("resource:///modules/Windows8WindowFrameColor.jsm", {}).Windows8WindowFrameColor;
let windowFrameColor = windows8WindowFrameColor.get();
- // Formula from W3C Techniques For Accessibility Evaluation And
- // Repair Tools, Section 2.2 http://www.w3.org/TR/AERT#color
- let brightnessThreshold = 125;
- let colorThreshold = 500;
- let bY = windowFrameColor[0] * .299 +
- windowFrameColor[1] * .587 +
- windowFrameColor[2] * .114;
- let fY = 0; // Default to black for foreground text.
- let brightnessDifference = Math.abs(bY - fY);
- // Color difference calculation is simplified because black is 0 for R,G,B.
- let colorDifference = windowFrameColor[0] + windowFrameColor[1] + windowFrameColor[2];
-
- // Brightness is defined within {0, 255}. Set an attribute
- // if the window frame color doesn't reach these thresholds
- // so the theme can be adjusted for readability.
- if (brightnessDifference < brightnessThreshold && colorDifference < colorThreshold) {
+ // Formula from W3C's WCAG 2.0 spec's color ratio and relative luminance,
+ // section 1.3.4, http://www.w3.org/TR/WCAG20/ .
+ windowFrameColor = windowFrameColor.map((color) => {
+ if (color <= 10) {
+ return color / 255 / 12.92;
+ }
+ return Math.pow(((color / 255) + 0.055) / 1.055, 2.4);
+ });
+ let backgroundLuminance = windowFrameColor[0] * 0.2126 +
+ windowFrameColor[1] * 0.7152 +
+ windowFrameColor[2] * 0.0722;
+ let foregroundLuminance = 0; // Default to black for foreground text.
+ let contrastRatio = (backgroundLuminance + 0.05) / (foregroundLuminance + 0.05);
+ if (contrastRatio < 3) {
document.documentElement.setAttribute("darkwindowframe", "true");
}
}
@@ -1882,55 +1880,47 @@ function loadURI(uri, referrer, postData, allowThirdPartyFixup) {
} catch (e) {}
}
-function getShortcutOrURIAndPostData(aURL) {
- return Task.spawn(function() {
- let mayInheritPrincipal = false;
- let postData = null;
- let shortcutURL = null;
- let keyword = aURL;
- let param = "";
+function getShortcutOrURIAndPostData(aURL, aCallback) {
+ let mayInheritPrincipal = false;
+ let postData = null;
+ let shortcutURL = null;
+ let keyword = aURL;
+ let param = "";
- let offset = aURL.indexOf(" ");
- if (offset > 0) {
- keyword = aURL.substr(0, offset);
- param = aURL.substr(offset + 1);
- }
+ let offset = aURL.indexOf(" ");
+ if (offset > 0) {
+ keyword = aURL.substr(0, offset);
+ param = aURL.substr(offset + 1);
+ }
- let engine = Services.search.getEngineByAlias(keyword);
- if (engine) {
- let submission = engine.getSubmission(param);
- postData = submission.postData;
- throw new Task.Result({ postData: submission.postData,
- url: submission.uri.spec,
- mayInheritPrincipal: mayInheritPrincipal });
- }
+ let engine = Services.search.getEngineByAlias(keyword);
+ if (engine) {
+ let submission = engine.getSubmission(param);
+ postData = submission.postData;
+ aCallback({ postData: submission.postData, url: submission.uri.spec,
+ mayInheritPrincipal: mayInheritPrincipal });
+ return;
+ }
- [shortcutURL, postData] =
- PlacesUtils.getURLAndPostDataForKeyword(keyword);
+ [shortcutURL, postData] =
+ PlacesUtils.getURLAndPostDataForKeyword(keyword);
- if (!shortcutURL)
- throw new Task.Result({ postData: postData, url: aURL,
- mayInheritPrincipal: mayInheritPrincipal });
+ if (!shortcutURL) {
+ aCallback({ postData: postData, url: aURL,
+ mayInheritPrincipal: mayInheritPrincipal });
+ return;
+ }
- let escapedPostData = "";
- if (postData)
- escapedPostData = unescape(postData);
+ let escapedPostData = "";
+ if (postData)
+ escapedPostData = unescape(postData);
- if (/%s/i.test(shortcutURL) || /%s/i.test(escapedPostData)) {
- let charset = "";
- const re = /^(.*)\&mozcharset=([a-zA-Z][_\-a-zA-Z0-9]+)\s*$/;
- let matches = shortcutURL.match(re);
- if (matches)
- [, shortcutURL, charset] = matches;
- else {
- // Try to get the saved character-set.
- try {
- // makeURI throws if URI is invalid.
- // Will return an empty string if character-set is not found.
- charset = yield PlacesUtils.getCharsetForURI(makeURI(shortcutURL));
- } catch (e) {}
- }
+ if (/%s/i.test(shortcutURL) || /%s/i.test(escapedPostData)) {
+ let charset = "";
+ const re = /^(.*)\&mozcharset=([a-zA-Z][_\-a-zA-Z0-9]+)\s*$/;
+ let matches = shortcutURL.match(re);
+ let continueOperation = function () {
// encodeURIComponent produces UTF-8, and cannot be used for other charsets.
// escape() works in those cases, but it doesn't uri-encode +, @, and /.
// Therefore we need to manually replace these ASCII characters by their
@@ -1948,23 +1938,45 @@ function getShortcutOrURIAndPostData(aURL) {
if (/%s/i.test(escapedPostData)) // POST keyword
postData = getPostDataStream(escapedPostData, param, encodedParam,
"application/x-www-form-urlencoded");
- }
- else if (param) {
- // This keyword doesn't take a parameter, but one was provided. Just return
- // the original URL.
- postData = null;
- throw new Task.Result({ postData: postData, url: aURL,
- mayInheritPrincipal: mayInheritPrincipal });
+ // This URL came from a bookmark, so it's safe to let it inherit the current
+ // document's principal.
+ mayInheritPrincipal = true;
+
+ aCallback({ postData: postData, url: shortcutURL,
+ mayInheritPrincipal: mayInheritPrincipal });
}
+ if (matches) {
+ [, shortcutURL, charset] = matches;
+ continueOperation();
+ } else {
+ // Try to get the saved character-set.
+ // makeURI throws if URI is invalid.
+ // Will return an empty string if character-set is not found.
+ try {
+ PlacesUtils.getCharsetForURI(makeURI(shortcutURL))
+ .then(c => { charset = c; continueOperation(); });
+ } catch (ex) {
+ continueOperation();
+ }
+ }
+ }
+ else if (param) {
+ // This keyword doesn't take a parameter, but one was provided. Just return
+ // the original URL.
+ postData = null;
+
+ aCallback({ postData: postData, url: aURL,
+ mayInheritPrincipal: mayInheritPrincipal });
+ } else {
// This URL came from a bookmark, so it's safe to let it inherit the current
// document's principal.
mayInheritPrincipal = true;
- throw new Task.Result({ postData: postData, url: shortcutURL,
- mayInheritPrincipal: mayInheritPrincipal });
- });
+ aCallback({ postData: postData, url: shortcutURL,
+ mayInheritPrincipal: mayInheritPrincipal });
+ }
}
function getPostDataStream(aStringData, aKeyword, aEncKeyword, aType) {
@@ -2765,8 +2777,7 @@ var newTabButtonObserver = {
onDrop: function (aEvent)
{
let url = browserDragAndDrop.drop(aEvent, { });
- Task.spawn(function() {
- let data = yield getShortcutOrURIAndPostData(url);
+ getShortcutOrURIAndPostData(url, data => {
if (data.url) {
// allow third-party services to fixup this URL
openNewTabWith(data.url, null, data.postData, aEvent, true);
@@ -2786,8 +2797,7 @@ var newWindowButtonObserver = {
onDrop: function (aEvent)
{
let url = browserDragAndDrop.drop(aEvent, { });
- Task.spawn(function() {
- let data = yield getShortcutOrURIAndPostData(url);
+ getShortcutOrURIAndPostData(url, data => {
if (data.url) {
// allow third-party services to fixup this URL
openNewWindowWith(data.url, null, data.postData, true);
@@ -5129,8 +5139,7 @@ function middleMousePaste(event) {
lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
}
- Task.spawn(function() {
- let data = yield getShortcutOrURIAndPostData(clipboard);
+ getShortcutOrURIAndPostData(clipboard, data => {
try {
makeURI(data.url);
} catch (ex) {
@@ -5161,8 +5170,7 @@ function handleDroppedLink(event, url, name)
{
let lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
- Task.spawn(function() {
- let data = yield getShortcutOrURIAndPostData(url);
+ getShortcutOrURIAndPostData(url, data => {
if (data.url &&
lastLocationChange == gBrowser.selectedBrowser.lastLocationChange)
loadURI(data.url, null, data.postData, false);
diff --git a/browser/base/content/newtab/grid.js b/browser/base/content/newtab/grid.js
index ec1235f4bf38..514432af122f 100644
--- a/browser/base/content/newtab/grid.js
+++ b/browser/base/content/newtab/grid.js
@@ -162,7 +162,9 @@ let gGrid = {
'' +
'';
+ ' class="newtab-control newtab-control-block"/>' +
+ '';
this._siteFragment = document.createDocumentFragment();
this._siteFragment.appendChild(site);
diff --git a/browser/base/content/newtab/newTab.css b/browser/base/content/newtab/newTab.css
index fe13d67fb107..e30483c8f2e3 100644
--- a/browser/base/content/newtab/newTab.css
+++ b/browser/base/content/newtab/newTab.css
@@ -129,16 +129,16 @@ input[type=button] {
.newtab-thumbnail[dragged],
.newtab-link:-moz-focusring > .newtab-thumbnail,
-.newtab-site:hover > .newtab-link > .newtab-thumbnail {
+.newtab-cell:not([ignorehover]) > .newtab-site:hover > .newtab-link > .newtab-thumbnail {
opacity: 1;
}
/* TITLES */
.newtab-title {
- bottom: -20px;
+ bottom: -21px;
position: absolute;
left: 0;
- line-height: 20px;
+ line-height: 21px;
right: 0;
text-align: start;
white-space: nowrap;
@@ -155,7 +155,7 @@ input[type=button] {
}
.newtab-control:-moz-focusring,
-.newtab-site:hover > .newtab-control {
+.newtab-cell:not([ignorehover]) > .newtab-site:hover > .newtab-control {
opacity: 1;
}
@@ -169,16 +169,31 @@ input[type=button] {
}
}
+.newtab-control-sponsored:-moz-locale-dir(rtl),
.newtab-control-pin:-moz-locale-dir(ltr),
.newtab-control-block:-moz-locale-dir(rtl) {
left: 4px;
}
+.newtab-control-sponsored:-moz-locale-dir(ltr),
.newtab-control-block:-moz-locale-dir(ltr),
.newtab-control-pin:-moz-locale-dir(rtl) {
right: 4px;
}
+.newtab-control.newtab-control-sponsored {
+ bottom: -20px;
+ height: 14px;
+ -moz-margin-end: -5px;
+ opacity: 1;
+ top: auto;
+ width: 14px;
+}
+
+.newtab-site:not([type=sponsored]) .newtab-control-sponsored {
+ display: none;
+}
+
/* DRAG & DROP */
/*
diff --git a/browser/base/content/newtab/newTab.js b/browser/base/content/newtab/newTab.js
index 2ca2ff433c01..af2f6a52ffb4 100644
--- a/browser/base/content/newtab/newTab.js
+++ b/browser/base/content/newtab/newTab.js
@@ -11,6 +11,7 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
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/DirectoryLinksProvider.jsm");
Cu.import("resource://gre/modules/NewTabUtils.jsm");
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
diff --git a/browser/base/content/newtab/page.js b/browser/base/content/newtab/page.js
index 112f7b8e9ea4..41326ceb52ee 100644
--- a/browser/base/content/newtab/page.js
+++ b/browser/base/content/newtab/page.js
@@ -36,7 +36,11 @@ let gPage = {
* thumbnail service.
*/
get allowBackgroundCaptures() {
- return document.documentElement.getAttribute("allow-background-captures") ==
+ // The preloader is bypassed altogether for private browsing windows, and
+ // therefore allow-background-captures will not be set. In that case, the
+ // page is not preloaded and so it's visible, so allow background captures.
+ return inPrivateBrowsingMode() ||
+ document.documentElement.getAttribute("allow-background-captures") ==
"true";
},
@@ -65,10 +69,13 @@ let gPage = {
/**
* Updates the whole page and the grid when the storage has changed.
+ * @param aOnlyIfHidden If true, the page is updated only if it's hidden in
+ * the preloader.
*/
- update: function Page_update() {
+ update: function Page_update(aOnlyIfHidden=false) {
+ let skipUpdate = aOnlyIfHidden && this.allowBackgroundCaptures;
// The grid might not be ready yet as we initialize it asynchronously.
- if (gGrid.ready) {
+ if (gGrid.ready && !skipUpdate) {
gGrid.refresh();
}
},
@@ -87,11 +94,29 @@ let gPage = {
if (this.allowBackgroundCaptures) {
Services.telemetry.getHistogramById("NEWTAB_PAGE_SHOWN").add(true);
+ // Initialize type counting with the types we want to count
+ let directoryCount = {};
+ for (let type of DirectoryLinksProvider.linkTypes) {
+ directoryCount[type] = 0;
+ }
+
for (let site of gGrid.sites) {
if (site) {
site.captureIfMissing();
+ let {type} = site.link;
+ if (type in directoryCount) {
+ directoryCount[type]++;
+ }
}
}
+
+ // Record how many directory sites were shown, but place counts over the
+ // default 9 in the same bucket
+ for (let [type, count] of Iterator(directoryCount)) {
+ let shownId = "NEWTAB_PAGE_DIRECTORY_" + type.toUpperCase() + "_SHOWN";
+ let shownCount = Math.min(10, count);
+ Services.telemetry.getHistogramById(shownId).add(shownCount);
+ }
}
});
this._mutationObserver.observe(document.documentElement, {
diff --git a/browser/base/content/newtab/sites.js b/browser/base/content/newtab/sites.js
index e91706fd0e8c..1a8f7ff4d8ee 100644
--- a/browser/base/content/newtab/sites.js
+++ b/browser/base/content/newtab/sites.js
@@ -128,6 +128,7 @@ Site.prototype = {
link.setAttribute("title", tooltip);
link.setAttribute("href", url);
this._querySelector(".newtab-title").textContent = title;
+ this.node.setAttribute("type", this.link.type);
if (this.isPinned())
this._updateAttributes(true);
@@ -143,17 +144,19 @@ Site.prototype = {
* existing thumbnail and the page allows background captures.
*/
captureIfMissing: function Site_captureIfMissing() {
- if (gPage.allowBackgroundCaptures)
+ if (gPage.allowBackgroundCaptures && !this.link.imageURISpec) {
BackgroundPageThumbs.captureIfMissing(this.url);
+ }
},
/**
* Refreshes the thumbnail for the site.
*/
refreshThumbnail: function Site_refreshThumbnail() {
- let thumbnailURL = PageThumbs.getThumbnailURL(this.url);
let thumbnail = this._querySelector(".newtab-thumbnail");
- thumbnail.style.backgroundImage = "url(" + thumbnailURL + ")";
+ thumbnail.style.backgroundColor = this.link.bgColor;
+ let uri = this.link.imageURISpec || PageThumbs.getThumbnailURL(this.url);
+ thumbnail.style.backgroundImage = "url(" + uri + ")";
},
/**
@@ -165,6 +168,15 @@ Site.prototype = {
this._node.addEventListener("dragend", this, false);
this._node.addEventListener("mouseover", this, false);
this._node.addEventListener("click", this, false);
+
+ // Specially treat the sponsored icon to prevent regular hover effects
+ let sponsored = this._querySelector(".newtab-control-sponsored");
+ sponsored.addEventListener("mouseover", () => {
+ this.cell.node.setAttribute("ignorehover", "true");
+ });
+ sponsored.addEventListener("mouseout", () => {
+ this.cell.node.removeAttribute("ignorehover");
+ });
},
/**
@@ -189,6 +201,13 @@ Site.prototype = {
}
Services.telemetry.getHistogramById("NEWTAB_PAGE_SITE_CLICKED")
.add(aIndex);
+
+ // Specially count clicks on directory tiles
+ let typeIndex = DirectoryLinksProvider.linkTypes.indexOf(this.link.type);
+ if (typeIndex != -1) {
+ Services.telemetry.getHistogramById("NEWTAB_PAGE_DIRECTORY_TYPE_CLICKED")
+ .add(typeIndex);
+ }
},
/**
diff --git a/browser/base/content/test/general/browser_getshortcutoruri.js b/browser/base/content/test/general/browser_getshortcutoruri.js
index bfc4b73595d4..dd47b84d4a14 100644
--- a/browser/base/content/test/general/browser_getshortcutoruri.js
+++ b/browser/base/content/test/general/browser_getshortcutoruri.js
@@ -103,7 +103,8 @@ function test() {
let query = data.keyword;
if (data.searchWord)
query += " " + data.searchWord;
- let returnedData = yield getShortcutOrURIAndPostData(query);
+ let returnedData = yield new Promise(
+ resolve => getShortcutOrURIAndPostData(query, resolve));
// null result.url means we should expect the same query we sent in
let expected = result.url || query;
is(returnedData.url, expected, "got correct URL for " + data.keyword);
diff --git a/browser/base/content/test/general/browser_tabopen_reflows.js b/browser/base/content/test/general/browser_tabopen_reflows.js
index 5597db65c0cc..aeaf168019ed 100644
--- a/browser/base/content/test/general/browser_tabopen_reflows.js
+++ b/browser/base/content/test/general/browser_tabopen_reflows.js
@@ -56,6 +56,7 @@ const EXPECTED_REFLOWS = [
];
const PREF_PRELOAD = "browser.newtab.preload";
+const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directorySource";
/*
* This test ensures that there are no unexpected
@@ -65,7 +66,11 @@ function test() {
waitForExplicitFinish();
Services.prefs.setBoolPref(PREF_PRELOAD, false);
- registerCleanupFunction(() => Services.prefs.clearUserPref(PREF_PRELOAD));
+ Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, "data:application/json,{}");
+ registerCleanupFunction(() => {
+ Services.prefs.clearUserPref(PREF_PRELOAD);
+ Services.prefs.clearUserPref(PREF_NEWTAB_DIRECTORYSOURCE);
+ });
// Add a reflow observer and open a new tab.
docShell.addWeakReflowObserver(observer);
diff --git a/browser/base/content/test/newtab/browser.ini b/browser/base/content/test/newtab/browser.ini
index c7b1c7f9449d..01ec5ab570da 100644
--- a/browser/base/content/test/newtab/browser.ini
+++ b/browser/base/content/test/newtab/browser.ini
@@ -24,3 +24,4 @@ skip-if = os == "mac" # Intermittent failures, bug 898317
[browser_newtab_tabsync.js]
[browser_newtab_undo.js]
[browser_newtab_unpin.js]
+[browser_newtab_update.js]
diff --git a/browser/base/content/test/newtab/browser_newtab_update.js b/browser/base/content/test/newtab/browser_newtab_update.js
new file mode 100644
index 000000000000..8c63f61c4b47
--- /dev/null
+++ b/browser/base/content/test/newtab/browser_newtab_update.js
@@ -0,0 +1,53 @@
+/* Any copyright is dedicated to the Public Domain.
+ http://creativecommons.org/publicdomain/zero/1.0/ */
+
+/**
+ * Checks that newtab is updated as its links change.
+ */
+
+function runTests() {
+ if (NewTabUtils.allPages.updateScheduledForHiddenPages) {
+ // Wait for dynamic updates triggered by the previous test to finish.
+ yield whenPagesUpdated(null, true);
+ }
+
+ // First, start with an empty page. setLinks will trigger a hidden page
+ // update because it calls clearHistory. We need to wait for that update to
+ // happen so that the next time we wait for a page update below, we catch the
+ // right update and not the one triggered by setLinks.
+ //
+ // Why this weird way of yielding? First, these two functions don't return
+ // promises, they call TestRunner.next when done. Second, the point at which
+ // setLinks is done is independent of when the page update will happen, so
+ // calling whenPagesUpdated cannot wait until that time.
+ setLinks([]);
+ whenPagesUpdated(null, true);
+ yield null;
+ yield null;
+
+ // Strategy: Add some visits, open a new page, check the grid, repeat.
+ fillHistory([link(1)]);
+ yield whenPagesUpdated(null, true);
+ yield addNewTabPageTab();
+ checkGrid("1,,,,,,,,");
+
+ fillHistory([link(2)]);
+ yield whenPagesUpdated(null, true);
+ yield addNewTabPageTab();
+ checkGrid("2,1,,,,,,,");
+
+ fillHistory([link(1)]);
+ yield whenPagesUpdated(null, true);
+ yield addNewTabPageTab();
+ checkGrid("1,2,,,,,,,");
+
+ // Wait for fillHistory to add all links before waiting for an update
+ yield fillHistory([link(2), link(3), link(4)], TestRunner.next);
+ yield whenPagesUpdated(null, true);
+ yield addNewTabPageTab();
+ checkGrid("2,1,3,4,,,,,");
+}
+
+function link(id) {
+ return { url: "http://example.com/#" + id, title: "site#" + id };
+}
diff --git a/browser/base/content/test/newtab/head.js b/browser/base/content/test/newtab/head.js
index 6df4bff9c620..8f823cf9738d 100644
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -2,8 +2,11 @@
http://creativecommons.org/publicdomain/zero/1.0/ */
const PREF_NEWTAB_ENABLED = "browser.newtabpage.enabled";
+const PREF_NEWTAB_DIRECTORYSOURCE = "browser.newtabpage.directorySource";
Services.prefs.setBoolPref(PREF_NEWTAB_ENABLED, true);
+// start with no directory links by default
+Services.prefs.setCharPref(PREF_NEWTAB_DIRECTORYSOURCE, "data:application/json,{}");
let tmp = {};
Cu.import("resource://gre/modules/Promise.jsm", tmp);
@@ -26,6 +29,7 @@ registerCleanupFunction(function () {
gWindow.gBrowser.removeTab(gWindow.gBrowser.tabs[1]);
Services.prefs.clearUserPref(PREF_NEWTAB_ENABLED);
+ Services.prefs.clearUserPref(PREF_NEWTAB_DIRECTORYSOURCE);
});
/**
@@ -159,20 +163,34 @@ function clearHistory(aCallback) {
function fillHistory(aLinks, aCallback) {
let numLinks = aLinks.length;
+ if (!numLinks) {
+ if (aCallback)
+ executeSoon(aCallback);
+ return;
+ }
+
let transitionLink = Ci.nsINavHistoryService.TRANSITION_LINK;
- for (let link of aLinks.reverse()) {
+ // Important: To avoid test failures due to clock jitter on Windows XP, call
+ // Date.now() once here, not each time through the loop.
+ let now = Date.now() * 1000;
+
+ for (let i = 0; i < aLinks.length; i++) {
+ let link = aLinks[i];
let place = {
uri: makeURI(link.url),
title: link.title,
- visits: [{visitDate: Date.now() * 1000, transitionType: transitionLink}]
+ // Links are secondarily sorted by visit date descending, so decrease the
+ // visit date as we progress through the array so that links appear in the
+ // grid in the order they're present in the array.
+ visits: [{visitDate: now - i, transitionType: transitionLink}]
};
PlacesUtils.asyncHistory.updatePlaces(place, {
handleError: function () ok(false, "couldn't add visit to history"),
handleResult: function () {},
handleCompletion: function () {
- if (--numLinks == 0)
+ if (--numLinks == 0 && aCallback)
aCallback();
}
});
@@ -503,12 +521,18 @@ function createDragEvent(aEventType, aData) {
/**
* Resumes testing when all pages have been updated.
+ * @param aCallback Called when done. If not specified, TestRunner.next is used.
+ * @param aOnlyIfHidden If true, this resumes testing only when an update that
+ * applies to pre-loaded, hidden pages is observed. If
+ * false, this resumes testing when any update is observed.
*/
-function whenPagesUpdated(aCallback) {
+function whenPagesUpdated(aCallback, aOnlyIfHidden=false) {
let page = {
- update: function () {
- NewTabUtils.allPages.unregister(this);
- executeSoon(aCallback || TestRunner.next);
+ update: function (onlyIfHidden=false) {
+ if (onlyIfHidden == aOnlyIfHidden) {
+ NewTabUtils.allPages.unregister(this);
+ executeSoon(aCallback || TestRunner.next);
+ }
}
};
diff --git a/browser/base/content/urlbarBindings.xml b/browser/base/content/urlbarBindings.xml
index f218cbe9edef..1468efb592cd 100644
--- a/browser/base/content/urlbarBindings.xml
+++ b/browser/base/content/urlbarBindings.xml
@@ -260,29 +260,35 @@
var action = this._parseActionUrl(url);
let lastLocationChange = gBrowser.selectedBrowser.lastLocationChange;
- Task.spawn(function() {
- let matchLastLocationChange = true;
- if (action) {
- url = action.param;
- if (this.hasAttribute("actiontype")) {
- if (action.type == "switchtab") {
- this.handleRevert();
- let prevTab = gBrowser.selectedTab;
- if (switchToTabHavingURI(url) &&
- isTabEmpty(prevTab))
- gBrowser.removeTab(prevTab);
- }
- return;
- }
- }
- else {
- [url, postData, mayInheritPrincipal] = yield this._canonizeURL(aTriggeringEvent);
- matchLastLocationChange = (lastLocationChange ==
- gBrowser.selectedBrowser.lastLocationChange);
- if (!url)
- return;
- }
+ let matchLastLocationChange = true;
+ if (action) {
+ url = action.param;
+ if (this.hasAttribute("actiontype")) {
+ if (action.type == "switchtab") {
+ this.handleRevert();
+ let prevTab = gBrowser.selectedTab;
+ if (switchToTabHavingURI(url) &&
+ isTabEmpty(prevTab))
+ gBrowser.removeTab(prevTab);
+ }
+ return;
+ }
+ continueOperation.call(this);
+ }
+ else {
+ this._canonizeURL(aTriggeringEvent, response => {
+ [url, postData, mayInheritPrincipal] = response;
+ if (url) {
+ matchLastLocationChange = (lastLocationChange ==
+ gBrowser.selectedBrowser.lastLocationChange);
+ continueOperation.call(this);
+ }
+ });
+ }
+
+ function continueOperation()
+ {
this.value = url;
gBrowser.userTypedValue = url;
try {
@@ -347,73 +353,74 @@
loadCurrent();
}
}
- }.bind(this));
+ }
]]>