blob: ae442203d5885462d0c2154499e10af18d810c20 [file] [log] [blame]
// 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.
#include "HtmlDialog.h"
#include "errorcodes.h"
#include "logging.h"
#include "BrowserFactory.h"
#include "StringUtilities.h"
#include "WebDriverConstants.h"
#define HIDDEN_PARENT_WINDOW_CLASS "Internet Explorer_Hidden"
namespace webdriver {
HtmlDialog::HtmlDialog(IHTMLWindow2* window, HWND hwnd, HWND session_handle) : DocumentHost(hwnd, session_handle) {
LOG(TRACE) << "Entering HtmlDialog::HtmlDialog";
this->is_navigating_ = false;
this->window_ = window;
this->AttachEvents();
}
HtmlDialog::~HtmlDialog(void) {
}
void HtmlDialog::AttachEvents() {
CComPtr<IUnknown> unknown;
this->window_->QueryInterface<IUnknown>(&unknown);
HRESULT hr = this->DispEventAdvise(unknown);
}
void HtmlDialog::DetachEvents() {
LOG(TRACE) << "Entering HtmlDialog::DetachEvents";
CComPtr<IUnknown> unknown;
this->window_->QueryInterface<IUnknown>(&unknown);
HRESULT hr = this->DispEventUnadvise(unknown);
}
void __stdcall HtmlDialog::OnBeforeUnload(IHTMLEventObj *pEvtObj) {
LOG(TRACE) << "Entering HtmlDialog::OnBeforeUnload";
this->is_navigating_ = true;
}
void __stdcall HtmlDialog::OnLoad(IHTMLEventObj *pEvtObj) {
LOG(TRACE) << "Entering HtmlDialog::OnLoad";
this->is_navigating_ = false;
}
void HtmlDialog::GetDocument(IHTMLDocument2** doc) {
this->GetDocument(false, doc);
}
void HtmlDialog::GetDocument(const bool force_top_level_document,
IHTMLDocument2** doc) {
LOG(TRACE) << "Entering HtmlDialog::GetDocument";
HRESULT hr = S_OK;
if (this->focused_frame_window() == NULL || force_top_level_document) {
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() {
LOG(TRACE) << "Entering HtmlDialog::Close";
if (!this->is_closing()) {
this->is_navigating_ = false;
this->set_is_closing(true);
// 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();
}
}
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;
}
bool HtmlDialog::SetFullScreen(bool is_full_screen) {
return false;
}
bool HtmlDialog::IsFullScreen() {
return false;
}
bool HtmlDialog::IsBusy() {
LOG(TRACE) << "Entering HtmlDialog::IsBusy";
return false;
}
bool HtmlDialog::Wait(const std::string& page_load_strategy) {
LOG(TRACE) << "Entering HtmlDialog::Wait";
// If the window is no longer valid, the window is closing,
// and the wait is completed.
if (!this->is_closing() && !this->IsValidWindow()) {
return true;
}
// 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;
}
// Otherwise, we wait a short amount and see if navigation is complete
// (signaled by the OnLoad event firing).
::Sleep(250);
return !this->is_navigating_;
}
HWND HtmlDialog::GetContentWindowHandle() {
LOG(TRACE) << "Entering HtmlDialog::GetContentWindowHandle";
return this->window_handle();
}
HWND HtmlDialog::GetBrowserWindowHandle() {
LOG(TRACE) << "Entering HtmlDialog::GetBrowserWindowHandle";
return this->window_handle();
}
std::string HtmlDialog::GetWindowName() {
LOG(TRACE) << "Entering HtmlDialog::GetWindowName";
return "";
}
std::string HtmlDialog::GetBrowserUrl() {
LOG(TRACE) << "Entering HtmlDialog::GetBrowserUrl";
return "";
}
std::string HtmlDialog::GetTitle() {
LOG(TRACE) << "Entering HtmlDialog::GetTitle";
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 "";
}
std::wstring converted_title = title;
std::string title_string = StringUtilities::ToString(converted_title);
return title_string;
}
HWND HtmlDialog::GetTopLevelWindowHandle(void) {
LOG(TRACE) << "Entering HtmlDialog::GetTopLevelWindowHandle";
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;
}
HWND HtmlDialog::GetActiveDialogWindowHandle() {
LOG(TRACE) << "Entering HtmlDialog::GetActiveDialogWindowHandle";
DialogWindowInfo info;
info.hwndOwner = this->GetTopLevelWindowHandle();
info.hwndDialog = NULL;
if (info.hwndOwner != NULL) {
::EnumWindows(&HtmlDialog::FindChildDialogWindow, reinterpret_cast<LPARAM>(&info));
}
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));
}
}
}
}
return info.hwndDialog;
}
long HtmlDialog::GetWidth() {
LOG(TRACE) << "Entering HtmlDialog::GetWidth";
// TODO: calculate width
return 0L;
}
long HtmlDialog::GetHeight() {
LOG(TRACE) << "Entering HtmlDialog::GetHeight";
// TODO: calculate height
return 0L;
}
void HtmlDialog::SetWidth(long width) {
LOG(TRACE) << "Entering HtmlDialog::SetWidth";
}
void HtmlDialog::SetHeight(long height) {
LOG(TRACE) << "Entering HtmlDialog::SetHeight";
}
int HtmlDialog::NavigateToUrl(const std::string& url,
std::string* error_message) {
LOG(TRACE) << "Entering HtmlDialog::NavigateToUrl";
// Cannot force navigation on windows opened with showModalDialog();
return ENOTIMPLEMENTED;
}
int HtmlDialog::NavigateBack() {
LOG(TRACE) << "Entering HtmlDialog::NavigateBack";
// Cannot force navigation on windows opened with showModalDialog();
return ENOTIMPLEMENTED;
}
int HtmlDialog::NavigateForward() {
LOG(TRACE) << "Entering HtmlDialog::NavigateForward";
// Cannot force navigation on windows opened with showModalDialog();
return ENOTIMPLEMENTED;
}
int HtmlDialog::Refresh() {
LOG(TRACE) << "Entering HtmlDialog::Refresh";
// 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;
}
std::vector<char> window_class_name(34);
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