| OLD | NEW |
| (Empty) |
| 1 var getURL = chrome.extension.getURL; | |
| 2 var deepEq = chrome.test.checkDeepEq; | |
| 3 var expectedEventData; | |
| 4 var capturedEventData; | |
| 5 var expectedEventOrder; | |
| 6 var tabId; | |
| 7 var testServerPort; | |
| 8 var eventsCaptured; | |
| 9 | |
| 10 function runTests(tests) { | |
| 11 chrome.tabs.create({url: "about:blank"}, function(tab) { | |
| 12 tabId = tab.id; | |
| 13 chrome.test.getConfig(function(config) { | |
| 14 testServerPort = config.testServer.port; | |
| 15 chrome.test.runTests(tests); | |
| 16 }); | |
| 17 }); | |
| 18 } | |
| 19 | |
| 20 // Returns an URL from the test server, fixing up the port. Must be called | |
| 21 // from within a test case passed to runTests. | |
| 22 function getServerURL(path) { | |
| 23 if (!testServerPort) | |
| 24 throw new Error("Called getServerURL outside of runTests."); | |
| 25 return "http://www.a.com:" + testServerPort + "/" + path; | |
| 26 } | |
| 27 | |
| 28 // Helper to advance to the next test only when the tab has finished loading. | |
| 29 // This is because tabs.update can sometimes fail if the tab is in the middle | |
| 30 // of a navigation (from the previous test), resulting in flakiness. | |
| 31 function navigateAndWait(url, callback) { | |
| 32 var done = chrome.test.listenForever(chrome.tabs.onUpdated, | |
| 33 function (_, info, tab) { | |
| 34 if (tab.id == tabId && info.status == "complete") { | |
| 35 if (callback) callback(); | |
| 36 done(); | |
| 37 } | |
| 38 }); | |
| 39 chrome.tabs.update(tabId, {url: url}); | |
| 40 } | |
| 41 | |
| 42 // data: array of extected events, each one is a dictionary: | |
| 43 // { label: "<unique identifier>", | |
| 44 // event: "<webrequest event type>", | |
| 45 // details: { <expected details of the webrequest event> }, | |
| 46 // retval: { <dictionary that the event handler shall return> } (optional) | |
| 47 // } | |
| 48 // order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that | |
| 49 // event with label "a" needs to occur before event with label "b". The | |
| 50 // relative order of "a" and "d" does not matter. | |
| 51 // filter: filter dictionary passed on to the event subscription of the | |
| 52 // webRequest API. | |
| 53 // extraInfoSpec: the union of all desired extraInfoSpecs for the events. | |
| 54 function expect(data, order, filter, extraInfoSpec) { | |
| 55 expectedEventData = data; | |
| 56 capturedEventData = []; | |
| 57 expectedEventOrder = order; | |
| 58 eventsCaptured = chrome.test.callbackAdded(); | |
| 59 tabAndFrameUrls = {}; // Maps "{tabId}-{frameId}" to the URL of the frame. | |
| 60 removeListeners(); | |
| 61 initListeners(filter || {}, extraInfoSpec || []); | |
| 62 } | |
| 63 | |
| 64 function checkExpectations() { | |
| 65 if (capturedEventData.length < expectedEventData.length) { | |
| 66 return; | |
| 67 } | |
| 68 if (capturedEventData.length > expectedEventData.length) { | |
| 69 chrome.test.fail("Recorded too many events. " + | |
| 70 JSON.stringify(capturedEventData)); | |
| 71 return; | |
| 72 } | |
| 73 // We have ensured that capturedEventData contains exactly the same elements | |
| 74 // as expectedEventData. Now we need to verify the ordering. | |
| 75 // Step 1: build positions such that | |
| 76 // positions[<event-label>]=<position of this event in capturedEventData> | |
| 77 var curPos = 0; | |
| 78 var positions = {} | |
| 79 capturedEventData.forEach(function (event) { | |
| 80 chrome.test.assertTrue(event.hasOwnProperty("label")); | |
| 81 positions[event.label] = curPos; | |
| 82 curPos++; | |
| 83 }); | |
| 84 // Step 2: check that elements arrived in correct order | |
| 85 expectedEventOrder.forEach(function (order) { | |
| 86 var previousLabel = undefined; | |
| 87 order.forEach(function(label) { | |
| 88 if (previousLabel === undefined) { | |
| 89 previousLabel = label; | |
| 90 return; | |
| 91 } | |
| 92 chrome.test.assertTrue(positions[previousLabel] < positions[label], | |
| 93 "Event " + previousLabel + " is supposed to arrive before " + | |
| 94 label + "."); | |
| 95 previousLabel = label; | |
| 96 }); | |
| 97 }); | |
| 98 | |
| 99 eventsCaptured(); | |
| 100 } | |
| 101 | |
| 102 // Simple check to see that we have a User-Agent header, and that it contains | |
| 103 // an expected value. This is a basic check that the request headers are valid. | |
| 104 function checkUserAgent(headers) { | |
| 105 for (var i in headers) { | |
| 106 if (headers[i].name.toLowerCase() == "user-agent") | |
| 107 return headers[i].value.toLowerCase().indexOf("chrome") != -1; | |
| 108 } | |
| 109 return false; | |
| 110 } | |
| 111 | |
| 112 function captureEvent(name, details) { | |
| 113 // Ignore system-level requests like safebrowsing updates and favicon fetches | |
| 114 // since they are unpredictable. | |
| 115 if (details.tabId == -1 || details.type == "other" || | |
| 116 details.url.match(/\/favicon.ico$/) || | |
| 117 details.url.match(/https:\/\/dl.google.com/)) | |
| 118 return; | |
| 119 | |
| 120 // Pull the extra per-event options out of the expected data. These let | |
| 121 // us specify special return values per event. | |
| 122 var currentIndex = capturedEventData.length; | |
| 123 var extraOptions; | |
| 124 if (expectedEventData.length > currentIndex) { | |
| 125 retval = expectedEventData[currentIndex].retval; | |
| 126 } | |
| 127 | |
| 128 // Check that the frameId can be used to reliably determine the URL of the | |
| 129 // frame that caused requests. | |
| 130 if (name == "onBeforeRequest") { | |
| 131 chrome.test.assertTrue('frameId' in details && | |
| 132 typeof details.frameId === 'number'); | |
| 133 chrome.test.assertTrue('tabId' in details && | |
| 134 typeof details.tabId === 'number'); | |
| 135 var key = details.tabId + "-" + details.frameId; | |
| 136 if (details.type == "main_frame" || details.type == "sub_frame") { | |
| 137 tabAndFrameUrls[key] = details.url; | |
| 138 } | |
| 139 details.frameUrl = tabAndFrameUrls[key] || "unknown frame URL"; | |
| 140 } | |
| 141 delete details.frameId; | |
| 142 | |
| 143 delete details.requestId; | |
| 144 delete details.timeStamp; | |
| 145 if (details.requestHeaders) { | |
| 146 details.requestHeadersValid = checkUserAgent(details.requestHeaders); | |
| 147 delete details.requestHeaders; | |
| 148 } | |
| 149 if (details.responseHeaders) { | |
| 150 details.responseHeadersExist = true; | |
| 151 delete details.responseHeaders; | |
| 152 } | |
| 153 | |
| 154 // find |details| in expectedEventData | |
| 155 var found = false; | |
| 156 var label = undefined; | |
| 157 expectedEventData.forEach(function (exp) { | |
| 158 if (deepEq(exp.event, name) && deepEq(exp.details, details)) { | |
| 159 if (found) { | |
| 160 chrome.test.fail("Received event twice '" + name + "':" + | |
| 161 JSON.stringify(details)); | |
| 162 } else { | |
| 163 found = true; | |
| 164 label = exp.label; | |
| 165 } | |
| 166 } | |
| 167 }); | |
| 168 if (!found) { | |
| 169 chrome.test.fail("Received unexpected event '" + name + "':" + | |
| 170 JSON.stringify(details)); | |
| 171 } | |
| 172 | |
| 173 capturedEventData.push({label: label, event: name, details: details}); | |
| 174 checkExpectations(); | |
| 175 return retval; | |
| 176 } | |
| 177 | |
| 178 // Simple array intersection. We use this to filter extraInfoSpec so | |
| 179 // that only the allowed specs are sent to each listener. | |
| 180 function intersect(array1, array2) { | |
| 181 return array1.filter(function(x) { return array2.indexOf(x) != -1; }); | |
| 182 } | |
| 183 | |
| 184 function initListeners(filter, extraInfoSpec) { | |
| 185 chrome.experimental.webRequest.onBeforeRequest.addListener( | |
| 186 function(details) { | |
| 187 return captureEvent("onBeforeRequest", details); | |
| 188 }, filter, intersect(extraInfoSpec, ["blocking"])); | |
| 189 chrome.experimental.webRequest.onBeforeSendHeaders.addListener( | |
| 190 function(details) { | |
| 191 return captureEvent("onBeforeSendHeaders", details); | |
| 192 }, filter, intersect(extraInfoSpec, ["blocking", "requestHeaders"])); | |
| 193 chrome.experimental.webRequest.onSendHeaders.addListener( | |
| 194 function(details) { | |
| 195 return captureEvent("onSendHeaders", details); | |
| 196 }, filter, intersect(extraInfoSpec, ["requestHeaders"])); | |
| 197 chrome.experimental.webRequest.onAuthRequired.addListener( | |
| 198 function(details) { | |
| 199 return captureEvent("onAuthRequired", details); | |
| 200 }, filter, intersect(extraInfoSpec, ["responseHeaders", "statusLine"])); | |
| 201 chrome.experimental.webRequest.onResponseStarted.addListener( | |
| 202 function(details) { | |
| 203 return captureEvent("onResponseStarted", details); | |
| 204 }, filter, intersect(extraInfoSpec, ["responseHeaders", "statusLine"])); | |
| 205 chrome.experimental.webRequest.onBeforeRedirect.addListener( | |
| 206 function(details) { | |
| 207 return captureEvent("onBeforeRedirect", details); | |
| 208 }, filter, intersect(extraInfoSpec, ["responseHeaders", "statusLine"])); | |
| 209 chrome.experimental.webRequest.onCompleted.addListener( | |
| 210 function(details) { | |
| 211 return captureEvent("onCompleted", details); | |
| 212 }, filter, intersect(extraInfoSpec, ["responseHeaders", "statusLine"])); | |
| 213 chrome.experimental.webRequest.onErrorOccurred.addListener( | |
| 214 function(details) { | |
| 215 return captureEvent("onErrorOccurred", details); | |
| 216 }, filter); | |
| 217 } | |
| 218 | |
| 219 function removeListeners() { | |
| 220 function helper(event) { | |
| 221 // Note: We're poking at the internal event data, but it's easier than | |
| 222 // the alternative. If this starts failing, we just need to update this | |
| 223 // helper. | |
| 224 for (var cb in event.callbackMap_) { | |
| 225 event.removeListener(cb); | |
| 226 } | |
| 227 chrome.test.assertFalse(event.hasListeners()); | |
| 228 } | |
| 229 helper(chrome.experimental.webRequest.onBeforeRequest); | |
| 230 helper(chrome.experimental.webRequest.onBeforeSendHeaders); | |
| 231 helper(chrome.experimental.webRequest.onAuthRequired); | |
| 232 helper(chrome.experimental.webRequest.onSendHeaders); | |
| 233 helper(chrome.experimental.webRequest.onResponseStarted); | |
| 234 helper(chrome.experimental.webRequest.onBeforeRedirect); | |
| 235 helper(chrome.experimental.webRequest.onCompleted); | |
| 236 helper(chrome.experimental.webRequest.onErrorOccurred); | |
| 237 } | |
| OLD | NEW |