summaryrefslogtreecommitdiffstats
path: root/browser/base/content/test/favicons/browser_favicon_change_not_in_document.js
blob: b8215dcc3e4a5b4e597476ffd8e801ecda82ce15 (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
"use strict";

const TEST_ROOT =
  "http://mochi.test:8888/browser/browser/base/content/test/favicons/";
const TEST_URL = TEST_ROOT + "file_favicon_change_not_in_document.html";

// Runs the given task in the document of the browser.
function runInDoc(browser, task) {
  return ContentTask.spawn(browser, `(${task.toString()})();`, scriptStr => {
    let script = content.document.createElement("script");
    script.textContent = scriptStr;
    content.document.body.appendChild(script);

    // Link events are dispatched asynchronously so allow the event loop to run
    // to ensure that any events are actually dispatched before returning.
    return new Promise(resolve => content.setTimeout(resolve, 0));
  });
}

/*
 * This test tests a link element won't fire DOMLinkChanged/DOMLinkAdded unless
 * it is added to the DOM. See more details in bug 1083895.
 *
 * Note that there is debounce logic in FaviconLoader.sys.mjs, adding a new
 * icon link after the icon parsing timeout will trigger a new icon extraction
 * cycle. Hence, there should be two favicons loads in this test as it appends
 * a new link to the DOM in the timeout callback defined in the test HTML page.
 * However, the not-yet-added link element with href as "http://example.org/other-icon"
 * should not fire the DOMLinkAdded event, nor should it fire the DOMLinkChanged
 * event after its href gets updated later.
 */
add_task(async function () {
  let extraTab = (gBrowser.selectedTab = BrowserTestUtils.addTab(
    gBrowser,
    TEST_ROOT
  ));
  let domLinkAddedFired = 0;
  let domLinkChangedFired = 0;
  const linkAddedHandler = event => domLinkAddedFired++;
  const linkChangedhandler = event => domLinkChangedFired++;
  BrowserTestUtils.addContentEventListener(
    gBrowser.selectedBrowser,
    "DOMLinkAdded",
    linkAddedHandler
  );
  BrowserTestUtils.addContentEventListener(
    gBrowser.selectedBrowser,
    "DOMLinkChanged",
    linkChangedhandler
  );

  let expectedFavicon = TEST_ROOT + "file_generic_favicon.ico";
  let faviconPromise = waitForFavicon(extraTab.linkedBrowser, expectedFavicon);

  BrowserTestUtils.startLoadingURIString(extraTab.linkedBrowser, TEST_URL);
  await BrowserTestUtils.browserLoaded(extraTab.linkedBrowser);

  await faviconPromise;

  is(
    domLinkAddedFired,
    2,
    "Should fire the correct number of DOMLinkAdded event."
  );
  is(domLinkChangedFired, 0, "Should not fire any DOMLinkChanged event.");

  gBrowser.removeTab(extraTab);
});

/*
 * This verifies that creating and manipulating link elements inside document
 * fragments doesn't trigger the link events.
 */
add_task(async function () {
  let extraTab = (gBrowser.selectedTab = BrowserTestUtils.addTab(
    gBrowser,
    TEST_ROOT
  ));
  let browser = extraTab.linkedBrowser;

  let domLinkAddedFired = 0;
  let domLinkChangedFired = 0;
  const linkAddedHandler = event => domLinkAddedFired++;
  const linkChangedhandler = event => domLinkChangedFired++;
  BrowserTestUtils.addContentEventListener(
    browser,
    "DOMLinkAdded",
    linkAddedHandler
  );
  BrowserTestUtils.addContentEventListener(
    browser,
    "DOMLinkChanged",
    linkChangedhandler
  );

  BrowserTestUtils.startLoadingURIString(browser, TEST_ROOT + "blank.html");
  await BrowserTestUtils.browserLoaded(browser);

  is(domLinkAddedFired, 0, "Should have been no link add events");
  is(domLinkChangedFired, 0, "Should have been no link change events");

  await runInDoc(browser, () => {
    let fragment = document
      .createRange()
      .createContextualFragment(
        '<link type="image/ico" href="file_generic_favicon.ico" rel="icon">'
      );
    fragment.firstElementChild.setAttribute("type", "image/png");
  });

  is(domLinkAddedFired, 0, "Should have been no link add events");
  is(domLinkChangedFired, 0, "Should have been no link change events");

  await runInDoc(browser, () => {
    let fragment = document.createDocumentFragment();
    let link = document.createElement("link");
    link.setAttribute("href", "file_generic_favicon.ico");
    link.setAttribute("rel", "icon");
    link.setAttribute("type", "image/ico");

    fragment.appendChild(link);
    link.setAttribute("type", "image/png");
  });

  is(domLinkAddedFired, 0, "Should have been no link add events");
  is(domLinkChangedFired, 0, "Should have been no link change events");

  let expectedFavicon = TEST_ROOT + "file_generic_favicon.ico";
  let faviconPromise = waitForFavicon(browser, expectedFavicon);

  // Moving an element from the fragment into the DOM should trigger the add
  // events and start favicon loading.
  await runInDoc(browser, () => {
    let fragment = document
      .createRange()
      .createContextualFragment(
        '<link type="image/ico" href="file_generic_favicon.ico" rel="icon">'
      );
    document.head.appendChild(fragment);
  });

  is(domLinkAddedFired, 1, "Should have been one link add events");
  is(domLinkChangedFired, 0, "Should have been no link change events");

  await faviconPromise;

  gBrowser.removeTab(extraTab);
});