selenium-webdriver 0.0.9 → 0.0.10

Sign up to get free protection for your applications and to get access to all the features.
Files changed (135) 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.js +1 -0
  4. data/chrome/src/extension/content_script.js +182 -177
  5. data/chrome/src/extension/manifest-nonwin.json +1 -1
  6. data/chrome/src/extension/manifest-win.json +1 -1
  7. data/chrome/src/rb/lib/selenium/webdriver/chrome/bridge.rb +9 -3
  8. data/chrome/src/rb/lib/selenium/webdriver/chrome/launcher.rb +1 -1
  9. data/common/src/js/core/Blank.html +7 -0
  10. data/common/src/js/core/InjectedRemoteRunner.html +8 -0
  11. data/common/src/js/core/RemoteRunner.html +101 -0
  12. data/common/src/js/core/SeleniumLog.html +109 -0
  13. data/common/src/js/core/TestPrompt.html +145 -0
  14. data/common/src/js/core/TestRunner-splash.html +55 -0
  15. data/common/src/js/core/TestRunner.html +177 -0
  16. data/common/src/js/core/icons/all.png +0 -0
  17. data/common/src/js/core/icons/continue.png +0 -0
  18. data/common/src/js/core/icons/continue_disabled.png +0 -0
  19. data/common/src/js/core/icons/pause.png +0 -0
  20. data/common/src/js/core/icons/pause_disabled.png +0 -0
  21. data/common/src/js/core/icons/selected.png +0 -0
  22. data/common/src/js/core/icons/step.png +0 -0
  23. data/common/src/js/core/icons/step_disabled.png +0 -0
  24. data/common/src/js/core/lib/cssQuery/cssQuery-p.js +6 -0
  25. data/common/src/js/core/lib/cssQuery/src/cssQuery-level2.js +142 -0
  26. data/common/src/js/core/lib/cssQuery/src/cssQuery-level3.js +150 -0
  27. data/common/src/js/core/lib/cssQuery/src/cssQuery-standard.js +53 -0
  28. data/common/src/js/core/lib/cssQuery/src/cssQuery.js +356 -0
  29. data/common/src/js/core/lib/prototype.js +2006 -0
  30. data/common/src/js/core/lib/scriptaculous/builder.js +101 -0
  31. data/common/src/js/core/lib/scriptaculous/controls.js +815 -0
  32. data/common/src/js/core/lib/scriptaculous/dragdrop.js +915 -0
  33. data/common/src/js/core/lib/scriptaculous/effects.js +958 -0
  34. data/common/src/js/core/lib/scriptaculous/scriptaculous.js +47 -0
  35. data/common/src/js/core/lib/scriptaculous/slider.js +283 -0
  36. data/common/src/js/core/lib/scriptaculous/unittest.js +383 -0
  37. data/common/src/js/core/lib/snapsie.js +91 -0
  38. data/common/src/js/core/scripts/find_matching_child.js +69 -0
  39. data/common/src/js/core/scripts/htmlutils.js +1623 -0
  40. data/common/src/js/core/scripts/injection.html +72 -0
  41. data/common/src/js/core/scripts/selenium-api.js +3294 -0
  42. data/common/src/js/core/scripts/selenium-browserbot.js +2430 -0
  43. data/common/src/js/core/scripts/selenium-browserdetect.js +153 -0
  44. data/common/src/js/core/scripts/selenium-commandhandlers.js +379 -0
  45. data/common/src/js/core/scripts/selenium-executionloop.js +175 -0
  46. data/common/src/js/core/scripts/selenium-logging.js +148 -0
  47. data/common/src/js/core/scripts/selenium-remoterunner.js +695 -0
  48. data/common/src/js/core/scripts/selenium-testrunner.js +1362 -0
  49. data/common/src/js/core/scripts/selenium-version.js +5 -0
  50. data/common/src/js/core/scripts/ui-doc.html +808 -0
  51. data/common/src/js/core/scripts/ui-element.js +1644 -0
  52. data/common/src/js/core/scripts/ui-map-sample.js +979 -0
  53. data/common/src/js/core/scripts/user-extensions.js +3 -0
  54. data/common/src/js/core/scripts/user-extensions.js.sample +75 -0
  55. data/common/src/js/core/scripts/xmlextras.js +153 -0
  56. data/common/src/js/core/selenium-logo.png +0 -0
  57. data/common/src/js/core/selenium-test.css +43 -0
  58. data/common/src/js/core/selenium.css +316 -0
  59. data/common/src/js/core/xpath/dom.js +566 -0
  60. data/common/src/js/core/xpath/javascript-xpath-0.1.11.js +2816 -0
  61. data/common/src/js/core/xpath/util.js +549 -0
  62. data/common/src/js/core/xpath/xmltoken.js +149 -0
  63. data/common/src/js/core/xpath/xpath.js +2481 -0
  64. data/common/src/js/jsunit/app/css/jsUnitStyle.css +50 -0
  65. data/common/src/js/jsunit/app/css/readme +10 -0
  66. data/common/src/js/jsunit/app/emptyPage.html +11 -0
  67. data/common/src/js/jsunit/app/jsUnitCore.js +534 -0
  68. data/common/src/js/jsunit/app/jsUnitMockTimeout.js +81 -0
  69. data/common/src/js/jsunit/app/jsUnitTestManager.js +705 -0
  70. data/common/src/js/jsunit/app/jsUnitTestSuite.js +44 -0
  71. data/common/src/js/jsunit/app/jsUnitTracer.js +102 -0
  72. data/common/src/js/jsunit/app/jsUnitVersionCheck.js +59 -0
  73. data/common/src/js/jsunit/app/main-counts-errors.html +12 -0
  74. data/common/src/js/jsunit/app/main-counts-failures.html +13 -0
  75. data/common/src/js/jsunit/app/main-counts-runs.html +13 -0
  76. data/common/src/js/jsunit/app/main-counts.html +21 -0
  77. data/common/src/js/jsunit/app/main-data.html +178 -0
  78. data/common/src/js/jsunit/app/main-errors.html +23 -0
  79. data/common/src/js/jsunit/app/main-frame.html +19 -0
  80. data/common/src/js/jsunit/app/main-loader.html +45 -0
  81. data/common/src/js/jsunit/app/main-progress.html +25 -0
  82. data/common/src/js/jsunit/app/main-results.html +67 -0
  83. data/common/src/js/jsunit/app/main-status.html +13 -0
  84. data/common/src/js/jsunit/app/testContainer.html +16 -0
  85. data/common/src/js/jsunit/app/testContainerController.html +77 -0
  86. data/common/src/js/jsunit/app/xbDebug.js +306 -0
  87. data/common/src/js/jsunit/changelog.txt +60 -0
  88. data/common/src/js/jsunit/css/jsUnitStyle.css +83 -0
  89. data/common/src/js/jsunit/images/green.gif +0 -0
  90. data/common/src/js/jsunit/images/logo_jsunit.gif +0 -0
  91. data/common/src/js/jsunit/images/powerby-transparent.gif +0 -0
  92. data/common/src/js/jsunit/images/red.gif +0 -0
  93. data/common/src/js/jsunit/licenses/JDOM_license.txt +56 -0
  94. data/common/src/js/jsunit/licenses/Jetty_license.html +213 -0
  95. data/common/src/js/jsunit/licenses/MPL-1.1.txt +470 -0
  96. data/common/src/js/jsunit/licenses/gpl-2.txt +340 -0
  97. data/common/src/js/jsunit/licenses/index.html +141 -0
  98. data/common/src/js/jsunit/licenses/lgpl-2.1.txt +504 -0
  99. data/common/src/js/jsunit/licenses/mpl-tri-license-c.txt +35 -0
  100. data/common/src/js/jsunit/licenses/mpl-tri-license-html.txt +35 -0
  101. data/common/src/js/jsunit/readme.txt +19 -0
  102. data/common/src/js/jsunit/testRunner.html +167 -0
  103. data/common/src/js/jsunit/version.txt +1 -0
  104. data/common/src/rb/README +29 -0
  105. data/common/src/rb/lib/selenium/webdriver/driver.rb +124 -12
  106. data/common/src/rb/lib/selenium/webdriver/element.rb +119 -3
  107. data/common/src/rb/lib/selenium/webdriver/error.rb +1 -2
  108. data/common/src/rb/lib/selenium/webdriver/find.rb +19 -2
  109. data/common/src/rb/lib/selenium/webdriver/keys.rb +5 -1
  110. data/common/src/rb/lib/selenium/webdriver/navigation.rb +8 -4
  111. data/common/src/rb/lib/selenium/webdriver/platform.rb +4 -2
  112. data/common/src/rb/lib/selenium/webdriver/target_locator.rb +18 -0
  113. data/firefox/prebuilt/Win32/Release/webdriver-firefox.dll +0 -0
  114. data/firefox/prebuilt/linux/Release/libwebdriver-firefox.so +0 -0
  115. data/firefox/prebuilt/linux/Release/x_ignore_nofocus.so +0 -0
  116. data/firefox/prebuilt/linux64/Release/libwebdriver-firefox.so +0 -0
  117. data/firefox/prebuilt/linux64/Release/x_ignore_nofocus.so +0 -0
  118. data/firefox/src/extension/components/utils.js +13 -2
  119. data/firefox/src/extension/install.rdf +1 -1
  120. data/firefox/src/rb/lib/selenium/webdriver/firefox.rb +3 -2
  121. data/firefox/src/rb/lib/selenium/webdriver/firefox/binary.rb +1 -7
  122. data/firefox/src/rb/lib/selenium/webdriver/firefox/bridge.rb +11 -4
  123. data/firefox/src/rb/lib/selenium/webdriver/firefox/profile.rb +56 -15
  124. data/firefox/src/rb/lib/selenium/webdriver/firefox/util.rb +1 -1
  125. data/jobbie/prebuilt/Win32/Release/InternetExplorerDriver.dll +0 -0
  126. data/jobbie/prebuilt/x64/Release/InternetExplorerDriver.dll +0 -0
  127. data/jobbie/src/rb/lib/selenium/webdriver/ie/bridge.rb +10 -5
  128. data/jobbie/src/rb/lib/selenium/webdriver/ie/util.rb +9 -10
  129. data/remote/client/src/rb/lib/selenium/webdriver/remote/bridge.rb +4 -2
  130. data/remote/client/src/rb/lib/selenium/webdriver/remote/capabilities.rb +23 -23
  131. metadata +103 -6
  132. data/jobbie/prebuilt/Win32/Release/webdriver-ie-test.dll +0 -0
  133. data/jobbie/prebuilt/Win32/Release/webdriver-ie.dll +0 -0
  134. data/jobbie/prebuilt/x64/Release/webdriver-ie-test.dll +0 -0
  135. data/jobbie/prebuilt/x64/Release/webdriver-ie.dll +0 -0
@@ -0,0 +1,2430 @@
1
+ /*
2
+ * Copyright 2004 ThoughtWorks, Inc
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ *
16
+ */
17
+
18
+ /*
19
+ * This script provides the Javascript API to drive the test application contained within
20
+ * a Browser Window.
21
+ * TODO:
22
+ * Add support for more events (keyboard and mouse)
23
+ * Allow to switch "user-entry" mode from mouse-based to keyboard-based, firing different
24
+ * events in different modes.
25
+ */
26
+
27
+ // The window to which the commands will be sent. For example, to click on a
28
+ // popup window, first select that window, and then do a normal click command.
29
+ var BrowserBot = function(topLevelApplicationWindow) {
30
+ this.topWindow = topLevelApplicationWindow;
31
+ this.topFrame = this.topWindow;
32
+ this.baseUrl=window.location.href;
33
+
34
+ // the buttonWindow is the Selenium window
35
+ // it contains the Run/Pause buttons... this should *not* be the AUT window
36
+ this.buttonWindow = window;
37
+ this.currentWindow = this.topWindow;
38
+ this.currentWindowName = null;
39
+ this.allowNativeXpath = true;
40
+ this.xpathLibrary = this.defaultXpathLibrary = 'ajaxslt' // change to "javascript-xpath" for the newer, faster engine
41
+
42
+ // We need to know this in advance, in case the frame closes unexpectedly
43
+ this.isSubFrameSelected = false;
44
+
45
+ this.altKeyDown = false;
46
+ this.controlKeyDown = false;
47
+ this.shiftKeyDown = false;
48
+ this.metaKeyDown = false;
49
+
50
+ this.modalDialogTest = null;
51
+ this.recordedAlerts = new Array();
52
+ this.recordedConfirmations = new Array();
53
+ this.recordedPrompts = new Array();
54
+ this.openedWindows = {};
55
+ this.nextConfirmResult = true;
56
+ this.nextPromptResult = '';
57
+ this.newPageLoaded = false;
58
+ this.pageLoadError = null;
59
+
60
+ this.ignoreResponseCode = false;
61
+ this.xhr = null;
62
+ this.abortXhr = false;
63
+ this.isXhrSent = false;
64
+ this.isXhrDone = false;
65
+ this.xhrOpenLocation = null;
66
+ this.xhrResponseCode = null;
67
+ this.xhrStatusText = null;
68
+
69
+ this.shouldHighlightLocatedElement = false;
70
+
71
+ this.uniqueId = "seleniumMarker" + new Date().getTime();
72
+ this.pollingForLoad = new Object();
73
+ this.permDeniedCount = new Object();
74
+ this.windowPollers = new Array();
75
+ // DGF for backwards compatibility
76
+ this.browserbot = this;
77
+
78
+ var self = this;
79
+
80
+ objectExtend(this, PageBot.prototype);
81
+ this._registerAllLocatorFunctions();
82
+
83
+ this.recordPageLoad = function(elementOrWindow) {
84
+ LOG.debug("Page load detected");
85
+ try {
86
+ if (elementOrWindow.location && elementOrWindow.location.href) {
87
+ LOG.debug("Page load location=" + elementOrWindow.location.href);
88
+ } else if (elementOrWindow.contentWindow && elementOrWindow.contentWindow.location && elementOrWindow.contentWindow.location.href) {
89
+ LOG.debug("Page load location=" + elementOrWindow.contentWindow.location.href);
90
+ } else {
91
+ LOG.debug("Page load location unknown, current window location=" + this.getCurrentWindow(true).location);
92
+ }
93
+ } catch (e) {
94
+ LOG.error("Caught an exception attempting to log location; this should get noticed soon!");
95
+ LOG.exception(e);
96
+ self.pageLoadError = e;
97
+ return;
98
+ }
99
+ self.newPageLoaded = true;
100
+ };
101
+
102
+ this.isNewPageLoaded = function() {
103
+ if (this.pageLoadError) {
104
+ LOG.error("isNewPageLoaded found an old pageLoadError");
105
+ var e = this.pageLoadError;
106
+ this.pageLoadError = null;
107
+ throw e;
108
+ }
109
+
110
+ if (self.ignoreResponseCode) {
111
+ return self.newPageLoaded;
112
+ } else {
113
+ if (self.isXhrSent && self.isXhrDone) {
114
+ if (!((self.xhrResponseCode >= 200 && self.xhrResponseCode <= 399) || self.xhrResponseCode == 0)) {
115
+ // TODO: for IE status like: 12002, 12007, ... provide corresponding statusText messages also.
116
+ LOG.error("XHR failed with message " + self.xhrStatusText);
117
+ var e = "XHR ERROR: URL = " + self.xhrOpenLocation + " Response_Code = " + self.xhrResponseCode + " Error_Message = " + self.xhrStatusText
118
+ self.abortXhr = false;
119
+ self.isXhrSent = false;
120
+ self.isXhrDone = false;
121
+ self.xhrResponseCode = null;
122
+ self.xhrStatusText = null;
123
+ throw new SeleniumError(e);
124
+ }
125
+ }
126
+ return self.newPageLoaded && (self.isXhrSent ? (self.abortXhr || self.isXhrDone) : true);
127
+ }
128
+ };
129
+
130
+ };
131
+
132
+ // DGF PageBot exists for backwards compatibility with old user-extensions
133
+ var PageBot = function(){};
134
+
135
+ BrowserBot.createForWindow = function(window, proxyInjectionMode) {
136
+ var browserbot;
137
+ LOG.debug('createForWindow');
138
+ LOG.debug("browserName: " + browserVersion.name);
139
+ LOG.debug("userAgent: " + navigator.userAgent);
140
+ if (browserVersion.isIE) {
141
+ browserbot = new IEBrowserBot(window);
142
+ }
143
+ else if (browserVersion.isKonqueror) {
144
+ browserbot = new KonquerorBrowserBot(window);
145
+ }
146
+ else if (browserVersion.isOpera) {
147
+ browserbot = new OperaBrowserBot(window);
148
+ }
149
+ else if (browserVersion.isSafari) {
150
+ browserbot = new SafariBrowserBot(window);
151
+ }
152
+ else {
153
+ // Use mozilla by default
154
+ browserbot = new MozillaBrowserBot(window);
155
+ }
156
+ // getCurrentWindow has the side effect of modifying it to handle page loads etc
157
+ browserbot.proxyInjectionMode = proxyInjectionMode;
158
+ browserbot.getCurrentWindow(); // for modifyWindow side effect. This is not a transparent style
159
+ return browserbot;
160
+ };
161
+
162
+ // todo: rename? This doesn't actually "do" anything.
163
+ BrowserBot.prototype.doModalDialogTest = function(test) {
164
+ this.modalDialogTest = test;
165
+ };
166
+
167
+ BrowserBot.prototype.cancelNextConfirmation = function(result) {
168
+ this.nextConfirmResult = result;
169
+ };
170
+
171
+ BrowserBot.prototype.setNextPromptResult = function(result) {
172
+ this.nextPromptResult = result;
173
+ };
174
+
175
+ BrowserBot.prototype.hasAlerts = function() {
176
+ return (this.recordedAlerts.length > 0);
177
+ };
178
+
179
+ BrowserBot.prototype.relayBotToRC = function(s) {
180
+ // DGF need to do this funny trick to see if we're in PI mode, because
181
+ // "this" might be the window, rather than the browserbot (e.g. during window.alert)
182
+ var piMode = this.proxyInjectionMode;
183
+ if (!piMode) {
184
+ if (typeof(selenium) != "undefined") {
185
+ piMode = selenium.browserbot && selenium.browserbot.proxyInjectionMode;
186
+ }
187
+ }
188
+ if (piMode) {
189
+ this.relayToRC("selenium." + s);
190
+ }
191
+ };
192
+
193
+ BrowserBot.prototype.relayToRC = function(name) {
194
+ var object = eval(name);
195
+ var s = 'state:' + serializeObject(name, object) + "\n";
196
+ sendToRC(s,"state=true");
197
+ }
198
+
199
+ BrowserBot.prototype.resetPopups = function() {
200
+ this.recordedAlerts = [];
201
+ this.recordedConfirmations = [];
202
+ this.recordedPrompts = [];
203
+ }
204
+
205
+ BrowserBot.prototype.getNextAlert = function() {
206
+ var t = this.recordedAlerts.shift();
207
+ if (t) {
208
+ t = t.replace(/\n/g, " "); // because Selenese loses \n's when retrieving text from HTML table
209
+ }
210
+ this.relayBotToRC("browserbot.recordedAlerts");
211
+ return t;
212
+ };
213
+
214
+ BrowserBot.prototype.hasConfirmations = function() {
215
+ return (this.recordedConfirmations.length > 0);
216
+ };
217
+
218
+ BrowserBot.prototype.getNextConfirmation = function() {
219
+ var t = this.recordedConfirmations.shift();
220
+ this.relayBotToRC("browserbot.recordedConfirmations");
221
+ return t;
222
+ };
223
+
224
+ BrowserBot.prototype.hasPrompts = function() {
225
+ return (this.recordedPrompts.length > 0);
226
+ };
227
+
228
+ BrowserBot.prototype.getNextPrompt = function() {
229
+ var t = this.recordedPrompts.shift();
230
+ this.relayBotToRC("browserbot.recordedPrompts");
231
+ return t;
232
+ };
233
+
234
+ /* Fire a mouse event in a browser-compatible manner */
235
+
236
+ BrowserBot.prototype.triggerMouseEvent = function(element, eventType, canBubble, clientX, clientY, button) {
237
+ clientX = clientX ? clientX : 0;
238
+ clientY = clientY ? clientY : 0;
239
+
240
+ LOG.debug("triggerMouseEvent assumes setting screenX and screenY to 0 is ok");
241
+ var screenX = 0;
242
+ var screenY = 0;
243
+
244
+ canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
245
+ if (element.fireEvent && element.ownerDocument && element.ownerDocument.createEventObject) { //IE
246
+ var evt = createEventObject(element, this.controlKeyDown, this.altKeyDown, this.shiftKeyDown, this.metaKeyDown);
247
+ evt.detail = 0;
248
+ evt.button = button ? button : 1; // default will be the left mouse click ( http://www.javascriptkit.com/jsref/event.shtml )
249
+ evt.relatedTarget = null;
250
+ if (!screenX && !screenY && !clientX && !clientY && !this.controlKeyDown && !this.altKeyDown && !this.shiftKeyDown && !this.metaKeyDown) {
251
+ element.fireEvent('on' + eventType);
252
+ }
253
+ else {
254
+ evt.screenX = screenX;
255
+ evt.screenY = screenY;
256
+ evt.clientX = clientX;
257
+ evt.clientY = clientY;
258
+
259
+ // when we go this route, window.event is never set to contain the event we have just created.
260
+ // ideally we could just slide it in as follows in the try-block below, but this normally
261
+ // doesn't work. This is why I try to avoid this code path, which is only required if we need to
262
+ // set attributes on the event (e.g., clientX).
263
+ try {
264
+ window.event = evt;
265
+ }
266
+ catch(e) {
267
+ // getting an "Object does not support this action or property" error. Save the event away
268
+ // for future reference.
269
+ // TODO: is there a way to update window.event?
270
+
271
+ // work around for http://jira.openqa.org/browse/SEL-280 -- make the event available somewhere:
272
+ selenium.browserbot.getCurrentWindow().selenium_event = evt;
273
+ }
274
+ element.fireEvent('on' + eventType, evt);
275
+ }
276
+ }
277
+ else {
278
+ var evt = document.createEvent('MouseEvents');
279
+ if (evt.initMouseEvent)
280
+ {
281
+ // see http://developer.mozilla.org/en/docs/DOM:event.button and
282
+ // http://developer.mozilla.org/en/docs/DOM:event.initMouseEvent for button ternary logic logic
283
+ //Safari
284
+ evt.initMouseEvent(eventType, canBubble, true, document.defaultView, 1, screenX, screenY, clientX, clientY,
285
+ this.controlKeyDown, this.altKeyDown, this.shiftKeyDown, this.metaKeyDown, button ? button : 0, null);
286
+ }
287
+ else {
288
+ LOG.warn("element doesn't have initMouseEvent; firing an event which should -- but doesn't -- have other mouse-event related attributes here, as well as controlKeyDown, altKeyDown, shiftKeyDown, metaKeyDown");
289
+ evt.initEvent(eventType, canBubble, true);
290
+
291
+ evt.shiftKey = this.shiftKeyDown;
292
+ evt.metaKey = this.metaKeyDown;
293
+ evt.altKey = this.altKeyDown;
294
+ evt.ctrlKey = this.controlKeyDown;
295
+ if(button)
296
+ {
297
+ evt.button = button;
298
+ }
299
+ }
300
+ element.dispatchEvent(evt);
301
+ }
302
+ }
303
+
304
+ BrowserBot.prototype._windowClosed = function(win) {
305
+ var c = win.closed;
306
+ if (c == null) return true;
307
+ return c;
308
+ };
309
+
310
+ BrowserBot.prototype._modifyWindow = function(win) {
311
+ // In proxyInjectionMode, have to suppress LOG calls in _modifyWindow to avoid an infinite loop
312
+ if (this._windowClosed(win)) {
313
+ if (!this.proxyInjectionMode) {
314
+ LOG.error("modifyWindow: Window was closed!");
315
+ }
316
+ return null;
317
+ }
318
+ if (!this.proxyInjectionMode) {
319
+ LOG.debug('modifyWindow ' + this.uniqueId + ":" + win[this.uniqueId]);
320
+ }
321
+ if (!win[this.uniqueId]) {
322
+ win[this.uniqueId] = 1;
323
+ this.modifyWindowToRecordPopUpDialogs(win, this);
324
+ }
325
+ // In proxyInjection mode, we have our own mechanism for detecting page loads
326
+ if (!this.proxyInjectionMode) {
327
+ this.modifySeparateTestWindowToDetectPageLoads(win);
328
+ }
329
+ if (win.frames && win.frames.length && win.frames.length > 0) {
330
+ for (var i = 0; i < win.frames.length; i++) {
331
+ try {
332
+ this._modifyWindow(win.frames[i]);
333
+ } catch (e) {} // we're just trying to be opportunistic; don't worry if this doesn't work out
334
+ }
335
+ }
336
+ return win;
337
+ };
338
+
339
+ BrowserBot.prototype.selectWindow = function(target) {
340
+ if (!target || target == "null") {
341
+ this._selectTopWindow();
342
+ return;
343
+ }
344
+ var result = target.match(/^([a-zA-Z]+)=(.*)/);
345
+ if (!result) {
346
+ this._selectWindowByWindowId(target);
347
+ return;
348
+ }
349
+ locatorType = result[1];
350
+ locatorValue = result[2];
351
+ if (locatorType == "title") {
352
+ this._selectWindowByTitle(locatorValue);
353
+ }
354
+ // TODO separate name and var into separate functions
355
+ else if (locatorType == "name") {
356
+ this._selectWindowByName(locatorValue);
357
+ } else if (locatorType == "var") {
358
+ this._selectWindowByName(locatorValue);
359
+ } else {
360
+ throw new SeleniumError("Window locator not recognized: " + locatorType);
361
+ }
362
+ };
363
+
364
+ BrowserBot.prototype.selectPopUp = function(windowId) {
365
+ if (! windowId || windowId == 'null') {
366
+ this._selectFirstNonTopWindow();
367
+ }
368
+ else {
369
+ this._selectWindowByWindowId(windowId);
370
+ }
371
+ };
372
+
373
+ BrowserBot.prototype._selectTopWindow = function() {
374
+ this.currentWindowName = null;
375
+ this.currentWindow = this.topWindow;
376
+ this.topFrame = this.topWindow;
377
+ this.isSubFrameSelected = false;
378
+ }
379
+
380
+ BrowserBot.prototype._selectWindowByWindowId = function(windowId) {
381
+ try {
382
+ this._selectWindowByName(windowId);
383
+ }
384
+ catch (e) {
385
+ this._selectWindowByTitle(windowId);
386
+ }
387
+ };
388
+
389
+ BrowserBot.prototype._selectWindowByName = function(target) {
390
+ this.currentWindow = this.getWindowByName(target, false);
391
+ this.topFrame = this.currentWindow;
392
+ this.currentWindowName = target;
393
+ this.isSubFrameSelected = false;
394
+ }
395
+
396
+ BrowserBot.prototype._selectWindowByTitle = function(target) {
397
+ var windowName = this.getWindowNameByTitle(target);
398
+ if (!windowName) {
399
+ this._selectTopWindow();
400
+ } else {
401
+ this._selectWindowByName(windowName);
402
+ }
403
+ }
404
+
405
+ BrowserBot.prototype._selectFirstNonTopWindow = function() {
406
+ var names = this.getNonTopWindowNames();
407
+ if (names.length) {
408
+ this._selectWindowByName(names[0]);
409
+ }
410
+ };
411
+
412
+ BrowserBot.prototype.selectFrame = function(target) {
413
+ if (target.indexOf("index=") == 0) {
414
+ target = target.substr(6);
415
+ var frame = this.getCurrentWindow().frames[target];
416
+ if (frame == null) {
417
+ throw new SeleniumError("Not found: frames["+target+"]");
418
+ }
419
+ if (!frame.document) {
420
+ throw new SeleniumError("frames["+target+"] is not a frame");
421
+ }
422
+ this.currentWindow = frame;
423
+ this.isSubFrameSelected = true;
424
+ }
425
+ else if (target == "relative=up" || target == "relative=parent") {
426
+ this.currentWindow = this.getCurrentWindow().parent;
427
+ this.isSubFrameSelected = (this._getFrameElement(this.currentWindow) != null);
428
+ } else if (target == "relative=top") {
429
+ this.currentWindow = this.topFrame;
430
+ this.isSubFrameSelected = false;
431
+ } else {
432
+ var frame = this.findElement(target);
433
+ if (frame == null) {
434
+ throw new SeleniumError("Not found: " + target);
435
+ }
436
+ // now, did they give us a frame or a frame ELEMENT?
437
+ var match = false;
438
+ if (frame.contentWindow) {
439
+ // this must be a frame element
440
+ if (browserVersion.isHTA) {
441
+ // stupid HTA bug; can't get in the front door
442
+ target = frame.contentWindow.name;
443
+ } else {
444
+ this.currentWindow = frame.contentWindow;
445
+ this.isSubFrameSelected = true;
446
+ match = true;
447
+ }
448
+ } else if (frame.document && frame.location) {
449
+ // must be an actual window frame
450
+ this.currentWindow = frame;
451
+ this.isSubFrameSelected = true;
452
+ match = true;
453
+ }
454
+
455
+ if (!match) {
456
+ // neither, let's loop through the frame names
457
+ var win = this.getCurrentWindow();
458
+
459
+ if (win && win.frames && win.frames.length) {
460
+ for (var i = 0; i < win.frames.length; i++) {
461
+ if (win.frames[i].name == target) {
462
+ this.currentWindow = win.frames[i];
463
+ this.isSubFrameSelected = true;
464
+ match = true;
465
+ break;
466
+ }
467
+ }
468
+ }
469
+ if (!match) {
470
+ throw new SeleniumError("Not a frame: " + target);
471
+ }
472
+ }
473
+ }
474
+ // modifies the window
475
+ this.getCurrentWindow();
476
+ };
477
+
478
+ BrowserBot.prototype.doesThisFrameMatchFrameExpression = function(currentFrameString, target) {
479
+ var isDom = false;
480
+ if (target.indexOf("dom=") == 0) {
481
+ target = target.substr(4);
482
+ isDom = true;
483
+ } else if (target.indexOf("index=") == 0) {
484
+ target = "frames[" + target.substr(6) + "]";
485
+ isDom = true;
486
+ }
487
+ var t;
488
+ try {
489
+ eval("t=" + currentFrameString + "." + target);
490
+ } catch (e) {
491
+ }
492
+ var autWindow = this.browserbot.getCurrentWindow();
493
+ if (t != null) {
494
+ try {
495
+ if (t.window == autWindow) {
496
+ return true;
497
+ }
498
+ if (t.window.uniqueId == autWindow.uniqueId) {
499
+ return true;
500
+ }
501
+ return false;
502
+ } catch (permDenied) {
503
+ // DGF if the windows are incomparable, they're probably not the same...
504
+ }
505
+ }
506
+ if (isDom) {
507
+ return false;
508
+ }
509
+ var currentFrame;
510
+ eval("currentFrame=" + currentFrameString);
511
+ if (target == "relative=up") {
512
+ if (currentFrame.window.parent == autWindow) {
513
+ return true;
514
+ }
515
+ return false;
516
+ }
517
+ if (target == "relative=top") {
518
+ if (currentFrame.window.top == autWindow) {
519
+ return true;
520
+ }
521
+ return false;
522
+ }
523
+ if (currentFrame.window == autWindow.parent) {
524
+ if (autWindow.name == target) {
525
+ return true;
526
+ }
527
+ try {
528
+ var element = this.findElement(target, currentFrame.window);
529
+ if (element.contentWindow == autWindow) {
530
+ return true;
531
+ }
532
+ } catch (e) {}
533
+ }
534
+ return false;
535
+ };
536
+
537
+ BrowserBot.prototype.abortXhrRequest = function() {
538
+ if (this.ignoreResponseCode) {
539
+ LOG.debug("XHR response code being ignored. Nothing to abort.");
540
+ } else {
541
+ if (this.abortXhr == false && this.isXhrSent && !this.isXhrDone) {
542
+ LOG.info("abortXhrRequest(): aborting request");
543
+ this.abortXhr = true;
544
+ this.xhr.abort();
545
+ }
546
+ }
547
+ }
548
+
549
+ BrowserBot.prototype.onXhrStateChange = function(method) {
550
+ LOG.info("onXhrStateChange(): xhr.readyState = " + this.xhr.readyState + " method = " + method + " time = " + new Date().getTime());
551
+ if (this.xhr.readyState == 4) {
552
+
553
+ // check if the request got aborted.
554
+ if (this.abortXhr == true) {
555
+ this.xhrResponseCode = 0;
556
+ this.xhrStatusText = "Request Aborted";
557
+ this.isXhrDone = true;
558
+ return;
559
+ }
560
+
561
+ try {
562
+ if (method == "HEAD" && (this.xhr.status == 501 || this.xhr.status == 405)) {
563
+ LOG.info("onXhrStateChange(): HEAD ajax returned 501 or 405, retrying with GET");
564
+ // handle 501 response code from servers that do not support 'HEAD' method.
565
+ // send GET ajax request with range 0-1.
566
+ this.xhr = XmlHttp.create();
567
+ this.xhr.onreadystatechange = this.onXhrStateChange.bind(this, "GET");
568
+ this.xhr.open("GET", this.xhrOpenLocation, true);
569
+ this.xhr.setRequestHeader("Range", "bytes:0-1");
570
+ this.xhr.send("");
571
+ this.isXhrSent = true;
572
+ return;
573
+ }
574
+ this.xhrResponseCode = this.xhr.status;
575
+ this.xhrStatusText = this.xhr.statusText;
576
+ } catch (ex) {
577
+ LOG.info("encountered exception while reading xhrResponseCode." + ex.message);
578
+ this.xhrResponseCode = -1;
579
+ this.xhrStatusText = "Request Error";
580
+ }
581
+
582
+ this.isXhrDone = true;
583
+ }
584
+ };
585
+
586
+ BrowserBot.prototype.checkedOpen = function(target) {
587
+ var url = absolutify(target, this.baseUrl);
588
+ LOG.debug("checkedOpen(): url = " + url);
589
+ this.isXhrDone = false;
590
+ this.abortXhr = false;
591
+ this.xhrResponseCode = null;
592
+ this.xhrOpenLocation = url;
593
+ try {
594
+ this.xhr = XmlHttp.create();
595
+ } catch (ex) {
596
+ LOG.error("Your browser doesnt support Xml Http Request");
597
+ return;
598
+ }
599
+ this.xhr.onreadystatechange = this.onXhrStateChange.bind(this, "HEAD");
600
+ this.xhr.open("HEAD", url, true);
601
+ this.xhr.send("");
602
+ this.isXhrSent = true;
603
+ }
604
+
605
+ BrowserBot.prototype.openLocation = function(target) {
606
+ // We're moving to a new page - clear the current one
607
+ var win = this.getCurrentWindow();
608
+ LOG.debug("openLocation newPageLoaded = false");
609
+ this.newPageLoaded = false;
610
+ if (!this.ignoreResponseCode) {
611
+ this.checkedOpen(target);
612
+ }
613
+ this.setOpenLocation(win, target);
614
+ };
615
+
616
+ BrowserBot.prototype.openWindow = function(url, windowID) {
617
+ if (url != "") {
618
+ url = absolutify(url, this.baseUrl);
619
+ }
620
+ if (browserVersion.isHTA) {
621
+ // in HTA mode, calling .open on the window interprets the url relative to that window
622
+ // we need to absolute-ize the URL to make it consistent
623
+ var child = this.getCurrentWindow().open(url, windowID);
624
+ selenium.browserbot.openedWindows[windowID] = child;
625
+ } else {
626
+ this.getCurrentWindow().open(url, windowID);
627
+ }
628
+ };
629
+
630
+ BrowserBot.prototype.setIFrameLocation = function(iframe, location) {
631
+ iframe.src = location;
632
+ };
633
+
634
+ BrowserBot.prototype.setOpenLocation = function(win, loc) {
635
+ loc = absolutify(loc, this.baseUrl);
636
+ if (browserVersion.isHTA) {
637
+ var oldHref = win.location.href;
638
+ win.location.href = loc;
639
+ var marker = null;
640
+ try {
641
+ marker = this.isPollingForLoad(win);
642
+ if (marker && win.location[marker]) {
643
+ win.location[marker] = false;
644
+ }
645
+ } catch (e) {} // DGF don't know why, but this often fails
646
+ } else {
647
+ win.location.href = loc;
648
+ }
649
+ };
650
+
651
+ BrowserBot.prototype.getCurrentPage = function() {
652
+ return this;
653
+ };
654
+
655
+ BrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
656
+ var self = this;
657
+
658
+ windowToModify.seleniumAlert = windowToModify.alert;
659
+
660
+ windowToModify.alert = function(alert) {
661
+ browserBot.recordedAlerts.push(alert);
662
+ self.relayBotToRC.call(self, "browserbot.recordedAlerts");
663
+ };
664
+
665
+ windowToModify.confirm = function(message) {
666
+ browserBot.recordedConfirmations.push(message);
667
+ var result = browserBot.nextConfirmResult;
668
+ browserBot.nextConfirmResult = true;
669
+ self.relayBotToRC.call(self, "browserbot.recordedConfirmations");
670
+ return result;
671
+ };
672
+
673
+ windowToModify.prompt = function(message) {
674
+ browserBot.recordedPrompts.push(message);
675
+ var result = !browserBot.nextConfirmResult ? null : browserBot.nextPromptResult;
676
+ browserBot.nextConfirmResult = true;
677
+ browserBot.nextPromptResult = '';
678
+ self.relayBotToRC.call(self, "browserbot.recordedPrompts");
679
+ return result;
680
+ };
681
+
682
+ // Keep a reference to all popup windows by name
683
+ // note that in IE the "windowName" argument must be a valid javascript identifier, it seems.
684
+ var originalOpen = windowToModify.open;
685
+ var originalOpenReference;
686
+ if (browserVersion.isHTA) {
687
+ originalOpenReference = 'selenium_originalOpen' + new Date().getTime();
688
+ windowToModify[originalOpenReference] = windowToModify.open;
689
+ }
690
+
691
+ var isHTA = browserVersion.isHTA;
692
+
693
+ var newOpen = function(url, windowName, windowFeatures, replaceFlag) {
694
+ var myOriginalOpen = originalOpen;
695
+ if (isHTA) {
696
+ myOriginalOpen = this[originalOpenReference];
697
+ }
698
+ if (windowName == "" || windowName == "_blank") {
699
+ windowName = "selenium_blank" + Math.round(100000 * Math.random());
700
+ LOG.warn("Opening window '_blank', which is not a real window name. Randomizing target to be: " + windowName);
701
+ }
702
+ var openedWindow = myOriginalOpen(url, windowName, windowFeatures, replaceFlag);
703
+ LOG.debug("window.open call intercepted; window ID (which you can use with selectWindow()) is \"" + windowName + "\"");
704
+ if (windowName!=null) {
705
+ openedWindow["seleniumWindowName"] = windowName;
706
+ }
707
+ selenium.browserbot.openedWindows[windowName] = openedWindow;
708
+ return openedWindow;
709
+ };
710
+
711
+ if (browserVersion.isHTA) {
712
+ originalOpenReference = 'selenium_originalOpen' + new Date().getTime();
713
+ newOpenReference = 'selenium_newOpen' + new Date().getTime();
714
+ var setOriginalRef = "this['" + originalOpenReference + "'] = this.open;";
715
+
716
+ if (windowToModify.eval) {
717
+ windowToModify.eval(setOriginalRef);
718
+ windowToModify.open = newOpen;
719
+ } else {
720
+ // DGF why can't I eval here? Seems like I'm querying the window at a bad time, maybe?
721
+ setOriginalRef += "this.open = this['" + newOpenReference + "'];";
722
+ windowToModify[newOpenReference] = newOpen;
723
+ windowToModify.setTimeout(setOriginalRef, 0);
724
+ }
725
+ } else {
726
+ windowToModify.open = newOpen;
727
+ }
728
+ };
729
+
730
+ /**
731
+ * Call the supplied function when a the current page unloads and a new one loads.
732
+ * This is done by polling continuously until the document changes and is fully loaded.
733
+ */
734
+ BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads = function(windowObject) {
735
+ // Since the unload event doesn't fire in Safari 1.3, we start polling immediately
736
+ if (!windowObject) {
737
+ LOG.warn("modifySeparateTestWindowToDetectPageLoads: no windowObject!");
738
+ return;
739
+ }
740
+ if (this._windowClosed(windowObject)) {
741
+ LOG.info("modifySeparateTestWindowToDetectPageLoads: windowObject was closed");
742
+ return;
743
+ }
744
+ var oldMarker = this.isPollingForLoad(windowObject);
745
+ if (oldMarker) {
746
+ LOG.debug("modifySeparateTestWindowToDetectPageLoads: already polling this window: " + oldMarker);
747
+ return;
748
+ }
749
+
750
+ var marker = 'selenium' + new Date().getTime();
751
+ LOG.debug("Starting pollForLoad (" + marker + "): " + windowObject.location);
752
+ this.pollingForLoad[marker] = true;
753
+ // if this is a frame, add a load listener, otherwise, attach a poller
754
+ var frameElement = this._getFrameElement(windowObject);
755
+ // DGF HTA mode can't attach load listeners to subframes (yuk!)
756
+ var htaSubFrame = this._isHTASubFrame(windowObject);
757
+ if (frameElement && !htaSubFrame) {
758
+ LOG.debug("modifySeparateTestWindowToDetectPageLoads: this window is a frame; attaching a load listener");
759
+ addLoadListener(frameElement, this.recordPageLoad);
760
+ frameElement[marker] = true;
761
+ frameElement["frame"+this.uniqueId] = marker;
762
+ LOG.debug("dgf this.uniqueId="+this.uniqueId);
763
+ LOG.debug("dgf marker="+marker);
764
+ LOG.debug("dgf frameElement['frame'+this.uniqueId]="+frameElement['frame'+this.uniqueId]);
765
+ frameElement[this.uniqueId] = marker;
766
+ LOG.debug("dgf frameElement[this.uniqueId]="+frameElement[this.uniqueId]);
767
+ } else {
768
+ windowObject.location[marker] = true;
769
+ windowObject[this.uniqueId] = marker;
770
+ this.pollForLoad(this.recordPageLoad, windowObject, windowObject.document, windowObject.location, windowObject.location.href, marker);
771
+ }
772
+ };
773
+
774
+ BrowserBot.prototype._isHTASubFrame = function(win) {
775
+ if (!browserVersion.isHTA) return false;
776
+ // DGF this is wrong! what if "win" isn't the selected window?
777
+ return this.isSubFrameSelected;
778
+ }
779
+
780
+ BrowserBot.prototype._getFrameElement = function(win) {
781
+ var frameElement = null;
782
+ var caught;
783
+ try {
784
+ frameElement = win.frameElement;
785
+ } catch (e) {
786
+ caught = true;
787
+ }
788
+ if (caught) {
789
+ // on IE, checking frameElement in a pop-up results in a "No such interface supported" exception
790
+ // but it might have a frame element anyway!
791
+ var parentContainsIdenticallyNamedFrame = false;
792
+ try {
793
+ parentContainsIdenticallyNamedFrame = win.parent.frames[win.name];
794
+ } catch (e) {} // this may fail if access is denied to the parent; in that case, assume it's not a pop-up
795
+
796
+ if (parentContainsIdenticallyNamedFrame) {
797
+ // it can't be a coincidence that the parent has a frame with the same name as myself!
798
+ var result;
799
+ try {
800
+ result = parentContainsIdenticallyNamedFrame.frameElement;
801
+ if (result) {
802
+ return result;
803
+ }
804
+ } catch (e) {} // it was worth a try! _getFrameElementsByName is often slow
805
+ result = this._getFrameElementByName(win.name, win.parent.document, win);
806
+ return result;
807
+ }
808
+ }
809
+ LOG.debug("_getFrameElement: frameElement="+frameElement);
810
+ if (frameElement) {
811
+ LOG.debug("frameElement.name="+frameElement.name);
812
+ }
813
+ return frameElement;
814
+ }
815
+
816
+ BrowserBot.prototype._getFrameElementByName = function(name, doc, win) {
817
+ var frames;
818
+ var frame;
819
+ var i;
820
+ frames = doc.getElementsByTagName("iframe");
821
+ for (i = 0; i < frames.length; i++) {
822
+ frame = frames[i];
823
+ if (frame.name === name) {
824
+ return frame;
825
+ }
826
+ }
827
+ frames = doc.getElementsByTagName("frame");
828
+ for (i = 0; i < frames.length; i++) {
829
+ frame = frames[i];
830
+ if (frame.name === name) {
831
+ return frame;
832
+ }
833
+ }
834
+ // DGF weird; we only call this function when we know the doc contains the frame
835
+ LOG.warn("_getFrameElementByName couldn't find a frame or iframe; checking every element for the name " + name);
836
+ return BrowserBot.prototype.locateElementByName(win.name, win.parent.document);
837
+ }
838
+
839
+
840
+ /**
841
+ * Set up a polling timer that will keep checking the readyState of the document until it's complete.
842
+ * Since we might call this before the original page is unloaded, we first check to see that the current location
843
+ * or href is different from the original one.
844
+ */
845
+ BrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
846
+ LOG.debug("pollForLoad original (" + marker + "): " + originalHref);
847
+ try {
848
+ if (this._windowClosed(windowObject)) {
849
+ LOG.debug("pollForLoad WINDOW CLOSED (" + marker + ")");
850
+ delete this.pollingForLoad[marker];
851
+ return;
852
+ }
853
+
854
+ var isSamePage = this._isSamePage(windowObject, originalDocument, originalLocation, originalHref, marker);
855
+ var rs = this.getReadyState(windowObject, windowObject.document);
856
+
857
+ if (!isSamePage && rs == 'complete') {
858
+ var currentHref = windowObject.location.href;
859
+ LOG.debug("pollForLoad FINISHED (" + marker + "): " + rs + " (" + currentHref + ")");
860
+ delete this.pollingForLoad[marker];
861
+ this._modifyWindow(windowObject);
862
+ var newMarker = this.isPollingForLoad(windowObject);
863
+ if (!newMarker) {
864
+ LOG.debug("modifyWindow didn't start new poller: " + newMarker);
865
+ this.modifySeparateTestWindowToDetectPageLoads(windowObject);
866
+ }
867
+ newMarker = this.isPollingForLoad(windowObject);
868
+ var currentlySelectedWindow;
869
+ var currentlySelectedWindowMarker;
870
+ currentlySelectedWindow =this.getCurrentWindow(true);
871
+ currentlySelectedWindowMarker = currentlySelectedWindow[this.uniqueId];
872
+
873
+ LOG.debug("pollForLoad (" + marker + ") restarting " + newMarker);
874
+ if (/(TestRunner-splash|Blank)\.html\?start=true$/.test(currentHref)) {
875
+ LOG.debug("pollForLoad Oh, it's just the starting page. Never mind!");
876
+ } else if (currentlySelectedWindowMarker == newMarker) {
877
+ loadFunction(currentlySelectedWindow);
878
+ } else {
879
+ LOG.debug("pollForLoad page load detected in non-current window; ignoring (currentlySelected="+currentlySelectedWindowMarker+", detection in "+newMarker+")");
880
+ }
881
+ return;
882
+ }
883
+ LOG.debug("pollForLoad continue (" + marker + "): " + currentHref);
884
+ this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
885
+ } catch (e) {
886
+ LOG.debug("Exception during pollForLoad; this should get noticed soon (" + e.message + ")!");
887
+ //DGF this is supposed to get logged later; log it at debug just in case
888
+ //LOG.exception(e);
889
+ this.pageLoadError = e;
890
+ }
891
+ };
892
+
893
+ BrowserBot.prototype._isSamePage = function(windowObject, originalDocument, originalLocation, originalHref, marker) {
894
+ var currentDocument = windowObject.document;
895
+ var currentLocation = windowObject.location;
896
+ var currentHref = currentLocation.href
897
+
898
+ var sameDoc = this._isSameDocument(originalDocument, currentDocument);
899
+
900
+ var sameLoc = (originalLocation === currentLocation);
901
+
902
+ // hash marks don't meant the page has loaded, so we need to strip them off if they exist...
903
+ var currentHash = currentHref.indexOf('#');
904
+ if (currentHash > 0) {
905
+ currentHref = currentHref.substring(0, currentHash);
906
+ }
907
+ var originalHash = originalHref.indexOf('#');
908
+ if (originalHash > 0) {
909
+ originalHref = originalHref.substring(0, originalHash);
910
+ }
911
+ LOG.debug("_isSamePage: currentHref: " + currentHref);
912
+ LOG.debug("_isSamePage: originalHref: " + originalHref);
913
+
914
+ var sameHref = (originalHref === currentHref);
915
+ var markedLoc = currentLocation[marker];
916
+
917
+ if (browserVersion.isKonqueror || browserVersion.isSafari) {
918
+ // the mark disappears too early on these browsers
919
+ markedLoc = true;
920
+ }
921
+
922
+ // since this is some _very_ important logic, especially for PI and multiWindow mode, we should log all these out
923
+ LOG.debug("_isSamePage: sameDoc: " + sameDoc);
924
+ LOG.debug("_isSamePage: sameLoc: " + sameLoc);
925
+ LOG.debug("_isSamePage: sameHref: " + sameHref);
926
+ LOG.debug("_isSamePage: markedLoc: " + markedLoc);
927
+
928
+ return sameDoc && sameLoc && sameHref && markedLoc
929
+ };
930
+
931
+ BrowserBot.prototype._isSameDocument = function(originalDocument, currentDocument) {
932
+ return originalDocument === currentDocument;
933
+ };
934
+
935
+
936
+ BrowserBot.prototype.getReadyState = function(windowObject, currentDocument) {
937
+ var rs = currentDocument.readyState;
938
+ if (rs == null) {
939
+ if ((this.buttonWindow!=null && this.buttonWindow.document.readyState == null) // not proxy injection mode (and therefore buttonWindow isn't null)
940
+ || (top.document.readyState == null)) { // proxy injection mode (and therefore everything's in the top window, but buttonWindow doesn't exist)
941
+ // uh oh! we're probably on Firefox with no readyState extension installed!
942
+ // We'll have to just take a guess as to when the document is loaded; this guess
943
+ // will never be perfect. :-(
944
+ if (typeof currentDocument.getElementsByTagName != 'undefined'
945
+ && typeof currentDocument.getElementById != 'undefined'
946
+ && ( currentDocument.getElementsByTagName('body')[0] != null
947
+ || currentDocument.body != null )) {
948
+ if (windowObject.frameElement && windowObject.location.href == "about:blank" && windowObject.frameElement.src != "about:blank") {
949
+ LOG.info("getReadyState not loaded, frame location was about:blank, but frame src = " + windowObject.frameElement.src);
950
+ return null;
951
+ }
952
+ LOG.debug("getReadyState = windowObject.frames.length = " + windowObject.frames.length);
953
+ for (var i = 0; i < windowObject.frames.length; i++) {
954
+ LOG.debug("i = " + i);
955
+ if (this.getReadyState(windowObject.frames[i], windowObject.frames[i].document) != 'complete') {
956
+ LOG.debug("getReadyState aha! the nested frame " + windowObject.frames[i].name + " wasn't ready!");
957
+ return null;
958
+ }
959
+ }
960
+
961
+ rs = 'complete';
962
+ } else {
963
+ LOG.debug("pollForLoad readyState was null and DOM appeared to not be ready yet");
964
+ }
965
+ }
966
+ }
967
+ else if (rs == "loading" && browserVersion.isIE) {
968
+ LOG.debug("pageUnloading = true!!!!");
969
+ this.pageUnloading = true;
970
+ }
971
+ LOG.debug("getReadyState returning " + rs);
972
+ return rs;
973
+ };
974
+
975
+ /** This function isn't used normally, but was the way we used to schedule pollers:
976
+ asynchronously executed autonomous units. This is deprecated, but remains here
977
+ for future reference.
978
+ */
979
+ BrowserBot.prototype.XXXreschedulePoller = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
980
+ var self = this;
981
+ window.setTimeout(function() {
982
+ self.pollForLoad(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
983
+ }, 500);
984
+ };
985
+
986
+ /** This function isn't used normally, but is useful for debugging asynchronous pollers
987
+ * To enable it, rename it to "reschedulePoller", so it will override the
988
+ * existing reschedulePoller function
989
+ */
990
+ BrowserBot.prototype.XXXreschedulePoller = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
991
+ var doc = this.buttonWindow.document;
992
+ var button = doc.createElement("button");
993
+ var buttonName = doc.createTextNode(marker + " - " + windowObject.name);
994
+ button.appendChild(buttonName);
995
+ var tools = doc.getElementById("tools");
996
+ var self = this;
997
+ button.onclick = function() {
998
+ tools.removeChild(button);
999
+ self.pollForLoad(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
1000
+ };
1001
+ tools.appendChild(button);
1002
+ window.setTimeout(button.onclick, 500);
1003
+ };
1004
+
1005
+ BrowserBot.prototype.reschedulePoller = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
1006
+ var self = this;
1007
+ var pollerFunction = function() {
1008
+ self.pollForLoad(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
1009
+ };
1010
+ this.windowPollers.push(pollerFunction);
1011
+ };
1012
+
1013
+ BrowserBot.prototype.runScheduledPollers = function() {
1014
+ LOG.debug("runScheduledPollers");
1015
+ var oldPollers = this.windowPollers;
1016
+ this.windowPollers = new Array();
1017
+ for (var i = 0; i < oldPollers.length; i++) {
1018
+ oldPollers[i].call();
1019
+ }
1020
+ LOG.debug("runScheduledPollers DONE");
1021
+ };
1022
+
1023
+ BrowserBot.prototype.isPollingForLoad = function(win) {
1024
+ var marker;
1025
+ var frameElement = this._getFrameElement(win);
1026
+ var htaSubFrame = this._isHTASubFrame(win);
1027
+ if (frameElement && !htaSubFrame) {
1028
+ marker = frameElement["frame"+this.uniqueId];
1029
+ } else {
1030
+ marker = win[this.uniqueId];
1031
+ }
1032
+ if (!marker) {
1033
+ LOG.debug("isPollingForLoad false, missing uniqueId " + this.uniqueId + ": " + marker);
1034
+ return false;
1035
+ }
1036
+ if (!this.pollingForLoad[marker]) {
1037
+ LOG.debug("isPollingForLoad false, this.pollingForLoad[" + marker + "]: " + this.pollingForLoad[marker]);
1038
+ return false;
1039
+ }
1040
+ return marker;
1041
+ };
1042
+
1043
+ BrowserBot.prototype.getWindowByName = function(windowName, doNotModify) {
1044
+ LOG.debug("getWindowByName(" + windowName + ")");
1045
+ // First look in the map of opened windows
1046
+ var targetWindow = this.openedWindows[windowName];
1047
+ if (!targetWindow) {
1048
+ targetWindow = this.topWindow[windowName];
1049
+ }
1050
+ if (!targetWindow && windowName == "_blank") {
1051
+ for (var winName in this.openedWindows) {
1052
+ // _blank can match selenium_blank*, if it looks like it's OK (valid href, not closed)
1053
+ if (/^selenium_blank/.test(winName)) {
1054
+ targetWindow = this.openedWindows[winName];
1055
+ var ok;
1056
+ try {
1057
+ if (!this._windowClosed(targetWindow)) {
1058
+ ok = targetWindow.location.href;
1059
+ }
1060
+ } catch (e) {}
1061
+ if (ok) break;
1062
+ }
1063
+ }
1064
+ }
1065
+ if (!targetWindow) {
1066
+ throw new SeleniumError("Window does not exist. If this looks like a Selenium bug, make sure to read http://selenium-core.openqa.org/reference.html#openWindow for potential workarounds.");
1067
+ }
1068
+ if (browserVersion.isHTA) {
1069
+ try {
1070
+ targetWindow.location.href;
1071
+ } catch (e) {
1072
+ targetWindow = window.open("", targetWindow.name);
1073
+ this.openedWindows[targetWindow.name] = targetWindow;
1074
+ }
1075
+ }
1076
+ if (!doNotModify) {
1077
+ this._modifyWindow(targetWindow);
1078
+ }
1079
+ return targetWindow;
1080
+ };
1081
+
1082
+ /**
1083
+ * Find a window name from the window title.
1084
+ */
1085
+ BrowserBot.prototype.getWindowNameByTitle = function(windowTitle) {
1086
+ LOG.debug("getWindowNameByTitle(" + windowTitle + ")");
1087
+
1088
+ // First look in the map of opened windows and iterate them
1089
+ for (var windowName in this.openedWindows) {
1090
+ var targetWindow = this.openedWindows[windowName];
1091
+
1092
+ // If the target window's title is our title
1093
+ try {
1094
+ // TODO implement Pattern Matching here
1095
+ if (!this._windowClosed(targetWindow) &&
1096
+ targetWindow.document.title == windowTitle) {
1097
+ return windowName;
1098
+ }
1099
+ } catch (e) {
1100
+ // You'll often get Permission Denied errors here in IE
1101
+ // eh, if we can't read this window's title,
1102
+ // it's probably not available to us right now anyway
1103
+ }
1104
+ }
1105
+
1106
+ try {
1107
+ if (this.topWindow.document.title == windowTitle) {
1108
+ return "";
1109
+ }
1110
+ } catch (e) {} // IE Perm denied
1111
+
1112
+ throw new SeleniumError("Could not find window with title " + windowTitle);
1113
+ };
1114
+
1115
+ BrowserBot.prototype.getNonTopWindowNames = function() {
1116
+ var nonTopWindowNames = [];
1117
+
1118
+ for (var windowName in this.openedWindows) {
1119
+ var win = this.openedWindows[windowName];
1120
+ if (! this._windowClosed(win) && win != this.topWindow) {
1121
+ nonTopWindowNames.push(windowName);
1122
+ }
1123
+ }
1124
+
1125
+ return nonTopWindowNames;
1126
+ };
1127
+
1128
+ BrowserBot.prototype.getCurrentWindow = function(doNotModify) {
1129
+ if (this.proxyInjectionMode) {
1130
+ return window;
1131
+ }
1132
+ var testWindow = this.currentWindow;
1133
+ if (!doNotModify) {
1134
+ this._modifyWindow(testWindow);
1135
+ LOG.debug("getCurrentWindow newPageLoaded = false");
1136
+ this.newPageLoaded = false;
1137
+ }
1138
+ testWindow = this._handleClosedSubFrame(testWindow, doNotModify);
1139
+ return testWindow;
1140
+ };
1141
+
1142
+ /**
1143
+ * Offer a method the end-user can reliably use to retrieve the current window.
1144
+ * This should work even for windows with an XPCNativeWrapper. Returns the
1145
+ * current window object.
1146
+ */
1147
+ BrowserBot.prototype.getUserWindow = function() {
1148
+ var userWindow = this.getCurrentWindow(true);
1149
+
1150
+ if (userWindow.wrappedJSObject) {
1151
+ userWindow = userWindow.wrappedJSObject;
1152
+ }
1153
+
1154
+ return userWindow;
1155
+ };
1156
+
1157
+ BrowserBot.prototype._handleClosedSubFrame = function(testWindow, doNotModify) {
1158
+ if (this.proxyInjectionMode) {
1159
+ return testWindow;
1160
+ }
1161
+
1162
+ if (this.isSubFrameSelected) {
1163
+ var missing = true;
1164
+ if (testWindow.parent && testWindow.parent.frames && testWindow.parent.frames.length) {
1165
+ for (var i = 0; i < testWindow.parent.frames.length; i++) {
1166
+ if (testWindow.parent.frames[i] == testWindow) {
1167
+ missing = false;
1168
+ break;
1169
+ }
1170
+ }
1171
+ }
1172
+ if (missing) {
1173
+ LOG.warn("Current subframe appears to have closed; selecting top frame");
1174
+ this.selectFrame("relative=top");
1175
+ return this.getCurrentWindow(doNotModify);
1176
+ }
1177
+ } else if (this._windowClosed(testWindow)) {
1178
+ var closedError = new SeleniumError("Current window or frame is closed!");
1179
+ closedError.windowClosed = true;
1180
+ throw closedError;
1181
+ }
1182
+ return testWindow;
1183
+ };
1184
+
1185
+ BrowserBot.prototype.highlight = function (element, force) {
1186
+ if (force || this.shouldHighlightLocatedElement) {
1187
+ try {
1188
+ highlight(element);
1189
+ } catch (e) {} // DGF element highlighting is low-priority and possibly dangerous
1190
+ }
1191
+ return element;
1192
+ }
1193
+
1194
+ BrowserBot.prototype.setShouldHighlightElement = function (shouldHighlight) {
1195
+ this.shouldHighlightLocatedElement = shouldHighlight;
1196
+ }
1197
+
1198
+ /*****************************************************************/
1199
+ /* BROWSER-SPECIFIC FUNCTIONS ONLY AFTER THIS LINE */
1200
+
1201
+
1202
+ BrowserBot.prototype._registerAllLocatorFunctions = function() {
1203
+ // TODO - don't do this in the constructor - only needed once ever
1204
+ this.locationStrategies = {};
1205
+ for (var functionName in this) {
1206
+ var result = /^locateElementBy([A-Z].+)$/.exec(functionName);
1207
+ if (result != null) {
1208
+ var locatorFunction = this[functionName];
1209
+ if (typeof(locatorFunction) != 'function') {
1210
+ continue;
1211
+ }
1212
+ // Use a specified prefix in preference to one generated from
1213
+ // the function name
1214
+ var locatorPrefix = locatorFunction.prefix || result[1].toLowerCase();
1215
+ this.locationStrategies[locatorPrefix] = locatorFunction;
1216
+ }
1217
+ }
1218
+
1219
+ /**
1220
+ * Find a locator based on a prefix.
1221
+ */
1222
+ this.findElementBy = function(locatorType, locator, inDocument, inWindow) {
1223
+ var locatorFunction = this.locationStrategies[locatorType];
1224
+ if (! locatorFunction) {
1225
+ throw new SeleniumError("Unrecognised locator type: '" + locatorType + "'");
1226
+ }
1227
+ return locatorFunction.call(this, locator, inDocument, inWindow);
1228
+ };
1229
+
1230
+ /**
1231
+ * The implicit locator, that is used when no prefix is supplied.
1232
+ */
1233
+ this.locationStrategies['implicit'] = function(locator, inDocument, inWindow) {
1234
+ if (locator.startsWith('//')) {
1235
+ return this.locateElementByXPath(locator, inDocument, inWindow);
1236
+ }
1237
+ if (locator.startsWith('document.')) {
1238
+ return this.locateElementByDomTraversal(locator, inDocument, inWindow);
1239
+ }
1240
+ return this.locateElementByIdentifier(locator, inDocument, inWindow);
1241
+ };
1242
+ }
1243
+
1244
+ BrowserBot.prototype.getDocument = function() {
1245
+ return this.getCurrentWindow().document;
1246
+ }
1247
+
1248
+ BrowserBot.prototype.getTitle = function() {
1249
+ var t = this.getDocument().title;
1250
+ if (typeof(t) == "string") {
1251
+ t = t.trim();
1252
+ }
1253
+ return t;
1254
+ }
1255
+
1256
+ BrowserBot.prototype.getCookieByName = function(cookieName, doc) {
1257
+ if (!doc) doc = this.getDocument();
1258
+ var ck = doc.cookie;
1259
+ if (!ck) return null;
1260
+ var ckPairs = ck.split(/;/);
1261
+ for (var i = 0; i < ckPairs.length; i++) {
1262
+ var ckPair = ckPairs[i].trim();
1263
+ var ckNameValue = ckPair.split(/=/);
1264
+ var ckName = decodeURIComponent(ckNameValue[0]);
1265
+ if (ckName === cookieName) {
1266
+ return decodeURIComponent(ckNameValue[1]);
1267
+ }
1268
+ }
1269
+ return null;
1270
+ }
1271
+
1272
+ BrowserBot.prototype.getAllCookieNames = function(doc) {
1273
+ if (!doc) doc = this.getDocument();
1274
+ var ck = doc.cookie;
1275
+ if (!ck) return [];
1276
+ var cookieNames = [];
1277
+ var ckPairs = ck.split(/;/);
1278
+ for (var i = 0; i < ckPairs.length; i++) {
1279
+ var ckPair = ckPairs[i].trim();
1280
+ var ckNameValue = ckPair.split(/=/);
1281
+ var ckName = decodeURIComponent(ckNameValue[0]);
1282
+ cookieNames.push(ckName);
1283
+ }
1284
+ return cookieNames;
1285
+ }
1286
+
1287
+ BrowserBot.prototype.deleteCookie = function(cookieName, domain, path, doc) {
1288
+ if (!doc) doc = this.getDocument();
1289
+ var expireDateInMilliseconds = (new Date()).getTime() + (-1 * 1000);
1290
+ var cookie = cookieName + "=deleted; ";
1291
+ if (path) {
1292
+ cookie += "path=" + path + "; ";
1293
+ }
1294
+ if (domain) {
1295
+ cookie += "domain=" + domain + "; ";
1296
+ }
1297
+ cookie += "expires=" + new Date(expireDateInMilliseconds).toGMTString();
1298
+ LOG.debug("Setting cookie to: " + cookie);
1299
+ doc.cookie = cookie;
1300
+ }
1301
+
1302
+ /** Try to delete cookie, return false if it didn't work */
1303
+ BrowserBot.prototype._maybeDeleteCookie = function(cookieName, domain, path, doc) {
1304
+ this.deleteCookie(cookieName, domain, path, doc);
1305
+ return (!this.getCookieByName(cookieName, doc));
1306
+ }
1307
+
1308
+
1309
+ BrowserBot.prototype._recursivelyDeleteCookieDomains = function(cookieName, domain, path, doc) {
1310
+ var deleted = this._maybeDeleteCookie(cookieName, domain, path, doc);
1311
+ if (deleted) return true;
1312
+ var dotIndex = domain.indexOf(".");
1313
+ if (dotIndex == 0) {
1314
+ return this._recursivelyDeleteCookieDomains(cookieName, domain.substring(1), path, doc);
1315
+ } else if (dotIndex != -1) {
1316
+ return this._recursivelyDeleteCookieDomains(cookieName, domain.substring(dotIndex), path, doc);
1317
+ } else {
1318
+ // No more dots; try just not passing in a domain at all
1319
+ return this._maybeDeleteCookie(cookieName, null, path, doc);
1320
+ }
1321
+ }
1322
+
1323
+ BrowserBot.prototype._recursivelyDeleteCookie = function(cookieName, domain, path, doc) {
1324
+ var slashIndex = path.lastIndexOf("/");
1325
+ var finalIndex = path.length-1;
1326
+ if (slashIndex == finalIndex) {
1327
+ slashIndex--;
1328
+ }
1329
+ if (slashIndex != -1) {
1330
+ deleted = this._recursivelyDeleteCookie(cookieName, domain, path.substring(0, slashIndex+1), doc);
1331
+ if (deleted) return true;
1332
+ }
1333
+ return this._recursivelyDeleteCookieDomains(cookieName, domain, path, doc);
1334
+ }
1335
+
1336
+ BrowserBot.prototype.recursivelyDeleteCookie = function(cookieName, domain, path, win) {
1337
+ if (!win) win = this.getCurrentWindow();
1338
+ var doc = win.document;
1339
+ if (!domain) {
1340
+ domain = doc.domain;
1341
+ }
1342
+ if (!path) {
1343
+ path = win.location.pathname;
1344
+ }
1345
+ var deleted = this._recursivelyDeleteCookie(cookieName, "." + domain, path, doc);
1346
+ if (deleted) return;
1347
+ // Finally try a null path (Try it last because it's uncommon)
1348
+ deleted = this._recursivelyDeleteCookieDomains(cookieName, "." + domain, null, doc);
1349
+ if (deleted) return;
1350
+ throw new SeleniumError("Couldn't delete cookie " + cookieName);
1351
+ }
1352
+
1353
+ /*
1354
+ * Finds an element recursively in frames and nested frames
1355
+ * in the specified document, using various lookup protocols
1356
+ */
1357
+ BrowserBot.prototype.findElementRecursive = function(locatorType, locatorString, inDocument, inWindow) {
1358
+
1359
+ var element = this.findElementBy(locatorType, locatorString, inDocument, inWindow);
1360
+ if (element != null) {
1361
+ return element;
1362
+ }
1363
+
1364
+ for (var i = 0; i < inWindow.frames.length; i++) {
1365
+ // On some browsers, the document object is undefined for third-party
1366
+ // frames. Make sure the document is valid before continuing.
1367
+ if (inWindow.frames[i].document) {
1368
+ element = this.findElementRecursive(locatorType, locatorString, inWindow.frames[i].document, inWindow.frames[i]);
1369
+
1370
+ if (element != null) {
1371
+ return element;
1372
+ }
1373
+ }
1374
+ }
1375
+ };
1376
+
1377
+ /*
1378
+ * Finds an element on the current page, using various lookup protocols
1379
+ */
1380
+ BrowserBot.prototype.findElementOrNull = function(locator, win) {
1381
+ locator = parse_locator(locator);
1382
+
1383
+ if (win == null) {
1384
+ win = this.getCurrentWindow();
1385
+ }
1386
+ var element = this.findElementRecursive(locator.type, locator.string, win.document, win);
1387
+
1388
+ if (element != null) {
1389
+ return this.browserbot.highlight(element);
1390
+ }
1391
+
1392
+ // Element was not found by any locator function.
1393
+ return null;
1394
+ };
1395
+
1396
+ BrowserBot.prototype.findElement = function(locator, win) {
1397
+ var element = this.findElementOrNull(locator, win);
1398
+ if (element == null) throw new SeleniumError("Element " + locator + " not found");
1399
+ return element;
1400
+ }
1401
+
1402
+ /**
1403
+ * In non-IE browsers, getElementById() does not search by name. Instead, we
1404
+ * we search separately by id and name.
1405
+ */
1406
+ BrowserBot.prototype.locateElementByIdentifier = function(identifier, inDocument, inWindow) {
1407
+ return BrowserBot.prototype.locateElementById(identifier, inDocument, inWindow)
1408
+ || BrowserBot.prototype.locateElementByName(identifier, inDocument, inWindow)
1409
+ || null;
1410
+ };
1411
+
1412
+ /**
1413
+ * Find the element with id - can't rely on getElementById, coz it returns by name as well in IE..
1414
+ */
1415
+ BrowserBot.prototype.locateElementById = function(identifier, inDocument, inWindow) {
1416
+ var element = inDocument.getElementById(identifier);
1417
+ if (element && element.getAttribute('id') === identifier) {
1418
+ return element;
1419
+ }
1420
+ else if (browserVersion.isIE || browserVersion.isOpera) {
1421
+ // SEL-484
1422
+ var xpath = '/descendant::*[@id=' + identifier.quoteForXPath() + ']';
1423
+ return BrowserBot.prototype
1424
+ .locateElementByXPath(xpath, inDocument, inWindow);
1425
+ }
1426
+ else {
1427
+ return null;
1428
+ }
1429
+ };
1430
+
1431
+ /**
1432
+ * Find an element by name, refined by (optional) element-filter
1433
+ * expressions.
1434
+ */
1435
+ BrowserBot.prototype.locateElementByName = function(locator, document, inWindow) {
1436
+ var elements = document.getElementsByTagName("*");
1437
+
1438
+ var filters = locator.split(' ');
1439
+ filters[0] = 'name=' + filters[0];
1440
+
1441
+ while (filters.length) {
1442
+ var filter = filters.shift();
1443
+ elements = this.selectElements(filter, elements, 'value');
1444
+ }
1445
+
1446
+ if (elements.length > 0) {
1447
+ return elements[0];
1448
+ }
1449
+ return null;
1450
+ };
1451
+
1452
+ /**
1453
+ * Finds an element using by evaluating the specfied string.
1454
+ */
1455
+ BrowserBot.prototype.locateElementByDomTraversal = function(domTraversal, document, window) {
1456
+
1457
+ var browserbot = this.browserbot;
1458
+ var element = null;
1459
+ try {
1460
+ element = eval(domTraversal);
1461
+ } catch (e) {
1462
+ return null;
1463
+ }
1464
+
1465
+ if (!element) {
1466
+ return null;
1467
+ }
1468
+
1469
+ return element;
1470
+ };
1471
+ BrowserBot.prototype.locateElementByDomTraversal.prefix = "dom";
1472
+
1473
+ /**
1474
+ * Finds an element identified by the xpath expression. Expressions _must_
1475
+ * begin with "//".
1476
+ */
1477
+ BrowserBot.prototype.locateElementByXPath = function(xpath, inDocument, inWindow) {
1478
+ var results = eval_xpath(xpath, inDocument, {
1479
+ returnOnFirstMatch : true,
1480
+ ignoreAttributesWithoutValue: this.ignoreAttributesWithoutValue,
1481
+ allowNativeXpath : this.allowNativeXpath,
1482
+ xpathLibrary : this.xpathLibrary,
1483
+ namespaceResolver : this._namespaceResolver
1484
+ });
1485
+ return (results.length > 0) ? results[0] : null;
1486
+ };
1487
+
1488
+ BrowserBot.prototype._namespaceResolver = function(prefix) {
1489
+ if (prefix == 'html' || prefix == 'xhtml' || prefix == 'x') {
1490
+ return 'http://www.w3.org/1999/xhtml';
1491
+ } else if (prefix == 'mathml') {
1492
+ return 'http://www.w3.org/1998/Math/MathML';
1493
+ } else {
1494
+ throw new Error("Unknown namespace: " + prefix + ".");
1495
+ }
1496
+ }
1497
+
1498
+ /**
1499
+ * Returns the number of xpath results.
1500
+ */
1501
+ BrowserBot.prototype.evaluateXPathCount = function(xpath, inDocument) {
1502
+ var results = eval_xpath(xpath, inDocument, {
1503
+ ignoreAttributesWithoutValue: this.ignoreAttributesWithoutValue,
1504
+ allowNativeXpath : this.allowNativeXpath,
1505
+ xpathLibrary : this.xpathLibrary,
1506
+ namespaceResolver : this._namespaceResolver
1507
+ });
1508
+ return results.length;
1509
+ };
1510
+
1511
+ /**
1512
+ * Finds a link element with text matching the expression supplied. Expressions must
1513
+ * begin with "link:".
1514
+ */
1515
+ BrowserBot.prototype.locateElementByLinkText = function(linkText, inDocument, inWindow) {
1516
+ var links = inDocument.getElementsByTagName('a');
1517
+ for (var i = 0; i < links.length; i++) {
1518
+ var element = links[i];
1519
+ if (PatternMatcher.matches(linkText, getText(element))) {
1520
+ return element;
1521
+ }
1522
+ }
1523
+ return null;
1524
+ };
1525
+ BrowserBot.prototype.locateElementByLinkText.prefix = "link";
1526
+
1527
+ /**
1528
+ * Returns an attribute based on an attribute locator. This is made up of an element locator
1529
+ * suffixed with @attribute-name.
1530
+ */
1531
+ BrowserBot.prototype.findAttribute = function(locator) {
1532
+ // Split into locator + attributeName
1533
+ var attributePos = locator.lastIndexOf("@");
1534
+ var elementLocator = locator.slice(0, attributePos);
1535
+ var attributeName = locator.slice(attributePos + 1);
1536
+
1537
+ // Find the element.
1538
+ var element = this.findElement(elementLocator);
1539
+
1540
+ // Handle missing "class" attribute in IE.
1541
+ if (browserVersion.isIE && attributeName == "class") {
1542
+ attributeName = "className";
1543
+ }
1544
+
1545
+ // Get the attribute value.
1546
+ var attributeValue = element.getAttribute(attributeName);
1547
+
1548
+ // IE returns an object for the "style" attribute
1549
+ if (attributeName == 'style' && typeof(attributeValue) != 'string') {
1550
+ attributeValue = attributeValue.cssText;
1551
+ }
1552
+
1553
+ return attributeValue ? attributeValue.toString() : null;
1554
+ };
1555
+
1556
+ /*
1557
+ * Select the specified option and trigger the relevant events of the element.
1558
+ */
1559
+ BrowserBot.prototype.selectOption = function(element, optionToSelect) {
1560
+ triggerEvent(element, 'focus', false);
1561
+ var changed = false;
1562
+ for (var i = 0; i < element.options.length; i++) {
1563
+ var option = element.options[i];
1564
+ if (option.selected && option != optionToSelect) {
1565
+ option.selected = false;
1566
+ changed = true;
1567
+ }
1568
+ else if (!option.selected && option == optionToSelect) {
1569
+ option.selected = true;
1570
+ changed = true;
1571
+ }
1572
+ }
1573
+
1574
+ if (changed) {
1575
+ triggerEvent(element, 'change', true);
1576
+ }
1577
+ };
1578
+
1579
+ /*
1580
+ * Select the specified option and trigger the relevant events of the element.
1581
+ */
1582
+ BrowserBot.prototype.addSelection = function(element, option) {
1583
+ this.checkMultiselect(element);
1584
+ triggerEvent(element, 'focus', false);
1585
+ if (!option.selected) {
1586
+ option.selected = true;
1587
+ triggerEvent(element, 'change', true);
1588
+ }
1589
+ };
1590
+
1591
+ /*
1592
+ * Select the specified option and trigger the relevant events of the element.
1593
+ */
1594
+ BrowserBot.prototype.removeSelection = function(element, option) {
1595
+ this.checkMultiselect(element);
1596
+ triggerEvent(element, 'focus', false);
1597
+ if (option.selected) {
1598
+ option.selected = false;
1599
+ triggerEvent(element, 'change', true);
1600
+ }
1601
+ };
1602
+
1603
+ BrowserBot.prototype.checkMultiselect = function(element) {
1604
+ if (!element.multiple)
1605
+ {
1606
+ throw new SeleniumError("Not a multi-select");
1607
+ }
1608
+
1609
+ };
1610
+
1611
+ BrowserBot.prototype.replaceText = function(element, stringValue) {
1612
+ triggerEvent(element, 'focus', false);
1613
+ triggerEvent(element, 'select', true);
1614
+ var maxLengthAttr = element.getAttribute("maxLength");
1615
+ var actualValue = stringValue;
1616
+ if (maxLengthAttr != null) {
1617
+ var maxLength = parseInt(maxLengthAttr);
1618
+ if (stringValue.length > maxLength) {
1619
+ actualValue = stringValue.substr(0, maxLength);
1620
+ }
1621
+ }
1622
+
1623
+ if (getTagName(element) == "body") {
1624
+ if (element.ownerDocument && element.ownerDocument.designMode) {
1625
+ var designMode = new String(element.ownerDocument.designMode).toLowerCase();
1626
+ if (designMode = "on") {
1627
+ // this must be a rich text control!
1628
+ element.innerHTML = actualValue;
1629
+ }
1630
+ }
1631
+ } else {
1632
+ element.value = actualValue;
1633
+ }
1634
+ // DGF this used to be skipped in chrome URLs, but no longer. Is xpcnativewrappers to blame?
1635
+ try {
1636
+ triggerEvent(element, 'change', true);
1637
+ } catch (e) {}
1638
+ };
1639
+
1640
+ BrowserBot.prototype.submit = function(formElement) {
1641
+ var actuallySubmit = true;
1642
+ this._modifyElementTarget(formElement);
1643
+ if (formElement.onsubmit) {
1644
+ if (browserVersion.isHTA) {
1645
+ // run the code in the correct window so alerts are handled correctly even in HTA mode
1646
+ var win = this.browserbot.getCurrentWindow();
1647
+ var now = new Date().getTime();
1648
+ var marker = 'marker' + now;
1649
+ win[marker] = formElement;
1650
+ win.setTimeout("var actuallySubmit = "+marker+".onsubmit();" +
1651
+ "if (actuallySubmit) { " +
1652
+ marker+".submit(); " +
1653
+ "if ("+marker+".target && !/^_/.test("+marker+".target)) {"+
1654
+ "window.open('', "+marker+".target);"+
1655
+ "}"+
1656
+ "};"+
1657
+ marker+"=null", 0);
1658
+ // pause for up to 2s while this command runs
1659
+ var terminationCondition = function () {
1660
+ return !win[marker];
1661
+ }
1662
+ return Selenium.decorateFunctionWithTimeout(terminationCondition, 2000);
1663
+ } else {
1664
+ actuallySubmit = formElement.onsubmit();
1665
+ if (actuallySubmit) {
1666
+ formElement.submit();
1667
+ if (formElement.target && !/^_/.test(formElement.target)) {
1668
+ this.browserbot.openWindow('', formElement.target);
1669
+ }
1670
+ }
1671
+ }
1672
+ } else {
1673
+ formElement.submit();
1674
+ }
1675
+ }
1676
+
1677
+ BrowserBot.prototype.clickElement = function(element, clientX, clientY) {
1678
+ this._fireEventOnElement("click", element, clientX, clientY);
1679
+ };
1680
+
1681
+ BrowserBot.prototype.doubleClickElement = function(element, clientX, clientY) {
1682
+ this._fireEventOnElement("dblclick", element, clientX, clientY);
1683
+ };
1684
+
1685
+ // The contextmenu event is fired when the user right-clicks to open the context menu
1686
+ BrowserBot.prototype.contextMenuOnElement = function(element, clientX, clientY) {
1687
+ this._fireEventOnElement("contextmenu", element, clientX, clientY);
1688
+ };
1689
+
1690
+ BrowserBot.prototype._modifyElementTarget = function(element) {
1691
+ if (element.target) {
1692
+ if (element.target == "_blank" || /^selenium_blank/.test(element.target) ) {
1693
+ var tagName = getTagName(element);
1694
+ if (tagName == "a" || tagName == "form") {
1695
+ var newTarget = "selenium_blank" + Math.round(100000 * Math.random());
1696
+ LOG.warn("Link has target '_blank', which is not supported in Selenium! Randomizing target to be: " + newTarget);
1697
+ this.browserbot.openWindow('', newTarget);
1698
+ element.target = newTarget;
1699
+ }
1700
+ }
1701
+ }
1702
+ }
1703
+
1704
+
1705
+ BrowserBot.prototype._handleClickingImagesInsideLinks = function(targetWindow, element) {
1706
+ var itrElement = element;
1707
+ while (itrElement != null) {
1708
+ if (itrElement.href) {
1709
+ targetWindow.location.href = itrElement.href;
1710
+ break;
1711
+ }
1712
+ itrElement = itrElement.parentNode;
1713
+ }
1714
+ }
1715
+
1716
+ BrowserBot.prototype._getTargetWindow = function(element) {
1717
+ var targetWindow = element.ownerDocument.defaultView;
1718
+ if (element.target) {
1719
+ targetWindow = this._getFrameFromGlobal(element.target);
1720
+ }
1721
+ return targetWindow;
1722
+ }
1723
+
1724
+ BrowserBot.prototype._getFrameFromGlobal = function(target) {
1725
+
1726
+ if (target == "_self") {
1727
+ return this.getCurrentWindow();
1728
+ }
1729
+ if (target == "_top") {
1730
+ return this.topFrame;
1731
+ } else if (target == "_parent") {
1732
+ return this.getCurrentWindow().parent;
1733
+ } else if (target == "_blank") {
1734
+ // TODO should this set cleverer window defaults?
1735
+ return this.getCurrentWindow().open('', '_blank');
1736
+ }
1737
+ var frameElement = this.findElementBy("implicit", target, this.topFrame.document, this.topFrame);
1738
+ if (frameElement) {
1739
+ return frameElement.contentWindow;
1740
+ }
1741
+ var win = this.getWindowByName(target);
1742
+ if (win) return win;
1743
+ return this.getCurrentWindow().open('', target);
1744
+ }
1745
+
1746
+
1747
+ BrowserBot.prototype.bodyText = function() {
1748
+ if (!this.getDocument().body) {
1749
+ throw new SeleniumError("Couldn't access document.body. Is this HTML page fully loaded?");
1750
+ }
1751
+ return getText(this.getDocument().body);
1752
+ };
1753
+
1754
+ BrowserBot.prototype.getAllButtons = function() {
1755
+ var elements = this.getDocument().getElementsByTagName('input');
1756
+ var result = [];
1757
+
1758
+ for (var i = 0; i < elements.length; i++) {
1759
+ if (elements[i].type == 'button' || elements[i].type == 'submit' || elements[i].type == 'reset') {
1760
+ result.push(elements[i].id);
1761
+ }
1762
+ }
1763
+
1764
+ return result;
1765
+ };
1766
+
1767
+
1768
+ BrowserBot.prototype.getAllFields = function() {
1769
+ var elements = this.getDocument().getElementsByTagName('input');
1770
+ var result = [];
1771
+
1772
+ for (var i = 0; i < elements.length; i++) {
1773
+ if (elements[i].type == 'text') {
1774
+ result.push(elements[i].id);
1775
+ }
1776
+ }
1777
+
1778
+ return result;
1779
+ };
1780
+
1781
+ BrowserBot.prototype.getAllLinks = function() {
1782
+ var elements = this.getDocument().getElementsByTagName('a');
1783
+ var result = [];
1784
+
1785
+ for (var i = 0; i < elements.length; i++) {
1786
+ result.push(elements[i].id);
1787
+ }
1788
+
1789
+ return result;
1790
+ };
1791
+
1792
+ function isDefined(value) {
1793
+ return typeof(value) != undefined;
1794
+ }
1795
+
1796
+ BrowserBot.prototype.goBack = function() {
1797
+ this.getCurrentWindow().history.back();
1798
+ };
1799
+
1800
+ BrowserBot.prototype.goForward = function() {
1801
+ this.getCurrentWindow().history.forward();
1802
+ };
1803
+
1804
+ BrowserBot.prototype.close = function() {
1805
+ if (browserVersion.isIE) {
1806
+ // fix "do you want to close this window" warning in IE
1807
+ // You can only close windows that you have opened.
1808
+ // So, let's "open" it.
1809
+ try {
1810
+ this.topFrame.name=new Date().getTime();
1811
+ window.open("", this.topFrame.name, "");
1812
+ this.topFrame.close();
1813
+ return;
1814
+ } catch (e) {}
1815
+ }
1816
+ if (browserVersion.isChrome || browserVersion.isSafari || browserVersion.isOpera) {
1817
+ this.topFrame.close();
1818
+ } else {
1819
+ this.getCurrentWindow().eval("window.top.close();");
1820
+ }
1821
+ };
1822
+
1823
+ BrowserBot.prototype.refresh = function() {
1824
+ this.getCurrentWindow().location.reload(true);
1825
+ };
1826
+
1827
+ /**
1828
+ * Refine a list of elements using a filter.
1829
+ */
1830
+ BrowserBot.prototype.selectElementsBy = function(filterType, filter, elements) {
1831
+ var filterFunction = BrowserBot.filterFunctions[filterType];
1832
+ if (! filterFunction) {
1833
+ throw new SeleniumError("Unrecognised element-filter type: '" + filterType + "'");
1834
+ }
1835
+
1836
+ return filterFunction(filter, elements);
1837
+ };
1838
+
1839
+ BrowserBot.filterFunctions = {};
1840
+
1841
+ BrowserBot.filterFunctions.name = function(name, elements) {
1842
+ var selectedElements = [];
1843
+ for (var i = 0; i < elements.length; i++) {
1844
+ if (elements[i].name === name) {
1845
+ selectedElements.push(elements[i]);
1846
+ }
1847
+ }
1848
+ return selectedElements;
1849
+ };
1850
+
1851
+ BrowserBot.filterFunctions.value = function(value, elements) {
1852
+ var selectedElements = [];
1853
+ for (var i = 0; i < elements.length; i++) {
1854
+ if (elements[i].value === value) {
1855
+ selectedElements.push(elements[i]);
1856
+ }
1857
+ }
1858
+ return selectedElements;
1859
+ };
1860
+
1861
+ BrowserBot.filterFunctions.index = function(index, elements) {
1862
+ index = Number(index);
1863
+ if (isNaN(index) || index < 0) {
1864
+ throw new SeleniumError("Illegal Index: " + index);
1865
+ }
1866
+ if (elements.length <= index) {
1867
+ throw new SeleniumError("Index out of range: " + index);
1868
+ }
1869
+ return [elements[index]];
1870
+ };
1871
+
1872
+ BrowserBot.prototype.selectElements = function(filterExpr, elements, defaultFilterType) {
1873
+
1874
+ var filterType = (defaultFilterType || 'value');
1875
+
1876
+ // If there is a filter prefix, use the specified strategy
1877
+ var result = filterExpr.match(/^([A-Za-z]+)=(.+)/);
1878
+ if (result) {
1879
+ filterType = result[1].toLowerCase();
1880
+ filterExpr = result[2];
1881
+ }
1882
+
1883
+ return this.selectElementsBy(filterType, filterExpr, elements);
1884
+ };
1885
+
1886
+ /**
1887
+ * Find an element by class
1888
+ */
1889
+ BrowserBot.prototype.locateElementByClass = function(locator, document) {
1890
+ return elementFindFirstMatchingChild(document,
1891
+ function(element) {
1892
+ return element.className == locator
1893
+ }
1894
+ );
1895
+ }
1896
+
1897
+ /**
1898
+ * Find an element by alt
1899
+ */
1900
+ BrowserBot.prototype.locateElementByAlt = function(locator, document) {
1901
+ return elementFindFirstMatchingChild(document,
1902
+ function(element) {
1903
+ return element.alt == locator
1904
+ }
1905
+ );
1906
+ }
1907
+
1908
+ /**
1909
+ * Find an element by css selector
1910
+ */
1911
+ BrowserBot.prototype.locateElementByCss = function(locator, document) {
1912
+ var elements = eval_css(locator, document);
1913
+ if (elements.length != 0)
1914
+ return elements[0];
1915
+ return null;
1916
+ }
1917
+
1918
+ /**
1919
+ * This function is responsible for mapping a UI specifier string to an element
1920
+ * on the page, and returning it. If no element is found, null is returned.
1921
+ * Returning null on failure to locate the element is part of the undocumented
1922
+ * API for locator strategies.
1923
+ */
1924
+ BrowserBot.prototype.locateElementByUIElement = function(locator, inDocument) {
1925
+ // offset locators are delimited by "->", which is much simpler than the
1926
+ // previous scheme involving detecting the close-paren.
1927
+ var locators = locator.split(/->/, 2);
1928
+
1929
+ var locatedElement = null;
1930
+ var pageElements = UIMap.getInstance()
1931
+ .getPageElements(locators[0], inDocument);
1932
+
1933
+ if (locators.length > 1) {
1934
+ for (var i = 0; i < pageElements.length; ++i) {
1935
+ var locatedElements = eval_locator(locators[1], inDocument,
1936
+ pageElements[i]);
1937
+ if (locatedElements.length) {
1938
+ locatedElement = locatedElements[0];
1939
+ break;
1940
+ }
1941
+ }
1942
+ }
1943
+ else if (pageElements.length) {
1944
+ locatedElement = pageElements[0];
1945
+ }
1946
+
1947
+ return locatedElement;
1948
+ }
1949
+
1950
+ BrowserBot.prototype.locateElementByUIElement.prefix = 'ui';
1951
+
1952
+ // define a function used to compare the result of a close UI element
1953
+ // match with the actual interacted element. If they are close enough
1954
+ // according to the heuristic, consider them a match.
1955
+ /**
1956
+ * A heuristic function for comparing a node with a target node. Typically the
1957
+ * node is specified in a UI element definition, while the target node is
1958
+ * returned by the recorder as the leaf element which had some event enacted
1959
+ * upon it. This particular heuristic covers the case where the anchor element
1960
+ * contains other inline tags, such as "em" or "img".
1961
+ *
1962
+ * @param node the node being compared to the target node
1963
+ * @param target the target node
1964
+ * @return true if node equals target, or if node is a link
1965
+ * element and target is its descendant, or if node has
1966
+ * an onclick attribute and target is its descendant.
1967
+ * False otherwise.
1968
+ */
1969
+ BrowserBot.prototype.locateElementByUIElement.is_fuzzy_match = function(node, target) {
1970
+ try {
1971
+ var isMatch = (
1972
+ (node == target) ||
1973
+ ((node.nodeName == 'A' || node.onclick) && is_ancestor(node, target))
1974
+ );
1975
+ return isMatch;
1976
+ }
1977
+ catch (e) {
1978
+ return false;
1979
+ }
1980
+ };
1981
+
1982
+ /*****************************************************************/
1983
+ /* BROWSER-SPECIFIC FUNCTIONS ONLY AFTER THIS LINE */
1984
+
1985
+ function MozillaBrowserBot(frame) {
1986
+ BrowserBot.call(this, frame);
1987
+ }
1988
+ objectExtend(MozillaBrowserBot.prototype, BrowserBot.prototype);
1989
+
1990
+ function KonquerorBrowserBot(frame) {
1991
+ BrowserBot.call(this, frame);
1992
+ }
1993
+ objectExtend(KonquerorBrowserBot.prototype, BrowserBot.prototype);
1994
+
1995
+ KonquerorBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
1996
+ // Window doesn't fire onload event when setting src to the current value,
1997
+ // so we set it to blank first.
1998
+ iframe.src = "about:blank";
1999
+ iframe.src = location;
2000
+ };
2001
+
2002
+ KonquerorBrowserBot.prototype.setOpenLocation = function(win, loc) {
2003
+ // Window doesn't fire onload event when setting src to the current value,
2004
+ // so we just refresh in that case instead.
2005
+ loc = absolutify(loc, this.baseUrl);
2006
+ loc = canonicalize(loc);
2007
+ var startUrl = win.location.href;
2008
+ if ("about:blank" != win.location.href) {
2009
+ var startLoc = parseUrl(win.location.href);
2010
+ startLoc.hash = null;
2011
+ var startUrl = reassembleLocation(startLoc);
2012
+ }
2013
+ LOG.debug("startUrl="+startUrl);
2014
+ LOG.debug("win.location.href="+win.location.href);
2015
+ LOG.debug("loc="+loc);
2016
+ if (startUrl == loc) {
2017
+ LOG.debug("opening exact same location");
2018
+ this.refresh();
2019
+ } else {
2020
+ LOG.debug("locations differ");
2021
+ win.location.href = loc;
2022
+ }
2023
+ // force the current polling thread to detect a page load
2024
+ var marker = this.isPollingForLoad(win);
2025
+ if (marker) {
2026
+ delete win.location[marker];
2027
+ }
2028
+ };
2029
+
2030
+ KonquerorBrowserBot.prototype._isSameDocument = function(originalDocument, currentDocument) {
2031
+ // under Konqueror, there may be this case:
2032
+ // originalDocument and currentDocument are different objects
2033
+ // while their location are same.
2034
+ if (originalDocument) {
2035
+ return originalDocument.location == currentDocument.location
2036
+ } else {
2037
+ return originalDocument === currentDocument;
2038
+ }
2039
+ };
2040
+
2041
+ function SafariBrowserBot(frame) {
2042
+ BrowserBot.call(this, frame);
2043
+ }
2044
+ objectExtend(SafariBrowserBot.prototype, BrowserBot.prototype);
2045
+
2046
+ SafariBrowserBot.prototype.setIFrameLocation = KonquerorBrowserBot.prototype.setIFrameLocation;
2047
+ SafariBrowserBot.prototype.setOpenLocation = KonquerorBrowserBot.prototype.setOpenLocation;
2048
+
2049
+
2050
+ function OperaBrowserBot(frame) {
2051
+ BrowserBot.call(this, frame);
2052
+ }
2053
+ objectExtend(OperaBrowserBot.prototype, BrowserBot.prototype);
2054
+ OperaBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
2055
+ if (iframe.src == location) {
2056
+ iframe.src = location + '?reload';
2057
+ } else {
2058
+ iframe.src = location;
2059
+ }
2060
+ }
2061
+
2062
+ function IEBrowserBot(frame) {
2063
+ BrowserBot.call(this, frame);
2064
+ }
2065
+ objectExtend(IEBrowserBot.prototype, BrowserBot.prototype);
2066
+
2067
+ IEBrowserBot.prototype._handleClosedSubFrame = function(testWindow, doNotModify) {
2068
+ if (this.proxyInjectionMode) {
2069
+ return testWindow;
2070
+ }
2071
+
2072
+ try {
2073
+ testWindow.location.href;
2074
+ this.permDenied = 0;
2075
+ } catch (e) {
2076
+ this.permDenied++;
2077
+ }
2078
+ if (this._windowClosed(testWindow) || this.permDenied > 4) {
2079
+ if (this.isSubFrameSelected) {
2080
+ LOG.warn("Current subframe appears to have closed; selecting top frame");
2081
+ this.selectFrame("relative=top");
2082
+ return this.getCurrentWindow(doNotModify);
2083
+ } else {
2084
+ var closedError = new SeleniumError("Current window or frame is closed!");
2085
+ closedError.windowClosed = true;
2086
+ throw closedError;
2087
+ }
2088
+ }
2089
+ return testWindow;
2090
+ };
2091
+
2092
+ IEBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
2093
+ BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
2094
+
2095
+ // we will call the previous version of this method from within our own interception
2096
+ oldShowModalDialog = windowToModify.showModalDialog;
2097
+
2098
+ windowToModify.showModalDialog = function(url, args, features) {
2099
+ // Get relative directory to where TestRunner.html lives
2100
+ // A risky assumption is that the user's TestRunner is named TestRunner.html
2101
+ var doc_location = document.location.toString();
2102
+ var end_of_base_ref = doc_location.indexOf('TestRunner.html');
2103
+ var base_ref = doc_location.substring(0, end_of_base_ref);
2104
+ var runInterval = '';
2105
+
2106
+ // Only set run interval if options is defined
2107
+ if (typeof(window.runOptions) != 'undefined') {
2108
+ runInterval = "&runInterval=" + runOptions.runInterval;
2109
+ }
2110
+
2111
+ var testRunnerURL = "TestRunner.html?auto=true&singletest="
2112
+ + escape(browserBot.modalDialogTest)
2113
+ + "&autoURL="
2114
+ + escape(url)
2115
+ + runInterval;
2116
+ var fullURL = base_ref + testRunnerURL;
2117
+ browserBot.modalDialogTest = null;
2118
+
2119
+ // If using proxy injection mode
2120
+ if (this.proxyInjectionMode) {
2121
+ var sessionId = runOptions.getSessionId();
2122
+ if (sessionId == undefined) {
2123
+ sessionId = injectedSessionId;
2124
+ }
2125
+ if (sessionId != undefined) {
2126
+ LOG.debug("Invoking showModalDialog and injecting URL " + fullURL);
2127
+ }
2128
+ fullURL = url;
2129
+ }
2130
+ var returnValue = oldShowModalDialog(fullURL, args, features);
2131
+ return returnValue;
2132
+ };
2133
+ };
2134
+
2135
+ IEBrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads = function(windowObject) {
2136
+ this.pageUnloading = false;
2137
+ var self = this;
2138
+ var pageUnloadDetector = function() {
2139
+ self.pageUnloading = true;
2140
+ };
2141
+ windowObject.attachEvent("onbeforeunload", pageUnloadDetector);
2142
+ BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads.call(this, windowObject);
2143
+ };
2144
+
2145
+ IEBrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
2146
+ LOG.debug("IEBrowserBot.pollForLoad: " + marker);
2147
+ if (!this.permDeniedCount[marker]) this.permDeniedCount[marker] = 0;
2148
+ BrowserBot.prototype.pollForLoad.call(this, loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
2149
+ if (this.pageLoadError) {
2150
+ if (this.pageUnloading) {
2151
+ var self = this;
2152
+ LOG.debug("pollForLoad UNLOADING (" + marker + "): caught exception while firing events on unloading page: " + this.pageLoadError.message);
2153
+ this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
2154
+ this.pageLoadError = null;
2155
+ return;
2156
+ } else if (((this.pageLoadError.message == "Permission denied") || (/^Access is denied/.test(this.pageLoadError.message)))
2157
+ && this.permDeniedCount[marker]++ < 8) {
2158
+ if (this.permDeniedCount[marker] > 4) {
2159
+ var canAccessThisWindow;
2160
+ var canAccessCurrentlySelectedWindow;
2161
+ try {
2162
+ windowObject.location.href;
2163
+ canAccessThisWindow = true;
2164
+ } catch (e) {}
2165
+ try {
2166
+ this.getCurrentWindow(true).location.href;
2167
+ canAccessCurrentlySelectedWindow = true;
2168
+ } catch (e) {}
2169
+ if (canAccessCurrentlySelectedWindow & !canAccessThisWindow) {
2170
+ LOG.debug("pollForLoad (" + marker + ") ABORTING: " + this.pageLoadError.message + " (" + this.permDeniedCount[marker] + "), but the currently selected window is fine");
2171
+ // returning without rescheduling
2172
+ this.pageLoadError = null;
2173
+ return;
2174
+ }
2175
+ }
2176
+
2177
+ var self = this;
2178
+ LOG.debug("pollForLoad (" + marker + "): " + this.pageLoadError.message + " (" + this.permDeniedCount[marker] + "), waiting to see if it goes away");
2179
+ this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
2180
+ this.pageLoadError = null;
2181
+ return;
2182
+ }
2183
+ //handy for debugging!
2184
+ //throw this.pageLoadError;
2185
+ }
2186
+ };
2187
+
2188
+ IEBrowserBot.prototype._windowClosed = function(win) {
2189
+ try {
2190
+ var c = win.closed;
2191
+ // frame windows claim to be non-closed when their parents are closed
2192
+ // but you can't access their document objects in that case
2193
+ if (!c) {
2194
+ try {
2195
+ win.document;
2196
+ } catch (de) {
2197
+ if (de.message == "Permission denied") {
2198
+ // the window is probably unloading, which means it's probably not closed yet
2199
+ return false;
2200
+ }
2201
+ else if (/^Access is denied/.test(de.message)) {
2202
+ // rare variation on "Permission denied"?
2203
+ LOG.debug("IEBrowserBot.windowClosed: got " + de.message + " (this.pageUnloading=" + this.pageUnloading + "); assuming window is unloading, probably not closed yet");
2204
+ return false;
2205
+ } else {
2206
+ // this is probably one of those frame window situations
2207
+ LOG.debug("IEBrowserBot.windowClosed: couldn't read win.document, assume closed: " + de.message + " (this.pageUnloading=" + this.pageUnloading + ")");
2208
+ return true;
2209
+ }
2210
+ }
2211
+ }
2212
+ if (c == null) {
2213
+ LOG.debug("IEBrowserBot.windowClosed: win.closed was null, assuming closed");
2214
+ return true;
2215
+ }
2216
+ return c;
2217
+ } catch (e) {
2218
+ LOG.debug("IEBrowserBot._windowClosed: Got an exception trying to read win.closed; we'll have to take a guess!");
2219
+
2220
+ if (browserVersion.isHTA) {
2221
+ if (e.message == "Permission denied") {
2222
+ // the window is probably unloading, which means it's not closed yet
2223
+ return false;
2224
+ } else {
2225
+ // there's a good chance that we've lost contact with the window object if it is closed
2226
+ return true;
2227
+ }
2228
+ } else {
2229
+ // the window is probably unloading, which means it's not closed yet
2230
+ return false;
2231
+ }
2232
+ }
2233
+ };
2234
+
2235
+ /**
2236
+ * In IE, getElementById() also searches by name - this is an optimisation for IE.
2237
+ */
2238
+ IEBrowserBot.prototype.locateElementByIdentifer = function(identifier, inDocument, inWindow) {
2239
+ return inDocument.getElementById(identifier);
2240
+ };
2241
+
2242
+ SafariBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
2243
+ BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
2244
+
2245
+ var originalOpen = windowToModify.open;
2246
+ /*
2247
+ * Safari seems to be broken, so that when we manually trigger the onclick method
2248
+ * of a button/href, any window.open calls aren't resolved relative to the app location.
2249
+ * So here we replace the open() method with one that does resolve the url correctly.
2250
+ */
2251
+ windowToModify.open = function(url, windowName, windowFeatures, replaceFlag) {
2252
+
2253
+ if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("/")) {
2254
+ return originalOpen(url, windowName, windowFeatures, replaceFlag);
2255
+ }
2256
+
2257
+ // Reduce the current path to the directory
2258
+ var currentPath = windowToModify.location.pathname || "/";
2259
+ currentPath = currentPath.replace(/\/[^\/]*$/, "/");
2260
+
2261
+ // Remove any leading "./" from the new url.
2262
+ url = url.replace(/^\.\//, "");
2263
+
2264
+ newUrl = currentPath + url;
2265
+
2266
+ var openedWindow = originalOpen(newUrl, windowName, windowFeatures, replaceFlag);
2267
+ LOG.debug("window.open call intercepted; window ID (which you can use with selectWindow()) is \"" + windowName + "\"");
2268
+ if (windowName!=null) {
2269
+ openedWindow["seleniumWindowName"] = windowName;
2270
+ }
2271
+ return openedWindow;
2272
+ };
2273
+ };
2274
+
2275
+ MozillaBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
2276
+ var win = this.getCurrentWindow();
2277
+ triggerEvent(element, 'focus', false);
2278
+
2279
+ // Add an event listener that detects if the default action has been prevented.
2280
+ // (This is caused by a javascript onclick handler returning false)
2281
+ // we capture the whole event, rather than the getPreventDefault() state at the time,
2282
+ // because we need to let the entire event bubbling and capturing to go through
2283
+ // before making a decision on whether we should force the href
2284
+ var savedEvent = null;
2285
+
2286
+ element.addEventListener(eventType, function(evt) {
2287
+ savedEvent = evt;
2288
+ }, false);
2289
+
2290
+ this._modifyElementTarget(element);
2291
+
2292
+ // Trigger the event.
2293
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
2294
+
2295
+ if (this._windowClosed(win)) {
2296
+ return;
2297
+ }
2298
+
2299
+ // Perform the link action if preventDefault was set.
2300
+ // In chrome URL, the link action is already executed by triggerMouseEvent.
2301
+ if (!browserVersion.isChrome && savedEvent != null && !savedEvent.getPreventDefault()) {
2302
+ var targetWindow = this.browserbot._getTargetWindow(element);
2303
+ if (element.href) {
2304
+ targetWindow.location.href = element.href;
2305
+ } else {
2306
+ this.browserbot._handleClickingImagesInsideLinks(targetWindow, element);
2307
+ }
2308
+ }
2309
+
2310
+ };
2311
+
2312
+
2313
+ OperaBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
2314
+ var win = this.getCurrentWindow();
2315
+ triggerEvent(element, 'focus', false);
2316
+
2317
+ this._modifyElementTarget(element);
2318
+
2319
+ // Trigger the click event.
2320
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
2321
+
2322
+ if (this._windowClosed(win)) {
2323
+ return;
2324
+ }
2325
+
2326
+ };
2327
+
2328
+
2329
+ KonquerorBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
2330
+ var win = this.getCurrentWindow();
2331
+ triggerEvent(element, 'focus', false);
2332
+
2333
+ this._modifyElementTarget(element);
2334
+
2335
+ if (element[eventType]) {
2336
+ element[eventType]();
2337
+ }
2338
+ else {
2339
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
2340
+ }
2341
+
2342
+ if (this._windowClosed(win)) {
2343
+ return;
2344
+ }
2345
+
2346
+ };
2347
+
2348
+ SafariBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
2349
+ triggerEvent(element, 'focus', false);
2350
+ var wasChecked = element.checked;
2351
+
2352
+ this._modifyElementTarget(element);
2353
+
2354
+ // For form element it is simple.
2355
+ if (element[eventType]) {
2356
+ element[eventType]();
2357
+ }
2358
+ // For links and other elements, event emulation is required.
2359
+ else {
2360
+ var targetWindow = this.browserbot._getTargetWindow(element);
2361
+ // todo: deal with anchors?
2362
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
2363
+
2364
+ }
2365
+
2366
+ };
2367
+
2368
+ SafariBrowserBot.prototype.refresh = function() {
2369
+ var win = this.getCurrentWindow();
2370
+ if (win.location.hash) {
2371
+ // DGF Safari refuses to refresh when there's a hash symbol in the URL
2372
+ win.location.hash = "";
2373
+ var actuallyReload = function() {
2374
+ win.location.reload(true);
2375
+ }
2376
+ window.setTimeout(actuallyReload, 1);
2377
+ } else {
2378
+ win.location.reload(true);
2379
+ }
2380
+ };
2381
+
2382
+ IEBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
2383
+ var win = this.getCurrentWindow();
2384
+ triggerEvent(element, 'focus', false);
2385
+
2386
+ var wasChecked = element.checked;
2387
+
2388
+ // Set a flag that records if the page will unload - this isn't always accurate, because
2389
+ // <a href="javascript:alert('foo'):"> triggers the onbeforeunload event, even thought the page won't unload
2390
+ var pageUnloading = false;
2391
+ var pageUnloadDetector = function() {
2392
+ pageUnloading = true;
2393
+ };
2394
+ win.attachEvent("onbeforeunload", pageUnloadDetector);
2395
+ this._modifyElementTarget(element);
2396
+ if (element[eventType]) {
2397
+ element[eventType]();
2398
+ }
2399
+ else {
2400
+ this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
2401
+ }
2402
+
2403
+
2404
+ // If the page is going to unload - still attempt to fire any subsequent events.
2405
+ // However, we can't guarantee that the page won't unload half way through, so we need to handle exceptions.
2406
+ try {
2407
+ win.detachEvent("onbeforeunload", pageUnloadDetector);
2408
+
2409
+ if (this._windowClosed(win)) {
2410
+ return;
2411
+ }
2412
+
2413
+ // Onchange event is not triggered automatically in IE.
2414
+ if (isDefined(element.checked) && wasChecked != element.checked) {
2415
+ triggerEvent(element, 'change', true);
2416
+ }
2417
+
2418
+ }
2419
+ catch (e) {
2420
+ // If the page is unloading, we may get a "Permission denied" or "Unspecified error".
2421
+ // Just ignore it, because the document may have unloaded.
2422
+ if (pageUnloading) {
2423
+ LOG.logHook = function() {
2424
+ };
2425
+ LOG.warn("Caught exception when firing events on unloading page: " + e.message);
2426
+ return;
2427
+ }
2428
+ throw e;
2429
+ }
2430
+ };