Skip to content

Commit 7604d12

Browse files
committed
Enabling handling of authentication dialogs in IE driver
1 parent 3fdc4cf commit 7604d12

19 files changed

+206
-33
lines changed

cpp/iedriver/Alert.cpp

+76-19
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ Alert::Alert(BrowserHandle browser, HWND handle) {
2626

2727
HWND direct_ui_child = this->GetDirectUIChild();
2828
this->is_standard_alert_ = direct_ui_child == NULL;
29+
30+
std::vector<HWND> text_boxes;
31+
::EnumChildWindows(this->alert_handle_,
32+
&Alert::FindTextBoxes,
33+
reinterpret_cast<LPARAM>(&text_boxes));
34+
this->is_security_alert_ = text_boxes.size() > 1;
2935
}
3036

3137

@@ -68,28 +74,53 @@ int Alert::Dismiss() {
6874
return WD_SUCCESS;
6975
}
7076

71-
int Alert::SendKeys(std::string keys) {
77+
int Alert::SendKeys(const std::string& keys) {
7278
LOG(TRACE) << "Entering Alert::SendKeys";
73-
HWND text_box_handle = NULL;
74-
// Alert present, find the OK button.
79+
return this->SendKeysInternal(keys, 0);
80+
}
81+
82+
int Alert::SetUserName(const std::string& username) {
83+
LOG(TRACE) << "Entering Alert::SetUserName";
84+
// If this isn't a security alert, return an error.
85+
if (!this->is_security_alert_) {
86+
return EUNEXPECTEDALERTOPEN;
87+
}
88+
return this->SendKeysInternal(username, 0);
89+
}
90+
91+
int Alert::SetPassword(const std::string& password) {
92+
LOG(TRACE) << "Entering Alert::SetPassword";
93+
// If this isn't a security alert, return an error.
94+
if (!this->is_security_alert_) {
95+
return EUNEXPECTEDALERTOPEN;
96+
}
97+
return this->SendKeysInternal(password, ES_PASSWORD);
98+
}
99+
100+
int Alert::SendKeysInternal(const std::string& keys, const long text_box_style) {
101+
LOG(TRACE) << "Entering Alert::SendKeysInternal";
102+
TextBoxFindInfo text_box_find_info;
103+
text_box_find_info.textbox_handle = NULL;
104+
text_box_find_info.style_match = text_box_style;
105+
// Alert present, find the text box.
75106
// Retry up to 10 times to find the dialog.
76107
int max_wait = 10;
77-
while ((text_box_handle == NULL) && --max_wait) {
108+
while ((text_box_find_info.textbox_handle == NULL) && --max_wait) {
78109
::EnumChildWindows(this->alert_handle_,
79110
&Alert::FindTextBox,
80-
reinterpret_cast<LPARAM>(&text_box_handle));
81-
if (text_box_handle == NULL) {
111+
reinterpret_cast<LPARAM>(&text_box_find_info));
112+
if (text_box_find_info.textbox_handle == NULL) {
82113
::Sleep(50);
83114
}
84115
}
85116

86-
if (text_box_handle == NULL) {
117+
if (text_box_find_info.textbox_handle == NULL) {
87118
LOG(WARN) << "Text box not found on alert";
88119
return EELEMENTNOTDISPLAYED;
89120
} else {
90121
LOG(DEBUG) << "Sending keystrokes to alert using SendMessage";
91122
std::wstring text = StringUtilities::ToWString(keys);
92-
::SendMessage(text_box_handle,
123+
::SendMessage(text_box_find_info.textbox_handle,
93124
WM_SETTEXT,
94125
NULL,
95126
reinterpret_cast<LPARAM>(text.c_str()));
@@ -104,9 +135,11 @@ std::string Alert::GetText() {
104135
alert_text_value = this->GetStandardDialogText();
105136
} else {
106137
std::string alert_text = this->GetDirectUIDialogText();
107-
size_t first_crlf = alert_text.find("\r\n\r\n");
108-
if (first_crlf != std::string::npos && first_crlf + 4 < alert_text.size()) {
109-
alert_text_value = alert_text.substr(first_crlf + 4);
138+
if (!this->is_security_alert_) {
139+
size_t first_crlf = alert_text.find("\r\n\r\n");
140+
if (first_crlf != std::string::npos && first_crlf + 4 < alert_text.size()) {
141+
alert_text_value = alert_text.substr(first_crlf + 4);
142+
}
110143
}
111144
}
112145
return alert_text_value;
@@ -133,11 +166,13 @@ std::string Alert::GetStandardDialogText() {
133166

134167
// BIG ASSUMPTION HERE! If we found the text label, assume that
135168
// all other controls on the alert are fully drawn too.
136-
HWND text_box_handle = NULL;
169+
TextBoxFindInfo textbox_find_info;
170+
textbox_find_info.textbox_handle = NULL;
171+
textbox_find_info.style_match = 0;
137172
::EnumChildWindows(this->alert_handle_,
138173
&Alert::FindTextBox,
139-
reinterpret_cast<LPARAM>(&text_box_handle));
140-
if (text_box_handle) {
174+
reinterpret_cast<LPARAM>(&textbox_find_info));
175+
if (textbox_find_info.textbox_handle) {
141176
// There's a text box on the alert. That means the first
142177
// label found is the system-provided label. Ignore that
143178
// one and return the next one.
@@ -367,7 +402,8 @@ bool Alert::IsOKButton(HWND button_handle) {
367402
::GetClassName(button_handle, &button_window_class[0], static_cast<int>(button_window_class.size()));
368403
if (wcscmp(&button_window_class[0], L"Button") == 0) {
369404
long window_long = ::GetWindowLong(button_handle, GWL_STYLE);
370-
return (window_long & BS_DEFCOMMANDLINK) == BS_DEFCOMMANDLINK;
405+
long button_style = window_long & BS_TYPEMASK;
406+
return button_style == BS_DEFCOMMANDLINK || button_style == BS_DEFPUSHBUTTON;
371407
}
372408
return false;
373409
}
@@ -381,9 +417,10 @@ bool Alert::IsCancelButton(HWND button_handle) {
381417
::GetClassName(button_handle, &button_window_class[0], static_cast<int>(button_window_class.size()));
382418
if (wcscmp(&button_window_class[0], L"Button") == 0) {
383419
long window_long = ::GetWindowLong(button_handle, GWL_STYLE);
420+
long button_style = window_long & BS_TYPEMASK;
384421
// The BS_DEFCOMMANDLINK mask includes BS_COMMANDLINK, but we
385422
// want only to match those without the default bits set.
386-
return (window_long & BS_DEFCOMMANDLINK) == BS_COMMANDLINK;
423+
return button_style == BS_COMMANDLINK || button_style == BS_PUSHBUTTON;
387424
}
388425
return false;
389426
}
@@ -400,13 +437,22 @@ BOOL CALLBACK Alert::FindDialogButton(HWND hwnd, LPARAM arg) {
400437
}
401438

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

407444
if (wcscmp(&child_window_class[0], L"Edit") == 0) {
408-
*dialog_handle = hwnd;
409-
return FALSE;
445+
if (find_info->style_match == 0) {
446+
find_info->textbox_handle = hwnd;;;
447+
return FALSE;
448+
} else {
449+
long window_long = ::GetWindowLong(hwnd, GWL_STYLE);
450+
long edit_style = window_long & find_info->style_match;
451+
if (edit_style == find_info->style_match) {
452+
find_info->textbox_handle = hwnd;
453+
return FALSE;
454+
}
455+
}
410456
}
411457
return TRUE;
412458
}
@@ -445,4 +491,15 @@ BOOL CALLBACK Alert::FindDirectUIChild(HWND hwnd, LPARAM arg){
445491
return FALSE;
446492
}
447493

494+
BOOL CALLBACK Alert::FindTextBoxes(HWND hwnd, LPARAM arg) {
495+
std::vector<HWND>* dialog_handles = reinterpret_cast<std::vector<HWND>*>(arg);
496+
std::vector<wchar_t> child_window_class(100);
497+
::GetClassName(hwnd, &child_window_class[0], 100);
498+
499+
if (wcscmp(&child_window_class[0], L"Edit") == 0) {
500+
dialog_handles->push_back(hwnd);
501+
}
502+
return TRUE;
503+
}
504+
448505
} // namespace webdriver

cpp/iedriver/Alert.h

+13-1
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,13 @@ class Alert {
3333

3434
int Accept(void);
3535
int Dismiss(void);
36-
int SendKeys(std::string keys);
36+
int SendKeys(const std::string& keys);
3737
std::string GetText(void);
38+
int SetUserName(const std::string& username);
39+
int SetPassword(const std::string& password);
3840

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

4144
private:
4245
typedef bool (__cdecl *ISBUTTONMATCHPROC)(HWND);
@@ -59,11 +62,18 @@ class Alert {
5962
int excluded_control_id;
6063
};
6164

65+
struct TextBoxFindInfo {
66+
HWND textbox_handle;
67+
long style_match;
68+
};
69+
6270
enum BUTTON_TYPE {
6371
OK,
6472
CANCEL
6573
};
6674

75+
int SendKeysInternal(const std::string& keys, const long text_box_style);
76+
6777
DialogButtonInfo GetDialogButton(BUTTON_TYPE button_type);
6878
int ClickAlertButton(DialogButtonInfo button_info);
6979
std::string GetStandardDialogText(void);
@@ -77,10 +87,12 @@ class Alert {
7787
static BOOL CALLBACK FindTextBox(HWND hwnd, LPARAM arg);
7888
static BOOL CALLBACK FindTextLabel(HWND hwnd, LPARAM arg);
7989
static BOOL CALLBACK FindDirectUIChild(HWND hwnd, LPARAM arg);
90+
static BOOL CALLBACK FindTextBoxes(HWND hwnd, LPARAM arg);
8091

8192
HWND alert_handle_;
8293
BrowserHandle browser_;
8394
bool is_standard_alert_;
95+
bool is_security_alert_;
8496
};
8597

8698

cpp/iedriver/CommandHandlers/AcceptAlertCommandHandler.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class AcceptAlertCommandHandler : public IECommandHandler {
4646
::Sleep(100);
4747
HWND alert_handle = browser_wrapper->GetActiveDialogWindowHandle();
4848
if (alert_handle == NULL) {
49-
response->SetErrorResponse(EMODALDIALOGOPEN, "No alert is active");
49+
response->SetErrorResponse(ENOSUCHALERT, "No alert is active");
5050
} else {
5151
Alert dialog(browser_wrapper, alert_handle);
5252
status_code = dialog.Accept();

cpp/iedriver/CommandHandlers/DismissAlertCommandHandler.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ class DismissAlertCommandHandler : public AcceptAlertCommandHandler {
4545
::Sleep(100);
4646
HWND alert_handle = browser_wrapper->GetActiveDialogWindowHandle();
4747
if (alert_handle == NULL) {
48-
response->SetErrorResponse(EMODALDIALOGOPEN, "No alert is active");
48+
response->SetErrorResponse(ENOSUCHALERT, "No alert is active");
4949
} else {
5050
Alert dialog(browser_wrapper, alert_handle);
5151
status_code = dialog.Dismiss();

cpp/iedriver/CommandHandlers/GetAlertTextCommandHandler.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ class GetAlertTextCommandHandler : public IECommandHandler {
4646
::Sleep(100);
4747
HWND alert_handle = browser_wrapper->GetActiveDialogWindowHandle();
4848
if (alert_handle == NULL) {
49-
response->SetErrorResponse(EMODALDIALOGOPEN, "No alert is active");
49+
response->SetErrorResponse(ENOSUCHALERT, "No alert is active");
5050
} else {
5151
Alert dialog(browser_wrapper, alert_handle);
5252
std::string alert_text_value = dialog.GetText();

cpp/iedriver/CommandHandlers/ScreenshotCommandHandler.h

+5-2
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,9 @@ class ScreenshotCommandHandler : public IECommandHandler {
161161
LOG(DEBUG) << "Initial browser window sizes are (w, h): "
162162
<< original_width << ", " << original_height;
163163

164+
bool requires_rezise = original_width <= max_image_dimensions.right ||
165+
original_height <= max_image_dimensions.bottom;
166+
164167
// The resize message is being ignored if the window appears to be
165168
// maximized. There's likely a way to bypass that. The kludgy way
166169
// is to unmaximize the window, then move on with setting the window
@@ -189,6 +192,8 @@ class ScreenshotCommandHandler : public IECommandHandler {
189192
hook.PushData(sizeof(max_image_dimensions), &max_image_dimensions);
190193
browser->SetHeight(max_image_dimensions.bottom);
191194

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

208-
hook.Dispose();
209-
210213
// Restore the browser to the original dimensions.
211214
if (is_maximized) {
212215
::ShowWindow(ie_window_handle, SW_MAXIMIZE);

cpp/iedriver/CommandHandlers/SendKeysToAlertCommandHandler.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ class SendKeysToAlertCommandHandler : public IECommandHandler {
5454
::Sleep(100);
5555
HWND alert_handle = browser_wrapper->GetActiveDialogWindowHandle();
5656
if (alert_handle == NULL) {
57-
response->SetErrorResponse(EMODALDIALOGOPEN, "No alert is active");
57+
response->SetErrorResponse(ENOSUCHALERT, "No alert is active");
5858
} else {
5959
Alert dialog(browser_wrapper, alert_handle);
6060
status_code = dialog.SendKeys(text_parameter_iterator->second.asString());
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
#ifndef WEBDRIVER_IE_SETALERTCREDENTIALSCOMMANDHANDLER_H_
18+
#define WEBDRIVER_IE_SETALERTCREDENTIALSCOMMANDHANDLER_H_
19+
20+
#include "../Alert.h"
21+
#include "../Browser.h"
22+
#include "../IECommandHandler.h"
23+
#include "../IECommandExecutor.h"
24+
25+
namespace webdriver {
26+
27+
class SetAlertCredentialsCommandHandler : public IECommandHandler {
28+
public:
29+
SetAlertCredentialsCommandHandler(void) {
30+
}
31+
32+
virtual ~SetAlertCredentialsCommandHandler(void) {
33+
}
34+
35+
protected:
36+
void ExecuteInternal(const IECommandExecutor& executor,
37+
const ParametersMap& command_parameters,
38+
Response* response) {
39+
ParametersMap::const_iterator username_parameter_iterator = command_parameters.find("username");
40+
if (username_parameter_iterator == command_parameters.end()) {
41+
response->SetErrorResponse(400, "Missing parameter: username");
42+
return;
43+
}
44+
std::string username = username_parameter_iterator->second.asString();
45+
46+
ParametersMap::const_iterator password_parameter_iterator = command_parameters.find("password");
47+
if (password_parameter_iterator == command_parameters.end()) {
48+
response->SetErrorResponse(400, "Missing parameter: password");
49+
return;
50+
}
51+
std::string password = password_parameter_iterator->second.asString();
52+
53+
BrowserHandle browser_wrapper;
54+
int status_code = executor.GetCurrentBrowser(&browser_wrapper);
55+
if (status_code != WD_SUCCESS) {
56+
response->SetErrorResponse(status_code, "Unable to get current browser");
57+
return;
58+
}
59+
// This sleep is required to give IE time to draw the dialog.
60+
::Sleep(100);
61+
HWND alert_handle = browser_wrapper->GetActiveDialogWindowHandle();
62+
if (alert_handle == NULL) {
63+
response->SetErrorResponse(ENOSUCHALERT, "No alert is active");
64+
} else {
65+
Alert dialog(browser_wrapper, alert_handle);
66+
status_code = dialog.SetUserName(username);
67+
if (status_code != WD_SUCCESS) {
68+
response->SetErrorResponse(status_code,
69+
"Could not set user name");
70+
return;
71+
}
72+
status_code = dialog.SetPassword(password);
73+
if (status_code != WD_SUCCESS) {
74+
response->SetErrorResponse(status_code,
75+
"Could not set password");
76+
return;
77+
}
78+
response->SetSuccessResponse(Json::Value::null);
79+
}
80+
}
81+
82+
};
83+
84+
} // namespace webdriver
85+
86+
#endif // WEBDRIVER_IE_SETALERTCREDENTIALSCOMMANDHANDLER_H_

0 commit comments

Comments
 (0)