"Fossies" - the Fresh Open Source Software Archive 
Member "selenium-selenium-4.8.1/cpp/iedriver/VariantUtilities.cpp" (17 Feb 2023, 22489 Bytes) of package /linux/www/selenium-selenium-4.8.1.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
For more information about "VariantUtilities.cpp" see the
Fossies "Dox" file reference documentation.
1 // Licensed to the Software Freedom Conservancy (SFC) under one
2 // or more contributor license agreements. See the NOTICE file
3 // distributed with this work for additional information
4 // regarding copyright ownership. The SFC licenses this file
5 // to you under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16
17 #include "VariantUtilities.h"
18
19 #include "errorcodes.h"
20 #include "json.h"
21 #include "logging.h"
22
23 #include "Element.h"
24 #include "IECommandExecutor.h"
25 #include "Script.h"
26 #include "StringUtilities.h"
27
28 namespace webdriver {
29 VariantUtilities::VariantUtilities(void) {
30 }
31
32 VariantUtilities::~VariantUtilities(void) {
33 }
34
35 bool VariantUtilities::VariantIsString(VARIANT value) {
36 return value.vt == VT_BSTR;
37 }
38
39 bool VariantUtilities::VariantIsInteger(VARIANT value) {
40 return value.vt == VT_I4 || value.vt == VT_I8;
41 }
42
43 bool VariantUtilities::VariantIsDouble(VARIANT value) {
44 return value.vt == VT_R4 || value.vt == VT_R8;
45 }
46
47 bool VariantUtilities::VariantIsBoolean(VARIANT value) {
48 return value.vt == VT_BOOL;
49 }
50
51 bool VariantUtilities::VariantIsEmpty(VARIANT value) {
52 return value.vt == VT_EMPTY;
53 }
54
55 bool VariantUtilities::VariantIsIDispatch(VARIANT value) {
56 return value.vt == VT_DISPATCH;
57 }
58
59 bool VariantUtilities::VariantIsElementCollection(VARIANT value) {
60 if (value.vt == VT_DISPATCH) {
61 CComPtr<IHTMLElementCollection> is_collection;
62 value.pdispVal->QueryInterface<IHTMLElementCollection>(&is_collection);
63 if (is_collection) {
64 return true;
65 }
66 }
67 return false;
68 }
69
70 bool VariantUtilities::VariantIsElement(VARIANT value) {
71 if (value.vt == VT_DISPATCH) {
72 CComPtr<IHTMLElement> is_element;
73 value.pdispVal->QueryInterface<IHTMLElement>(&is_element);
74 if (is_element) {
75 return true;
76 }
77 }
78 return false;
79 }
80
81 bool VariantUtilities::VariantIsArray(VARIANT value) {
82 if (value.vt != VT_DISPATCH) {
83 return false;
84 }
85
86 std::wstring type_name = GetVariantObjectTypeName(value);
87
88 // If the name is DispStaticNodeList, we can be pretty sure it's an array
89 // (or at least has array semantics). It is unclear to what extent checking
90 // for DispStaticNodeList is supported behaviour.
91 if (type_name == L"DispStaticNodeList") {
92 LOG(DEBUG) << "Result type is DispStaticNodeList";
93 return true;
94 }
95
96 // If the name is JScriptTypeInfo then this *may* be a Javascript array.
97 // Note that strictly speaking, to determine if the result is *actually*
98 // a JavaScript array object, we should also be testing to see if
99 // propertyIsEnumerable('length') == false, but that does not find the
100 // array-like objects returned by some of the calls we make to the Google
101 // Closure library.
102 // IMPORTANT: Using this script, user-defined objects with a length
103 // property defined will be seen as arrays instead of objects.
104 if (type_name == L"JScriptTypeInfo" || type_name == L"") {
105 LOG(DEBUG) << "Result type is JScriptTypeInfo";
106 LPOLESTR length_property_name = L"length";
107 DISPID dispid_length = 0;
108 HRESULT hr = value.pdispVal->GetIDsOfNames(IID_NULL,
109 &length_property_name,
110 1,
111 LOCALE_USER_DEFAULT,
112 &dispid_length);
113 if (SUCCEEDED(hr)) {
114 return true;
115 }
116 }
117
118 return false;
119 }
120
121 bool VariantUtilities::VariantIsObject(VARIANT value) {
122 if (value.vt != VT_DISPATCH) {
123 return false;
124 }
125 std::wstring type_name = GetVariantObjectTypeName(value);
126 if (type_name == L"JScriptTypeInfo") {
127 return true;
128 }
129 return false;
130 }
131
132 int VariantUtilities::VariantAsJsonValue(IElementManager* element_manager,
133 VARIANT variant_value,
134 Json::Value* value) {
135 std::vector<IDispatch*> visited;
136 if (HasSelfReferences(variant_value, &visited)) {
137 return EUNEXPECTEDJSERROR;
138 }
139 return ConvertVariantToJsonValue(element_manager, variant_value, value);
140 }
141
142 bool VariantUtilities::HasSelfReferences(VARIANT current_object,
143 std::vector<IDispatch*>* visited) {
144 int status_code = WD_SUCCESS;
145 bool has_self_references = false;
146 if (VariantIsArray(current_object) || VariantIsObject(current_object)) {
147 std::vector<std::wstring> property_names;
148 if (VariantIsArray(current_object)) {
149 long length = 0;
150 status_code = GetArrayLength(current_object.pdispVal, &length);
151 for (long index = 0; index < length; ++index) {
152 std::wstring index_string = std::to_wstring(static_cast<long long>(index));
153 property_names.push_back(index_string);
154 }
155 } else {
156 status_code = GetPropertyNameList(current_object.pdispVal,
157 &property_names);
158 }
159
160 visited->push_back(current_object.pdispVal);
161 for (size_t i = 0; i < property_names.size(); ++i) {
162 CComVariant property_value;
163 GetVariantObjectPropertyValue(current_object.pdispVal,
164 property_names[i],
165 &property_value);
166 if (VariantIsIDispatch(property_value)) {
167 for (size_t i = 0; i < visited->size(); ++i) {
168 CComPtr<IDispatch> visited_dispatch((*visited)[i]);
169 if (visited_dispatch.IsEqualObject(property_value.pdispVal)) {
170 return true;
171 }
172 }
173 has_self_references = has_self_references || HasSelfReferences(property_value, visited);
174 if (has_self_references) {
175 break;
176 }
177 }
178 }
179 visited->pop_back();
180 }
181 return has_self_references;
182 }
183
184 int VariantUtilities::ConvertVariantToJsonValue(IElementManager* element_manager,
185 VARIANT variant_value,
186 Json::Value* value) {
187 int status_code = WD_SUCCESS;
188 if (VariantIsString(variant_value)) {
189 std::string string_value = "";
190 if (variant_value.bstrVal) {
191 std::wstring bstr_value = variant_value.bstrVal;
192 string_value = StringUtilities::ToString(bstr_value);
193 }
194 *value = string_value;
195 } else if (VariantIsInteger(variant_value)) {
196 *value = variant_value.lVal;
197 } else if (VariantIsDouble(variant_value)) {
198 double int_part;
199 if (std::modf(variant_value.dblVal, &int_part) == 0.0) {
200 // This bears some explaining. Due to inconsistencies between versions
201 // of the JSON serializer we use, if the value is floating-point, but
202 // has no fractional part, convert it to a 64-bit integer so that it
203 // will be serialized in a way consistent with language bindings'
204 // expectations.
205 *value = static_cast<long long>(int_part);
206 } else {
207 *value = variant_value.dblVal;
208 }
209 } else if (VariantIsBoolean(variant_value)) {
210 *value = variant_value.boolVal == VARIANT_TRUE;
211 } else if (VariantIsEmpty(variant_value)) {
212 *value = Json::Value::null;
213 } else if (variant_value.vt == VT_NULL) {
214 *value = Json::Value::null;
215 } else if (VariantIsIDispatch(variant_value)) {
216 if (VariantIsArray(variant_value) ||
217 VariantIsElementCollection(variant_value)) {
218 Json::Value result_array(Json::arrayValue);
219
220 long length = 0;
221 status_code = GetArrayLength(variant_value.pdispVal, &length);
222 if (status_code != WD_SUCCESS) {
223 LOG(WARN) << "Did not successfully get array length.";
224 return EUNEXPECTEDJSERROR;
225 }
226
227 for (long i = 0; i < length; ++i) {
228 CComVariant array_item;
229 int array_item_status = GetArrayItem(variant_value.pdispVal,
230 i,
231 &array_item);
232 if (array_item_status != WD_SUCCESS) {
233 LOG(WARN) << "Did not successfully get item with index "
234 << i << " from array.";
235 return EUNEXPECTEDJSERROR;
236 }
237 Json::Value array_item_result;
238 ConvertVariantToJsonValue(element_manager,
239 array_item,
240 &array_item_result);
241 result_array[i] = array_item_result;
242 }
243 *value = result_array;
244 } else if (VariantIsObject(variant_value)) {
245 Json::Value result_object(Json::objectValue);
246 CComVariant json_serialized;
247 if (ExecuteToJsonMethod(variant_value, &json_serialized)) {
248 ConvertVariantToJsonValue(element_manager, json_serialized, &result_object);
249 } else {
250 int property_enum_status = GetAllVariantObjectPropertyValues(element_manager,
251 variant_value,
252 &result_object);
253 if (property_enum_status != WD_SUCCESS) {
254 return EUNEXPECTEDJSERROR;
255 }
256 }
257 *value = result_object;
258 } else {
259 CComPtr<IHTMLElement> node;
260 HRESULT hr = variant_value.pdispVal->QueryInterface<IHTMLElement>(&node);
261 if (FAILED(hr)) {
262 LOG(DEBUG) << "Unknown type of dispatch not IHTMLElement, checking for IHTMLWindow2";
263 CComPtr<IHTMLWindow2> window_node;
264 hr = variant_value.pdispVal->QueryInterface<IHTMLWindow2>(&window_node);
265 if (SUCCEEDED(hr) && window_node) {
266 // TODO: We need to track window objects and return a custom JSON
267 // object according to the spec, but that will require a fair
268 // amount of refactoring.
269 LOG(WARN) << "Returning window object from JavaScript is not supported";
270 return EUNEXPECTEDJSERROR;
271 }
272
273 LOG(DEBUG) << "Unknown type of dispatch not IHTMLWindow2, checking for toJSON function";
274 CComVariant json_serialized_variant;
275 if (ExecuteToJsonMethod(variant_value, &json_serialized_variant)) {
276 Json::Value interim_value;
277 ConvertVariantToJsonValue(element_manager,
278 json_serialized_variant,
279 &interim_value);
280 *value = interim_value;
281 return WD_SUCCESS;
282 }
283
284 UINT typeinfo_count = 0;
285 variant_value.pdispVal->GetTypeInfoCount(&typeinfo_count);
286 if (typeinfo_count != 0) {
287 LOG(DEBUG) << "Unknown type of dispatch with no toJSON function, "
288 << "trying to blindly enumerate properties";
289 Json::Value final_result_object;
290 int property_enum_status = GetAllVariantObjectPropertyValues(element_manager,
291 variant_value,
292 &final_result_object);
293 if (property_enum_status != WD_SUCCESS) {
294 return EUNEXPECTEDJSERROR;
295 }
296 *value = final_result_object;
297 return WD_SUCCESS;
298 }
299 // We've already done our best to check if the object is an array or
300 // an object. We now know it doesn't implement IHTMLElement. We have
301 // no choice but to throw up our hands here.
302 LOG(WARN) << "Dispatch value is not recognized as a JavaScript object, array, or element reference";
303 return EUNEXPECTEDJSERROR;
304 }
305 ElementHandle element_wrapper;
306 bool element_added = element_manager->AddManagedElement(node, &element_wrapper);
307 Json::Value element_value(Json::objectValue);
308 element_value[JSON_ELEMENT_PROPERTY_NAME] = element_wrapper->element_id();
309 *value = element_value;
310 }
311 } else {
312 LOG(WARN) << "Unknown type of result is found";
313 status_code = EUNKNOWNSCRIPTRESULT;
314 }
315 return status_code;
316 }
317
318 bool VariantUtilities::ExecuteToJsonMethod(VARIANT object_to_serialize,
319 VARIANT* json_object_variant) {
320 CComVariant to_json_method;
321 bool has_to_json_property = GetVariantObjectPropertyValue(object_to_serialize.pdispVal,
322 L"toJSON",
323 &to_json_method);
324 if (!has_to_json_property) {
325 LOG(DEBUG) << "No toJSON property found on IDispatch";
326 return false;
327 }
328
329 // Grab the "call" method out of the returned function
330 DISPID call_member_id;
331 OLECHAR FAR* call_member_name = L"call";
332 HRESULT hr = to_json_method.pdispVal->GetIDsOfNames(IID_NULL,
333 &call_member_name,
334 1,
335 LOCALE_USER_DEFAULT,
336 &call_member_id);
337 if (FAILED(hr)) {
338 LOGHR(WARN, hr) << "Cannot locate call method on toJSON function";
339 return false;
340 }
341
342 // IDispatch::Invoke() expects the arguments to be passed into it
343 // in reverse order. To accomplish this, we create a new variant
344 // array of size n + 1 where n is the number of arguments we have.
345 // we copy each element of arguments_array_ into the new array in
346 // reverse order, and add an extra argument, the window object,
347 // to the end of the array to use as the "this" parameter for the
348 // function invocation.
349 std::vector<CComVariant> argument_array(1);
350 argument_array[0].Copy(&object_to_serialize);
351
352 DISPPARAMS call_parameters = { 0 };
353 memset(&call_parameters, 0, sizeof call_parameters);
354 call_parameters.cArgs = static_cast<unsigned int>(argument_array.size());
355 call_parameters.rgvarg = &argument_array[0];
356
357 CComBSTR error_description = L"";
358
359 int return_code = WD_SUCCESS;
360 EXCEPINFO exception;
361 memset(&exception, 0, sizeof exception);
362 hr = to_json_method.pdispVal->Invoke(call_member_id,
363 IID_NULL,
364 LOCALE_USER_DEFAULT,
365 DISPATCH_METHOD,
366 &call_parameters,
367 json_object_variant,
368 &exception,
369 0);
370
371 if (FAILED(hr)) {
372 if (DISP_E_EXCEPTION == hr) {
373 error_description = exception.bstrDescription ? exception.bstrDescription : L"EUNEXPECTEDJSERROR";
374 CComBSTR error_source(exception.bstrSource ? exception.bstrSource : L"EUNEXPECTEDJSERROR");
375 LOG(INFO) << "Exception message was: '" << error_description << "'";
376 LOG(INFO) << "Exception source was: '" << error_source << "'";
377 }
378 else {
379 LOGHR(DEBUG, hr) << "Failed to execute anonymous function, no exception information retrieved";
380 }
381 return false;
382 }
383
384 return true;
385 }
386
387 bool VariantUtilities::GetVariantObjectPropertyValue(IDispatch* variant_object_dispatch,
388 std::wstring property_name,
389 VARIANT* property_value) {
390 LPOLESTR property_name_pointer = reinterpret_cast<LPOLESTR>(const_cast<wchar_t*>(property_name.data()));
391 DISPID dispid_property;
392 HRESULT hr = variant_object_dispatch->GetIDsOfNames(IID_NULL,
393 &property_name_pointer,
394 1,
395 LOCALE_USER_DEFAULT,
396 &dispid_property);
397 if (FAILED(hr)) {
398 // Only log failures to find dispid to debug level, not warn level.
399 // Querying for the existence of a property is a normal thing to
400 // want to accomplish.
401 LOGHR(DEBUG, hr) << "Unable to get dispatch ID (dispid) for property "
402 << StringUtilities::ToString(property_name);
403 return false;
404 }
405
406 // get the value of eval result
407 DISPPARAMS no_args_dispatch_parameters = { 0 };
408 hr = variant_object_dispatch->Invoke(dispid_property,
409 IID_NULL,
410 LOCALE_USER_DEFAULT,
411 DISPATCH_PROPERTYGET,
412 &no_args_dispatch_parameters,
413 property_value,
414 NULL,
415 NULL);
416 if (FAILED(hr)) {
417 LOGHR(WARN, hr) << "Unable to get result for property "
418 << StringUtilities::ToString(property_name);
419 return false;
420 }
421 return true;
422 }
423
424 std::wstring VariantUtilities::GetVariantObjectTypeName(VARIANT value) {
425 std::wstring name = L"";
426 if (value.vt == VT_DISPATCH && value.pdispVal) {
427 CComPtr<ITypeInfo> typeinfo;
428 HRESULT get_type_info_result = value.pdispVal->GetTypeInfo(0,
429 LOCALE_USER_DEFAULT,
430 &typeinfo);
431 TYPEATTR* type_attr;
432 CComBSTR name_bstr;
433 if (SUCCEEDED(get_type_info_result) &&
434 SUCCEEDED(typeinfo->GetTypeAttr(&type_attr)) &&
435 SUCCEEDED(typeinfo->GetDocumentation(-1, &name_bstr, 0, 0, 0))) {
436 typeinfo->ReleaseTypeAttr(type_attr);
437 name = name_bstr.Copy();
438 } else {
439 LOG(WARN) << "Unable to get object type";
440 }
441 } else {
442 LOG(DEBUG) << "Unable to get object type for non-object result, result is not IDispatch or IDispatch pointer is NULL";
443 }
444 return name;
445 }
446
447 int VariantUtilities::GetPropertyNameList(IDispatch* object_dispatch,
448 std::vector<std::wstring>* property_names) {
449 LOG(TRACE) << "Entering Script::GetPropertyNameList";
450
451 CComPtr<IDispatchEx> dispatchex;
452 HRESULT hr = object_dispatch->QueryInterface<IDispatchEx>(&dispatchex);
453 DISPID current_disp_id;
454 hr = dispatchex->GetNextDispID(fdexEnumAll,
455 DISPID_STARTENUM,
456 ¤t_disp_id);
457 while (hr == S_OK) {
458 CComBSTR member_name_bstr;
459 dispatchex->GetMemberName(current_disp_id, &member_name_bstr);
460 std::wstring member_name = member_name_bstr;
461 property_names->push_back(member_name);
462 hr = dispatchex->GetNextDispID(fdexEnumAll,
463 current_disp_id,
464 ¤t_disp_id);
465 }
466 return WD_SUCCESS;
467 }
468
469 int VariantUtilities::GetArrayLength(IDispatch* array_dispatch, long* length) {
470 LOG(TRACE) << "Entering Script::GetArrayLength";
471 CComVariant length_result;
472 bool get_length_success = GetVariantObjectPropertyValue(array_dispatch,
473 L"length",
474 &length_result);
475 if (!get_length_success) {
476 // Failure already logged by GetVariantObjectPropertyValue
477 return EUNEXPECTEDJSERROR;
478 }
479
480 *length = length_result.lVal;
481 return WD_SUCCESS;
482 }
483
484 int VariantUtilities::GetArrayItem(IDispatch* array_dispatch,
485 long index,
486 VARIANT* item){
487 LOG(TRACE) << "Entering Script::GetArrayItem";
488 std::wstring index_string = std::to_wstring(static_cast<long long>(index));
489 CComVariant array_item_variant;
490 bool get_array_item_success = GetVariantObjectPropertyValue(array_dispatch,
491 index_string,
492 item);
493
494 if (!get_array_item_success) {
495 // Array-like item doesn't have indexed items; try using the
496 // 'item' method to access the elements in the collection.
497 LPOLESTR item_method_pointer = L"item";
498 DISPID dispid_item;
499 HRESULT hr = array_dispatch->GetIDsOfNames(IID_NULL,
500 &item_method_pointer,
501 1,
502 LOCALE_USER_DEFAULT,
503 &dispid_item);
504 if (FAILED(hr)) {
505 return EUNEXPECTEDJSERROR;
506 }
507
508 std::vector<CComVariant> argument_array_copy(2);
509 argument_array_copy[0] = index;
510 argument_array_copy[1] = array_dispatch;
511 DISPPARAMS call_parameters = { 0 };
512 memset(&call_parameters, 0, sizeof call_parameters);
513 call_parameters.cArgs = 2;
514 call_parameters.rgvarg = &argument_array_copy[0];
515 hr = array_dispatch->Invoke(dispid_item,
516 IID_NULL,
517 LOCALE_USER_DEFAULT,
518 DISPATCH_METHOD,
519 &call_parameters,
520 item,
521 NULL,
522 NULL);
523 if (FAILED(hr)) {
524 return EUNEXPECTEDJSERROR;
525 }
526 }
527 return WD_SUCCESS;
528 }
529
530 int VariantUtilities::GetAllVariantObjectPropertyValues(IElementManager* element_manager,
531 VARIANT variant_value,
532 Json::Value* value) {
533 std::vector<std::wstring> property_names;
534 GetPropertyNameList(variant_value.pdispVal, &property_names);
535
536 for (size_t i = 0; i < property_names.size(); ++i) {
537 CComVariant property_value_variant;
538 bool property_value_retrieved =
539 GetVariantObjectPropertyValue(variant_value.pdispVal,
540 property_names[i],
541 &property_value_variant);
542 if (!property_value_retrieved) {
543 LOG(WARN) << "Did not successfully get value for property '"
544 << StringUtilities::ToString(property_names[i])
545 << "' from object.";
546 return EUNEXPECTEDJSERROR;
547 }
548
549 Json::Value property_value;
550 ConvertVariantToJsonValue(element_manager,
551 property_value_variant,
552 &property_value);
553
554 std::string name = StringUtilities::ToString(property_names[i]);
555 (*value)[name] = property_value;
556 }
557
558 return WD_SUCCESS;
559 }
560
561 } // namespace webdriver