Skip to content

Commit

Permalink
Enabling handling of authentication dialogs in IE driver
Browse files Browse the repository at this point in the history
  • Loading branch information
jimevans committed Jul 14, 2015
1 parent 3fdc4cf commit 7604d12
Show file tree
Hide file tree
Showing 19 changed files with 206 additions and 33 deletions.
95 changes: 76 additions & 19 deletions cpp/iedriver/Alert.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ Alert::Alert(BrowserHandle browser, HWND handle) {

HWND direct_ui_child = this->GetDirectUIChild();
this->is_standard_alert_ = direct_ui_child == NULL;

std::vector<HWND> text_boxes;
::EnumChildWindows(this->alert_handle_,
&Alert::FindTextBoxes,
reinterpret_cast<LPARAM>(&text_boxes));
this->is_security_alert_ = text_boxes.size() > 1;
}


Expand Down Expand Up @@ -68,28 +74,53 @@ int Alert::Dismiss() {
return WD_SUCCESS;
}

int Alert::SendKeys(std::string keys) {
int Alert::SendKeys(const std::string& keys) {
LOG(TRACE) << "Entering Alert::SendKeys";
HWND text_box_handle = NULL;
// Alert present, find the OK button.
return this->SendKeysInternal(keys, 0);
}

int Alert::SetUserName(const std::string& username) {
LOG(TRACE) << "Entering Alert::SetUserName";
// If this isn't a security alert, return an error.
if (!this->is_security_alert_) {
return EUNEXPECTEDALERTOPEN;
}
return this->SendKeysInternal(username, 0);
}

int Alert::SetPassword(const std::string& password) {
LOG(TRACE) << "Entering Alert::SetPassword";
// If this isn't a security alert, return an error.
if (!this->is_security_alert_) {
return EUNEXPECTEDALERTOPEN;
}
return this->SendKeysInternal(password, ES_PASSWORD);
}

int Alert::SendKeysInternal(const std::string& keys, const long text_box_style) {
LOG(TRACE) << "Entering Alert::SendKeysInternal";
TextBoxFindInfo text_box_find_info;
text_box_find_info.textbox_handle = NULL;
text_box_find_info.style_match = text_box_style;
// Alert present, find the text box.
// Retry up to 10 times to find the dialog.
int max_wait = 10;
while ((text_box_handle == NULL) && --max_wait) {
while ((text_box_find_info.textbox_handle == NULL) && --max_wait) {
::EnumChildWindows(this->alert_handle_,
&Alert::FindTextBox,
reinterpret_cast<LPARAM>(&text_box_handle));
if (text_box_handle == NULL) {
reinterpret_cast<LPARAM>(&text_box_find_info));
if (text_box_find_info.textbox_handle == NULL) {
::Sleep(50);
}
}

if (text_box_handle == NULL) {
if (text_box_find_info.textbox_handle == NULL) {
LOG(WARN) << "Text box not found on alert";
return EELEMENTNOTDISPLAYED;
} else {
LOG(DEBUG) << "Sending keystrokes to alert using SendMessage";
std::wstring text = StringUtilities::ToWString(keys);
::SendMessage(text_box_handle,
::SendMessage(text_box_find_info.textbox_handle,
WM_SETTEXT,
NULL,
reinterpret_cast<LPARAM>(text.c_str()));
Expand All @@ -104,9 +135,11 @@ std::string Alert::GetText() {
alert_text_value = this->GetStandardDialogText();
} else {
std::string alert_text = this->GetDirectUIDialogText();
size_t first_crlf = alert_text.find("\r\n\r\n");
if (first_crlf != std::string::npos && first_crlf + 4 < alert_text.size()) {
alert_text_value = alert_text.substr(first_crlf + 4);
if (!this->is_security_alert_) {
size_t first_crlf = alert_text.find("\r\n\r\n");
if (first_crlf != std::string::npos && first_crlf + 4 < alert_text.size()) {
alert_text_value = alert_text.substr(first_crlf + 4);
}
}
}
return alert_text_value;
Expand All @@ -133,11 +166,13 @@ std::string Alert::GetStandardDialogText() {

// BIG ASSUMPTION HERE! If we found the text label, assume that
// all other controls on the alert are fully drawn too.
HWND text_box_handle = NULL;
TextBoxFindInfo textbox_find_info;
textbox_find_info.textbox_handle = NULL;
textbox_find_info.style_match = 0;
::EnumChildWindows(this->alert_handle_,
&Alert::FindTextBox,
reinterpret_cast<LPARAM>(&text_box_handle));
if (text_box_handle) {
reinterpret_cast<LPARAM>(&textbox_find_info));
if (textbox_find_info.textbox_handle) {
// There's a text box on the alert. That means the first
// label found is the system-provided label. Ignore that
// one and return the next one.
Expand Down Expand Up @@ -367,7 +402,8 @@ bool Alert::IsOKButton(HWND button_handle) {
::GetClassName(button_handle, &button_window_class[0], static_cast<int>(button_window_class.size()));
if (wcscmp(&button_window_class[0], L"Button") == 0) {
long window_long = ::GetWindowLong(button_handle, GWL_STYLE);
return (window_long & BS_DEFCOMMANDLINK) == BS_DEFCOMMANDLINK;
long button_style = window_long & BS_TYPEMASK;
return button_style == BS_DEFCOMMANDLINK || button_style == BS_DEFPUSHBUTTON;
}
return false;
}
Expand All @@ -381,9 +417,10 @@ bool Alert::IsCancelButton(HWND button_handle) {
::GetClassName(button_handle, &button_window_class[0], static_cast<int>(button_window_class.size()));
if (wcscmp(&button_window_class[0], L"Button") == 0) {
long window_long = ::GetWindowLong(button_handle, GWL_STYLE);
long button_style = window_long & BS_TYPEMASK;
// The BS_DEFCOMMANDLINK mask includes BS_COMMANDLINK, but we
// want only to match those without the default bits set.
return (window_long & BS_DEFCOMMANDLINK) == BS_COMMANDLINK;
return button_style == BS_COMMANDLINK || button_style == BS_PUSHBUTTON;
}
return false;
}
Expand All @@ -400,13 +437,22 @@ BOOL CALLBACK Alert::FindDialogButton(HWND hwnd, LPARAM arg) {
}

BOOL CALLBACK Alert::FindTextBox(HWND hwnd, LPARAM arg) {
HWND *dialog_handle = reinterpret_cast<HWND*>(arg);
TextBoxFindInfo* find_info = reinterpret_cast<TextBoxFindInfo*>(arg);
std::vector<wchar_t> child_window_class(100);
::GetClassName(hwnd, &child_window_class[0], 100);

if (wcscmp(&child_window_class[0], L"Edit") == 0) {
*dialog_handle = hwnd;
return FALSE;
if (find_info->style_match == 0) {
find_info->textbox_handle = hwnd;;;
return FALSE;
} else {
long window_long = ::GetWindowLong(hwnd, GWL_STYLE);
long edit_style = window_long & find_info->style_match;
if (edit_style == find_info->style_match) {
find_info->textbox_handle = hwnd;
return FALSE;
}
}
}
return TRUE;
}
Expand Down Expand Up @@ -445,4 +491,15 @@ BOOL CALLBACK Alert::FindDirectUIChild(HWND hwnd, LPARAM arg){
return FALSE;
}

BOOL CALLBACK Alert::FindTextBoxes(HWND hwnd, LPARAM arg) {
std::vector<HWND>* dialog_handles = reinterpret_cast<std::vector<HWND>*>(arg);
std::vector<wchar_t> child_window_class(100);
::GetClassName(hwnd, &child_window_class[0], 100);

if (wcscmp(&child_window_class[0], L"Edit") == 0) {
dialog_handles->push_back(hwnd);
}
return TRUE;
}

} // namespace webdriver
14 changes: 13 additions & 1 deletion cpp/iedriver/Alert.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,13 @@ class Alert {

int Accept(void);
int Dismiss(void);
int SendKeys(std::string keys);
int SendKeys(const std::string& keys);
std::string GetText(void);
int SetUserName(const std::string& username);
int SetPassword(const std::string& password);

bool is_standard_alert(void) const { return this->is_standard_alert_; }
bool is_security_alert(void) const { return this->is_security_alert_; }

private:
typedef bool (__cdecl *ISBUTTONMATCHPROC)(HWND);
Expand All @@ -59,11 +62,18 @@ class Alert {
int excluded_control_id;
};

struct TextBoxFindInfo {
HWND textbox_handle;
long style_match;
};

enum BUTTON_TYPE {
OK,
CANCEL
};

int SendKeysInternal(const std::string& keys, const long text_box_style);

DialogButtonInfo GetDialogButton(BUTTON_TYPE button_type);
int ClickAlertButton(DialogButtonInfo button_info);
std::string GetStandardDialogText(void);
Expand All @@ -77,10 +87,12 @@ class Alert {
static BOOL CALLBACK FindTextBox(HWND hwnd, LPARAM arg);
static BOOL CALLBACK FindTextLabel(HWND hwnd, LPARAM arg);
static BOOL CALLBACK FindDirectUIChild(HWND hwnd, LPARAM arg);
static BOOL CALLBACK FindTextBoxes(HWND hwnd, LPARAM arg);

HWND alert_handle_;
BrowserHandle browser_;
bool is_standard_alert_;
bool is_security_alert_;
};


Expand Down
2 changes: 1 addition & 1 deletion cpp/iedriver/CommandHandlers/AcceptAlertCommandHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class AcceptAlertCommandHandler : public IECommandHandler {
::Sleep(100);
HWND alert_handle = browser_wrapper->GetActiveDialogWindowHandle();
if (alert_handle == NULL) {
response->SetErrorResponse(EMODALDIALOGOPEN, "No alert is active");
response->SetErrorResponse(ENOSUCHALERT, "No alert is active");
} else {
Alert dialog(browser_wrapper, alert_handle);
status_code = dialog.Accept();
Expand Down
2 changes: 1 addition & 1 deletion cpp/iedriver/CommandHandlers/DismissAlertCommandHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class DismissAlertCommandHandler : public AcceptAlertCommandHandler {
::Sleep(100);
HWND alert_handle = browser_wrapper->GetActiveDialogWindowHandle();
if (alert_handle == NULL) {
response->SetErrorResponse(EMODALDIALOGOPEN, "No alert is active");
response->SetErrorResponse(ENOSUCHALERT, "No alert is active");
} else {
Alert dialog(browser_wrapper, alert_handle);
status_code = dialog.Dismiss();
Expand Down
2 changes: 1 addition & 1 deletion cpp/iedriver/CommandHandlers/GetAlertTextCommandHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class GetAlertTextCommandHandler : public IECommandHandler {
::Sleep(100);
HWND alert_handle = browser_wrapper->GetActiveDialogWindowHandle();
if (alert_handle == NULL) {
response->SetErrorResponse(EMODALDIALOGOPEN, "No alert is active");
response->SetErrorResponse(ENOSUCHALERT, "No alert is active");
} else {
Alert dialog(browser_wrapper, alert_handle);
std::string alert_text_value = dialog.GetText();
Expand Down
7 changes: 5 additions & 2 deletions cpp/iedriver/CommandHandlers/ScreenshotCommandHandler.h
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,9 @@ class ScreenshotCommandHandler : public IECommandHandler {
LOG(DEBUG) << "Initial browser window sizes are (w, h): "
<< original_width << ", " << original_height;

bool requires_rezise = original_width <= max_image_dimensions.right ||
original_height <= max_image_dimensions.bottom;

// The resize message is being ignored if the window appears to be
// maximized. There's likely a way to bypass that. The kludgy way
// is to unmaximize the window, then move on with setting the window
Expand Down Expand Up @@ -189,6 +192,8 @@ class ScreenshotCommandHandler : public IECommandHandler {
hook.PushData(sizeof(max_image_dimensions), &max_image_dimensions);
browser->SetHeight(max_image_dimensions.bottom);

hook.Dispose();

// Capture the window's canvas to a DIB.
BOOL created = this->image_->Create(document_info.width,
document_info.height,
Expand All @@ -205,8 +210,6 @@ class ScreenshotCommandHandler : public IECommandHandler {
LOG(WARN) << "PrintWindow API is not able to get content window screenshot";
}

hook.Dispose();

// Restore the browser to the original dimensions.
if (is_maximized) {
::ShowWindow(ie_window_handle, SW_MAXIMIZE);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ class SendKeysToAlertCommandHandler : public IECommandHandler {
::Sleep(100);
HWND alert_handle = browser_wrapper->GetActiveDialogWindowHandle();
if (alert_handle == NULL) {
response->SetErrorResponse(EMODALDIALOGOPEN, "No alert is active");
response->SetErrorResponse(ENOSUCHALERT, "No alert is active");
} else {
Alert dialog(browser_wrapper, alert_handle);
status_code = dialog.SendKeys(text_parameter_iterator->second.asString());
Expand Down
86 changes: 86 additions & 0 deletions cpp/iedriver/CommandHandlers/SetAlertCredentialsCommandHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you 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.

#ifndef WEBDRIVER_IE_SETALERTCREDENTIALSCOMMANDHANDLER_H_
#define WEBDRIVER_IE_SETALERTCREDENTIALSCOMMANDHANDLER_H_

#include "../Alert.h"
#include "../Browser.h"
#include "../IECommandHandler.h"
#include "../IECommandExecutor.h"

namespace webdriver {

class SetAlertCredentialsCommandHandler : public IECommandHandler {
public:
SetAlertCredentialsCommandHandler(void) {
}

virtual ~SetAlertCredentialsCommandHandler(void) {
}

protected:
void ExecuteInternal(const IECommandExecutor& executor,
const ParametersMap& command_parameters,
Response* response) {
ParametersMap::const_iterator username_parameter_iterator = command_parameters.find("username");
if (username_parameter_iterator == command_parameters.end()) {
response->SetErrorResponse(400, "Missing parameter: username");
return;
}
std::string username = username_parameter_iterator->second.asString();

ParametersMap::const_iterator password_parameter_iterator = command_parameters.find("password");
if (password_parameter_iterator == command_parameters.end()) {
response->SetErrorResponse(400, "Missing parameter: password");
return;
}
std::string password = password_parameter_iterator->second.asString();

BrowserHandle browser_wrapper;
int status_code = executor.GetCurrentBrowser(&browser_wrapper);
if (status_code != WD_SUCCESS) {
response->SetErrorResponse(status_code, "Unable to get current browser");
return;
}
// This sleep is required to give IE time to draw the dialog.
::Sleep(100);
HWND alert_handle = browser_wrapper->GetActiveDialogWindowHandle();
if (alert_handle == NULL) {
response->SetErrorResponse(ENOSUCHALERT, "No alert is active");
} else {
Alert dialog(browser_wrapper, alert_handle);
status_code = dialog.SetUserName(username);
if (status_code != WD_SUCCESS) {
response->SetErrorResponse(status_code,
"Could not set user name");
return;
}
status_code = dialog.SetPassword(password);
if (status_code != WD_SUCCESS) {
response->SetErrorResponse(status_code,
"Could not set password");
return;
}
response->SetSuccessResponse(Json::Value::null);
}
}

};

} // namespace webdriver

#endif // WEBDRIVER_IE_SETALERTCREDENTIALSCOMMANDHANDLER_H_
Loading

0 comments on commit 7604d12

Please sign in to comment.