// 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 |