summaryrefslogtreecommitdiffstats
path: root/devtools/shared/resources/tests/browser_resources_css_messages.js
blob: 0e82bacd575cc8a61c7ae778a9e82d2a0b94ce14 (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
/* Any copyright is dedicated to the Public Domain.
   http://creativecommons.org/publicdomain/zero/1.0/ */

"use strict";

// Test the ResourceWatcher API around CSS_MESSAGE
// Reproduces the CSS message assertions from devtools/shared/webconsole/test/chrome/test_page_errors.html

const {
  ResourceWatcher,
} = require("devtools/shared/resources/resource-watcher");
const { MESSAGE_CATEGORY } = require("devtools/shared/constants");

// Create a simple server so we have a nice sourceName in the resources packets.
const httpServer = createTestHTTPServer();
httpServer.registerPathHandler(`/test_css_messages.html`, (req, res) => {
  res.setStatusLine(req.httpVersion, 200, "OK");
  res.write(`<meta charset=utf8>
    <style>
      html {
        color: bloup;
      }
    </style>Test CSS Messages`);
});

const TEST_URI = `http://localhost:${httpServer.identity.primaryPort}/test_css_messages.html`;

add_task(async function() {
  await testWatchingCssMessages();
  await testWatchingCachedCssMessages();
});

async function testWatchingCssMessages() {
  // Disable the preloaded process as it creates processes intermittently
  // which forces the emission of RDP requests we aren't correctly waiting for.
  await pushPref("dom.ipc.processPrelaunch.enabled", false);

  // Open a test tab
  const tab = await addTab(TEST_URI);

  const { client, resourceWatcher, targetList } = await initResourceWatcher(
    tab
  );

  const receivedMessages = [];
  const { onAvailable, onAllMessagesReceived } = setupOnAvailableFunction(
    targetList,
    receivedMessages
  );
  await resourceWatcher.watchResources([ResourceWatcher.TYPES.CSS_MESSAGE], {
    onAvailable,
  });

  info(
    "Now log CSS warning *after* the call to ResourceWatcher.watchResources and after " +
      "having received the existing message"
  );
  // We need to wait for the first CSS Warning as it is not a cached message; when we
  // start watching, the `cssErrorReportingEnabled` is checked on the target docShell, and
  // if it is false, we re-parse the stylesheets to get the messages.
  await BrowserTestUtils.waitForCondition(() => receivedMessages.length === 1);

  info("Trigger a CSS Warning");
  triggerCSSWarning(tab);

  info("Waiting for all expected CSS messages to be received");
  await onAllMessagesReceived;
  ok(true, "All the expected CSS messages were received");

  Services.console.reset();
  targetList.destroy();
  await client.close();
}

async function testWatchingCachedCssMessages() {
  // Disable the preloaded process as it creates processes intermittently
  // which forces the emission of RDP requests we aren't correctly waiting for.
  await pushPref("dom.ipc.processPrelaunch.enabled", false);

  // Open a test tab
  const tab = await addTab(TEST_URI);

  // By default, the CSS Parser does not emit warnings at all, for performance matter.
  // Since we actually want the Parser to emit those messages _before_ we start listening
  // for CSS messages, we need to set the cssErrorReportingEnabled flag on the docShell.
  await SpecialPowers.spawn(gBrowser.selectedBrowser, [], function() {
    content.docShell.cssErrorReportingEnabled = true;
  });

  // Setting the docShell flag only indicates to the Parser that from now on, it should
  // emit warnings. But it does not automatically emit warnings for the existing CSS
  // errors in the stylesheets. So here we reload the tab, which will make the Parser
  // parse the stylesheets again, this time emitting warnings.
  const loaded = BrowserTestUtils.browserLoaded(tab.linkedBrowser);
  tab.linkedBrowser.reload();
  // wait for the tab to be fully loaded
  await loaded;
  // and trigger more CSS warnings
  await triggerCSSWarning(tab);

  // At this point, all messages should be in the ConsoleService cache, and we can begin
  // to watch and check that we do retrieve those messages.
  const { client, resourceWatcher, targetList } = await initResourceWatcher(
    tab
  );

  const receivedMessages = [];
  const { onAvailable } = setupOnAvailableFunction(
    targetList,
    receivedMessages
  );
  await resourceWatcher.watchResources([ResourceWatcher.TYPES.CSS_MESSAGE], {
    onAvailable,
  });
  is(receivedMessages.length, 3, "Cached messages were retrieved as expected");

  Services.console.reset();
  targetList.destroy();
  await client.close();
}

function setupOnAvailableFunction(targetList, receivedMessages) {
  // The expected messages are the CSS warnings:
  // - one for the rule in the style element
  // - two for the JS modified style we're doing in the test.
  const expectedMessages = [
    {
      pageError: {
        errorMessage: /Expected color but found ‘bloup’/,
        sourceName: /test_css_messages/,
        category: MESSAGE_CATEGORY.CSS_PARSER,
        timeStamp: /^\d+$/,
        error: false,
        warning: true,
      },
      cssSelectors: "html",
    },
    {
      pageError: {
        errorMessage: /Error in parsing value for ‘width’/,
        sourceName: /test_css_messages/,
        category: MESSAGE_CATEGORY.CSS_PARSER,
        timeStamp: /^\d+$/,
        error: false,
        warning: true,
      },
    },
    {
      pageError: {
        errorMessage: /Error in parsing value for ‘height’/,
        sourceName: /test_css_messages/,
        category: MESSAGE_CATEGORY.CSS_PARSER,
        timeStamp: /^\d+$/,
        error: false,
        warning: true,
      },
    },
  ];

  let done;
  const onAllMessagesReceived = new Promise(resolve => (done = resolve));
  const onAvailable = resources => {
    for (const resource of resources) {
      const { pageError } = resource;

      is(
        resource.targetFront,
        targetList.targetFront,
        "The targetFront property is the expected one"
      );

      if (!pageError.sourceName.includes("test_css_messages")) {
        info(`Ignore error from unknown source: "${pageError.sourceName}"`);
        continue;
      }

      const index = receivedMessages.length;
      receivedMessages.push(pageError);

      info(
        `checking received css message #${index}: ${pageError.errorMessage}`
      );
      ok(pageError, "The resource has a pageError attribute");
      checkObject(resource, expectedMessages[index]);

      if (receivedMessages.length == expectedMessages.length) {
        done();
      }
    }
  };
  return { onAvailable, onAllMessagesReceived };
}

/**
 * Sets invalid values for width and height on the document's body style attribute.
 */
function triggerCSSWarning(tab) {
  return ContentTask.spawn(tab.linkedBrowser, null, function frameScript() {
    content.document.body.style.width = "red";
    content.document.body.style.height = "blue";
  });
}