<commit_before>// Copyright 2011 WebDriver committers
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "Script.h"
#include "IECommandExecutor.h"
#include "logging.h"

namespace webdriver {

Script::Script(IHTMLDocument2* document, std::string script_source, unsigned long argument_count) {
	std::wstring wide_script = CA2W(script_source.c_str(), CP_UTF8);
	this->Initialize(document, wide_script, argument_count);
}

Script::Script(IHTMLDocument2* document, std::wstring script_source, unsigned long argument_count) {
	this->Initialize(document, script_source, argument_count);
}

Script::~Script(void) {
	::SafeArrayDestroy(this->argument_array_);
}

void Script::Initialize(IHTMLDocument2* document, const std::wstring& script_source, const unsigned long argument_count) {
	this->script_engine_host_ = document;
	this->source_code_ = script_source;
	this->argument_count_ = argument_count;
	this->current_arg_index_ = 0;

	SAFEARRAYBOUND argument_bounds;
	argument_bounds.lLbound = 0;
	argument_bounds.cElements = this->argument_count_;

	this->argument_array_ = ::SafeArrayCreate(VT_VARIANT, 1, &argument_bounds);

	::VariantInit(&this->result_);
}

void Script::AddArgument(const std::string& argument) {
	std::wstring wide_argument = CA2W(argument.c_str(), CP_UTF8);
	this->AddArgument(wide_argument);
}

void Script::AddArgument(const std::wstring& argument) {
	CComVariant dest_argument(argument.c_str());
	this->AddArgument(dest_argument);
}

void Script::AddArgument(const int argument) {
	CComVariant dest_argument((long)argument);
	this->AddArgument(dest_argument);
}

void Script::AddArgument(const double argument) {
	CComVariant dest_argument(argument);
	this->AddArgument(dest_argument);
}

void Script::AddArgument(const bool argument) {
	CComVariant dest_argument(argument);
	this->AddArgument(dest_argument);
}

void Script::AddArgument(ElementHandle argument) {
	this->AddArgument(argument->element());
}

void Script::AddArgument(IHTMLElement* argument) {
	CComVariant dest_argument(argument);
	this->AddArgument(dest_argument);
}

void Script::AddArgument(VARIANT argument) {
	HRESULT hr = ::SafeArrayPutElement(this->argument_array_, &this->current_arg_index_, &argument);
	++this->current_arg_index_;
}

bool Script::ResultIsString() {
	return this->result_.vt == VT_BSTR;
}

bool Script::ResultIsInteger() {
	return this->result_.vt == VT_I4 || this->result_.vt == VT_I8;
}

bool Script::ResultIsDouble() {
	return this->result_.vt == VT_R4 || this->result_.vt == VT_R8;
}

bool Script::ResultIsBoolean() {
	return this->result_.vt == VT_BOOL;
}

bool Script::ResultIsEmpty() {
	return this->result_.vt == VT_EMPTY;
}

bool Script::ResultIsIDispatch() {
	return this->result_.vt == VT_DISPATCH;
}

bool Script::ResultIsElementCollection() {
	if (this->result_.vt == VT_DISPATCH) {
		CComQIPtr<IHTMLElementCollection> is_collection(this->result_.pdispVal);
		if (is_collection) {
			return true;
		}
	}
	return false;
}

bool Script::ResultIsElement() {
	if (this->result_.vt == VT_DISPATCH) {
		CComQIPtr<IHTMLElement> is_element(this->result_.pdispVal);
		if (is_element) {
			return true;
		}
	}
	return false;
}

bool Script::ResultIsArray() {
	std::wstring type_name = this->GetResultObjectTypeName();

	// If the name is DispStaticNodeList, we can be pretty sure it's an array
	// (or at least has array semantics). It is unclear to what extent checking
	// for DispStaticNodeList is supported behaviour.
	if (type_name == L"DispStaticNodeList") {
		return true;
	}

	// If the name is JScriptTypeInfo then this *may* be a Javascript array.
	// Note that strictly speaking, to determine if the result is *actually*
	// a JavaScript array object, we should also be testing to see if
	// propertyIsEnumerable('length') == false, but that does not find the
	// array-like objects returned by some of the calls we make to the Google
	// Closure library.
	// IMPORTANT: Using this script, user-defined objects with a length
	// property defined will be seen as arrays instead of objects.
	if (type_name == L"JScriptTypeInfo") {
		const std::wstring script_source = L"(function() { return function(){ return arguments[0] && arguments[0].hasOwnProperty('length') && typeof arguments[0] === 'object' && typeof arguments[0].length === 'number';};})();";
		Script is_array_wrapper(this->script_engine_host_, script_source, 1);
		is_array_wrapper.AddArgument(this->result_);
		is_array_wrapper.Execute();
		return is_array_wrapper.result().boolVal == VARIANT_TRUE;
	}

	return false;
}

bool Script::ResultIsObject() {
	std::wstring type_name = this->GetResultObjectTypeName();
	if (type_name == L"JScriptTypeInfo") {
		return true;
	}
	return false;
}

int Script::Execute() {
	VARIANT result;
	CComVariant temp_function;
	if (!this->CreateAnonymousFunction(&temp_function)) {
		// Debug level since this is normally the point we find out that 
		// a page refresh has occured. *sigh*
		// LOG(DEBUG) << "Cannot create anonymous function: " << _bstr_t(script) << endl;
		return EUNEXPECTEDJSERROR;
	}

	if (temp_function.vt != VT_DISPATCH) {
		// No return value that we care about
		::VariantClear(&result);
		result.vt = VT_EMPTY;
		return SUCCESS;
	}

	// Grab the "call" method out of the returned function
	DISPID call_member_id;
	OLECHAR FAR* call_member_name = L"call";
	HRESULT hr = temp_function.pdispVal->GetIDsOfNames(IID_NULL, &call_member_name, 1, LOCALE_USER_DEFAULT, &call_member_id);
	if (FAILED(hr)) {
//		LOGHR(DEBUG, hr) << "Cannot locate call method on anonymous function: " << _bstr_t(script) << endl;
		return EUNEXPECTEDJSERROR;
	}

	DISPPARAMS call_parameters = { 0 };
	memset(&call_parameters, 0, sizeof call_parameters);

	long lower = 0;
	::SafeArrayGetLBound(this->argument_array_, 1, &lower);
	long upper = 0;
	::SafeArrayGetUBound(this->argument_array_, 1, &upper);
	long nargs = 1 + upper - lower;<commit_msg>JimEvans: Removing improper clearing of VARIANT type in IE native code.<commit_after>// Copyright 2011 WebDriver committers
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "Script.h"
#include "IECommandExecutor.h"
#include "logging.h"

namespace webdriver {

Script::Script(IHTMLDocument2* document, std::string script_source, unsigned long argument_count) {
	std::wstring wide_script = CA2W(script_source.c_str(), CP_UTF8);
	this->Initialize(document, wide_script, argument_count);
}

Script::Script(IHTMLDocument2* document, std::wstring script_source, unsigned long argument_count) {
	this->Initialize(document, script_source, argument_count);
}

Script::~Script(void) {
	::SafeArrayDestroy(this->argument_array_);
}

void Script::Initialize(IHTMLDocument2* document, const std::wstring& script_source, const unsigned long argument_count) {
	this->script_engine_host_ = document;
	this->source_code_ = script_source;
	this->argument_count_ = argument_count;
	this->current_arg_index_ = 0;

	SAFEARRAYBOUND argument_bounds;
	argument_bounds.lLbound = 0;
	argument_bounds.cElements = this->argument_count_;

	this->argument_array_ = ::SafeArrayCreate(VT_VARIANT, 1, &argument_bounds);

	::VariantInit(&this->result_);
}

void Script::AddArgument(const std::string& argument) {
	std::wstring wide_argument = CA2W(argument.c_str(), CP_UTF8);
	this->AddArgument(wide_argument);
}

void Script::AddArgument(const std::wstring& argument) {
	CComVariant dest_argument(argument.c_str());
	this->AddArgument(dest_argument);
}

void Script::AddArgument(const int argument) {
	CComVariant dest_argument((long)argument);
	this->AddArgument(dest_argument);
}

void Script::AddArgument(const double argument) {
	CComVariant dest_argument(argument);
	this->AddArgument(dest_argument);
}

void Script::AddArgument(const bool argument) {
	CComVariant dest_argument(argument);
	this->AddArgument(dest_argument);
}

void Script::AddArgument(ElementHandle argument) {
	this->AddArgument(argument->element());
}

void Script::AddArgument(IHTMLElement* argument) {
	CComVariant dest_argument(argument);
	this->AddArgument(dest_argument);
}

void Script::AddArgument(VARIANT argument) {
	HRESULT hr = ::SafeArrayPutElement(this->argument_array_, &this->current_arg_index_, &argument);
	++this->current_arg_index_;
}

bool Script::ResultIsString() {
	return this->result_.vt == VT_BSTR;
}

bool Script::ResultIsInteger() {
	return this->result_.vt == VT_I4 || this->result_.vt == VT_I8;
}

bool Script::ResultIsDouble() {
	return this->result_.vt == VT_R4 || this->result_.vt == VT_R8;
}

bool Script::ResultIsBoolean() {
	return this->result_.vt == VT_BOOL;
}

bool Script::ResultIsEmpty() {
	return this->result_.vt == VT_EMPTY;
}

bool Script::ResultIsIDispatch() {
	return this->result_.vt == VT_DISPATCH;
}

bool Script::ResultIsElementCollection() {
	if (this->result_.vt == VT_DISPATCH) {
		CComQIPtr<IHTMLElementCollection> is_collection(this->result_.pdispVal);
		if (is_collection) {
			return true;
		}
	}
	return false;
}

bool Script::ResultIsElement() {
	if (this->result_.vt == VT_DISPATCH) {
		CComQIPtr<IHTMLElement> is_element(this->result_.pdispVal);
		if (is_element) {
			return true;
		}
	}
	return false;
}

bool Script::ResultIsArray() {
	std::wstring type_name = this->GetResultObjectTypeName();

	// If the name is DispStaticNodeList, we can be pretty sure it's an array
	// (or at least has array semantics). It is unclear to what extent checking
	// for DispStaticNodeList is supported behaviour.
	if (type_name == L"DispStaticNodeList") {
		return true;
	}

	// If the name is JScriptTypeInfo then this *may* be a Javascript array.
	// Note that strictly speaking, to determine if the result is *actually*
	// a JavaScript array object, we should also be testing to see if
	// propertyIsEnumerable('length') == false, but that does not find the
	// array-like objects returned by some of the calls we make to the Google
	// Closure library.
	// IMPORTANT: Using this script, user-defined objects with a length
	// property defined will be seen as arrays instead of objects.
	if (type_name == L"JScriptTypeInfo") {
		const std::wstring script_source = L"(function() { return function(){ return arguments[0] && arguments[0].hasOwnProperty('length') && typeof arguments[0] === 'object' && typeof arguments[0].length === 'number';};})();";
		Script is_array_wrapper(this->script_engine_host_, script_source, 1);
		is_array_wrapper.AddArgument(this->result_);
		is_array_wrapper.Execute();
		return is_array_wrapper.result().boolVal == VARIANT_TRUE;
	}

	return false;
}

bool Script::ResultIsObject() {
	std::wstring type_name = this->GetResultObjectTypeName();
	if (type_name == L"JScriptTypeInfo") {
		return true;
	}
	return false;
}

int Script::Execute() {
	VARIANT result;
	CComVariant temp_function;
	if (!this->CreateAnonymousFunction(&temp_function)) {
		// Debug level since this is normally the point we find out that 
		// a page refresh has occured. *sigh*
		// LOG(DEBUG) << "Cannot create anonymous function: " << _bstr_t(script) << endl;
		return EUNEXPECTEDJSERROR;
	}

	if (temp_function.vt != VT_DISPATCH) {
		// No return value that we care about
		return SUCCESS;
	}

	// Grab the "call" method out of the returned function
	DISPID call_member_id;
	OLECHAR FAR* call_member_name = L"call";
	HRESULT hr = temp_function.pdispVal->GetIDsOfNames(IID_NULL, &call_member_name, 1, LOCALE_USER_DEFAULT, &call_member_id);
	if (FAILED(hr)) {
//		LOGHR(DEBUG, hr) << "Cannot locate call method on anonymous function: " << _bstr_t(script) << endl;
		return EUNEXPECTEDJSERROR;
	}

	DISPPARAMS call_parameters = { 0 };
	memset(&call_parameters, 0, sizeof call_parameters);

	long lower = 0;
	::SafeArrayGetLBound(this->argument_array_, 1, &lower);
	long upper = 0;
	::SafeArrayGetUBound(this->argument_array_, 1, &upper);
	long nargs = 1 + upper - lower;
{'fraction_non_alphanumeric': 0.12346162890961825, 'fraction_numerical': 0.005487183507094144, 'mean_word_length': 5.363092269326684, 'pattern_counts': {'":': 0, '<': 31, '<?xml version=': 0, '>': 89, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, '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': '4191030', 'n_tokens_mistral': 4129, 'n_tokens_neox': 4028, 'n_words': 1074}