Project

General

Profile

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
    
40
    // We need to know this in advance, in case the frame closes unexpectedly
41
    this.isSubFrameSelected = false;
42
    
43
    this.altKeyDown = false;
44
    this.controlKeyDown = false;
45
    this.shiftKeyDown = false;
46
    this.metaKeyDown = false;
47

    
48
    this.modalDialogTest = null;
49
    this.recordedAlerts = new Array();
50
    this.recordedConfirmations = new Array();
51
    this.recordedPrompts = new Array();
52
    this.openedWindows = {};
53
    this.nextConfirmResult = true;
54
    this.nextPromptResult = '';
55
    this.newPageLoaded = false;
56
    this.pageLoadError = null;
57
    
58
    this.shouldHighlightLocatedElement = false;
59

    
60
    this.uniqueId = new Date().getTime();
61
    this.pollingForLoad = new Object();
62
    this.permDeniedCount = new Object();
63
    this.windowPollers = new Array();
64
    // DGF for backwards compatibility
65
    this.browserbot = this;
66

    
67
    var self = this;
68
    
69
    objectExtend(this, PageBot.prototype);
70
    this._registerAllLocatorFunctions();
71
    
72
    this.recordPageLoad = function(elementOrWindow) {
73
        LOG.debug("Page load detected");
74
        try {
75
            if (elementOrWindow.location && elementOrWindow.location.href) {
76
                LOG.debug("Page load location=" + elementOrWindow.location.href);
77
            } else if (elementOrWindow.contentWindow && elementOrWindow.contentWindow.location && elementOrWindow.contentWindow.location.href) {
78
                LOG.debug("Page load location=" + elementOrWindow.contentWindow.location.href);
79
            } else {
80
                LOG.debug("Page load location unknown, current window location=" + this.getCurrentWindow(true).location);
81
            }
82
        } catch (e) {
83
            LOG.error("Caught an exception attempting to log location; this should get noticed soon!");
84
            LOG.exception(e);
85
            self.pageLoadError = e;
86
            return;
87
        }
88
        self.newPageLoaded = true;
89
    };
90

    
91
    this.isNewPageLoaded = function() {
92
        if (this.pageLoadError) {
93
            LOG.error("isNewPageLoaded found an old pageLoadError");
94
            var e = this.pageLoadError;
95
            this.pageLoadError = null;
96
            throw e;
97
        }
98
        return self.newPageLoaded;
99
    };
100

    
101
};
102

    
103
// DGF PageBot exists for backwards compatibility with old user-extensions
104
var PageBot = function(){};
105

    
106
BrowserBot.createForWindow = function(window, proxyInjectionMode) {
107
    var browserbot;
108
    LOG.debug('createForWindow');
109
    LOG.debug("browserName: " + browserVersion.name);
110
    LOG.debug("userAgent: " + navigator.userAgent);
111
    if (browserVersion.isIE) {
112
        browserbot = new IEBrowserBot(window);
113
    }
114
    else if (browserVersion.isKonqueror) {
115
        browserbot = new KonquerorBrowserBot(window);
116
    }
117
    else if (browserVersion.isOpera) {
118
        browserbot = new OperaBrowserBot(window);
119
    }
120
    else if (browserVersion.isSafari) {
121
        browserbot = new SafariBrowserBot(window);
122
    }
123
    else {
124
        // Use mozilla by default
125
        browserbot = new MozillaBrowserBot(window);
126
    }
127
    // getCurrentWindow has the side effect of modifying it to handle page loads etc
128
    browserbot.proxyInjectionMode = proxyInjectionMode;
129
    browserbot.getCurrentWindow();	// for modifyWindow side effect.  This is not a transparent style
130
    return browserbot;
131
};
132

    
133
// todo: rename?  This doesn't actually "do" anything.
134
BrowserBot.prototype.doModalDialogTest = function(test) {
135
    this.modalDialogTest = test;
136
};
137

    
138
BrowserBot.prototype.cancelNextConfirmation = function() {
139
    this.nextConfirmResult = false;
140
};
141

    
142
BrowserBot.prototype.setNextPromptResult = function(result) {
143
    this.nextPromptResult = result;
144
};
145

    
146
BrowserBot.prototype.hasAlerts = function() {
147
    return (this.recordedAlerts.length > 0);
148
};
149

    
150
BrowserBot.prototype.relayBotToRC = function() {
151
};
152
// override in injection.html
153

    
154
BrowserBot.prototype.resetPopups = function() {
155
    this.recordedAlerts = [];
156
    this.recordedConfirmations = [];
157
    this.recordedPrompts = [];
158
}
159

    
160
BrowserBot.prototype.getNextAlert = function() {
161
    var t = this.recordedAlerts.shift();
162
    this.relayBotToRC("browserbot.recordedAlerts");
163
    return t;
164
};
165

    
166
BrowserBot.prototype.hasConfirmations = function() {
167
    return (this.recordedConfirmations.length > 0);
168
};
169

    
170
BrowserBot.prototype.getNextConfirmation = function() {
171
    var t = this.recordedConfirmations.shift();
172
    this.relayBotToRC("browserbot.recordedConfirmations");
173
    return t;
174
};
175

    
176
BrowserBot.prototype.hasPrompts = function() {
177
    return (this.recordedPrompts.length > 0);
178
};
179

    
180
BrowserBot.prototype.getNextPrompt = function() {
181
    var t = this.recordedPrompts.shift();
182
    this.relayBotToRC("browserbot.recordedPrompts");
183
    return t;
184
};
185

    
186
/* Fire a mouse event in a browser-compatible manner */
187

    
188
BrowserBot.prototype.triggerMouseEvent = function(element, eventType, canBubble, clientX, clientY) {
189
    clientX = clientX ? clientX : 0;
190
    clientY = clientY ? clientY : 0;
191

    
192
    LOG.warn("triggerMouseEvent assumes setting screenX and screenY to 0 is ok");
193
    var screenX = 0;
194
    var screenY = 0;
195

    
196
    canBubble = (typeof(canBubble) == undefined) ? true : canBubble;
197
    if (element.fireEvent) {
198
        LOG.info("element has fireEvent");
199
        var evt = createEventObject(element, this.controlKeyDown, this.altKeyDown, this.shiftKeyDown, this.metaKeyDown);
200
        evt.detail = 0;
201
        evt.button = 1;
202
        evt.relatedTarget = null;
203
        if (!screenX && !screenY && !clientX && !clientY && !this.controlKeyDown && !this.altKeyDown && !this.shiftKeyDown && !this.metaKeyDown) {
204
            element.fireEvent('on' + eventType);
205
        }
206
        else {
207
            evt.screenX = screenX;
208
            evt.screenY = screenY;
209
            evt.clientX = clientX;
210
            evt.clientY = clientY;
211

    
212
            // when we go this route, window.event is never set to contain the event we have just created.
213
            // ideally we could just slide it in as follows in the try-block below, but this normally
214
            // doesn't work.  This is why I try to avoid this code path, which is only required if we need to
215
            // set attributes on the event (e.g., clientX).
216
            try {
217
                window.event = evt;
218
            }
219
            catch(e) {
220
                // getting an "Object does not support this action or property" error.  Save the event away
221
                // for future reference.
222
                // TODO: is there a way to update window.event?
223

    
224
                // work around for http://jira.openqa.org/browse/SEL-280 -- make the event available somewhere:
225
                selenium.browserbot.getCurrentWindow().selenium_event = evt;
226
            }
227
            element.fireEvent('on' + eventType, evt);
228
        }
229
    }
230
    else {
231
        LOG.info("element doesn't have fireEvent");
232
        var evt = document.createEvent('MouseEvents');
233
        if (evt.initMouseEvent)
234
        {
235
            LOG.info("element has initMouseEvent");
236
            //Safari
237
            evt.initMouseEvent(eventType, canBubble, true, document.defaultView, 1, screenX, screenY, clientX, clientY, 
238
            	this.controlKeyDown, this.altKeyDown, this.shiftKeyDown, this.metaKeyDown, 0, null);
239
        }
240
        else {
241
        	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");
242
            evt.initEvent(eventType, canBubble, true);
243
            
244
            evt.shiftKey = this.shiftKeyDown;
245
            evt.metaKey = this.metaKeyDown;
246
            evt.altKey = this.altKeyDown;
247
            evt.ctrlKey = this.controlKeyDown;
248

    
249
        }
250
        element.dispatchEvent(evt);
251
    }
252
}
253

    
254
BrowserBot.prototype._windowClosed = function(win) {
255
    var c = win.closed;
256
    if (c == null) return true;
257
    return c;
258
};
259

    
260
BrowserBot.prototype._modifyWindow = function(win) {
261
    // In proxyInjectionMode, have to suppress LOG calls in _modifyWindow to avoid an infinite loop
262
    if (this._windowClosed(win)) {
263
        if (!this.proxyInjectionMode) {
264
            LOG.error("modifyWindow: Window was closed!");
265
        }
266
        return null;
267
    }
268
    if (!this.proxyInjectionMode) {
269
        LOG.debug('modifyWindow ' + this.uniqueId + ":" + win[this.uniqueId]);
270
    }
271
    if (!win[this.uniqueId]) {
272
        win[this.uniqueId] = true;
273
        this.modifyWindowToRecordPopUpDialogs(win, this);
274
    }
275
    // In proxyInjection mode, we have our own mechanism for detecting page loads
276
    if (!this.proxyInjectionMode) {
277
        this.modifySeparateTestWindowToDetectPageLoads(win);
278
    }
279
    if (win.frames && win.frames.length && win.frames.length > 0) {
280
        for (var i = 0; i < win.frames.length; i++) {
281
            try {
282
                this._modifyWindow(win.frames[i]);
283
            } catch (e) {} // we're just trying to be opportunistic; don't worry if this doesn't work out
284
        }
285
    }
286
    return win;
287
};
288

    
289
BrowserBot.prototype.selectWindow = function(target) {
290
    if (target && target != "null") {
291
        this._selectWindowByName(target);
292
    } else {
293
        this._selectTopWindow();
294
    }
295
};
296

    
297
BrowserBot.prototype._selectTopWindow = function() {
298
    this.currentWindowName = null;
299
    this.currentWindow = this.topWindow;
300
    this.topFrame = this.topWindow;
301
    this.isSubFrameSelected = false;
302
}
303

    
304
BrowserBot.prototype._selectWindowByName = function(target) {
305
    this.currentWindow = this.getWindowByName(target, false);
306
    this.topFrame = this.currentWindow;
307
    this.currentWindowName = target;
308
    this.isSubFrameSelected = false;
309
}
310

    
311
BrowserBot.prototype.selectFrame = function(target) {
312
    if (target == "relative=up") {
313
        this.currentWindow = this.getCurrentWindow().parent;
314
        this.isSubFrameSelected = (this._getFrameElement(this.currentWindow) != null);
315
    } else if (target == "relative=top") {
316
        this.currentWindow = this.topFrame;
317
        this.isSubFrameSelected = false;
318
    } else {
319
        var frame = this.findElement(target);
320
        if (frame == null) {
321
            throw new SeleniumError("Not found: " + target);
322
        }
323
        // now, did they give us a frame or a frame ELEMENT?
324
        var match = false;
325
        if (frame.contentWindow) {
326
            // this must be a frame element
327
            if (browserVersion.isHTA) {
328
                // stupid HTA bug; can't get in the front door
329
                target = frame.contentWindow.name;
330
            } else {
331
                this.currentWindow = frame.contentWindow;
332
                this.isSubFrameSelected = true;
333
                match = true;
334
            }
335
        } else if (frame.document && frame.location) {
336
            // must be an actual window frame
337
            this.currentWindow = frame;
338
            this.isSubFrameSelected = true;
339
            match = true;
340
        }
341
        
342
        if (!match) {
343
            // neither, let's loop through the frame names
344
            var win = this.getCurrentWindow();
345
            
346
            if (win && win.frames && win.frames.length) {
347
                for (var i = 0; i < win.frames.length; i++) {
348
                    if (win.frames[i].name == target) {
349
                        this.currentWindow = win.frames[i];
350
                        this.isSubFrameSelected = true;
351
                        match = true;
352
                        break;
353
                    }
354
                }
355
            }
356
            if (!match) {
357
                throw new SeleniumError("Not a frame: " + target);
358
            }
359
        }
360
    }
361
    // modifies the window
362
    this.getCurrentWindow();
363
};
364

    
365
BrowserBot.prototype.openLocation = function(target) {
366
    // We're moving to a new page - clear the current one
367
    var win = this.getCurrentWindow();
368
    LOG.debug("openLocation newPageLoaded = false");
369
    this.newPageLoaded = false;
370

    
371
    this.setOpenLocation(win, target);
372
};
373

    
374
BrowserBot.prototype.openWindow = function(url, windowID) {
375
    if (url != "") {
376
        url = absolutify(url, this.baseUrl);
377
    }
378
    if (browserVersion.isHTA) {
379
        // in HTA mode, calling .open on the window interprets the url relative to that window
380
        // we need to absolute-ize the URL to make it consistent
381
        var child = this.getCurrentWindow().open(url, windowID);
382
        selenium.browserbot.openedWindows[windowID] = child;
383
    } else {
384
        this.getCurrentWindow().open(url, windowID);
385
    }
386
};
387

    
388
BrowserBot.prototype.setIFrameLocation = function(iframe, location) {
389
    iframe.src = location;
390
};
391

    
392
BrowserBot.prototype.setOpenLocation = function(win, loc) {
393
    loc = absolutify(loc, this.baseUrl);
394
    if (browserVersion.isHTA) {
395
        var oldHref = win.location.href;
396
        win.location.href = loc;
397
        var marker = null;
398
        try {
399
            marker = this.isPollingForLoad(win);
400
            if (marker && win.location[marker]) {
401
                win.location[marker] = false;
402
            }
403
        } catch (e) {} // DGF don't know why, but this often fails
404
    } else {
405
        win.location.href = loc;
406
    }
407
};
408

    
409
BrowserBot.prototype.getCurrentPage = function() {
410
    return this;
411
};
412

    
413
BrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
414
    var self = this;
415

    
416
    windowToModify.alert = function(alert) {
417
        browserBot.recordedAlerts.push(alert);
418
        self.relayBotToRC("browserbot.recordedAlerts");
419
    };
420

    
421
    windowToModify.confirm = function(message) {
422
        browserBot.recordedConfirmations.push(message);
423
        var result = browserBot.nextConfirmResult;
424
        browserBot.nextConfirmResult = true;
425
        self.relayBotToRC("browserbot.recordedConfirmations");
426
        return result;
427
    };
428

    
429
    windowToModify.prompt = function(message) {
430
        browserBot.recordedPrompts.push(message);
431
        var result = !browserBot.nextConfirmResult ? null : browserBot.nextPromptResult;
432
        browserBot.nextConfirmResult = true;
433
        browserBot.nextPromptResult = '';
434
        self.relayBotToRC("browserbot.recordedPrompts");
435
        return result;
436
    };
437

    
438
    // Keep a reference to all popup windows by name
439
    // note that in IE the "windowName" argument must be a valid javascript identifier, it seems.
440
    var originalOpen = windowToModify.open;
441
    var originalOpenReference;
442
    if (browserVersion.isHTA) {
443
        originalOpenReference = 'selenium_originalOpen' + new Date().getTime();
444
        windowToModify[originalOpenReference] = windowToModify.open;
445
    }
446
    
447
    var isHTA = browserVersion.isHTA;
448
    
449
    var newOpen = function(url, windowName, windowFeatures, replaceFlag) {
450
        var myOriginalOpen = originalOpen;
451
        if (isHTA) {
452
            myOriginalOpen = this[originalOpenReference];
453
        }
454
        var openedWindow = myOriginalOpen(url, windowName, windowFeatures, replaceFlag);
455
        LOG.debug("window.open call intercepted; window ID (which you can use with selectWindow()) is \"" +  windowName + "\"");
456
        if (windowName!=null) {
457
        	openedWindow["seleniumWindowName"] = windowName;
458
        }
459
        selenium.browserbot.openedWindows[windowName] = openedWindow;
460
        return openedWindow;
461
    };
462
    
463
    if (browserVersion.isHTA) {
464
        originalOpenReference = 'selenium_originalOpen' + new Date().getTime();
465
        newOpenReference = 'selenium_newOpen' + new Date().getTime();
466
        var setOriginalRef = "this['" + originalOpenReference + "'] = this.open;";
467
        
468
        if (windowToModify.eval) {
469
            windowToModify.eval(setOriginalRef);
470
            windowToModify.open = newOpen;
471
        } else {
472
            // DGF why can't I eval here?  Seems like I'm querying the window at a bad time, maybe?
473
            setOriginalRef += "this.open = this['" + newOpenReference + "'];";
474
            windowToModify[newOpenReference] = newOpen;
475
            windowToModify.setTimeout(setOriginalRef, 0);
476
        }
477
    } else {
478
        windowToModify.open = newOpen;
479
    }
480
};
481

    
482
/**
483
 * Call the supplied function when a the current page unloads and a new one loads.
484
 * This is done by polling continuously until the document changes and is fully loaded.
485
 */
486
BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads = function(windowObject) {
487
    // Since the unload event doesn't fire in Safari 1.3, we start polling immediately
488
    if (!windowObject) {
489
        LOG.warn("modifySeparateTestWindowToDetectPageLoads: no windowObject!");
490
        return;
491
    }
492
    if (this._windowClosed(windowObject)) {
493
        LOG.info("modifySeparateTestWindowToDetectPageLoads: windowObject was closed");
494
        return;
495
    }
496
    var oldMarker = this.isPollingForLoad(windowObject);
497
    if (oldMarker) {
498
        LOG.debug("modifySeparateTestWindowToDetectPageLoads: already polling this window: " + oldMarker);
499
        return;
500
    }
501

    
502
    var marker = 'selenium' + new Date().getTime();
503
    LOG.debug("Starting pollForLoad (" + marker + "): " + windowObject.location);
504
    this.pollingForLoad[marker] = true;
505
    // if this is a frame, add a load listener, otherwise, attach a poller
506
    var frameElement = this._getFrameElement(windowObject);
507
    // DGF HTA mode can't attach load listeners to subframes (yuk!)
508
    var htaSubFrame = this._isHTASubFrame(windowObject);
509
    if (frameElement && !htaSubFrame) {
510
        LOG.debug("modifySeparateTestWindowToDetectPageLoads: this window is a frame; attaching a load listener");
511
        addLoadListener(frameElement, this.recordPageLoad);
512
        frameElement[marker] = true;
513
        frameElement[this.uniqueId] = marker;
514
    } else {
515
        windowObject.location[marker] = true;
516
        windowObject[this.uniqueId] = marker;
517
        this.pollForLoad(this.recordPageLoad, windowObject, windowObject.document, windowObject.location, windowObject.location.href, marker);
518
    }
519
};
520

    
521
BrowserBot.prototype._isHTASubFrame = function(win) {
522
    if (!browserVersion.isHTA) return false;
523
    // DGF this is wrong! what if "win" isn't the selected window?
524
    return this.isSubFrameSelected;
525
}
526

    
527
BrowserBot.prototype._getFrameElement = function(win) {
528
    var frameElement = null;
529
    var caught;
530
    try {
531
        frameElement = win.frameElement;
532
    } catch (e) {
533
        caught = true;
534
    }
535
    if (caught) {
536
        // on IE, checking frameElement in a pop-up results in a "No such interface supported" exception
537
        // but it might have a frame element anyway!
538
        var parentContainsIdenticallyNamedFrame = false;
539
        try {
540
            parentContainsIdenticallyNamedFrame = win.parent.frames[win.name];
541
        } catch (e) {} // this may fail if access is denied to the parent; in that case, assume it's not a pop-up
542
        
543
        if (parentContainsIdenticallyNamedFrame) {
544
            // it can't be a coincidence that the parent has a frame with the same name as myself!
545
            return BrowserBot.prototype.locateElementByName(win.name, win.parent.document, win.parent);
546
        }
547
    } 
548
    return frameElement;
549
}
550

    
551
/**
552
 * Set up a polling timer that will keep checking the readyState of the document until it's complete.
553
 * Since we might call this before the original page is unloaded, we first check to see that the current location
554
 * or href is different from the original one.
555
 */
556
BrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
557
    LOG.debug("pollForLoad original (" + marker + "): " + originalHref);
558
    try {
559
        if (this._windowClosed(windowObject)) {
560
            LOG.debug("pollForLoad WINDOW CLOSED (" + marker + ")");
561
            delete this.pollingForLoad[marker];
562
            return;
563
        }
564

    
565
        var isSamePage = this._isSamePage(windowObject, originalDocument, originalLocation, originalHref, marker);
566
        var rs = this.getReadyState(windowObject, windowObject.document);
567

    
568
        if (!isSamePage && rs == 'complete') {
569
            var currentHref = windowObject.location.href;
570
            LOG.debug("pollForLoad FINISHED (" + marker + "): " + rs + " (" + currentHref + ")");
571
            delete this.pollingForLoad[marker];
572
            this._modifyWindow(windowObject);
573
            var newMarker = this.isPollingForLoad(windowObject);
574
            if (!newMarker) {
575
                LOG.debug("modifyWindow didn't start new poller: " + newMarker);
576
                this.modifySeparateTestWindowToDetectPageLoads(windowObject);
577
            }
578
            newMarker = this.isPollingForLoad(windowObject);
579
            var currentlySelectedWindow;
580
            var currentlySelectedWindowMarker;
581
            currentlySelectedWindow =this.getCurrentWindow(true);
582
            currentlySelectedWindowMarker = currentlySelectedWindow[this.uniqueId];
583

    
584
            LOG.debug("pollForLoad (" + marker + ") restarting " + newMarker);
585
            if (/(TestRunner-splash|Blank)\.html\?start=true$/.test(currentHref)) {
586
                LOG.debug("pollForLoad Oh, it's just the starting page.  Never mind!");
587
            } else if (currentlySelectedWindowMarker == newMarker) {
588
                loadFunction(currentlySelectedWindow);
589
            } else {
590
                LOG.debug("pollForLoad page load detected in non-current window; ignoring (currentlySelected="+currentlySelectedWindowMarker+", detection in "+newMarker+")");
591
            }
592
            return;
593
        }
594
        LOG.debug("pollForLoad continue (" + marker + "): " + currentHref);
595
        this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
596
    } catch (e) {
597
        LOG.error("Exception during pollForLoad; this should get noticed soon (" + e.message + ")!");
598
        LOG.exception(e);
599
        this.pageLoadError = e;
600
    }
601
};
602

    
603
BrowserBot.prototype._isSamePage = function(windowObject, originalDocument, originalLocation, originalHref, marker) {
604
    var currentDocument = windowObject.document;
605
    var currentLocation = windowObject.location;
606
    var currentHref = currentLocation.href
607

    
608
    var sameDoc = this._isSameDocument(originalDocument, currentDocument);
609

    
610
    var sameLoc = (originalLocation === currentLocation);
611

    
612
    // hash marks don't meant the page has loaded, so we need to strip them off if they exist...
613
    var currentHash = currentHref.indexOf('#');
614
    if (currentHash > 0) {
615
        currentHref = currentHref.substring(0, currentHash);
616
    }
617
    var originalHash = originalHref.indexOf('#');
618
    if (originalHash > 0) {
619
        originalHref = originalHref.substring(0, originalHash);
620
    }
621
    LOG.debug("_isSamePage: currentHref: " + currentHref);
622
    LOG.debug("_isSamePage: originalHref: " + originalHref);
623

    
624
    var sameHref = (originalHref === currentHref);
625
    var markedLoc = currentLocation[marker];
626

    
627
    if (browserVersion.isKonqueror || browserVersion.isSafari) {
628
        // the mark disappears too early on these browsers
629
        markedLoc = true;
630
    }
631

    
632
    // since this is some _very_ important logic, especially for PI and multiWindow mode, we should log all these out
633
    LOG.debug("_isSamePage: sameDoc: " + sameDoc);
634
    LOG.debug("_isSamePage: sameLoc: " + sameLoc);
635
    LOG.debug("_isSamePage: sameHref: " + sameHref);
636
    LOG.debug("_isSamePage: markedLoc: " + markedLoc);
637

    
638
    return sameDoc && sameLoc && sameHref && markedLoc
639
};
640

    
641
BrowserBot.prototype._isSameDocument = function(originalDocument, currentDocument) {
642
    return originalDocument === currentDocument;
643
};
644

    
645

    
646
BrowserBot.prototype.getReadyState = function(windowObject, currentDocument) {
647
    var rs = currentDocument.readyState;
648
    if (rs == null) {
649
       if ((this.buttonWindow!=null && this.buttonWindow.document.readyState == null) // not proxy injection mode (and therefore buttonWindow isn't null)
650
       || (top.document.readyState == null)) {                                               // proxy injection mode (and therefore everything's in the top window, but buttonWindow doesn't exist)
651
            // uh oh!  we're probably on Firefox with no readyState extension installed!
652
            // We'll have to just take a guess as to when the document is loaded; this guess
653
            // will never be perfect. :-(
654
            if (typeof currentDocument.getElementsByTagName != 'undefined'
655
                    && typeof currentDocument.getElementById != 'undefined'
656
                    && ( currentDocument.getElementsByTagName('body')[0] != null
657
                    || currentDocument.body != null )) {
658
                if (windowObject.frameElement && windowObject.location.href == "about:blank" && windowObject.frameElement.src != "about:blank") {
659
                    LOG.info("getReadyState not loaded, frame location was about:blank, but frame src = " + windowObject.frameElement.src);
660
                    return null;
661
                }
662
                LOG.debug("getReadyState = windowObject.frames.length = " + windowObject.frames.length);
663
                for (var i = 0; i < windowObject.frames.length; i++) {
664
                    LOG.debug("i = " + i);
665
                    if (this.getReadyState(windowObject.frames[i], windowObject.frames[i].document) != 'complete') {
666
                        LOG.debug("getReadyState aha! the nested frame " + windowObject.frames[i].name + " wasn't ready!");
667
                        return null;
668
                    }
669
                }
670

    
671
                rs = 'complete';
672
            } else {
673
                LOG.debug("pollForLoad readyState was null and DOM appeared to not be ready yet");
674
            }
675
        }
676
    }
677
    else if (rs == "loading" && browserVersion.isIE) {
678
        LOG.debug("pageUnloading = true!!!!");
679
        this.pageUnloading = true;
680
    }
681
    LOG.debug("getReadyState returning " + rs);
682
    return rs;
683
};
684

    
685
/** This function isn't used normally, but was the way we used to schedule pollers:
686
 asynchronously executed autonomous units.  This is deprecated, but remains here
687
 for future reference.
688
 */
689
BrowserBot.prototype.XXXreschedulePoller = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
690
    var self = this;
691
    window.setTimeout(function() {
692
        self.pollForLoad(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
693
    }, 500);
694
};
695

    
696
/** This function isn't used normally, but is useful for debugging asynchronous pollers
697
 * To enable it, rename it to "reschedulePoller", so it will override the
698
 * existing reschedulePoller function
699
 */
700
BrowserBot.prototype.XXXreschedulePoller = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
701
    var doc = this.buttonWindow.document;
702
    var button = doc.createElement("button");
703
    var buttonName = doc.createTextNode(marker + " - " + windowObject.name);
704
    button.appendChild(buttonName);
705
    var tools = doc.getElementById("tools");
706
    var self = this;
707
    button.onclick = function() {
708
        tools.removeChild(button);
709
        self.pollForLoad(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
710
    };
711
    tools.appendChild(button);
712
    window.setTimeout(button.onclick, 500);
713
};
714

    
715
BrowserBot.prototype.reschedulePoller = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
716
    var self = this;
717
    var pollerFunction = function() {
718
        self.pollForLoad(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
719
    };
720
    this.windowPollers.push(pollerFunction);
721
};
722

    
723
BrowserBot.prototype.runScheduledPollers = function() {
724
    LOG.debug("runScheduledPollers");
725
    var oldPollers = this.windowPollers;
726
    this.windowPollers = new Array();
727
    for (var i = 0; i < oldPollers.length; i++) {
728
        oldPollers[i].call();
729
    }
730
    LOG.debug("runScheduledPollers DONE");
731
};
732

    
733
BrowserBot.prototype.isPollingForLoad = function(win) {
734
    var marker;
735
    var frameElement = this._getFrameElement(win);
736
    var htaSubFrame = this._isHTASubFrame(win);
737
    if (frameElement && !htaSubFrame) {
738
        marker = frameElement[this.uniqueId];
739
    } else {
740
        marker = win[this.uniqueId];
741
    }
742
    if (!marker) {
743
        LOG.debug("isPollingForLoad false, missing uniqueId " + this.uniqueId + ": " + marker);
744
        return false;
745
    }
746
    if (!this.pollingForLoad[marker]) {
747
        LOG.debug("isPollingForLoad false, this.pollingForLoad[" + marker + "]: " + this.pollingForLoad[marker]);
748
        return false;
749
    }
750
    return marker;
751
};
752

    
753
BrowserBot.prototype.getWindowByName = function(windowName, doNotModify) {
754
    LOG.debug("getWindowByName(" + windowName + ")");
755
    // First look in the map of opened windows
756
    var targetWindow = this.openedWindows[windowName];
757
    if (!targetWindow) {
758
        targetWindow = this.topWindow[windowName];
759
    }
760
    if (!targetWindow && windowName == "_blank") {
761
        for (var winName in this.openedWindows) {
762
            // _blank can match selenium_blank*, if it looks like it's OK (valid href, not closed)
763
            if (/^selenium_blank/.test(winName)) {
764
                targetWindow = this.openedWindows[winName];
765
                var ok;
766
                try {
767
                    if (!this._windowClosed(targetWindow)) {
768
                        ok = targetWindow.location.href;
769
                    }
770
                } catch (e) {}
771
                if (ok) break;
772
            }
773
        }
774
    }
775
    if (!targetWindow) {
776
        throw new SeleniumError("Window does not exist");
777
    }
778
    if (browserVersion.isHTA) {
779
        try {
780
            targetWindow.location.href;
781
        } catch (e) {
782
            targetWindow = window.open("", targetWindow.name);
783
            this.openedWindows[targetWindow.name] = targetWindow;
784
        }
785
    }
786
    if (!doNotModify) {
787
        this._modifyWindow(targetWindow);
788
    }
789
    return targetWindow;
790
};
791

    
792
BrowserBot.prototype.getCurrentWindow = function(doNotModify) {
793
    var testWindow = this.currentWindow;
794
    if (!doNotModify) {
795
        this._modifyWindow(testWindow);
796
        if (!this.proxyInjectionMode) {
797
            // In proxy injection mode, have to avoid logging during getCurrentWindow to avoid an infinite loop
798
            LOG.debug("getCurrentWindow newPageLoaded = false");
799
        }
800
        this.newPageLoaded = false;
801
    }
802
    testWindow = this._handleClosedSubFrame(testWindow, doNotModify);
803
    return testWindow;
804
};
805

    
806
BrowserBot.prototype._handleClosedSubFrame = function(testWindow, doNotModify) {
807
    if (this.proxyInjectionMode) {
808
        return testWindow;
809
    }
810
    
811
    if (this.isSubFrameSelected) {
812
        var missing = true;
813
        if (testWindow.parent && testWindow.parent.frames && testWindow.parent.frames.length) {
814
            for (var i = 0; i < testWindow.parent.frames.length; i++) {
815
                if (testWindow.parent.frames[i] == testWindow) {
816
                    missing = false;
817
                    break;
818
                }
819
            }
820
        }
821
        if (missing) {
822
            LOG.warn("Current subframe appears to have closed; selecting top frame");
823
            this.selectFrame("relative=top");
824
            return this.getCurrentWindow(doNotModify);
825
        }
826
    } else if (this._windowClosed(testWindow)) {
827
        var closedError = new SeleniumError("Current window or frame is closed!");
828
        closedError.windowClosed = true;
829
        throw closedError;
830
    }
831
    return testWindow;
832
};
833

    
834
BrowserBot.prototype.highlight = function (element, force) {
835
    if (force || this.shouldHighlightLocatedElement) {
836
        try {
837
            highlight(element);
838
        } catch (e) {} // DGF element highlighting is low-priority and possibly dangerous
839
    }
840
    return element;
841
}
842

    
843
BrowserBot.prototype.setShouldHighlightElement = function (shouldHighlight) {
844
    this.shouldHighlightLocatedElement = shouldHighlight;
845
}
846

    
847
/*****************************************************************/
848
/* BROWSER-SPECIFIC FUNCTIONS ONLY AFTER THIS LINE */
849

    
850

    
851
BrowserBot.prototype._registerAllLocatorFunctions = function() {
852
    // TODO - don't do this in the constructor - only needed once ever
853
    this.locationStrategies = {};
854
    for (var functionName in this) {
855
        var result = /^locateElementBy([A-Z].+)$/.exec(functionName);
856
        if (result != null) {
857
            var locatorFunction = this[functionName];
858
            if (typeof(locatorFunction) != 'function') {
859
                continue;
860
            }
861
            // Use a specified prefix in preference to one generated from
862
            // the function name
863
            var locatorPrefix = locatorFunction.prefix || result[1].toLowerCase();
864
            this.locationStrategies[locatorPrefix] = locatorFunction;
865
        }
866
    }
867

    
868
    /**
869
     * Find a locator based on a prefix.
870
     */
871
    this.findElementBy = function(locatorType, locator, inDocument, inWindow) {
872
        var locatorFunction = this.locationStrategies[locatorType];
873
        if (! locatorFunction) {
874
            throw new SeleniumError("Unrecognised locator type: '" + locatorType + "'");
875
        }
876
        return locatorFunction.call(this, locator, inDocument, inWindow);
877
    };
878

    
879
    /**
880
     * The implicit locator, that is used when no prefix is supplied.
881
     */
882
    this.locationStrategies['implicit'] = function(locator, inDocument, inWindow) {
883
        if (locator.startsWith('//')) {
884
            return this.locateElementByXPath(locator, inDocument, inWindow);
885
        }
886
        if (locator.startsWith('document.')) {
887
            return this.locateElementByDomTraversal(locator, inDocument, inWindow);
888
        }
889
        return this.locateElementByIdentifier(locator, inDocument, inWindow);
890
    };
891
}
892

    
893
BrowserBot.prototype.getDocument = function() {
894
    return this.getCurrentWindow().document;
895
}
896

    
897
BrowserBot.prototype.getTitle = function() {
898
    var t = this.getDocument().title;
899
    if (typeof(t) == "string") {
900
        t = t.trim();
901
    }
902
    return t;
903
}
904

    
905
/*
906
 * Finds an element recursively in frames and nested frames
907
 * in the specified document, using various lookup protocols
908
 */
909
BrowserBot.prototype.findElementRecursive = function(locatorType, locatorString, inDocument, inWindow) {
910

    
911
    var element = this.findElementBy(locatorType, locatorString, inDocument, inWindow);
912
    if (element != null) {
913
        return element;
914
    }
915
    
916
    for (var i = 0; i < inWindow.frames.length; i++) {        
917
        element = this.findElementRecursive(locatorType, locatorString, inWindow.frames[i].document, inWindow.frames[i]);
918
        
919
        if (element != null) {
920
        	return element;
921
        }
922
    }
923
};
924

    
925
/*
926
* Finds an element on the current page, using various lookup protocols
927
*/
928
BrowserBot.prototype.findElement = function(locator) {
929
    var locatorType = 'implicit';
930
    var locatorString = locator;
931

    
932
    // If there is a locator prefix, use the specified strategy
933
    var result = locator.match(/^([A-Za-z]+)=(.+)/);
934
    if (result) {
935
        locatorType = result[1].toLowerCase();
936
        locatorString = result[2];
937
    }
938
    
939
    var element = this.findElementRecursive(locatorType, locatorString, this.getDocument(), this.getCurrentWindow())
940
    
941
    if (element != null) {
942
    	return this.browserbot.highlight(element);
943
    }
944

    
945
    // Element was not found by any locator function.
946
    throw new SeleniumError("Element " + locator + " not found");
947
};
948

    
949
/**
950
 * In non-IE browsers, getElementById() does not search by name.  Instead, we
951
 * we search separately by id and name.
952
 */
953
BrowserBot.prototype.locateElementByIdentifier = function(identifier, inDocument, inWindow) {
954
    return BrowserBot.prototype.locateElementById(identifier, inDocument, inWindow)
955
            || BrowserBot.prototype.locateElementByName(identifier, inDocument, inWindow)
956
            || null;
957
};
958

    
959
/**
960
 * Find the element with id - can't rely on getElementById, coz it returns by name as well in IE..
961
 */
962
BrowserBot.prototype.locateElementById = function(identifier, inDocument, inWindow) {
963
    var element = inDocument.getElementById(identifier);
964
    if (element && element.id === identifier) {
965
        return element;
966
    }
967
    else {
968
        return null;
969
    }
970
};
971

    
972
/**
973
 * Find an element by name, refined by (optional) element-filter
974
 * expressions.
975
 */
976
BrowserBot.prototype.locateElementByName = function(locator, document, inWindow) {
977
    var elements = document.getElementsByTagName("*");
978

    
979
    var filters = locator.split(' ');
980
    filters[0] = 'name=' + filters[0];
981

    
982
    while (filters.length) {
983
        var filter = filters.shift();
984
        elements = this.selectElements(filter, elements, 'value');
985
    }
986

    
987
    if (elements.length > 0) {
988
        return elements[0];
989
    }
990
    return null;
991
};
992

    
993
/**
994
 * Finds an element using by evaluating the specfied string.
995
 */
996
BrowserBot.prototype.locateElementByDomTraversal = function(domTraversal, document, window) {
997

    
998
    var browserbot = this.browserbot;
999
    var element = null;
1000
    try {
1001
        element = eval(domTraversal);
1002
    } catch (e) {
1003
        e.isSeleniumError = true;
1004
        throw e;
1005
    }
1006

    
1007
    if (!element) {
1008
        return null;
1009
    }
1010

    
1011
    return element;
1012
};
1013
BrowserBot.prototype.locateElementByDomTraversal.prefix = "dom";
1014

    
1015
/**
1016
 * Finds an element identified by the xpath expression. Expressions _must_
1017
 * begin with "//".
1018
 */
1019
BrowserBot.prototype.locateElementByXPath = function(xpath, inDocument, inWindow) {
1020

    
1021
    // Trim any trailing "/": not valid xpath, and remains from attribute
1022
    // locator.
1023
    if (xpath.charAt(xpath.length - 1) == '/') {
1024
        xpath = xpath.slice(0, -1);
1025
    }
1026

    
1027
    // Handle //tag
1028
    var match = xpath.match(/^\/\/(\w+|\*)$/);
1029
    if (match) {
1030
        var elements = inDocument.getElementsByTagName(match[1].toUpperCase());
1031
        if (elements == null) return null;
1032
        return elements[0];
1033
    }
1034

    
1035
    // Handle //tag[@attr='value']
1036
    var match = xpath.match(/^\/\/(\w+|\*)\[@(\w+)=('([^\']+)'|"([^\"]+)")\]$/);
1037
    if (match) {
1038
        // We don't return the value without checking if it is null first.
1039
        // This is beacuse in some rare cases, this shortcut actually WONT work
1040
        // but that the full XPath WILL. A known case, for example, is in IE
1041
        // when the attribute is onclick/onblur/onsubmit/etc. Due to a bug in IE
1042
        // this shortcut won't work because the actual function is returned
1043
        // by getAttribute() rather than the text of the attribute.
1044
        var val = this._findElementByTagNameAndAttributeValue(
1045
                inDocument,
1046
                match[1].toUpperCase(),
1047
                match[2].toLowerCase(),
1048
                match[3].slice(1, -1)
1049
                );
1050
        if (val) {
1051
            return val;
1052
        }
1053
    }
1054

    
1055
    // Handle //tag[text()='value']
1056
    var match = xpath.match(/^\/\/(\w+|\*)\[text\(\)=('([^\']+)'|"([^\"]+)")\]$/);
1057
    if (match) {
1058
        return this._findElementByTagNameAndText(
1059
                inDocument,
1060
                match[1].toUpperCase(),
1061
                match[2].slice(1, -1)
1062
                );
1063
    }
1064

    
1065
    return this._findElementUsingFullXPath(xpath, inDocument);
1066
};
1067

    
1068
BrowserBot.prototype._findElementByTagNameAndAttributeValue = function(
1069
        inDocument, tagName, attributeName, attributeValue
1070
        ) {
1071
    if (browserVersion.isIE && attributeName == "class") {
1072
        attributeName = "className";
1073
    }
1074
    var elements = inDocument.getElementsByTagName(tagName);
1075
    for (var i = 0; i < elements.length; i++) {
1076
        var elementAttr = elements[i].getAttribute(attributeName);
1077
        if (elementAttr == attributeValue) {
1078
            return elements[i];
1079
        }
1080
    }
1081
    return null;
1082
};
1083

    
1084
BrowserBot.prototype._findElementByTagNameAndText = function(
1085
        inDocument, tagName, text
1086
        ) {
1087
    var elements = inDocument.getElementsByTagName(tagName);
1088
    for (var i = 0; i < elements.length; i++) {
1089
        if (getText(elements[i]) == text) {
1090
            return elements[i];
1091
        }
1092
    }
1093
    return null;
1094
};
1095

    
1096
BrowserBot.prototype._namespaceResolver = function(prefix) {
1097
    if (prefix == 'html' || prefix == 'xhtml' || prefix == 'x') {
1098
        return 'http://www.w3.org/1999/xhtml';
1099
    } else if (prefix == 'mathml') {
1100
        return 'http://www.w3.org/1998/Math/MathML';
1101
    } else {
1102
        throw new Error("Unknown namespace: " + prefix + ".");
1103
    }
1104
}
1105

    
1106
BrowserBot.prototype._findElementUsingFullXPath = function(xpath, inDocument, inWindow) {
1107
    // HUGE hack - remove namespace from xpath for IE
1108
    if (browserVersion.isIE) {
1109
        xpath = xpath.replace(/x:/g, '')
1110
    }
1111

    
1112
    // Use document.evaluate() if it's available
1113
    if (inDocument.evaluate) {
1114
        return inDocument.evaluate(xpath, inDocument, this._namespaceResolver, 0, null).iterateNext();
1115
    }
1116

    
1117
    // If not, fall back to slower JavaScript implementation
1118
    var context = new ExprContext(inDocument);
1119
    var xpathObj = xpathParse(xpath);
1120
    var xpathResult = xpathObj.evaluate(context);
1121
    if (xpathResult && xpathResult.value) {
1122
        return xpathResult.value[0];
1123
    }
1124
    return null;
1125
};
1126

    
1127
/**
1128
 * Finds a link element with text matching the expression supplied. Expressions must
1129
 * begin with "link:".
1130
 */
1131
BrowserBot.prototype.locateElementByLinkText = function(linkText, inDocument, inWindow) {
1132
    var links = inDocument.getElementsByTagName('a');
1133
    for (var i = 0; i < links.length; i++) {
1134
        var element = links[i];
1135
        if (PatternMatcher.matches(linkText, getText(element))) {
1136
            return element;
1137
        }
1138
    }
1139
    return null;
1140
};
1141
BrowserBot.prototype.locateElementByLinkText.prefix = "link";
1142

    
1143
/**
1144
 * Returns an attribute based on an attribute locator. This is made up of an element locator
1145
 * suffixed with @attribute-name.
1146
 */
1147
BrowserBot.prototype.findAttribute = function(locator) {
1148
    // Split into locator + attributeName
1149
    var attributePos = locator.lastIndexOf("@");
1150
    var elementLocator = locator.slice(0, attributePos);
1151
    var attributeName = locator.slice(attributePos + 1);
1152

    
1153
    // Find the element.
1154
    var element = this.findElement(elementLocator);
1155

    
1156
    // Handle missing "class" attribute in IE.
1157
    if (browserVersion.isIE && attributeName == "class") {
1158
        attributeName = "className";
1159
    }
1160

    
1161
    // Get the attribute value.
1162
    var attributeValue = element.getAttribute(attributeName);
1163

    
1164
    return attributeValue ? attributeValue.toString() : null;
1165
};
1166

    
1167
/*
1168
* Select the specified option and trigger the relevant events of the element.
1169
*/
1170
BrowserBot.prototype.selectOption = function(element, optionToSelect) {
1171
    triggerEvent(element, 'focus', false);
1172
    var changed = false;
1173
    for (var i = 0; i < element.options.length; i++) {
1174
        var option = element.options[i];
1175
        if (option.selected && option != optionToSelect) {
1176
            option.selected = false;
1177
            changed = true;
1178
        }
1179
        else if (!option.selected && option == optionToSelect) {
1180
            option.selected = true;
1181
            changed = true;
1182
        }
1183
    }
1184

    
1185
    if (changed) {
1186
        triggerEvent(element, 'change', true);
1187
    }
1188
};
1189

    
1190
/*
1191
* Select the specified option and trigger the relevant events of the element.
1192
*/
1193
BrowserBot.prototype.addSelection = function(element, option) {
1194
    this.checkMultiselect(element);
1195
    triggerEvent(element, 'focus', false);
1196
    if (!option.selected) {
1197
        option.selected = true;
1198
        triggerEvent(element, 'change', true);
1199
    }
1200
};
1201

    
1202
/*
1203
* Select the specified option and trigger the relevant events of the element.
1204
*/
1205
BrowserBot.prototype.removeSelection = function(element, option) {
1206
    this.checkMultiselect(element);
1207
    triggerEvent(element, 'focus', false);
1208
    if (option.selected) {
1209
        option.selected = false;
1210
        triggerEvent(element, 'change', true);
1211
    }
1212
};
1213

    
1214
BrowserBot.prototype.checkMultiselect = function(element) {
1215
    if (!element.multiple)
1216
    {
1217
        throw new SeleniumError("Not a multi-select");
1218
    }
1219

    
1220
};
1221

    
1222
BrowserBot.prototype.replaceText = function(element, stringValue) {
1223
    triggerEvent(element, 'focus', false);
1224
    triggerEvent(element, 'select', true);
1225
    var maxLengthAttr = element.getAttribute("maxLength");
1226
    var actualValue = stringValue;
1227
    if (maxLengthAttr != null) {
1228
        var maxLength = parseInt(maxLengthAttr);
1229
        if (stringValue.length > maxLength) {
1230
            LOG.warn("BEFORE")
1231
            actualValue = stringValue.substr(0, maxLength);
1232
            LOG.warn("AFTER")
1233
        }
1234
    }
1235
    
1236
    if (getTagName(element) == "body") {
1237
        if (element.ownerDocument && element.ownerDocument.designMode) {
1238
            var designMode = new String(element.ownerDocument.designMode).toLowerCase();
1239
            if (designMode = "on") {
1240
                // this must be a rich text control!
1241
                element.innerHTML = actualValue;
1242
            }
1243
        }
1244
    } else {
1245
        element.value = actualValue;
1246
    }
1247
    // DGF this used to be skipped in chrome URLs, but no longer.  Is xpcnativewrappers to blame?
1248
    try {
1249
        triggerEvent(element, 'change', true);
1250
    } catch (e) {}
1251
};
1252

    
1253
BrowserBot.prototype.submit = function(formElement) {
1254
    var actuallySubmit = true;
1255
    this._modifyElementTarget(formElement);
1256
    if (formElement.onsubmit) {
1257
        if (browserVersion.isHTA) {
1258
            // run the code in the correct window so alerts are handled correctly even in HTA mode
1259
            var win = this.browserbot.getCurrentWindow();
1260
            var now = new Date().getTime();
1261
            var marker = 'marker' + now;
1262
            win[marker] = formElement;
1263
            win.setTimeout("var actuallySubmit = "+marker+".onsubmit();" +
1264
                "if (actuallySubmit) { " +
1265
                    marker+".submit(); " +
1266
                    "if ("+marker+".target && !/^_/.test("+marker+".target)) {"+
1267
                        "window.open('', "+marker+".target);"+
1268
                    "}"+
1269
                "};"+
1270
                marker+"=null", 0);
1271
            // pause for up to 2s while this command runs
1272
            var terminationCondition = function () {
1273
                return !win[marker];
1274
            }
1275
            return Selenium.decorateFunctionWithTimeout(terminationCondition, 2000);
1276
        } else {
1277
            actuallySubmit = formElement.onsubmit();
1278
            if (actuallySubmit) {
1279
                formElement.submit();
1280
                if (formElement.target && !/^_/.test(formElement.target)) {
1281
                    this.browserbot.openWindow('', formElement.target);
1282
                }
1283
            }
1284
        }
1285
    } else {
1286
        formElement.submit();
1287
    }
1288
}
1289

    
1290
BrowserBot.prototype.clickElement = function(element, clientX, clientY) {
1291
       this._fireEventOnElement("click", element, clientX, clientY);
1292
};
1293
    
1294
BrowserBot.prototype.doubleClickElement = function(element, clientX, clientY) {
1295
       this._fireEventOnElement("dblclick", element, clientX, clientY);
1296
};
1297

    
1298
BrowserBot.prototype._modifyElementTarget = function(element) {
1299
    if (element.target) {
1300
        if (element.target == "_blank" || /^selenium_blank/.test(element.target) ) {
1301
            var tagName = getTagName(element);
1302
            if (tagName == "a" || tagName == "form") {
1303
                var newTarget = "selenium_blank" + Math.round(100000 * Math.random());
1304
                LOG.warn("Link has target '_blank', which is not supported in Selenium!  Randomizing target to be: " + newTarget);
1305
                this.browserbot.openWindow('', newTarget);
1306
                element.target = newTarget;
1307
            }
1308
        }
1309
    }
1310
}
1311

    
1312

    
1313
BrowserBot.prototype._handleClickingImagesInsideLinks = function(targetWindow, element) {
1314
    if (element.parentNode && element.parentNode.href) {
1315
        targetWindow.location.href = element.parentNode.href;
1316
    }
1317
}
1318

    
1319
BrowserBot.prototype._getTargetWindow = function(element) {
1320
    var targetWindow = element.ownerDocument.defaultView;
1321
    if (element.target) {
1322
        targetWindow = this._getFrameFromGlobal(element.target);
1323
    }
1324
    return targetWindow;
1325
}
1326

    
1327
BrowserBot.prototype._getFrameFromGlobal = function(target) {
1328
    
1329
    if (target == "_top") {
1330
        return this.topFrame;
1331
    } else if (target == "_parent") {
1332
        return this.getCurrentWindow().parent;
1333
    } else if (target == "_blank") {
1334
        // TODO should this set cleverer window defaults?
1335
        return this.getCurrentWindow().open('', '_blank');
1336
    }
1337
    var frameElement = this.findElementBy("implicit", target, this.topFrame.document, this.topFrame);
1338
    if (frameElement) {
1339
        return frameElement.contentWindow;
1340
    }
1341
    var win = this.getWindowByName(target);
1342
    if (win) return win;
1343
    return this.getCurrentWindow().open('', target);
1344
}
1345

    
1346

    
1347
BrowserBot.prototype.bodyText = function() {
1348
    return getText(this.getDocument().body);
1349
};
1350

    
1351
BrowserBot.prototype.getAllButtons = function() {
1352
    var elements = this.getDocument().getElementsByTagName('input');
1353
    var result = '';
1354

    
1355
    for (var i = 0; i < elements.length; i++) {
1356
        if (elements[i].type == 'button' || elements[i].type == 'submit' || elements[i].type == 'reset') {
1357
            result += elements[i].id;
1358

    
1359
            result += ',';
1360
        }
1361
    }
1362

    
1363
    return result;
1364
};
1365

    
1366

    
1367
BrowserBot.prototype.getAllFields = function() {
1368
    var elements = this.getDocument().getElementsByTagName('input');
1369
    var result = '';
1370

    
1371
    for (var i = 0; i < elements.length; i++) {
1372
        if (elements[i].type == 'text') {
1373
            result += elements[i].id;
1374

    
1375
            result += ',';
1376
        }
1377
    }
1378

    
1379
    return result;
1380
};
1381

    
1382
BrowserBot.prototype.getAllLinks = function() {
1383
    var elements = this.getDocument().getElementsByTagName('a');
1384
    var result = '';
1385

    
1386
    for (var i = 0; i < elements.length; i++) {
1387
        result += elements[i].id;
1388

    
1389
        result += ',';
1390
    }
1391

    
1392
    return result;
1393
};
1394

    
1395
BrowserBot.prototype.setContext = function(strContext, logLevel) {
1396

    
1397
    //set the current test title
1398
    var ctx = document.getElementById("context");
1399
    if (ctx != null) {
1400
        ctx.innerHTML = strContext;
1401
    }
1402
    if (logLevel != null) {
1403
        LOG.setLogLevelThreshold(logLevel);
1404
    }
1405
};
1406

    
1407
function isDefined(value) {
1408
    return typeof(value) != undefined;
1409
}
1410

    
1411
BrowserBot.prototype.goBack = function() {
1412
    this.getCurrentWindow().history.back();
1413
};
1414

    
1415
BrowserBot.prototype.goForward = function() {
1416
    this.getCurrentWindow().history.forward();
1417
};
1418

    
1419
BrowserBot.prototype.close = function() {
1420
    if (browserVersion.isChrome || browserVersion.isSafari || browserVersion.isOpera) {
1421
        this.getCurrentWindow().close();
1422
    } else {
1423
        this.getCurrentWindow().eval("window.close();");
1424
    }
1425
};
1426

    
1427
BrowserBot.prototype.refresh = function() {
1428
    this.getCurrentWindow().location.reload(true);
1429
};
1430

    
1431
/**
1432
 * Refine a list of elements using a filter.
1433
 */
1434
BrowserBot.prototype.selectElementsBy = function(filterType, filter, elements) {
1435
    var filterFunction = BrowserBot.filterFunctions[filterType];
1436
    if (! filterFunction) {
1437
        throw new SeleniumError("Unrecognised element-filter type: '" + filterType + "'");
1438
    }
1439

    
1440
    return filterFunction(filter, elements);
1441
};
1442

    
1443
BrowserBot.filterFunctions = {};
1444

    
1445
BrowserBot.filterFunctions.name = function(name, elements) {
1446
    var selectedElements = [];
1447
    for (var i = 0; i < elements.length; i++) {
1448
        if (elements[i].name === name) {
1449
            selectedElements.push(elements[i]);
1450
        }
1451
    }
1452
    return selectedElements;
1453
};
1454

    
1455
BrowserBot.filterFunctions.value = function(value, elements) {
1456
    var selectedElements = [];
1457
    for (var i = 0; i < elements.length; i++) {
1458
        if (elements[i].value === value) {
1459
            selectedElements.push(elements[i]);
1460
        }
1461
    }
1462
    return selectedElements;
1463
};
1464

    
1465
BrowserBot.filterFunctions.index = function(index, elements) {
1466
    index = Number(index);
1467
    if (isNaN(index) || index < 0) {
1468
        throw new SeleniumError("Illegal Index: " + index);
1469
    }
1470
    if (elements.length <= index) {
1471
        throw new SeleniumError("Index out of range: " + index);
1472
    }
1473
    return [elements[index]];
1474
};
1475

    
1476
BrowserBot.prototype.selectElements = function(filterExpr, elements, defaultFilterType) {
1477

    
1478
    var filterType = (defaultFilterType || 'value');
1479

    
1480
    // If there is a filter prefix, use the specified strategy
1481
    var result = filterExpr.match(/^([A-Za-z]+)=(.+)/);
1482
    if (result) {
1483
        filterType = result[1].toLowerCase();
1484
        filterExpr = result[2];
1485
    }
1486

    
1487
    return this.selectElementsBy(filterType, filterExpr, elements);
1488
};
1489

    
1490
/**
1491
 * Find an element by class
1492
 */
1493
BrowserBot.prototype.locateElementByClass = function(locator, document) {
1494
    return elementFindFirstMatchingChild(document,
1495
            function(element) {
1496
                return element.className == locator
1497
            }
1498
            );
1499
}
1500

    
1501
/**
1502
 * Find an element by alt
1503
 */
1504
BrowserBot.prototype.locateElementByAlt = function(locator, document) {
1505
    return elementFindFirstMatchingChild(document,
1506
            function(element) {
1507
                return element.alt == locator
1508
            }
1509
            );
1510
}
1511

    
1512
/**
1513
 * Find an element by css selector
1514
 */
1515
BrowserBot.prototype.locateElementByCss = function(locator, document) {
1516
    var elements = cssQuery(locator, document);
1517
    if (elements.length != 0)
1518
        return elements[0];
1519
    return null;
1520
}
1521

    
1522

    
1523
/*****************************************************************/
1524
/* BROWSER-SPECIFIC FUNCTIONS ONLY AFTER THIS LINE */
1525

    
1526
function MozillaBrowserBot(frame) {
1527
    BrowserBot.call(this, frame);
1528
}
1529
objectExtend(MozillaBrowserBot.prototype, BrowserBot.prototype);
1530

    
1531
function KonquerorBrowserBot(frame) {
1532
    BrowserBot.call(this, frame);
1533
}
1534
objectExtend(KonquerorBrowserBot.prototype, BrowserBot.prototype);
1535

    
1536
KonquerorBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
1537
    // Window doesn't fire onload event when setting src to the current value,
1538
    // so we set it to blank first.
1539
    iframe.src = "about:blank";
1540
    iframe.src = location;
1541
};
1542

    
1543
KonquerorBrowserBot.prototype.setOpenLocation = function(win, loc) {
1544
    // Window doesn't fire onload event when setting src to the current value,
1545
    // so we just refresh in that case instead.
1546
    loc = absolutify(loc, this.baseUrl);
1547
    loc = canonicalize(loc);
1548
    var startLoc = parseUrl(win.location.href);
1549
    startLoc.hash = null;
1550
    var startUrl = reassembleLocation(startLoc);
1551
    LOG.debug("startUrl="+startUrl);
1552
    LOG.debug("win.location.href="+win.location.href);
1553
    LOG.debug("loc="+loc);
1554
    if (startUrl == loc) {
1555
        LOG.debug("opening exact same location");
1556
        this.refresh();
1557
    } else {
1558
        LOG.debug("locations differ");
1559
        win.location.href = loc;
1560
    }
1561
    // force the current polling thread to detect a page load
1562
    var marker = this.isPollingForLoad(win);
1563
    if (marker) {
1564
        delete win.location[marker];
1565
    }
1566
};
1567

    
1568
KonquerorBrowserBot.prototype._isSameDocument = function(originalDocument, currentDocument) {
1569
    // under Konqueror, there may be this case:
1570
    // originalDocument and currentDocument are different objects
1571
    // while their location are same.
1572
    if (originalDocument) {
1573
        return originalDocument.location == currentDocument.location
1574
    } else {
1575
        return originalDocument === currentDocument;
1576
    }
1577
};
1578

    
1579
function SafariBrowserBot(frame) {
1580
    BrowserBot.call(this, frame);
1581
}
1582
objectExtend(SafariBrowserBot.prototype, BrowserBot.prototype);
1583

    
1584
SafariBrowserBot.prototype.setIFrameLocation = KonquerorBrowserBot.prototype.setIFrameLocation;
1585
SafariBrowserBot.prototype.setOpenLocation = KonquerorBrowserBot.prototype.setOpenLocation;
1586

    
1587

    
1588
function OperaBrowserBot(frame) {
1589
    BrowserBot.call(this, frame);
1590
}
1591
objectExtend(OperaBrowserBot.prototype, BrowserBot.prototype);
1592
OperaBrowserBot.prototype.setIFrameLocation = function(iframe, location) {
1593
    if (iframe.src == location) {
1594
        iframe.src = location + '?reload';
1595
    } else {
1596
        iframe.src = location;
1597
    }
1598
}
1599

    
1600
function IEBrowserBot(frame) {
1601
    BrowserBot.call(this, frame);
1602
}
1603
objectExtend(IEBrowserBot.prototype, BrowserBot.prototype);
1604

    
1605
IEBrowserBot.prototype._handleClosedSubFrame = function(testWindow, doNotModify) {
1606
    if (this.proxyInjectionMode) {
1607
        return testWindow;
1608
    }
1609
    
1610
    try {
1611
        testWindow.location.href;
1612
        this.permDenied = 0;
1613
    } catch (e) {
1614
        this.permDenied++;
1615
    }
1616
    if (this._windowClosed(testWindow) || this.permDenied > 4) {
1617
        if (this.isSubFrameSelected) {
1618
            LOG.warn("Current subframe appears to have closed; selecting top frame");
1619
            this.selectFrame("relative=top");
1620
            return this.getCurrentWindow(doNotModify);
1621
        } else {
1622
            var closedError = new SeleniumError("Current window or frame is closed!");
1623
            closedError.windowClosed = true;
1624
            throw closedError;
1625
        }
1626
    }
1627
    return testWindow;
1628
};
1629

    
1630
IEBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
1631
    BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
1632

    
1633
    // we will call the previous version of this method from within our own interception
1634
    oldShowModalDialog = windowToModify.showModalDialog;
1635

    
1636
    windowToModify.showModalDialog = function(url, args, features) {
1637
        // Get relative directory to where TestRunner.html lives
1638
        // A risky assumption is that the user's TestRunner is named TestRunner.html
1639
        var doc_location = document.location.toString();
1640
        var end_of_base_ref = doc_location.indexOf('TestRunner.html');
1641
        var base_ref = doc_location.substring(0, end_of_base_ref);
1642

    
1643
        var fullURL = base_ref + "TestRunner.html?singletest=" + escape(browserBot.modalDialogTest) + "&autoURL=" + escape(url) + "&runInterval=" + runOptions.runInterval;
1644
        browserBot.modalDialogTest = null;
1645

    
1646
        var returnValue = oldShowModalDialog(fullURL, args, features);
1647
        return returnValue;
1648
    };
1649
};
1650

    
1651
IEBrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads = function(windowObject) {
1652
    this.pageUnloading = false;
1653
    var self = this;
1654
    var pageUnloadDetector = function() {
1655
        self.pageUnloading = true;
1656
    };
1657
    windowObject.attachEvent("onbeforeunload", pageUnloadDetector);
1658
    BrowserBot.prototype.modifySeparateTestWindowToDetectPageLoads.call(this, windowObject);
1659
};
1660

    
1661
IEBrowserBot.prototype.pollForLoad = function(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker) {
1662
    LOG.debug("IEBrowserBot.pollForLoad: " + marker);
1663
    if (!this.permDeniedCount[marker]) this.permDeniedCount[marker] = 0;
1664
    BrowserBot.prototype.pollForLoad.call(this, loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
1665
    if (this.pageLoadError) {
1666
        if (this.pageUnloading) {
1667
            var self = this;
1668
            LOG.warn("pollForLoad UNLOADING (" + marker + "): caught exception while firing events on unloading page: " + this.pageLoadError.message);
1669
            this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
1670
            this.pageLoadError = null;
1671
            return;
1672
        } else if (((this.pageLoadError.message == "Permission denied") || (/^Access is denied/.test(this.pageLoadError.message)))
1673
                && this.permDeniedCount[marker]++ < 8) {
1674
            if (this.permDeniedCount[marker] > 4) {
1675
                var canAccessThisWindow;
1676
                var canAccessCurrentlySelectedWindow;
1677
                try {
1678
                    windowObject.location.href;
1679
                    canAccessThisWindow = true;
1680
                } catch (e) {}
1681
                try {
1682
                    this.getCurrentWindow(true).location.href;
1683
                    canAccessCurrentlySelectedWindow = true;
1684
                } catch (e) {}
1685
                if (canAccessCurrentlySelectedWindow & !canAccessThisWindow) {
1686
                    LOG.warn("pollForLoad (" + marker + ") ABORTING: " + this.pageLoadError.message + " (" + this.permDeniedCount[marker] + "), but the currently selected window is fine");
1687
                    // returning without rescheduling
1688
                    this.pageLoadError = null;
1689
                    return;
1690
                }
1691
            }
1692
            
1693
            var self = this;
1694
            LOG.warn("pollForLoad (" + marker + "): " + this.pageLoadError.message + " (" + this.permDeniedCount[marker] + "), waiting to see if it goes away");
1695
            this.reschedulePoller(loadFunction, windowObject, originalDocument, originalLocation, originalHref, marker);
1696
            this.pageLoadError = null;
1697
            return;
1698
        }
1699
        //handy for debugging!
1700
        //throw this.pageLoadError;
1701
    }
1702
};
1703

    
1704
IEBrowserBot.prototype._windowClosed = function(win) {
1705
    try {
1706
        var c = win.closed;
1707
        // frame windows claim to be non-closed when their parents are closed
1708
        // but you can't access their document objects in that case
1709
        if (!c) {
1710
            try {
1711
                win.document;
1712
            } catch (de) {
1713
                if (de.message == "Permission denied") {
1714
                    // the window is probably unloading, which means it's probably not closed yet
1715
                    return false;
1716
                }
1717
                else if (/^Access is denied/.test(de.message)) {
1718
                    // rare variation on "Permission denied"?
1719
                    LOG.debug("IEBrowserBot.windowClosed: got " + de.message + " (this.pageUnloading=" + this.pageUnloading + "); assuming window is unloading, probably not closed yet");
1720
                    return false;
1721
                } else {
1722
                    // this is probably one of those frame window situations
1723
                    LOG.debug("IEBrowserBot.windowClosed: couldn't read win.document, assume closed: " + de.message + " (this.pageUnloading=" + this.pageUnloading + ")");
1724
                    return true;
1725
                }
1726
            }
1727
        }
1728
        if (c == null) {
1729
            LOG.debug("IEBrowserBot.windowClosed: win.closed was null, assuming closed");
1730
            return true;
1731
        }
1732
        return c;
1733
    } catch (e) {
1734
        LOG.debug("IEBrowserBot._windowClosed: Got an exception trying to read win.closed; we'll have to take a guess!");
1735
            
1736
        if (browserVersion.isHTA) {
1737
            if (e.message == "Permission denied") {
1738
                // the window is probably unloading, which means it's not closed yet
1739
                return false;
1740
            } else {
1741
                // there's a good chance that we've lost contact with the window object if it is closed
1742
                return true;
1743
            }
1744
        } else {
1745
            // the window is probably unloading, which means it's not closed yet
1746
            return false;
1747
        }
1748
    }
1749
};
1750

    
1751
/**
1752
 * In IE, getElementById() also searches by name - this is an optimisation for IE.
1753
 */
1754
IEBrowserBot.prototype.locateElementByIdentifer = function(identifier, inDocument, inWindow) {
1755
    return inDocument.getElementById(identifier);
1756
};
1757

    
1758
SafariBrowserBot.prototype.modifyWindowToRecordPopUpDialogs = function(windowToModify, browserBot) {
1759
    BrowserBot.prototype.modifyWindowToRecordPopUpDialogs(windowToModify, browserBot);
1760

    
1761
    var originalOpen = windowToModify.open;
1762
    /*
1763
     * Safari seems to be broken, so that when we manually trigger the onclick method
1764
     * of a button/href, any window.open calls aren't resolved relative to the app location.
1765
     * So here we replace the open() method with one that does resolve the url correctly.
1766
     */
1767
    windowToModify.open = function(url, windowName, windowFeatures, replaceFlag) {
1768

    
1769
        if (url.startsWith("http://") || url.startsWith("https://") || url.startsWith("/")) {
1770
            return originalOpen(url, windowName, windowFeatures, replaceFlag);
1771
        }
1772

    
1773
        // Reduce the current path to the directory
1774
        var currentPath = windowToModify.location.pathname || "/";
1775
        currentPath = currentPath.replace(/\/[^\/]*$/, "/");
1776

    
1777
        // Remove any leading "./" from the new url.
1778
        url = url.replace(/^\.\//, "");
1779

    
1780
        newUrl = currentPath + url;
1781

    
1782
        var openedWindow = originalOpen(newUrl, windowName, windowFeatures, replaceFlag);
1783
        LOG.debug("window.open call intercepted; window ID (which you can use with selectWindow()) is \"" +  windowName + "\"");
1784
        if (windowName!=null) {
1785
        	openedWindow["seleniumWindowName"] = windowName;
1786
        }
1787
        return openedWindow;
1788
    };
1789
};
1790

    
1791
MozillaBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
1792
    var win = this.getCurrentWindow();
1793
    triggerEvent(element, 'focus', false);
1794

    
1795
    // Add an event listener that detects if the default action has been prevented.
1796
    // (This is caused by a javascript onclick handler returning false)
1797
    // we capture the whole event, rather than the getPreventDefault() state at the time,
1798
    // because we need to let the entire event bubbling and capturing to go through
1799
    // before making a decision on whether we should force the href
1800
    var savedEvent = null;
1801

    
1802
    element.addEventListener(eventType, function(evt) {
1803
        savedEvent = evt;
1804
    }, false);
1805
    
1806
    this._modifyElementTarget(element);
1807

    
1808
    // Trigger the event.
1809
    this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
1810

    
1811
    if (this._windowClosed(win)) {
1812
        return;
1813
    }
1814
    
1815
    // Perform the link action if preventDefault was set.
1816
    // In chrome URL, the link action is already executed by triggerMouseEvent.
1817
    if (!browserVersion.isChrome && savedEvent != null && !savedEvent.getPreventDefault()) {
1818
        var targetWindow = this.browserbot._getTargetWindow(element);
1819
        if (element.href) {
1820
            targetWindow.location.href = element.href;
1821
        } else {
1822
            this.browserbot._handleClickingImagesInsideLinks(targetWindow, element);
1823
        }
1824
    }
1825

    
1826
};
1827

    
1828

    
1829
OperaBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
1830
    var win = this.getCurrentWindow();
1831
    triggerEvent(element, 'focus', false);
1832
    
1833
    this._modifyElementTarget(element);
1834

    
1835
    // Trigger the click event.
1836
    this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
1837

    
1838
    if (this._windowClosed(win)) {
1839
        return;
1840
    }
1841

    
1842
};
1843

    
1844

    
1845
KonquerorBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
1846
    var win = this.getCurrentWindow();
1847
    triggerEvent(element, 'focus', false);
1848
    
1849
    this._modifyElementTarget(element);
1850

    
1851
    if (element[eventType]) {
1852
    	element[eventType]();
1853
    }
1854
    else {
1855
        this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
1856
    }
1857

    
1858
    if (this._windowClosed(win)) {
1859
        return;
1860
    }
1861

    
1862
};
1863

    
1864
SafariBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
1865
    triggerEvent(element, 'focus', false);
1866
    var wasChecked = element.checked;
1867
    
1868
    this._modifyElementTarget(element);
1869

    
1870
    // For form element it is simple.
1871
    if (element[eventType]) {
1872
    	element[eventType]();
1873
    }
1874
    // For links and other elements, event emulation is required.
1875
    else {
1876
        var targetWindow = this.browserbot._getTargetWindow(element);
1877
        // todo: deal with anchors?
1878
        this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
1879

    
1880
    }
1881

    
1882
};
1883

    
1884
SafariBrowserBot.prototype.refresh = function() {
1885
    var win = this.getCurrentWindow();
1886
    if (win.location.hash) {
1887
        // DGF Safari refuses to refresh when there's a hash symbol in the URL
1888
        win.location.hash = "";
1889
        var actuallyReload = function() {
1890
            win.location.reload(true);
1891
        }
1892
        window.setTimeout(actuallyReload, 1);
1893
    } else {
1894
        win.location.reload(true);
1895
    }
1896
};
1897

    
1898
IEBrowserBot.prototype._fireEventOnElement = function(eventType, element, clientX, clientY) {
1899
    var win = this.getCurrentWindow();
1900
    triggerEvent(element, 'focus', false);
1901

    
1902
    var wasChecked = element.checked;
1903

    
1904
    // Set a flag that records if the page will unload - this isn't always accurate, because
1905
    // <a href="javascript:alert('foo'):"> triggers the onbeforeunload event, even thought the page won't unload
1906
    var pageUnloading = false;
1907
    var pageUnloadDetector = function() {
1908
        pageUnloading = true;
1909
    };
1910
    win.attachEvent("onbeforeunload", pageUnloadDetector);
1911
    this._modifyElementTarget(element);
1912
    if (element[eventType]) {
1913
    	element[eventType]();
1914
    }
1915
    else {
1916
        this.browserbot.triggerMouseEvent(element, eventType, true, clientX, clientY);
1917
    }
1918

    
1919

    
1920
    // If the page is going to unload - still attempt to fire any subsequent events.
1921
    // However, we can't guarantee that the page won't unload half way through, so we need to handle exceptions.
1922
    try {
1923
        win.detachEvent("onbeforeunload", pageUnloadDetector);
1924

    
1925
        if (this._windowClosed(win)) {
1926
            return;
1927
        }
1928

    
1929
        // Onchange event is not triggered automatically in IE.
1930
        if (isDefined(element.checked) && wasChecked != element.checked) {
1931
            triggerEvent(element, 'change', true);
1932
        }
1933

    
1934
    }
1935
    catch (e) {
1936
        // If the page is unloading, we may get a "Permission denied" or "Unspecified error".
1937
        // Just ignore it, because the document may have unloaded.
1938
        if (pageUnloading) {
1939
            LOG.logHook = function() {
1940
            };
1941
            LOG.warn("Caught exception when firing events on unloading page: " + e.message);
1942
            return;
1943
        }
1944
        throw e;
1945
    }
1946
};
(11-11/20)