selenium-webdriver 0.0.8 → 0.0.9

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,943 +1,975 @@
1
- /** @namespace */
2
- ChromeDriver = {};
3
-
4
-
5
- /**
6
- * Array of all information about currently loaded tabs (where a WebDriver
7
- * window is probably a tab)
8
- * Entries of form:
9
- * {Int tabId, String windowName, Port mainPort, Boolean isFrameset, FrameData[] frames}
10
- * FrameData ::= {[Int frameId], String frameName, Port framePort, FrameData[]}
11
- * frameId can be undefined, if it has not yet been looked up, but should be
12
- * added once it is known
13
- * @type {Array.<Object>} TODO(jmleyba): Type info
14
- */
15
- ChromeDriver.tabs = [];
16
-
17
-
18
- /**
19
- * Port to the currently active frame (or tab, if the current page is not a
20
- * frameset).
21
- * @type {?Port}
22
- */
23
- ChromeDriver.activePort = null;
24
-
25
-
26
- /**
27
- * ID of the currently active tab.
28
- * @type {?string}
29
- */
30
- ChromeDriver.activeTabId = null;
31
-
32
-
33
- /**
34
- * Whether we should switch to the next tab which opens. Should be set if the
35
- * last active tab was closed.
36
- * @type {boolean}
37
- */
38
- ChromeDriver.doFocusOnNextOpenedTab = true;
39
-
40
-
41
- /**
42
- * Place to temporarily store the URL currently being loaded, so that we can
43
- * retry if needed, because opening a URL is an async callback.
44
- * @type {?string}
45
- */
46
- ChromeDriver.urlBeingLoaded = null;
47
-
48
-
49
- /**
50
- * URL we believe we're currently on.
51
- * @type {?string}
52
- */
53
- ChromeDriver.currentUrl = null;
54
-
55
-
56
- /**
57
- * Whether we are loading a new URL that difers from the current URL only in
58
- * fragment.
59
- * @type {boolean}
60
- */
61
- ChromeDriver.isGettingUrlButOnlyChangingByFragment = false;
62
-
63
-
64
- /**
65
- * Whether we are currently executing a {@code ChromeDriver#close()}, and
66
- * accordingly should send a success response when the tab closes.
67
- * @type {boolean}
68
- */
69
- ChromeDriver.isClosingTab = false;
70
-
71
-
72
- /**
73
- * Whether we have sent a response to the {currently, most recently loading
74
- * page.
75
- * @type {boolean}
76
- */
77
- ChromeDriver.hasSentResponseToThisPageLoading = false;
78
-
79
-
80
- /**
81
- * Whether we believe a page is open to which we have no content script.
82
- * @type {boolean}
83
- */
84
- ChromeDriver.hasNoConnectionToPage = true;
85
-
86
-
87
- /**
88
- * Stores the remaining frames to traverse when switching frames.
89
- * @type {Array.<string>}
90
- */
91
- ChromeDriver.restOfCurrentFramePath = [];
92
-
93
-
94
- /**
95
- * Port to the frameset or main content page we currently have loaded, so that
96
- * we can probe it for information about its frames.
97
- * @type {?Port}
98
- */
99
- ChromeDriver.portToUseForFrameLookups = null;
100
-
101
-
102
- /**
103
- * The last request we sent that has not been answered, so that if we change
104
- * page between sending a request and receiving a response, we can re-send it to
105
- * the newly loaded page.
106
- * @type {*} TODO(jmleyba)
107
- */
108
- ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet = null;
109
-
110
-
111
- /**
112
- * Whether the plugin has the OS-specific window handle for the active tab. This
113
- * is called HWND rather than window handle to avoid confusion with the other
114
- * use of window handle to mean 'name of window'.
115
- * @type {boolean}
116
- */
117
- ChromeDriver.hasHwnd = false;
118
-
119
-
120
- /**
121
- * THe last XMLHttpRequest we made (used for communication with test language
122
- * bindings).
123
- * @type {?XMLHttpRequest}
124
- */
125
- ChromeDriver.xmlHttpRequest = null;
126
-
127
- /**
128
- * URL to ping for commands.
129
- * TODO(danielwh): Get this from the initial URL - see http://crbug.com/11547,
130
- * the ChromeDriverInternals wiki page.
131
- * There is a patch to fix this on the Downloads page of the Selenium project
132
- * @type {string}
133
- */
134
- ChromeDriver.xmlHttpRequestUrl = "http://127.0.0.1:9700/chromeCommandExecutor";
135
-
136
-
137
- /**
138
- * @type {number}
139
- */
140
- ChromeDriver.requestSequenceNumber = 0;
141
-
142
-
143
- /**
144
- * @type {number}
145
- */
146
- ChromeDriver.getUrlRequestSequenceNumber = 0;
147
-
148
-
149
- /**
150
- * Prefix prepended to the hopefully unique javascript window name, in hopes of
151
- * further removing conflict.
152
- * @type {string}
153
- */
154
- ChromeDriver.windowHandlePrefix = '__webdriver_chromedriver_windowhandle';
155
-
156
-
157
- /**
158
- * Whether we will not execute any commands because we are already executing
159
- * one.
160
- * @type {boolean}
161
- */
162
- ChromeDriver.isBlockedWaitingForResponse = false;
163
-
164
- /**
165
- * It's possible that the page has completed loading,
166
- * but the content script has not yet fired.
167
- * In this case, to not report that there is no page,
168
- * when we are just too fast, we wait up to this amount of time.
169
- * @type {number} unit: milliseconds
170
- */
171
- ChromeDriver.timeoutUntilGiveUpOnContentScriptLoading = 5000;
172
-
173
- /**
174
- * How long we are currently waiting for the content script to load
175
- * after loading the page
176
- * @type {number} unit: milliseconds
177
- */
178
- ChromeDriver.currentlyWaitingUntilGiveUpOnContentScriptLoading;
179
-
180
- //Set ChromeDriver.currentlyWaitingUntilGiveUpOnContentScriptLoading;
181
- resetCurrentlyWaitingOnContentScriptTime();
182
-
183
- /**
184
- * How long we wait between poling whether we have a content script,
185
- * when loading a new page, up until
186
- * ChromeDriver.timeoutUntilGiveUpOnContentScriptLoading
187
- * @type {number} unit: milliseconds
188
- */
189
- ChromeDriver.waitForContentScriptIncrement = 100;
190
-
191
- chrome.extension.onConnect.addListener(function(port) {
192
- console.log("Connected to " + port.name);
193
- // Note: The frameset port *always* connects before any frame port. After
194
- // that, the order is in page loading time
195
- ChromeDriver.hasNoConnectionToPage = false;
196
- var foundTab = false;
197
- for (var tab in ChromeDriver.tabs) {
198
- if (ChromeDriver.tabs[tab].tabId == port.tab.id) {
199
- //We must be a new [i]frame in the page, because when a page closes, it is
200
- // removed from ChromeDriver.tabs
201
- //TODO(danielwh): Work out WHICH page it's a sub-frame of (I don't look
202
- // forward to this)
203
- ChromeDriver.tabs[tab].frames.push({
204
- frameName: port.name,
205
- framePort: port,
206
- frames: []
207
- });
208
- //Loaded a frame. Pushed it to the array. We don't know which page it's
209
- // a sub-frame of, in the case of nested frames, if they have the same
210
- // names. It would be nice to think people didn't use frames, let alone
211
- // several layers of nesting of frames with the same name, but if it turns
212
- // out to be a problem... Well, we'll see.
213
- foundTab = true;
214
- break;
215
- }
216
- }
217
- if (!foundTab) {
218
- //New tab!
219
- //We don't know if it's a frameset yet, so we leave that as undefined
220
- ChromeDriver.tabs.push({
221
- tabId: port.tab.id,
222
- windowName: ChromeDriver.windowHandlePrefix + "_" + port.tab.id,
223
- mainPort: port,
224
- frames: []
225
- });
226
- }
227
-
228
- if (ChromeDriver.doFocusOnNextOpenedTab) {
229
- ChromeDriver.activePort = port;
230
- setActiveTabDetails(port.tab);
231
- //Re-parse the last request we sent if we didn't get a response,
232
- //because we ain't seeing a response any time soon
233
-
234
- if (ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet != null) {
235
- if (ChromeDriver.urlBeingLoaded != null) {
236
- ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet = null;
237
- } else {
238
- ChromeDriver.isBlockedWaitingForResponse = false;
239
- console.log("Re-trying request which was sent but not answered");
240
- parseRequest(ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet);
241
- }
242
- }
243
- }
244
-
245
- if (ChromeDriver.urlBeingLoaded != null) {
246
- //This was the result of a getUrl. Need to issue a response
247
- sendEmptyResponseWhenTabIsLoaded(port.tab);
248
- }
249
- port.onMessage.addListener(parsePortMessage);
250
- port.onDisconnect.addListener(function disconnectPort(port) {
251
- console.log("Disconnected from " + port.name);
252
- var remainingTabs = [];
253
- for (var tab in ChromeDriver.tabs) {
254
- if (ChromeDriver.tabs[tab].tabId == port.tab.id) {
255
- if (ChromeDriver.tabs[tab].mainPort == port) {
256
- //This main tab is being closed.
257
- //Don't include it in the new version of ChromeDriver.tabs.
258
- //Any subframes will also disconnect,
259
- //but their tabId won't be present in the array,
260
- //so they will be ignored.
261
- continue;
262
- } else {
263
- //This is a subFrame being ditched
264
- var remainingFrames = [];
265
- for (var frame in ChromeDriver.tabs[tab].frames) {
266
- if (ChromeDriver.tabs[tab].frames[frame].framePort == port) {
267
- continue;
268
- }
269
- remainingFrames.push(ChromeDriver.tabs[tab].frames[frame]);
270
- }
271
- ChromeDriver.tabs[tab].frames = remainingFrames;
272
- }
273
- }
274
- remainingTabs.push(ChromeDriver.tabs[tab]);
275
- }
276
- ChromeDriver.tabs = remainingTabs;
277
- if (ChromeDriver.tabs.length == 0 || ChromeDriver.activePort == null ||
278
- ChromeDriver.activePort.tab.id == port.tab.id) {
279
- //If it is the active tab, perhaps we have followed a link,
280
- //so we should focus on it.
281
- //We have nothing better to focus on, anyway.
282
- resetActiveTabDetails();
283
- }
284
- if (ChromeDriver.isClosingTab) {
285
- //We are actively closing the tab, and expect a response to this
286
- sendResponseToParsedRequest({statusCode: 0}, false)
287
- ChromeDriver.isClosingTab = false;
288
- }
289
- });
290
- });
291
-
292
- //Tell the ChromeCommandExecutor that we are here
293
- sendResponseByXHR({statusCode: 0}, false);
294
-
295
- /**
296
- * Sends the passed argument as the result of a command
297
- * @param result object encapsulating result to send
298
- * @param wait whether we expect this command to possibly make changes
299
- * we need to wait for (e.g. adding elements, opening windows) - if so,
300
- * we wait until we think these effects are done
301
- */
302
- function sendResponseByXHR(result, wait) {
303
- console.log("Sending result by XHR: " + JSON.stringify(result));
304
- if (ChromeDriver.xmlHttpRequest != null) {
305
- ChromeDriver.xmlHttpRequest.abort();
306
- }
307
- ChromeDriver.xmlHttpRequest = new XMLHttpRequest();
308
- ChromeDriver.xmlHttpRequest.onreadystatechange =
309
- handleXmlHttpRequestReadyStateChange;
310
- ChromeDriver.xmlHttpRequest.open(
311
- "POST", ChromeDriver.xmlHttpRequestUrl, true);
312
- ChromeDriver.xmlHttpRequest.setRequestHeader(
313
- "Content-type", "application/json");
314
- //Default to waiting for page changes, just in case
315
- //TODO(danielwh): Iterate over tabs checking their status
316
- if (wait === undefined || wait == null || wait) {
317
- setTimeout(sendResult, 600, [result]);
318
- } else {
319
- sendResult(result);
320
- }
321
- }
322
-
323
- /**
324
- * Actually sends the result by XHR
325
- * Should only EVER be called by sendResponseByXHR,
326
- * as it ignores things like setting up XHR and blocking,
327
- * and just forces the sending over an assumed open XHR
328
- * @param result String to send
329
- */
330
- function sendResult(result) {
331
- //TODO(danielwh): Iterate over tabs checking their status
332
- ChromeDriver.xmlHttpRequest.send(result + "\nEOResponse\n");
333
- console.log("Sent result by XHR: " + JSON.stringify(result));
334
- }
335
-
336
- /**
337
- * Sends the response to a request, which has been parsed by parseRequest
338
- * Should be used only from within parseRequest (or methods called from it),
339
- * because it adheres to the blocking semantics of parseRequest
340
- */
341
- function sendResponseToParsedRequest(toSend, wait) {
342
- if (!ChromeDriver.isBlockedWaitingForResponse) {
343
- console.log("Tried to send a response (" + toSend +
344
- ") when not waiting for one. Dropping response.");
345
- return;
346
- }
347
- ChromeDriver.isBlockedWaitingForResponse = false;
348
- ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet = null;
349
- console.log("SENDING RESPOND TO PARSED REQUEST");
350
- sendResponseByXHR(JSON.stringify(toSend), wait);
351
- setExtensionBusyIndicator(false);
352
- }
353
-
354
- /**
355
- * When we receive a request, dispatches parseRequest to execute it
356
- */
357
- function handleXmlHttpRequestReadyStateChange() {
358
- if (this.readyState == 4) {
359
- if (this.status != 200) {
360
- console.log("Request state was 4 but status: " + this.status +
361
- ". responseText: " + this.responseText);
362
- } else {
363
- console.log("GOT XHR RESPONSE: " + this.responseText);
364
- var request = JSON.parse(this.responseText);
365
- if (request.request == "quit") {
366
- //We're only allowed to send a response if we're blocked waiting for one, so pretend
367
- console.log("SENDING QUIT XHR");
368
- sendResponseByXHR(JSON.stringify({statusCode: 0}), false);
369
- } else {
370
- console.log("Got request to execute from XHR: " + this.responseText);
371
- parseRequest(request);
372
- }
373
- }
374
- }
375
- }
376
-
377
- /**
378
- * Parses a request received from the ChromeCommandExecutor and either sends the
379
- * response, or sends a message to the content script with a command to execute
380
- * @param request object encapsulating the request (e.g.
381
- * {request: url, url: "http://www.google.co.uk"})
382
- */
383
- function parseRequest(request) {
384
- if (ChromeDriver.isBlockedWaitingForResponse) {
385
- console.log("Already sent a request which hasn't been replied to yet. " +
386
- "Not parsing any more.");
387
- return;
388
- }
389
- ChromeDriver.isBlockedWaitingForResponse = true;
390
- setExtensionBusyIndicator(true);
391
-
392
- switch (request.request) {
393
- case "get":
394
- getUrl(request.url);
395
- break;
396
- case "close":
397
- //Doesn't re-focus the ChromeDriver.activePort on any tab.
398
- chrome.tabs.remove(ChromeDriver.activeTabId);
399
- ChromeDriver.isClosingTab = true;
400
- break;
401
- case "getCurrentWindowHandle":
402
- //TODO(danielwh): Get window's handle, not frame's
403
- var handle =
404
- (ChromeDriver.activePort == null ? ChromeDriver.activePort.name : "");
405
- sendResponseToParsedRequest({statusCode: 0, value: handle}, false);
406
- break;
407
- case "getWindowHandles":
408
- sendResponseToParsedRequest(getWindowHandles(), false);
409
- break;
410
- case "switchToDefaultContent":
411
- switchToDefaultContent();
412
- break;
413
- case "switchToFrameByIndex":
414
- switchToFrame(null, request.index);
415
- break;
416
- case "switchToFrameByName":
417
- switchToFrame(request.name, null);
418
- break;
419
- case "switchToWindow":
420
- ChromeDriver.hasHwnd = false;
421
- if (request.windowName !== undefined) {
422
- setActivePortByWindowName(request.windowName);
423
- } else {
424
- sendResponseToParsedRequest({
425
- statusCode: 3,
426
- value: {
427
- message: 'Window to switch to was not given'
428
- }
429
- }, false);
430
- }
431
- break;
432
- case "screenshot":
433
- getScreenshot();
434
- break;
435
- case "clickElement":
436
- case "hoverOverElement":
437
- // Falling through, as native events are handled the same
438
- case "sendKeysToElement":
439
- if (typeof(request.keys) == "object" && request.keys.length !== undefined) {
440
- request.keys = request.keys.join("");
441
- }
442
- sendMessageOnActivePortAndAlsoKeepTrackOfIt(
443
- wrapInjectEmbedIfNecessary(request));
444
- break;
445
- case "getCurrentUrl":
446
- case "getTitle":
447
- if (hasNoPage()) {
448
- console.log("Not got a page, but asked for string, so sending empty string");
449
- sendResponseToParsedRequest({statusCode: 0, value: ''});
450
- break;
451
- }
452
- // Falling through, as if we do have a page, we want to treat this like a
453
- // normal request
454
- case "findElement":
455
- case "findChildElement":
456
- if (hasNoPage()) {
457
- console.log("Not got a page, but asked for element, so throwing NoSuchElementException");
458
- sendResponseToParsedRequest({statusCode: 7, value: {message: 'Was not on a page, so could not find elements'}});
459
- break;
460
- }
461
- // Falling through, as if we do have a page, we want to treat this like a
462
- // normal request
463
- case "findElements":
464
- case "findChildElements":
465
- if (hasNoPage()) {
466
- console.log("Not got a page, but asked for elements, so returning no elements");
467
- sendResponseToParsedRequest({statusCode: 0, value: []});
468
- break;
469
- }
470
- // Falling through, as if we do have a page, we want to treat this like a
471
- // normal request
472
- case "deleteAllCookies":
473
- case "deleteCookie":
474
- if (hasNoPage()) {
475
- console.log("Not got a page, but asked to delete cookies, so returning ok");
476
- sendResponseToParsedRequest({statusCode: 0});
477
- break;
478
- }
479
- // Falling through, as if we do have a page, we want to treat this like a
480
- // normal request
481
- default:
482
- var sequenceNumber = ChromeDriver.requestSequenceNumber;
483
- ChromeDriver.requestSequenceNumber++;
484
- sendMessageOnActivePortAndAlsoKeepTrackOfIt({
485
- request: request,
486
- sequenceNumber: sequenceNumber
487
- });
488
- break;
489
- }
490
- }
491
-
492
- function getScreenshot() {
493
- chrome.tabs.captureVisibleTab(null, getScreenshotResult);
494
- }
495
-
496
- function getScreenshotResult(snapshotDataUrl) {
497
- var index = snapshotDataUrl.indexOf('base64,');
498
- if (index == -1) {
499
- sendResponseToParsedRequest({statusCode: 99}, false);
500
- return;
501
- }
502
- var base64 = snapshotDataUrl.substring(index + 'base64,'.length);
503
- sendResponseToParsedRequest({statusCode: 0, value: base64}, false);
504
- }
505
-
506
- function sendMessageOnActivePortAndAlsoKeepTrackOfIt(message) {
507
- ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet = message.request;
508
- try {
509
- ChromeDriver.activePort.postMessage(message);
510
- } catch (e) {
511
- console.log("Tried to send request without an active port. " +
512
- "Request will retry when connected, but will hang until then.");
513
- }
514
- }
515
-
516
- /**
517
- * Parse messages coming in on the port (responses from the content script).
518
- * @param message JSON message of format:
519
- * {response: "some command",
520
- * value: {statusCode: STATUS_CODE
521
- * [, optional params]}}
522
- */
523
- function parsePortMessage(message) {
524
- console.log(
525
- "Received response from content script: " + JSON.stringify(message));
526
- if (!message || !message.response || !message.response.value ||
527
- message.response.value.statusCode === undefined ||
528
- message.response.value.statusCode == null ||
529
- message.sequenceNumber === undefined) {
530
- // Should only ever happen if we sent a bad request,
531
- // or the content script is broken
532
- console.log("Got invalid response from the content script.");
533
- return;
534
- }
535
- var toSend = {statusCode: 12};
536
- ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet = null;
537
- switch (message.response.value.statusCode) {
538
- // Error codes are loosely based on native exception codes, see
539
- // common/src/cpp/webdriver-interactions/errorcodes.h
540
- case 0:
541
- case 2: //org.openqa.selenium.WebDriverException [Cookies]
542
- case 3: //org.openqa.selenium.NoSuchWindowException
543
- case 7: //org.openqa.selenium.NoSuchElementException
544
- case 8: //org.openqa.selenium.NoSuchFrameException
545
- case 9: //java.lang.UnsupportedOperationException [Unknown command]
546
- case 10: //org.openqa.selenium.StaleElementReferenceException
547
- case 11: //org.openqa.selenium.ElementNotVisibleException
548
- case 12: //java.lang.UnsupportedOperationException [Invalid element state ]
549
- case 17: //org.openqa.selenium.WebDriverException [Bad javascript]
550
- case 19: //org.openqa.selenium.XPathLookupException
551
- case 99: //org.openqa.selenium.WebDriverException [Native event]
552
- toSend = {statusCode: message.response.value.statusCode, value: null};
553
- if (message.response.value !== undefined && message.response.value != null &&
554
- message.response.value.value !== undefined) {
555
- toSend.value = message.response.value.value;
556
- }
557
- sendResponseToParsedRequest(toSend, message.response.wait);
558
- break;
559
- case "no-op":
560
- //Some special operation which isn't sending HTTP
561
- switch (message.response.response) {
562
- case "clickElement":
563
- try {
564
- if (document.embeds[0].clickAt(message.response.value.x, message.response.value.y)) {
565
- sendResponseToParsedRequest({statusCode: 0}, true);
566
- } else {
567
- sendResponseToParsedRequest({statusCode: 99}, true);
568
- }
569
- } catch(e) {
570
- console.log("Error natively clicking. Trying non-native.");
571
- ChromeDriver.isBlockedWaitingForResponse = false;
572
- parseRequest({
573
- request: 'nonNativeClickElement',
574
- elementId: message.response.value.elementId
575
- });
576
- }
577
- break;
578
- case "hoverElement":
579
- try {
580
- var points = message.response.value;
581
- if (document.embeds[0].mouseMoveTo(15, points.oldX, points.oldY, points.newX, points.newY)) {
582
- sendResponseToParsedRequest({statusCode: 0}, true);
583
- } else {
584
- sendResponseToParsedRequest({statusCode: 99}, true);
585
- }
586
- } catch(e) {
587
- sendResponseToParsedRequest({statusCode: 99}, true);
588
- }
589
- break;
590
- case "sendKeysToElement":
591
- try {
592
- if (document.embeds[0].sendKeys(message.response.value.keys)) {
593
- sendResponseToParsedRequest({statusCode: 0}, true);
594
- } else {
595
- sendResponseToParsedRequest({statusCode: 99}, true);
596
- }
597
- } catch(e) {
598
- console.log("Error natively sending keys. Trying non-native.");
599
- ChromeDriver.isBlockedWaitingForResponse = false;
600
- parseRequest({
601
- request: 'sendElementNonNativeKeys',
602
- elementId: message.response.value.elementId,
603
- keys: message.response.value.keys
604
- });
605
- }
606
- break;
607
- case "sniffForMetaRedirects":
608
- if (!message.response.value.value &&
609
- !ChromeDriver.hasSentResponseToThisPageLoading) {
610
- ChromeDriver.urlBeingLoaded = null;
611
- ChromeDriver.hasSentResponseToThisPageLoading = true;
612
- switchToDefaultContent();
613
- }
614
- break;
615
- case "newTabInformation":
616
- var response = message.response.value;
617
- for (var tab in ChromeDriver.tabs) {
618
- //RACE CONDITION!!!
619
- //This call should happen before another content script
620
- //connects and returns this value,
621
- //but if it doesn't, we may get mismatched information
622
- if (ChromeDriver.tabs[tab].isFrameset === undefined) {
623
- ChromeDriver.tabs[tab].isFrameset = response.isFrameset;
624
- return;
625
- } else {
626
- for (var frame in ChromeDriver.tabs[tab].frames) {
627
- var theFrame = ChromeDriver.tabs[tab].frames[frame];
628
- if (theFrame.isFrameset === undefined) {
629
- theFrame.isFrameset = response.isFrameset;
630
- return;
631
- }
632
- }
633
- }
634
- }
635
- break;
636
- case "getFrameNameFromIndex":
637
- var newName = message.response.value.name;
638
- if (ChromeDriver.restOfCurrentFramePath.length != 0) {
639
- newName += "." + ChromeDriver.restOfCurrentFramePath.join(".");
640
- }
641
- switchToFrameByName(newName);
642
- break;
643
- }
644
- break;
645
- }
646
- }
647
-
648
- /**
649
- * If the plugin doesn't currently have an HWND for this page,
650
- * we need to get one by injecting an embed
651
- */
652
- function wrapInjectEmbedIfNecessary(requestObject) {
653
- if (ChromeDriver.hasHwnd) {
654
- var sequenceNumber = ChromeDriver.requestSequenceNumber;
655
- ChromeDriver.requestSequenceNumber++;
656
- return {
657
- sequenceNumber: sequenceNumber,
658
- request: requestObject
659
- };
660
- } else {
661
- var wrappedObject = {
662
- sequenceNumber: ChromeDriver.requestSequenceNumber,
663
- request: {
664
- request: "injectEmbed",
665
- followup: {
666
- sequenceNumber: ChromeDriver.requestSequenceNumber + 1,
667
- request: requestObject
668
- }
669
- }
670
- };
671
- ChromeDriver.requestSequenceNumber += 2;
672
- return wrappedObject;
673
- }
674
- }
675
-
676
- /**
677
- * Gets all current window handles
678
- * @return an array containing all of the current window handles
679
- */
680
- function getWindowHandles() {
681
- var windowHandles = [];
682
- for (var tab in ChromeDriver.tabs) {
683
- windowHandles.push(ChromeDriver.tabs[tab].windowName);
684
- }
685
- return {statusCode: 0, value: windowHandles}
686
- }
687
-
688
- function resetActiveTabDetails() {
689
- ChromeDriver.activePort = null;
690
- ChromeDriver.hasHwnd = false;
691
- ChromeDriver.activeTabId = null;
692
- ChromeDriver.doFocusOnNextOpenedTab = true;
693
- ChromeDriver.hasSentResponseToThisPageLoading = false;
694
- ChromeDriver.portToUseForFrameLookups = null;
695
- ChromeDriver.currentUrl = null;
696
- resetCurrentlyWaitingOnContentScriptTime();
697
- }
698
-
699
- function setActiveTabDetails(tab) {
700
- ChromeDriver.activeTabId = tab.id;
701
- ChromeDriver.activeWindowId = tab.windowId;
702
- ChromeDriver.doFocusOnNextOpenedTab = false;
703
- ChromeDriver.currentUrl = tab.url;
704
- resetCurrentlyWaitingOnContentScriptTime();
705
- }
706
-
707
- function switchToDefaultContent() {
708
- ChromeDriver.hasHwnd = false;
709
- for (var tab in ChromeDriver.tabs) {
710
- if (ChromeDriver.tabs[tab].tabId == ChromeDriver.activeTabId) {
711
- if (ChromeDriver.tabs[tab].isFrameset) {
712
- ChromeDriver.isBlockedWaitingForResponse = false;
713
- parseRequest({request: 'switchToFrameByIndex', index: 0});
714
- } else {
715
- ChromeDriver.activePort = ChromeDriver.tabs[tab].mainPort;
716
- sendResponseToParsedRequest({statusCode: 0}, false);
717
- }
718
- return;
719
- }
720
- }
721
- }
722
-
723
- function switchToFrame(name, index) {
724
- ChromeDriver.hasHwnd = false;
725
- for (var tab in ChromeDriver.tabs) {
726
- if (ChromeDriver.tabs[tab].tabId == ChromeDriver.activeTabId) {
727
- ChromeDriver.portToUseForFrameLookups = ChromeDriver.tabs[tab].mainPort;
728
- break;
729
- }
730
- }
731
- if (name !== undefined && name != null) {
732
- switchToFrameByName(name);
733
- } else if (index !== undefined && index != null) {
734
- getFrameNameFromIndex(index);
735
- } else {
736
- sendResponseToParsedRequest({
737
- statusCode: 9,
738
- value: {
739
- message: "Switching frames other than by name or id is unsupported"
740
- }
741
- });
742
- }
743
- }
744
-
745
- function switchToFrameByName(name) {
746
- var names = name.split(".");
747
-
748
- for (var tab in ChromeDriver.tabs) {
749
- if (ChromeDriver.tabs[tab].tabId == ChromeDriver.activeTabId) {
750
- var frame;
751
- for (frame in ChromeDriver.tabs[tab].frames) {
752
- // Maybe name was a fully qualified name, which perhaps just happened to
753
- // include .s
754
- if (ChromeDriver.tabs[tab].frames[frame].frameName == name) {
755
- ChromeDriver.activePort =
756
- ChromeDriver.tabs[tab].frames[frame].framePort;
757
- ChromeDriver.restOfCurrentFramePath = [];
758
- sendResponseToParsedRequest({statusCode: 0}, false);
759
- return;
760
- }
761
- }
762
- for (frame in ChromeDriver.tabs[tab].frames) {
763
- // Maybe we're looking for a child, see if this is the parent of it
764
- if (ChromeDriver.tabs[tab].frames[frame].frameName == names[0]) {
765
- ChromeDriver.activePort =
766
- ChromeDriver.tabs[tab].frames[frame].framePort;
767
- ChromeDriver.portToUseForFrameLookups = ChromeDriver.activePort;
768
- names.shift();
769
- ChromeDriver.restOfCurrentFramePath = names;
770
- if (names.length == 0) {
771
- sendResponseToParsedRequest({statusCode: 0}, false);
772
- return;
773
- } else {
774
- switchToFrameByName(names.join("."));
775
- return;
776
- }
777
- }
778
- }
779
- }
780
- }
781
-
782
- //Maybe the "name" was actually an index? Let's find out...
783
- var index = null;
784
- try {
785
- index = parseInt(names[0]);
786
- } catch (e) {
787
- }
788
- if (!isNaN(index)) {
789
- names.shift();
790
- ChromeDriver.restOfCurrentFramePath = names;
791
- getFrameNameFromIndex(index);
792
- return;
793
- }
794
-
795
- ChromeDriver.isBlockedWaitingForResponse = false;
796
- parseRequest({request: 'switchToNamedIFrameIfOneExists', name: name});
797
- }
798
-
799
- function getFrameNameFromIndex(index) {
800
- var message = {
801
- request: {
802
- request: "getFrameNameFromIndex",
803
- index: index
804
- },
805
- sequenceNumber: ChromeDriver.requestSequenceNumber
806
- };
807
- ChromeDriver.requestSequenceNumber++;
808
- ChromeDriver.portToUseForFrameLookups.postMessage(message);
809
- }
810
-
811
- /**
812
- * Closes the current tab if it exists, and opens a new one, in which it
813
- * gets the URL passed
814
- * @param url the URL to load
815
- */
816
- function getUrl(url) {
817
- ChromeDriver.urlBeingLoaded = url;
818
- var tempActiveTagId = ChromeDriver.activeTabId;
819
- if (url.indexOf("#") > -1 && ChromeDriver.currentUrl != null &&
820
- ChromeDriver.currentUrl.split("#")[0] == url.split("#")[0]) {
821
- ChromeDriver.isGettingUrlButOnlyChangingByFragment = true;
822
- } else {
823
- resetActiveTabDetails();
824
- }
825
- ChromeDriver.currentUrl = url;
826
- if (tempActiveTagId == null) {
827
- chrome.tabs.create({url: url, selected: true}, getUrlCallback);
828
- } else {
829
- ChromeDriver.activeTabId = tempActiveTagId;
830
- if (ChromeDriver.isGettingUrlButOnlyChangingByFragment) {
831
- chrome.tabs.update(ChromeDriver.activeTabId, {url: url, selected: true},
832
- getUrlCallback);
833
- } else {
834
- // we need to create the new tab before deleting the old one
835
- // in order to avoid hanging on OS X
836
- var oldId = ChromeDriver.activeTabId;
837
- resetActiveTabDetails();
838
- chrome.tabs.create({url: url, selected: true}, getUrlCallback);
839
- chrome.tabs.remove(oldId);
840
- }
841
- }
842
- }
843
-
844
- function getUrlCallback(tab) {
845
- if (chrome.extension.lastError) {
846
- // An error probably arose because Chrome didn't have a window yet
847
- // (see crbug.com 19846)
848
- // If we retry, we *should* be fine. Unless something really bad is
849
- // happening, in which case we will probably hang indefinitely trying to
850
- // reload the same URL
851
- getUrl(ChromeDriver.urlBeingLoaded);
852
- return;
853
- }
854
- if (tab == null) {
855
- //chrome.tabs.update's callback doesn't pass a Tab argument,
856
- //so we need to populate it ourselves
857
- chrome.tabs.get(ChromeDriver.activeTabId, getUrlCallback);
858
- return;
859
- }
860
- if (tab.status != "complete") {
861
- // Use the helper calback so that we actually get updated version of the tab
862
- // we're getting
863
- setTimeout("getUrlCallbackById(" + tab.id + ")", 10);
864
- } else {
865
- ChromeDriver.getUrlRequestSequenceNumber++;
866
- if (ChromeDriver.activePort == null) {
867
- if (ChromeDriver.currentlyWaitingUntilGiveUpOnContentScriptLoading <= 0) {
868
- ChromeDriver.hasNoConnectionToPage = true;
869
- sendEmptyResponseWhenTabIsLoaded(tab);
870
- } else {
871
- ChromeDriver.currentlyWaitingUntilGiveUpOnContentScriptLoading -=
872
- ChromeDriver.waitForContentScriptIncrement;
873
- setTimeout("getUrlCallbackById(" + tab.id + ")", ChromeDriver.waitForContentScriptIncrement);
874
- return;
875
- }
876
- }
877
- setActiveTabDetails(tab);
878
- }
879
- if (ChromeDriver.isGettingUrlButOnlyChangingByFragment) {
880
- ChromeDriver.urlBeingLoaded = null;
881
- resetCurrentlyWaitingOnContentScriptTime();
882
- sendResponseToParsedRequest({statusCode: 0}, false);
883
- ChromeDriver.isGettingUrlButOnlyChangingByFragment = false;
884
- }
885
- }
886
-
887
- function getUrlCallbackById(tabId) {
888
- chrome.tabs.get(tabId, getUrlCallback);
889
- }
890
-
891
- function sendEmptyResponseWhenTabIsLoaded(tab) {
892
- if (tab.status == "complete") {
893
- if (ChromeDriver.activePort) {
894
- ChromeDriver.isBlockedWaitingForResponse = false;
895
- parseRequest({request: 'sniffForMetaRedirects'});
896
- } else {
897
- if (!ChromeDriver.hasSentResponseToThisPageLoading) {
898
- ChromeDriver.urlBeingLoaded = null;
899
- sendResponseToParsedRequest({statusCode: 0}, false);
900
- }
901
- }
902
- } else {
903
- chrome.tabs.get(tab.id, sendEmptyResponseWhenTabIsLoaded);
904
- }
905
- }
906
-
907
-
908
- function setExtensionBusyIndicator(busy) {
909
- if (busy) {
910
- chrome.browserAction.setIcon({path: "icons/busy.png"})
911
- } else {
912
- chrome.browserAction.setIcon({path: "icons/free.png"})
913
- }
914
- }
915
-
916
- function setActivePortByWindowName(handle) {
917
- for (var tab in ChromeDriver.tabs) {
918
- if (ChromeDriver.tabs[tab].windowName == handle ||
919
- ChromeDriver.tabs[tab].mainPort.name == handle) {
920
- ChromeDriver.activePort = ChromeDriver.tabs[tab].mainPort;
921
- chrome.tabs.get(ChromeDriver.tabs[tab].tabId, setActiveTabDetails);
922
- chrome.tabs.update(ChromeDriver.tabs[tab].tabId, {selected: true});
923
- sendResponseToParsedRequest({statusCode: 0}, false);
924
- return;
925
- }
926
- }
927
- sendResponseToParsedRequest({statusCode: 3, value: {message: 'Could not find window to switch to by handle: ' + handle}}, false);
928
- }
929
-
930
-
931
- /**
932
- * @return {boolean} Whether there is currently no active page.
933
- */
934
- function hasNoPage() {
935
- return ChromeDriver.hasNoConnectionToPage ||
936
- ChromeDriver.activePort == null ||
937
- ChromeDriver.activeTabId == null;
938
- }
939
-
940
- function resetCurrentlyWaitingOnContentScriptTime() {
941
- ChromeDriver.currentlyWaitingUntilGiveUpOnContentScriptLoading =
942
- ChromeDriver.timeoutUntilGiveUpOnContentScriptLoading;
943
- }
1
+ /** @namespace */
2
+ ChromeDriver = {};
3
+
4
+
5
+ /**
6
+ * Array of all information about currently loaded tabs (where a WebDriver
7
+ * window is probably a tab)
8
+ * Entries of form:
9
+ * {Int tabId, String windowName, Port mainPort, Boolean isFrameset, FrameData[] frames}
10
+ * FrameData ::= {[Int frameId], String frameName, Port framePort, FrameData[]}
11
+ * frameId can be undefined, if it has not yet been looked up, but should be
12
+ * added once it is known
13
+ * @type {Array.<Object>} TODO(jmleyba): Type info
14
+ */
15
+ ChromeDriver.tabs = [];
16
+
17
+
18
+ /**
19
+ * Port to the currently active frame (or tab, if the current page is not a
20
+ * frameset).
21
+ * @type {?Port}
22
+ */
23
+ ChromeDriver.activePort = null;
24
+
25
+
26
+ /**
27
+ * ID of the currently active tab.
28
+ * @type {?string}
29
+ */
30
+ ChromeDriver.activeTabId = null;
31
+
32
+
33
+ /**
34
+ * Whether we should switch to the next tab which opens. Should be set if the
35
+ * last active tab was closed.
36
+ * @type {boolean}
37
+ */
38
+ ChromeDriver.doFocusOnNextOpenedTab = true;
39
+
40
+
41
+ /**
42
+ * Place to temporarily store the URL currently being loaded, so that we can
43
+ * retry if needed, because opening a URL is an async callback.
44
+ * @type {?string}
45
+ */
46
+ ChromeDriver.urlBeingLoaded = null;
47
+
48
+
49
+ /**
50
+ * URL we believe we're currently on.
51
+ * @type {?string}
52
+ */
53
+ ChromeDriver.currentUrl = null;
54
+
55
+
56
+ /**
57
+ * Whether we are loading a new URL that difers from the current URL only in
58
+ * fragment.
59
+ * @type {boolean}
60
+ */
61
+ ChromeDriver.isGettingUrlButOnlyChangingByFragment = false;
62
+
63
+
64
+ /**
65
+ * Whether we are currently executing a {@code ChromeDriver#close()}, and
66
+ * accordingly should send a success response when the tab closes.
67
+ * @type {boolean}
68
+ */
69
+ ChromeDriver.isClosingTab = false;
70
+
71
+
72
+ /**
73
+ * Whether we have sent a response to the {currently, most recently loading
74
+ * page.
75
+ * @type {boolean}
76
+ */
77
+ ChromeDriver.hasSentResponseToThisPageLoading = false;
78
+
79
+
80
+ /**
81
+ * Whether we believe a page is open to which we have no content script.
82
+ * @type {boolean}
83
+ */
84
+ ChromeDriver.hasNoConnectionToPage = true;
85
+
86
+
87
+ /**
88
+ * Stores the remaining frames to traverse when switching frames.
89
+ * @type {Array.<string>}
90
+ */
91
+ ChromeDriver.restOfCurrentFramePath = [];
92
+
93
+
94
+ /**
95
+ * Port to the frameset or main content page we currently have loaded, so that
96
+ * we can probe it for information about its frames.
97
+ * @type {?Port}
98
+ */
99
+ ChromeDriver.portToUseForFrameLookups = null;
100
+
101
+
102
+ /**
103
+ * The last request we sent that has not been answered, so that if we change
104
+ * page between sending a request and receiving a response, we can re-send it to
105
+ * the newly loaded page.
106
+ * @type {*} TODO(jmleyba)
107
+ */
108
+ ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet = null;
109
+
110
+
111
+ /**
112
+ * Whether the plugin has the OS-specific window handle for the active tab. This
113
+ * is called HWND rather than window handle to avoid confusion with the other
114
+ * use of window handle to mean 'name of window'.
115
+ * @type {boolean}
116
+ */
117
+ ChromeDriver.hasHwnd = false;
118
+
119
+
120
+ /**
121
+ * THe last XMLHttpRequest we made (used for communication with test language
122
+ * bindings).
123
+ * @type {?XMLHttpRequest}
124
+ */
125
+ ChromeDriver.xmlHttpRequest = null;
126
+
127
+ /**
128
+ * URL to ping for commands.
129
+ * TODO(danielwh): Get this from the initial URL - see http://crbug.com/11547,
130
+ * the ChromeDriverInternals wiki page.
131
+ * There is a patch to fix this on the Downloads page of the Selenium project
132
+ * @type {string}
133
+ */
134
+ ChromeDriver.xmlHttpRequestUrl = "http://127.0.0.1:9700/chromeCommandExecutor";
135
+
136
+
137
+ /**
138
+ * @type {number}
139
+ */
140
+ ChromeDriver.requestSequenceNumber = 0;
141
+
142
+
143
+ /**
144
+ * @type {number}
145
+ */
146
+ ChromeDriver.getUrlRequestSequenceNumber = 0;
147
+
148
+
149
+ /**
150
+ * Prefix prepended to the hopefully unique javascript window name, in hopes of
151
+ * further removing conflict.
152
+ * @type {string}
153
+ */
154
+ ChromeDriver.windowHandlePrefix = '__webdriver_chromedriver_windowhandle';
155
+
156
+
157
+ /**
158
+ * Whether we will not execute any commands because we are already executing
159
+ * one.
160
+ * @type {boolean}
161
+ */
162
+ ChromeDriver.isBlockedWaitingForResponse = false;
163
+
164
+ /**
165
+ * It's possible that the page has completed loading,
166
+ * but the content script has not yet fired.
167
+ * In this case, to not report that there is no page,
168
+ * when we are just too fast, we wait up to this amount of time.
169
+ * @type {number} unit: milliseconds
170
+ */
171
+ ChromeDriver.timeoutUntilGiveUpOnContentScriptLoading = 5000;
172
+
173
+ /**
174
+ * How long we are currently waiting for the content script to load
175
+ * after loading the page
176
+ * @type {number} unit: milliseconds
177
+ */
178
+ ChromeDriver.currentlyWaitingUntilGiveUpOnContentScriptLoading;
179
+
180
+ //Set ChromeDriver.currentlyWaitingUntilGiveUpOnContentScriptLoading;
181
+ resetCurrentlyWaitingOnContentScriptTime();
182
+
183
+ /**
184
+ * How long we wait between poling whether we have a content script,
185
+ * when loading a new page, up until
186
+ * ChromeDriver.timeoutUntilGiveUpOnContentScriptLoading
187
+ * @type {number} unit: milliseconds
188
+ */
189
+ ChromeDriver.waitForContentScriptIncrement = 100;
190
+
191
+ chrome.extension.onConnect.addListener(function(port) {
192
+ console.log("Connected to " + port.name);
193
+ // Note: The frameset port *always* connects before any frame port. After
194
+ // that, the order is in page loading time
195
+ ChromeDriver.hasNoConnectionToPage = false;
196
+ var foundTab = false;
197
+ for (var tab in ChromeDriver.tabs) {
198
+ if (ChromeDriver.tabs[tab].tabId == port.tab.id) {
199
+ //We must be a new [i]frame in the page, because when a page closes, it is
200
+ // removed from ChromeDriver.tabs
201
+ //TODO(danielwh): Work out WHICH page it's a sub-frame of (I don't look
202
+ // forward to this)
203
+ ChromeDriver.tabs[tab].frames.push({
204
+ frameName: port.name,
205
+ framePort: port,
206
+ frames: []
207
+ });
208
+ //Loaded a frame. Pushed it to the array. We don't know which page it's
209
+ // a sub-frame of, in the case of nested frames, if they have the same
210
+ // names. It would be nice to think people didn't use frames, let alone
211
+ // several layers of nesting of frames with the same name, but if it turns
212
+ // out to be a problem... Well, we'll see.
213
+ foundTab = true;
214
+ break;
215
+ }
216
+ }
217
+ if (!foundTab) {
218
+ //New tab!
219
+ //We don't know if it's a frameset yet, so we leave that as undefined
220
+ ChromeDriver.tabs.push({
221
+ tabId: port.tab.id,
222
+ windowName: ChromeDriver.windowHandlePrefix + "_" + port.tab.id,
223
+ mainPort: port,
224
+ frames: []
225
+ });
226
+ }
227
+
228
+ if (ChromeDriver.doFocusOnNextOpenedTab) {
229
+ ChromeDriver.activePort = port;
230
+ setActiveTabDetails(port.tab);
231
+ //Re-parse the last request we sent if we didn't get a response,
232
+ //because we ain't seeing a response any time soon
233
+
234
+ if (ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet != null) {
235
+ if (ChromeDriver.urlBeingLoaded != null) {
236
+ ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet = null;
237
+ } else {
238
+ ChromeDriver.isBlockedWaitingForResponse = false;
239
+ console.log("Re-trying request which was sent but not answered");
240
+ parseRequest(ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet);
241
+ }
242
+ }
243
+ }
244
+
245
+ if (ChromeDriver.urlBeingLoaded != null) {
246
+ //This was the result of a getUrl. Need to issue a response
247
+ sendEmptyResponseWhenTabIsLoaded(port.tab);
248
+ }
249
+ port.onMessage.addListener(parsePortMessage);
250
+ port.onDisconnect.addListener(function disconnectPort(port) {
251
+ console.log("Disconnected from " + port.name);
252
+ var remainingTabs = [];
253
+ for (var tab in ChromeDriver.tabs) {
254
+ if (ChromeDriver.tabs[tab].tabId == port.tab.id) {
255
+ if (ChromeDriver.tabs[tab].mainPort == port) {
256
+ //This main tab is being closed.
257
+ //Don't include it in the new version of ChromeDriver.tabs.
258
+ //Any subframes will also disconnect,
259
+ //but their tabId won't be present in the array,
260
+ //so they will be ignored.
261
+ continue;
262
+ } else {
263
+ //This is a subFrame being ditched
264
+ var remainingFrames = [];
265
+ for (var frame in ChromeDriver.tabs[tab].frames) {
266
+ if (ChromeDriver.tabs[tab].frames[frame].framePort == port) {
267
+ continue;
268
+ }
269
+ remainingFrames.push(ChromeDriver.tabs[tab].frames[frame]);
270
+ }
271
+ ChromeDriver.tabs[tab].frames = remainingFrames;
272
+ }
273
+ }
274
+ remainingTabs.push(ChromeDriver.tabs[tab]);
275
+ }
276
+ ChromeDriver.tabs = remainingTabs;
277
+ if (ChromeDriver.tabs.length == 0 || ChromeDriver.activePort == null ||
278
+ ChromeDriver.activePort.tab.id == port.tab.id) {
279
+ //If it is the active tab, perhaps we have followed a link,
280
+ //so we should focus on it.
281
+ //We have nothing better to focus on, anyway.
282
+ resetActiveTabDetails();
283
+ }
284
+ if (ChromeDriver.isClosingTab) {
285
+ //We are actively closing the tab, and expect a response to this
286
+ sendResponseToParsedRequest({statusCode: 0}, false)
287
+ ChromeDriver.isClosingTab = false;
288
+ if (ChromeDriver.tabs.length == 0) {
289
+ chrome.windows.getAll({}, function(windows) {
290
+ for (var window in windows) {
291
+ chrome.windows.remove(windows[window].id);
292
+ }
293
+ });
294
+ }
295
+ }
296
+ });
297
+ });
298
+
299
+ //Tell the ChromeCommandExecutor that we are here
300
+ sendResponseByXHR({statusCode: 0}, false);
301
+
302
+ /**
303
+ * Sends the passed argument as the result of a command
304
+ * @param result object encapsulating result to send
305
+ * @param wait whether we expect this command to possibly make changes
306
+ * we need to wait for (e.g. adding elements, opening windows) - if so,
307
+ * we wait until we think these effects are done
308
+ */
309
+ function sendResponseByXHR(result, wait) {
310
+ console.log("Sending result by XHR: " + JSON.stringify(result));
311
+ if (ChromeDriver.xmlHttpRequest != null) {
312
+ ChromeDriver.xmlHttpRequest.abort();
313
+ }
314
+ ChromeDriver.xmlHttpRequest = new XMLHttpRequest();
315
+ ChromeDriver.xmlHttpRequest.onreadystatechange =
316
+ handleXmlHttpRequestReadyStateChange;
317
+ ChromeDriver.xmlHttpRequest.open(
318
+ "POST", ChromeDriver.xmlHttpRequestUrl, true);
319
+ ChromeDriver.xmlHttpRequest.setRequestHeader(
320
+ "Content-type", "application/json");
321
+ //Default to waiting for page changes, just in case
322
+ //TODO(danielwh): Iterate over tabs checking their status
323
+ if (wait === undefined || wait == null || wait) {
324
+ setTimeout(sendResult, 600, [result]);
325
+ } else {
326
+ sendResult(result);
327
+ }
328
+ }
329
+
330
+ /**
331
+ * Actually sends the result by XHR
332
+ * Should only EVER be called by sendResponseByXHR,
333
+ * as it ignores things like setting up XHR and blocking,
334
+ * and just forces the sending over an assumed open XHR
335
+ * @param result String to send
336
+ */
337
+ function sendResult(result) {
338
+ //TODO(danielwh): Iterate over tabs checking their status
339
+ ChromeDriver.xmlHttpRequest.send(result + "\nEOResponse\n");
340
+ console.log("Sent result by XHR: " + JSON.stringify(result));
341
+ }
342
+
343
+ /**
344
+ * Sends the response to a request, which has been parsed by parseRequest
345
+ * Should be used only from within parseRequest (or methods called from it),
346
+ * because it adheres to the blocking semantics of parseRequest
347
+ */
348
+ function sendResponseToParsedRequest(toSend, wait) {
349
+ if (!ChromeDriver.isBlockedWaitingForResponse) {
350
+ console.log("Tried to send a response (" + toSend +
351
+ ") when not waiting for one. Dropping response.");
352
+ return;
353
+ }
354
+ ChromeDriver.isBlockedWaitingForResponse = false;
355
+ ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet = null;
356
+ console.log("SENDING RESPOND TO PARSED REQUEST");
357
+ sendResponseByXHR(JSON.stringify(toSend), wait);
358
+ setExtensionBusyIndicator(false);
359
+ }
360
+
361
+ /**
362
+ * When we receive a request, dispatches parseRequest to execute it
363
+ */
364
+ function handleXmlHttpRequestReadyStateChange() {
365
+ if (this.readyState == 4) {
366
+ if (this.status != 200) {
367
+ console.log("Request state was 4 but status: " + this.status +
368
+ ". responseText: " + this.responseText);
369
+ } else {
370
+ console.log("GOT XHR RESPONSE: " + this.responseText);
371
+ var request = JSON.parse(this.responseText);
372
+ if (request.request == "quit") {
373
+ //We're only allowed to send a response if we're blocked waiting for one, so pretend
374
+ console.log("SENDING QUIT XHR");
375
+ sendResponseByXHR(JSON.stringify({statusCode: 0}), false);
376
+ } else {
377
+ console.log("Got request to execute from XHR: " + this.responseText);
378
+ parseRequest(request);
379
+ }
380
+ }
381
+ }
382
+ }
383
+
384
+ /**
385
+ * Parses a request received from the ChromeCommandExecutor and either sends the
386
+ * response, or sends a message to the content script with a command to execute
387
+ * @param request object encapsulating the request (e.g.
388
+ * {request: url, url: "http://www.google.co.uk"})
389
+ */
390
+ function parseRequest(request) {
391
+ if (ChromeDriver.isBlockedWaitingForResponse) {
392
+ console.log("Already sent a request which hasn't been replied to yet. " +
393
+ "Not parsing any more.");
394
+ return;
395
+ }
396
+ ChromeDriver.isBlockedWaitingForResponse = true;
397
+ setExtensionBusyIndicator(true);
398
+
399
+ switch (request.request) {
400
+ case "get":
401
+ getUrl(request.url);
402
+ break;
403
+ case "close":
404
+ //Doesn't re-focus the ChromeDriver.activePort on any tab.
405
+ chrome.tabs.remove(ChromeDriver.activeTabId);
406
+ ChromeDriver.isClosingTab = true;
407
+ break;
408
+ case "getCurrentWindowHandle":
409
+ if (ChromeDriver.activePort == null) {
410
+ // console.log("No active port right now.");
411
+ // Fine. Find the active tab.
412
+ // TODO(simon): This is lame and error prone
413
+ var len = ChromeDriver.tabs.length;
414
+ for (var i = 0; i < len; i++) {
415
+ if (ChromeDriver.tabs[i].selected) {
416
+ sendResponseToParsedRequest({statusCode: 0, value: ChromeDriver.tabs[i].id}, false);
417
+ }
418
+ }
419
+
420
+ // Hohoho. The first argument to tabs.getSelected is optional, but must be set.
421
+ chrome.windows.getCurrent(function(win) {
422
+ chrome.tabs.getSelected(win.id, function(tab) {
423
+ var len = ChromeDriver.tabs.length;
424
+ for (var i = 0; i < len; i++) {
425
+ if (ChromeDriver.tabs[i].tabId == tab.id) {
426
+ sendResponseToParsedRequest({statusCode: 0, value: ChromeDriver.tabs[i].tabId}, false);
427
+ return;
428
+ }
429
+ }
430
+ });
431
+ });
432
+ } else {
433
+ // Wow. I can't see this being error prone in the slightest
434
+ var handle = ChromeDriver.activePort.sender.tab.id;
435
+ sendResponseToParsedRequest({statusCode: 0, value: handle}, false);
436
+ };
437
+ break;
438
+ case "getWindowHandles":
439
+ sendResponseToParsedRequest(getWindowHandles(), false);
440
+ break;
441
+ case "switchToDefaultContent":
442
+ switchToDefaultContent();
443
+ break;
444
+ case "switchToFrameByIndex":
445
+ switchToFrame(null, request.index);
446
+ break;
447
+ case "switchToFrameByName":
448
+ switchToFrame(request.name, null);
449
+ break;
450
+ case "switchToWindow":
451
+ ChromeDriver.hasHwnd = false;
452
+ if (request.windowName !== undefined) {
453
+ setActivePortByWindowName(request.windowName);
454
+ } else {
455
+ sendResponseToParsedRequest({
456
+ statusCode: 3,
457
+ value: {
458
+ message: 'Window to switch to was not given'
459
+ }
460
+ }, false);
461
+ }
462
+ break;
463
+ case "screenshot":
464
+ getScreenshot();
465
+ break;
466
+ case "clickElement":
467
+ case "hoverOverElement":
468
+ // Falling through, as native events are handled the same
469
+ case "sendKeysToElement":
470
+ if (typeof(request.keys) == "object" && request.keys.length !== undefined) {
471
+ request.keys = request.keys.join("");
472
+ }
473
+ sendMessageOnActivePortAndAlsoKeepTrackOfIt(
474
+ wrapInjectEmbedIfNecessary(request));
475
+ break;
476
+ case "getCurrentUrl":
477
+ case "getTitle":
478
+ if (hasNoPage()) {
479
+ console.log("Not got a page, but asked for string, so sending empty string");
480
+ sendResponseToParsedRequest({statusCode: 0, value: ''});
481
+ break;
482
+ }
483
+ // Falling through, as if we do have a page, we want to treat this like a
484
+ // normal request
485
+ case "findElement":
486
+ case "findChildElement":
487
+ if (hasNoPage()) {
488
+ console.log("Not got a page, but asked for element, so throwing NoSuchElementException");
489
+ sendResponseToParsedRequest({statusCode: 7, value: {message: 'Was not on a page, so could not find elements'}});
490
+ break;
491
+ }
492
+ // Falling through, as if we do have a page, we want to treat this like a
493
+ // normal request
494
+ case "findElements":
495
+ case "findChildElements":
496
+ if (hasNoPage()) {
497
+ console.log("Not got a page, but asked for elements, so returning no elements");
498
+ sendResponseToParsedRequest({statusCode: 0, value: []});
499
+ break;
500
+ }
501
+ // Falling through, as if we do have a page, we want to treat this like a
502
+ // normal request
503
+ case "deleteAllCookies":
504
+ case "deleteCookie":
505
+ if (hasNoPage()) {
506
+ console.log("Not got a page, but asked to delete cookies, so returning ok");
507
+ sendResponseToParsedRequest({statusCode: 0});
508
+ break;
509
+ }
510
+ // Falling through, as if we do have a page, we want to treat this like a
511
+ // normal request
512
+ default:
513
+ var sequenceNumber = ChromeDriver.requestSequenceNumber;
514
+ ChromeDriver.requestSequenceNumber++;
515
+ sendMessageOnActivePortAndAlsoKeepTrackOfIt({
516
+ request: request,
517
+ sequenceNumber: sequenceNumber
518
+ });
519
+ break;
520
+ }
521
+ }
522
+
523
+ function getScreenshot() {
524
+ chrome.tabs.captureVisibleTab(null, getScreenshotResult);
525
+ }
526
+
527
+ function getScreenshotResult(snapshotDataUrl) {
528
+ var index = snapshotDataUrl.indexOf('base64,');
529
+ if (index == -1) {
530
+ sendResponseToParsedRequest({statusCode: 99}, false);
531
+ return;
532
+ }
533
+ var base64 = snapshotDataUrl.substring(index + 'base64,'.length);
534
+ sendResponseToParsedRequest({statusCode: 0, value: base64}, false);
535
+ }
536
+
537
+ function sendMessageOnActivePortAndAlsoKeepTrackOfIt(message) {
538
+ ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet = message.request;
539
+ try {
540
+ ChromeDriver.activePort.postMessage(message);
541
+ } catch (e) {
542
+ console.log("Tried to send request without an active port. " +
543
+ "Request will retry when connected, but will hang until then.");
544
+ }
545
+ }
546
+
547
+ /**
548
+ * Parse messages coming in on the port (responses from the content script).
549
+ * @param message JSON message of format:
550
+ * {response: "some command",
551
+ * value: {statusCode: STATUS_CODE
552
+ * [, optional params]}}
553
+ */
554
+ function parsePortMessage(message) {
555
+ console.log(
556
+ "Received response from content script: " + JSON.stringify(message));
557
+ if (!message || !message.response || !message.response.value ||
558
+ message.response.value.statusCode === undefined ||
559
+ message.response.value.statusCode == null ||
560
+ message.sequenceNumber === undefined) {
561
+ // Should only ever happen if we sent a bad request,
562
+ // or the content script is broken
563
+ console.log("Got invalid response from the content script.");
564
+ return;
565
+ }
566
+ var toSend = {statusCode: 12};
567
+ ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet = null;
568
+ switch (message.response.value.statusCode) {
569
+ // Error codes are loosely based on native exception codes, see
570
+ // common/src/cpp/webdriver-interactions/errorcodes.h
571
+ case 0:
572
+ case 2: //org.openqa.selenium.WebDriverException [Cookies]
573
+ case 3: //org.openqa.selenium.NoSuchWindowException
574
+ case 7: //org.openqa.selenium.NoSuchElementException
575
+ case 8: //org.openqa.selenium.NoSuchFrameException
576
+ case 9: //java.lang.UnsupportedOperationException [Unknown command]
577
+ case 10: //org.openqa.selenium.StaleElementReferenceException
578
+ case 11: //org.openqa.selenium.ElementNotVisibleException
579
+ case 12: //java.lang.UnsupportedOperationException [Invalid element state ]
580
+ case 17: //org.openqa.selenium.WebDriverException [Bad javascript]
581
+ case 19: //org.openqa.selenium.XPathLookupException
582
+ case 99: //org.openqa.selenium.WebDriverException [Native event]
583
+ toSend = {statusCode: message.response.value.statusCode, value: null};
584
+ if (message.response.value !== undefined && message.response.value != null &&
585
+ message.response.value.value !== undefined) {
586
+ toSend.value = message.response.value.value;
587
+ }
588
+ sendResponseToParsedRequest(toSend, message.response.wait);
589
+ break;
590
+ case "no-op":
591
+ //Some special operation which isn't sending HTTP
592
+ switch (message.response.response) {
593
+ case "clickElement":
594
+ try {
595
+ if (document.embeds[0].clickAt(message.response.value.x, message.response.value.y)) {
596
+ sendResponseToParsedRequest({statusCode: 0}, true);
597
+ } else {
598
+ sendResponseToParsedRequest({statusCode: 99}, true);
599
+ }
600
+ } catch(e) {
601
+ console.log("Error natively clicking. Trying non-native.");
602
+ ChromeDriver.isBlockedWaitingForResponse = false;
603
+ parseRequest({
604
+ request: 'nonNativeClickElement',
605
+ elementId: message.response.value.elementId
606
+ });
607
+ }
608
+ break;
609
+ case "hoverElement":
610
+ try {
611
+ var points = message.response.value;
612
+ if (document.embeds[0].mouseMoveTo(15, points.oldX, points.oldY, points.newX, points.newY)) {
613
+ sendResponseToParsedRequest({statusCode: 0}, true);
614
+ } else {
615
+ sendResponseToParsedRequest({statusCode: 99}, true);
616
+ }
617
+ } catch(e) {
618
+ sendResponseToParsedRequest({statusCode: 99}, true);
619
+ }
620
+ break;
621
+ case "sendKeysToElement":
622
+ try {
623
+ if (document.embeds[0].sendKeys(message.response.value.keys)) {
624
+ sendResponseToParsedRequest({statusCode: 0}, true);
625
+ } else {
626
+ sendResponseToParsedRequest({statusCode: 99}, true);
627
+ }
628
+ } catch(e) {
629
+ console.log("Error natively sending keys. Trying non-native.");
630
+ ChromeDriver.isBlockedWaitingForResponse = false;
631
+ parseRequest({
632
+ request: 'sendElementNonNativeKeys',
633
+ elementId: message.response.value.elementId,
634
+ keys: message.response.value.keys
635
+ });
636
+ }
637
+ break;
638
+ case "sniffForMetaRedirects":
639
+ if (!message.response.value.value &&
640
+ !ChromeDriver.hasSentResponseToThisPageLoading) {
641
+ ChromeDriver.urlBeingLoaded = null;
642
+ ChromeDriver.hasSentResponseToThisPageLoading = true;
643
+ switchToDefaultContent();
644
+ }
645
+ break;
646
+ case "newTabInformation":
647
+ var response = message.response.value;
648
+ for (var tab in ChromeDriver.tabs) {
649
+ //RACE CONDITION!!!
650
+ //This call should happen before another content script
651
+ //connects and returns this value,
652
+ //but if it doesn't, we may get mismatched information
653
+ if (ChromeDriver.tabs[tab].isFrameset === undefined) {
654
+ ChromeDriver.tabs[tab].isFrameset = response.isFrameset;
655
+ return;
656
+ } else {
657
+ for (var frame in ChromeDriver.tabs[tab].frames) {
658
+ var theFrame = ChromeDriver.tabs[tab].frames[frame];
659
+ if (theFrame.isFrameset === undefined) {
660
+ theFrame.isFrameset = response.isFrameset;
661
+ return;
662
+ }
663
+ }
664
+ }
665
+ }
666
+ break;
667
+ case "getFrameNameFromIndex":
668
+ var newName = message.response.value.name;
669
+ if (ChromeDriver.restOfCurrentFramePath.length != 0) {
670
+ newName += "." + ChromeDriver.restOfCurrentFramePath.join(".");
671
+ }
672
+ switchToFrameByName(newName);
673
+ break;
674
+ }
675
+ break;
676
+ }
677
+ }
678
+
679
+ /**
680
+ * If the plugin doesn't currently have an HWND for this page,
681
+ * we need to get one by injecting an embed
682
+ */
683
+ function wrapInjectEmbedIfNecessary(requestObject) {
684
+ if (ChromeDriver.hasHwnd) {
685
+ var sequenceNumber = ChromeDriver.requestSequenceNumber;
686
+ ChromeDriver.requestSequenceNumber++;
687
+ return {
688
+ sequenceNumber: sequenceNumber,
689
+ request: requestObject
690
+ };
691
+ } else {
692
+ var wrappedObject = {
693
+ sequenceNumber: ChromeDriver.requestSequenceNumber,
694
+ request: {
695
+ request: "injectEmbed",
696
+ followup: {
697
+ sequenceNumber: ChromeDriver.requestSequenceNumber + 1,
698
+ request: requestObject
699
+ }
700
+ }
701
+ };
702
+ ChromeDriver.requestSequenceNumber += 2;
703
+ return wrappedObject;
704
+ }
705
+ }
706
+
707
+ /**
708
+ * Gets all current window handles
709
+ * @return an array containing all of the current window handles
710
+ */
711
+ function getWindowHandles() {
712
+ var windowHandles = [];
713
+ for (var tab in ChromeDriver.tabs) {
714
+ windowHandles.push(ChromeDriver.tabs[tab].windowName);
715
+ }
716
+ return {statusCode: 0, value: windowHandles}
717
+ }
718
+
719
+ function resetActiveTabDetails() {
720
+ ChromeDriver.activePort = null;
721
+ ChromeDriver.hasHwnd = false;
722
+ ChromeDriver.activeTabId = null;
723
+ ChromeDriver.doFocusOnNextOpenedTab = true;
724
+ ChromeDriver.hasSentResponseToThisPageLoading = false;
725
+ ChromeDriver.portToUseForFrameLookups = null;
726
+ ChromeDriver.currentUrl = null;
727
+ resetCurrentlyWaitingOnContentScriptTime();
728
+ }
729
+
730
+ function setActiveTabDetails(tab) {
731
+ ChromeDriver.activeTabId = tab.id;
732
+ ChromeDriver.activeWindowId = tab.windowId;
733
+ ChromeDriver.doFocusOnNextOpenedTab = false;
734
+ ChromeDriver.currentUrl = tab.url;
735
+ resetCurrentlyWaitingOnContentScriptTime();
736
+ }
737
+
738
+ function switchToDefaultContent() {
739
+ ChromeDriver.hasHwnd = false;
740
+ for (var tab in ChromeDriver.tabs) {
741
+ if (ChromeDriver.tabs[tab].tabId == ChromeDriver.activeTabId) {
742
+ if (ChromeDriver.tabs[tab].isFrameset) {
743
+ ChromeDriver.isBlockedWaitingForResponse = false;
744
+ parseRequest({request: 'switchToFrameByIndex', index: 0});
745
+ } else {
746
+ ChromeDriver.activePort = ChromeDriver.tabs[tab].mainPort;
747
+ sendResponseToParsedRequest({statusCode: 0}, false);
748
+ }
749
+ return;
750
+ }
751
+ }
752
+ }
753
+
754
+ function switchToFrame(name, index) {
755
+ ChromeDriver.hasHwnd = false;
756
+ for (var tab in ChromeDriver.tabs) {
757
+ if (ChromeDriver.tabs[tab].tabId == ChromeDriver.activeTabId) {
758
+ ChromeDriver.portToUseForFrameLookups = ChromeDriver.tabs[tab].mainPort;
759
+ break;
760
+ }
761
+ }
762
+ if (name !== undefined && name != null) {
763
+ switchToFrameByName(name);
764
+ } else if (index !== undefined && index != null) {
765
+ getFrameNameFromIndex(index);
766
+ } else {
767
+ sendResponseToParsedRequest({
768
+ statusCode: 9,
769
+ value: {
770
+ message: "Switching frames other than by name or id is unsupported"
771
+ }
772
+ });
773
+ }
774
+ }
775
+
776
+ function switchToFrameByName(name) {
777
+ var names = name.split(".");
778
+
779
+ for (var tab in ChromeDriver.tabs) {
780
+ if (ChromeDriver.tabs[tab].tabId == ChromeDriver.activeTabId) {
781
+ var frame;
782
+ for (frame in ChromeDriver.tabs[tab].frames) {
783
+ // Maybe name was a fully qualified name, which perhaps just happened to
784
+ // include .s
785
+ if (ChromeDriver.tabs[tab].frames[frame].frameName == name) {
786
+ ChromeDriver.activePort =
787
+ ChromeDriver.tabs[tab].frames[frame].framePort;
788
+ ChromeDriver.restOfCurrentFramePath = [];
789
+ sendResponseToParsedRequest({statusCode: 0}, false);
790
+ return;
791
+ }
792
+ }
793
+ for (frame in ChromeDriver.tabs[tab].frames) {
794
+ // Maybe we're looking for a child, see if this is the parent of it
795
+ if (ChromeDriver.tabs[tab].frames[frame].frameName == names[0]) {
796
+ ChromeDriver.activePort =
797
+ ChromeDriver.tabs[tab].frames[frame].framePort;
798
+ ChromeDriver.portToUseForFrameLookups = ChromeDriver.activePort;
799
+ names.shift();
800
+ ChromeDriver.restOfCurrentFramePath = names;
801
+ if (names.length == 0) {
802
+ sendResponseToParsedRequest({statusCode: 0}, false);
803
+ return;
804
+ } else {
805
+ switchToFrameByName(names.join("."));
806
+ return;
807
+ }
808
+ }
809
+ }
810
+ }
811
+ }
812
+
813
+ //Maybe the "name" was actually an index? Let's find out...
814
+ var index = null;
815
+ try {
816
+ index = parseInt(names[0]);
817
+ } catch (e) {
818
+ }
819
+ if (!isNaN(index)) {
820
+ names.shift();
821
+ ChromeDriver.restOfCurrentFramePath = names;
822
+ getFrameNameFromIndex(index);
823
+ return;
824
+ }
825
+
826
+ ChromeDriver.isBlockedWaitingForResponse = false;
827
+ parseRequest({request: 'switchToNamedIFrameIfOneExists', name: name});
828
+ }
829
+
830
+ function getFrameNameFromIndex(index) {
831
+ var message = {
832
+ request: {
833
+ request: "getFrameNameFromIndex",
834
+ index: index
835
+ },
836
+ sequenceNumber: ChromeDriver.requestSequenceNumber
837
+ };
838
+ ChromeDriver.requestSequenceNumber++;
839
+ ChromeDriver.portToUseForFrameLookups.postMessage(message);
840
+ }
841
+
842
+ /**
843
+ * Closes the current tab if it exists, and opens a new one, in which it
844
+ * gets the URL passed
845
+ * @param url the URL to load
846
+ */
847
+ function getUrl(url) {
848
+ ChromeDriver.urlBeingLoaded = url;
849
+ var tempActiveTagId = ChromeDriver.activeTabId;
850
+ if (url.indexOf("#") > -1 && ChromeDriver.currentUrl != null &&
851
+ ChromeDriver.currentUrl.split("#")[0] == url.split("#")[0]) {
852
+ ChromeDriver.isGettingUrlButOnlyChangingByFragment = true;
853
+ } else {
854
+ resetActiveTabDetails();
855
+ }
856
+ ChromeDriver.currentUrl = url;
857
+ if (tempActiveTagId == null) {
858
+ chrome.tabs.create({url: url, selected: true}, getUrlCallback);
859
+ } else {
860
+ ChromeDriver.activeTabId = tempActiveTagId;
861
+ if (ChromeDriver.isGettingUrlButOnlyChangingByFragment) {
862
+ chrome.tabs.update(ChromeDriver.activeTabId, {url: url, selected: true},
863
+ getUrlCallback);
864
+ } else {
865
+ // we need to create the new tab before deleting the old one
866
+ // in order to avoid hanging on OS X
867
+ var oldId = ChromeDriver.activeTabId;
868
+ resetActiveTabDetails();
869
+ chrome.tabs.create({url: url, selected: true}, getUrlCallback);
870
+ chrome.tabs.remove(oldId);
871
+ }
872
+ }
873
+ }
874
+
875
+ function getUrlCallback(tab) {
876
+ if (chrome.extension.lastError) {
877
+ // An error probably arose because Chrome didn't have a window yet
878
+ // (see crbug.com 19846)
879
+ // If we retry, we *should* be fine. Unless something really bad is
880
+ // happening, in which case we will probably hang indefinitely trying to
881
+ // reload the same URL
882
+ getUrl(ChromeDriver.urlBeingLoaded);
883
+ return;
884
+ }
885
+ if (tab == null) {
886
+ //chrome.tabs.update's callback doesn't pass a Tab argument,
887
+ //so we need to populate it ourselves
888
+ chrome.tabs.get(ChromeDriver.activeTabId, getUrlCallback);
889
+ return;
890
+ }
891
+ if (tab.status != "complete") {
892
+ // Use the helper calback so that we actually get updated version of the tab
893
+ // we're getting
894
+ setTimeout("getUrlCallbackById(" + tab.id + ")", 10);
895
+ } else {
896
+ ChromeDriver.getUrlRequestSequenceNumber++;
897
+ if (ChromeDriver.activePort == null) {
898
+ if (ChromeDriver.currentlyWaitingUntilGiveUpOnContentScriptLoading <= 0) {
899
+ ChromeDriver.hasNoConnectionToPage = true;
900
+ sendEmptyResponseWhenTabIsLoaded(tab);
901
+ } else {
902
+ ChromeDriver.currentlyWaitingUntilGiveUpOnContentScriptLoading -=
903
+ ChromeDriver.waitForContentScriptIncrement;
904
+ setTimeout("getUrlCallbackById(" + tab.id + ")", ChromeDriver.waitForContentScriptIncrement);
905
+ return;
906
+ }
907
+ }
908
+ setActiveTabDetails(tab);
909
+ }
910
+ if (ChromeDriver.isGettingUrlButOnlyChangingByFragment) {
911
+ ChromeDriver.urlBeingLoaded = null;
912
+ resetCurrentlyWaitingOnContentScriptTime();
913
+ sendResponseToParsedRequest({statusCode: 0}, false);
914
+ ChromeDriver.isGettingUrlButOnlyChangingByFragment = false;
915
+ }
916
+ }
917
+
918
+ function getUrlCallbackById(tabId) {
919
+ chrome.tabs.get(tabId, getUrlCallback);
920
+ }
921
+
922
+ function sendEmptyResponseWhenTabIsLoaded(tab) {
923
+ if (tab.status == "complete") {
924
+ if (ChromeDriver.activePort) {
925
+ ChromeDriver.isBlockedWaitingForResponse = false;
926
+ parseRequest({request: 'sniffForMetaRedirects'});
927
+ } else {
928
+ if (!ChromeDriver.hasSentResponseToThisPageLoading) {
929
+ ChromeDriver.urlBeingLoaded = null;
930
+ sendResponseToParsedRequest({statusCode: 0}, false);
931
+ }
932
+ }
933
+ } else {
934
+ chrome.tabs.get(tab.id, sendEmptyResponseWhenTabIsLoaded);
935
+ }
936
+ }
937
+
938
+
939
+ function setExtensionBusyIndicator(busy) {
940
+ if (busy) {
941
+ chrome.browserAction.setIcon({path: "icons/busy.png"})
942
+ } else {
943
+ chrome.browserAction.setIcon({path: "icons/free.png"})
944
+ }
945
+ }
946
+
947
+ function setActivePortByWindowName(handle) {
948
+ for (var tab in ChromeDriver.tabs) {
949
+ if (ChromeDriver.tabs[tab].windowName == handle ||
950
+ ChromeDriver.tabs[tab].mainPort.name == handle ||
951
+ ChromeDriver.tabs[tab].tabId.toString() == handle) {
952
+ ChromeDriver.activePort = ChromeDriver.tabs[tab].mainPort;
953
+ chrome.tabs.get(ChromeDriver.tabs[tab].tabId, setActiveTabDetails);
954
+ chrome.tabs.update(ChromeDriver.tabs[tab].tabId, {selected: true});
955
+ sendResponseToParsedRequest({statusCode: 0}, false);
956
+ return;
957
+ }
958
+ }
959
+ sendResponseToParsedRequest({statusCode: 3, value: {message: 'Could not find window to switch to by handle: ' + handle}}, false);
960
+ }
961
+
962
+
963
+ /**
964
+ * @return {boolean} Whether there is currently no active page.
965
+ */
966
+ function hasNoPage() {
967
+ return ChromeDriver.hasNoConnectionToPage ||
968
+ ChromeDriver.activePort == null ||
969
+ ChromeDriver.activeTabId == null;
970
+ }
971
+
972
+ function resetCurrentlyWaitingOnContentScriptTime() {
973
+ ChromeDriver.currentlyWaitingUntilGiveUpOnContentScriptLoading =
974
+ ChromeDriver.timeoutUntilGiveUpOnContentScriptLoading;
975
+ }