~vasilev/psiphon/sprint3-579678

« back to all changes in this revision

Viewing changes to trunk/testing/selenium_scripts/selenium/core/scripts/selenium-browserbot.js

  • Committer: Adam Pritchard
  • Date: 2010-07-16 19:31:33 UTC
  • mfrom: (95.1.8 testing-2.5)
  • Revision ID: adam@adampsidev-20100716193133-n6rxnrqwt68d0ck8
Made Selenium work; added some tests

Show diffs side-by-side

added added

removed removed

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