"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                                  &current_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                                    &current_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