summaryrefslogtreecommitdiffstats
path: root/devtools/shared/commands/resource/tests/browser_resources_stylesheets_navigation.js
blob: 1ee8913bda7f5b0ab70b74e86da6d2ce5768d652 (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
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

// Test the ResourceCommand API around STYLESHEET and navigation (reloading, creation of new browsing context, …)

const ORG_DOC_BUILDER = "https://example.org/document-builder.sjs";
const COM_DOC_BUILDER = "https://example.com/document-builder.sjs";

// Since the order of resources is not guaranteed, we put a number in the title attribute
// of the <style> elements so we can sort them in a way that makes it easier for us to assert.
let currentStyleTitle = 0;

const TEST_URI =
  `${ORG_DOC_BUILDER}?html=1<h1>top-level example.org</h1>` +
  `<style title="${currentStyleTitle++}">.top-level-org{}</style>` +
  `<iframe id="same-origin-1" src="${ORG_DOC_BUILDER}?html=<h2>example.org 1</h2><style title=${currentStyleTitle++}>.frame-org-1{}</style>"></iframe>` +
  `<iframe id="same-origin-2" src="${ORG_DOC_BUILDER}?html=<h2>example.org 2</h2><style title=${currentStyleTitle++}>.frame-org-2{}</style>"></iframe>` +
  `<iframe id="remote-origin-1" src="${COM_DOC_BUILDER}?html=<h2>example.com 1</h2><style title=${currentStyleTitle++}>.frame-com-1{}</style>"></iframe>` +
  `<iframe id="remote-origin-2" src="${COM_DOC_BUILDER}?html=<h2>example.com 2</h2><style title=${currentStyleTitle++}>.frame-com-2{}</style>"></iframe>`;

const COOP_HEADERS = "Cross-Origin-Opener-Policy:same-origin";
const TEST_URI_NEW_BROWSING_CONTEXT =
  `${ORG_DOC_BUILDER}?headers=${COOP_HEADERS}` +
  `&html=<h1>top-level example.org</div>` +
  `<style>.top-level-org-new-bc{}</style>`;

add_task(async function () {
  info(
    "Open a new tab and check that styleSheetChangeEventsEnabled is false by default"
  );
  const tab = await addTab(TEST_URI);

  is(
    await getDocumentStyleSheetChangeEventsEnabled(tab.linkedBrowser),
    false,
    `styleSheetChangeEventsEnabled is false at the beginning`
  );

  const { client, resourceCommand, targetCommand } = await initResourceCommand(
    tab
  );

  let availableResources = [];
  await resourceCommand.watchResources([resourceCommand.TYPES.STYLESHEET], {
    onAvailable: resources => {
      availableResources.push(...resources);
    },
  });

  info("Wait for all the stylesheets resources of main document and iframes");
  await waitFor(() => availableResources.length === 5);
  is(availableResources.length, 5, "Retrieved the expected stylesheets");

  // the order of the resources is not guaranteed.
  sortResourcesByExpectedOrder(availableResources);
  await assertResource(availableResources[0], {
    styleText: `.top-level-org{}`,
  });
  await assertResource(availableResources[1], {
    styleText: `.frame-org-1{}`,
  });
  await assertResource(availableResources[2], {
    styleText: `.frame-org-2{}`,
  });
  await assertResource(availableResources[3], {
    styleText: `.frame-com-1{}`,
  });
  await assertResource(availableResources[4], {
    styleText: `.frame-com-2{}`,
  });

  // clear availableResources so it's easier to test
  availableResources = [];

  is(
    await getDocumentStyleSheetChangeEventsEnabled(tab.linkedBrowser),
    true,
    `styleSheetChangeEventsEnabled is true after watching stylesheets`
  );

  info("Navigate a remote frame to a different page");
  const iframeNewUrl =
    `https://example.com/document-builder.sjs?` +
    `html=<h2>example.com new bc</h2><style title=6>.frame-com-new-bc{}</style>`;
  await SpecialPowers.spawn(tab.linkedBrowser, [iframeNewUrl], url => {
    const { browsingContext } =
      content.document.querySelector("#remote-origin-2");
    return SpecialPowers.spawn(browsingContext, [url], innerUrl => {
      content.document.location = innerUrl;
    });
  });
  await waitFor(() => availableResources.length == 1);
  ok(true, "We're notified about the iframe new document stylesheet");
  await assertResource(availableResources[0], {
    styleText: `.frame-com-new-bc{}`,
  });
  const iframeNewBrowsingContext = await SpecialPowers.spawn(
    tab.linkedBrowser,
    [],
    () => content.document.querySelector("#remote-origin-2").browsingContext
  );

  is(
    await getDocumentStyleSheetChangeEventsEnabled(iframeNewBrowsingContext),
    true,
    `styleSheetChangeEventsEnabled is still true after navigating the iframe`
  );

  // clear availableResources so it's easier to test
  availableResources = [];

  info("Check that styleSheetChangeEventsEnabled persist after reloading");
  await reloadBrowser();

  // ⚠️ When EFT is disabled, we're only getting the stylesheets for the top-level document
  // and the remote frames; the same-origin iframes stylesheets are missing.
  const expectedStylesheetResources = isEveryFrameTargetEnabled() ? 5 : 3;
  info(
    "Wait until we're notified about all the stylesheets (top-level document + iframe)"
  );
  await waitFor(
    () => availableResources.length === expectedStylesheetResources
  );
  is(
    availableResources.length,
    expectedStylesheetResources,
    "Retrieved the expected stylesheets after the page was reloaded"
  );

  // the order of the resources is not guaranteed.
  sortResourcesByExpectedOrder(availableResources);
  await assertResource(availableResources[0], {
    styleText: `.top-level-org{}`,
  });
  if (isEveryFrameTargetEnabled()) {
    await assertResource(availableResources[1], {
      styleText: `.frame-org-1{}`,
    });
    await assertResource(availableResources[2], {
      styleText: `.frame-org-2{}`,
    });
    await assertResource(availableResources[3], {
      styleText: `.frame-com-1{}`,
    });
    await assertResource(availableResources[4], {
      styleText: `.frame-com-new-bc{}`,
    });
  } else {
    await assertResource(availableResources[1], {
      styleText: `.frame-com-1{}`,
    });
    await assertResource(availableResources[2], {
      styleText: `.frame-com-new-bc{}`,
    });
  }

  is(
    await getDocumentStyleSheetChangeEventsEnabled(tab.linkedBrowser),
    true,
    `styleSheetChangeEventsEnabled is still true on the top level document after reloading`
  );

  if (isEveryFrameTargetEnabled()) {
    const bc = await SpecialPowers.spawn(
      tab.linkedBrowser,
      [],
      () => content.document.querySelector("#same-origin-1").browsingContext
    );
    is(
      await getDocumentStyleSheetChangeEventsEnabled(bc),
      true,
      `styleSheetChangeEventsEnabled is still true on the iframe after reloading`
    );
  }

  // clear availableResources so it's easier to test
  availableResources = [];

  info(
    "Check that styleSheetChangeEventsEnabled persist when navigating to a page that creates a new browsing context"
  );
  const previousBrowsingContextId = tab.linkedBrowser.browsingContext.id;
  const onLoaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
  BrowserTestUtils.startLoadingURIString(
    tab.linkedBrowser,
    TEST_URI_NEW_BROWSING_CONTEXT
  );
  await onLoaded;

  isnot(
    tab.linkedBrowser.browsingContext.id,
    previousBrowsingContextId,
    "A new browsing context was created"
  );

  info("Wait to get the stylesheet for the new document");
  await waitFor(() => availableResources.length === 1);
  ok(true, "We received the stylesheet for the new document");
  await assertResource(availableResources[0], {
    styleText: `.top-level-org-new-bc{}`,
  });
  is(
    await getDocumentStyleSheetChangeEventsEnabled(tab.linkedBrowser),
    true,
    `styleSheetChangeEventsEnabled is still true after navigating to a new browsing context`
  );

  targetCommand.destroy();
  await client.close();
});

/**
 * Returns the value of the browser/browsingContext document `styleSheetChangeEventsEnabled`
 * property.
 *
 * @param {Browser|BrowsingContext} browserOrBrowsingContext: The browser element or a
 *        browsing context.
 * @returns {Promise<Boolean>}
 */
function getDocumentStyleSheetChangeEventsEnabled(browserOrBrowsingContext) {
  return SpecialPowers.spawn(browserOrBrowsingContext, [], () => {
    return content.document.styleSheetChangeEventsEnabled;
  });
}

/**
 * Sort the passed array of stylesheet resources.
 *
 * Since the order of resources are not guaranteed, the <style> elements we use in this test
 * have a "title" attribute that represent their expected order so we can sort them in
 * a way that makes it easier for us to assert.
 *
 * @param {Array<Object>} resources: Array of stylesheet resources
 */
function sortResourcesByExpectedOrder(resources) {
  resources.sort((a, b) => {
    return Number(a.title) > Number(b.title);
  });
}

/**
 * Check that the resources have the expected text
 *
 * @param {Array<Object>} resources: Array of stylesheet resources
 * @param {Array<Object>} expected: Array of object of the following shape:
 * @param {Object} expected[]
 * @param {Object} expected[].styleText: Expected text content of the stylesheet
 */
async function assertResource(resource, expected) {
  const styleText = (await getStyleSheetResourceText(resource)).trim();
  is(styleText, expected.styleText, "Style text is correct");
}