| OLD | NEW |
| (Empty) |
| 1 // Copyright 2013 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 deepEq = chrome.test.checkDeepEq; | |
| 6 var expectedEventData; | |
| 7 var expectedEventOrder; | |
| 8 var capturedEventData; | |
| 9 var nextFrameId; | |
| 10 var frameIds; | |
| 11 var nextTabId; | |
| 12 var tabIds; | |
| 13 var nextProcessId; | |
| 14 var processIds; | |
| 15 var initialized = false; | |
| 16 | |
| 17 var debug = false; | |
| 18 | |
| 19 function deepCopy(obj) { | |
| 20 if (obj === null) | |
| 21 return null; | |
| 22 if (typeof(obj) != 'object') | |
| 23 return obj; | |
| 24 if (Array.isArray(obj)) { | |
| 25 var tmp_array = new Array; | |
| 26 for (var i = 0; i < obj.length; i++) { | |
| 27 tmp_array.push(deepCopy(obj[i])); | |
| 28 } | |
| 29 return tmp_array; | |
| 30 } | |
| 31 | |
| 32 var tmp_object = {} | |
| 33 for (var p in obj) { | |
| 34 tmp_object[p] = deepCopy(obj[p]); | |
| 35 } | |
| 36 return tmp_object; | |
| 37 } | |
| 38 | |
| 39 // data: array of expected events, each one is a dictionary: | |
| 40 // { label: "<unique identifier>", | |
| 41 // event: "<webnavigation event type>", | |
| 42 // details: { <expected details of the event> } | |
| 43 // } | |
| 44 // order: an array of sequences, e.g. [ ["a", "b", "c"], ["d", "e"] ] means that | |
| 45 // event with label "a" needs to occur before event with label "b". The | |
| 46 // relative order of "a" and "d" does not matter. | |
| 47 function expect(data, order) { | |
| 48 expectedEventData = data; | |
| 49 capturedEventData = []; | |
| 50 expectedEventOrder = order; | |
| 51 nextFrameId = 1; | |
| 52 frameIds = {}; | |
| 53 nextTabId = 0; | |
| 54 tabIds = {}; | |
| 55 nextProcessId = -1; | |
| 56 processIds = {} | |
| 57 initListeners(); | |
| 58 } | |
| 59 | |
| 60 function checkExpectations() { | |
| 61 if (capturedEventData.length < expectedEventData.length) { | |
| 62 return; | |
| 63 } | |
| 64 if (capturedEventData.length > expectedEventData.length) { | |
| 65 chrome.test.fail("Recorded too many events. " + | |
| 66 JSON.stringify(capturedEventData)); | |
| 67 } | |
| 68 // We have ensured that capturedEventData contains exactly the same elements | |
| 69 // as expectedEventData. Now we need to verify the ordering. | |
| 70 // Step 1: build positions such that | |
| 71 // position[<event-label>]=<position of this event in capturedEventData> | |
| 72 var curPos = 0; | |
| 73 var positions = {}; | |
| 74 capturedEventData.forEach(function (event) { | |
| 75 chrome.test.assertTrue(event.hasOwnProperty("label")); | |
| 76 positions[event.label] = curPos; | |
| 77 curPos++; | |
| 78 }); | |
| 79 // Step 2: check that elements arrived in correct order | |
| 80 expectedEventOrder.forEach(function (order) { | |
| 81 var previousLabel = undefined; | |
| 82 order.forEach(function (label) { | |
| 83 if (previousLabel === undefined) { | |
| 84 previousLabel = label; | |
| 85 return; | |
| 86 } | |
| 87 chrome.test.assertTrue(positions[previousLabel] < positions[label], | |
| 88 "Event " + previousLabel + " is supposed to arrive before " + | |
| 89 label + "."); | |
| 90 previousLabel = label; | |
| 91 }); | |
| 92 }); | |
| 93 chrome.test.succeed(); | |
| 94 } | |
| 95 | |
| 96 function captureEvent(name, details) { | |
| 97 if ('url' in details) { | |
| 98 // Skip about:blank navigations | |
| 99 if (details.url == 'about:blank') { | |
| 100 return; | |
| 101 } | |
| 102 // Strip query parameter as it is hard to predict. | |
| 103 details.url = details.url.replace(new RegExp('\\?[^#]*'), ''); | |
| 104 } | |
| 105 // normalize details. | |
| 106 if ('timeStamp' in details) { | |
| 107 details.timeStamp = 0; | |
| 108 } | |
| 109 if (('frameId' in details) && (details.frameId != 0)) { | |
| 110 if (frameIds[details.frameId] === undefined) { | |
| 111 frameIds[details.frameId] = nextFrameId++; | |
| 112 } | |
| 113 details.frameId = frameIds[details.frameId]; | |
| 114 } | |
| 115 if (('parentFrameId' in details) && (details.parentFrameId > 0)) { | |
| 116 if (frameIds[details.parentFrameId] === undefined) { | |
| 117 frameIds[details.parentFrameId] = nextFrameId++; | |
| 118 } | |
| 119 details.parentFrameId = frameIds[details.parentFrameId]; | |
| 120 } | |
| 121 if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) { | |
| 122 if (frameIds[details.sourceFrameId] === undefined) { | |
| 123 frameIds[details.sourceFrameId] = nextFrameId++; | |
| 124 } | |
| 125 details.sourceFrameId = frameIds[details.sourceFrameId]; | |
| 126 } | |
| 127 if ('tabId' in details) { | |
| 128 if (tabIds[details.tabId] === undefined) { | |
| 129 tabIds[details.tabId] = nextTabId++; | |
| 130 } | |
| 131 details.tabId = tabIds[details.tabId]; | |
| 132 } | |
| 133 if ('sourceTabId' in details) { | |
| 134 if (tabIds[details.sourceTabId] === undefined) { | |
| 135 tabIds[details.sourceTabId] = nextTabId++; | |
| 136 } | |
| 137 details.sourceTabId = tabIds[details.sourceTabId]; | |
| 138 } | |
| 139 if ('replacedTabId' in details) { | |
| 140 if (tabIds[details.replacedTabId] === undefined) { | |
| 141 tabIds[details.replacedTabId] = nextTabId++; | |
| 142 } | |
| 143 details.replacedTabId = tabIds[details.replacedTabId]; | |
| 144 } | |
| 145 if ('processId' in details) { | |
| 146 if (processIds[details.processId] === undefined) { | |
| 147 processIds[details.processId] = nextProcessId++; | |
| 148 } | |
| 149 details.processId = processIds[details.processId]; | |
| 150 } | |
| 151 if ('sourceProcessId' in details) { | |
| 152 if (processIds[details.sourceProcessId] === undefined) { | |
| 153 processIds[details.sourceProcessId] = nextProcessId++; | |
| 154 } | |
| 155 details.sourceProcessId = processIds[details.sourceProcessId]; | |
| 156 } | |
| 157 | |
| 158 if (debug) | |
| 159 console.log("Received event '" + name + "':" + JSON.stringify(details)); | |
| 160 | |
| 161 // find |details| in expectedEventData | |
| 162 var found = false; | |
| 163 var label = undefined; | |
| 164 expectedEventData.forEach(function (exp) { | |
| 165 if (exp.event == name) { | |
| 166 var exp_details; | |
| 167 var alt_details; | |
| 168 if ('transitionQualifiers' in exp.details) { | |
| 169 var idx = exp.details['transitionQualifiers'].indexOf( | |
| 170 'maybe_client_redirect'); | |
| 171 if (idx >= 0) { | |
| 172 exp_details = deepCopy(exp.details); | |
| 173 exp_details['transitionQualifiers'].splice(idx, 1); | |
| 174 alt_details = deepCopy(exp_details); | |
| 175 alt_details['transitionQualifiers'].push('client_redirect'); | |
| 176 } else { | |
| 177 exp_details = exp.details; | |
| 178 alt_details = exp.details; | |
| 179 } | |
| 180 } else { | |
| 181 exp_details = exp.details; | |
| 182 alt_details = exp.details; | |
| 183 } | |
| 184 if (deepEq(exp_details, details) || deepEq(alt_details, details)) { | |
| 185 if (!found) { | |
| 186 found = true; | |
| 187 label = exp.label; | |
| 188 exp.event = undefined; | |
| 189 } | |
| 190 } | |
| 191 } | |
| 192 }); | |
| 193 if (!found) { | |
| 194 chrome.test.fail("Received unexpected event '" + name + "':" + | |
| 195 JSON.stringify(details)); | |
| 196 } | |
| 197 capturedEventData.push({label: label, event: name, details: details}); | |
| 198 checkExpectations(); | |
| 199 } | |
| 200 | |
| 201 function initListeners() { | |
| 202 if (initialized) | |
| 203 return; | |
| 204 initialized = true; | |
| 205 chrome.webNavigation.onBeforeNavigate.addListener( | |
| 206 function(details) { | |
| 207 captureEvent("onBeforeNavigate", details); | |
| 208 }); | |
| 209 chrome.webNavigation.onCommitted.addListener( | |
| 210 function(details) { | |
| 211 captureEvent("onCommitted", details); | |
| 212 }); | |
| 213 chrome.webNavigation.onDOMContentLoaded.addListener( | |
| 214 function(details) { | |
| 215 captureEvent("onDOMContentLoaded", details); | |
| 216 }); | |
| 217 chrome.webNavigation.onCompleted.addListener( | |
| 218 function(details) { | |
| 219 captureEvent("onCompleted", details); | |
| 220 }); | |
| 221 chrome.webNavigation.onCreatedNavigationTarget.addListener( | |
| 222 function(details) { | |
| 223 captureEvent("onCreatedNavigationTarget", details); | |
| 224 }); | |
| 225 chrome.webNavigation.onReferenceFragmentUpdated.addListener( | |
| 226 function(details) { | |
| 227 captureEvent("onReferenceFragmentUpdated", details); | |
| 228 }); | |
| 229 chrome.webNavigation.onErrorOccurred.addListener( | |
| 230 function(details) { | |
| 231 captureEvent("onErrorOccurred", details); | |
| 232 }); | |
| 233 chrome.webNavigation.onTabReplaced.addListener( | |
| 234 function(details) { | |
| 235 captureEvent("onTabReplaced", details); | |
| 236 }); | |
| 237 chrome.webNavigation.onHistoryStateUpdated.addListener( | |
| 238 function(details) { | |
| 239 captureEvent("onHistoryStateUpdated", details); | |
| 240 }); | |
| 241 } | |
| 242 | |
| 243 // Returns the usual order of navigation events. | |
| 244 function navigationOrder(prefix) { | |
| 245 return [ prefix + "onBeforeNavigate", | |
| 246 prefix + "onCommitted", | |
| 247 prefix + "onDOMContentLoaded", | |
| 248 prefix + "onCompleted" ]; | |
| 249 } | |
| 250 | |
| 251 // Returns the constraints expressing that a frame is an iframe of another | |
| 252 // frame. | |
| 253 function isIFrameOf(iframe, main_frame) { | |
| 254 return [ main_frame + "onCommitted", | |
| 255 iframe + "onBeforeNavigate", | |
| 256 iframe + "onCompleted", | |
| 257 main_frame + "onCompleted" ]; | |
| 258 } | |
| 259 | |
| 260 // Returns the constraint expressing that a frame was loaded by another. | |
| 261 function isLoadedBy(target, source) { | |
| 262 return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"]; | |
| 263 } | |
| OLD | NEW |