Esempio n. 1
0
bool Element::GetFrameDetails(LocationInfo* location, std::vector<LocationInfo>* frame_locations) {
  LOG(TRACE) << "Entering Element::GetFrameDetails";

  CComPtr<IHTMLDocument2> owner_doc;
  int status_code = this->GetContainingDocument(true, &owner_doc);
  if (status_code != WD_SUCCESS) {
    LOG(WARN) << "Unable to get containing document";
    return false;
  }

  CComPtr<IHTMLWindow2> owner_doc_window;
  HRESULT hr = owner_doc->get_parentWindow(&owner_doc_window);
  if (!owner_doc_window) {
    LOG(WARN) << "Unable to get parent window, call to IHTMLDocument2::get_parentWindow failed";
    return false;
  }

  // Get the parent window to the current window, where "current window" is
  // the window containing the parent document of this element. If that parent
  // window exists, and it is not the same as the current window, we assume
  // this element exists inside a frame or iframe. If it is in a frame, get
  // the parent document containing the frame, so we can get the information
  // about the frame or iframe element hosting the document of this element.
  CComPtr<IHTMLWindow2> parent_window;
  hr = owner_doc_window->get_parent(&parent_window);
  if (parent_window && !owner_doc_window.IsEqualObject(parent_window)) {
    LOG(DEBUG) << "Element is in a frame.";
    CComPtr<IHTMLDocument2> parent_doc;
    status_code = this->GetDocumentFromWindow(parent_window, &parent_doc);

    CComPtr<IHTMLFramesCollection2> frames;
    hr = parent_doc->get_frames(&frames);

    long frame_count(0);
    hr = frames->get_length(&frame_count);
    CComVariant index;
    index.vt = VT_I4;
    for (long i = 0; i < frame_count; ++i) {
      // See if the document in each frame is this element's 
      // owner document.
      index.lVal = i;
      CComVariant result;
      hr = frames->item(&index, &result);
      CComPtr<IHTMLWindow2> frame_window;
      result.pdispVal->QueryInterface<IHTMLWindow2>(&frame_window);
      if (!frame_window) {
        // Frame is not an HTML frame.
        continue;
      }

      CComPtr<IHTMLDocument2> frame_doc;
      status_code = this->GetDocumentFromWindow(frame_window, &frame_doc);

      if (frame_doc.IsEqualObject(owner_doc)) {
        // The document in this frame *is* this element's owner
        // document. Get the frameElement property of the document's
        // containing window (which is itself an HTML element, either
        // a frame or an iframe). Then get the x and y coordinates of
        // that frame element.
        // N.B. We must use JavaScript here, as directly using
        // IHTMLWindow4.get_frameElement() returns E_NOINTERFACE under
        // some circumstances.
        LOG(DEBUG) << "Located host frame. Attempting to get hosting element";
        std::wstring script_source = L"(function(){ return function() { return arguments[0].frameElement };})();";
        Script script_wrapper(frame_doc, script_source, 1);
        CComVariant window_variant(frame_window);
        script_wrapper.AddArgument(window_variant);
        status_code = script_wrapper.Execute();
        CComPtr<IHTMLFrameBase> frame_base;
        if (status_code == WD_SUCCESS) {
          hr = script_wrapper.result().pdispVal->QueryInterface<IHTMLFrameBase>(&frame_base);
          if (FAILED(hr)) {
            LOG(WARN) << "Found the frame element, but could not QueryInterface to IHTMLFrameBase.";
          }
        } else {
          // Can't get the frameElement property, likely because the frames are from different
          // domains. So start at the parent document, and use getElementsByTagName to retrieve
          // all of the iframe elements (if there are no iframe elements, get the frame elements)
          // **** BIG HUGE ASSUMPTION!!! ****
          // The index of the frame from the document.frames collection will correspond to the 
          // index into the collection of iframe/frame elements returned by getElementsByTagName.
          LOG(WARN) << "Attempting to get frameElement via JavaScript failed. "
                    << "This usually means the frame is in a different domain than the parent frame. "
                    << "Browser security against cross-site scripting attacks will not allow this. "
                    << "Attempting alternative method.";
          long collection_count = 0;
          CComPtr<IDispatch> element_dispatch;
          CComPtr<IHTMLDocument3> doc;
          parent_doc->QueryInterface<IHTMLDocument3>(&doc);
          if (doc) {
            LOG(DEBUG) << "Looking for <iframe> elements in parent document.";
            CComBSTR iframe_tag_name = L"iframe";
            CComPtr<IHTMLElementCollection> iframe_collection;
            hr = doc->getElementsByTagName(iframe_tag_name, &iframe_collection);
            hr = iframe_collection->get_length(&collection_count);
            if (collection_count != 0) {
              if (collection_count > index.lVal) {
                LOG(DEBUG) << "Found <iframe> elements in parent document, retrieving element" << index.lVal << ".";
                hr = iframe_collection->item(index, index, &element_dispatch);
                hr = element_dispatch->QueryInterface<IHTMLFrameBase>(&frame_base);
              }
            } else {
              LOG(DEBUG) << "No <iframe> elements, looking for <frame> elements in parent document.";
              CComBSTR frame_tag_name = L"frame";
              CComPtr<IHTMLElementCollection> frame_collection;
              hr = doc->getElementsByTagName(frame_tag_name, &frame_collection);
              hr = frame_collection->get_length(&collection_count);
              if (collection_count > index.lVal) {
                LOG(DEBUG) << "Found <frame> elements in parent document, retrieving element" << index.lVal << ".";
                hr = frame_collection->item(index, index, &element_dispatch);
                hr = element_dispatch->QueryInterface<IHTMLFrameBase>(&frame_base);
              }
            }
          } else {
            LOG(WARN) << "QueryInterface of parent document to IHTMLDocument3 failed.";
          }
        }

        if (frame_base) {
          LOG(DEBUG) << "Successfully found frame hosting element";
          LocationInfo frame_doc_info;
          bool doc_dimensions_success = DocumentHost::GetDocumentDimensions(
              frame_doc,
              &frame_doc_info);

          // Wrap the element so we can find its location. Note that
          // GetLocation() may recursively call into this method.
          CComPtr<IHTMLElement> frame_element;
          frame_base->QueryInterface<IHTMLElement>(&frame_element);
          Element element_wrapper(frame_element, this->containing_window_handle_);
          CComPtr<IHTMLStyle> style;
          frame_element->get_style(&style);

          LocationInfo frame_location = {};
          status_code = element_wrapper.GetLocation(&frame_location,
                                                    frame_locations);

          // Take the border of the frame element into account.
          // N.B. We don't have to do this for non-frame elements,
          // because the border is part of the hit-test region. For
          // finding offsets to get absolute position of elements 
          // within frames, the origin of the frame document is offset
          // by the border width.
          CComPtr<IHTMLElement2> border_width_element;
          frame_element->QueryInterface<IHTMLElement2>(&border_width_element);
          long left_border_width = 0;
          border_width_element->get_clientLeft(&left_border_width);
          long top_border_width = 0;
          border_width_element->get_clientTop(&top_border_width);

          if (status_code == WD_SUCCESS) {
            // Take into account the presence of scrollbars in the frame.
            long frame_element_width = frame_location.width;
            long frame_element_height = frame_location.height;
            if (doc_dimensions_success) {
              if (frame_doc_info.height > frame_element_height) {
                int horizontal_scrollbar_height = ::GetSystemMetrics(SM_CYHSCROLL);
                frame_element_height -= horizontal_scrollbar_height;
              }
              if (frame_doc_info.width > frame_element_width) {
                int vertical_scrollbar_width = ::GetSystemMetrics(SM_CXVSCROLL);
                frame_element_width -= vertical_scrollbar_width;
              }
            }
            location->x = frame_location.x + left_border_width;
            location->y = frame_location.y + top_border_width;
            location->width = frame_element_width;
            location->height = frame_element_height;
          }
          return true;
        }
      }
    }
  }

  // If we reach here, the element isn't in a frame/iframe.
  return false;
}
Esempio n. 2
0
int Element::GetFrameOffset(long* x, long* y) {
  LOG(TRACE) << "Entering Element::GetFrameOffset";

  CComPtr<IHTMLDocument2> owner_doc;
  int status_code = this->GetContainingDocument(true, &owner_doc);
  if (status_code != SUCCESS) {
    LOG(WARN) << "Unable to get containing document";
    return status_code;
  }

  CComPtr<IHTMLWindow2> owner_doc_window;
  HRESULT hr = owner_doc->get_parentWindow(&owner_doc_window);
  if (!owner_doc_window) {
    LOG(WARN) << "Unable to get parent window, call to IHTMLDocument2::get_parentWindow failed";
    return ENOSUCHDOCUMENT;
  }

  CComPtr<IHTMLWindow2> parent_window;
  hr = owner_doc_window->get_parent(&parent_window);
  if (parent_window && !owner_doc_window.IsEqualObject(parent_window)) {
    CComPtr<IHTMLDocument2> parent_doc;
    status_code = this->GetParentDocument(parent_window, &parent_doc);

    CComPtr<IHTMLFramesCollection2> frames;
    hr = parent_doc->get_frames(&frames);

    long frame_count(0);
    hr = frames->get_length(&frame_count);
    CComVariant index;
    index.vt = VT_I4;
    for (long i = 0; i < frame_count; ++i) {
      // See if the document in each frame is this element's 
      // owner document.
      index.lVal = i;
      CComVariant result;
      hr = frames->item(&index, &result);
      CComQIPtr<IHTMLWindow2> frame_window(result.pdispVal);
      if (!frame_window) {
        // Frame is not an HTML frame.
        continue;
      }

      CComPtr<IHTMLDocument2> frame_doc;
      hr = frame_window->get_document(&frame_doc);

      if (frame_doc.IsEqualObject(owner_doc)) {
        // The document in this frame *is* this element's owner
        // document. Get the frameElement property of the document's
        // containing window (which is itself an HTML element, either
        // a frame or an iframe). Then get the x and y coordinates of
        // that frame element.
        std::wstring script_source = L"(function(){ return function() { return arguments[0].frameElement };})();";
        Script script_wrapper(frame_doc, script_source, 1);
        CComVariant window_variant(frame_window);
        script_wrapper.AddArgument(window_variant);
        script_wrapper.Execute();
        CComQIPtr<IHTMLElement> frame_element(script_wrapper.result().pdispVal);

        // Wrap the element so we can find its location.
        Element element_wrapper(frame_element, this->containing_window_handle_);
        long frame_x, frame_y, frame_width, frame_height;
        status_code = element_wrapper.GetLocation(&frame_x,
                                                  &frame_y,
                                                  &frame_width,
                                                  &frame_height);
        if (status_code == SUCCESS) {
          *x = frame_x;
          *y = frame_y;
        }
        break;
      }
    }
  }

  return SUCCESS;
}