示例#1
0
void HTMLElement::mapLanguageAttributeToLocale(const AtomicString& value, MutableStylePropertySet* style)
{
    if (!value.isEmpty()) {
        // Have to quote so the locale id is treated as a string instead of as a CSS keyword.
        addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLocale, quoteCSSString(value));

        // FIXME: Remove the following UseCounter code when we collect enough
        // data.
        UseCounter::count(document(), UseCounter::LangAttribute);
        if (isHTMLHtmlElement(*this))
            UseCounter::count(document(), UseCounter::LangAttributeOnHTML);
        else if (isHTMLBodyElement(*this))
            UseCounter::count(document(), UseCounter::LangAttributeOnBody);
        String htmlLanguage = value.string();
        size_t firstSeparator = htmlLanguage.find('-');
        if (firstSeparator != kNotFound)
            htmlLanguage = htmlLanguage.left(firstSeparator);
        String uiLanguage = defaultLanguage();
        firstSeparator = uiLanguage.find('-');
        if (firstSeparator != kNotFound)
            uiLanguage = uiLanguage.left(firstSeparator);
        firstSeparator = uiLanguage.find('_');
        if (firstSeparator != kNotFound)
            uiLanguage = uiLanguage.left(firstSeparator);
        if (!equalIgnoringCase(htmlLanguage, uiLanguage))
            UseCounter::count(document(), UseCounter::LangAttributeDoesNotMatchToUILocale);
    } else {
        // The empty string means the language is explicitly unknown.
        addPropertyToPresentationAttributeStyle(style, CSSPropertyWebkitLocale, CSSValueAuto);
    }
}
void findGoodTouchTargets(const IntRect& touchBox, LocalFrame* mainFrame, Vector<IntRect>& goodTargets, WillBeHeapVector<RawPtrWillBeMember<Node> >& highlightNodes)
{
    goodTargets.clear();

    int touchPointPadding = ceil(std::max(touchBox.width(), touchBox.height()) * 0.5);

    IntPoint touchPoint = touchBox.center();
    IntPoint contentsPoint = mainFrame->view()->windowToContents(touchPoint);

    HitTestResult result = mainFrame->eventHandler().hitTestResultAtPoint(contentsPoint, HitTestRequest::ReadOnly | HitTestRequest::Active | HitTestRequest::ConfusingAndOftenMisusedDisallowShadowContent, IntSize(touchPointPadding, touchPointPadding));
    const WillBeHeapListHashSet<RefPtrWillBeMember<Node> >& hitResults = result.rectBasedTestResult();

    // Blacklist nodes that are container of disambiguated nodes.
    // It is not uncommon to have a clickable <div> that contains other clickable objects.
    // This heuristic avoids excessive disambiguation in that case.
    WillBeHeapHashSet<RawPtrWillBeMember<Node> > blackList;
    for (WillBeHeapListHashSet<RefPtrWillBeMember<Node> >::const_iterator it = hitResults.begin(); it != hitResults.end(); ++it) {
        // Ignore any Nodes that can't be clicked on.
        RenderObject* renderer = it->get()->renderer();
        if (!renderer || !it->get()->willRespondToMouseClickEvents())
            continue;

        // Blacklist all of the Node's containers.
        for (RenderBlock* container = renderer->containingBlock(); container; container = container->containingBlock()) {
            Node* containerNode = container->node();
            if (!containerNode)
                continue;
            if (!blackList.add(containerNode).isNewEntry)
                break;
        }
    }

    WillBeHeapHashMap<RawPtrWillBeMember<Node>, TouchTargetData> touchTargets;
    float bestScore = 0;
    for (WillBeHeapListHashSet<RefPtrWillBeMember<Node> >::const_iterator it = hitResults.begin(); it != hitResults.end(); ++it) {
        for (Node* node = it->get(); node; node = node->parentNode()) {
            if (blackList.contains(node))
                continue;
            if (node->isDocumentNode() || isHTMLHtmlElement(*node) || isHTMLBodyElement(*node))
                break;
            if (node->willRespondToMouseClickEvents()) {
                TouchTargetData& targetData = touchTargets.add(node, TouchTargetData()).storedValue->value;
                targetData.windowBoundingBox = boundingBoxForEventNodes(node);
                targetData.score = scoreTouchTarget(touchPoint, touchPointPadding, targetData.windowBoundingBox);
                bestScore = std::max(bestScore, targetData.score);
                break;
            }
        }
    }

    for (WillBeHeapHashMap<RawPtrWillBeMember<Node>, TouchTargetData>::iterator it = touchTargets.begin(); it != touchTargets.end(); ++it) {
        // Currently the scoring function uses the overlap area with the fat point as the score.
        // We ignore the candidates that has less than 1/2 overlap (we consider not really ambiguous enough) than the best candidate to avoid excessive popups.
        if (it->value.score < bestScore * 0.5)
            continue;
        goodTargets.append(it->value.windowBoundingBox);
        highlightNodes.append(it->key);
    }
}
Node* DOMPatchSupport::patchNode(Node* node, const String& markup, ExceptionState& exceptionState)
{
    // Don't parse <html> as a fragment.
    if (node->isDocumentNode() || (node->parentNode() && node->parentNode()->isDocumentNode())) {
        patchDocument(markup);
        return 0;
    }

    Node* previousSibling = node->previousSibling();
    RefPtrWillBeRawPtr<DocumentFragment> fragment = DocumentFragment::create(m_document);
    Node* targetNode = node->parentElementOrShadowRoot() ? node->parentElementOrShadowRoot() : m_document.documentElement();

    // Use the document BODY as the context element when editing immediate shadow root children,
    // as it provides an equivalent parsing context.
    if (targetNode->isShadowRoot())
        targetNode = m_document.body();
    Element* targetElement = toElement(targetNode);

    // FIXME: This code should use one of createFragment* in markup.h
    if (m_document.isHTMLDocument())
        fragment->parseHTML(markup, targetElement);
    else
        fragment->parseXML(markup, targetElement);

    // Compose the old list.
    ContainerNode* parentNode = node->parentNode();
    Vector<OwnPtr<Digest> > oldList;
    for (Node* child = parentNode->firstChild(); child; child = child->nextSibling())
        oldList.append(createDigest(child, 0));

    // Compose the new list.
    String markupCopy = markup.lower();
    Vector<OwnPtr<Digest> > newList;
    for (Node* child = parentNode->firstChild(); child != node; child = child->nextSibling())
        newList.append(createDigest(child, 0));
    for (Node* child = fragment->firstChild(); child; child = child->nextSibling()) {
        if (isHTMLHeadElement(*child) && !child->firstChild() && markupCopy.find("</head>") == kNotFound)
            continue; // HTML5 parser inserts empty <head> tag whenever it parses <body>
        if (isHTMLBodyElement(*child) && !child->firstChild() && markupCopy.find("</body>") == kNotFound)
            continue; // HTML5 parser inserts empty <body> tag whenever it parses </head>
        newList.append(createDigest(child, &m_unusedNodesMap));
    }
    for (Node* child = node->nextSibling(); child; child = child->nextSibling())
        newList.append(createDigest(child, 0));

    if (!innerPatchChildren(parentNode, oldList, newList, exceptionState)) {
        // Fall back to total replace.
        if (!m_domEditor->replaceChild(parentNode, fragment.release(), node, exceptionState))
            return 0;
    }
    return previousSibling ? previousSibling->nextSibling() : parentNode->firstChild();
}
示例#4
0
DocumentFragment* createContextualFragment(
    const String& markup,
    Element* element,
    ParserContentPolicy parserContentPolicy,
    ExceptionState& exceptionState) {
  DCHECK(element);
  if (!isSupportedContainer(element)) {
    exceptionState.throwDOMException(
        NotSupportedError, "The range's container is '" + element->localName() +
                               "', which is not supported.");
    return nullptr;
  }

  DocumentFragment* fragment = createFragmentForInnerOuterHTML(
      markup, element, parserContentPolicy, "createContextualFragment",
      exceptionState);
  if (!fragment)
    return nullptr;

  // We need to pop <html> and <body> elements and remove <head> to
  // accommodate folks passing complete HTML documents to make the
  // child of an element.

  Node* nextNode = nullptr;
  for (Node* node = fragment->firstChild(); node; node = nextNode) {
    nextNode = node->nextSibling();
    if (isHTMLHtmlElement(*node) || isHTMLHeadElement(*node) ||
        isHTMLBodyElement(*node)) {
      HTMLElement* element = toHTMLElement(node);
      if (Node* firstChild = element->firstChild())
        nextNode = firstChild;
      removeElementPreservingChildren(fragment, element);
    }
  }
  return fragment;
}
示例#5
0
DocumentFragment* createFragmentFromText(const EphemeralRange& context,
                                         const String& text) {
  if (context.isNull())
    return nullptr;

  Document& document = context.document();
  DocumentFragment* fragment = document.createDocumentFragment();

  if (text.isEmpty())
    return fragment;

  String string = text;
  string.replace("\r\n", "\n");
  string.replace('\r', '\n');

  if (!isRichlyEditablePosition(context.startPosition()) ||
      shouldPreserveNewline(context)) {
    fragment->appendChild(document.createTextNode(string));
    if (string.endsWith('\n')) {
      HTMLBRElement* element = HTMLBRElement::create(document);
      element->setAttribute(classAttr, AppleInterchangeNewline);
      fragment->appendChild(element);
    }
    return fragment;
  }

  // A string with no newlines gets added inline, rather than being put into a
  // paragraph.
  if (string.find('\n') == kNotFound) {
    fillContainerFromString(fragment, string);
    return fragment;
  }

  // Break string into paragraphs. Extra line breaks turn into empty paragraphs.
  Element* block =
      enclosingBlock(context.startPosition().nodeAsRangeFirstNode());
  bool useClonesOfEnclosingBlock =
      block && !isHTMLBodyElement(*block) && !isHTMLHtmlElement(*block) &&
      block != rootEditableElementOf(context.startPosition());

  Vector<String> list;
  string.split('\n', true, list);  // true gets us empty strings in the list
  size_t numLines = list.size();
  for (size_t i = 0; i < numLines; ++i) {
    const String& s = list[i];

    Element* element = nullptr;
    if (s.isEmpty() && i + 1 == numLines) {
      // For last line, use the "magic BR" rather than a P.
      element = HTMLBRElement::create(document);
      element->setAttribute(classAttr, AppleInterchangeNewline);
    } else {
      if (useClonesOfEnclosingBlock)
        element = block->cloneElementWithoutChildren();
      else
        element = createDefaultParagraphElement(document);
      fillContainerFromString(element, s);
    }
    fragment->appendChild(element);
  }
  return fragment;
}
bool DOMPatchSupport::innerPatchChildren(ContainerNode* parentNode, const Vector<OwnPtr<Digest> >& oldList, const Vector<OwnPtr<Digest> >& newList, ExceptionState& exceptionState)
{
    pair<ResultMap, ResultMap> resultMaps = diff(oldList, newList);
    ResultMap& oldMap = resultMaps.first;
    ResultMap& newMap = resultMaps.second;

    Digest* oldHead = 0;
    Digest* oldBody = 0;

    // 1. First strip everything except for the nodes that retain. Collect pending merges.
    HashMap<Digest*, Digest*> merges;
    HashSet<size_t, WTF::IntHash<size_t>, WTF::UnsignedWithZeroKeyHashTraits<size_t> > usedNewOrdinals;
    for (size_t i = 0; i < oldList.size(); ++i) {
        if (oldMap[i].first) {
            if (usedNewOrdinals.add(oldMap[i].second).isNewEntry)
                continue;
            oldMap[i].first = 0;
            oldMap[i].second = 0;
        }

        // Always match <head> and <body> tags with each other - we can't remove them from the DOM
        // upon patching.
        if (isHTMLHeadElement(*oldList[i]->m_node)) {
            oldHead = oldList[i].get();
            continue;
        }
        if (isHTMLBodyElement(*oldList[i]->m_node)) {
            oldBody = oldList[i].get();
            continue;
        }

        // Check if this change is between stable nodes. If it is, consider it as "modified".
        if (!m_unusedNodesMap.contains(oldList[i]->m_sha1) && (!i || oldMap[i - 1].first) && (i == oldMap.size() - 1 || oldMap[i + 1].first)) {
            size_t anchorCandidate = i ? oldMap[i - 1].second + 1 : 0;
            size_t anchorAfter = (i == oldMap.size() - 1) ? anchorCandidate + 1 : oldMap[i + 1].second;
            if (anchorAfter - anchorCandidate == 1 && anchorCandidate < newList.size())
                merges.set(newList[anchorCandidate].get(), oldList[i].get());
            else {
                if (!removeChildAndMoveToNew(oldList[i].get(), exceptionState))
                    return false;
            }
        } else {
            if (!removeChildAndMoveToNew(oldList[i].get(), exceptionState))
                return false;
        }
    }

    // Mark retained nodes as used, do not reuse node more than once.
    HashSet<size_t, WTF::IntHash<size_t>, WTF::UnsignedWithZeroKeyHashTraits<size_t> >  usedOldOrdinals;
    for (size_t i = 0; i < newList.size(); ++i) {
        if (!newMap[i].first)
            continue;
        size_t oldOrdinal = newMap[i].second;
        if (usedOldOrdinals.contains(oldOrdinal)) {
            // Do not map node more than once
            newMap[i].first = 0;
            newMap[i].second = 0;
            continue;
        }
        usedOldOrdinals.add(oldOrdinal);
        markNodeAsUsed(newMap[i].first);
    }

    // Mark <head> and <body> nodes for merge.
    if (oldHead || oldBody) {
        for (size_t i = 0; i < newList.size(); ++i) {
            if (oldHead && isHTMLHeadElement(*newList[i]->m_node))
                merges.set(newList[i].get(), oldHead);
            if (oldBody && isHTMLBodyElement(*newList[i]->m_node))
                merges.set(newList[i].get(), oldBody);
        }
    }

    // 2. Patch nodes marked for merge.
    for (HashMap<Digest*, Digest*>::iterator it = merges.begin(); it != merges.end(); ++it) {
        if (!innerPatchNode(it->value, it->key, exceptionState))
            return false;
    }

    // 3. Insert missing nodes.
    for (size_t i = 0; i < newMap.size(); ++i) {
        if (newMap[i].first || merges.contains(newList[i].get()))
            continue;
        if (!insertBeforeAndMarkAsUsed(parentNode, newList[i].get(), parentNode->traverseToChildAt(i), exceptionState))
            return false;
    }

    // 4. Then put all nodes that retained into their slots (sort by new index).
    for (size_t i = 0; i < oldMap.size(); ++i) {
        if (!oldMap[i].first)
            continue;
        RefPtrWillBeRawPtr<Node> node = oldMap[i].first->m_node;
        Node* anchorNode = parentNode->traverseToChildAt(oldMap[i].second);
        if (node == anchorNode)
            continue;
        if (isHTMLBodyElement(*node) || isHTMLHeadElement(*node))
            continue; // Never move head or body, move the rest of the nodes around them.

        if (!m_domEditor->insertBefore(parentNode, node.release(), anchorNode, exceptionState))
            return false;
    }
    return true;
}