2015-04-13 12:09:26 -04:00
|
|
|
// 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");
|
2013-01-11 22:18:32 +01:00
|
|
|
// 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.
|
|
|
|
|
|
|
|
|
|
#include "HtmlDialog.h"
|
2017-02-14 09:48:56 -08:00
|
|
|
|
|
|
|
|
#include "errorcodes.h"
|
2013-01-11 22:18:32 +01:00
|
|
|
#include "logging.h"
|
|
|
|
|
|
2017-02-14 09:48:56 -08:00
|
|
|
#include "BrowserFactory.h"
|
|
|
|
|
#include "StringUtilities.h"
|
2019-01-18 15:54:41 -08:00
|
|
|
#include "WebDriverConstants.h"
|
2017-02-14 09:48:56 -08:00
|
|
|
|
2018-10-25 12:31:18 -07:00
|
|
|
#define HIDDEN_PARENT_WINDOW_CLASS "Internet Explorer_Hidden"
|
|
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
namespace webdriver {
|
|
|
|
|
|
|
|
|
|
HtmlDialog::HtmlDialog(IHTMLWindow2* window, HWND hwnd, HWND session_handle) : DocumentHost(hwnd, session_handle) {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::HtmlDialog";
|
2013-01-11 22:18:32 +01:00
|
|
|
this->is_navigating_ = false;
|
|
|
|
|
this->window_ = window;
|
|
|
|
|
this->AttachEvents();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HtmlDialog::~HtmlDialog(void) {
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HtmlDialog::AttachEvents() {
|
2013-03-07 17:06:31 -05:00
|
|
|
CComPtr<IUnknown> unknown;
|
2013-03-25 13:34:23 -04:00
|
|
|
this->window_->QueryInterface<IUnknown>(&unknown);
|
2013-01-11 22:18:32 +01:00
|
|
|
HRESULT hr = this->DispEventAdvise(unknown);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HtmlDialog::DetachEvents() {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::DetachEvents";
|
2013-03-07 17:06:31 -05:00
|
|
|
CComPtr<IUnknown> unknown;
|
2013-03-25 13:34:23 -04:00
|
|
|
this->window_->QueryInterface<IUnknown>(&unknown);
|
2013-01-11 22:18:32 +01:00
|
|
|
HRESULT hr = this->DispEventUnadvise(unknown);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void __stdcall HtmlDialog::OnBeforeUnload(IHTMLEventObj *pEvtObj) {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::OnBeforeUnload";
|
2013-01-11 22:18:32 +01:00
|
|
|
this->is_navigating_ = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void __stdcall HtmlDialog::OnLoad(IHTMLEventObj *pEvtObj) {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::OnLoad";
|
2013-01-11 22:18:32 +01:00
|
|
|
this->is_navigating_ = false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HtmlDialog::GetDocument(IHTMLDocument2** doc) {
|
2015-07-13 15:19:16 -04:00
|
|
|
this->GetDocument(false, doc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HtmlDialog::GetDocument(const bool force_top_level_document,
|
|
|
|
|
IHTMLDocument2** doc) {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::GetDocument";
|
2013-01-11 22:18:32 +01:00
|
|
|
HRESULT hr = S_OK;
|
2015-07-13 15:19:16 -04:00
|
|
|
if (this->focused_frame_window() == NULL || force_top_level_document) {
|
2013-01-11 22:18:32 +01:00
|
|
|
hr = this->window_->get_document(doc);
|
|
|
|
|
} else {
|
|
|
|
|
hr = this->focused_frame_window()->get_document(doc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(DEBUG, hr) << "Unable to get document";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HtmlDialog::Close() {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::Close";
|
2013-01-11 22:18:32 +01:00
|
|
|
if (!this->is_closing()) {
|
|
|
|
|
this->is_navigating_ = false;
|
2018-05-11 06:16:04 -07:00
|
|
|
this->set_is_closing(true);
|
2013-01-11 22:18:32 +01:00
|
|
|
// Closing the browser, so having focus on a frame doesn't
|
|
|
|
|
// make any sense.
|
|
|
|
|
this->SetFocusedFrameByElement(NULL);
|
|
|
|
|
this->DetachEvents();
|
|
|
|
|
this->window_->close();
|
|
|
|
|
|
|
|
|
|
// Must manually release the CComPtr<IHTMLWindow> so that the
|
|
|
|
|
// destructor will not try to release a no-longer-valid object.
|
|
|
|
|
this->window_.Release();
|
|
|
|
|
this->window_ = NULL;
|
|
|
|
|
|
|
|
|
|
this->PostQuitMessage();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-10 12:44:27 -04:00
|
|
|
bool HtmlDialog::IsValidWindow() {
|
|
|
|
|
LOG(TRACE) << "Entering HtmlDialog::IsValidWindow";
|
|
|
|
|
// If the window handle is no longer valid, the window is closing,
|
|
|
|
|
// and we must post the quit message.
|
|
|
|
|
if (!::IsWindow(this->GetTopLevelWindowHandle())) {
|
|
|
|
|
this->is_navigating_ = false;
|
|
|
|
|
this->DetachEvents();
|
|
|
|
|
this->PostQuitMessage();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-31 09:41:44 -07:00
|
|
|
bool HtmlDialog::SetFullScreen(bool is_full_screen) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool HtmlDialog::IsFullScreen() {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
bool HtmlDialog::IsBusy() {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::IsBusy";
|
2013-01-11 22:18:32 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 14:14:11 -04:00
|
|
|
bool HtmlDialog::Wait(const std::string& page_load_strategy) {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::Wait";
|
2013-10-10 12:44:27 -04:00
|
|
|
// If the window is no longer valid, the window is closing,
|
|
|
|
|
// and the wait is completed.
|
|
|
|
|
if (!this->is_closing() && !this->IsValidWindow()) {
|
2013-01-11 22:18:32 +01:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-29 16:54:31 -05:00
|
|
|
// Check to see if a new dialog has opened up on top of this one.
|
|
|
|
|
// If so, the wait is completed, no matter whether the OnUnload
|
|
|
|
|
// event has fired signaling navigation started, nor whether the
|
|
|
|
|
// OnLoad event has fired signaling navigation complete. Set the
|
|
|
|
|
// flag so that the Wait method is no longer called.
|
|
|
|
|
HWND child_dialog_handle = this->GetActiveDialogWindowHandle();
|
|
|
|
|
if (child_dialog_handle != NULL) {
|
|
|
|
|
this->is_navigating_ = false;
|
|
|
|
|
this->set_wait_required(false);
|
|
|
|
|
return true;
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
2014-01-29 16:54:31 -05:00
|
|
|
// Otherwise, we wait a short amount and see if navigation is complete
|
|
|
|
|
// (signaled by the OnLoad event firing).
|
2013-01-11 22:18:32 +01:00
|
|
|
::Sleep(250);
|
|
|
|
|
return !this->is_navigating_;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-15 11:34:03 -05:00
|
|
|
HWND HtmlDialog::GetContentWindowHandle() {
|
|
|
|
|
LOG(TRACE) << "Entering HtmlDialog::GetContentWindowHandle";
|
2013-01-11 22:18:32 +01:00
|
|
|
return this->window_handle();
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-15 12:06:56 -05:00
|
|
|
HWND HtmlDialog::GetBrowserWindowHandle() {
|
|
|
|
|
LOG(TRACE) << "Entering HtmlDialog::GetBrowserWindowHandle";
|
|
|
|
|
return this->window_handle();
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
std::string HtmlDialog::GetWindowName() {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::GetWindowName";
|
2013-01-11 22:18:32 +01:00
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-19 13:03:28 -04:00
|
|
|
std::string HtmlDialog::GetBrowserUrl() {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::GetBrowserUrl";
|
2013-03-19 13:03:28 -04:00
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
std::string HtmlDialog::GetTitle() {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::GetTitle";
|
2013-01-11 22:18:32 +01:00
|
|
|
CComPtr<IHTMLDocument2> doc;
|
|
|
|
|
this->GetDocument(&doc);
|
|
|
|
|
CComBSTR title;
|
|
|
|
|
HRESULT hr = doc->get_title(&title);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Unable to get document title";
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-21 13:58:47 -04:00
|
|
|
std::wstring converted_title = title;
|
|
|
|
|
std::string title_string = StringUtilities::ToString(converted_title);
|
2013-01-11 22:18:32 +01:00
|
|
|
return title_string;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HWND HtmlDialog::GetTopLevelWindowHandle(void) {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::GetTopLevelWindowHandle";
|
2018-10-25 12:31:18 -07:00
|
|
|
HWND parent_handle = ::GetParent(this->window_handle());
|
|
|
|
|
|
|
|
|
|
// "Internet Explorer_Hidden\0" == 25
|
|
|
|
|
std::vector<char> parent_class_buffer(25);
|
|
|
|
|
if (::GetClassNameA(parent_handle, &parent_class_buffer[0], 25)) {
|
|
|
|
|
if (strcmp(HIDDEN_PARENT_WINDOW_CLASS, &parent_class_buffer[0]) == 0) {
|
|
|
|
|
// Some versions of Internet Explorer re-parent a closing showModalDialog
|
|
|
|
|
// window to a hidden parent window. If that is what we see happening
|
|
|
|
|
// here, that will be equivalent to the parent window no longer being
|
|
|
|
|
// valid, and we can return an invalid handle, indicating the window is
|
|
|
|
|
// "closed."
|
|
|
|
|
return NULL;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return parent_handle;
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HWND HtmlDialog::GetActiveDialogWindowHandle() {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::GetActiveDialogWindowHandle";
|
2013-01-11 22:18:32 +01:00
|
|
|
DialogWindowInfo info;
|
|
|
|
|
info.hwndOwner = this->GetTopLevelWindowHandle();
|
|
|
|
|
info.hwndDialog = NULL;
|
2013-02-05 13:25:12 -05:00
|
|
|
if (info.hwndOwner != NULL) {
|
|
|
|
|
::EnumWindows(&HtmlDialog::FindChildDialogWindow, reinterpret_cast<LPARAM>(&info));
|
|
|
|
|
}
|
2013-11-04 15:34:15 -05:00
|
|
|
if (info.hwndDialog != NULL) {
|
|
|
|
|
std::vector<char> window_class_name(34);
|
|
|
|
|
if (::GetClassNameA(info.hwndDialog, &window_class_name[0], 34)) {
|
|
|
|
|
if (strcmp(HTML_DIALOG_WINDOW_CLASS, &window_class_name[0]) == 0) {
|
|
|
|
|
HWND content_window_handle = this->FindContentWindowHandle(info.hwndDialog);
|
|
|
|
|
if (content_window_handle != NULL) {
|
|
|
|
|
// Must have a sleep here to give IE a chance to draw the window.
|
|
|
|
|
::Sleep(250);
|
|
|
|
|
::PostMessage(this->executor_handle(),
|
|
|
|
|
WD_NEW_HTML_DIALOG,
|
|
|
|
|
NULL,
|
|
|
|
|
reinterpret_cast<LPARAM>(content_window_handle));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
return info.hwndDialog;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long HtmlDialog::GetWidth() {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::GetWidth";
|
2013-01-11 22:18:32 +01:00
|
|
|
// TODO: calculate width
|
|
|
|
|
return 0L;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long HtmlDialog::GetHeight() {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::GetHeight";
|
2013-01-11 22:18:32 +01:00
|
|
|
// TODO: calculate height
|
|
|
|
|
return 0L;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HtmlDialog::SetWidth(long width) {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::SetWidth";
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void HtmlDialog::SetHeight(long height) {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::SetHeight";
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
2019-07-18 15:20:45 -07:00
|
|
|
int HtmlDialog::NavigateToUrl(const std::string& url,
|
|
|
|
|
std::string* error_message) {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::NavigateToUrl";
|
2013-01-11 22:18:32 +01:00
|
|
|
// Cannot force navigation on windows opened with showModalDialog();
|
|
|
|
|
return ENOTIMPLEMENTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int HtmlDialog::NavigateBack() {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::NavigateBack";
|
2013-01-11 22:18:32 +01:00
|
|
|
// Cannot force navigation on windows opened with showModalDialog();
|
|
|
|
|
return ENOTIMPLEMENTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int HtmlDialog::NavigateForward() {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::NavigateForward";
|
2013-01-11 22:18:32 +01:00
|
|
|
// Cannot force navigation on windows opened with showModalDialog();
|
|
|
|
|
return ENOTIMPLEMENTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int HtmlDialog::Refresh() {
|
2013-07-10 15:52:16 -04:00
|
|
|
LOG(TRACE) << "Entering HtmlDialog::Refresh";
|
2013-01-11 22:18:32 +01:00
|
|
|
// Cannot force navigation on windows opened with showModalDialog();
|
|
|
|
|
return ENOTIMPLEMENTED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
BOOL CALLBACK HtmlDialog::FindChildDialogWindow(HWND hwnd, LPARAM arg) {
|
|
|
|
|
DialogWindowInfo* window_info = reinterpret_cast<DialogWindowInfo*>(arg);
|
|
|
|
|
if (::GetWindow(hwnd, GW_OWNER) != window_info->hwndOwner) {
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
2013-03-25 13:34:23 -04:00
|
|
|
std::vector<char> window_class_name(34);
|
2013-01-11 22:18:32 +01:00
|
|
|
if (::GetClassNameA(hwnd, &window_class_name[0], 34) == 0) {
|
|
|
|
|
// No match found. Skip
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
if (strcmp(ALERT_WINDOW_CLASS, &window_class_name[0]) != 0 &&
|
|
|
|
|
strcmp(HTML_DIALOG_WINDOW_CLASS, &window_class_name[0]) != 0) {
|
|
|
|
|
return TRUE;
|
|
|
|
|
} else {
|
|
|
|
|
// If the window style has the WS_DISABLED bit set or the
|
|
|
|
|
// WS_VISIBLE bit unset, it can't be handled via the UI,
|
|
|
|
|
// and must not be a visible dialog.
|
|
|
|
|
if ((::GetWindowLong(hwnd, GWL_STYLE) & WS_DISABLED) != 0 ||
|
|
|
|
|
(::GetWindowLong(hwnd, GWL_STYLE) & WS_VISIBLE) == 0) {
|
|
|
|
|
return TRUE;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
window_info->hwndDialog = hwnd;
|
|
|
|
|
return FALSE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} // namespace webdriver
|