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.
7 // We are going to kill all of the builtins, so hold onto the ones we need.
8 var defineGetter
= Object
.prototype.__defineGetter__
;
9 var defineSetter
= Object
.prototype.__defineSetter__
;
10 var Error
= window
.Error
;
11 var forEach
= Array
.prototype.forEach
;
12 var push
= Array
.prototype.push
;
13 var hasOwnProperty
= Object
.prototype.hasOwnProperty
;
14 var getOwnPropertyNames
= Object
.getOwnPropertyNames
;
15 var stringify
= JSON
.stringify
;
17 // Kill all of the builtins functions to give us a fairly high confidence that
18 // the environment our bindings run in can't interfere with our code.
19 // These are taken from the ECMAScript spec.
21 Object
, Function
, Array
, String
, Boolean
, Number
, Math
, Date
, RegExp
, JSON
,
24 function clobber(obj
, name
, qualifiedName
) {
25 // Clobbering constructors would break everything.
26 // Clobbering toString is annoying.
27 // Clobbering __proto__ breaks in ways that grep can't find.
28 // Clobbering function name will break because
29 // SafeBuiltins does not support getters yet. See crbug.com/463526.
30 // Clobbering Function.call would make it impossible to implement these tests.
31 // Clobbering Object.valueOf breaks v8.
32 // Clobbering %FunctionPrototype%.caller and .arguments will break because
33 // these properties are poisoned accessors in ES6.
34 if (name
== 'constructor' ||
36 name
== '__proto__' ||
37 name
== 'name' && typeof obj
== 'function' ||
38 qualifiedName
== 'Function.call' ||
39 (obj
!== Function
&& qualifiedName
== 'Function.caller') ||
40 (obj
!== Function
&& qualifiedName
== 'Function.arguments') ||
41 qualifiedName
== 'Object.valueOf') {
44 if (typeof obj
[name
] == 'function') {
45 obj
[name
] = function() {
46 throw new Error('Clobbered ' + qualifiedName
+ ' function');
49 defineGetter
.call(obj
, name
, function() {
50 throw new Error('Clobbered ' + qualifiedName
+ ' getter');
55 forEach
.call(builtinTypes
, function(builtin
) {
56 var prototype = builtin
.prototype;
57 var typename
= '<unknown>';
59 typename
= prototype.constructor.name
;
60 forEach
.call(getOwnPropertyNames(prototype), function(name
) {
61 clobber(prototype, name
, typename
+ '.' + name
);
64 forEach
.call(getOwnPropertyNames(builtin
), function(name
) {
65 clobber(builtin
, name
, typename
+ '.' + name
);
68 clobber(window
, builtin
.name
, 'window.' + builtin
.name
);
71 // Codes for test results. Must match ExternallyConnectableMessagingTest::Result
72 // in c/b/extensions/extension_messages_apitest.cc.
75 NAMESPACE_NOT_DEFINED
: 1,
76 FUNCTION_NOT_DEFINED
: 2,
77 COULD_NOT_ESTABLISH_CONNECTION_ERROR
: 3,
79 INCORRECT_RESPONSE_SENDER
: 5,
80 INCORRECT_RESPONSE_MESSAGE
: 6,
83 // Make the messages sent vaguely complex, but unambiguously JSON-ifiable.
84 var kMessage
= [{'a': {'b': 10}}, 20, 'c\x10\x11'];
86 // Our tab's location. Normally this would be our document's location but if
87 // we're an iframe it will be the location of the parent - in which case,
89 var tabLocationHref
= null;
91 if (parent
== window
) {
92 tabLocationHref
= document
.location
.href
;
94 window
.addEventListener('message', function listener(event
) {
95 window
.removeEventListener('message', listener
);
96 tabLocationHref
= event
.data
;
100 function checkLastError(reply
) {
101 if (!chrome
.runtime
.lastError
)
103 var kCouldNotEstablishConnection
=
104 'Could not establish connection. Receiving end does not exist.';
105 if (chrome
.runtime
.lastError
.message
== kCouldNotEstablishConnection
)
106 reply(results
.COULD_NOT_ESTABLISH_CONNECTION_ERROR
);
108 reply(results
.OTHER_ERROR
);
112 function checkResponse(response
, reply
, expectedMessage
, isApp
) {
113 // The response will be an echo of both the original message *and* the
114 // MessageSender (with the tab field stripped down).
116 // First check the sender was correct.
117 var incorrectSender
= false;
119 // Only extensions get access to a 'tab' property.
120 if (!hasOwnProperty
.call(response
.sender
, 'tab')) {
121 console
.warn('Expected a tab, got none');
122 incorrectSender
= true;
124 if (response
.sender
.tab
.url
!= tabLocationHref
) {
125 console
.warn('Expected tab url ' + tabLocationHref
+ ' got ' +
126 response
.sender
.tab
.url
);
127 incorrectSender
= true;
130 if (hasOwnProperty
.call(response
.sender
, 'id')) {
131 console
.warn('Expected no id, got "' + response
.sender
.id
+ '"');
132 incorrectSender
= true;
134 if (response
.sender
.url
!= document
.location
.href
) {
135 console
.warn('Expected url ' + document
.location
.href
+ ' got ' +
136 response
.sender
.url
);
137 incorrectSender
= true;
139 if (incorrectSender
) {
140 reply(results
.INCORRECT_RESPONSE_SENDER
);
144 // Check the correct content was echoed.
145 var expectedJson
= stringify(expectedMessage
);
146 var actualJson
= stringify(response
.message
);
147 if (actualJson
== expectedJson
)
149 console
.warn('Expected message ' + expectedJson
+ ' got ' + actualJson
);
150 reply(results
.INCORRECT_RESPONSE_MESSAGE
);
154 function sendToBrowser(msg
) {
155 domAutomationController
.send(msg
);
158 function sendToBrowserForTlsChannelId(result
) {
159 // Because the TLS channel ID tests read the TLS either an error code or the
160 // TLS channel ID string from the same value, they require the result code
161 // to be sent as a string.
162 // String() is clobbered, so coerce string creation with +.
163 sendToBrowser("" + result
);
166 function checkRuntime(reply
) {
168 reply
= sendToBrowser
;
170 if (!chrome
.runtime
) {
171 reply(results
.NAMESPACE_NOT_DEFINED
);
175 if (!chrome
.runtime
.connect
|| !chrome
.runtime
.sendMessage
) {
176 reply(results
.FUNCTION_NOT_DEFINED
);
182 function checkRuntimeForTlsChannelId() {
183 return checkRuntime(sendToBrowserForTlsChannelId
);
186 function checkTlsChannelIdResponse(response
) {
187 if (chrome
.runtime
.lastError
) {
188 if (chrome
.runtime
.lastError
.message
== kCouldNotEstablishConnection
)
189 sendToBrowserForTlsChannelId(
190 results
.COULD_NOT_ESTABLISH_CONNECTION_ERROR
);
192 sendToBrowserForTlsChannelId(results
.OTHER_ERROR
);
195 if (response
.sender
.tlsChannelId
!== undefined)
196 sendToBrowserForTlsChannelId(response
.sender
.tlsChannelId
);
198 sendToBrowserForTlsChannelId('');
202 appendIframe: function(src
) {
203 var iframe
= document
.createElement('iframe');
204 // When iframe has loaded, notify it of our tab location (probably
205 // document.location) to use in its assertions, then continue.
206 iframe
.addEventListener('load', function listener() {
207 iframe
.removeEventListener('load', listener
);
208 iframe
.contentWindow
.postMessage(tabLocationHref
, '*');
212 document
.body
.appendChild(iframe
);
216 window
.assertions
= {
217 canConnectAndSendMessages: function(extensionId
, isApp
, message
) {
224 function canSendMessage(reply
) {
225 chrome
.runtime
.sendMessage(extensionId
, message
, function(response
) {
226 if (checkLastError(reply
) &&
227 checkResponse(response
, reply
, message
, isApp
)) {
233 function canConnectAndSendMessages(reply
) {
234 var port
= chrome
.runtime
.connect(extensionId
);
235 port
.postMessage(message
, function() {
236 checkLastError(reply
);
238 port
.postMessage(message
, function() {
239 checkLastError(reply
);
241 var pendingResponses
= 2;
243 port
.onMessage
.addListener(function(response
) {
245 ok
= ok
&& checkLastError(reply
) &&
246 checkResponse(response
, reply
, message
, isApp
);
247 if (pendingResponses
== 0 && ok
)
252 canSendMessage(function(result
) {
253 if (result
!= results
.OK
)
254 sendToBrowser(result
);
256 canConnectAndSendMessages(sendToBrowser
);
260 trySendMessage: function(extensionId
) {
261 chrome
.runtime
.sendMessage(extensionId
, kMessage
, function(response
) {
262 // The result is unimportant. All that matters is the attempt.
266 tryIllegalArguments: function() {
267 // Tests that illegal arguments to messaging functions throw exceptions.
268 // Regression test for crbug.com/472700, where they crashed the renderer.
269 function runIllegalFunction(fun
) {
275 console
.error('Function did not throw exception: ' + fun
);
276 sendToBrowser(false);
280 runIllegalFunction(chrome
.runtime
.connect
) &&
281 runIllegalFunction(function() {
282 chrome
.runtime
.connect('');
284 runIllegalFunction(function() {
285 chrome
.runtime
.connect(42);
287 runIllegalFunction(function() {
288 chrome
.runtime
.connect('', 42);
290 runIllegalFunction(function() {
291 chrome
.runtime
.connect({name
: 'noname'});
293 runIllegalFunction(chrome
.runtime
.sendMessage
) &&
294 runIllegalFunction(function() {
295 chrome
.runtime
.sendMessage('');
297 runIllegalFunction(function() {
298 chrome
.runtime
.sendMessage(42);
300 runIllegalFunction(function() {
301 chrome
.runtime
.sendMessage('', 42);
306 areAnyRuntimePropertiesDefined: function(names
) {
308 if (chrome
.runtime
) {
309 forEach
.call(names
, function(name
) {
310 if (chrome
.runtime
[name
]) {
311 console
.log('runtime.' + name
+ ' is defined');
316 sendToBrowser(result
);
319 getTlsChannelIdFromPortConnect: function(extensionId
, includeTlsChannelId
,
321 if (!checkRuntimeForTlsChannelId())
327 var port
= chrome
.runtime
.connect(extensionId
,
328 {'includeTlsChannelId': includeTlsChannelId
});
329 port
.onMessage
.addListener(checkTlsChannelIdResponse
);
330 port
.postMessage(message
);
333 getTlsChannelIdFromSendMessage: function(extensionId
, includeTlsChannelId
,
335 if (!checkRuntimeForTlsChannelId())
341 chrome
.runtime
.sendMessage(extensionId
, message
,
342 {'includeTlsChannelId': includeTlsChannelId
},
343 checkTlsChannelIdResponse
);