Skip to content

Commit

Permalink
Implementing create new window commmand
Browse files Browse the repository at this point in the history
The W3C WebDriver Specification recently added the ability to create
a new top-level browsing context, or what appears to the user to be a
new tab or window. This change implements that command. Though the
command payload contains a hint argument as to what type of new top-
level browsing context the user desires (tab or window), the driver
implementation is free to ignore this type hint if it does not
support the type being requested. Since the IE driver has never
supported, and still does not support, creation of new top-level
browsing contexts using a new tab, in accordance with the spec, the
driver will create the new context in a new window, even if a new
tab is requested. Please note this is not a bug; it is as designed,
and is still fully compliant with the specification with this
behavior.
  • Loading branch information
jimevans committed Jan 24, 2019
1 parent 5a34ea7 commit 3c528a0
Show file tree
Hide file tree
Showing 16 changed files with 236 additions and 32 deletions.
2 changes: 2 additions & 0 deletions cpp/iedriver/CommandHandlerRepository.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
#include "CommandHandlers/ClickElementCommandHandler.h"
#include "CommandHandlers/ClearElementCommandHandler.h"
#include "CommandHandlers/CloseWindowCommandHandler.h"
#include "CommandHandlers/CreateNewWindowCommandHandler.h"
#include "CommandHandlers/DeleteAllCookiesCommandHandler.h"
#include "CommandHandlers/DeleteCookieCommandHandler.h"
#include "CommandHandlers/DismissAlertCommandHandler.h"
Expand Down Expand Up @@ -123,6 +124,7 @@ void CommandHandlerRepository::PopulateCommandHandlers() {
this->command_handlers_[webdriver::CommandType::CloseWindow] = CommandHandlerHandle(new CloseWindowCommandHandler);
this->command_handlers_[webdriver::CommandType::SwitchToWindow] = CommandHandlerHandle(new SwitchToWindowCommandHandler);
this->command_handlers_[webdriver::CommandType::GetWindowHandles] = CommandHandlerHandle(new GetAllWindowHandlesCommandHandler);
this->command_handlers_[webdriver::CommandType::NewWindow] = CommandHandlerHandle(new CreateNewWindowCommandHandler);
this->command_handlers_[webdriver::CommandType::SwitchToFrame] = CommandHandlerHandle(new SwitchToFrameCommandHandler);
this->command_handlers_[webdriver::CommandType::SwitchToParentFrame] = CommandHandlerHandle(new SwitchToParentFrameCommandHandler);
this->command_handlers_[webdriver::CommandType::GetWindowRect] = CommandHandlerHandle(new GetWindowRectCommandHandler);
Expand Down
71 changes: 71 additions & 0 deletions cpp/iedriver/CommandHandlers/CreateNewWindowCommandHandler.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "CreateNewWindowCommandHandler.h"
#include "errorcodes.h"
#include "../Browser.h"
#include "../IECommandExecutor.h"

#define WINDOW_WINDOW_TYPE "window"
#define TAB_WINDOW_TYPE "tab"

namespace webdriver {

CreateNewWindowCommandHandler::CreateNewWindowCommandHandler(void) {
}

CreateNewWindowCommandHandler::~CreateNewWindowCommandHandler(void) {
}

void CreateNewWindowCommandHandler::ExecuteInternal(
const IECommandExecutor& executor,
const ParametersMap& command_parameters,
Response* response) {
ParametersMap::const_iterator type_parameter_iterator = command_parameters.find("type");
if (type_parameter_iterator == command_parameters.end()) {
response->SetErrorResponse(ERROR_INVALID_ARGUMENT, "Missing parameter: type");
return;
}
if (!type_parameter_iterator->second.isString() &&
!type_parameter_iterator->second.isNull()) {
response->SetErrorResponse(ERROR_INVALID_ARGUMENT, "type parameter must be a string or null");
return;
}

BrowserHandle browser_wrapper;
int status_code = executor.GetCurrentBrowser(&browser_wrapper);
if (status_code != WD_SUCCESS) {
response->SetErrorResponse(ERROR_NO_SUCH_WINDOW,
"Error retrieving current window");
return;
}

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");
if (new_window_handle.size() == 0) {
response->SetErrorResponse(ERROR_NO_SUCH_WINDOW, "New window not created");
return;
}
Json::Value result;
result["handle"] = new_window_handle;
result["type"] = WINDOW_WINDOW_TYPE;
response->SetSuccessResponse(result);
}

} // namespace webdriver
37 changes: 37 additions & 0 deletions cpp/iedriver/CommandHandlers/CreateNewWindowCommandHandler.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef WEBDRIVER_IE_CREATENEWWINDOWCOMMANDHANDLER_H_
#define WEBDRIVER_IE_CREATENEWWINDOWCOMMANDHANDLER_H_

#include "../IECommandHandler.h"

namespace webdriver {

class CreateNewWindowCommandHandler : public IECommandHandler {
public:
CreateNewWindowCommandHandler(void);
virtual ~CreateNewWindowCommandHandler(void);

protected:
void ExecuteInternal(const IECommandExecutor& executor,
const ParametersMap& command_parameters,
Response* response);
};

} // namespace webdriver

#endif // WEBDRIVER_IE_CREATENEWWINDOWCOMMANDHANDLER_H_
2 changes: 2 additions & 0 deletions cpp/iedriver/DocumentHost.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class DocumentHost {
virtual bool SetFullScreen(bool is_full_screen) = 0;
void Restore(void);

virtual IWebBrowser2* browser(void) = 0;

std::string GetCurrentUrl(void);
std::string GetPageSource(void);

Expand Down
2 changes: 2 additions & 0 deletions cpp/iedriver/HtmlDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ class HtmlDialog : public DocumentHost, public IDispEventSimpleImpl<1, HtmlDialo
bool IsFullScreen(void);
bool SetFullScreen(bool is_full_screen);

IWebBrowser2* browser(void) { return NULL; }

private:
static BOOL CALLBACK FindChildDialogWindow(HWND hwnd, LPARAM arg);

Expand Down
78 changes: 54 additions & 24 deletions cpp/iedriver/IECommandExecutor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -288,30 +288,33 @@ LRESULT IECommandExecutor::OnBrowserNewWindow(UINT uMsg,
LPARAM lParam,
BOOL& bHandled) {
LOG(TRACE) << "Entering IECommandExecutor::OnBrowserNewWindow";

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);
std::string new_browser_id = this->OpenNewBrowserWindow();
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 @@ -1055,6 +1058,33 @@ void IECommandExecutor::AddManagedBrowser(BrowserHandle browser_wrapper) {
}
}

std::string IECommandExecutor::OpenNewBrowserWindow() {
CComPtr<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 "";
}
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);
return new_window_wrapper->browser_id();
}

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

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

int CreateNewBrowser(std::string* error_message);
std::string OpenNewBrowserWindow(void);

int GetManagedBrowser(const std::string& browser_id,
BrowserHandle* browser_wrapper) const;
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,0
PRODUCTVERSION 3,141,5,0
FILEVERSION 3,141,5,1
PRODUCTVERSION 3,141,5,1
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.0"
VALUE "FileVersion", "3.141.5.1"
VALUE "InternalName", "IEDriver.dll"
VALUE "LegalCopyright", "Copyright (C) 2019"
VALUE "OriginalFilename", "IEDriver.dll"
VALUE "ProductName", "Selenium WebDriver"
VALUE "ProductVersion", "3.141.5.0"
VALUE "ProductVersion", "3.141.5.1"
END
END
BLOCK "VarFileInfo"
Expand Down
2 changes: 2 additions & 0 deletions cpp/iedriver/IEDriver.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@
<ClCompile Include="CommandHandlers\ClearElementCommandHandler.cpp" />
<ClCompile Include="CommandHandlers\ClickElementCommandHandler.cpp" />
<ClCompile Include="CommandHandlers\CloseWindowCommandHandler.cpp" />
<ClCompile Include="CommandHandlers\CreateNewWindowCommandHandler.cpp" />
<ClCompile Include="CommandHandlers\DeleteAllCookiesCommandHandler.cpp" />
<ClCompile Include="CommandHandlers\DeleteCookieCommandHandler.cpp" />
<ClCompile Include="CommandHandlers\DismissAlertCommandHandler.cpp" />
Expand Down Expand Up @@ -281,6 +282,7 @@
<ClInclude Include="CommandHandlers\ClearElementCommandHandler.h" />
<ClInclude Include="CommandHandlers\ClickElementCommandHandler.h" />
<ClInclude Include="CommandHandlers\CloseWindowCommandHandler.h" />
<ClInclude Include="CommandHandlers\CreateNewWindowCommandHandler.h" />
<ClInclude Include="CommandHandlers\DeleteAllCookiesCommandHandler.h" />
<ClInclude Include="CommandHandlers\DeleteCookieCommandHandler.h" />
<ClInclude Include="CommandHandlers\DismissAlertCommandHandler.h" />
Expand Down
6 changes: 6 additions & 0 deletions cpp/iedriver/IEDriver.vcxproj.filters
Original file line number Diff line number Diff line change
Expand Up @@ -303,6 +303,9 @@
<ClCompile Include="cominterfaces.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="CommandHandlers\CreateNewWindowCommandHandler.cpp">
<Filter>Source Files\CommandHandlers</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Alert.h">
Expand Down Expand Up @@ -620,6 +623,9 @@
<ClInclude Include="WebDriverConstants.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="CommandHandlers\CreateNewWindowCommandHandler.h">
<Filter>Header Files\CommandHandlers</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<None Include="IEDriver.def">
Expand Down
49 changes: 49 additions & 0 deletions cpp/iedriverserver/CHANGELOG
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,55 @@ 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.1
==========
* Implemented create new window commmand. The W3C WebDriver
Specification recently added the ability to create a new top-level
browsing context, or what appears to the user to be a new tab or
window. This change implements that command. Though the command
payload contains a hint argument as to what type of new top-level
browsing context the user desires (tab or window), the driver
implementation is free to ignore this type hint if it does not
support the type being requested. Since the IE driver has never
supported, and still does not support, creation of new top-level
browsing contexts using a new tab, in accordance with the spec, the
driver will create the new context in a new window, even if a new
tab is requested. Please note this is not a bug; it is as designed,
and is still fully compliant with the specification with this
behavior.
* Fixed edge case for clicking element. In the case of a fixed element
where the top-left corner of the element is outside the view port,
the click point calculation was not taking the offset of the top-
left when looking for the in-view center of the element. This change
fixes that issue.
* Updatws Unicode character processing for combining characters. In a
previous revision, the IE driver was modified to normalize Unicode
strings that used combining characters to compose a single glyph when
sending keystrokes. This commit implements the reverse of that
operation when reading text data, decomposing the glyph into its
combining characters if required. This is a potential destablizing
change for text sequences that use combining characters, and care
should be taken by users to understand the potential differences if
handling text using such character combinations causes unexpected
results.
* Corrected error statuses returned for switching frames. The IE
driver now returns the correct status as specified by the W3C
WebDriver Specification for error conditions encountered when
attempting to switch frames.
* Modified to allow null value for script timeout. The W3C WebDriver
Specification has contradictory language regarding the script
timeout. On the one hand, it specifies that the value of the script
timeout can be `null`, indicating an indefinite timeout. On the
other hand, it specifies that when setting or getting timeouts, the
value of any timeout must be an integer between 0 and 2^53 - 1.
Since geckodriver has made the assumption that the former condition
is the correct interpretation, and the maintainers of geckodriver
have added a test to the W3C Web Platform Tests for WebDriver that
expects this interpretation, the IE driver is being modified to
follow that decision. It is the opinion of the developers of the IE
driver that this is the incorrect interpretation, but there is no
recourse to have the geckodriver maintainers revisit their decision.

v3.141.5.0
==========
* Added logging of JavaScript errors when executing scripts.
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,0
PRODUCTVERSION 3,141,5,0
FILEVERSION 3,141,5,1
PRODUCTVERSION 3,141,5,1
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.0"
VALUE "FileVersion", "3.141.5.1"
VALUE "InternalName", "IEDriverServer.exe"
VALUE "LegalCopyright", "Copyright (C) 2019"
VALUE "OriginalFilename", "IEDriverServer.exe"
VALUE "ProductName", "Selenium WebDriver"
VALUE "ProductVersion", "3.141.5.0"
VALUE "ProductVersion", "3.141.5.1"
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.
1 change: 1 addition & 0 deletions cpp/webdriver-server/command_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ namespace CommandType {
const std::string CloseWindow = "closeWindow";
const std::string SwitchToWindow = "switchToWindow";
const std::string GetWindowHandles = "getWindowHandles";
const std::string NewWindow = "newWindow";
const std::string SwitchToFrame = "switchToFrame";
const std::string SwitchToParentFrame = "switchToParentFrame";
const std::string GetWindowRect = "getWindowRect";
Expand Down

0 comments on commit 3c528a0

Please sign in to comment.