Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / chrome / test / data / extensions / api_test / webrequest / framework.js
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 var getURL = chrome.extension.getURL;
6 var deepEq = chrome.test.checkDeepEq;
7 var expectedEventData;
8 var capturedEventData;
9 var capturedUnexpectedData;
10 var expectedEventOrder;
11 var tabId;
12 var tabIdMap;
13 var frameIdMap;
14 var testServerPort;
15 var testServer = "www.a.com";
16 var defaultScheme = "http";
17 var eventsCaptured;
18 var listeners = {
19   'onBeforeRequest': [],
20   'onBeforeSendHeaders': [],
21   'onAuthRequired': [],
22   'onSendHeaders': [],
23   'onHeadersReceived': [],
24   'onResponseStarted': [],
25   'onBeforeRedirect': [],
26   'onCompleted': [],
27   'onErrorOccurred': []
28 };
29
30 // If true, don't bark on events that were not registered via expect().
31 // These events are recorded in capturedUnexpectedData instead of
32 // capturedEventData.
33 var ignoreUnexpected = false;
34
35 // This is a debugging aid to print all received events as well as the
36 // information whether they were expected.
37 var logAllRequests = false;
38
39 function runTests(tests) {
40   var waitForAboutBlank = function(_, info, tab) {
41     if (info.status == "complete" && tab.url == "about:blank") {
42       tabId = tab.id;
43       tabIdMap = {};
44       tabIdMap[tabId] = 0;
45       chrome.tabs.onUpdated.removeListener(waitForAboutBlank);
46       chrome.test.getConfig(function(config) {
47         testServerPort = config.testServer.port;
48         chrome.test.runTests(tests);
49       });
50     }
51   };
52   chrome.tabs.onUpdated.addListener(waitForAboutBlank);
53   chrome.tabs.create({url: "about:blank"});
54 }
55
56 // Returns an URL from the test server, fixing up the port. Must be called
57 // from within a test case passed to runTests.
58 function getServerURL(path, opt_host, opt_scheme) {
59   if (!testServerPort)
60     throw new Error("Called getServerURL outside of runTests.");
61   var host = opt_host || testServer;
62   var scheme = opt_scheme || defaultScheme;
63   return scheme + "://" + host + ":" + testServerPort + "/" + path;
64 }
65
66 // Helper to advance to the next test only when the tab has finished loading.
67 // This is because tabs.update can sometimes fail if the tab is in the middle
68 // of a navigation (from the previous test), resulting in flakiness.
69 function navigateAndWait(url, callback) {
70   var done = chrome.test.listenForever(chrome.tabs.onUpdated,
71       function (_, info, tab) {
72     if (tab.id == tabId && info.status == "complete") {
73       if (callback) callback();
74       done();
75     }
76   });
77   chrome.tabs.update(tabId, {url: url});
78 }
79
80 // data: array of extected events, each one is a dictionary:
81 //     { label: "<unique identifier>",
82 //       event: "<webrequest event type>",
83 //       details: { <expected details of the webrequest event> },
84 //       retval: { <dictionary that the event handler shall return> } (optional)
85 //     }
86 // order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that
87 //     event with label "a" needs to occur before event with label "b". The
88 //     relative order of "a" and "d" does not matter.
89 // filter: filter dictionary passed on to the event subscription of the
90 //     webRequest API.
91 // extraInfoSpec: the union of all desired extraInfoSpecs for the events.
92 function expect(data, order, filter, extraInfoSpec) {
93   expectedEventData = data || [];
94   capturedEventData = [];
95   capturedUnexpectedData = [];
96   expectedEventOrder = order || [];
97   if (expectedEventData.length > 0) {
98     eventsCaptured = chrome.test.callbackAdded();
99   }
100   tabAndFrameUrls = {};  // Maps "{tabId}-{frameId}" to the URL of the frame.
101   frameIdMap = {"-1": -1};
102   removeListeners();
103   resetDeclarativeRules();
104   initListeners(filter || {urls: ["<all_urls>"]}, extraInfoSpec || []);
105   // Fill in default values.
106   for (var i = 0; i < expectedEventData.length; ++i) {
107     if (!('method' in expectedEventData[i].details)) {
108       expectedEventData[i].details.method = "GET";
109     }
110     if (!('tabId' in expectedEventData[i].details)) {
111       expectedEventData[i].details.tabId = tabIdMap[tabId];
112     }
113     if (!('frameId' in expectedEventData[i].details)) {
114       expectedEventData[i].details.frameId = 0;
115     }
116     if (!('parentFrameId' in expectedEventData[i].details)) {
117       expectedEventData[i].details.parentFrameId = -1;
118     }
119     if (!('type' in expectedEventData[i].details)) {
120       expectedEventData[i].details.type = "main_frame";
121     }
122   }
123 }
124
125 function checkExpectations() {
126   if (capturedEventData.length < expectedEventData.length) {
127     return;
128   }
129   if (capturedEventData.length > expectedEventData.length) {
130     chrome.test.fail("Recorded too many events. " +
131         JSON.stringify(capturedEventData));
132     return;
133   }
134   // We have ensured that capturedEventData contains exactly the same elements
135   // as expectedEventData. Now we need to verify the ordering.
136   // Step 1: build positions such that
137   //     positions[<event-label>]=<position of this event in capturedEventData>
138   var curPos = 0;
139   var positions = {}
140   capturedEventData.forEach(function (event) {
141     chrome.test.assertTrue(event.hasOwnProperty("label"));
142     positions[event.label] = curPos;
143     curPos++;
144   });
145   // Step 2: check that elements arrived in correct order
146   expectedEventOrder.forEach(function (order) {
147     var previousLabel = undefined;
148     order.forEach(function(label) {
149       if (previousLabel === undefined) {
150         previousLabel = label;
151         return;
152       }
153       chrome.test.assertTrue(positions[previousLabel] < positions[label],
154           "Event " + previousLabel + " is supposed to arrive before " +
155           label + ".");
156       previousLabel = label;
157     });
158   });
159
160   eventsCaptured();
161 }
162
163 // Simple check to see that we have a User-Agent header, and that it contains
164 // an expected value. This is a basic check that the request headers are valid.
165 function checkUserAgent(headers) {
166   for (var i in headers) {
167     if (headers[i].name.toLowerCase() == "user-agent")
168       return headers[i].value.toLowerCase().indexOf("chrome") != -1;
169   }
170   return false;
171 }
172
173 function captureEvent(name, details, callback) {
174   // Ignore system-level requests like safebrowsing updates and favicon fetches
175   // since they are unpredictable.
176   if (details.tabId == -1 || details.type == "other" ||
177       details.url.match(/\/favicon.ico$/) ||
178       details.url.match(/https:\/\/dl.google.com/))
179     return;
180
181   // Pull the extra per-event options out of the expected data. These let
182   // us specify special return values per event.
183   var currentIndex = capturedEventData.length;
184   var extraOptions;
185   var retval;
186   if (expectedEventData.length > currentIndex) {
187     retval =
188         expectedEventData[currentIndex].retval_function ?
189         expectedEventData[currentIndex].retval_function(name, details) :
190         expectedEventData[currentIndex].retval;
191   }
192
193   // Check that the frameId can be used to reliably determine the URL of the
194   // frame that caused requests.
195   if (name == "onBeforeRequest") {
196     chrome.test.assertTrue('frameId' in details &&
197                            typeof details.frameId === 'number');
198     chrome.test.assertTrue('tabId' in details &&
199                             typeof details.tabId === 'number');
200     var key = details.tabId + "-" + details.frameId;
201     if (details.type == "main_frame" || details.type == "sub_frame") {
202       tabAndFrameUrls[key] = details.url;
203     }
204     details.frameUrl = tabAndFrameUrls[key] || "unknown frame URL";
205   }
206
207   // This assigns unique IDs to frames. The new IDs are only deterministic, if
208   // the frames documents are loaded in order. Don't write browser tests with
209   // more than one frame ID and rely on their numbers.
210   if (!(details.frameId in frameIdMap)) {
211     // Subtract one to discount for {"-1": -1} mapping that always exists.
212     // This gives the first frame the ID 0.
213     frameIdMap[details.frameId] = Object.keys(frameIdMap).length - 1;
214   }
215   details.frameId = frameIdMap[details.frameId];
216   details.parentFrameId = frameIdMap[details.parentFrameId];
217
218   // This assigns unique IDs to newly opened tabs. However, the new IDs are only
219   // deterministic, if the order in which the tabs are opened is deterministic.
220   if (!(details.tabId in tabIdMap)) {
221     tabIdMap[details.tabId] = Object.keys(tabIdMap).length;
222   }
223   details.tabId = tabIdMap[details.tabId];
224
225   delete details.requestId;
226   delete details.timeStamp;
227   if (details.requestHeaders) {
228     details.requestHeadersValid = checkUserAgent(details.requestHeaders);
229     delete details.requestHeaders;
230   }
231   if (details.responseHeaders) {
232     details.responseHeadersExist = true;
233     delete details.responseHeaders;
234   }
235
236   // find |details| in expectedEventData
237   var found = false;
238   var label = undefined;
239   expectedEventData.forEach(function (exp) {
240     if (deepEq(exp.event, name) && deepEq(exp.details, details)) {
241       if (found) {
242         chrome.test.fail("Received event twice '" + name + "':" +
243             JSON.stringify(details));
244       } else {
245         found = true;
246         label = exp.label;
247       }
248     }
249   });
250   if (!found && !ignoreUnexpected) {
251     console.log("Expected events: " +
252         JSON.stringify(expectedEventData, null, 2));
253     chrome.test.fail("Received unexpected event '" + name + "':" +
254         JSON.stringify(details, null, 2));
255   }
256
257   if (found) {
258     if (logAllRequests) {
259       console.log("Expected: " + name + ": " + JSON.stringify(details));
260     }
261     capturedEventData.push({label: label, event: name, details: details});
262
263     // checkExpecations decrements the counter of pending events. We may only
264     // call it if an expected event has occurred.
265     checkExpectations();
266   } else {
267     if (logAllRequests) {
268       console.log("NOT Expected: " + name + ": " + JSON.stringify(details));
269     }
270     capturedUnexpectedData.push({label: label, event: name, details: details});
271   }
272
273   if (callback) {
274     window.setTimeout(callback, 0, retval);
275   } else {
276     return retval;
277   }
278 }
279
280 // Simple array intersection. We use this to filter extraInfoSpec so
281 // that only the allowed specs are sent to each listener.
282 function intersect(array1, array2) {
283   return array1.filter(function(x) { return array2.indexOf(x) != -1; });
284 }
285
286 function initListeners(filter, extraInfoSpec) {
287   var onBeforeRequest = function(details) {
288     return captureEvent("onBeforeRequest", details);
289   };
290   listeners['onBeforeRequest'].push(onBeforeRequest);
291
292   var onBeforeSendHeaders = function(details) {
293     return captureEvent("onBeforeSendHeaders", details);
294   };
295   listeners['onBeforeSendHeaders'].push(onBeforeSendHeaders);
296
297   var onSendHeaders = function(details) {
298     return captureEvent("onSendHeaders", details);
299   };
300   listeners['onSendHeaders'].push(onSendHeaders);
301
302   var onHeadersReceived = function(details) {
303     return captureEvent("onHeadersReceived", details);
304   };
305   listeners['onHeadersReceived'].push(onHeadersReceived);
306
307   var onAuthRequired = function(details) {
308     return captureEvent("onAuthRequired", details, callback);
309   };
310   listeners['onAuthRequired'].push(onAuthRequired);
311
312   var onResponseStarted = function(details) {
313     return captureEvent("onResponseStarted", details);
314   };
315   listeners['onResponseStarted'].push(onResponseStarted);
316
317   var onBeforeRedirect = function(details) {
318     return captureEvent("onBeforeRedirect", details);
319   };
320   listeners['onBeforeRedirect'].push(onBeforeRedirect);
321
322   var onCompleted = function(details) {
323     return captureEvent("onCompleted", details);
324   };
325   listeners['onCompleted'].push(onCompleted);
326
327   var onErrorOccurred = function(details) {
328     return captureEvent("onErrorOccurred", details);
329   };
330   listeners['onErrorOccurred'].push(onErrorOccurred);
331
332   chrome.webRequest.onBeforeRequest.addListener(
333       onBeforeRequest, filter,
334       intersect(extraInfoSpec, ["blocking", "requestBody"]));
335
336   chrome.webRequest.onBeforeSendHeaders.addListener(
337       onBeforeSendHeaders, filter,
338       intersect(extraInfoSpec, ["blocking", "requestHeaders"]));
339
340   chrome.webRequest.onSendHeaders.addListener(
341       onSendHeaders, filter,
342       intersect(extraInfoSpec, ["requestHeaders"]));
343
344   chrome.webRequest.onHeadersReceived.addListener(
345       onHeadersReceived, filter,
346       intersect(extraInfoSpec, ["blocking", "responseHeaders"]));
347
348   chrome.webRequest.onAuthRequired.addListener(
349       onAuthRequired, filter,
350       intersect(extraInfoSpec, ["asyncBlocking", "blocking",
351                                 "responseHeaders"]));
352
353   chrome.webRequest.onResponseStarted.addListener(
354       onResponseStarted, filter,
355       intersect(extraInfoSpec, ["responseHeaders"]));
356
357   chrome.webRequest.onBeforeRedirect.addListener(
358       onBeforeRedirect, filter, intersect(extraInfoSpec, ["responseHeaders"]));
359
360   chrome.webRequest.onCompleted.addListener(
361       onCompleted, filter,
362       intersect(extraInfoSpec, ["responseHeaders"]));
363
364   chrome.webRequest.onErrorOccurred.addListener(onErrorOccurred, filter);
365 }
366
367 function removeListeners() {
368   function helper(eventName) {
369     for (var i in listeners[eventName]) {
370       chrome.webRequest[eventName].removeListener(listeners[eventName][i]);
371     }
372     listeners[eventName].length = 0;
373     chrome.test.assertFalse(chrome.webRequest[eventName].hasListeners());
374   }
375   helper('onBeforeRequest');
376   helper('onBeforeSendHeaders');
377   helper('onAuthRequired');
378   helper('onSendHeaders');
379   helper('onHeadersReceived');
380   helper('onResponseStarted');
381   helper('onBeforeRedirect');
382   helper('onCompleted');
383   helper('onErrorOccurred');
384 }
385
386 function resetDeclarativeRules() {
387   chrome.declarativeWebRequest.onRequest.removeRules();
388 }