PageRenderTime 86ms CodeModel.GetById 15ms app.highlight 56ms RepoModel.GetById 2ms app.codeStats 0ms

/testing/selenium-core/scripts/selenium-browserbot.js

http://datanucleus-appengine.googlecode.com/
JavaScript | 2285 lines | 1743 code | 263 blank | 279 comment | 485 complexity | 37b7d76fc9be9ccd2d0513be52305ee1 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   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.
  29var 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 = '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
 106var PageBot = function(){};
 107
 108BrowserBot.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.
 136BrowserBot.prototype.doModalDialogTest = function(test) {
 137    this.modalDialogTest = test;
 138};
 139
 140BrowserBot.prototype.cancelNextConfirmation = function(result) {
 141    this.nextConfirmResult = result;
 142};
 143
 144BrowserBot.prototype.setNextPromptResult = function(result) {
 145    this.nextPromptResult = result;
 146};
 147
 148BrowserBot.prototype.hasAlerts = function() {
 149    return (this.recordedAlerts.length > 0);
 150};
 151
 152BrowserBot.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
 166BrowserBot.prototype.relayToRC = function(name) {
 167        var object = eval(name);
 168        var s = 'state:' + serializeObject(name, object) + "\n";
 169        sendToRC(s,"state=true");
 170}
 171
 172BrowserBot.prototype.resetPopups = function() {
 173    this.recordedAlerts = [];
 174    this.recordedConfirmations = [];
 175    this.recordedPrompts = [];
 176}
 177
 178BrowserBot.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
 187BrowserBot.prototype.hasConfirmations = function() {
 188    return (this.recordedConfirmations.length > 0);
 189};
 190
 191BrowserBot.prototype.getNextConfirmation = function() {
 192    var t = this.recordedConfirmations.shift();
 193    this.relayBotToRC("browserbot.recordedConfirmations");
 194    return t;
 195};
 196
 197BrowserBot.prototype.hasPrompts = function() {
 198    return (this.recordedPrompts.length > 0);
 199};
 200
 201BrowserBot.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
 209BrowserBot.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
 277BrowserBot.prototype._windowClosed = function(win) {
 278    var c = win.closed;
 279    if (c == null) return true;
 280    return c;
 281};
 282
 283BrowserBot.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
 312BrowserBot.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
 342BrowserBot.prototype._selectTopWindow = function() {
 343    this.currentWindowName = null;
 344    this.currentWindow = this.topWindow;
 345    this.topFrame = this.topWindow;
 346    this.isSubFrameSelected = false;
 347}
 348
 349BrowserBot.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
 356BrowserBot.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
 365BrowserBot.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
 431BrowserBot.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
 490BrowserBot.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
 499BrowserBot.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
 513BrowserBot.prototype.setIFrameLocation = function(iframe, location) {
 514    iframe.src = location;
 515};
 516
 517BrowserBot.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
 534BrowserBot.prototype.getCurrentPage = function() {
 535    return this;
 536};
 537
 538BrowserBot.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 */
 617BrowserBot.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]);
 648frameElement[this.uniqueId] = marker;
 649LOG.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
 657BrowserBot.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
 663BrowserBot.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
 699BrowserBot.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 */
 728BrowserBot.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
 776BrowserBot.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
 814BrowserBot.prototype._isSameDocument = function(originalDocument, currentDocument) {
 815    return originalDocument === currentDocument;
 816};
 817
 818
 819BrowserBot.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 */
 862BrowserBot.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 */
 873BrowserBot.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
 888BrowserBot.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
 896BrowserBot.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
 906BrowserBot.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
 926BrowserBot.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 */
 968BrowserBot.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
 998BrowserBot.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
1012BrowserBot.prototype._handleClosedSubFrame = function(testWindow, doNotModify) {
1013    if (this.proxyInjectionMode) {
1014        return testWindow;
1015    }
1016
1017    if (this.isSubFrameSelected) {
1018        var missing = true;
1019        if (testWindow.parent && testWindow.parent.frames && testWindow.parent.frames.length) {
1020            for (var i = 0; i < testWindow.parent.frames.length; i++) {
1021                if (testWindow.parent.frames[i] == testWindow) {
1022                    missing = false;
1023                    break;
1024                }
1025            }
1026        }
1027        if (missing) {
1028            LOG.warn("Current subframe appears to have closed; selecting top frame");
1029            this.selectFrame("relative=top");
1030            return this.getCurrentWindow(doNotModify);
1031        }
1032    } else if (this._windowClosed(testWindow)) {
1033        var closedError = new SeleniumError("Current window or frame is closed!");
1034        closedError.windowClosed = true;
1035        throw closedError;
1036    }
1037    return testWindow;
1038};
1039
1040BrowserBot.prototype.highlight = function (element, force) {
1041    if (force || this.shouldHighlightLocatedElement) {
1042        try {
1043            highlight(element);
1044        } catch (e) {} // DGF element highlighting is low-priority and possibly dangerous
1045    }
1046    return element;
1047}
1048
1049BrowserBot.prototype.setShouldHighlightElement = function (shouldHighlight) {
1050    this.shouldHighlightLocatedElement = shouldHighlight;
1051}
1052
1053/*****************************************************************/
1054/* BROWSER-SPECIFIC FUNCTIONS ONLY AFTER THIS LINE */
1055
1056
1057BrowserBot.prototype._registerAllLocatorFunctions = function() {
1058    // TODO - don't do this in the constructor - only needed once ever
1059    this.locationStrategies = {};
1060    for (var functionName in this) {
1061        var result = /^locateElementBy([A-Z].+)$/.exec(functionName);
1062        if (result != null) {
1063            var locatorFunction = this[functionName];
1064            if (typeof(locatorFunction) != 'function') {
1065                continue;
1066            }
1067            // Use a specified prefix in preference to one generated from
1068            // the function name
1069            var locatorPrefix = locatorFunction.prefix || result[1].toLowerCase();
1070            this.locationStrategies[locatorPrefix] = locatorFunction;
1071        }
1072    }
1073
1074    /**
1075     * Find a locator based on a prefix.
1076     */
1077    this.findElementBy = function(locatorType, locator, inDocument, inWindow) {
1078        var locatorFunction = this.locationStrategies[locatorType];
1079        if (! locatorFunction) {
1080            throw new SeleniumError("Unrecognised locator type: '" + locatorType + "'");
1081        }
1082        return locatorFunction.call(this, locator, inDocument, inWindow);
1083    };
1084
1085    /**
1086     * The implicit locator, that is used when no prefix is supplied.
1087     */
1088    this.locationStrategies['implicit'] = function(locator, inDocument, inWindow) {
1089        if (locator.startsWith('//')) {
1090            return this.locateElementByXPath(locator, inDocument, inWindow);
1091        }
1092        if (locator.startsWith('document.')) {
1093            return this.locateElementByDomTraversal(locator, inDocument, inWindow);
1094        }
1095        return this.locateElementByIdentifier(locator, inDocument, inWindow);
1096    };
1097}
1098
1099BrowserBot.prototype.getDocument = function() {
1100    return this.getCurrentWindow().document;
1101}
1102
1103BrowserBot.prototype.getTitle = function() {
1104    var t = this.getDocument().title;
1105    if (typeof(t) == "string") {
1106        t = t.trim();
1107    }
1108    return t;
1109}
1110
1111BrowserBot.prototype.getCookieByName = function(cookieName, doc) {
1112    if (!doc) doc = this.getDocument();
1113    var ck = doc.cookie;
1114    if (!ck) return null;
1115    var ckPairs = ck.split(/;/);
1116    for (var i = 0; i < ckPairs.length; i++) {
1117        var ckPair = ckPairs[i].trim();
1118        var ckNameValue = ckPair.split(/=/);
1119        var ckName = decodeURIComponent(ckNameValue[0]);
1120        if (ckName === cookieName) {
1121            return decodeURIComponent(ckNameValue[1]);
1122        }
1123    }
1124    return null;
1125}
1126
1127BrowserBot.prototype.getAllCookieNames = function(doc) {
1128    if (!doc) doc = this.getDocument();
1129    var ck = doc.cookie;
1130    if (!ck) return [];
1131    var cookieNames = [];
1132    var ckPairs = ck.split(/;/);
1133    for (var i = 0; i < ckPairs.length; i++) {
1134        var ckPair = ckPairs[i].trim();
1135        var ckNameValue = ckPair.split(/=/);
1136        var ckName = decodeURIComponent(ckNameValue[0]);
1137        cookieNames.push(ckName);
1138    }
1139    return cookieNames;
1140}
1141
1142BrowserBot.prototype.deleteCookie = function(cookieName, domain, path, doc) {
1143    if (!doc) doc = this.getDocument();
1144    var expireDateInMilliseconds = (new Date()).getTime() + (-1 * 1000);
1145    var cookie = cookieName + "=deleted; ";
1146    if (path) {
1147        cookie += "path=" + path + "; ";
1148    }
1149    if (domain) {
1150        cookie += "domain=" + domain + "; ";
1151    }
1152    cookie += "expires=" + new Date(expireDateInMilliseconds).toGMTString();
1153    LOG.debug("Setting cookie to: " + cookie);
1154    doc.cookie = cookie;
1155}
1156
1157/** Try to delete cookie, return false if it didn't work */
1158BrowserBot.prototype._maybeDeleteCookie = function(cookieName, domain, path, doc) {
1159    this.deleteCookie(cookieName, domain, path, doc);
1160    return (!this.getCookieByName(cookieName, doc));
1161}
1162    
1163
1164BrowserBot.prototype._recursivelyDeleteCookieDomains = function(cookieName, domain, path, doc) {
1165    var deleted = this._maybeDeleteCookie(cookieName, domain, path, doc);
1166    if (deleted) return true;
1167    var dotIndex = domain.indexOf(".");
1168    if (dotIndex == 0) {
1169        return this._recursivelyDeleteCookieDomains(cookieName, domain.substring(1), path, doc);
1170    } else if (dotIndex != -1) {
1171        return this._recursivelyDeleteCookieDomains(cookieName, domain.substring(dotIndex), path, doc);
1172    } else {
1173        // No more dots; try just not passing in a domain at all
1174        return this._maybeDeleteCookie(cookieName, null, path, doc);
1175    }
1176}
1177
1178BrowserBot.prototype._recursivelyDeleteCookie = function(cookieName, domain, path, doc) {
1179    var slashIndex = path.lastIndexOf("/");
1180    var finalIndex = path.length-1;
1181    if (slashIndex == finalIndex) {
1182        slashIndex--;
1183    }
1184    if (slashIndex != -1) {
1185        deleted = this._recursivelyDeleteCookie(cookieName, domain, path.substring(0, slashIndex+1), doc);
1186        if (deleted) return true;
1187    }
1188    return this._recursivelyDeleteCookieDomains(cookieName, domain, path, doc);
1189}
1190
1191BrowserBot.prototype.recursivelyDeleteCookie = function(cookieName, domain, path, win) {
1192    if (!win) win = this.getCurrentWindow();
1193    var doc = win.document;
1194    if (!domain) {
1195        domain = doc.domain;
1196    }
1197    if (!path) {
1198        path = win.location.pathname;
1199    }
1200    var deleted = this._recursivelyDeleteCookie(cookieName, "." + domain, path, doc);
1201    if (deleted) return;
1202    // Finally try a null path (Try it last because it's uncommon)
1203    deleted = this._recursivelyDeleteCookieDomains(cookieName, "." + domain, null, doc);
1204    if (deleted) return;
1205    throw new SeleniumError("Couldn't delete cookie " + cookieName);
1206}
1207
1208/*
1209 * Finds an element recursively in frames and nested frames
1210 * in the specified document, using various lookup protocols
1211 */
1212BrowserBot.prototype.findElementRecursive = function(locatorType, locatorString, inDocument, inWindow) {
1213
1214    var element = this.findElementBy(locatorType, locatorString, inDocument, inWindow);
1215    if (element != null) {
1216        return element;
1217    }
1218
1219    for (var i = 0; i < inWindow.frames.length; i++) {
1220        // On some browsers, the document object is undefined for third-party
1221        // frames.  Make sure the document is valid before continuing.
1222        if (inWindow.frames[i].document) {
1223            element = this.findElementRecursive(locatorType, locatorString, inWindow.frames[i].document, inWindow.frames[i]);
1224
1225            if (element != null) {
1226                return element;
1227            }
1228        }
1229    }
1230};
1231
1232/*
1233* Finds an element on the current page, using various lookup protocols
1234*/
1235BrowserBot.prototype.findElementOrNull = function(locator, win) {
1236    locator = parse_locator(locator);
1237
1238    if (win == null) {
1239        win = this.getCurrentWindow();
1240    }
1241    var element = this.findElementRecursive(locator.type, locator.string, win.document, win);
1242
1243    if (element != null) {
1244        return this.browserbot.highlight(element);
1245    }
1246
1247    // Element was not found by any locator function.
1248    return null;
1249};
1250
1251BrowserBot.prototype.findElement = function(locator, win) {
1252    var element = this.findElementOrNull(locator, win);
1253    if (element == null) throw new SeleniumError("Element " + locator + " not found");
1254    return element;
1255}
1256
1257/**
1258 * In non-IE browsers, getElementById() does not search by name.  Instead, we
1259 * we search separately by id and name.
1260 */
1261BrowserBot.prototype.locateElementByIdentifier = function(identifier, inDocument, inWindow) {
1262    return BrowserBot.prototype.locateElementById(identifier, inDocument, inWindow)
1263            || BrowserBot.prototype.locateElementByName(identifier, inDocument, inWindow)
1264            || null;
1265};
1266
1267/**
1268 * Find the element with id - can't rely on getElementById, coz it returns by name as well in IE..
1269 */
1270BrowserBot.prototype.locateElementById = function(identifier, inDocument, inWindow) {
1271    var element = inDocument.getElementById(identifier);
1272    if (element && element.id === identifier) {
1273        return element;
1274    }
1275    else if (browserVersion.isIE || browserVersion.isOpera) {
1276        // SEL-484
1277        var xpath = '/descendant::*[@id=' + identifier.quoteForXPath() + ']';
1278        return BrowserBot.prototype
1279            .locateElementByXPath(xpath, inDocument, inWindow);
1280    }
1281    else {
1282        return null;
1283    }
1284};
1285
1286/**
1287 * Find an element by name, refined by (optional) element-filter
1288 * expressions.
1289 */
1290BrowserBot.prototype.locateElementByName = function(locator, document, inWindow) {
1291    var elements = document.getElementsByTagName("*");
1292
1293    var filters = locator.split(' ');
1294    filters[0] = 'name=' + filters[0];
1295
1296    while (filters.length) {
1297        var filter = filters.shift();
1298        elements = this.selectElements(filter, elements, 'value');
1299    }
1300
1301    if (elements.length > 0) {
1302        return elements[0];
1303    }
1304    return null;
1305};
1306
1307/**
1308 * Finds an element using by evaluating the specfied string.
1309 */
1310BrowserBot.prototype.locateElementByDomTraversal = function(domTraversal, document, window) {
1311
1312    var browserbot = this.browserbot;
1313    var element = null;
1314    try {
1315        element = eval(domTraversal);
1316    } catch (e) {
1317        return null;
1318    }
1319
1320    if (!element) {
1321        return null;
1322    }
1323
1324    return element;
1325};
1326BrowserBot.prototype.locateElementByDomTraversal.prefix = "dom";
1327
1328/**
1329 * Finds an element identified by the xpath expression. Expressions _must_
1330 * begin with "//".
1331 */
1332BrowserBot.prototype.locateElementByXPath = function(xpath, inDocument, inWindow) {
1333    var results = eval_xpath(xpath, inDocument, {
1334        returnOnFirstMatch          : true,
1335        ignoreAttributesWithoutValue: this.ignoreAttributesWithoutValue,
1336        allowNativeXpath            : this.allow

Large files files are truncated, but you can click here to view the full file