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 defaultUrl = 'http://www.google.com';
7 // Utility function to open a URL in a new tab. If the useIncognito global is
8 // true, the URL is opened in a new incognito window, otherwise it is opened in
9 // a new tab in the current window. Alternatively, whether to use incognito
10 // can be specified as a second argument which overrides the global setting.
11 var useIncognito = false;
13 // For the automated tests a port needs to be specified for the URLs in order to
14 // contact the embedded test server. The manual tests do not use an embedded
15 // test server so the port will remain undefined for manual testing and no
16 // modifications will be made to the URLs.
17 var testServerPort = undefined;
19 chrome.test.getConfig(function(config) {
20 // config is undefined in manual mode, this check required to stop crashes in
22 if (config != undefined)
23 testServerPort = config.testServer.port;
26 function getURLWithPort(url) {
27 if (testServerPort == undefined || url.substring(0, 4) != 'http')
29 return url + ':' + testServerPort;
32 function openTab(url, incognito) {
33 var testUrl = getURLWithPort(url);
34 if (incognito == undefined ? useIncognito : incognito) {
35 chrome.windows.create({'url': testUrl, 'incognito': true});
41 // CHROME API TEST METHODS -- PUT YOUR TESTS BELOW HERE
42 ////////////////////////////////////////////////////////////////////////////////
45 function makeApiCall() {
48 'url': 'https://www.cnn.com',
49 'name': 'activity_log_test_cookie',
52 appendCompleted('makeApiCall');
55 // Makes an API call that has a custom binding.
56 function makeSpecialApiCalls() {
58 var url = chrome.extension.getURL('image/cat.jpg');
59 var noparam = chrome.extension.getViews();
60 appendCompleted('makeSpecialApiCalls');
63 // Checks that we don't double-log calls that go through setHandleRequest
64 // *and* the ExtensionFunction machinery.
65 function checkNoDoubleLogging() {
67 chrome.omnibox.setDefaultSuggestion({description: 'hello world'});
68 appendCompleted('checkNoDoubleLogging');
71 // Check whether we log calls to chrome.app.*;
72 function checkAppCalls() {
74 var callback = function() {};
75 chrome.app.getDetails();
76 var b = chrome.app.isInstalled;
77 var c = chrome.app.installState(callback);
78 appendCompleted('checkAppCalls');
81 function callObjectMethod() {
83 var storageArea = chrome.storage.sync;
85 appendCompleted('callObjectMethod');
88 // Modifies the headers sent and received in an HTTP request using the
90 function doWebRequestModifications() {
92 // Install a webRequest handler that will add an HTTP header to the outgoing
93 // request for the main page.
94 function doModifyHeaders(details) {
97 var headers = details.requestHeaders;
98 if (headers === undefined) {
101 headers.push({'name': 'X-Test-Activity-Log-Send',
102 'value': 'Present'});
103 response['requestHeaders'] = headers;
105 headers = details.responseHeaders;
106 if (headers === undefined) {
109 headers = headers.filter(
110 function(x) {return x['name'] != 'Cache-Control'});
111 headers.push({'name': 'X-Test-Response-Header',
112 'value': 'Inserted'});
113 headers.push({'name': 'Set-Cookie',
114 'value': 'ActivityLog=InsertedCookie'});
115 response['responseHeaders'] = headers;
119 chrome.webRequest.onBeforeSendHeaders.addListener(
121 {'urls': ['http://*/*'], 'types': ['main_frame']},
122 ['blocking', 'requestHeaders']);
123 chrome.webRequest.onHeadersReceived.addListener(
125 {'urls': ['http://*/*'], 'types': ['main_frame']},
126 ['blocking', 'responseHeaders']);
128 // Open a tab, then close it when it has finished loading--this should give
129 // the webRequest handler a chance to run.
130 chrome.tabs.onUpdated.addListener(
131 function closeTab(tabId, changeInfo, tab) {
132 if (changeInfo['status'] === 'complete' &&
133 tab.url.match(/google\.com/g)) {
134 chrome.webRequest.onBeforeSendHeaders.removeListener(doModifyHeaders);
135 chrome.webRequest.onHeadersReceived.removeListener(doModifyHeaders);
136 chrome.tabs.onUpdated.removeListener(closeTab);
137 chrome.tabs.remove(tabId);
138 appendCompleted('doWebRequestModifications');
145 function sendMessageToSelf() {
148 chrome.runtime.sendMessage('hello hello');
149 appendCompleted('sendMessageToSelf');
151 setError(err + ' in function: sendMessageToSelf');
155 function sendMessageToOther() {
158 chrome.runtime.sendMessage('ocacnieaapoflmkebkeaidpgfngocapl',
160 function response() {
161 console.log("who's there?");
162 appendCompleted('sendMessageToOther');
165 setError(err + ' in function: sendMessageToOther');
169 function connectToOther() {
172 chrome.runtime.connect('ocacnieaapoflmkebkeaidpgfngocapl');
173 appendCompleted('connectToOther');
175 setError(err + ' in function:connectToOther');
179 function tabIdTranslation() {
181 var tabIds = [-1, -1];
183 // Test the case of a single int
184 chrome.tabs.onUpdated.addListener(
185 function testSingleInt(tabId, changeInfo, tab) {
186 if (changeInfo['status'] === 'complete' &&
187 tab.url.match(/google\.com/g)) {
188 chrome.tabs.executeScript(
190 {'file': 'google_cs.js'},
192 chrome.tabs.onUpdated.removeListener(testSingleInt);
194 openTab('http://www.google.be');
200 // Test the case of arrays
201 chrome.tabs.onUpdated.addListener(
202 function testArray(tabId, changeInfo, tab) {
203 if (changeInfo['status'] === 'complete' && tab.url.match(/google\.be/g)) {
204 chrome.tabs.move(tabId, {'index': -1});
206 chrome.tabs.onUpdated.removeListener(testArray);
207 chrome.tabs.remove(tabIds);
208 appendCompleted('tabIdTranslation');
216 function executeApiCallsOnTabUpdated() {
218 chrome.tabs.onUpdated.addListener(
219 function callback(tabId, changeInfo, tab) {
220 if (changeInfo['status'] === 'complete' &&
221 tab.url.match(/google\.com/g)) {
222 chrome.tabs.onUpdated.removeListener(callback);
225 chrome.tabs.sendMessage(tabId, 'hellooooo!');
226 appendCompleted('sendMessageToCS');
228 // Inject a content script
229 chrome.tabs.executeScript(
231 {'file': 'google_cs.js'},
233 appendCompleted('injectContentScript');
236 // Injects a blob of script into a page and cleans up the tab when
238 chrome.tabs.executeScript(
240 {'code': 'document.write("g o o g l e");'},
242 appendCompleted('injectScriptBlob');
243 chrome.tabs.remove(tabId);
252 // DOM API TEST METHODS -- PUT YOUR TESTS BELOW HERE
253 ////////////////////////////////////////////////////////////////////////////////
255 // Does an XHR from this [privileged] context.
256 function doBackgroundXHR() {
258 var request = new XMLHttpRequest();
259 request.open('POST', defaultUrl, false);
260 request.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
264 // doesn't matter if it works or not; should be recorded either way
266 appendCompleted('doBackgroundXHR');
269 function executeDOMChangesOnTabUpdated() {
273 // Accesses the Location object from inside a content script.
274 code = 'window.location = "http://www.google.com/#foo"; ' +
275 'document.location = "http://www.google.com/#bar"; ' +
276 'var loc = window.location; ' +
277 'loc.assign("http://www.google.com/#fo"); ' +
278 'loc.replace("http://www.google.com/#bar");';
280 // Mutates the DOM tree from inside a content script.
281 code += 'var d1 = document.createElement("div"); ' +
282 'var d2 = document.createElement("div"); ' +
283 'document.body.appendChild(d1); ' +
284 'document.body.insertBefore(d2, d1); ' +
285 'document.body.replaceChild(d1, d2);';
287 code += 'document.write("Hello using document.write"); ' +
288 'document.writeln("Hello using document.writeln"); ' +
289 'document.body.innerHTML = "Hello using innerHTML";';
291 // Accesses the HTML5 Navigator API from inside a content script.
292 code += 'var geo = navigator.geolocation; ' +
293 'var successCallback = function(x) { }; ' +
294 'var errorCallback = function(x) { }; ' +
295 'geo.getCurrentPosition(successCallback, errorCallback); ' +
296 'var id = geo.watchPosition(successCallback, errorCallback);';
298 // Accesses the HTML5 WebStorage API from inside a content script.
299 code += 'var store = window.sessionStorage; ' +
300 'store.setItem("foo", 42); ' +
301 'var val = store.getItem("foo"); ' +
302 'store.removeItem("foo"); ' +
305 // Same but for localStorage.
306 code += 'var store = window.localStorage; ' +
307 'store.setItem("foo", 42); ' +
308 'var val = store.getItem("foo"); ' +
309 'store.removeItem("foo"); ' +
312 // Accesses the HTML5 ApplicationCache API from inside a content script.
313 code += 'var appCache = window.applicationCache;';
315 // Accesses the HTML5 WebDatabase API from inside a content script.
316 code += 'var db = openDatabase("testdb", "1.0", "test database", ' +
319 // Accesses the HTML5 Canvas API from inside a content script.
320 code += 'var testCanvas = document.createElement("canvas"); ' +
321 'var testContext = testCanvas.getContext("2d");';
323 // Does an XHR from inside a content script.
324 var cnnUrl = getURLWithPort('http://www.cnn.com');
325 code += 'var request = new XMLHttpRequest(); ' +
326 'request.open("POST", "' + cnnUrl + '", false); ' +
327 'request.setRequestHeader("Content-type", ' +
328 ' "text/plain;charset=UTF-8"); ' +
330 'document.write("sent an XHR");';
332 // This function is used as a handler for hooking mouse and keyboard events.
333 code += 'function handlerHook(event) { };';
335 hookNames = ['onclick', 'ondblclick', 'ondrag', 'ondragend', 'ondragenter',
336 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'oninput',
337 'onkeydown', 'onkeypress', 'onkeyup', 'onmousedown',
338 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout',
339 'onmouseover', 'onmouseup', 'onmousewheel'];
341 // Access to each hook can be monitored for Element, Document, and Window.
342 for (var i = 0; i < hookNames.length; i++) {
343 // handler on Element
344 code += 'document.body.' + hookNames[i] + ' = handlerHook;';
346 // handler on a Document
347 code += 'document.' + hookNames[i] + ' = handlerHook;';
349 // handler on a Window
350 code += 'window.' + hookNames[i] + ' = handlerHook;';
353 chrome.tabs.onUpdated.addListener(
354 function callback(tabId, changeInfo, tab) {
355 if (changeInfo['status'] === 'complete' &&
356 tab.url.match(/google\.com/g)) {
357 chrome.tabs.onUpdated.removeListener(callback);
358 chrome.tabs.executeScript(
359 tabId, {'code': code},
361 chrome.tabs.remove(tabId);
362 appendCompleted('executeDOMChangesOnTabUpdated');
370 function executeDOMFullscreen() {
372 appendCompleted('Switching to fullscreen...');
373 $('status').webkitRequestFullscreen();
375 function() {document.webkitExitFullscreen(); window.close()}, 100);
378 // Opens the extensions options page and then runs the executeDOMFullscreen
380 function launchDOMFullscreenTest() {
381 openTab(chrome.extension.getURL('/options.html#dom_fullscreen'));
384 // ADD TESTS CASES TO THE MAP HERE.
386 fnMap['api_call'] = makeApiCall;
387 fnMap['special_call'] = makeSpecialApiCalls;
388 fnMap['double'] = checkNoDoubleLogging;
389 fnMap['app_bindings'] = checkAppCalls;
390 fnMap['object_methods'] = callObjectMethod;
391 fnMap['message_self'] = sendMessageToSelf;
392 fnMap['message_other'] = sendMessageToOther;
393 fnMap['connect_other'] = connectToOther;
394 fnMap['background_xhr'] = doBackgroundXHR;
395 fnMap['webrequest'] = doWebRequestModifications;
396 fnMap['tab_ids'] = tabIdTranslation;
397 fnMap['dom_tab_updated'] = executeDOMChangesOnTabUpdated;
398 fnMap['api_tab_updated'] = executeApiCallsOnTabUpdated;
399 fnMap['dom_fullscreen'] = executeDOMFullscreen;
400 fnMap['launch_dom_fullscreen'] = launchDOMFullscreenTest;
402 // Setup function mapping for the automated tests, except when running on the
404 if (window.location.pathname !== '/options.html') {
406 chrome.runtime.onMessageExternal.addListener(
407 function(message, sender, response) {
408 useIncognito = false;
409 if (message.match(/_incognito$/)) {
410 // Enable incognito windows for this test, then strip the
411 // _incognito suffix for the lookup below.
413 message = message.slice(0, -10);
415 if (fnMap.hasOwnProperty(message)) {
418 console.log('UNKNOWN METHOD: ' + message);
423 console.log('Error while adding listeners: ' + err);
426 console.log('Not installing extension message listener on options.html');
429 // Convenience functions for the manual run mode.
431 return document.getElementById(o);
435 function resetStatus(str) {
437 if ($('status') != null) {
438 $('status').innerText = '';
442 function appendCompleted(str) {
443 if ($('status') != null) {
445 $('status').innerText += ', ' + str;
447 $('status').innerText = 'Completed: ' + str;
451 console.log('Completed ' + str);
454 function appendError(str) {
455 if ($('status') != null) {
456 $('status').innerText += 'Error: ' + str;
460 // Set up the event listeners for use in manual run mode. Then, if the URL
461 // contains a test name in the URL fragment
462 // (chrome-extension://pkn.../options.html#dom_fullscreen), launch that test
464 function setupEvents() {
465 for (var key in fnMap) {
466 if (fnMap.hasOwnProperty(key) && key != '' && $(key) != null) {
467 $(key).addEventListener('click', fnMap[key]);
470 if ($('incognito_checkbox') != null) {
471 $('incognito_checkbox').addEventListener(
473 function() { useIncognito = $('incognito_checkbox').checked; });
476 appendCompleted('setup events');
478 // Automatically launch a requested test if specified in the URL.
479 if (window.location.hash) {
480 var requestedTest = window.location.hash.substr(1);
481 if (fnMap.hasOwnProperty(requestedTest)) {
482 fnMap[requestedTest]();
486 document.addEventListener('DOMContentLoaded', setupEvents);