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 doModifyRequestHeaders(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
;
107 function doModifyResponseHeaders(details
) {
110 headers
= details
.responseHeaders
;
111 if (headers
=== undefined) {
114 headers
= headers
.filter(
115 function(x
) {return x
['name'] != 'Cache-Control'});
116 headers
.push({'name': 'X-Test-Response-Header',
117 'value': 'Inserted'});
118 headers
.push({'name': 'Set-Cookie',
119 'value': 'ActivityLog=InsertedCookie'});
120 response
['responseHeaders'] = headers
;
124 chrome
.webRequest
.onBeforeSendHeaders
.addListener(
125 doModifyRequestHeaders
,
126 {'urls': ['http://*/*'], 'types': ['main_frame']},
127 ['blocking', 'requestHeaders']);
128 chrome
.webRequest
.onHeadersReceived
.addListener(
129 doModifyResponseHeaders
,
130 {'urls': ['http://*/*'], 'types': ['main_frame']},
131 ['blocking', 'responseHeaders']);
133 // Open a tab, then close it when it has finished loading--this should give
134 // the webRequest handler a chance to run.
135 chrome
.tabs
.onUpdated
.addListener(
136 function closeTab(tabId
, changeInfo
, tab
) {
137 if (changeInfo
['status'] === 'complete' &&
138 tab
.url
.match(/google\.com/g)) {
139 chrome
.webRequest
.onBeforeSendHeaders
.removeListener(
140 doModifyRequestHeaders
);
141 chrome
.webRequest
.onHeadersReceived
.removeListener(
142 doModifyResponseHeaders
);
143 chrome
.tabs
.onUpdated
.removeListener(closeTab
);
144 chrome
.tabs
.remove(tabId
);
145 appendCompleted('doWebRequestModifications');
152 function sendMessageToSelf() {
155 chrome
.runtime
.sendMessage('hello hello');
156 appendCompleted('sendMessageToSelf');
158 setError(err
+ ' in function: sendMessageToSelf');
162 function sendMessageToOther() {
165 chrome
.runtime
.sendMessage('ocacnieaapoflmkebkeaidpgfngocapl',
167 function response() {
168 console
.log("who's there?");
169 appendCompleted('sendMessageToOther');
172 setError(err
+ ' in function: sendMessageToOther');
176 function connectToOther() {
179 chrome
.runtime
.connect('ocacnieaapoflmkebkeaidpgfngocapl');
180 appendCompleted('connectToOther');
182 setError(err
+ ' in function:connectToOther');
186 function tabIdTranslation() {
188 var tabIds
= [-1, -1];
190 // Test the case of a single int
191 chrome
.tabs
.onUpdated
.addListener(
192 function testSingleInt(tabId
, changeInfo
, tab
) {
193 if (changeInfo
['status'] === 'complete' &&
194 tab
.url
.match(/google\.com/g)) {
195 chrome
.tabs
.executeScript(
197 {'file': 'google_cs.js'},
199 chrome
.tabs
.onUpdated
.removeListener(testSingleInt
);
201 openTab('http://www.google.be');
207 // Test the case of arrays
208 chrome
.tabs
.onUpdated
.addListener(
209 function testArray(tabId
, changeInfo
, tab
) {
210 if (changeInfo
['status'] === 'complete' && tab
.url
.match(/google\.be/g)) {
211 chrome
.tabs
.move(tabId
, {'index': -1});
213 chrome
.tabs
.onUpdated
.removeListener(testArray
);
214 chrome
.tabs
.remove(tabIds
);
215 appendCompleted('tabIdTranslation');
223 function executeApiCallsOnTabUpdated() {
225 chrome
.tabs
.onUpdated
.addListener(
226 function callback(tabId
, changeInfo
, tab
) {
227 if (changeInfo
['status'] === 'complete' &&
228 tab
.url
.match(/google\.com/g)) {
229 chrome
.tabs
.onUpdated
.removeListener(callback
);
232 chrome
.tabs
.sendMessage(tabId
, 'hellooooo!');
233 appendCompleted('sendMessageToCS');
235 // Inject a content script
236 chrome
.tabs
.executeScript(
238 {'file': 'google_cs.js'},
240 appendCompleted('injectContentScript');
243 // Injects a blob of script into a page and cleans up the tab when
245 chrome
.tabs
.executeScript(
247 {'code': 'document.write("g o o g l e");'},
249 appendCompleted('injectScriptBlob');
250 chrome
.tabs
.remove(tabId
);
259 // DOM API TEST METHODS -- PUT YOUR TESTS BELOW HERE
260 ////////////////////////////////////////////////////////////////////////////////
262 // Does an XHR from this [privileged] context.
263 function doBackgroundXHR() {
265 var request
= new XMLHttpRequest();
266 request
.open('POST', defaultUrl
, false);
267 request
.setRequestHeader('Content-type', 'text/plain;charset=UTF-8');
271 // doesn't matter if it works or not; should be recorded either way
273 appendCompleted('doBackgroundXHR');
276 function executeDOMChangesOnTabUpdated() {
280 // Accesses the Location object from inside a content script.
281 code
= 'window.location = "http://www.google.com/#foo"; ' +
282 'document.location = "http://www.google.com/#bar"; ' +
283 'var loc = window.location; ' +
284 'loc.assign("http://www.google.com/#fo"); ' +
285 'loc.replace("http://www.google.com/#bar");';
287 // Mutates the DOM tree from inside a content script.
288 code
+= 'var d1 = document.createElement("div"); ' +
289 'var d2 = document.createElement("div"); ' +
290 'document.body.appendChild(d1); ' +
291 'document.body.insertBefore(d2, d1); ' +
292 'document.body.replaceChild(d1, d2);';
294 code
+= 'document.write("Hello using document.write"); ' +
295 'document.writeln("Hello using document.writeln"); ' +
296 'document.body.innerHTML = "Hello using innerHTML";';
298 // Accesses the HTML5 Navigator API from inside a content script.
299 code
+= 'var geo = navigator.geolocation; ' +
300 'var successCallback = function(x) { }; ' +
301 'var errorCallback = function(x) { }; ' +
302 'geo.getCurrentPosition(successCallback, errorCallback); ' +
303 'var id = geo.watchPosition(successCallback, errorCallback);';
305 // Accesses the HTML5 WebStorage API from inside a content script.
306 code
+= 'var store = window.sessionStorage; ' +
307 'store.setItem("foo", 42); ' +
308 'var val = store.getItem("foo"); ' +
309 'store.removeItem("foo"); ' +
312 // Same but for localStorage.
313 code
+= 'var store = window.localStorage; ' +
314 'store.setItem("foo", 42); ' +
315 'var val = store.getItem("foo"); ' +
316 'store.removeItem("foo"); ' +
319 // Accesses the HTML5 ApplicationCache API from inside a content script.
320 code
+= 'var appCache = window.applicationCache;';
322 // Accesses the HTML5 WebDatabase API from inside a content script.
323 code
+= 'var db = openDatabase("testdb", "1.0", "test database", ' +
326 // Accesses the HTML5 Canvas API from inside a content script.
327 code
+= 'var testCanvas = document.createElement("canvas"); ' +
328 'var testContext = testCanvas.getContext("2d");';
330 // Does an XHR from inside a content script.
331 var cnnUrl
= getURLWithPort('http://www.cnn.com');
332 code
+= 'var request = new XMLHttpRequest(); ' +
333 'request.open("POST", "' + cnnUrl
+ '", false); ' +
334 'request.setRequestHeader("Content-type", ' +
335 ' "text/plain;charset=UTF-8"); ' +
337 'document.write("sent an XHR");';
339 // This function is used as a handler for hooking mouse and keyboard events.
340 code
+= 'function handlerHook(event) { };';
342 hookNames
= ['onclick', 'ondblclick', 'ondrag', 'ondragend', 'ondragenter',
343 'ondragleave', 'ondragover', 'ondragstart', 'ondrop', 'oninput',
344 'onkeydown', 'onkeypress', 'onkeyup', 'onmousedown',
345 'onmouseenter', 'onmouseleave', 'onmousemove', 'onmouseout',
346 'onmouseover', 'onmouseup', 'onmousewheel'];
348 // Access to each hook can be monitored for Element, Document, and Window.
349 for (var i
= 0; i
< hookNames
.length
; i
++) {
350 // handler on Element
351 code
+= 'document.body.' + hookNames
[i
] + ' = handlerHook;';
353 // handler on a Document
354 code
+= 'document.' + hookNames
[i
] + ' = handlerHook;';
356 // handler on a Window
357 code
+= 'window.' + hookNames
[i
] + ' = handlerHook;';
360 chrome
.tabs
.onUpdated
.addListener(
361 function callback(tabId
, changeInfo
, tab
) {
362 if (changeInfo
['status'] === 'complete' &&
363 tab
.url
.match(/google\.com/g)) {
364 chrome
.tabs
.onUpdated
.removeListener(callback
);
365 chrome
.tabs
.executeScript(
366 tabId
, {'code': code
},
368 chrome
.tabs
.remove(tabId
);
369 appendCompleted('executeDOMChangesOnTabUpdated');
377 function executeDOMFullscreen() {
379 appendCompleted('Switching to fullscreen...');
380 $('status').webkitRequestFullscreen();
382 function() {document
.webkitExitFullscreen(); window
.close()}, 100);
385 // Opens the extensions options page and then runs the executeDOMFullscreen
387 function launchDOMFullscreenTest() {
388 openTab(chrome
.extension
.getURL('/options.html#dom_fullscreen'));
391 // ADD TESTS CASES TO THE MAP HERE.
393 fnMap
['api_call'] = makeApiCall
;
394 fnMap
['special_call'] = makeSpecialApiCalls
;
395 fnMap
['double'] = checkNoDoubleLogging
;
396 fnMap
['app_bindings'] = checkAppCalls
;
397 fnMap
['object_methods'] = callObjectMethod
;
398 fnMap
['message_self'] = sendMessageToSelf
;
399 fnMap
['message_other'] = sendMessageToOther
;
400 fnMap
['connect_other'] = connectToOther
;
401 fnMap
['background_xhr'] = doBackgroundXHR
;
402 fnMap
['webrequest'] = doWebRequestModifications
;
403 fnMap
['tab_ids'] = tabIdTranslation
;
404 fnMap
['dom_tab_updated'] = executeDOMChangesOnTabUpdated
;
405 fnMap
['api_tab_updated'] = executeApiCallsOnTabUpdated
;
406 fnMap
['dom_fullscreen'] = executeDOMFullscreen
;
407 fnMap
['launch_dom_fullscreen'] = launchDOMFullscreenTest
;
409 // Setup function mapping for the automated tests, except when running on the
411 if (window
.location
.pathname
!== '/options.html') {
413 chrome
.runtime
.onMessageExternal
.addListener(
414 function(message
, sender
, response
) {
415 useIncognito
= false;
416 if (message
.match(/_incognito$/)) {
417 // Enable incognito windows for this test, then strip the
418 // _incognito suffix for the lookup below.
420 message
= message
.slice(0, -10);
422 if (fnMap
.hasOwnProperty(message
)) {
425 console
.log('UNKNOWN METHOD: ' + message
);
430 console
.log('Error while adding listeners: ' + err
);
433 console
.log('Not installing extension message listener on options.html');
436 // Convenience functions for the manual run mode.
438 return document
.getElementById(o
);
442 function resetStatus(str
) {
444 if ($('status') != null) {
445 $('status').innerText
= '';
449 function appendCompleted(str
) {
450 if ($('status') != null) {
452 $('status').innerText
+= ', ' + str
;
454 $('status').innerText
= 'Completed: ' + str
;
458 console
.log('Completed ' + str
);
461 function appendError(str
) {
462 if ($('status') != null) {
463 $('status').innerText
+= 'Error: ' + str
;
467 // Set up the event listeners for use in manual run mode. Then, if the URL
468 // contains a test name in the URL fragment
469 // (chrome-extension://pkn.../options.html#dom_fullscreen), launch that test
471 function setupEvents() {
472 for (var key
in fnMap
) {
473 if (fnMap
.hasOwnProperty(key
) && key
!= '' && $(key
) != null) {
474 $(key
).addEventListener('click', fnMap
[key
]);
477 if ($('incognito_checkbox') != null) {
478 $('incognito_checkbox').addEventListener(
480 function() { useIncognito
= $('incognito_checkbox').checked
; });
483 appendCompleted('setup events');
485 // Automatically launch a requested test if specified in the URL.
486 if (window
.location
.hash
) {
487 var requestedTest
= window
.location
.hash
.substr(1);
488 if (fnMap
.hasOwnProperty(requestedTest
)) {
489 fnMap
[requestedTest
]();
493 document
.addEventListener('DOMContentLoaded', setupEvents
);