Merge f-t to m-c, a=merge
authorPhil Ringnalda <philringnalda@gmail.com>
Sun, 08 Feb 2015 17:12:05 -0800
changeset 228053 50ea2cac6d648cf852c322dd7d050467c7bf8dd5
parent 228048 34e78fc863a91bff4ed72bb1c8943418bd5f1f95 (current diff)
parent 228052 966cd36a361a38a51896dc47f223b366a4216308 (diff)
child 228058 3436787a82d0e1f7c4b43d8ec7b9b0be2f00bd04
push id28255
push userphilringnalda@gmail.com
push dateMon, 09 Feb 2015 01:20:57 +0000
treeherdermozilla-central@50ea2cac6d64 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmerge
milestone38.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Merge f-t to m-c, a=merge
browser/base/content/test/general/browser_customize_popupNotification.js
--- a/browser/base/content/test/general/browser.ini
+++ b/browser/base/content/test/general/browser.ini
@@ -291,17 +291,16 @@ skip-if = os == 'win' || e10s # Bug 1056
 [browser_bug1070778.js]
 [browser_canonizeURL.js]
 skip-if = e10s # Bug 1094510 - test hits the network in e10s mode only
 [browser_contentAreaClick.js]
 skip-if = e10s
 [browser_contextSearchTabPosition.js]
 skip-if = os == "mac" || e10s # bug 967013; e10s: bug 1094761 - test hits the network in e10s, causing next test to crash
 [browser_ctrlTab.js]
-[browser_customize_popupNotification.js]
 [browser_datareporting_notification.js]
 skip-if = !datareporting
 [browser_devedition.js]
 [browser_devices_get_user_media.js]
 skip-if = buildapp == 'mulet' || (os == "linux" && debug) || e10s # linux: bug 976544; e10s: bug 1071623
 [browser_devices_get_user_media_about_urls.js]
 skip-if = e10s # Bug 1071623
 [browser_devices_get_user_media_in_frame.js]
deleted file mode 100644
--- a/browser/base/content/test/general/browser_customize_popupNotification.js
+++ /dev/null
@@ -1,35 +0,0 @@
-/*
-Any copyright is dedicated to the Public Domain.
-http://creativecommons.org/publicdomain/zero/1.0/
-*/
-
-function test() {
-  waitForExplicitFinish();
-  let newWin = OpenBrowserWindow();
-  registerCleanupFunction(() => {
-    newWin.close()
-    newWin = null;
-  });
-  whenDelayedStartupFinished(newWin, function () {
-    // Remove the URL bar
-    newWin.gURLBar.parentNode.removeChild(newWin.gURLBar);
-
-    waitForFocus(function () {
-      let PN = newWin.PopupNotifications;
-      try {
-        let panelPromise = promisePopupShown(PN.panel);
-        let notification = PN.show(newWin.gBrowser.selectedBrowser, "some-notification", "Some message");
-        panelPromise.then(function() {
-          ok(notification, "showed the notification");
-          ok(PN.isPanelOpen, "panel is open");
-          is(PN.panel.anchorNode, newWin.gBrowser.selectedTab, "notification is correctly anchored to the tab");
-          PN.panel.hidePopup();
-          finish();
-        });
-      } catch (ex) {
-        ok(false, "threw exception: " + ex);
-        finish();
-      }
-    }, newWin);
-  });
-}
--- a/browser/components/loop/MozLoopService.jsm
+++ b/browser/components/loop/MozLoopService.jsm
@@ -1530,23 +1530,39 @@ this.MozLoopService = {
    *
    * @param {String} aSrc A string representing the entry point to begin the tour, optional.
    * @param {Object} aAdditionalParams An object with keys used as query parameter names
    */
   getTourURL: function(aSrc = null, aAdditionalParams = {}) {
     let urlStr = this.getLoopPref("gettingStarted.url");
     let url = new URL(Services.urlFormatter.formatURL(urlStr));
     for (let paramName in aAdditionalParams) {
-      url.searchParams.append(paramName, aAdditionalParams[paramName]);
+      url.searchParams.set(paramName, aAdditionalParams[paramName]);
     }
     if (aSrc) {
       url.searchParams.set("utm_source", "firefox-browser");
       url.searchParams.set("utm_medium", "firefox-browser");
       url.searchParams.set("utm_campaign", aSrc);
     }
+
+    // Find the most recent pageID that has the Loop prefix.
+    let mostRecentLoopPageID = {id: null, lastSeen: null};
+    for (let pageID of UITour.pageIDsForSession) {
+      if (pageID[0] && pageID[0].startsWith("hello-tour_OpenPanel_") &&
+          pageID[1] && pageID[1].lastSeen > mostRecentLoopPageID.lastSeen) {
+        mostRecentLoopPageID.id = pageID[0];
+        mostRecentLoopPageID.lastSeen = pageID[1].lastSeen;
+      }
+    }
+
+    const PAGE_ID_EXPIRATION_MS = 60 * 60 * 1000;
+    if (mostRecentLoopPageID.id &&
+        mostRecentLoopPageID.lastSeen > Date.now() - PAGE_ID_EXPIRATION_MS) {
+      url.searchParams.set("utm_content", mostRecentLoopPageID.id);
+    }
     return url;
   },
 
   resumeTour: function(aIncomingConversationState) {
     if (!this.getLoopPref("gettingStarted.resumeOnFirstJoin")) {
       return;
     }
 
--- a/browser/components/uitour/UITour.jsm
+++ b/browser/components/uitour/UITour.jsm
@@ -69,16 +69,19 @@ XPCOMUtils.defineLazyGetter(this, "log",
     prefix: "UITour",
   };
   return new ConsoleAPI(consoleOptions);
 });
 
 this.UITour = {
   url: null,
   seenPageIDs: null,
+  // This map is not persisted and is used for
+  // building the content source of a potential tour.
+  pageIDsForSession: new Map(),
   pageIDSourceBrowsers: new WeakMap(),
   /* Map from browser chrome windows to a Set of <browser>s in which a tour is open (both visible and hidden) */
   tourBrowsersByWindow: new WeakMap(),
   urlbarCapture: new WeakMap(),
   appMenuOpenForAnnotation: new Set(),
   availableTargetsCache: new WeakMap(),
 
   _annotationPanelMutationObservers: new WeakMap(),
@@ -370,26 +373,32 @@ this.UITour = {
       return false;
     }
 
     // Do this before bailing if there's no tab, so later we can pick up the pieces:
     window.gBrowser.tabContainer.addEventListener("TabSelect", this);
 
     switch (action) {
       case "registerPageID": {
-        // This is only relevant if Telemtry is enabled.
+        if (typeof data.pageID != "string") {
+          log.warn("registerPageID: pageID must be a string");
+          break;
+        }
+
+        this.pageIDsForSession.set(data.pageID, {lastSeen: Date.now()});
+
+        // The rest is only relevant if Telemetry is enabled.
         if (!UITelemetry.enabled) {
-          log.debug("registerPageID: Telemery disabled, not doing anything");
+          log.debug("registerPageID: Telemetry disabled, not doing anything");
           break;
         }
 
         // We don't want to allow BrowserUITelemetry.BUCKET_SEPARATOR in the
         // pageID, as it could make parsing the telemetry bucket name difficult.
-        if (typeof data.pageID != "string" ||
-            data.pageID.contains(BrowserUITelemetry.BUCKET_SEPARATOR)) {
+        if (data.pageID.contains(BrowserUITelemetry.BUCKET_SEPARATOR)) {
           log.warn("registerPageID: Invalid page ID specified");
           break;
         }
 
         this.addSeenPageID(data.pageID);
         this.pageIDSourceBrowsers.set(browser, data.pageID);
         this.setTelemetryBucket(data.pageID);
 
--- a/browser/components/uitour/test/browser.ini
+++ b/browser/components/uitour/test/browser.ini
@@ -15,17 +15,17 @@ skip-if = e10s # Bug 941428 - UITour.jsm
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
 [browser_UITour_detach_tab.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
 [browser_UITour_annotation_size_attributes.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
 [browser_UITour_heartbeat.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
 [browser_UITour_loop.js]
-skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
+skip-if = os == "linux" || e10s # Bug 941428 - UITour.jsm not e10s friendly.
 [browser_UITour_modalDialog.js]
 run-if = os == "mac" # modal dialog disabling only working on OS X
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly
 [browser_UITour_observe.js]
 skip-if = e10s # Bug 941428 - UITour.jsm not e10s friendly.
 [browser_UITour_panel_close_annotation.js]
 skip-if = true # Disabled due to frequent failures, bugs 1026310 and 1032137
 [browser_UITour_registerPageID.js]
--- a/browser/components/uitour/test/browser_UITour_heartbeat.js
+++ b/browser/components/uitour/test/browser_UITour_heartbeat.js
@@ -167,22 +167,22 @@ let tests = [
           // The UI was just shown. We can simulate a click on a rating element (i.e., "star").
           simulateVote(flowId, expectedScore);
           break;
         }
         case "Heartbeat:Voted": {
           info("'Heartbeat:Voted' notification received (timestamp " + aData.timestamp.toString() + ").");
           ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
           is(aData.score, expectedScore, "Should report a score of " + expectedScore);
-          done();
           break;
         }
         case "Heartbeat:NotificationClosed": {
           info("'Heartbeat:NotificationClosed' notification received (timestamp " + aData.timestamp.toString() + ").");
           ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          done();
           break;
         }
         default:
           // We are not expecting other states for this test.
           ok(false, "Unexpected notification received: " + aEventName);
       }
     });
 
@@ -192,32 +192,35 @@ let tests = [
   /**
    * Test that the engagement page is correctly opened when voting.
    */
   function test_heartbeat_engagement_tab(done) {
     let engagementURL = "http://example.com";
     let flowId = "ui-ratefirefox-" + Math.random();
     let originalTabCount = gBrowser.tabs.length;
     const expectedTabCount = originalTabCount + 1;
+    let heartbeatVoteSeen = false;
 
     gContentAPI.observe(function (aEventName, aData) {
       switch (aEventName) {
         case "Heartbeat:NotificationOffered": {
           info("'Heartbeat:Offered' notification received (timestamp " + aData.timestamp.toString() + ").");
           ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
           // The UI was just shown. We can simulate a click on a rating element (i.e., "star").
           simulateVote(flowId, 1);
           break;
         }
         case "Heartbeat:Voted": {
           info("'Heartbeat:Voted' notification received (timestamp " + aData.timestamp.toString() + ").");
           ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
+          heartbeatVoteSeen = true;
           break;
         }
         case "Heartbeat:NotificationClosed": {
+          ok(heartbeatVoteSeen, "Heartbeat vote should have been received");
           info("'Heartbeat:NotificationClosed' notification received (timestamp " + aData.timestamp.toString() + ").");
           ok(Number.isFinite(aData.timestamp), "Timestamp must be a number.");
           is(gBrowser.tabs.length, expectedTabCount, "Engagement URL should open in a new tab.");
           gBrowser.removeCurrentTab();
           done();
           break;
         }
         default:
--- a/browser/components/uitour/test/browser_UITour_loop.js
+++ b/browser/components/uitour/test/browser_UITour_loop.js
@@ -22,17 +22,94 @@ function runOffline(fun) {
     fun(function onComplete() {
       Services.io.offline = false;
       done();
     });
   }
 }
 
 let tests = [
+  taskify(function* test_gettingStartedClicked_linkOpenedWithExpectedParams() {
+    Services.prefs.setBoolPref("loop.gettingStarted.seen", false);
+    Services.prefs.setCharPref("loop.gettingStarted.url", "http://example.com");
+    ise(loopButton.open, false, "Menu should initially be closed");
+    loopButton.click();
+
+    yield waitForConditionPromise(() => {
+      return loopButton.open;
+    }, "Menu should be visible after showMenu()");
+
+    gContentAPI.registerPageID("hello-tour_OpenPanel_testPage");
+    yield new Promise(resolve => {
+      gContentAPI.ping(() => resolve());
+    });
+
+    let loopDoc = document.getElementById("loop-notification-panel").children[0].contentDocument;
+    let gettingStartedButton = loopDoc.getElementById("fte-button");
+    ok(gettingStartedButton, "Getting Started button should be found");
+
+    let newTabPromise = waitForConditionPromise(() => {
+      return gBrowser.currentURI.path.contains("utm_source=firefox-browser");
+    }, "New tab with utm_content=testPageNewID should have opened");
+
+    gettingStartedButton.click();
+    yield newTabPromise;
+    ok(gBrowser.currentURI.path.contains("utm_content=hello-tour_OpenPanel_testPage"),
+        "Expected URL opened (" + gBrowser.currentURI.path + ")");
+    yield gBrowser.removeCurrentTab();
+
+    checkLoopPanelIsHidden();
+  }),
+  taskify(function* test_gettingStartedClicked_linkOpenedWithExpectedParams2() {
+    Services.prefs.setBoolPref("loop.gettingStarted.seen", false);
+    // Force a refresh of the loop panel since going from seen -> unseen doesn't trigger
+    // automatic re-rendering.
+    let loopWin = document.getElementById("loop-notification-panel").children[0].contentWindow;
+    var event = new loopWin.CustomEvent("GettingStartedSeen");
+    loopWin.dispatchEvent(event);
+
+    UITour.pageIDsForSession.clear();
+    Services.prefs.setCharPref("loop.gettingStarted.url", "http://example.com");
+    ise(loopButton.open, false, "Menu should initially be closed");
+    loopButton.click();
+
+    yield waitForConditionPromise(() => {
+      return loopButton.open;
+    }, "Menu should be visible after showMenu()");
+
+
+    gContentAPI.registerPageID("hello-tour_OpenPanel_testPageOldId");
+    yield new Promise(resolve => {
+      gContentAPI.ping(() => resolve());
+    });
+    // Set the time of the page ID to 10 hours earlier, so that it is considered "expired".
+    UITour.pageIDsForSession.set("hello-tour_OpenPanel_testPageOldId",
+                                   {lastSeen: Date.now() - (10 * 60 * 60 * 1000)});
+
+    let loopDoc = loopWin.document;
+    let gettingStartedButton = loopDoc.getElementById("fte-button");
+    ok(gettingStartedButton, "Getting Started button should be found");
+
+    let newTabPromise = waitForConditionPromise(() => {
+      Services.console.logStringMessage(gBrowser.currentURI.path);
+      return gBrowser.currentURI.path.contains("utm_source=firefox-browser");
+    }, "New tab with utm_content=testPageNewID should have opened");
+
+    gettingStartedButton.click();
+    yield newTabPromise;
+    ok(!gBrowser.currentURI.path.contains("utm_content=hello-tour_OpenPanel_testPageOldId"),
+       "Expected URL opened without the utm_content parameter (" +
+        gBrowser.currentURI.path + ")");
+    yield gBrowser.removeCurrentTab();
+
+    checkLoopPanelIsHidden();
+  }),
   taskify(function* test_menu_show_hide() {
+    // The targets to highlight only appear after getting started is launched.
+    Services.prefs.setBoolPref("loop.gettingStarted.seen", true);
     ise(loopButton.open, false, "Menu should initially be closed");
     gContentAPI.showMenu("loop");
 
     yield waitForConditionPromise(() => {
       return loopButton.open;
     }, "Menu should be visible after showMenu()");
 
     ok(loopPanel.hasAttribute("noautohide"), "@noautohide should be on the loop panel");
@@ -272,18 +349,16 @@ function setupFakeRoom() {
     [room.roomToken, room]
   ]);
   LoopRooms.stubCache(roomsMap);
   return roomsMap;
 }
 
 if (Services.prefs.getBoolPref("loop.enabled")) {
   loopButton = window.LoopUI.toolbarButton.node;
-  // The targets to highlight only appear after getting started is launched.
-  Services.prefs.setBoolPref("loop.gettingStarted.seen", true);
 
   registerCleanupFunction(() => {
     Services.prefs.clearUserPref("loop.gettingStarted.resumeOnFirstJoin");
     Services.prefs.clearUserPref("loop.gettingStarted.seen");
     Services.prefs.clearUserPref("loop.gettingStarted.url");
     Services.io.offline = false;
 
     // Copied from browser/components/loop/test/mochitest/head.js
--- a/mobile/android/modules/AppConstants.jsm
+++ b/mobile/android/modules/AppConstants.jsm
@@ -1,9 +1,9 @@
-#filter substitution;
+#filter substitution
 /* 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";
 
 this.EXPORTED_SYMBOLS = ["AppConstants"];
 
@@ -58,23 +58,16 @@ let AppConstants = Object.freeze({
 
   MOZ_DEVICES:
 #ifdef MOZ_DEVICES
   true,
 #else
   false,
 #endif
 
-  MOZ_DEVICES:
-#ifdef MOZ_DEVICES
-  true,
-#else
-  false,
-#endif
-
   MOZ_SAFE_BROWSING:
 #ifdef MOZ_SAFE_BROWSING
   true,
 #else
   false,
 #endif
 
   MOZ_TELEMETRY_REPORTING: