selenium-webdriver 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/chrome/prebuilt/Win32/Release/npchromedriver.dll +0 -0
  2. data/chrome/prebuilt/x64/Release/npchromedriver.dll +0 -0
  3. data/chrome/src/extension/background.html +9 -0
  4. data/chrome/src/extension/background.js +933 -0
  5. data/chrome/src/extension/content_script.js +1286 -0
  6. data/chrome/src/extension/manifest-nonwin.json +15 -0
  7. data/chrome/src/extension/manifest-win.json +16 -0
  8. data/chrome/src/extension/toolstrip.html +28 -0
  9. data/chrome/src/extension/utils.js +196 -0
  10. data/chrome/src/rb/lib/selenium/webdriver/chrome.rb +8 -0
  11. data/chrome/src/rb/lib/selenium/webdriver/chrome/bridge.rb +324 -0
  12. data/chrome/src/rb/lib/selenium/webdriver/chrome/command_executor.rb +70 -0
  13. data/chrome/src/rb/lib/selenium/webdriver/chrome/launcher.rb +119 -0
  14. data/common/src/js/abstractcommandprocessor.js +161 -0
  15. data/common/src/js/asserts.js +296 -0
  16. data/common/src/js/by.js +147 -0
  17. data/common/src/js/command.js +274 -0
  18. data/common/src/js/context.js +58 -0
  19. data/common/src/js/extension/README +2 -0
  20. data/common/src/js/extension/dommessenger.js +152 -0
  21. data/common/src/js/factory.js +55 -0
  22. data/common/src/js/future.js +118 -0
  23. data/common/src/js/key.js +117 -0
  24. data/common/src/js/localcommandprocessor.js +181 -0
  25. data/common/src/js/logging.js +249 -0
  26. data/common/src/js/testrunner.js +605 -0
  27. data/common/src/js/timing.js +89 -0
  28. data/common/src/js/wait.js +199 -0
  29. data/common/src/js/webdriver.js +853 -0
  30. data/common/src/js/webelement.js +683 -0
  31. data/common/src/rb/lib/selenium-webdriver.rb +1 -0
  32. data/common/src/rb/lib/selenium/webdriver.rb +52 -0
  33. data/common/src/rb/lib/selenium/webdriver/bridge_helper.rb +88 -0
  34. data/common/src/rb/lib/selenium/webdriver/child_process.rb +85 -0
  35. data/common/src/rb/lib/selenium/webdriver/core_ext/dir.rb +41 -0
  36. data/common/src/rb/lib/selenium/webdriver/driver.rb +128 -0
  37. data/common/src/rb/lib/selenium/webdriver/element.rb +126 -0
  38. data/common/src/rb/lib/selenium/webdriver/error.rb +68 -0
  39. data/common/src/rb/lib/selenium/webdriver/find.rb +69 -0
  40. data/common/src/rb/lib/selenium/webdriver/navigation.rb +23 -0
  41. data/common/src/rb/lib/selenium/webdriver/options.rb +50 -0
  42. data/common/src/rb/lib/selenium/webdriver/platform.rb +82 -0
  43. data/common/src/rb/lib/selenium/webdriver/target_locator.rb +23 -0
  44. data/firefox/prebuilt/nsICommandProcessor.xpt +0 -0
  45. data/firefox/prebuilt/nsINativeEvents.xpt +0 -0
  46. data/firefox/prebuilt/nsIResponseHandler.xpt +0 -0
  47. data/firefox/src/extension/chrome.manifest +3 -0
  48. data/firefox/src/extension/components/context.js +37 -0
  49. data/firefox/src/extension/components/driver-component.js +127 -0
  50. data/firefox/src/extension/components/firefoxDriver.js +706 -0
  51. data/firefox/src/extension/components/json2.js +273 -0
  52. data/firefox/src/extension/components/keytest.html +554 -0
  53. data/firefox/src/extension/components/nsCommandProcessor.js +586 -0
  54. data/firefox/src/extension/components/screenshooter.js +70 -0
  55. data/firefox/src/extension/components/socketListener.js +185 -0
  56. data/firefox/src/extension/components/utils.js +1200 -0
  57. data/firefox/src/extension/components/webLoadingListener.js +57 -0
  58. data/firefox/src/extension/components/webdriverserver.js +101 -0
  59. data/firefox/src/extension/components/wrappedElement.js +609 -0
  60. data/firefox/src/extension/content/fxdriver.xul +30 -0
  61. data/firefox/src/extension/content/server.js +95 -0
  62. data/firefox/src/extension/idl/nsICommandProcessor.idl +38 -0
  63. data/firefox/src/extension/idl/nsIResponseHandler.idl +34 -0
  64. data/firefox/src/extension/install.rdf +29 -0
  65. data/firefox/src/rb/lib/selenium/webdriver/firefox.rb +21 -0
  66. data/firefox/src/rb/lib/selenium/webdriver/firefox/binary.rb +86 -0
  67. data/firefox/src/rb/lib/selenium/webdriver/firefox/bridge.rb +426 -0
  68. data/firefox/src/rb/lib/selenium/webdriver/firefox/extension_connection.rb +82 -0
  69. data/firefox/src/rb/lib/selenium/webdriver/firefox/launcher.rb +132 -0
  70. data/firefox/src/rb/lib/selenium/webdriver/firefox/profile.rb +174 -0
  71. data/firefox/src/rb/lib/selenium/webdriver/firefox/profiles_ini.rb +60 -0
  72. data/firefox/src/rb/lib/selenium/webdriver/firefox/util.rb +23 -0
  73. data/jobbie/prebuilt/Win32/Release/InternetExplorerDriver.dll +0 -0
  74. data/jobbie/prebuilt/x64/Release/InternetExplorerDriver.dll +0 -0
  75. data/jobbie/src/rb/lib/selenium/webdriver/ie.rb +14 -0
  76. data/jobbie/src/rb/lib/selenium/webdriver/ie/bridge.rb +552 -0
  77. data/jobbie/src/rb/lib/selenium/webdriver/ie/lib.rb +94 -0
  78. data/jobbie/src/rb/lib/selenium/webdriver/ie/util.rb +147 -0
  79. data/remote/client/src/rb/lib/selenium/webdriver/remote.rb +16 -0
  80. data/remote/client/src/rb/lib/selenium/webdriver/remote/bridge.rb +374 -0
  81. data/remote/client/src/rb/lib/selenium/webdriver/remote/capabilities.rb +105 -0
  82. data/remote/client/src/rb/lib/selenium/webdriver/remote/commands.rb +53 -0
  83. data/remote/client/src/rb/lib/selenium/webdriver/remote/default_http_client.rb +71 -0
  84. data/remote/client/src/rb/lib/selenium/webdriver/remote/response.rb +43 -0
  85. data/remote/client/src/rb/lib/selenium/webdriver/remote/server_error.rb +32 -0
  86. metadata +182 -0
@@ -0,0 +1,9 @@
1
+ <html>
2
+ <head>
3
+ <title>WebDriver</title>
4
+ <script type="text/javascript" src="background.js"></script>
5
+ </head>
6
+ <body>
7
+ <p><embed type="application/x-chromedriver" /></p>
8
+ </body>
9
+ </html>
@@ -0,0 +1,933 @@
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
+ setToolstripsBusy(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
+ setToolstripsBusy(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 "clickElement":
433
+ case "hoverOverElement":
434
+ // Falling through, as native events are handled the same
435
+ case "sendKeysToElement":
436
+ if (typeof(request.keys) == "object" && request.keys.length !== undefined) {
437
+ request.keys = request.keys.join("");
438
+ }
439
+ sendMessageOnActivePortAndAlsoKeepTrackOfIt(
440
+ wrapInjectEmbedIfNecessary(request));
441
+ break;
442
+ case "getCurrentUrl":
443
+ case "getTitle":
444
+ if (hasNoPage()) {
445
+ console.log("Not got a page, but asked for string, so sending empty string");
446
+ sendResponseToParsedRequest({statusCode: 0, value: ''});
447
+ break;
448
+ }
449
+ // Falling through, as if we do have a page, we want to treat this like a
450
+ // normal request
451
+ case "findElement":
452
+ case "findChildElement":
453
+ if (hasNoPage()) {
454
+ console.log("Not got a page, but asked for element, so throwing NoSuchElementException");
455
+ sendResponseToParsedRequest({statusCode: 7, value: {message: 'Was not on a page, so could not find elements'}});
456
+ break;
457
+ }
458
+ // Falling through, as if we do have a page, we want to treat this like a
459
+ // normal request
460
+ case "findElements":
461
+ case "findChildElements":
462
+ if (hasNoPage()) {
463
+ console.log("Not got a page, but asked for elements, so returning no elements");
464
+ sendResponseToParsedRequest({statusCode: 0, value: []});
465
+ break;
466
+ }
467
+ // Falling through, as if we do have a page, we want to treat this like a
468
+ // normal request
469
+ case "deleteAllCookies":
470
+ case "deleteCookie":
471
+ if (hasNoPage()) {
472
+ console.log("Not got a page, but asked to delete cookies, so returning ok");
473
+ sendResponseToParsedRequest({statusCode: 0});
474
+ break;
475
+ }
476
+ // Falling through, as if we do have a page, we want to treat this like a
477
+ // normal request
478
+ default:
479
+ var sequenceNumber = ChromeDriver.requestSequenceNumber;
480
+ ChromeDriver.requestSequenceNumber++;
481
+ sendMessageOnActivePortAndAlsoKeepTrackOfIt({
482
+ request: request,
483
+ sequenceNumber: sequenceNumber
484
+ });
485
+ break;
486
+ }
487
+ }
488
+
489
+
490
+ function sendMessageOnActivePortAndAlsoKeepTrackOfIt(message) {
491
+ ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet = message.request;
492
+ try {
493
+ ChromeDriver.activePort.postMessage(message);
494
+ } catch (e) {
495
+ console.log("Tried to send request without an active port. " +
496
+ "Request will retry when connected, but will hang until then.");
497
+ }
498
+ }
499
+
500
+ /**
501
+ * Parse messages coming in on the port (responses from the content script).
502
+ * @param message JSON message of format:
503
+ * {response: "some command",
504
+ * value: {statusCode: STATUS_CODE
505
+ * [, optional params]}}
506
+ */
507
+ function parsePortMessage(message) {
508
+ console.log(
509
+ "Received response from content script: " + JSON.stringify(message));
510
+ if (!message || !message.response || !message.response.value ||
511
+ message.response.value.statusCode === undefined ||
512
+ message.response.value.statusCode == null ||
513
+ message.sequenceNumber === undefined) {
514
+ // Should only ever happen if we sent a bad request,
515
+ // or the content script is broken
516
+ console.log("Got invalid response from the content script.");
517
+ return;
518
+ }
519
+ var toSend = {statusCode: 12};
520
+ ChromeDriver.lastRequestToBeSentWhichHasntBeenAnsweredYet = null;
521
+ switch (message.response.value.statusCode) {
522
+ // Error codes are loosely based on native exception codes, see
523
+ // common/src/cpp/webdriver-interactions/errorcodes.h
524
+ case 0:
525
+ case 2: //org.openqa.selenium.WebDriverException [Cookies]
526
+ case 3: //org.openqa.selenium.NoSuchWindowException
527
+ case 7: //org.openqa.selenium.NoSuchElementException
528
+ case 8: //org.openqa.selenium.NoSuchFrameException
529
+ case 9: //java.lang.UnsupportedOperationException [Unknown command]
530
+ case 10: //org.openqa.selenium.StaleElementReferenceException
531
+ case 11: //org.openqa.selenium.ElementNotVisibleException
532
+ case 12: //java.lang.UnsupportedOperationException [Invalid element state ]
533
+ case 17: //org.openqa.selenium.WebDriverException [Bad javascript]
534
+ case 19: //org.openqa.selenium.XPathLookupException
535
+ case 99: //org.openqa.selenium.WebDriverException [Native event]
536
+ toSend = {statusCode: message.response.value.statusCode, value: null};
537
+ if (message.response.value !== undefined && message.response.value != null &&
538
+ message.response.value.value !== undefined) {
539
+ toSend.value = message.response.value.value;
540
+ }
541
+ sendResponseToParsedRequest(toSend, message.response.wait);
542
+ break;
543
+ case "no-op":
544
+ //Some special operation which isn't sending HTTP
545
+ switch (message.response.response) {
546
+ case "clickElement":
547
+ try {
548
+ if (document.embeds[0].clickAt(message.response.value.x, message.response.value.y)) {
549
+ sendResponseToParsedRequest({statusCode: 0}, true);
550
+ } else {
551
+ sendResponseToParsedRequest({statusCode: 99}, true);
552
+ }
553
+ } catch(e) {
554
+ console.log("Error natively clicking. Trying non-native.");
555
+ ChromeDriver.isBlockedWaitingForResponse = false;
556
+ parseRequest({
557
+ request: 'nonNativeClickElement',
558
+ elementId: message.response.value.elementId
559
+ });
560
+ }
561
+ break;
562
+ case "hoverElement":
563
+ try {
564
+ var points = message.response.value;
565
+ if (document.embeds[0].mouseMoveTo(15, points.oldX, points.oldY, points.newX, points.newY)) {
566
+ sendResponseToParsedRequest({statusCode: 0}, true);
567
+ } else {
568
+ sendResponseToParsedRequest({statusCode: 99}, true);
569
+ }
570
+ } catch(e) {
571
+ sendResponseToParsedRequest({statusCode: 99}, true);
572
+ }
573
+ break;
574
+ case "sendKeysToElement":
575
+ try {
576
+ if (document.embeds[0].sendKeys(message.response.value.keys)) {
577
+ sendResponseToParsedRequest({statusCode: 0}, true);
578
+ } else {
579
+ sendResponseToParsedRequest({statusCode: 99}, true);
580
+ }
581
+ } catch(e) {
582
+ console.log("Error natively sending keys. Trying non-native.");
583
+ ChromeDriver.isBlockedWaitingForResponse = false;
584
+ parseRequest({
585
+ request: 'sendElementNonNativeKeys',
586
+ elementId: message.response.value.elementId,
587
+ keys: message.response.value.keys
588
+ });
589
+ }
590
+ break;
591
+ case "sniffForMetaRedirects":
592
+ if (!message.response.value.value &&
593
+ !ChromeDriver.hasSentResponseToThisPageLoading) {
594
+ ChromeDriver.urlBeingLoaded = null;
595
+ ChromeDriver.hasSentResponseToThisPageLoading = true;
596
+ switchToDefaultContent();
597
+ }
598
+ break;
599
+ case "newTabInformation":
600
+ var response = message.response.value;
601
+ for (var tab in ChromeDriver.tabs) {
602
+ //RACE CONDITION!!!
603
+ //This call should happen before another content script
604
+ //connects and returns this value,
605
+ //but if it doesn't, we may get mismatched information
606
+ if (ChromeDriver.tabs[tab].isFrameset === undefined) {
607
+ ChromeDriver.tabs[tab].isFrameset = response.isFrameset;
608
+ return;
609
+ } else {
610
+ for (var frame in ChromeDriver.tabs[tab].frames) {
611
+ var theFrame = ChromeDriver.tabs[tab].frames[frame];
612
+ if (theFrame.isFrameset === undefined) {
613
+ theFrame.isFrameset = response.isFrameset;
614
+ return;
615
+ }
616
+ }
617
+ }
618
+ }
619
+ break;
620
+ case "getFrameNameFromIndex":
621
+ var newName = message.response.value.name;
622
+ if (ChromeDriver.restOfCurrentFramePath.length != 0) {
623
+ newName += "." + ChromeDriver.restOfCurrentFramePath.join(".");
624
+ }
625
+ switchToFrameByName(newName);
626
+ break;
627
+ }
628
+ break;
629
+ }
630
+ }
631
+
632
+ /**
633
+ * If the plugin doesn't currently have an HWND for this page,
634
+ * we need to get one by injecting an embed
635
+ */
636
+ function wrapInjectEmbedIfNecessary(requestObject) {
637
+ if (ChromeDriver.hasHwnd) {
638
+ var sequenceNumber = ChromeDriver.requestSequenceNumber;
639
+ ChromeDriver.requestSequenceNumber++;
640
+ return {
641
+ sequenceNumber: sequenceNumber,
642
+ request: requestObject
643
+ };
644
+ } else {
645
+ var wrappedObject = {
646
+ sequenceNumber: ChromeDriver.requestSequenceNumber,
647
+ request: {
648
+ request: "injectEmbed",
649
+ followup: {
650
+ sequenceNumber: ChromeDriver.requestSequenceNumber + 1,
651
+ request: requestObject
652
+ }
653
+ }
654
+ };
655
+ ChromeDriver.requestSequenceNumber += 2;
656
+ return wrappedObject;
657
+ }
658
+ }
659
+
660
+ /**
661
+ * Gets all current window handles
662
+ * @return an array containing all of the current window handles
663
+ */
664
+ function getWindowHandles() {
665
+ var windowHandles = [];
666
+ for (var tab in ChromeDriver.tabs) {
667
+ windowHandles.push(ChromeDriver.tabs[tab].windowName);
668
+ }
669
+ return {statusCode: 0, value: windowHandles}
670
+ }
671
+
672
+ function resetActiveTabDetails() {
673
+ ChromeDriver.activePort = null;
674
+ ChromeDriver.hasHwnd = false;
675
+ ChromeDriver.activeTabId = null;
676
+ ChromeDriver.doFocusOnNextOpenedTab = true;
677
+ ChromeDriver.hasSentResponseToThisPageLoading = false;
678
+ ChromeDriver.portToUseForFrameLookups = null;
679
+ ChromeDriver.currentUrl = null;
680
+ resetCurrentlyWaitingOnContentScriptTime();
681
+ }
682
+
683
+ function setActiveTabDetails(tab) {
684
+ ChromeDriver.activeTabId = tab.id;
685
+ ChromeDriver.activeWindowId = tab.windowId;
686
+ ChromeDriver.doFocusOnNextOpenedTab = false;
687
+ ChromeDriver.currentUrl = tab.url;
688
+ resetCurrentlyWaitingOnContentScriptTime();
689
+ }
690
+
691
+ function switchToDefaultContent() {
692
+ ChromeDriver.hasHwnd = false;
693
+ for (var tab in ChromeDriver.tabs) {
694
+ if (ChromeDriver.tabs[tab].tabId == ChromeDriver.activeTabId) {
695
+ if (ChromeDriver.tabs[tab].isFrameset) {
696
+ ChromeDriver.isBlockedWaitingForResponse = false;
697
+ parseRequest({request: 'switchToFrameByIndex', index: 0});
698
+ } else {
699
+ ChromeDriver.activePort = ChromeDriver.tabs[tab].mainPort;
700
+ sendResponseToParsedRequest({statusCode: 0}, false);
701
+ }
702
+ return;
703
+ }
704
+ }
705
+ }
706
+
707
+ function switchToFrame(name, index) {
708
+ ChromeDriver.hasHwnd = false;
709
+ for (var tab in ChromeDriver.tabs) {
710
+ if (ChromeDriver.tabs[tab].tabId == ChromeDriver.activeTabId) {
711
+ ChromeDriver.portToUseForFrameLookups = ChromeDriver.tabs[tab].mainPort;
712
+ break;
713
+ }
714
+ }
715
+ if (name !== undefined && name != null) {
716
+ switchToFrameByName(name);
717
+ } else if (index !== undefined && index != null) {
718
+ getFrameNameFromIndex(index);
719
+ } else {
720
+ sendResponseToParsedRequest({
721
+ statusCode: 9,
722
+ value: {
723
+ message: "Switching frames other than by name or id is unsupported"
724
+ }
725
+ });
726
+ }
727
+ }
728
+
729
+ function switchToFrameByName(name) {
730
+ var names = name.split(".");
731
+
732
+ for (var tab in ChromeDriver.tabs) {
733
+ if (ChromeDriver.tabs[tab].tabId == ChromeDriver.activeTabId) {
734
+ var frame;
735
+ for (frame in ChromeDriver.tabs[tab].frames) {
736
+ // Maybe name was a fully qualified name, which perhaps just happened to
737
+ // include .s
738
+ if (ChromeDriver.tabs[tab].frames[frame].frameName == name) {
739
+ ChromeDriver.activePort =
740
+ ChromeDriver.tabs[tab].frames[frame].framePort;
741
+ ChromeDriver.restOfCurrentFramePath = [];
742
+ sendResponseToParsedRequest({statusCode: 0}, false);
743
+ return;
744
+ }
745
+ }
746
+ for (frame in ChromeDriver.tabs[tab].frames) {
747
+ // Maybe we're looking for a child, see if this is the parent of it
748
+ if (ChromeDriver.tabs[tab].frames[frame].frameName == names[0]) {
749
+ ChromeDriver.activePort =
750
+ ChromeDriver.tabs[tab].frames[frame].framePort;
751
+ ChromeDriver.portToUseForFrameLookups = ChromeDriver.activePort;
752
+ names.shift();
753
+ ChromeDriver.restOfCurrentFramePath = names;
754
+ if (names.length == 0) {
755
+ sendResponseToParsedRequest({statusCode: 0}, false);
756
+ return;
757
+ } else {
758
+ switchToFrameByName(names.join("."));
759
+ return;
760
+ }
761
+ }
762
+ }
763
+ }
764
+ }
765
+
766
+ //Maybe the "name" was actually an index? Let's find out...
767
+ var index = null;
768
+ try {
769
+ index = parseInt(names[0]);
770
+ } catch (e) {
771
+ }
772
+ if (!isNaN(index)) {
773
+ names.shift();
774
+ ChromeDriver.restOfCurrentFramePath = names;
775
+ getFrameNameFromIndex(index);
776
+ return;
777
+ }
778
+
779
+ ChromeDriver.isBlockedWaitingForResponse = false;
780
+ parseRequest({request: 'switchToNamedIFrameIfOneExists', name: name});
781
+ }
782
+
783
+ function getFrameNameFromIndex(index) {
784
+ var message = {
785
+ request: {
786
+ request: "getFrameNameFromIndex",
787
+ index: index
788
+ },
789
+ sequenceNumber: ChromeDriver.requestSequenceNumber
790
+ };
791
+ ChromeDriver.requestSequenceNumber++;
792
+ ChromeDriver.portToUseForFrameLookups.postMessage(message);
793
+ }
794
+
795
+ /**
796
+ * Closes the current tab if it exists, and opens a new one, in which it
797
+ * gets the URL passed
798
+ * @param url the URL to load
799
+ */
800
+ function getUrl(url) {
801
+ ChromeDriver.urlBeingLoaded = url;
802
+ var tempActiveTagId = ChromeDriver.activeTabId;
803
+ if (url.indexOf("#") > -1 && ChromeDriver.currentUrl != null &&
804
+ ChromeDriver.currentUrl.split("#")[0] == url.split("#")[0]) {
805
+ ChromeDriver.isGettingUrlButOnlyChangingByFragment = true;
806
+ } else {
807
+ resetActiveTabDetails();
808
+ }
809
+ ChromeDriver.currentUrl = url;
810
+ if (tempActiveTagId == null) {
811
+ chrome.tabs.create({url: url, selected: true}, getUrlCallback);
812
+ } else {
813
+ ChromeDriver.activeTabId = tempActiveTagId;
814
+ if (ChromeDriver.isGettingUrlButOnlyChangingByFragment) {
815
+ chrome.tabs.update(ChromeDriver.activeTabId, {url: url, selected: true},
816
+ getUrlCallback);
817
+ } else {
818
+ // we need to create the new tab before deleting the old one
819
+ // in order to avoid hanging on OS X
820
+ var oldId = ChromeDriver.activeTabId;
821
+ resetActiveTabDetails();
822
+ chrome.tabs.create({url: url, selected: true}, getUrlCallback);
823
+ chrome.tabs.remove(oldId);
824
+ }
825
+ }
826
+ }
827
+
828
+ function getUrlCallback(tab) {
829
+ if (chrome.extension.lastError) {
830
+ // An error probably arose because Chrome didn't have a window yet
831
+ // (see crbug.com 19846)
832
+ // If we retry, we *should* be fine. Unless something really bad is
833
+ // happening, in which case we will probably hang indefinitely trying to
834
+ // reload the same URL
835
+ getUrl(ChromeDriver.urlBeingLoaded);
836
+ return;
837
+ }
838
+ if (tab == null) {
839
+ //chrome.tabs.update's callback doesn't pass a Tab argument,
840
+ //so we need to populate it ourselves
841
+ chrome.tabs.get(ChromeDriver.activeTabId, getUrlCallback);
842
+ return;
843
+ }
844
+ if (tab.status != "complete") {
845
+ // Use the helper calback so that we actually get updated version of the tab
846
+ // we're getting
847
+ setTimeout("getUrlCallbackById(" + tab.id + ")", 10);
848
+ } else {
849
+ ChromeDriver.getUrlRequestSequenceNumber++;
850
+ if (ChromeDriver.activePort == null) {
851
+ if (ChromeDriver.currentlyWaitingUntilGiveUpOnContentScriptLoading <= 0) {
852
+ ChromeDriver.hasNoConnectionToPage = true;
853
+ sendEmptyResponseWhenTabIsLoaded(tab);
854
+ } else {
855
+ ChromeDriver.currentlyWaitingUntilGiveUpOnContentScriptLoading -=
856
+ ChromeDriver.waitForContentScriptIncrement;
857
+ setTimeout("getUrlCallbackById(" + tab.id + ")", ChromeDriver.waitForContentScriptIncrement);
858
+ return;
859
+ }
860
+ }
861
+ setActiveTabDetails(tab);
862
+ }
863
+ if (ChromeDriver.isGettingUrlButOnlyChangingByFragment) {
864
+ ChromeDriver.urlBeingLoaded = null;
865
+ resetCurrentlyWaitingOnContentScriptTime();
866
+ sendResponseToParsedRequest({statusCode: 0}, false);
867
+ ChromeDriver.isGettingUrlButOnlyChangingByFragment = false;
868
+ }
869
+ }
870
+
871
+ function getUrlCallbackById(tabId) {
872
+ chrome.tabs.get(tabId, getUrlCallback);
873
+ }
874
+
875
+ function sendEmptyResponseWhenTabIsLoaded(tab) {
876
+ if (tab.status == "complete") {
877
+ if (ChromeDriver.activePort) {
878
+ ChromeDriver.isBlockedWaitingForResponse = false;
879
+ parseRequest({request: 'sniffForMetaRedirects'});
880
+ } else {
881
+ if (!ChromeDriver.hasSentResponseToThisPageLoading) {
882
+ ChromeDriver.urlBeingLoaded = null;
883
+ sendResponseToParsedRequest({statusCode: 0}, false);
884
+ }
885
+ }
886
+ } else {
887
+ chrome.tabs.get(tab.id, sendEmptyResponseWhenTabIsLoaded);
888
+ }
889
+ }
890
+
891
+
892
+ function setToolstripsBusy(busy) {
893
+ var toolstrips = chrome.extension.getToolstrips(ChromeDriver.activeWindowId);
894
+ for (var toolstrip in toolstrips) {
895
+ if (toolstrips[toolstrip].setWebdriverToolstripBusy &&
896
+ toolstrips[toolstrip].setWebdriverToolstripFree) {
897
+ if (busy) {
898
+ toolstrips[toolstrip].setWebdriverToolstripBusy();
899
+ } else {
900
+ toolstrips[toolstrip].setWebdriverToolstripFree();
901
+ }
902
+ }
903
+ }
904
+ }
905
+
906
+ function setActivePortByWindowName(handle) {
907
+ for (var tab in ChromeDriver.tabs) {
908
+ if (ChromeDriver.tabs[tab].windowName == handle ||
909
+ ChromeDriver.tabs[tab].mainPort.name == handle) {
910
+ ChromeDriver.activePort = ChromeDriver.tabs[tab].mainPort;
911
+ chrome.tabs.get(ChromeDriver.tabs[tab].tabId, setActiveTabDetails);
912
+ chrome.tabs.update(ChromeDriver.tabs[tab].tabId, {selected: true});
913
+ sendResponseToParsedRequest({statusCode: 0}, false);
914
+ return;
915
+ }
916
+ }
917
+ sendResponseToParsedRequest({statusCode: 3, value: {message: 'Could not find window to switch to by handle: ' + handle}}, false);
918
+ }
919
+
920
+
921
+ /**
922
+ * @return {boolean} Whether there is currently no active page.
923
+ */
924
+ function hasNoPage() {
925
+ return ChromeDriver.hasNoConnectionToPage ||
926
+ ChromeDriver.activePort == null ||
927
+ ChromeDriver.activeTabId == null;
928
+ }
929
+
930
+ function resetCurrentlyWaitingOnContentScriptTime() {
931
+ ChromeDriver.currentlyWaitingUntilGiveUpOnContentScriptLoading =
932
+ ChromeDriver.timeoutUntilGiveUpOnContentScriptLoading;
933
+ }