Skip to content

Commit

Permalink
Enabling create new window command to create tabs in IE
Browse files Browse the repository at this point in the history
This change overcomes the limitation of only creating top-level
browser contexts in new top-level windows. The IE driver can now open
new tabs when using the create new window command, as in the .NET
language bindings code below:

    driver.SwitchTo().NewWindow(WindowType.Tab);

Note carefully that this change is only valid when explicitly using
the create new window command. It does not affect clicking on links
that open new top-level browsing contexts; those will continue to
open in new top-level windows. Furthermore, it is imperative to note
that this feature will absolutely not work when Protected Mode
properties are misconfigured. In other words, if your code uses the
.NET InternetExplorerOptions
`IntroduceInstabilityByIgnoringProtectedModeSettings` property (or
its equivalent in any other language bindings), this feature will not
work. Issue reports submitted regarding this will be summarily closed.
  • Loading branch information
jimevans committed Jan 28, 2019
1 parent 4949267 commit e8d326e
Show file tree
Hide file tree
Showing 9 changed files with 161 additions and 44 deletions.
24 changes: 16 additions & 8 deletions cpp/iedriver/CommandHandlers/CreateNewWindowCommandHandler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,7 @@
#include "errorcodes.h"
#include "../Browser.h"
#include "../IECommandExecutor.h"

#define WINDOW_WINDOW_TYPE "window"
#define TAB_WINDOW_TYPE "tab"
#include "../WebDriverConstants.h"

namespace webdriver {

Expand All @@ -45,6 +43,14 @@ void CreateNewWindowCommandHandler::ExecuteInternal(
return;
}

std::string window_type = WINDOW_WINDOW_TYPE;
if (type_parameter_iterator->second.isString()) {
std::string parameter_value = type_parameter_iterator->second.asString();
if (parameter_value == TAB_WINDOW_TYPE) {
window_type = TAB_WINDOW_TYPE;
}
}

BrowserHandle browser_wrapper;
int status_code = executor.GetCurrentBrowser(&browser_wrapper);
if (status_code != WD_SUCCESS) {
Expand All @@ -54,17 +60,19 @@ void CreateNewWindowCommandHandler::ExecuteInternal(
}

IECommandExecutor& mutable_executor = const_cast<IECommandExecutor&>(executor);
std::string new_window_handle = mutable_executor.OpenNewBrowserWindow();
BrowserHandle tmp_browser;
executor.GetManagedBrowser(new_window_handle, &tmp_browser);
tmp_browser->NavigateToUrl("about:blank");
std::string new_window_handle = mutable_executor.OpenNewBrowsingContext(window_type);
if (new_window_handle.size() == 0) {
response->SetErrorResponse(ERROR_NO_SUCH_WINDOW, "New window not created");
return;
}
if (window_type == WINDOW_WINDOW_TYPE) {
BrowserHandle tmp_browser;
executor.GetManagedBrowser(new_window_handle, &tmp_browser);
tmp_browser->NavigateToUrl("about:blank");
}
Json::Value result;
result["handle"] = new_window_handle;
result["type"] = WINDOW_WINDOW_TYPE;
result["type"] = window_type;
response->SetSuccessResponse(result);
}

Expand Down
133 changes: 106 additions & 27 deletions cpp/iedriver/IECommandExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,33 +288,10 @@ LRESULT IECommandExecutor::OnBrowserNewWindow(UINT uMsg,
LPARAM lParam,
BOOL& bHandled) {
LOG(TRACE) << "Entering IECommandExecutor::OnBrowserNewWindow";
std::string new_browser_id = this->OpenNewBrowserWindow();
std::string new_browser_id = this->OpenNewBrowsingContext(WINDOW_WINDOW_TYPE);
BrowserHandle new_window_wrapper;
this->GetManagedBrowser(new_browser_id, &new_window_wrapper);
IWebBrowser2* browser = new_window_wrapper->browser();
//IWebBrowser2* browser = this->factory_->CreateBrowser();
//if (browser == NULL) {
// // No browser was created, so we have to bail early.
// // Check the log for the HRESULT why.
// return 1;
//}
//LOG(DEBUG) << "New browser window was opened.";
//BrowserHandle new_window_wrapper(new Browser(browser, NULL, this->m_hWnd));
//// It is acceptable to set the proxy settings here, as the newly-created
//// browser window has not yet been navigated to any page. Only after the
//// interface has been marshaled back across the thread boundary to the
//// NewWindow3 event handler will the navigation begin, which ensures that
//// even the initial navigation will get captured by the proxy, if one is
//// set. Likewise, the cookie manager needs to have its window handle
//// properly set to a non-NULL value so that windows messages are routed
//// to the correct window.
//// N.B. DocumentHost::GetBrowserWindowHandle returns the tab window handle
//// for IE 7 and above, and the top-level window for IE6. This is the window
//// required for setting the proxy settings.
//HWND new_window_handle = new_window_wrapper->GetBrowserWindowHandle();
//this->proxy_manager_->SetProxySettings(new_window_handle);
//new_window_wrapper->cookie_manager()->Initialize(new_window_handle);
//this->AddManagedBrowser(new_window_wrapper);
LOG(DEBUG) << "Attempting to marshal interface pointer to requesting thread.";
LPSTREAM* stream = reinterpret_cast<LPSTREAM*>(lParam);
HRESULT hr = ::CoMarshalInterThreadInterfaceInStream(IID_IWebBrowser2,
Expand Down Expand Up @@ -1058,6 +1035,23 @@ void IECommandExecutor::AddManagedBrowser(BrowserHandle browser_wrapper) {
}
}

std::string IECommandExecutor::OpenNewBrowsingContext(const std::string& window_type) {
std::string new_browser_id = "";
if (window_type == TAB_WINDOW_TYPE) {
new_browser_id = this->OpenNewBrowserTab(L"about:blank");
} else {
new_browser_id = this->OpenNewBrowserWindow();
}

BrowserHandle new_window_wrapper;
this->GetManagedBrowser(new_browser_id, &new_window_wrapper);
HWND new_window_handle = new_window_wrapper->GetBrowserWindowHandle();
this->proxy_manager_->SetProxySettings(new_window_handle);
new_window_wrapper->cookie_manager()->Initialize(new_window_handle);

return new_browser_id;
}

std::string IECommandExecutor::OpenNewBrowserWindow() {
CComPtr<IWebBrowser2> browser = this->factory_->CreateBrowser();
if (browser == NULL) {
Expand All @@ -1078,13 +1072,98 @@ std::string IECommandExecutor::OpenNewBrowserWindow() {
// N.B. DocumentHost::GetBrowserWindowHandle returns the tab window handle
// for IE 7 and above, and the top-level window for IE6. This is the window
// required for setting the proxy settings.
HWND new_window_handle = new_window_wrapper->GetBrowserWindowHandle();
this->proxy_manager_->SetProxySettings(new_window_handle);
new_window_wrapper->cookie_manager()->Initialize(new_window_handle);
this->AddManagedBrowser(new_window_wrapper);
return new_window_wrapper->browser_id();
}

std::string IECommandExecutor::OpenNewBrowserTab(const std::wstring& url) {
BrowserHandle browser_wrapper;
this->GetCurrentBrowser(&browser_wrapper);
HWND top_level_handle = browser_wrapper->GetTopLevelWindowHandle();

std::vector<HWND> original_handles;
::EnumChildWindows(top_level_handle,
&FindAllBrowserHandles,
reinterpret_cast<LPARAM>(&original_handles));
std::sort(original_handles.begin(), original_handles.end());

// IWebBrowser2::Navigate2 will open the specified URL in a new tab,
// if requested. The Sleep() call after the navigate is necessary,
// since the IECommandExecutor class doesn't have access to the events
// to indicate the navigation is completed.
CComVariant url_variant = url.c_str();
CComVariant flags = navOpenInNewTab;
browser_wrapper->browser()->Navigate2(&url_variant,
&flags,
NULL,
NULL,
NULL);
::Sleep(500);

clock_t end_time = clock() + 5 * CLOCKS_PER_SEC;
std::vector<HWND> new_handles;
::EnumChildWindows(top_level_handle,
&FindAllBrowserHandles,
reinterpret_cast<LPARAM>(&new_handles));
while (new_handles.size() <= original_handles.size() &&
clock() < end_time) {
::Sleep(50);
::EnumChildWindows(top_level_handle,
&FindAllBrowserHandles,
reinterpret_cast<LPARAM>(&new_handles));
}
std::sort(new_handles.begin(), new_handles.end());

if (new_handles.size() <= original_handles.size()) {
LOG(WARN) << "No new window handle found after attempt to open";
return "";
}

// We are guaranteed to have at least one HWND difference
// between the two vectors if we reach this point, because
// we know the vectors are different sizes.
std::vector<HWND> diff(new_handles.size());
std::vector<HWND>::iterator it = std::set_difference(new_handles.begin(),
new_handles.end(),
original_handles.begin(),
original_handles.end(),
diff.begin());
diff.resize(it - diff.begin());
HWND new_tab_window = diff[0];

DWORD process_id;
::GetWindowThreadProcessId(new_tab_window, &process_id);
ProcessWindowInfo info;
info.dwProcessId = process_id;
info.hwndBrowser = new_tab_window;
info.pBrowser = NULL;
std::string error_message = "";
this->factory_->AttachToBrowser(&info, &error_message);
BrowserHandle new_tab_wrapper(new Browser(info.pBrowser,
NULL,
this->m_hWnd));
this->AddManagedBrowser(new_tab_wrapper);
return new_tab_wrapper->browser_id();
}

BOOL CALLBACK IECommandExecutor::FindAllBrowserHandles(HWND hwnd, LPARAM arg) {
std::vector<HWND>* handles = reinterpret_cast<std::vector<HWND>*>(arg);

// Could this be an Internet Explorer Server window?
// 25 == "Internet Explorer_Server\0"
char name[25];
if (::GetClassNameA(hwnd, name, 25) == 0) {
// No match found. Skip
return TRUE;
}

if (strcmp("Internet Explorer_Server", name) == 0) {
handles->push_back(hwnd);
}

return TRUE;
}

int IECommandExecutor::CreateNewBrowser(std::string* error_message) {
LOG(TRACE) << "Entering IECommandExecutor::CreateNewBrowser";

Expand Down
6 changes: 5 additions & 1 deletion cpp/iedriver/IECommandExecutor.h
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ class IECommandExecutor : public CWindowImpl<IECommandExecutor>, public IElement
}

int CreateNewBrowser(std::string* error_message);
std::string OpenNewBrowserWindow(void);
std::string OpenNewBrowsingContext(const std::string& window_type);

int GetManagedBrowser(const std::string& browser_id,
BrowserHandle* browser_wrapper) const;
Expand Down Expand Up @@ -232,6 +232,10 @@ class IECommandExecutor : public CWindowImpl<IECommandExecutor>, public IElement
bool force_use_dismiss,
std::string* alert_text);

std::string OpenNewBrowserWindow(void);
std::string OpenNewBrowserTab(const std::wstring& url);
static BOOL CALLBACK FindAllBrowserHandles(HWND hwnd, LPARAM arg);

BrowserMap managed_browsers_;
ElementRepository* managed_elements_;
ElementFindMethodMap element_find_methods_;
Expand Down
8 changes: 4 additions & 4 deletions cpp/iedriver/IEDriver.rc
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ END
//

VS_VERSION_INFO VERSIONINFO
FILEVERSION 3,141,5,1
PRODUCTVERSION 3,141,5,1
FILEVERSION 3,141,5,2
PRODUCTVERSION 3,141,5,2
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
Expand All @@ -68,12 +68,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Software Freedom Conservancy"
VALUE "FileDescription", "Driver library for the IE driver"
VALUE "FileVersion", "3.141.5.1"
VALUE "FileVersion", "3.141.5.2"
VALUE "InternalName", "IEDriver.dll"
VALUE "LegalCopyright", "Copyright (C) 2019"
VALUE "OriginalFilename", "IEDriver.dll"
VALUE "ProductName", "Selenium WebDriver"
VALUE "ProductVersion", "3.141.5.1"
VALUE "ProductVersion", "3.141.5.2"
END
END
BLOCK "VarFileInfo"
Expand Down
4 changes: 4 additions & 0 deletions cpp/iedriver/WebDriverConstants.h
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,10 @@
#define USE_LEGACY_FILE_UPLOAD_DIALOG_HANDLING_CAPABILITY "ie.useLegacyFileUploadDialogHandling"
#define ENABLE_FULL_PAGE_SCREENSHOT_CAPABILITY "ie.enableFullPageScreenshot"

// New top-level browsing context types
#define WINDOW_WINDOW_TYPE "window"
#define TAB_WINDOW_TYPE "tab"

// Window classes
#define ALERT_WINDOW_CLASS "#32770"
#define HTML_DIALOG_WINDOW_CLASS "Internet Explorer_TridentDlgFrame"
Expand Down
22 changes: 22 additions & 0 deletions cpp/iedriverserver/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,28 @@ available via the project downloads page. Changes in "revision" field indicate
private releases checked into the prebuilts directory of the source tree, but
not made generally available on the downloads page.

v3.141.5.2
==========
* Enabled create new window command to create tabs. This change
overcomes the limitation of only creating top-level browser
contexts in new top-level windows. The IE driver can now open
new tabs when using the create new window command, as in the
.NET language bindings:

driver.SwitchTo().NewWindow(WindowType.Tab);

Note carefully that this change is only valid when explicitly
using the create new window command. It does not affect clicking
on links that open new top-level browsing contexts; those will
continue to open in new top-level windows. Furthermore, it is
imperative to note that this feature will absolutely not work
when Protected Mode properties are misconfigured. In other words,
if your code uses the .NET InternetExplorerOptions
`IntroduceInstabilityByIgnoringProtectedModeSettings` property
(or its equivalent in any other language bindings), this feature
will not work. Issue reports submitted regarding this will be
summarily closed.

v3.141.5.1
==========
* Implemented create new window commmand. The W3C WebDriver
Expand Down
8 changes: 4 additions & 4 deletions cpp/iedriverserver/IEDriverServer.rc
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ END
//

VS_VERSION_INFO VERSIONINFO
FILEVERSION 3,141,5,1
PRODUCTVERSION 3,141,5,1
FILEVERSION 3,141,5,2
PRODUCTVERSION 3,141,5,2
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS 0x1L
Expand All @@ -68,12 +68,12 @@ BEGIN
BEGIN
VALUE "CompanyName", "Software Freedom Conservancy"
VALUE "FileDescription", "Command line server for the IE driver"
VALUE "FileVersion", "3.141.5.1"
VALUE "FileVersion", "3.141.5.2"
VALUE "InternalName", "IEDriverServer.exe"
VALUE "LegalCopyright", "Copyright (C) 2019"
VALUE "OriginalFilename", "IEDriverServer.exe"
VALUE "ProductName", "Selenium WebDriver"
VALUE "ProductVersion", "3.141.5.1"
VALUE "ProductVersion", "3.141.5.2"
END
END
BLOCK "VarFileInfo"
Expand Down
Binary file modified cpp/prebuilt/Win32/Release/IEDriverServer.exe
Binary file not shown.
Binary file modified cpp/prebuilt/x64/Release/IEDriverServer.exe
Binary file not shown.

0 comments on commit e8d326e

Please sign in to comment.