blob: 623fa3d30d6eef6fbb34c8edafe8023a1c6b9230 [file] [log] [blame]
// Copyright 2011 Software Freedom Conservancy
// 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.
#include "HtmlDialog.h"
#include "logging.h"
namespace webdriver {
HtmlDialog::HtmlDialog(IHTMLWindow2* window, HWND hwnd, HWND session_handle) : DocumentHost(hwnd, session_handle) {
this->is_navigating_ = false;
this->window_ = window;
this->AttachEvents();
}
HtmlDialog::~HtmlDialog(void) {
}
void HtmlDialog::AttachEvents() {
CComQIPtr<IDispatch> dispatch(this->window_);
CComPtr<IUnknown> unknown(dispatch);
HRESULT hr = this->DispEventAdvise(unknown);
}
void HtmlDialog::DetachEvents() {
CComQIPtr<IDispatch> dispatch(this->window_);
CComPtr<IUnknown> unknown(dispatch);
HRESULT hr = this->DispEventUnadvise(unknown);
}
void __stdcall HtmlDialog::OnBeforeUnload(IHTMLEventObj *pEvtObj) {
this->is_navigating_ = true;
}
void __stdcall HtmlDialog::OnLoad(IHTMLEventObj *pEvtObj) {
this->is_navigating_ = false;
}
void HtmlDialog::GetDocument(IHTMLDocument2** doc) {
HRESULT hr = S_OK;
if (this->focused_frame_window() == NULL) {
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() {
if (!this->is_closing()) {
this->is_navigating_ = false;
// 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::IsBusy() {
return false;
}
bool HtmlDialog::Wait() {
// If the window handle is no longer valid, the window is closing,
// the wait is completed, and we must post the quit message.
if (!this->is_closing() && !::IsWindow(this->GetTopLevelWindowHandle())) {
this->is_navigating_ = false;
this->DetachEvents();
this->PostQuitMessage();
return true;
}
// If we're not navigating to a new location, we should check to see if
// a new modal dialog or alert has been opened. If one has, the wait is complete,
// so we must set the flag indicating to the message loop not to call wait
// anymore.
if (!this->is_navigating_) {
HWND child_dialog_handle = this->GetActiveDialogWindowHandle();
if (child_dialog_handle != NULL) {
// Check to see if the dialog opened is another HTML dialog. If so,
// notify the IECommandExecutor that a new window exists.
vector<char> window_class_name(34);
if (::GetClassNameA(child_dialog_handle, &window_class_name[0], 34)) {
if (strcmp(HTML_DIALOG_WINDOW_CLASS, &window_class_name[0]) == 0) {
HWND content_window_handle = this->FindContentWindowHandle(child_dialog_handle);
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));
}
}
}
this->set_wait_required(false);
return true;
}
}
// Otherwise, we wait until navigation is complete.
::Sleep(250);
return !this->is_navigating_;
}
HWND HtmlDialog::GetWindowHandle() {
return this->window_handle();
}
std::string HtmlDialog::GetWindowName() {
return "";
}
std::string 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::string title_string = CW2A(title, CP_UTF8);
return title_string;
}
HWND HtmlDialog::GetTopLevelWindowHandle(void) {
return ::GetParent(this->window_handle());
}
HWND HtmlDialog::GetActiveDialogWindowHandle() {
DialogWindowInfo info;
info.hwndOwner = this->GetTopLevelWindowHandle();
info.hwndDialog = NULL;
::EnumWindows(&HtmlDialog::FindChildDialogWindow, reinterpret_cast<LPARAM>(&info));
return info.hwndDialog;
}
long HtmlDialog::GetWidth() {
// TODO: calculate width
return 0L;
}
long HtmlDialog::GetHeight() {
// TODO: calculate height
return 0L;
}
void HtmlDialog::SetWidth(long width) {
}
void HtmlDialog::SetHeight(long height) {
}
int HtmlDialog::NavigateToUrl(const std::string& url) {
// Cannot force navigation on windows opened with showModalDialog();
return ENOTIMPLEMENTED;
}
int HtmlDialog::NavigateBack() {
// Cannot force navigation on windows opened with showModalDialog();
return ENOTIMPLEMENTED;
}
int HtmlDialog::NavigateForward() {
// Cannot force navigation on windows opened with showModalDialog();
return ENOTIMPLEMENTED;
}
int 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;
}
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