blob: 5f80bd56666fcbcd88d5363c6c3bed28235dfd97 [file] [log] [blame]
/*
Copyright 2010 WebDriver committers
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/**
* @fileoverview Methods for dealing with modal dialogs.
*/
goog.provide('fxdriver.modals');
goog.require('WebDriverError');
goog.require('bot.ErrorCode');
goog.require('fxdriver.Timer');
goog.require('fxdriver.logging');
goog.require('fxdriver.moz');
goog.require('fxdriver.utils');
fxdriver.modals.isModalPresent = function(callback, timeout) {
fxdriver.modaltimer = new fxdriver.Timer();
fxdriver.modaltimer.runWhenTrue(
function() {
var modal = fxdriver.modals.find_();
return modal && modal.document;
},
function() { callback(true) },
timeout,
function() { callback(false) });
};
fxdriver.modals.acceptAlert = function(driver) {
var modal = fxdriver.modals.find_();
var button = fxdriver.modals.findButton_(modal, 'accept');
button.click();
fxdriver.modals.clearFlag_(driver);
};
fxdriver.modals.dismissAlert = function(driver) {
var modal = fxdriver.modals.find_();
var button = fxdriver.modals.findButton_(modal, 'cancel');
if (!button) {
fxdriver.logging.info('No cancel button Falling back to the accept button');
button = fxdriver.modals.findButton_(modal, 'accept');
}
button.click();
fxdriver.modals.clearFlag_(driver);
};
fxdriver.modals.getText = function(driver) {
return driver.modalOpen;
};
fxdriver.modals.setValue = function(driver, value) {
var modal = fxdriver.modals.find_();
var textbox = modal.document.getElementById('loginTextbox');
try {
var isVisible = false;
if (bot.userAgent.isProductVersion(8)) {
isVisible = textbox.clientHeight != 0;
} else {
isVisible = textbox.selectionStart > -1;
}
if (isVisible) {
textbox.value = value;
return;
}
} catch (ignored) {}
throw new WebDriverError(bot.ErrorCode.ELEMENT_NOT_VISIBLE, 'Alert did not have a text box');
};
fxdriver.modals.find_ = function() {
var window = fxdriver.utils.windowMediator().getMostRecentWindow('');
window = fxdriver.moz.unwrap(window);
// Get the WebBrowserChrome and check if it's a modal window
var chrome = window.QueryInterface(CI.nsIInterfaceRequestor).
getInterface(CI.nsIWebNavigation).
QueryInterface(CI.nsIDocShellTreeItem).
treeOwner.
QueryInterface(CI.nsIInterfaceRequestor).
getInterface(CI.nsIWebBrowserChrome);
if (!chrome.isWindowModal()) {
return null;
}
return window;
};
fxdriver.modals.findButton_ = function(modal, value) {
var doc = modal.document;
var dialog = doc.getElementsByTagName('dialog')[0];
return dialog.getButton(value);
};
fxdriver.modals.setFlag = function(driver, flagValue) {
driver.modalOpen = flagValue;
};
fxdriver.modals.clearFlag_ = function(driver) {
fxdriver.modals.setFlag(driver, false);
};
fxdriver.modals.findAssociatedDriver_ = function(window) {
var ww = CC['@mozilla.org/embedcomp/window-watcher;1'].getService(CI['nsIWindowWatcher']);
var parent = window ? window : ww.activeWindow;
if (parent.wrappedJSObject) {
parent = parent.wrappedJSObject;
}
var top = parent.top;
// Now iterate over all open browsers to find the one we belong to
var wm = CC['@mozilla.org/appshell/window-mediator;1'].getService(CI['nsIWindowMediator']);
var allWindows = wm.getEnumerator('navigator:browser');
while (allWindows.hasMoreElements()) {
var chrome = allWindows.getNext().QueryInterface(CI.nsIDOMWindow);
if (chrome.content == window) {
return chrome.fxdriver;
} else if (chrome.content.top == window.top) {
return chrome.fxdriver;
}
}
// There's no meaningful way we can reach this.
fxdriver.logging.info('Unable to find the associated driver');
return undefined;
};
fxdriver.modals.signalOpenModal = function(parent, text) {
fxdriver.logging.info('signalOpenModal');
// Try to grab the top level window
var driver = fxdriver.modals.findAssociatedDriver_(parent);
if (driver && driver.response_) {
fxdriver.modals.setFlag(driver, text);
var res = driver.response_;
if (driver.response_.name == 'executeAsyncScript' && !driver.response_.responseSent_) {
// Special case handling. If a modal is open when executeAsyncScript
// tries to respond with a result, it doesn't do anything, so that this
// codepath can be followed.
fxdriver.modals.isModalPresent(function(present) {
var errorMessage = 'Unexpected modal dialog (text: ' + text + ')';
if (present) {
try {
fxdriver.modals.dismissAlert(driver);
} catch (e) {
errorMessage +=
' The alert could not be closed. The browser may be in a ' +
'wildly inconsistent state, and the alert may still be ' +
'open. This is not good. If you can reliably reproduce this' +
', please report a new issue at ' +
'https://code.google.com/p/selenium/issues/entry ' +
'with reproduction steps. Exception message: ' + e;
}
} else {
errorMessage += ' The alert disappeared before it could be closed.';
}
res.sendError(new WebDriverError(bot.ErrorCode.MODAL_DIALOG_OPENED,
errorMessage, {alert: {text: text}}));
}, 2000);
} else {
// We hope that modals can only be opened by actions which don't pay
// attention to the results of the command. Given this, the only ways we
// can get to this code path are:
// * The command being executed was a getAlertText command, in which
// case we expect the text of the alert to be returned
// * The command being executed was an action which caused the alert
// to be displayed, but we ignore the results of actions, so the
// value will be ignored.
res.value = text;
res.send();
}
}
};
/**
* Ensure that the given value is an acceptable one for the unexpected alert
* behaviour setting.
*
* @param {string?} value The value to check.
* @return {string} The acceptable value, or 'dismiss'.
*/
fxdriver.modals.asAcceptableAlertValue = function(value) {
if ('accept' == value || 'ignore' == value) {
return value;
}
return 'dismiss';
};
/**
* Configure modal behaviours for the firefox driver.
*
* @param {string?} unexpectedAlertBehaviour How to handle unexpected alerts.
*/
fxdriver.modals.configure = function(unexpectedAlertBehaviour) {
var prefs = fxdriver.moz.getService(
'@mozilla.org/preferences-service;1', 'nsIPrefBranch');
var value = fxdriver.modals.asAcceptableAlertValue(unexpectedAlertBehaviour);
prefs.setCharPref('webdriver_unexpected_alert_behaviour', value);
};
/**
* @return {string} The behavior when an unexpected alert is seen.
*/
fxdriver.modals.getUnexpectedAlertBehaviour = function() {
var prefs = fxdriver.moz.getService(
'@mozilla.org/preferences-service;1', 'nsIPrefBranch');
if (!prefs.prefHasUserValue('webdriver_unexpected_alert_behaviour')) {
return 'dismiss';
}
var raw = prefs.getCharPref('webdriver_unexpected_alert_behaviour');
return fxdriver.modals.asAcceptableAlertValue(raw);
};