forked from SeleniumHQ/selenium
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathVariantUtilities.cpp
More file actions
306 lines (273 loc) · 11.5 KB
/
Copy pathVariantUtilities.cpp
File metadata and controls
306 lines (273 loc) · 11.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
// 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 "VariantUtilities.h"
#include "IECommandExecutor.h"
#include "json.h"
#include "logging.h"
namespace webdriver {
VariantUtilities::VariantUtilities(void) {
}
VariantUtilities::~VariantUtilities(void) {
}
bool VariantUtilities::VariantIsString(VARIANT value) {
return value.vt == VT_BSTR;
}
bool VariantUtilities::VariantIsInteger(VARIANT value) {
return value.vt == VT_I4 || value.vt == VT_I8;
}
bool VariantUtilities::VariantIsDouble(VARIANT value) {
return value.vt == VT_R4 || value.vt == VT_R8;
}
bool VariantUtilities::VariantIsBoolean(VARIANT value) {
return value.vt == VT_BOOL;
}
bool VariantUtilities::VariantIsEmpty(VARIANT value) {
return value.vt == VT_EMPTY;
}
bool VariantUtilities::VariantIsIDispatch(VARIANT value) {
return value.vt == VT_DISPATCH;
}
bool VariantUtilities::VariantIsElementCollection(VARIANT value) {
if (value.vt == VT_DISPATCH) {
CComPtr<IHTMLElementCollection> is_collection;
value.pdispVal->QueryInterface<IHTMLElementCollection>(&is_collection);
if (is_collection) {
return true;
}
}
return false;
}
bool VariantUtilities::VariantIsElement(VARIANT value) {
if (value.vt == VT_DISPATCH) {
CComPtr<IHTMLElement> is_element;
value.pdispVal->QueryInterface<IHTMLElement>(&is_element);
if (is_element) {
return true;
}
}
return false;
}
bool VariantUtilities::VariantIsArray(VARIANT value) {
std::wstring type_name = GetVariantObjectTypeName(value);
// 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") {
LOG(DEBUG) << "Result type is 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") {
LOG(DEBUG) << "Result type is JScriptTypeInfo";
LPOLESTR length_property_name = L"length";
DISPID dispid_length = 0;
HRESULT hr = value.pdispVal->GetIDsOfNames(IID_NULL,
&length_property_name,
1,
LOCALE_USER_DEFAULT,
&dispid_length);
if (SUCCEEDED(hr)) {
return true;
}
}
return false;
}
bool VariantUtilities::VariantIsObject(VARIANT value) {
std::wstring type_name = GetVariantObjectTypeName(value);
if (type_name == L"JScriptTypeInfo") {
return true;
}
return false;
}
int VariantUtilities::ConvertVariantToJsonValue(const IECommandExecutor& executor,
VARIANT variant_value,
Json::Value* value) {
int status_code = WD_SUCCESS;
if (VariantIsString(variant_value)) {
std::string string_value = "";
if (variant_value.bstrVal) {
std::wstring bstr_value = variant_value.bstrVal;
string_value = StringUtilities::ToString(bstr_value);
}
*value = string_value;
} else if (VariantIsInteger(variant_value)) {
*value = variant_value.lVal;
} else if (VariantIsDouble(variant_value)) {
*value = variant_value.dblVal;
} else if (VariantIsBoolean(variant_value)) {
*value = variant_value.boolVal == VARIANT_TRUE;
} else if (VariantIsEmpty(variant_value)) {
*value = Json::Value::null;
} else if (variant_value.vt == VT_NULL) {
*value = Json::Value::null;
} else if (VariantIsIDispatch(variant_value)) {
if (VariantIsArray(variant_value) ||
VariantIsElementCollection(variant_value)) {
Json::Value result_array(Json::arrayValue);
long length = 0;
status_code = GetArrayLength(variant_value.pdispVal, &length);
for (long i = 0; i < length; ++i) {
Json::Value array_item_result;
int array_item_status = GetArrayItem(executor,
variant_value.pdispVal,
i,
&array_item_result);
result_array[i] = array_item_result;
}
*value = result_array;
} else if (VariantIsObject(variant_value)) {
Json::Value result_object;
std::vector<std::wstring> property_names;
status_code = GetPropertyNameList(variant_value.pdispVal,
&property_names);
for (size_t i = 0; i < property_names.size(); ++i) {
CComVariant property_value_variant;
GetVariantObjectPropertyValue(variant_value.pdispVal,
property_names[i],
&property_value_variant);
Json::Value property_value;
ConvertVariantToJsonValue(executor,
property_value_variant,
&property_value);
std::string name = StringUtilities::ToString(property_names[i]);
result_object[name] = property_value;
}
*value = result_object;
} else {
LOG(INFO) << "Unknown type of dispatch is found in result, assuming IHTMLElement";
IECommandExecutor& mutable_executor = const_cast<IECommandExecutor&>(executor);
CComPtr<IHTMLElement> node;
variant_value.pdispVal->QueryInterface<IHTMLElement>(&node);
ElementHandle element_wrapper;
mutable_executor.AddManagedElement(node, &element_wrapper);
*value = element_wrapper->ConvertToJson();
}
} else {
LOG(WARN) << "Unknown type of result is found";
status_code = EUNKNOWNSCRIPTRESULT;
}
return status_code;
}
bool VariantUtilities::GetVariantObjectPropertyValue(IDispatch* variant_object_dispatch,
std::wstring property_name,
VARIANT* property_value) {
LPOLESTR property_name_pointer = reinterpret_cast<LPOLESTR>(const_cast<wchar_t*>(property_name.data()));
DISPID dispid_property;
HRESULT hr = variant_object_dispatch->GetIDsOfNames(IID_NULL,
&property_name_pointer,
1,
LOCALE_USER_DEFAULT,
&dispid_property);
if (FAILED(hr)) {
LOGHR(WARN, hr) << "Unable to get dispatch ID (dispid) for property "
<< StringUtilities::ToString(property_name);
return false;
}
// get the value of eval result
DISPPARAMS no_args_dispatch_parameters = { 0 };
hr = variant_object_dispatch->Invoke(dispid_property,
IID_NULL,
LOCALE_USER_DEFAULT,
DISPATCH_PROPERTYGET,
&no_args_dispatch_parameters,
property_value,
NULL,
NULL);
if (FAILED(hr)) {
LOGHR(WARN, hr) << "Unable to get result for property "
<< StringUtilities::ToString(property_name);
return false;
}
return true;
}
std::wstring VariantUtilities::GetVariantObjectTypeName(VARIANT value) {
std::wstring name = L"";
if (value.vt == VT_DISPATCH && value.pdispVal) {
CComPtr<ITypeInfo> typeinfo;
HRESULT get_type_info_result = value.pdispVal->GetTypeInfo(0,
LOCALE_USER_DEFAULT,
&typeinfo);
TYPEATTR* type_attr;
CComBSTR name_bstr;
if (SUCCEEDED(get_type_info_result) &&
SUCCEEDED(typeinfo->GetTypeAttr(&type_attr)) &&
SUCCEEDED(typeinfo->GetDocumentation(-1, &name_bstr, 0, 0, 0))) {
typeinfo->ReleaseTypeAttr(type_attr);
name = name_bstr.Copy();
} else {
LOG(WARN) << "Unable to get object type";
}
} else {
LOG(DEBUG) << "Unable to get object type for non-object result, result is not IDispatch or IDispatch pointer is NULL";
}
return name;
}
int VariantUtilities::GetPropertyNameList(IDispatch* object_dispatch,
std::vector<std::wstring>* property_names) {
LOG(TRACE) << "Entering Script::GetPropertyNameList";
CComPtr<IDispatchEx> dispatchex;
HRESULT hr = object_dispatch->QueryInterface<IDispatchEx>(&dispatchex);
DISPID current_disp_id;
hr = dispatchex->GetNextDispID(fdexEnumAll, DISPID_STARTENUM, ¤t_disp_id);
while (hr == S_OK) {
CComBSTR member_name_bstr;
dispatchex->GetMemberName(current_disp_id, &member_name_bstr);
std::wstring member_name = member_name_bstr;
property_names->push_back(member_name);
hr = dispatchex->GetNextDispID(fdexEnumAll, current_disp_id, ¤t_disp_id);
}
return WD_SUCCESS;
}
int VariantUtilities::GetArrayLength(IDispatch* array_dispatch, long* length) {
LOG(TRACE) << "Entering Script::GetArrayLength";
CComVariant length_result;
bool get_length_success = GetVariantObjectPropertyValue(array_dispatch,
L"length",
&length_result);
if (!get_length_success) {
// Failure already logged by GetVariantObjectPropertyValue
return EUNEXPECTEDJSERROR;
}
*length = length_result.lVal;
return WD_SUCCESS;
}
int VariantUtilities::GetArrayItem(const IECommandExecutor& executor,
IDispatch* array_dispatch,
long index,
Json::Value* item){
LOG(TRACE) << "Entering Script::GetArrayItem";
std::wstring index_string = std::to_wstring(static_cast<long long>(index));
CComVariant array_item_variant;
bool get_array_item_success = GetVariantObjectPropertyValue(array_dispatch,
index_string,
&array_item_variant);
if (!get_array_item_success) {
// Failure already logged by GetVariantObjectPropertyValue
return EUNEXPECTEDJSERROR;
}
int array_item_status = ConvertVariantToJsonValue(executor,
array_item_variant,
item);
return WD_SUCCESS;
}
} // namespace webdriver