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.
5 var deepEq = chrome.test.checkDeepEq;
7 var expectedEventOrder;
15 var initialized = false;
19 function deepCopy(obj) {
22 if (typeof(obj) != 'object')
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]));
34 tmp_object[p] = deepCopy(obj[p]);
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> }
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;
60 function checkExpectations() {
61 if (capturedEventData.length < expectedEventData.length) {
64 if (capturedEventData.length > expectedEventData.length) {
65 chrome.test.fail("Recorded too many events. " +
66 JSON.stringify(capturedEventData));
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>
74 capturedEventData.forEach(function (event) {
75 chrome.test.assertTrue(event.hasOwnProperty("label"));
76 positions[event.label] = curPos;
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;
87 chrome.test.assertTrue(positions[previousLabel] < positions[label],
88 "Event " + previousLabel + " is supposed to arrive before " +
90 previousLabel = label;
93 chrome.test.succeed();
96 function captureEvent(name, details) {
97 if ('url' in details) {
98 // Skip about:blank navigations
99 if (details.url == 'about:blank') {
102 // Strip query parameter as it is hard to predict.
103 details.url = details.url.replace(new RegExp('\\?[^#]*'), '');
105 // normalize details.
106 if ('timeStamp' in details) {
107 details.timeStamp = 0;
109 if (('frameId' in details) && (details.frameId != 0)) {
110 if (frameIds[details.frameId] === undefined) {
111 frameIds[details.frameId] = nextFrameId++;
113 details.frameId = frameIds[details.frameId];
115 if (('parentFrameId' in details) && (details.parentFrameId > 0)) {
116 if (frameIds[details.parentFrameId] === undefined) {
117 frameIds[details.parentFrameId] = nextFrameId++;
119 details.parentFrameId = frameIds[details.parentFrameId];
121 if (('sourceFrameId' in details) && (details.sourceFrameId != 0)) {
122 if (frameIds[details.sourceFrameId] === undefined) {
123 frameIds[details.sourceFrameId] = nextFrameId++;
125 details.sourceFrameId = frameIds[details.sourceFrameId];
127 if ('tabId' in details) {
128 if (tabIds[details.tabId] === undefined) {
129 tabIds[details.tabId] = nextTabId++;
131 details.tabId = tabIds[details.tabId];
133 if ('sourceTabId' in details) {
134 if (tabIds[details.sourceTabId] === undefined) {
135 tabIds[details.sourceTabId] = nextTabId++;
137 details.sourceTabId = tabIds[details.sourceTabId];
139 if ('replacedTabId' in details) {
140 if (tabIds[details.replacedTabId] === undefined) {
141 tabIds[details.replacedTabId] = nextTabId++;
143 details.replacedTabId = tabIds[details.replacedTabId];
145 if ('processId' in details) {
146 if (processIds[details.processId] === undefined) {
147 processIds[details.processId] = nextProcessId++;
149 details.processId = processIds[details.processId];
151 if ('sourceProcessId' in details) {
152 if (processIds[details.sourceProcessId] === undefined) {
153 processIds[details.sourceProcessId] = nextProcessId++;
155 details.sourceProcessId = processIds[details.sourceProcessId];
159 console.log("Received event '" + name + "':" + JSON.stringify(details));
161 // find |details| in expectedEventData
163 var label = undefined;
164 expectedEventData.forEach(function (exp) {
165 if (exp.event == name) {
168 if ('transitionQualifiers' in exp.details) {
169 var idx = exp.details['transitionQualifiers'].indexOf(
170 'maybe_client_redirect');
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');
177 exp_details = exp.details;
178 alt_details = exp.details;
181 exp_details = exp.details;
182 alt_details = exp.details;
184 if (deepEq(exp_details, details) || deepEq(alt_details, details)) {
188 exp.event = undefined;
194 chrome.test.fail("Received unexpected event '" + name + "':" +
195 JSON.stringify(details));
197 capturedEventData.push({label: label, event: name, details: details});
201 function initListeners() {
205 chrome.webNavigation.onBeforeNavigate.addListener(
207 captureEvent("onBeforeNavigate", details);
209 chrome.webNavigation.onCommitted.addListener(
211 captureEvent("onCommitted", details);
213 chrome.webNavigation.onDOMContentLoaded.addListener(
215 captureEvent("onDOMContentLoaded", details);
217 chrome.webNavigation.onCompleted.addListener(
219 captureEvent("onCompleted", details);
221 chrome.webNavigation.onCreatedNavigationTarget.addListener(
223 captureEvent("onCreatedNavigationTarget", details);
225 chrome.webNavigation.onReferenceFragmentUpdated.addListener(
227 captureEvent("onReferenceFragmentUpdated", details);
229 chrome.webNavigation.onErrorOccurred.addListener(
231 captureEvent("onErrorOccurred", details);
233 chrome.webNavigation.onTabReplaced.addListener(
235 captureEvent("onTabReplaced", details);
237 chrome.webNavigation.onHistoryStateUpdated.addListener(
239 captureEvent("onHistoryStateUpdated", details);
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" ];
251 // Returns the constraints expressing that a frame is an iframe of another
253 function isIFrameOf(iframe, main_frame) {
254 return [ main_frame + "onCommitted",
255 iframe + "onBeforeNavigate",
256 iframe + "onCompleted",
257 main_frame + "onCompleted" ];
260 // Returns the constraint expressing that a frame was loaded by another.
261 function isLoadedBy(target, source) {
262 return [ source + "onDOMContentLoaded", target + "onBeforeNavigate"];