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 "Browser.h"
|
2017-02-14 09:48:56 -08:00
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
#include <comutil.h>
|
2014-08-19 21:11:00 +00:00
|
|
|
#include <ShlGuid.h>
|
2017-02-14 09:48:56 -08:00
|
|
|
|
|
|
|
|
#include "errorcodes.h"
|
|
|
|
|
#include "logging.h"
|
|
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
#include "Alert.h"
|
2014-08-19 21:11:00 +00:00
|
|
|
#include "BrowserFactory.h"
|
2018-05-26 08:19:04 -07:00
|
|
|
#include "CustomTypes.h"
|
2017-02-14 09:48:56 -08:00
|
|
|
#include "messages.h"
|
Improving IE driver use with invalid Protected Mode settings
Now, when the user does not set the Protected Mode settings of the
browser and sends the capability to bypass the checks for those
settings, the driver will attempt to predict when a Protected Mode
boundary will be crossed, and set in motion a process to reattach
itself to the newly created browser. This process is far from perfect.
It is subject to really challenging race conditions that are truly
impossible to eliminate entirely, because of the architecture of the
browser itself. Nevertheless, even in its flawed state, this is still
a better outcome than it was previously for users.
Please note that the advice and support policy of the IE driver will
continue to be that the user must set the Protected Mode settings of
the browser properly before using the driver. Any "issues" that arise
by not having the settings set, but that disappear when the settings
are corrected, are not considered by the project to be valid issues.
This will include, but not be limited to, issues like abandoned
browser instances not being closed, use of multiple instances of the
driver where the wrong browser window is connected to and automated,
and issues where the driver appears to hang upon navigation to a new
page. If the problem disappears when the browser is properly
configured, any issue reports will be immediately closed with a note
to properly configure the browser and remove the capability.
The following situations should be at least partially mitigated by the
change:
* Navigation to a new page
* Clicking on a link (specifically an <a> tag) that will lead to a
navigation to a new page
* Clicking a link that opens a new window
Other cases, like navigating backward and forward through the browser
history, clicking an element that submits a form, and so on, may not
be handled. In those cases, issue reports will be summarily closed,
unless a specific pull request fixing the issue is also provided.
Additionally, use of things like proxies to capture traffic between the
browser and web server may miss some traffic because of the race
conditions inherent in the mechanism used to reattach to a newly
created browser. Again, these race conditions are unavoidable, and
issue reports that are based on them will be immediately closed with a
note indicating that the browser must have its settings properly set.
These strict guidelines are not intended to be harsh, and are not put
in place with the intent to avoid investigating and fixing issues;
rather, they must be enforced because the underlying architecture of
the browser makes them unavoidable.
2019-02-17 09:29:01 -08:00
|
|
|
#include "HookProcessor.h"
|
2019-01-29 17:27:04 -08:00
|
|
|
#include "Script.h"
|
2017-02-14 09:48:56 -08:00
|
|
|
#include "StringUtilities.h"
|
2019-01-18 15:54:41 -08:00
|
|
|
#include "WebDriverConstants.h"
|
Improving IE driver use with invalid Protected Mode settings
Now, when the user does not set the Protected Mode settings of the
browser and sends the capability to bypass the checks for those
settings, the driver will attempt to predict when a Protected Mode
boundary will be crossed, and set in motion a process to reattach
itself to the newly created browser. This process is far from perfect.
It is subject to really challenging race conditions that are truly
impossible to eliminate entirely, because of the architecture of the
browser itself. Nevertheless, even in its flawed state, this is still
a better outcome than it was previously for users.
Please note that the advice and support policy of the IE driver will
continue to be that the user must set the Protected Mode settings of
the browser properly before using the driver. Any "issues" that arise
by not having the settings set, but that disappear when the settings
are corrected, are not considered by the project to be valid issues.
This will include, but not be limited to, issues like abandoned
browser instances not being closed, use of multiple instances of the
driver where the wrong browser window is connected to and automated,
and issues where the driver appears to hang upon navigation to a new
page. If the problem disappears when the browser is properly
configured, any issue reports will be immediately closed with a note
to properly configure the browser and remove the capability.
The following situations should be at least partially mitigated by the
change:
* Navigation to a new page
* Clicking on a link (specifically an <a> tag) that will lead to a
navigation to a new page
* Clicking a link that opens a new window
Other cases, like navigating backward and forward through the browser
history, clicking an element that submits a form, and so on, may not
be handled. In those cases, issue reports will be summarily closed,
unless a specific pull request fixing the issue is also provided.
Additionally, use of things like proxies to capture traffic between the
browser and web server may miss some traffic because of the race
conditions inherent in the mechanism used to reattach to a newly
created browser. Again, these race conditions are unavoidable, and
issue reports that are based on them will be immediately closed with a
note indicating that the browser must have its settings properly set.
These strict guidelines are not intended to be harsh, and are not put
in place with the intent to avoid investigating and fixing issues;
rather, they must be enforced because the underlying architecture of
the browser makes them unavoidable.
2019-02-17 09:29:01 -08:00
|
|
|
#include "WindowUtilities.h"
|
2013-01-11 22:18:32 +01:00
|
|
|
|
|
|
|
|
namespace webdriver {
|
|
|
|
|
|
2019-06-10 17:45:31 -07:00
|
|
|
Browser::Browser(IWebBrowser2* browser, HWND hwnd, HWND session_handle, bool is_edge_chromium) : DocumentHost(hwnd, session_handle) {
|
2013-01-11 22:18:32 +01:00
|
|
|
LOG(TRACE) << "Entering Browser::Browser";
|
2018-03-14 12:09:55 -07:00
|
|
|
this->is_explicit_close_requested_ = false;
|
2013-01-11 22:18:32 +01:00
|
|
|
this->is_navigation_started_ = false;
|
|
|
|
|
this->browser_ = browser;
|
|
|
|
|
this->AttachEvents();
|
2021-11-03 18:50:35 -04:00
|
|
|
this->set_is_edge_chromium(is_edge_chromium);
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Browser::~Browser(void) {
|
|
|
|
|
this->DetachEvents();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void __stdcall Browser::BeforeNavigate2(IDispatch* pObject,
|
|
|
|
|
VARIANT* pvarUrl,
|
|
|
|
|
VARIANT* pvarFlags,
|
|
|
|
|
VARIANT* pvarTargetFrame,
|
|
|
|
|
VARIANT* pvarData,
|
|
|
|
|
VARIANT* pvarHeaders,
|
|
|
|
|
VARIANT_BOOL* pbCancel) {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::BeforeNavigate2";
|
Improving IE driver use with invalid Protected Mode settings
Now, when the user does not set the Protected Mode settings of the
browser and sends the capability to bypass the checks for those
settings, the driver will attempt to predict when a Protected Mode
boundary will be crossed, and set in motion a process to reattach
itself to the newly created browser. This process is far from perfect.
It is subject to really challenging race conditions that are truly
impossible to eliminate entirely, because of the architecture of the
browser itself. Nevertheless, even in its flawed state, this is still
a better outcome than it was previously for users.
Please note that the advice and support policy of the IE driver will
continue to be that the user must set the Protected Mode settings of
the browser properly before using the driver. Any "issues" that arise
by not having the settings set, but that disappear when the settings
are corrected, are not considered by the project to be valid issues.
This will include, but not be limited to, issues like abandoned
browser instances not being closed, use of multiple instances of the
driver where the wrong browser window is connected to and automated,
and issues where the driver appears to hang upon navigation to a new
page. If the problem disappears when the browser is properly
configured, any issue reports will be immediately closed with a note
to properly configure the browser and remove the capability.
The following situations should be at least partially mitigated by the
change:
* Navigation to a new page
* Clicking on a link (specifically an <a> tag) that will lead to a
navigation to a new page
* Clicking a link that opens a new window
Other cases, like navigating backward and forward through the browser
history, clicking an element that submits a form, and so on, may not
be handled. In those cases, issue reports will be summarily closed,
unless a specific pull request fixing the issue is also provided.
Additionally, use of things like proxies to capture traffic between the
browser and web server may miss some traffic because of the race
conditions inherent in the mechanism used to reattach to a newly
created browser. Again, these race conditions are unavoidable, and
issue reports that are based on them will be immediately closed with a
note indicating that the browser must have its settings properly set.
These strict guidelines are not intended to be harsh, and are not put
in place with the intent to avoid investigating and fixing issues;
rather, they must be enforced because the underlying architecture of
the browser makes them unavoidable.
2019-02-17 09:29:01 -08:00
|
|
|
std::wstring url(pvarUrl->bstrVal);
|
|
|
|
|
|
|
|
|
|
LOG(DEBUG) << "BeforeNavigate2: Url: " << LOGWSTRING(url) << ", TargetFrame: " << pvarTargetFrame->bstrVal;
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void __stdcall Browser::OnQuit() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::OnQuit";
|
2018-03-14 12:09:55 -07:00
|
|
|
if (!this->is_explicit_close_requested_) {
|
Improving IE driver use with invalid Protected Mode settings
Now, when the user does not set the Protected Mode settings of the
browser and sends the capability to bypass the checks for those
settings, the driver will attempt to predict when a Protected Mode
boundary will be crossed, and set in motion a process to reattach
itself to the newly created browser. This process is far from perfect.
It is subject to really challenging race conditions that are truly
impossible to eliminate entirely, because of the architecture of the
browser itself. Nevertheless, even in its flawed state, this is still
a better outcome than it was previously for users.
Please note that the advice and support policy of the IE driver will
continue to be that the user must set the Protected Mode settings of
the browser properly before using the driver. Any "issues" that arise
by not having the settings set, but that disappear when the settings
are corrected, are not considered by the project to be valid issues.
This will include, but not be limited to, issues like abandoned
browser instances not being closed, use of multiple instances of the
driver where the wrong browser window is connected to and automated,
and issues where the driver appears to hang upon navigation to a new
page. If the problem disappears when the browser is properly
configured, any issue reports will be immediately closed with a note
to properly configure the browser and remove the capability.
The following situations should be at least partially mitigated by the
change:
* Navigation to a new page
* Clicking on a link (specifically an <a> tag) that will lead to a
navigation to a new page
* Clicking a link that opens a new window
Other cases, like navigating backward and forward through the browser
history, clicking an element that submits a form, and so on, may not
be handled. In those cases, issue reports will be summarily closed,
unless a specific pull request fixing the issue is also provided.
Additionally, use of things like proxies to capture traffic between the
browser and web server may miss some traffic because of the race
conditions inherent in the mechanism used to reattach to a newly
created browser. Again, these race conditions are unavoidable, and
issue reports that are based on them will be immediately closed with a
note indicating that the browser must have its settings properly set.
These strict guidelines are not intended to be harsh, and are not put
in place with the intent to avoid investigating and fixing issues;
rather, they must be enforced because the underlying architecture of
the browser makes them unavoidable.
2019-02-17 09:29:01 -08:00
|
|
|
if (this->is_awaiting_new_process()) {
|
|
|
|
|
LOG(WARN) << "A new browser process was requested. This means a Protected "
|
|
|
|
|
<< "Mode boundary has been crossed, and that future commands to "
|
|
|
|
|
<< "the current browser instance will fail. The driver will "
|
|
|
|
|
<< "attempt to reconnect to the newly created browser object, "
|
|
|
|
|
<< "but there is no guarantee it will work.";
|
|
|
|
|
DWORD process_id;
|
|
|
|
|
HWND window_handle = this->GetBrowserWindowHandle();
|
|
|
|
|
::GetWindowThreadProcessId(window_handle, &process_id);
|
|
|
|
|
|
|
|
|
|
BrowserReattachInfo* info = new BrowserReattachInfo;
|
|
|
|
|
info->browser_id = this->browser_id();
|
|
|
|
|
info->current_process_id = process_id;
|
|
|
|
|
info->known_process_ids = this->known_process_ids_;
|
|
|
|
|
|
|
|
|
|
this->DetachEvents();
|
|
|
|
|
this->browser_ = NULL;
|
|
|
|
|
::PostMessage(this->executor_handle(),
|
|
|
|
|
WD_BROWSER_REATTACH,
|
|
|
|
|
NULL,
|
|
|
|
|
reinterpret_cast<LPARAM>(info));
|
|
|
|
|
return;
|
|
|
|
|
} else {
|
2019-02-22 08:13:23 -08:00
|
|
|
LOG(WARN) << "This instance of Internet Explorer (" << this->browser_id()
|
|
|
|
|
<< ") is exiting without an explicit request to close it. "
|
|
|
|
|
<< "Unless you clicked a link that specifically attempts to "
|
|
|
|
|
<< "close the page, that likely means a Protected Mode "
|
|
|
|
|
<< "boundary has been crossed (either entering or exiting "
|
|
|
|
|
<< "Protected Mode). It is highly likely that any subsequent "
|
|
|
|
|
<< "commands to this driver instance will fail. THIS IS NOT A "
|
|
|
|
|
<< "BUG IN THE IE DRIVER! Fix your code and/or browser "
|
|
|
|
|
<< "configuration so that a Protected Mode boundary is not "
|
|
|
|
|
<< "crossed.";
|
Improving IE driver use with invalid Protected Mode settings
Now, when the user does not set the Protected Mode settings of the
browser and sends the capability to bypass the checks for those
settings, the driver will attempt to predict when a Protected Mode
boundary will be crossed, and set in motion a process to reattach
itself to the newly created browser. This process is far from perfect.
It is subject to really challenging race conditions that are truly
impossible to eliminate entirely, because of the architecture of the
browser itself. Nevertheless, even in its flawed state, this is still
a better outcome than it was previously for users.
Please note that the advice and support policy of the IE driver will
continue to be that the user must set the Protected Mode settings of
the browser properly before using the driver. Any "issues" that arise
by not having the settings set, but that disappear when the settings
are corrected, are not considered by the project to be valid issues.
This will include, but not be limited to, issues like abandoned
browser instances not being closed, use of multiple instances of the
driver where the wrong browser window is connected to and automated,
and issues where the driver appears to hang upon navigation to a new
page. If the problem disappears when the browser is properly
configured, any issue reports will be immediately closed with a note
to properly configure the browser and remove the capability.
The following situations should be at least partially mitigated by the
change:
* Navigation to a new page
* Clicking on a link (specifically an <a> tag) that will lead to a
navigation to a new page
* Clicking a link that opens a new window
Other cases, like navigating backward and forward through the browser
history, clicking an element that submits a form, and so on, may not
be handled. In those cases, issue reports will be summarily closed,
unless a specific pull request fixing the issue is also provided.
Additionally, use of things like proxies to capture traffic between the
browser and web server may miss some traffic because of the race
conditions inherent in the mechanism used to reattach to a newly
created browser. Again, these race conditions are unavoidable, and
issue reports that are based on them will be immediately closed with a
note indicating that the browser must have its settings properly set.
These strict guidelines are not intended to be harsh, and are not put
in place with the intent to avoid investigating and fixing issues;
rather, they must be enforced because the underlying architecture of
the browser makes them unavoidable.
2019-02-17 09:29:01 -08:00
|
|
|
}
|
2018-03-14 12:09:55 -07:00
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
this->PostQuitMessage();
|
|
|
|
|
}
|
|
|
|
|
|
Improving IE driver use with invalid Protected Mode settings
Now, when the user does not set the Protected Mode settings of the
browser and sends the capability to bypass the checks for those
settings, the driver will attempt to predict when a Protected Mode
boundary will be crossed, and set in motion a process to reattach
itself to the newly created browser. This process is far from perfect.
It is subject to really challenging race conditions that are truly
impossible to eliminate entirely, because of the architecture of the
browser itself. Nevertheless, even in its flawed state, this is still
a better outcome than it was previously for users.
Please note that the advice and support policy of the IE driver will
continue to be that the user must set the Protected Mode settings of
the browser properly before using the driver. Any "issues" that arise
by not having the settings set, but that disappear when the settings
are corrected, are not considered by the project to be valid issues.
This will include, but not be limited to, issues like abandoned
browser instances not being closed, use of multiple instances of the
driver where the wrong browser window is connected to and automated,
and issues where the driver appears to hang upon navigation to a new
page. If the problem disappears when the browser is properly
configured, any issue reports will be immediately closed with a note
to properly configure the browser and remove the capability.
The following situations should be at least partially mitigated by the
change:
* Navigation to a new page
* Clicking on a link (specifically an <a> tag) that will lead to a
navigation to a new page
* Clicking a link that opens a new window
Other cases, like navigating backward and forward through the browser
history, clicking an element that submits a form, and so on, may not
be handled. In those cases, issue reports will be summarily closed,
unless a specific pull request fixing the issue is also provided.
Additionally, use of things like proxies to capture traffic between the
browser and web server may miss some traffic because of the race
conditions inherent in the mechanism used to reattach to a newly
created browser. Again, these race conditions are unavoidable, and
issue reports that are based on them will be immediately closed with a
note indicating that the browser must have its settings properly set.
These strict guidelines are not intended to be harsh, and are not put
in place with the intent to avoid investigating and fixing issues;
rather, they must be enforced because the underlying architecture of
the browser makes them unavoidable.
2019-02-17 09:29:01 -08:00
|
|
|
void __stdcall Browser::NewProcess(DWORD lCauseFlag,
|
|
|
|
|
IDispatch* pWB2,
|
|
|
|
|
VARIANT_BOOL* pbCancel) {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::NewProcess";
|
|
|
|
|
this->InitiateBrowserReattach();
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
void __stdcall Browser::NewWindow3(IDispatch** ppDisp,
|
|
|
|
|
VARIANT_BOOL* pbCancel,
|
|
|
|
|
DWORD dwFlags,
|
|
|
|
|
BSTR bstrUrlContext,
|
|
|
|
|
BSTR bstrUrl) {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::NewWindow3";
|
2018-09-08 08:13:09 -07:00
|
|
|
::PostMessage(this->executor_handle(), WD_BEFORE_NEW_WINDOW, NULL, NULL);
|
2021-10-25 06:57:17 -07:00
|
|
|
std::vector<HWND>* ie_window_handles = nullptr;
|
2021-11-03 18:50:35 -04:00
|
|
|
WPARAM param_flag = 0;
|
2021-10-25 06:57:17 -07:00
|
|
|
|
2021-11-03 18:50:35 -04:00
|
|
|
if (this->is_edge_chromium()) {
|
|
|
|
|
param_flag = 1000;
|
|
|
|
|
//HWND top_level_handle = this->GetTopLevelWindowHandle();
|
2021-10-25 06:57:17 -07:00
|
|
|
// 1) find all Edge browser window handles
|
|
|
|
|
std::vector<HWND>edge_window_handles;
|
|
|
|
|
::EnumWindows(&BrowserFactory::FindEdgeBrowserHandles,
|
|
|
|
|
reinterpret_cast<LPARAM>(&edge_window_handles));
|
|
|
|
|
|
|
|
|
|
// 2) find all IE browser window handlers as child window when Edge runs in IEMode
|
|
|
|
|
ie_window_handles = new std::vector<HWND>;
|
2021-11-03 18:50:35 -04:00
|
|
|
for (HWND& edge_window_handle : edge_window_handles) {
|
2021-10-25 06:57:17 -07:00
|
|
|
std::vector<HWND> child_window_handles;
|
2021-11-03 18:50:35 -04:00
|
|
|
::EnumChildWindows(edge_window_handle,
|
|
|
|
|
&BrowserFactory::FindIEBrowserHandles,
|
|
|
|
|
reinterpret_cast<LPARAM>(&child_window_handles));
|
2021-10-25 06:57:17 -07:00
|
|
|
|
2021-11-03 18:50:35 -04:00
|
|
|
for (HWND& child_window_handle : child_window_handles) {
|
|
|
|
|
ie_window_handles->push_back(child_window_handle);
|
2021-10-25 06:57:17 -07:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// Handle the NewWindow3 event to allow us to immediately hook
|
|
|
|
|
// the events of the new browser window opened by the user action.
|
|
|
|
|
// The three ways we can respond to this event are documented at
|
|
|
|
|
// http://msdn.microsoft.com/en-us/library/aa768337%28v=vs.85%29.aspx
|
|
|
|
|
// We potentially use two of those response methods.
|
|
|
|
|
// This will not allow us to handle windows created by the JavaScript
|
|
|
|
|
// showModalDialog function().
|
|
|
|
|
std::wstring url = bstrUrl;
|
|
|
|
|
IWebBrowser2* browser;
|
|
|
|
|
NewWindowInfo info;
|
|
|
|
|
info.target_url = StringUtilities::ToString(url);
|
|
|
|
|
LRESULT create_result = ::SendMessage(this->executor_handle(),
|
|
|
|
|
WD_BROWSER_NEW_WINDOW,
|
|
|
|
|
NULL,
|
|
|
|
|
reinterpret_cast<LPARAM>(&info));
|
|
|
|
|
if (create_result != 0) {
|
|
|
|
|
// The new, blank IWebBrowser2 object was not created,
|
|
|
|
|
// so we can't really do anything appropriate here.
|
|
|
|
|
// Note this is "response method 2" of the aforementioned
|
|
|
|
|
// documentation.
|
|
|
|
|
LOG(WARN) << "A valid IWebBrowser2 object could not be created.";
|
|
|
|
|
*pbCancel = VARIANT_TRUE;
|
|
|
|
|
::PostMessage(this->executor_handle(), WD_AFTER_NEW_WINDOW, NULL, NULL);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We received a valid IWebBrowser2 pointer, so deserialize it onto this
|
|
|
|
|
// thread, and pass the result back to the caller.
|
|
|
|
|
HRESULT hr = ::CoGetInterfaceAndReleaseStream(info.browser_stream,
|
|
|
|
|
IID_IWebBrowser2,
|
|
|
|
|
reinterpret_cast<void**>(&browser));
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Failed to marshal IWebBrowser2 interface from stream.";
|
|
|
|
|
}
|
Fixing IE driver crash when clicking on link that opens a new window.
When IWebBrowser2::Quit() is called, the wrapper process doesn't exit
right away. When that happens, CoCreateInstance can fail while the
abandoned iexplore.exe instance is still valid. The "right" way to do
this would be to call ::EnumProcesses before calling CoCreateInstance,
finding all of the iexplore.exe processes, waiting for one to exit,
and then proceed. However, there is no way to tell if a process ID
belongs to an Internet Explorer instance, particularly when a 32-bit
process tries to enumerate 64-bit processes on 64-bit Windows. So, we
take the brute force way out, just retrying the call to
CoCreateInstance until it succeeds (the old iexplore.exe process has
exited), or we get a different error code. We also set a 45-second
timeout, with 45 seconds being chosen because it's below the default
60 second HTTP request timeout of most language bindings.
Fixes issue #5848. Fixes issue #7021.
2014-05-08 11:17:09 -04:00
|
|
|
|
2021-10-25 06:57:17 -07:00
|
|
|
*ppDisp = browser;
|
2018-09-08 08:13:09 -07:00
|
|
|
}
|
|
|
|
|
|
2021-10-25 06:57:17 -07:00
|
|
|
// 3) pass all IE window handles to WD_AFTER_NEW_WINDOW
|
2021-11-03 18:50:35 -04:00
|
|
|
::PostMessage(this->executor_handle(),
|
|
|
|
|
WD_AFTER_NEW_WINDOW,
|
|
|
|
|
param_flag,
|
|
|
|
|
reinterpret_cast<LPARAM>(ie_window_handles));
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void __stdcall Browser::DocumentComplete(IDispatch* pDisp, VARIANT* URL) {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::DocumentComplete";
|
|
|
|
|
|
|
|
|
|
// Flag the browser as navigation having started.
|
|
|
|
|
this->is_navigation_started_ = true;
|
|
|
|
|
|
|
|
|
|
// DocumentComplete fires last for the top-level frame. If it fires
|
|
|
|
|
// for the top-level frame and the focused_frame_window_ member variable
|
|
|
|
|
// is not NULL, we assume we have navigated from within a frameset to a
|
|
|
|
|
// link that has a target of "_top", which replaces the frameset with the
|
|
|
|
|
// target page. On a top-level navigation, we are supposed to reset the
|
|
|
|
|
// focused frame to the top-level, so we do that here.
|
|
|
|
|
// NOTE: This is a possible source of unreliability if the above
|
|
|
|
|
// assumptions turn out to be wrong and/or the event firing doesn't work
|
|
|
|
|
// the way we expect it to.
|
|
|
|
|
CComPtr<IDispatch> dispatch(this->browser_);
|
|
|
|
|
if (dispatch.IsEqualObject(pDisp)) {
|
|
|
|
|
if (this->focused_frame_window() != NULL) {
|
|
|
|
|
LOG(DEBUG) << "DocumentComplete happened from within a frameset";
|
|
|
|
|
this->SetFocusedFrameByElement(NULL);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
Improving IE driver use with invalid Protected Mode settings
Now, when the user does not set the Protected Mode settings of the
browser and sends the capability to bypass the checks for those
settings, the driver will attempt to predict when a Protected Mode
boundary will be crossed, and set in motion a process to reattach
itself to the newly created browser. This process is far from perfect.
It is subject to really challenging race conditions that are truly
impossible to eliminate entirely, because of the architecture of the
browser itself. Nevertheless, even in its flawed state, this is still
a better outcome than it was previously for users.
Please note that the advice and support policy of the IE driver will
continue to be that the user must set the Protected Mode settings of
the browser properly before using the driver. Any "issues" that arise
by not having the settings set, but that disappear when the settings
are corrected, are not considered by the project to be valid issues.
This will include, but not be limited to, issues like abandoned
browser instances not being closed, use of multiple instances of the
driver where the wrong browser window is connected to and automated,
and issues where the driver appears to hang upon navigation to a new
page. If the problem disappears when the browser is properly
configured, any issue reports will be immediately closed with a note
to properly configure the browser and remove the capability.
The following situations should be at least partially mitigated by the
change:
* Navigation to a new page
* Clicking on a link (specifically an <a> tag) that will lead to a
navigation to a new page
* Clicking a link that opens a new window
Other cases, like navigating backward and forward through the browser
history, clicking an element that submits a form, and so on, may not
be handled. In those cases, issue reports will be summarily closed,
unless a specific pull request fixing the issue is also provided.
Additionally, use of things like proxies to capture traffic between the
browser and web server may miss some traffic because of the race
conditions inherent in the mechanism used to reattach to a newly
created browser. Again, these race conditions are unavoidable, and
issue reports that are based on them will be immediately closed with a
note indicating that the browser must have its settings properly set.
These strict guidelines are not intended to be harsh, and are not put
in place with the intent to avoid investigating and fixing issues;
rather, they must be enforced because the underlying architecture of
the browser makes them unavoidable.
2019-02-17 09:29:01 -08:00
|
|
|
void Browser::InitiateBrowserReattach() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::InitiateBrowserReattach";
|
|
|
|
|
LOG(DEBUG) << "Requesting browser reattach";
|
|
|
|
|
this->known_process_ids_.clear();
|
|
|
|
|
WindowUtilities::GetProcessesByName(L"iexplore.exe",
|
|
|
|
|
&this->known_process_ids_);
|
|
|
|
|
this->set_is_awaiting_new_process(true);
|
|
|
|
|
::SendMessage(this->executor_handle(), WD_BEFORE_BROWSER_REATTACH, NULL, NULL);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Browser::ReattachBrowser(IWebBrowser2* browser) {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::ReattachBrowser";
|
|
|
|
|
this->browser_ = browser;
|
|
|
|
|
this->AttachEvents();
|
|
|
|
|
this->set_is_awaiting_new_process(false);
|
|
|
|
|
LOG(DEBUG) << "Reattach complete";
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
void Browser::GetDocument(IHTMLDocument2** doc) {
|
2015-07-13 15:19:16 -04:00
|
|
|
this->GetDocument(false, doc);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Browser::GetDocument(const bool force_top_level_document,
|
|
|
|
|
IHTMLDocument2** doc) {
|
2013-01-11 22:18:32 +01:00
|
|
|
LOG(TRACE) << "Entering Browser::GetDocument";
|
|
|
|
|
CComPtr<IHTMLWindow2> window;
|
|
|
|
|
|
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
|
|
|
LOG(INFO) << "No child frame focus. Focus is on top-level frame";
|
|
|
|
|
|
|
|
|
|
CComPtr<IDispatch> dispatch;
|
|
|
|
|
HRESULT hr = this->browser_->get_Document(&dispatch);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Unable to get document, IWebBrowser2::get_Document call failed";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CComPtr<IHTMLDocument2> dispatch_doc;
|
|
|
|
|
hr = dispatch->QueryInterface(&dispatch_doc);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Have document but cannot cast, IDispatch::QueryInterface call failed";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
dispatch_doc->get_parentWindow(&window);
|
|
|
|
|
} else {
|
|
|
|
|
window = this->focused_frame_window();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (window) {
|
|
|
|
|
bool result = this->GetDocumentFromWindow(window, doc);
|
|
|
|
|
if (!result) {
|
|
|
|
|
LOG(WARN) << "Cannot get document";
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
LOG(WARN) << "No window is found";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string Browser::GetTitle() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::GetTitle";
|
|
|
|
|
|
|
|
|
|
CComPtr<IDispatch> dispatch;
|
|
|
|
|
HRESULT hr = this->browser_->get_Document(&dispatch);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Unable to get document, IWebBrowser2::get_Document call failed";
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CComPtr<IHTMLDocument2> doc;
|
|
|
|
|
hr = dispatch->QueryInterface(&doc);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Have document but cannot cast, IDispatch::QueryInterface call failed";
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CComBSTR title;
|
|
|
|
|
hr = doc->get_title(&title);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Unable to get document title, call to IHTMLDocument2::get_title failed";
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-19 13:03:28 -04:00
|
|
|
std::string Browser::GetBrowserUrl() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::GetBrowserUrl";
|
|
|
|
|
|
|
|
|
|
CComBSTR url;
|
|
|
|
|
HRESULT hr = this->browser_->get_LocationURL(&url);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Unable to get current URL, call to IWebBrowser2::get_LocationURL failed";
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-21 13:58:47 -04:00
|
|
|
std::wstring converted_url = url;
|
|
|
|
|
std::string current_url = StringUtilities::ToString(converted_url);
|
2013-03-19 13:03:28 -04:00
|
|
|
return current_url;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-15 11:34:03 -05:00
|
|
|
HWND Browser::GetContentWindowHandle() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::GetContentWindowHandle";
|
2013-01-11 22:18:32 +01:00
|
|
|
|
2018-09-08 08:13:09 -07:00
|
|
|
HWND current_content_window_handle = this->window_handle();
|
|
|
|
|
// If this window is closing, the only reason to care about
|
|
|
|
|
// a valid window handle is to check for alerts whose parent
|
|
|
|
|
// is this window handle, so return the stored window handle.
|
|
|
|
|
if (!this->is_closing()) {
|
|
|
|
|
// If, for some reason, the window handle is no longer valid, set the
|
|
|
|
|
// member variable to NULL so that we can reacquire the valid window
|
|
|
|
|
// handle. Note that this can happen when browsing from one type of
|
|
|
|
|
// content to another, like from HTML to a transformed XML page that
|
|
|
|
|
// renders content. If the member variable is NULL upon entering this
|
|
|
|
|
// method, that is okay, as it typically means only that this object
|
|
|
|
|
// is newly constructed, and has not yet had its handle set.
|
|
|
|
|
bool window_handle_is_valid = ::IsWindow(current_content_window_handle);
|
|
|
|
|
if (!window_handle_is_valid) {
|
|
|
|
|
LOG(INFO) << "Flushing window handle as it is no longer valid";
|
|
|
|
|
this->set_window_handle(NULL);
|
|
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
|
2018-09-08 08:13:09 -07:00
|
|
|
if (this->window_handle() == NULL) {
|
|
|
|
|
LOG(INFO) << "Restore window handle from tab";
|
|
|
|
|
// GetBrowserWindowHandle gets the TabWindowClass window in IE 7 and 8,
|
|
|
|
|
// and the top-level window frame in IE 6. The window we need is the
|
|
|
|
|
// InternetExplorer_Server window.
|
|
|
|
|
HWND tab_window_handle = this->GetBrowserWindowHandle();
|
|
|
|
|
if (tab_window_handle == NULL) {
|
|
|
|
|
LOG(WARN) << "No tab window found";
|
|
|
|
|
}
|
|
|
|
|
HWND content_window_handle = this->FindContentWindowHandle(tab_window_handle);
|
|
|
|
|
this->set_window_handle(content_window_handle);
|
|
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return this->window_handle();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string Browser::GetWindowName() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::GetWindowName";
|
|
|
|
|
|
|
|
|
|
CComPtr<IDispatch> dispatch;
|
|
|
|
|
HRESULT hr = this->browser_->get_Document(&dispatch);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Unable to get document, IWebBrowser2::get_Document call failed";
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-07 17:06:31 -05:00
|
|
|
CComPtr<IHTMLDocument2> doc;
|
|
|
|
|
dispatch->QueryInterface<IHTMLDocument2>(&doc);
|
2013-01-11 22:18:32 +01:00
|
|
|
if (!doc) {
|
|
|
|
|
LOGHR(WARN, hr) << "Have document but cannot cast, IDispatch::QueryInterface call failed";
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CComPtr<IHTMLWindow2> window;
|
|
|
|
|
hr = doc->get_parentWindow(&window);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Unable to get parent window, call to IHTMLDocument2::get_parentWindow failed";
|
|
|
|
|
return "";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
std::string name = "";
|
|
|
|
|
CComBSTR window_name;
|
|
|
|
|
hr = window->get_name(&window_name);
|
|
|
|
|
if (window_name) {
|
2013-03-21 13:58:47 -04:00
|
|
|
std::wstring converted_window_name = window_name;
|
|
|
|
|
name = StringUtilities::ToString(converted_window_name);
|
2013-01-11 22:18:32 +01:00
|
|
|
} else {
|
|
|
|
|
LOG(WARN) << "Unable to get window name, IHTMLWindow2::get_name failed or returned a NULL value";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long Browser::GetWidth() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::GetWidth";
|
|
|
|
|
long width = 0;
|
|
|
|
|
this->browser_->get_Width(&width);
|
|
|
|
|
return width;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
long Browser::GetHeight() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::GetHeight";
|
|
|
|
|
long height = 0;
|
|
|
|
|
this->browser_->get_Height(&height);
|
|
|
|
|
return height;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Browser::SetWidth(long width) {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::SetWidth";
|
|
|
|
|
this->browser_->put_Width(width);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Browser::SetHeight(long height) {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::SetHeight";
|
|
|
|
|
this->browser_->put_Height(height);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Browser::AttachEvents() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::AttachEvents";
|
2013-03-07 17:06:31 -05:00
|
|
|
CComPtr<IUnknown> unknown;
|
2013-03-25 13:34:23 -04:00
|
|
|
this->browser_->QueryInterface<IUnknown>(&unknown);
|
2013-01-11 22:18:32 +01:00
|
|
|
HRESULT hr = this->DispEventAdvise(unknown);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Browser::DetachEvents() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::DetachEvents";
|
2013-03-07 17:06:31 -05:00
|
|
|
CComPtr<IUnknown> unknown;
|
2013-03-25 13:34:23 -04:00
|
|
|
this->browser_->QueryInterface<IUnknown>(&unknown);
|
2013-01-11 22:18:32 +01:00
|
|
|
HRESULT hr = this->DispEventUnadvise(unknown);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Browser::Close() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::Close";
|
2021-11-05 15:48:25 -04:00
|
|
|
if (this->is_edge_chromium()) {
|
|
|
|
|
// For Edge in IE Mode, cache the top-level hosting Chromium window
|
|
|
|
|
// handle so they can be properly closed on quit.
|
|
|
|
|
HWND top_level_window_handle = this->GetTopLevelWindowHandle();
|
|
|
|
|
::SendMessage(this->executor_handle(),
|
|
|
|
|
WD_ADD_CHROMIUM_WINDOW_HANDLE,
|
|
|
|
|
reinterpret_cast<WPARAM>(top_level_window_handle),
|
|
|
|
|
NULL);
|
|
|
|
|
}
|
|
|
|
|
|
2018-03-14 12:09:55 -07:00
|
|
|
this->is_explicit_close_requested_ = true;
|
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);
|
2019-06-10 17:45:31 -07:00
|
|
|
|
2021-11-05 04:38:39 +09:00
|
|
|
HRESULT hr = S_OK;
|
2021-11-05 15:48:25 -04:00
|
|
|
hr = this->browser_->Stop();
|
2021-11-05 04:38:39 +09:00
|
|
|
hr = this->browser_->Quit();
|
2019-06-10 17:45:31 -07:00
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Call to IWebBrowser2::Quit failed";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2019-07-18 15:20:45 -07:00
|
|
|
int Browser::NavigateToUrl(const std::string& url,
|
|
|
|
|
std::string* error_message) {
|
2013-01-11 22:18:32 +01:00
|
|
|
LOG(TRACE) << "Entring Browser::NavigateToUrl";
|
|
|
|
|
|
2013-03-21 13:58:47 -04:00
|
|
|
std::wstring wide_url = StringUtilities::ToWString(url);
|
2013-01-11 22:18:32 +01:00
|
|
|
CComVariant url_variant(wide_url.c_str());
|
|
|
|
|
CComVariant dummy;
|
|
|
|
|
|
|
|
|
|
HRESULT hr = this->browser_->Navigate2(&url_variant,
|
|
|
|
|
&dummy,
|
|
|
|
|
&dummy,
|
|
|
|
|
&dummy,
|
|
|
|
|
&dummy);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Call to IWebBrowser2::Navigate2 failed";
|
2019-07-18 15:20:45 -07:00
|
|
|
_com_error error(hr);
|
|
|
|
|
std::wstring formatted_message = StringUtilities::Format(
|
|
|
|
|
L"Received error: 0x%08x ['%s']",
|
|
|
|
|
hr,
|
|
|
|
|
error.ErrorMessage());
|
|
|
|
|
*error_message = StringUtilities::ToString(formatted_message);
|
2013-01-11 22:18:32 +01:00
|
|
|
return EUNHANDLEDERROR;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->set_wait_required(true);
|
2013-01-16 14:37:30 -05:00
|
|
|
return WD_SUCCESS;
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Browser::NavigateBack() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::NavigateBack";
|
|
|
|
|
LPSTREAM stream;
|
|
|
|
|
HRESULT hr = ::CoMarshalInterThreadInterfaceInStream(IID_IWebBrowser2, this->browser_, &stream);
|
|
|
|
|
unsigned int thread_id = 0;
|
|
|
|
|
HANDLE thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL,
|
|
|
|
|
0,
|
|
|
|
|
&Browser::GoBackThreadProc,
|
|
|
|
|
(void *)stream,
|
|
|
|
|
0,
|
|
|
|
|
&thread_id));
|
|
|
|
|
if (thread_handle != NULL) {
|
|
|
|
|
::CloseHandle(thread_handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->set_wait_required(true);
|
2013-01-16 14:37:30 -05:00
|
|
|
return WD_SUCCESS;
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int WINAPI Browser::GoBackThreadProc(LPVOID param) {
|
|
|
|
|
HRESULT hr = ::CoInitialize(NULL);
|
|
|
|
|
IWebBrowser2* browser;
|
|
|
|
|
LPSTREAM message_payload = reinterpret_cast<LPSTREAM>(param);
|
|
|
|
|
hr = ::CoGetInterfaceAndReleaseStream(message_payload,
|
|
|
|
|
IID_IWebBrowser2,
|
|
|
|
|
reinterpret_cast<void**>(&browser));
|
2013-02-25 11:32:30 -05:00
|
|
|
if (browser != NULL) {
|
|
|
|
|
hr = browser->GoBack();
|
|
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Browser::NavigateForward() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::NavigateForward";
|
|
|
|
|
LPSTREAM stream;
|
|
|
|
|
HRESULT hr = ::CoMarshalInterThreadInterfaceInStream(IID_IWebBrowser2, this->browser_, &stream);
|
|
|
|
|
unsigned int thread_id = 0;
|
|
|
|
|
HANDLE thread_handle = reinterpret_cast<HANDLE>(_beginthreadex(NULL,
|
|
|
|
|
0,
|
|
|
|
|
&Browser::GoForwardThreadProc,
|
|
|
|
|
(void *)stream,
|
|
|
|
|
0,
|
|
|
|
|
&thread_id));
|
|
|
|
|
if (thread_handle != NULL) {
|
|
|
|
|
::CloseHandle(thread_handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->set_wait_required(true);
|
2013-01-16 14:37:30 -05:00
|
|
|
return WD_SUCCESS;
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int WINAPI Browser::GoForwardThreadProc(LPVOID param) {
|
|
|
|
|
HRESULT hr = ::CoInitialize(NULL);
|
|
|
|
|
IWebBrowser2* browser;
|
|
|
|
|
LPSTREAM message_payload = reinterpret_cast<LPSTREAM>(param);
|
|
|
|
|
hr = ::CoGetInterfaceAndReleaseStream(message_payload,
|
|
|
|
|
IID_IWebBrowser2,
|
|
|
|
|
reinterpret_cast<void**>(&browser));
|
2013-02-25 11:32:30 -05:00
|
|
|
if (browser != NULL) {
|
|
|
|
|
hr = browser->GoForward();
|
|
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
int Browser::Refresh() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::Refresh";
|
|
|
|
|
|
|
|
|
|
HRESULT hr = this->browser_->Refresh();
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Call to IWebBrowser2::Refresh failed";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
this->set_wait_required(true);
|
2013-01-16 14:37:30 -05:00
|
|
|
return WD_SUCCESS;
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
HWND Browser::GetTopLevelWindowHandle() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::GetTopLevelWindowHandle";
|
|
|
|
|
|
|
|
|
|
HWND top_level_window_handle = NULL;
|
2018-09-08 08:13:09 -07:00
|
|
|
HRESULT hr = this->browser_->get_HWND(reinterpret_cast<SHANDLE_PTR*>(&top_level_window_handle));
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Getting HWND property of IWebBrowser2 object failed";
|
|
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
|
|
|
|
|
return top_level_window_handle;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-10 12:44:27 -04:00
|
|
|
bool Browser::IsValidWindow() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::IsValidWindow";
|
|
|
|
|
// This is a no-op for this class. Full browser windows can properly notify
|
|
|
|
|
// of their window's validity by using the proper events.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
bool Browser::IsBusy() {
|
|
|
|
|
VARIANT_BOOL is_busy(VARIANT_FALSE);
|
|
|
|
|
HRESULT hr = this->browser_->get_Busy(&is_busy);
|
|
|
|
|
return SUCCEEDED(hr) && is_busy == VARIANT_TRUE;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 14:14:11 -04:00
|
|
|
bool Browser::Wait(const std::string& page_load_strategy) {
|
2013-01-11 22:18:32 +01:00
|
|
|
LOG(TRACE) << "Entering Browser::Wait";
|
Improving IE driver use with invalid Protected Mode settings
Now, when the user does not set the Protected Mode settings of the
browser and sends the capability to bypass the checks for those
settings, the driver will attempt to predict when a Protected Mode
boundary will be crossed, and set in motion a process to reattach
itself to the newly created browser. This process is far from perfect.
It is subject to really challenging race conditions that are truly
impossible to eliminate entirely, because of the architecture of the
browser itself. Nevertheless, even in its flawed state, this is still
a better outcome than it was previously for users.
Please note that the advice and support policy of the IE driver will
continue to be that the user must set the Protected Mode settings of
the browser properly before using the driver. Any "issues" that arise
by not having the settings set, but that disappear when the settings
are corrected, are not considered by the project to be valid issues.
This will include, but not be limited to, issues like abandoned
browser instances not being closed, use of multiple instances of the
driver where the wrong browser window is connected to and automated,
and issues where the driver appears to hang upon navigation to a new
page. If the problem disappears when the browser is properly
configured, any issue reports will be immediately closed with a note
to properly configure the browser and remove the capability.
The following situations should be at least partially mitigated by the
change:
* Navigation to a new page
* Clicking on a link (specifically an <a> tag) that will lead to a
navigation to a new page
* Clicking a link that opens a new window
Other cases, like navigating backward and forward through the browser
history, clicking an element that submits a form, and so on, may not
be handled. In those cases, issue reports will be summarily closed,
unless a specific pull request fixing the issue is also provided.
Additionally, use of things like proxies to capture traffic between the
browser and web server may miss some traffic because of the race
conditions inherent in the mechanism used to reattach to a newly
created browser. Again, these race conditions are unavoidable, and
issue reports that are based on them will be immediately closed with a
note indicating that the browser must have its settings properly set.
These strict guidelines are not intended to be harsh, and are not put
in place with the intent to avoid investigating and fixing issues;
rather, they must be enforced because the underlying architecture of
the browser makes them unavoidable.
2019-02-17 09:29:01 -08:00
|
|
|
|
2018-05-26 08:19:04 -07:00
|
|
|
if (page_load_strategy == NONE_PAGE_LOAD_STRATEGY) {
|
2015-04-16 14:14:11 -04:00
|
|
|
LOG(DEBUG) << "Page load strategy is 'none'. Aborting wait.";
|
|
|
|
|
this->set_wait_required(false);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
|
Improving IE driver use with invalid Protected Mode settings
Now, when the user does not set the Protected Mode settings of the
browser and sends the capability to bypass the checks for those
settings, the driver will attempt to predict when a Protected Mode
boundary will be crossed, and set in motion a process to reattach
itself to the newly created browser. This process is far from perfect.
It is subject to really challenging race conditions that are truly
impossible to eliminate entirely, because of the architecture of the
browser itself. Nevertheless, even in its flawed state, this is still
a better outcome than it was previously for users.
Please note that the advice and support policy of the IE driver will
continue to be that the user must set the Protected Mode settings of
the browser properly before using the driver. Any "issues" that arise
by not having the settings set, but that disappear when the settings
are corrected, are not considered by the project to be valid issues.
This will include, but not be limited to, issues like abandoned
browser instances not being closed, use of multiple instances of the
driver where the wrong browser window is connected to and automated,
and issues where the driver appears to hang upon navigation to a new
page. If the problem disappears when the browser is properly
configured, any issue reports will be immediately closed with a note
to properly configure the browser and remove the capability.
The following situations should be at least partially mitigated by the
change:
* Navigation to a new page
* Clicking on a link (specifically an <a> tag) that will lead to a
navigation to a new page
* Clicking a link that opens a new window
Other cases, like navigating backward and forward through the browser
history, clicking an element that submits a form, and so on, may not
be handled. In those cases, issue reports will be summarily closed,
unless a specific pull request fixing the issue is also provided.
Additionally, use of things like proxies to capture traffic between the
browser and web server may miss some traffic because of the race
conditions inherent in the mechanism used to reattach to a newly
created browser. Again, these race conditions are unavoidable, and
issue reports that are based on them will be immediately closed with a
note indicating that the browser must have its settings properly set.
These strict guidelines are not intended to be harsh, and are not put
in place with the intent to avoid investigating and fixing issues;
rather, they must be enforced because the underlying architecture of
the browser makes them unavoidable.
2019-02-17 09:29:01 -08:00
|
|
|
if (this->is_awaiting_new_process()) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
bool is_navigating = true;
|
|
|
|
|
|
|
|
|
|
LOG(DEBUG) << "Navigate Events Completed.";
|
|
|
|
|
this->is_navigation_started_ = false;
|
|
|
|
|
|
|
|
|
|
HWND dialog = this->GetActiveDialogWindowHandle();
|
|
|
|
|
if (dialog != NULL) {
|
|
|
|
|
LOG(DEBUG) << "Found alert. Aborting wait.";
|
|
|
|
|
this->set_wait_required(false);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Navigate events completed. Waiting for browser.Busy != false...
|
|
|
|
|
is_navigating = this->is_navigation_started_;
|
2018-05-26 08:19:04 -07:00
|
|
|
if (is_navigating || (page_load_strategy == NORMAL_PAGE_LOAD_STRATEGY && this->IsBusy())) {
|
2013-10-10 08:08:49 -04:00
|
|
|
if (is_navigating) {
|
|
|
|
|
LOG(DEBUG) << "DocumentComplete event fired, indicating a new navigation.";
|
|
|
|
|
} else {
|
|
|
|
|
LOG(DEBUG) << "Browser busy property is true.";
|
|
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2016-03-24 14:03:40 -07:00
|
|
|
READYSTATE expected_ready_state = READYSTATE_COMPLETE;
|
2018-05-26 08:19:04 -07:00
|
|
|
if (page_load_strategy == EAGER_PAGE_LOAD_STRATEGY) {
|
2016-03-24 14:03:40 -07:00
|
|
|
expected_ready_state = READYSTATE_INTERACTIVE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Waiting for browser.ReadyState >= expected ready state
|
2013-01-11 22:18:32 +01:00
|
|
|
is_navigating = this->is_navigation_started_;
|
|
|
|
|
READYSTATE ready_state;
|
|
|
|
|
HRESULT hr = this->browser_->get_ReadyState(&ready_state);
|
2016-03-24 14:03:40 -07:00
|
|
|
if (is_navigating || FAILED(hr) || ready_state < expected_ready_state) {
|
2013-10-10 08:08:49 -04:00
|
|
|
if (is_navigating) {
|
|
|
|
|
LOG(DEBUG) << "DocumentComplete event fired, indicating a new navigation.";
|
|
|
|
|
} else if (FAILED(hr)) {
|
|
|
|
|
LOGHR(DEBUG, hr) << "IWebBrowser2::get_ReadyState failed.";
|
|
|
|
|
} else {
|
2016-03-24 14:03:40 -07:00
|
|
|
LOG(DEBUG) << "Browser ReadyState is not at least '" << expected_ready_state << "'; it was " << ready_state;
|
2013-10-10 08:08:49 -04:00
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Waiting for document property != null...
|
|
|
|
|
is_navigating = this->is_navigation_started_;
|
2013-03-07 17:06:31 -05:00
|
|
|
CComPtr<IDispatch> document_dispatch;
|
2013-01-11 22:18:32 +01:00
|
|
|
hr = this->browser_->get_Document(&document_dispatch);
|
2013-03-19 10:41:38 -04:00
|
|
|
if (is_navigating || FAILED(hr) || !document_dispatch) {
|
2013-10-10 08:08:49 -04:00
|
|
|
if (is_navigating) {
|
|
|
|
|
LOG(DEBUG) << "DocumentComplete event fired, indicating a new navigation.";
|
|
|
|
|
} else if (FAILED(hr)) {
|
|
|
|
|
LOGHR(DEBUG, hr) << "IWebBrowser2::get_Document failed.";
|
|
|
|
|
} else {
|
|
|
|
|
LOG(DEBUG) << "Get Document failed; IWebBrowser2::get_Document did not return a valid IDispatch object.";
|
|
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Waiting for document to complete...
|
|
|
|
|
CComPtr<IHTMLDocument2> doc;
|
|
|
|
|
hr = document_dispatch->QueryInterface(&doc);
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
|
LOG(DEBUG) << "Waiting for document to complete...";
|
2015-04-16 14:14:11 -04:00
|
|
|
is_navigating = this->IsDocumentNavigating(page_load_strategy, doc);
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!is_navigating) {
|
|
|
|
|
LOG(DEBUG) << "Not in navigating state";
|
|
|
|
|
this->set_wait_required(false);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return !is_navigating;
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 14:14:11 -04:00
|
|
|
bool Browser::IsDocumentNavigating(const std::string& page_load_strategy,
|
|
|
|
|
IHTMLDocument2* doc) {
|
2013-01-11 22:18:32 +01:00
|
|
|
LOG(TRACE) << "Entering Browser::IsDocumentNavigating";
|
|
|
|
|
|
|
|
|
|
bool is_navigating = true;
|
|
|
|
|
|
|
|
|
|
// Starting WaitForDocumentComplete()
|
|
|
|
|
is_navigating = this->is_navigation_started_;
|
2015-04-17 16:21:19 -04:00
|
|
|
CComBSTR ready_state_bstr;
|
|
|
|
|
HRESULT hr = doc->get_readyState(&ready_state_bstr);
|
|
|
|
|
if (FAILED(hr) || is_navigating) {
|
2013-10-10 08:08:49 -04:00
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(DEBUG, hr) << "IHTMLDocument2::get_readyState failed.";
|
|
|
|
|
} else if (is_navigating) {
|
|
|
|
|
LOG(DEBUG) << "DocumentComplete event fired, indicating a new navigation.";
|
2015-04-17 16:21:19 -04:00
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
std::wstring ready_state = ready_state_bstr;
|
|
|
|
|
if ((ready_state == L"complete") ||
|
2018-05-26 08:19:04 -07:00
|
|
|
(page_load_strategy == EAGER_PAGE_LOAD_STRATEGY && ready_state == L"interactive")) {
|
2015-04-17 16:21:19 -04:00
|
|
|
is_navigating = false;
|
2013-10-10 08:08:49 -04:00
|
|
|
} else {
|
2018-05-26 08:19:04 -07:00
|
|
|
if (page_load_strategy == EAGER_PAGE_LOAD_STRATEGY) {
|
2015-04-17 16:21:19 -04:00
|
|
|
LOG(DEBUG) << "document.readyState is not 'complete' or 'interactive'; it was " << LOGWSTRING(ready_state);
|
2015-04-16 14:14:11 -04:00
|
|
|
} else {
|
2015-04-17 16:21:19 -04:00
|
|
|
LOG(DEBUG) << "document.readyState is not 'complete'; it was " << LOGWSTRING(ready_state);
|
2015-04-16 14:14:11 -04:00
|
|
|
}
|
2015-04-17 16:21:19 -04:00
|
|
|
return true;
|
2013-10-10 08:08:49 -04:00
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// document.readyState == complete
|
|
|
|
|
is_navigating = this->is_navigation_started_;
|
|
|
|
|
CComPtr<IHTMLFramesCollection2> frames;
|
|
|
|
|
hr = doc->get_frames(&frames);
|
|
|
|
|
if (is_navigating || FAILED(hr)) {
|
|
|
|
|
LOG(DEBUG) << "Could not get frames, navigation has started or call to IHTMLDocument2::get_frames failed";
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (frames != NULL) {
|
|
|
|
|
long frame_count = 0;
|
|
|
|
|
hr = frames->get_length(&frame_count);
|
|
|
|
|
|
|
|
|
|
CComVariant index;
|
|
|
|
|
index.vt = VT_I4;
|
|
|
|
|
for (long i = 0; i < frame_count; ++i) {
|
|
|
|
|
// Waiting on each frame
|
|
|
|
|
index.lVal = i;
|
|
|
|
|
CComVariant result;
|
|
|
|
|
hr = frames->item(&index, &result);
|
|
|
|
|
if (FAILED(hr)) {
|
2013-10-10 08:08:49 -04:00
|
|
|
LOGHR(DEBUG, hr) << "Could not get frame item for index "
|
|
|
|
|
<< i
|
|
|
|
|
<< ", call to IHTMLFramesCollection2::item failed, frame/frameset is broken";
|
2013-08-13 05:49:10 +04:00
|
|
|
continue;
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
|
2013-03-07 17:06:31 -05:00
|
|
|
CComPtr<IHTMLWindow2> window;
|
|
|
|
|
result.pdispVal->QueryInterface<IHTMLWindow2>(&window);
|
2013-08-13 05:49:10 +04:00
|
|
|
if (!window) {
|
2013-10-10 08:08:49 -04:00
|
|
|
LOG(DEBUG) << "Could not get window for frame item with index "
|
|
|
|
|
<< i
|
|
|
|
|
<< ", cast to IHTMLWindow2 failed, frame is not an HTML frame";
|
2013-01-11 22:18:32 +01:00
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CComPtr<IHTMLDocument2> frame_document;
|
|
|
|
|
bool is_valid_frame_document = this->GetDocumentFromWindow(window,
|
|
|
|
|
&frame_document);
|
|
|
|
|
|
|
|
|
|
is_navigating = this->is_navigation_started_;
|
|
|
|
|
if (is_navigating) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Recursively call to wait for the frame document to complete
|
|
|
|
|
if (is_valid_frame_document) {
|
2015-04-16 14:14:11 -04:00
|
|
|
is_navigating = this->IsDocumentNavigating(page_load_strategy, frame_document);
|
2013-01-11 22:18:32 +01:00
|
|
|
if (is_navigating) {
|
|
|
|
|
break;
|
2013-08-13 05:49:10 +04:00
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
LOG(DEBUG) << "IHTMLDocument2.get_frames() returned empty collection";
|
|
|
|
|
}
|
|
|
|
|
return is_navigating;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Browser::GetDocumentFromWindow(IHTMLWindow2* window,
|
|
|
|
|
IHTMLDocument2** doc) {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::GetDocumentFromWindow";
|
|
|
|
|
|
|
|
|
|
HRESULT hr = window->get_document(doc);
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (hr == E_ACCESSDENIED) {
|
|
|
|
|
// Cross-domain documents may throw Access Denied. If so,
|
|
|
|
|
// get the document through the IWebBrowser2 interface.
|
2013-03-07 17:06:31 -05:00
|
|
|
CComPtr<IServiceProvider> service_provider;
|
|
|
|
|
hr = window->QueryInterface<IServiceProvider>(&service_provider);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Unable to get browser, call to IHTMLWindow2::QueryService failed for IServiceProvider";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
CComPtr<IWebBrowser2> window_browser;
|
|
|
|
|
hr = service_provider->QueryService(IID_IWebBrowserApp, &window_browser);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Unable to get browser, call to IServiceProvider::QueryService failed for IID_IWebBrowserApp";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2013-03-07 17:06:31 -05:00
|
|
|
CComPtr<IDispatch> document_dispatch;
|
2013-01-11 22:18:32 +01:00
|
|
|
hr = window_browser->get_Document(&document_dispatch);
|
|
|
|
|
if (FAILED(hr) || hr == S_FALSE) {
|
|
|
|
|
LOGHR(WARN, hr) << "Unable to get document, call to IWebBrowser2::get_Document failed";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hr = document_dispatch->QueryInterface(doc);
|
|
|
|
|
if (FAILED(hr)) {
|
|
|
|
|
LOGHR(WARN, hr) << "Unable to query document, call to IDispatch::QueryInterface failed.";
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
LOGHR(WARN, hr) << "Unable to get main document, IHTMLWindow2::get_document returned other than E_ACCESSDENIED";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-15 12:06:56 -05:00
|
|
|
HWND Browser::GetBrowserWindowHandle() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::GetBrowserWindowHandle";
|
2013-01-11 22:18:32 +01:00
|
|
|
|
|
|
|
|
HWND hwnd = NULL;
|
2013-03-07 17:06:31 -05:00
|
|
|
CComPtr<IServiceProvider> service_provider;
|
2013-01-11 22:18:32 +01:00
|
|
|
HRESULT hr = this->browser_->QueryInterface(IID_IServiceProvider,
|
|
|
|
|
reinterpret_cast<void**>(&service_provider));
|
2018-09-08 08:13:09 -07:00
|
|
|
HWND hwnd_tmp = NULL;
|
|
|
|
|
this->browser_->get_HWND(reinterpret_cast<SHANDLE_PTR*>(&hwnd_tmp));
|
2013-01-11 22:18:32 +01:00
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
|
CComPtr<IOleWindow> window;
|
|
|
|
|
hr = service_provider->QueryService(SID_SShellBrowser,
|
|
|
|
|
IID_IOleWindow,
|
|
|
|
|
reinterpret_cast<void**>(&window));
|
|
|
|
|
if (SUCCEEDED(hr)) {
|
|
|
|
|
// This gets the TabWindowClass window in IE 7 and 8,
|
2014-12-15 12:06:56 -05:00
|
|
|
// and the top-level window frame in IE 6.
|
2013-01-11 22:18:32 +01:00
|
|
|
window->GetWindow(&hwnd);
|
|
|
|
|
} else {
|
|
|
|
|
LOGHR(WARN, hr) << "Unable to get window, call to IOleWindow::QueryService for SID_SShellBrowser failed";
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
LOGHR(WARN, hr) << "Unable to get service, call to IWebBrowser2::QueryInterface for IID_IServiceProvider failed";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return hwnd;
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-31 09:41:44 -07:00
|
|
|
bool Browser::SetFullScreen(bool is_full_screen) {
|
2019-01-29 17:27:04 -08:00
|
|
|
VARIANT_BOOL full_screen_value = VARIANT_TRUE;
|
|
|
|
|
std::wstring full_screen_script = L"window.fullScreen = true;";
|
|
|
|
|
if (!is_full_screen) {
|
|
|
|
|
full_screen_value = VARIANT_FALSE;
|
|
|
|
|
full_screen_script = L"delete window.fullScreen;";
|
|
|
|
|
}
|
|
|
|
|
this->browser_->put_FullScreen(full_screen_value);
|
|
|
|
|
|
|
|
|
|
// IE does not support the W3C Fullscreen API (and likely never will).
|
|
|
|
|
// The prefixed version cannot be triggered via JavaScript outside of
|
|
|
|
|
// a user interaction, so we're going to cheat here and manually set
|
|
|
|
|
// the fullScreen property of the window object to the appropriate
|
|
|
|
|
// value. This may interfere with polyfills in use, and if that's
|
|
|
|
|
// the case, we'll revisit this hack.
|
|
|
|
|
CComPtr<IHTMLDocument2> doc;
|
|
|
|
|
this->GetDocument(true, &doc);
|
|
|
|
|
std::wstring script = ANONYMOUS_FUNCTION_START;
|
|
|
|
|
script += full_screen_script;
|
|
|
|
|
script += ANONYMOUS_FUNCTION_END;
|
|
|
|
|
Script script_wrapper(doc, script, 0);
|
|
|
|
|
script_wrapper.Execute();
|
2017-08-31 09:41:44 -07:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
bool Browser::IsFullScreen() {
|
|
|
|
|
VARIANT_BOOL is_full_screen = VARIANT_FALSE;
|
|
|
|
|
this->browser_->get_FullScreen(&is_full_screen);
|
|
|
|
|
return is_full_screen == VARIANT_TRUE;
|
|
|
|
|
}
|
2014-12-15 12:06:56 -05:00
|
|
|
|
2013-01-11 22:18:32 +01:00
|
|
|
HWND Browser::GetActiveDialogWindowHandle() {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::GetActiveDialogWindowHandle";
|
|
|
|
|
|
|
|
|
|
HWND active_dialog_handle = NULL;
|
|
|
|
|
|
2018-09-08 08:13:09 -07:00
|
|
|
HWND content_window_handle = this->GetContentWindowHandle();
|
|
|
|
|
if (content_window_handle == NULL) {
|
|
|
|
|
return active_dialog_handle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
DWORD process_id = 0;
|
|
|
|
|
::GetWindowThreadProcessId(content_window_handle, &process_id);
|
|
|
|
|
if (process_id == 0) {
|
|
|
|
|
return active_dialog_handle;
|
|
|
|
|
}
|
2013-01-11 22:18:32 +01:00
|
|
|
|
|
|
|
|
ProcessWindowInfo process_win_info;
|
|
|
|
|
process_win_info.dwProcessId = process_id;
|
|
|
|
|
process_win_info.hwndBrowser = NULL;
|
|
|
|
|
::EnumWindows(&BrowserFactory::FindDialogWindowForProcess,
|
|
|
|
|
reinterpret_cast<LPARAM>(&process_win_info));
|
|
|
|
|
if (process_win_info.hwndBrowser != NULL) {
|
|
|
|
|
active_dialog_handle = process_win_info.hwndBrowser;
|
|
|
|
|
this->CheckDialogType(active_dialog_handle);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return active_dialog_handle;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Browser::CheckDialogType(HWND dialog_window_handle) {
|
|
|
|
|
LOG(TRACE) << "Entering Browser::CheckDialogType";
|
|
|
|
|
|
2013-07-11 17:46:24 -04:00
|
|
|
std::vector<char> window_class_name(34);
|
2013-01-11 22:18:32 +01:00
|
|
|
if (GetClassNameA(dialog_window_handle, &window_class_name[0], 34)) {
|
|
|
|
|
if (strcmp(HTML_DIALOG_WINDOW_CLASS,
|
|
|
|
|
&window_class_name[0]) == 0) {
|
|
|
|
|
HWND content_window_handle = this->FindContentWindowHandle(dialog_window_handle);
|
|
|
|
|
::PostMessage(this->executor_handle(),
|
|
|
|
|
WD_NEW_HTML_DIALOG,
|
|
|
|
|
NULL,
|
|
|
|
|
reinterpret_cast<LPARAM>(content_window_handle));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2017-08-31 09:41:44 -07:00
|
|
|
} // namespace webdriver
|