1// Copyright (c) 2014 The Chromium OS 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 5var cycle_tabs = {}; 6var cycles = {}; 7var time_ratio = 3600 * 1000 / test_time_ms; // default test time is 1 hour 8var preexisting_windows = []; 9var log_lines = []; 10var error_codes = {}; //for each active tabId 11var page_timestamps = []; 12var page_timestamps_recorder = {}; 13var unique_url_salt = 1; 14 15function setupTest() { 16 //adding these listeners to track request failure codes 17 chrome.webRequest.onCompleted.addListener(capture_completed_status, 18 {urls: ["<all_urls>"]}); 19 chrome.windows.getAll(null, function(windows) { 20 preexisting_windows = windows; 21 for (var i = 0; i < tasks.length; i++) { 22 setTimeout(launch_task, tasks[i].start / time_ratio, tasks[i]); 23 } 24 var end = 3600 * 1000 / time_ratio; 25 log_lines = []; 26 page_timestamps = []; 27 page_timestamps_recorder = {}; 28 record_log_entry(dateToString(new Date()) + " Loop started"); 29 setTimeout(send_summary, end); 30 }); 31} 32 33function close_preexisting_windows() { 34 for (var i = 0; i < preexisting_windows.length; i++) { 35 chrome.windows.remove(preexisting_windows[i].id); 36 } 37 preexisting_windows.length = 0; 38} 39 40function get_active_url(cycle) { 41 active_idx = cycle.idx == 0 ? cycle.urls.length - 1 : cycle.idx - 1; 42 return cycle.urls[active_idx]; 43} 44 45function testListener(request, sender, sendResponse) { 46 end = Date.now() 47 page = page_timestamps_recorder[sender.tab.id]; 48 page['end_load_time'] = end; 49 console.log("page_timestamps_recorder:"); 50 console.log(JSON.stringify(page_timestamps_recorder)); 51 if (sender.tab.id in cycle_tabs) { 52 cycle = cycle_tabs[sender.tab.id]; 53 cycle.successful_loads++; 54 url = get_active_url(cycle); 55 record_log_entry(dateToString(new Date()) + " [load success] " + url); 56 if (request.action == "should_scroll" && cycle.focus) { 57 sendResponse({"should_scroll": should_scroll, 58 "should_scroll_up": should_scroll_up, 59 "scroll_loop": scroll_loop, 60 "scroll_interval": scroll_interval_ms, 61 "scroll_by": scroll_by_pixels}); 62 } 63 delete cycle_tabs[sender.tab.id]; 64 } 65} 66 67function report_page_nav_to_test() { 68 //Sends message to PLT informing that user is navigating to new page. 69 var ping_url = 'http://localhost:8001/pagenav'; 70 var req = new XMLHttpRequest(); 71 req.open('GET', ping_url, true); 72 req.send(""); 73} 74 75function capture_completed_status(details) { 76 var tabId = details.tabId; 77 if (!(details.tabId in error_codes)) { 78 error_codes[tabId] = []; 79 } 80 var report = { 81 'url':details.url, 82 'code': details.statusCode, 83 'status': details.statusLine, 84 'time': new Date(details.timeStamp) 85 } 86 error_codes[tabId].push(report); 87} 88 89 90function cycle_navigate(cycle) { 91 cycle_tabs[cycle.id] = cycle; 92 var url = cycle.urls[cycle.idx]; 93 // Resetting the error codes. 94 // TODO(coconutruben) Verify if reseeting here might give us 95 // garbage data since some requests/responses might still come 96 // in before we update the tab, but we'll register them as 97 // information about the subsequent url 98 error_codes[cycle.id] = []; 99 record_log_entry(dateToString(new Date()) + " [load start] " + url) 100 var start = Date.now(); 101 // start_time of next page is end_browse_time of previous page 102 if (cycle.id in page_timestamps_recorder) { 103 page = page_timestamps_recorder[cycle.id]; 104 page['end_browse_time'] = start; 105 page_timestamps.push(page); 106 console.log(JSON.stringify(page_timestamps)); 107 } 108 page_timestamps_new_record(cycle.id, url, start); 109 chrome.tabs.update(cycle.id, {'url': url, 'selected': true}); 110 report_page_nav_to_test() 111 cycle.idx = (cycle.idx + 1) % cycle.urls.length; 112 if (cycle.timeout < cycle.delay / time_ratio && cycle.timeout > 0) { 113 cycle.timer = setTimeout(cycle_check_timeout, cycle.timeout, cycle); 114 } else { 115 cycle.timer = setTimeout(cycle_navigate, cycle.delay / time_ratio, cycle); 116 } 117} 118 119function record_error_codes(cycle) { 120 var error_report = dateToString(new Date()) + " [load failure details] " 121 + get_active_url(cycle) + "\n"; 122 var reports = error_codes[cycle.id]; 123 for (var i = 0; i < reports.length; i++) { 124 report = reports[i]; 125 error_report = error_report + "\t\t" + 126 dateToString(report.time) + " | " + 127 "[response code] " + report.code + " | " + 128 "[url] " + report.url + " | " + 129 "[status line] " + report.status + "\n"; 130 } 131 log_lines.push(error_report); 132 console.log(error_report); 133} 134 135function cycle_check_timeout(cycle) { 136 if (cycle.id in cycle_tabs) { 137 cycle.failed_loads++; 138 record_error_codes(cycle); 139 record_log_entry(dateToString(new Date()) + " [load failure] " + 140 get_active_url(cycle)); 141 cycle_navigate(cycle); 142 } else { 143 cycle.timer = setTimeout(cycle_navigate, 144 cycle.delay / time_ratio - cycle.timeout, 145 cycle); 146 } 147} 148 149function launch_task(task) { 150 if (task.type == 'window' && task.tabs) { 151 chrome.windows.create({'url': '/focus.html'}, function (win) { 152 close_preexisting_windows(); 153 chrome.tabs.getSelected(win.id, function(tab) { 154 for (var i = 1; i < task.tabs.length; i++) { 155 chrome.tabs.create({'windowId':win.id, 'url': '/focus.html'}); 156 } 157 chrome.tabs.getAllInWindow(win.id, function(tabs) { 158 for (var i = 0; i < tabs.length; i++) { 159 tab = tabs[i]; 160 url = task.tabs[i]; 161 start = Date.now(); 162 page_timestamps_new_record(tab.id, url, start); 163 chrome.tabs.update(tab.id, {'url': url, 'selected': true}); 164 } 165 console.log(JSON.stringify(page_timestamps_recorder)); 166 }); 167 setTimeout(function(win_id) { 168 record_end_browse_time_for_window(win_id); 169 chrome.windows.remove(win_id); 170 }, task.duration / time_ratio, win.id); 171 }); 172 }); 173 } else if (task.type == 'cycle' && task.urls) { 174 chrome.windows.create({'url': '/focus.html'}, function (win) { 175 close_preexisting_windows(); 176 chrome.tabs.getSelected(win.id, function(tab) { 177 var cycle = { 178 'timeout': task.timeout, 179 'name': task.name, 180 'delay': task.delay, 181 'urls': task.urls, 182 'id': tab.id, 183 'idx': 0, 184 'timer': null, 185 'focus': !!task.focus, 186 'successful_loads': 0, 187 'failed_loads': 0 188 }; 189 cycles[task.name] = cycle; 190 cycle_navigate(cycle); 191 setTimeout(function(cycle, win_id) { 192 clearTimeout(cycle.timer); 193 record_end_browse_time_for_window(win_id); 194 chrome.windows.remove(win_id); 195 }, task.duration / time_ratio, cycle, win.id); 196 }); 197 }); 198 } 199} 200 201function page_timestamps_new_record(tab_id, url, start) { 202 // sanitize url, make http(s)://www.abc.com/d/e/f into www.abc.com 203 sanitized_url = url.replace(/https?:\/\//, '').split('/')[0]; 204 page_timestamps_recorder[tab_id] = { 205 'url': sanitized_url, 206 'start_time': start, 207 'end_load_time': null, 208 'end_browse_time': null 209 } 210} 211 212function record_end_browse_time_for_window(win_id) { 213 chrome.tabs.getAllInWindow(win_id, function(tabs) { 214 end = Date.now(); 215 console.log("page_timestamps_recorder:"); 216 console.log(JSON.stringify(page_timestamps_recorder)); 217 tabs.forEach(function (tab) { 218 if (tab.id in page_timestamps_recorder) { 219 page = page_timestamps_recorder[tab.id]; 220 page['end_browse_time'] = end; 221 page_timestamps.push(page); 222 } 223 }); 224 console.log(JSON.stringify("page_timestamps:")); 225 console.log(JSON.stringify(page_timestamps)); 226 }); 227} 228 229function record_log_entry(entry) { 230 log_lines.push(entry); 231} 232 233function send_log_entries() { 234 var post = []; 235 log_lines.forEach(function (item, index, array) { 236 var entry = encodeURIComponent(item); 237 post.push('url'+ index + '=' + entry); 238 }); 239 240 var log_url = 'http://localhost:8001/log'; 241 // TODO(coconutruben): code-snippet below is shared 242 // across record_log_entry and send_keyvals. Consider 243 // pull into helper if we use more urls. 244 var req = new XMLHttpRequest(); 245 req.open('POST', log_url, true); 246 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 247 req.send(post.join("&")); 248 console.log(post.join("&")); 249} 250 251function send_keyvals() { 252 var post = ["status=good"]; 253 254 for (var name in cycles) { 255 var cycle = cycles[name]; 256 post.push(name + "_successful_loads=" + cycle.successful_loads); 257 post.push(name + "_failed_loads=" + cycle.failed_loads); 258 } 259 260 chrome.runtime.onMessage.removeListener(testListener); 261 262 var status_url = 'http://localhost:8001/status'; 263 var req = new XMLHttpRequest(); 264 req.open('POST', status_url, true); 265 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 266 req.send(post.join("&")); 267 console.log(post.join("&")); 268} 269 270function send_raw_page_time_info() { 271 var post = []; 272 page_timestamps.forEach(function (item) { 273 var unique_url = (unique_url_salt++) + item.url; 274 var key = encodeURIComponent(unique_url); 275 post.push(key + "=" + JSON.stringify(item)); 276 }) 277 278 var pagetime_info_url = 'http://localhost:8001/pagetime'; 279 var req = new XMLHttpRequest(); 280 req.open('POST', pagetime_info_url, true); 281 req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded"); 282 req.send(post.join("&")); 283 console.log(post.join("&")); 284} 285 286function send_summary() { 287 send_log_entries(); 288 send_raw_page_time_info(); 289 send_keyvals(); 290} 291 292function startTest() { 293 time_ratio = 3600 * 1000 / test_time_ms; // default test time is 1 hour 294 chrome.runtime.onMessage.addListener(testListener); 295 setTimeout(setupTest, 1000); 296} 297 298function initialize() { 299 // Called when the user clicks on the browser action. 300 chrome.browserAction.onClicked.addListener(function(tab) { 301 // Start the test with default settings. 302 chrome.runtime.onMessage.addListener(testListener); 303 setTimeout(setupTest, 1000); 304 }); 305} 306 307window.addEventListener("load", initialize); 308