<commit_before>#include "StdAfx.h"
#include "BrowserFactory.h"

namespace webdriver {

BrowserFactory::BrowserFactory(void) {
	this->GetExecutableLocation();
	this->GetIEVersion();
	this->GetOSVersion();
	this->html_getobject_msg_ = ::RegisterWindowMessage(L"WM_HTML_GETOBJECT");

	// Explicitly load MSAA so we know if it's installed
	this->oleacc_instance_handle_ = ::LoadLibrary(L"OLEACC.DLL");
}

BrowserFactory::~BrowserFactory(void) {
	if (this->oleacc_instance_handle_) {
		::FreeLibrary(this->oleacc_instance_handle_);
	}
}

DWORD BrowserFactory::LaunchBrowserProcess(int port) {
	DWORD process_id = NULL;
	STARTUPINFO start_info;
	PROCESS_INFORMATION proc_info;

	::ZeroMemory(&start_info, sizeof(start_info));
    start_info.cb = sizeof(start_info);
	::ZeroMemory(&proc_info, sizeof(proc_info));

	std::wstringstream url_stream;
	url_stream << L"http://localhost:" << port << L"/";
	std::wstring initial_url(url_stream.str());

	FARPROC proc_address = 0;
	HMODULE library_handle = ::LoadLibrary(L"ieframe.dll");
	if (library_handle != NULL) {
		proc_address = ::GetProcAddress(library_handle, "IELaunchURL");
	}

	if (proc_address != 0) {
		// If we have the IELaunchURL API, expressly use it. This will
		// guarantee a new session. Simply using CoCreateInstance to 
		// create the browser will merge sessions, making separate cookie
		// handling impossible.
		::IELaunchURL(initial_url.c_str(), &proc_info, NULL);
	} else {
		std::wstring executable_and_url = this->ie_executable_location_ + L" " + initial_url;
		LPWSTR command_line = new WCHAR[executable_and_url.size() + 1];
		wcscpy_s(command_line, executable_and_url.size() + 1, executable_and_url.c_str());
		command_line[executable_and_url.size()] = L'\0';
		::CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL, &start_info, &proc_info);
		delete[] command_line;
	}

	process_id = proc_info.dwProcessId;

	if (proc_info.hThread != NULL) {
		::CloseHandle(proc_info.hThread);
	}

	if (proc_info.hProcess != NULL) {
		::CloseHandle(proc_info.hProcess);
	}

	if (library_handle != NULL) {
		::FreeLibrary(library_handle);
	}

	return process_id;
}

void BrowserFactory::AttachToBrowser(ProcessWindowInfo* process_window_info) {
	while (process_window_info->hwndBrowser == NULL) {
		// TODO: create a timeout for this. We shouldn't need it, since
		// we got a valid process ID, but we should bulletproof it.
		::EnumWindows(&BrowserFactory::FindBrowserWindow, reinterpret_cast<LPARAM>(process_window_info));
		if (process_window_info->hwndBrowser == NULL) {
			::Sleep(250);
		}
	}

	if (process_window_info->hwndBrowser != NULL) {
		if (this->oleacc_instance_handle_) {
			CComPtr<IHTMLDocument2> document;
			LRESULT result;
			::SendMessageTimeout(process_window_info->hwndBrowser, this->html_getobject_msg_, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&result);

			LPFNOBJECTFROMLRESULT object_pointer =  reinterpret_cast<LPFNOBJECTFROMLRESULT>(::GetProcAddress(this->oleacc_instance_handle_, "ObjectFromLresult"));
			if (object_pointer != NULL) {
				HRESULT hr;
				hr = (*object_pointer)(result, IID_IHTMLDocument2, 0, reinterpret_cast<void**>(&document));
				if (SUCCEEDED(hr)) {
				   CComPtr<IHTMLWindow2> window;
				   hr = document->get_parentWindow(&window);
				   if (SUCCEEDED(hr)) {
						// http://support.microsoft.com/kb/257717
						CComQIPtr<IServiceProvider> provider(window);
						if (provider) {
							CComPtr<IServiceProvider> child_provider;
							hr = provider->QueryService(SID_STopLevelBrowser, IID_IServiceProvider, reinterpret_cast<void**>(&child_provider));
							if (SUCCEEDED(hr)) {
								IWebBrowser2* browser;
								hr = child_provider->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, reinterpret_cast<void**>(&browser));
								if (SUCCEEDED(hr)) {
									process_window_info->pBrowser = browser;
								}
							}
						}
				   }
				}
			}
		}
	} // else Active Accessibility is not installed
}

IWebBrowser2* BrowserFactory::CreateBrowser() {
	// TODO: Error and exception handling and return value checking.
	IWebBrowser2* browser;
	if (this->windows_major_version_ >= 6) {
		// Only Windows Vista and above have mandatory integrity levels.
		this->SetThreadIntegrityLevel();
	}

	DWORD context = CLSCTX_LOCAL_SERVER;
	if (this->ie_major_version_ == 7 && this->windows_major_version_ >= 6) {
		// ONLY for IE 7 on Windows Vista. XP and below do not have Protected Mode;
		// Windows 7 shipped with IE8.
		context = context | CLSCTX_ENABLE_CLOAKING;
	}

	::CoCreateInstance(CLSID_InternetExplorer, NULL, context, IID_IWebBrowser2, reinterpret_cast<void**>(&browser));
	browser->put_Visible(VARIANT_TRUE);

	if (this->windows_major_version_ >= 6) {
		// Only Windows Vista and above have mandatory integrity levels.
		this->ResetThreadIntegrityLevel();
	}

	return browser;
}


void BrowserFactory::SetThreadIntegrityLevel() {
	// TODO: Error handling and return value checking.
	HANDLE process_token = NULL;
	HANDLE process_handle = ::GetCurrentProcess();
	BOOL result = ::OpenProcessToken(process_handle, TOKEN_DUPLICATE, &process_token);

	HANDLE thread_token = NULL;
	result = ::DuplicateTokenEx(
		process_token, 
		TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_ADJUST_DEFAULT,
		NULL, 
		SecurityImpersonation,
		TokenImpersonation,
		&thread_token);

	PSID sid = NULL;
	result = ::ConvertStringSidToSid(SDDL_ML_LOW, &sid);

	TOKEN_MANDATORY_LABEL tml;
	tml.Label.Attributes = SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED;
	tml.Label.Sid = sid;

	result = ::SetTokenInformation(thread_token, TokenIntegrityLevel, &tml, sizeof(tml) + ::GetLengthSid(sid));
	::LocalFree(sid);

	HANDLE thread_handle = ::GetCurrentThread();
	result = ::SetThreadToken(&thread_handle, thread_token);
	result = ::ImpersonateLoggedOnUser(thread_token);

	result = ::CloseHandle(thread_token);
	result = ::CloseHandle(process_token);
}

void BrowserFactory::ResetThreadIntegrityLevel() {
	::RevertToSelf();
}

HWND BrowserFactory::GetTabWindowHandle(IWebBrowser2* browser) {
	ProcessWindowInfo process_window_info;
	process_window_info.pBrowser = browser;
	process_window_info.hwndBrowser = NULL;

	HWND hwnd = NULL;
	CComQIPtr<IServiceProvider> service_provider;
	HRESULT hr = browser->QueryInterface(IID_IServiceProvider, reinterpret_cast<void**>(&service_provider));
	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,
			// and the top-level window frame in IE 6. The window
			// we need is the InternetExplorer_Server window.
			window->GetWindow(&hwnd);

			DWORD process_id;
			::GetWindowThreadProcessId(hwnd, &process_id);
			process_window_info.dwProcessId = process_id;

			::EnumChildWindows(hwnd, &BrowserFactory::FindChildWindowForProcess, (LPARAM)&process_window_info);
			hwnd = process_window_info.hwndBrowser;
		}
	}

	return hwnd;
}

BOOL CALLBACK BrowserFactory::FindBrowserWindow(HWND hwnd, LPARAM arg) {
	// Could this be an IE instance?
	// 8 == "IeFrame\0"
	// 21 == "Shell DocObject View\0";
	char name[21];
	if (GetClassNameA(hwnd, name, 21) == 0) {
		// No match found. Skip
		return TRUE;
	}
	
	if (strcmp("IEFrame", name) != 0 && strcmp("Shell DocObject View", name) != 0) {
		return TRUE;
	}

	return EnumChildWindows(hwnd, FindChildWindowForProcess, arg);
}

BOOL CALLBACK BrowserFactory::FindChildWindowForProcess(HWND hwnd, LPARAM arg) {
	ProcessWindowInfo *process_window_info = reinterpret_cast<ProcessWindowInfo*>(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) {
		return TRUE;
	} else {
		DWORD process_id = NULL;
		::GetWindowThreadProcessId(hwnd, &process_id);
		if (process_window_info->dwProcessId == process_id) {
			// Once we've found the first Internet Explorer_Server window
			// for the process we want, we can stop.
			process_window_info->hwndBrowser = hwnd;
			return FALSE;
		}
	}

	return TRUE;
}

BOOL CALLBACK BrowserFactory::FindDialogWindowForProcess(HWND hwnd, LPARAM arg) {
	ProcessWindowInfo* process_win_info = reinterpret_cast<ProcessWindowInfo*>(arg);

	// Could this be an dialog window?
	// 7 == "#32770\0"
	char name[7];
	if (GetClassNameA(hwnd, name, 7) == 0) {
		// No match found. Skip
		return TRUE;
	}
	
	if (strcmp("#32770", name) != 0) {
		return TRUE;
	} else {
		DWORD process_id = NULL;
		::GetWindowThreadProcessId(hwnd, &process_id);
		if (process_win_info->dwProcessId == process_id) {
			// Once we've found the first dialog (#32770) window
			// for the process we want, we can stop.
			process_win_info->hwndBrowser = hwnd;<commit_msg>JimEvans: IE native code should no longer hang when presented with a window created by showModalDialog().<commit_after>#include "StdAfx.h"
#include "BrowserFactory.h"

namespace webdriver {

BrowserFactory::BrowserFactory(void) {
	this->GetExecutableLocation();
	this->GetIEVersion();
	this->GetOSVersion();
	this->html_getobject_msg_ = ::RegisterWindowMessage(L"WM_HTML_GETOBJECT");

	// Explicitly load MSAA so we know if it's installed
	this->oleacc_instance_handle_ = ::LoadLibrary(L"OLEACC.DLL");
}

BrowserFactory::~BrowserFactory(void) {
	if (this->oleacc_instance_handle_) {
		::FreeLibrary(this->oleacc_instance_handle_);
	}
}

DWORD BrowserFactory::LaunchBrowserProcess(int port) {
	DWORD process_id = NULL;
	STARTUPINFO start_info;
	PROCESS_INFORMATION proc_info;

	::ZeroMemory(&start_info, sizeof(start_info));
    start_info.cb = sizeof(start_info);
	::ZeroMemory(&proc_info, sizeof(proc_info));

	std::wstringstream url_stream;
	url_stream << L"http://localhost:" << port << L"/";
	std::wstring initial_url(url_stream.str());

	FARPROC proc_address = 0;
	HMODULE library_handle = ::LoadLibrary(L"ieframe.dll");
	if (library_handle != NULL) {
		proc_address = ::GetProcAddress(library_handle, "IELaunchURL");
	}

	if (proc_address != 0) {
		// If we have the IELaunchURL API, expressly use it. This will
		// guarantee a new session. Simply using CoCreateInstance to 
		// create the browser will merge sessions, making separate cookie
		// handling impossible.
		::IELaunchURL(initial_url.c_str(), &proc_info, NULL);
	} else {
		std::wstring executable_and_url = this->ie_executable_location_ + L" " + initial_url;
		LPWSTR command_line = new WCHAR[executable_and_url.size() + 1];
		wcscpy_s(command_line, executable_and_url.size() + 1, executable_and_url.c_str());
		command_line[executable_and_url.size()] = L'\0';
		::CreateProcess(NULL, command_line, NULL, NULL, FALSE, 0, NULL, NULL, &start_info, &proc_info);
		delete[] command_line;
	}

	process_id = proc_info.dwProcessId;

	if (proc_info.hThread != NULL) {
		::CloseHandle(proc_info.hThread);
	}

	if (proc_info.hProcess != NULL) {
		::CloseHandle(proc_info.hProcess);
	}

	if (library_handle != NULL) {
		::FreeLibrary(library_handle);
	}

	return process_id;
}

void BrowserFactory::AttachToBrowser(ProcessWindowInfo* process_window_info) {
	while (process_window_info->hwndBrowser == NULL) {
		// TODO: create a timeout for this. We shouldn't need it, since
		// we got a valid process ID, but we should bulletproof it.
		::EnumWindows(&BrowserFactory::FindBrowserWindow, reinterpret_cast<LPARAM>(process_window_info));
		if (process_window_info->hwndBrowser == NULL) {
			::Sleep(250);
		}
	}

	if (process_window_info->hwndBrowser != NULL) {
		if (this->oleacc_instance_handle_) {
			CComPtr<IHTMLDocument2> document;
			LRESULT result;
			::SendMessageTimeout(process_window_info->hwndBrowser, this->html_getobject_msg_, 0L, 0L, SMTO_ABORTIFHUNG, 1000, (PDWORD_PTR)&result);

			LPFNOBJECTFROMLRESULT object_pointer =  reinterpret_cast<LPFNOBJECTFROMLRESULT>(::GetProcAddress(this->oleacc_instance_handle_, "ObjectFromLresult"));
			if (object_pointer != NULL) {
				HRESULT hr;
				hr = (*object_pointer)(result, IID_IHTMLDocument2, 0, reinterpret_cast<void**>(&document));
				if (SUCCEEDED(hr)) {
				   CComPtr<IHTMLWindow2> window;
				   hr = document->get_parentWindow(&window);
				   if (SUCCEEDED(hr)) {
						// http://support.microsoft.com/kb/257717
						CComQIPtr<IServiceProvider> provider(window);
						if (provider) {
							CComPtr<IServiceProvider> child_provider;
							hr = provider->QueryService(SID_STopLevelBrowser, IID_IServiceProvider, reinterpret_cast<void**>(&child_provider));
							if (SUCCEEDED(hr)) {
								IWebBrowser2* browser;
								hr = child_provider->QueryService(SID_SWebBrowserApp, IID_IWebBrowser2, reinterpret_cast<void**>(&browser));
								if (SUCCEEDED(hr)) {
									process_window_info->pBrowser = browser;
								}
							}
						}
				   }
				}
			}
		}
	} // else Active Accessibility is not installed
}

IWebBrowser2* BrowserFactory::CreateBrowser() {
	// TODO: Error and exception handling and return value checking.
	IWebBrowser2* browser;
	if (this->windows_major_version_ >= 6) {
		// Only Windows Vista and above have mandatory integrity levels.
		this->SetThreadIntegrityLevel();
	}

	DWORD context = CLSCTX_LOCAL_SERVER;
	if (this->ie_major_version_ == 7 && this->windows_major_version_ >= 6) {
		// ONLY for IE 7 on Windows Vista. XP and below do not have Protected Mode;
		// Windows 7 shipped with IE8.
		context = context | CLSCTX_ENABLE_CLOAKING;
	}

	::CoCreateInstance(CLSID_InternetExplorer, NULL, context, IID_IWebBrowser2, reinterpret_cast<void**>(&browser));
	browser->put_Visible(VARIANT_TRUE);

	if (this->windows_major_version_ >= 6) {
		// Only Windows Vista and above have mandatory integrity levels.
		this->ResetThreadIntegrityLevel();
	}

	return browser;
}


void BrowserFactory::SetThreadIntegrityLevel() {
	// TODO: Error handling and return value checking.
	HANDLE process_token = NULL;
	HANDLE process_handle = ::GetCurrentProcess();
	BOOL result = ::OpenProcessToken(process_handle, TOKEN_DUPLICATE, &process_token);

	HANDLE thread_token = NULL;
	result = ::DuplicateTokenEx(
		process_token, 
		TOKEN_QUERY | TOKEN_IMPERSONATE | TOKEN_ADJUST_DEFAULT,
		NULL, 
		SecurityImpersonation,
		TokenImpersonation,
		&thread_token);

	PSID sid = NULL;
	result = ::ConvertStringSidToSid(SDDL_ML_LOW, &sid);

	TOKEN_MANDATORY_LABEL tml;
	tml.Label.Attributes = SE_GROUP_INTEGRITY | SE_GROUP_INTEGRITY_ENABLED;
	tml.Label.Sid = sid;

	result = ::SetTokenInformation(thread_token, TokenIntegrityLevel, &tml, sizeof(tml) + ::GetLengthSid(sid));
	::LocalFree(sid);

	HANDLE thread_handle = ::GetCurrentThread();
	result = ::SetThreadToken(&thread_handle, thread_token);
	result = ::ImpersonateLoggedOnUser(thread_token);

	result = ::CloseHandle(thread_token);
	result = ::CloseHandle(process_token);
}

void BrowserFactory::ResetThreadIntegrityLevel() {
	::RevertToSelf();
}

HWND BrowserFactory::GetTabWindowHandle(IWebBrowser2* browser) {
	ProcessWindowInfo process_window_info;
	process_window_info.pBrowser = browser;
	process_window_info.hwndBrowser = NULL;

	HWND hwnd = NULL;
	CComQIPtr<IServiceProvider> service_provider;
	HRESULT hr = browser->QueryInterface(IID_IServiceProvider, reinterpret_cast<void**>(&service_provider));
	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,
			// and the top-level window frame in IE 6. The window
			// we need is the InternetExplorer_Server window.
			window->GetWindow(&hwnd);

			DWORD process_id;
			::GetWindowThreadProcessId(hwnd, &process_id);
			process_window_info.dwProcessId = process_id;

			::EnumChildWindows(hwnd, &BrowserFactory::FindChildWindowForProcess, (LPARAM)&process_window_info);
			hwnd = process_window_info.hwndBrowser;
		}
	}

	return hwnd;
}

BOOL CALLBACK BrowserFactory::FindBrowserWindow(HWND hwnd, LPARAM arg) {
	// Could this be an IE instance?
	// 8 == "IeFrame\0"
	// 21 == "Shell DocObject View\0";
	char name[21];
	if (GetClassNameA(hwnd, name, 21) == 0) {
		// No match found. Skip
		return TRUE;
	}
	
	if (strcmp("IEFrame", name) != 0 && strcmp("Shell DocObject View", name) != 0) {
		return TRUE;
	}

	return EnumChildWindows(hwnd, FindChildWindowForProcess, arg);
}

BOOL CALLBACK BrowserFactory::FindChildWindowForProcess(HWND hwnd, LPARAM arg) {
	ProcessWindowInfo *process_window_info = reinterpret_cast<ProcessWindowInfo*>(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) {
		return TRUE;
	} else {
		DWORD process_id = NULL;
		::GetWindowThreadProcessId(hwnd, &process_id);
		if (process_window_info->dwProcessId == process_id) {
			// Once we've found the first Internet Explorer_Server window
			// for the process we want, we can stop.
			process_window_info->hwndBrowser = hwnd;
			return FALSE;
		}
	}

	return TRUE;
}

BOOL CALLBACK BrowserFactory::FindDialogWindowForProcess(HWND hwnd, LPARAM arg) {
	ProcessWindowInfo* process_win_info = reinterpret_cast<ProcessWindowInfo*>(arg);

	// Could this be an dialog window?
	// 7 == "#32770\0"
	char name[7];
	if (GetClassNameA(hwnd, name, 7) == 0) {
		// No match found. Skip
		return TRUE;
	}
	
	if (strcmp("#32770", name) != 0 && strcmp("Internet Explorer_TridentDlgFrame", name) != 0){
		return TRUE;
	} else {
		DWORD process_id = NULL;
		::GetWindowThreadProcessId(hwnd, &process_id);
		if (process_win_info->dwProcessId == process_id) {
			// Once we've found the first dialog (#32770) window
			// for the process we want, we can stop.
			process_win_info->hwndBrowser = hwnd;
{'fraction_non_alphanumeric': 0.12207704816912465, 'fraction_numerical': 0.00936466102170134, 'mean_word_length': 5.008760107816712, 'pattern_counts': {'":': 0, '<': 47, '<?xml version=': 0, '>': 107, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5301164', 'n_tokens_mistral': 6615, 'n_tokens_neox': 5976, 'n_words': 1296}