summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/urlbar/browser_urlbarSearchSuggestionsNotification.js
blob: 94ae8a3ffea38f42af9d64d3cbb8e68c5f046519 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
const SUGGEST_ALL_PREF = "browser.search.suggest.enabled";
const SUGGEST_URLBAR_PREF = "browser.urlbar.suggest.searches";
const CHOICE_PREF = "browser.urlbar.userMadeSearchSuggestionsChoice";
const TEST_ENGINE_BASENAME = "searchSuggestionEngine.xml";

// Must run first.
add_task(function* prepare() {
  let engine = yield promiseNewSearchEngine(TEST_ENGINE_BASENAME);
  let oldCurrentEngine = Services.search.currentEngine;
  Services.search.currentEngine = engine;
  registerCleanupFunction(function* () {
    Services.search.currentEngine = oldCurrentEngine;
    Services.prefs.clearUserPref(SUGGEST_ALL_PREF);
    Services.prefs.clearUserPref(SUGGEST_URLBAR_PREF);

    // Disable the notification for future tests so it doesn't interfere with
    // them.  clearUserPref() won't work because by default the pref is false.
    yield setUserMadeChoicePref(true);

    // Make sure the popup is closed for the next test.
    gURLBar.blur();
    Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
  });
});

add_task(function* focus() {
  // Focusing the urlbar used to open the popup in order to show the
  // notification, but it doesn't anymore.  Make sure it does not.
  Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true);
  yield setUserMadeChoicePref(false);
  gURLBar.blur();
  gURLBar.focus();
  Assert.ok(!gURLBar.popup.popupOpen, "popup should remain closed");
});

add_task(function* dismissWithoutResults() {
  Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true);
  yield setUserMadeChoicePref(false);
  gURLBar.blur();
  gURLBar.focus();
  let popupPromise = promisePopupShown(gURLBar.popup);
  gURLBar.openPopup();
  yield popupPromise;
  Assert.ok(gURLBar.popup.popupOpen, "popup should be open");
  assertVisible(true);
  Assert.equal(gURLBar.popup._matchCount, 0, "popup should have no results");
  let disableButton = document.getAnonymousElementByAttribute(
    gURLBar.popup, "anonid", "search-suggestions-notification-disable"
  );
  let transitionPromise = promiseTransition();
  disableButton.click();
  yield transitionPromise;
  Assert.ok(!gURLBar.popup.popupOpen, "popup should be closed");
  gURLBar.blur();
  gURLBar.focus();
  Assert.ok(!gURLBar.popup.popupOpen, "popup should remain closed");
  yield promiseAutocompleteResultPopup("foo");
  Assert.ok(gURLBar.popup.popupOpen, "popup should be open");
  assertVisible(false);
});

add_task(function* dismissWithResults() {
  Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true);
  yield setUserMadeChoicePref(false);
  gURLBar.blur();
  gURLBar.focus();
  yield promiseAutocompleteResultPopup("foo");
  Assert.ok(gURLBar.popup.popupOpen, "popup should be open");
  assertVisible(true);
  Assert.ok(gURLBar.popup._matchCount > 0, "popup should have results");
  let disableButton = document.getAnonymousElementByAttribute(
    gURLBar.popup, "anonid", "search-suggestions-notification-disable"
  );
  let transitionPromise = promiseTransition();
  disableButton.click();
  yield transitionPromise;
  Assert.ok(gURLBar.popup.popupOpen, "popup should remain open");
  gURLBar.blur();
  gURLBar.focus();
  Assert.ok(!gURLBar.popup.popupOpen, "popup should remain closed");
  yield promiseAutocompleteResultPopup("foo");
  Assert.ok(gURLBar.popup.popupOpen, "popup should be open");
  assertVisible(false);
});

add_task(function* disable() {
  Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true);
  yield setUserMadeChoicePref(false);
  gURLBar.blur();
  gURLBar.focus();
  yield promiseAutocompleteResultPopup("foo");
  Assert.ok(gURLBar.popup.popupOpen, "popup should be open");
  assertVisible(true);
  let disableButton = document.getAnonymousElementByAttribute(
    gURLBar.popup, "anonid", "search-suggestions-notification-disable"
  );
  let transitionPromise = promiseTransition();
  disableButton.click();
  yield transitionPromise;
  gURLBar.blur();
  yield promiseAutocompleteResultPopup("foo");
  Assert.ok(!suggestionsPresent());
});

add_task(function* enable() {
  Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true);
  Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, false);
  yield setUserMadeChoicePref(false);
  gURLBar.blur();
  gURLBar.focus();
  yield promiseAutocompleteResultPopup("foo");
  assertVisible(true);
  Assert.ok(!suggestionsPresent());
  let enableButton = document.getAnonymousElementByAttribute(
    gURLBar.popup, "anonid", "search-suggestions-notification-enable"
  );
  let searchPromise = BrowserTestUtils.waitForCondition(suggestionsPresent,
                                                        "waiting for suggestions");
  enableButton.click();
  yield searchPromise;
  // Clicking Yes should trigger a new search so that suggestions appear
  // immediately.
  Assert.ok(suggestionsPresent());
  gURLBar.blur();
  gURLBar.focus();
  // Suggestions should still be present in a new search of course.
  yield promiseAutocompleteResultPopup("bar");
  Assert.ok(suggestionsPresent());
});

add_task(function* privateWindow() {
  // Since suggestions are disabled in private windows, the notification should
  // not appear even when suggestions are otherwise enabled.
  let win = yield BrowserTestUtils.openNewBrowserWindow({ private: true });
  win.gURLBar.blur();
  win.gURLBar.focus();
  yield promiseAutocompleteResultPopup("foo", win);
  assertVisible(false, win);
  win.gURLBar.blur();
  yield BrowserTestUtils.closeWindow(win);
});

add_task(function* multipleWindows() {
  // Opening multiple windows, using their urlbars, and then dismissing the
  // notification in one should dismiss the notification in all.
  Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true);
  Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, false);
  yield setUserMadeChoicePref(false);

  gURLBar.focus();
  yield promiseAutocompleteResultPopup("win1");
  assertVisible(true);

  let win2 = yield BrowserTestUtils.openNewBrowserWindow();
  win2.gURLBar.focus();
  yield promiseAutocompleteResultPopup("win2", win2);
  assertVisible(true, win2);

  let win3 = yield BrowserTestUtils.openNewBrowserWindow();
  win3.gURLBar.focus();
  yield promiseAutocompleteResultPopup("win3", win3);
  assertVisible(true, win3);

  let enableButton = win3.document.getAnonymousElementByAttribute(
    win3.gURLBar.popup, "anonid", "search-suggestions-notification-enable"
  );
  let transitionPromise = promiseTransition(win3);
  enableButton.click();
  yield transitionPromise;
  assertVisible(false, win3);

  win2.gURLBar.focus();
  yield promiseAutocompleteResultPopup("win2done", win2);
  assertVisible(false, win2);

  gURLBar.focus();
  yield promiseAutocompleteResultPopup("win1done");
  assertVisible(false);

  yield BrowserTestUtils.closeWindow(win2);
  yield BrowserTestUtils.closeWindow(win3);
});

add_task(function* enableOutsideNotification() {
  // Setting the suggest.searches pref outside the notification (e.g., by
  // ticking the checkbox in the preferences window) should hide it.
  Services.prefs.setBoolPref(SUGGEST_ALL_PREF, true);
  Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, false);
  yield setUserMadeChoicePref(false);

  Services.prefs.setBoolPref(SUGGEST_URLBAR_PREF, true);
  gURLBar.focus();
  yield promiseAutocompleteResultPopup("foo");
  assertVisible(false);
});

/**
 * Setting the choice pref triggers a pref observer in the urlbar, which hides
 * the notification if it's present.  This function returns a promise that's
 * resolved once the observer fires.
 *
 * @param userMadeChoice  A boolean, the pref's new value.
 * @return A Promise that's resolved when the observer fires -- or, if the pref
 *         is currently the given value, that's resolved immediately.
 */
function setUserMadeChoicePref(userMadeChoice) {
  return new Promise(resolve => {
    let currentUserMadeChoice = Services.prefs.getBoolPref(CHOICE_PREF);
    if (currentUserMadeChoice != userMadeChoice) {
      Services.prefs.addObserver(CHOICE_PREF, function obs(subj, topic, data) {
        Services.prefs.removeObserver(CHOICE_PREF, obs);
        resolve();
      }, false);
    }
    Services.prefs.setBoolPref(CHOICE_PREF, userMadeChoice);
    if (currentUserMadeChoice == userMadeChoice) {
      resolve();
    }
  });
}

function suggestionsPresent() {
  let controller = gURLBar.popup.input.controller;
  let matchCount = controller.matchCount;
  for (let i = 0; i < matchCount; i++) {
    let url = controller.getValueAt(i);
    let mozActionMatch = url.match(/^moz-action:([^,]+),(.*)$/);
    if (mozActionMatch) {
      let [, type, paramStr] = mozActionMatch;
      let params = JSON.parse(paramStr);
      if (type == "searchengine" && "searchSuggestion" in params) {
        return true;
      }
    }
  }
  return false;
}

function assertVisible(visible, win=window) {
  let style =
    win.getComputedStyle(win.gURLBar.popup.searchSuggestionsNotification);
  Assert.equal(style.visibility, visible ? "visible" : "collapse");
}

function promiseTransition(win=window) {
  return new Promise(resolve => {
    win.gURLBar.popup.addEventListener("transitionend", function onEnd() {
      win.gURLBar.popup.removeEventListener("transitionend", onEnd, true);
      // The urlbar needs to handle the transitionend first, but that happens
      // naturally since promises are resolved at the end of the current tick.
      resolve();
    }, true);
  });
}