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