diff options
author | Simon Hausmann <simon.hausmann@nokia.com> | 2012-02-24 16:36:50 +0100 |
---|---|---|
committer | Simon Hausmann <simon.hausmann@nokia.com> | 2012-02-24 16:36:50 +0100 |
commit | ad0d549d4cc13433f77c1ac8f0ab379c83d93f28 (patch) | |
tree | b34b0daceb7c8e7fdde4b4ec43650ab7caadb0a9 /Source/WebKit/blackberry/WebKitSupport/DOMSupport.cpp | |
parent | 03e12282df9aa1e1fb05a8b90f1cfc2e08764cec (diff) | |
download | qtwebkit-ad0d549d4cc13433f77c1ac8f0ab379c83d93f28.tar.gz |
Imported WebKit commit bb52bf3c0119e8a128cd93afe5572413a8617de9 (http://svn.webkit.org/repository/webkit/trunk@108790)
Diffstat (limited to 'Source/WebKit/blackberry/WebKitSupport/DOMSupport.cpp')
-rw-r--r-- | Source/WebKit/blackberry/WebKitSupport/DOMSupport.cpp | 404 |
1 files changed, 404 insertions, 0 deletions
diff --git a/Source/WebKit/blackberry/WebKitSupport/DOMSupport.cpp b/Source/WebKit/blackberry/WebKitSupport/DOMSupport.cpp new file mode 100644 index 000000000..9dd5d2117 --- /dev/null +++ b/Source/WebKit/blackberry/WebKitSupport/DOMSupport.cpp @@ -0,0 +1,404 @@ +/* + * Copyright (C) 2011, 2012 Research In Motion Limited. All rights reserved. + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "config.h" +#include "DOMSupport.h" + +#include "FloatQuad.h" +#include "Frame.h" +#include "FrameView.h" +#include "HTMLFormElement.h" +#include "HTMLInputElement.h" +#include "HTMLNames.h" +#include "HTMLTextAreaElement.h" +#include "Node.h" +#include "Range.h" +#include "RenderObject.h" +#include "RenderText.h" +#include "RenderTextControl.h" +#include "TextIterator.h" +#include "VisibleSelection.h" +#include "WTFString.h" + +#include "htmlediting.h" +#include "visible_units.h" + +#include <limits> + +using WTF::Vector; + +using namespace WebCore; + +namespace BlackBerry { +namespace WebKit { +namespace DOMSupport { + +void visibleTextQuads(const Range& range, Vector<FloatQuad>& quads, bool useSelectionHeight) +{ + // Range::textQuads includes hidden text, which we don't want. + // To work around this, this is a copy of it which skips hidden elements. + Node* startContainer = range.startContainer(); + Node* endContainer = range.endContainer(); + + if (!startContainer || !endContainer) + return; + + Node* stopNode = range.pastLastNode(); + for (Node* node = range.firstNode(); node != stopNode; node = node->traverseNextNode()) { + RenderObject* r = node->renderer(); + if (!r || !r->isText()) + continue; + + if (r->style()->visibility() != VISIBLE) + continue; + + RenderText* renderText = toRenderText(r); + int startOffset = node == startContainer ? range.startOffset() : 0; + int endOffset = node == endContainer ? range.endOffset() : std::numeric_limits<int>::max(); + renderText->absoluteQuadsForRange(quads, startOffset, endOffset, useSelectionHeight); + } +} + +bool isTextInputElement(Element* element) +{ + return element->isTextFormControl() + || element->hasTagName(HTMLNames::textareaTag) + || element->isContentEditable(); +} + +bool isPasswordElement(const Element* element) +{ + return element && element->hasTagName(HTMLNames::inputTag) + && static_cast<const HTMLInputElement*>(element)->isPasswordField(); +} + +WTF::String inputElementText(Element* element) +{ + if (!element) + return WTF::String(); + + WTF::String elementText; + if (element->hasTagName(HTMLNames::inputTag)) { + const HTMLInputElement* inputElement = static_cast<const HTMLInputElement*>(element); + elementText = inputElement->value(); + } else if (element->hasTagName(HTMLNames::textareaTag)) { + const HTMLTextAreaElement* inputElement = static_cast<const HTMLTextAreaElement*>(element); + elementText = inputElement->value(); + } else if (element->isContentEditable()) { + RefPtr<Range> rangeForNode = rangeOfContents(element); + elementText = rangeForNode.get()->text(); + } + return elementText; +} + +bool isElementTypePlugin(const Element* element) +{ + if (!element) + return false; + + if (element->hasTagName(HTMLNames::objectTag) + || element->hasTagName(HTMLNames::embedTag) + || element->hasTagName(HTMLNames::appletTag)) + return true; + + return false; +} + +HTMLTextFormControlElement* toTextControlElement(Node* node) +{ + if (!(node && node->isElementNode())) + return 0; + + Element* element = static_cast<Element*>(node); + if (!element->isFormControlElement()) + return 0; + + HTMLFormControlElement* formElement = static_cast<HTMLFormControlElement*>(element); + if (!formElement->isTextFormControl()) + return 0; + + return static_cast<HTMLTextFormControlElement*>(formElement); +} + +bool isPopupInputField(const Element* element) +{ + return isDateTimeInputField(element) || isColorInputField(element); +} + +bool isDateTimeInputField(const Element* element) +{ + if (!element->hasTagName(HTMLNames::inputTag)) + return false; + + const HTMLInputElement* inputElement = static_cast<const HTMLInputElement*>(element); + + // The following types have popup's. + if (inputElement->isDateControl() + || inputElement->isDateTimeControl() + || inputElement->isDateTimeLocalControl() + || inputElement->isTimeControl() + || inputElement->isMonthControl()) + return true; + + return false; +} + +bool isColorInputField(const Element* element) +{ + if (!element->hasTagName(HTMLNames::inputTag)) + return false; + + const HTMLInputElement* inputElement = static_cast<const HTMLInputElement*>(element); + +#if ENABLE(INPUT_COLOR) + if (inputElement->isColorControl()) + return true; +#endif + + return false; +} + +AttributeState elementSupportsAutocorrect(const Element* element) +{ + // First we check the input item itself. If the attribute is not defined, + // we check its parent form. + QualifiedName autocorrectAttr = QualifiedName(nullAtom, "autocorrect", nullAtom); + if (element->fastHasAttribute(autocorrectAttr)) { + AtomicString attributeString = element->fastGetAttribute(autocorrectAttr); + if (equalIgnoringCase(attributeString, "off")) + return Off; + if (equalIgnoringCase(attributeString, "on")) + return On; + // If we haven't returned, it wasn't set properly. Check the form for an explicit setting + // because the attribute was provided, but invalid. + } + if (element->isFormControlElement()) { + const HTMLFormControlElement* formElement = static_cast<const HTMLFormControlElement*>(element); + if (formElement->form() && formElement->form()->fastHasAttribute(autocorrectAttr)) { + AtomicString attributeString = formElement->form()->fastGetAttribute(autocorrectAttr); + if (equalIgnoringCase(attributeString, "off")) + return Off; + if (equalIgnoringCase(attributeString, "on")) + return On; + } + } + + return Default; +} + +// Check if this is an input field that will be focused & require input support. +bool isTextBasedContentEditableElement(Element* element) +{ + if (!element) + return false; + + if (element->isReadOnlyFormControl()) + return false; + + if (isPopupInputField(element)) + return false; + + return element->isTextFormControl() || element->isContentEditable(); +} + +IntRect transformedBoundingBoxForRange(const Range& range) +{ + // Based on Range::boundingBox, which does not handle transforms, and + // RenderObject::absoluteBoundingBoxRect, which does. + IntRect result; + Vector<FloatQuad> quads; + visibleTextQuads(range, quads); + const size_t n = quads.size(); + for (size_t i = 0; i < n; ++i) + result.unite(quads[i].enclosingBoundingBox()); + + return result; +} + +VisibleSelection visibleSelectionForInputElement(Element* element) +{ + return visibleSelectionForRangeInputElement(element, 0, inputElementText(element).length()); +} + +VisibleSelection visibleSelectionForRangeInputElement(Element* element, int start, int end) +{ + if (DOMSupport::toTextControlElement(element)) { + RenderTextControl* textRender = toRenderTextControl(element->renderer()); + if (!textRender) + return VisibleSelection(); + + VisiblePosition startPosition = textRender->visiblePositionForIndex(start); + VisiblePosition endPosition; + if (start == end) + endPosition = startPosition; + else + endPosition = textRender->visiblePositionForIndex(end); + + return VisibleSelection(startPosition, endPosition); + } + + // Must be content editable, generate the range. + RefPtr<Range> selectionRange = TextIterator::rangeFromLocationAndLength(element, start, end - start); + if (start == end) + return VisibleSelection(selectionRange.get()->startPosition(), DOWNSTREAM); + + VisiblePosition visibleStart(selectionRange->startPosition(), DOWNSTREAM); + VisiblePosition visibleEnd(selectionRange->endPosition(), SEL_DEFAULT_AFFINITY); + + return VisibleSelection(visibleStart, visibleEnd); +} + +Node* DOMContainerNodeForPosition(const Position& position) +{ + Node* nodeAtPos = position.containerNode(); + if (nodeAtPos->isInShadowTree()) + nodeAtPos = nodeAtPos->shadowAncestorNode(); + + return nodeAtPos; +} + +bool isPositionInNode(Node* node, const Position& position) +{ + int offset = 0; + Node* domNodeAtPos = DOMContainerNodeForPosition(position); + if (domNodeAtPos == position.containerNode()) + offset = position.computeOffsetInContainerNode(); + + RefPtr<Range> rangeForNode = rangeOfContents(node); + int ec; + + return rangeForNode->isPointInRange(domNodeAtPos, offset, ec); +} + +// This is a Tristate return to allow us to override name matching when +// autocomplete is expressly requested for a field. Default indicates +// that the setting is On which is the default but not expressly requested +// for the element being checked. On indicates that it is directly added +// to the element. +AttributeState elementSupportsAutocomplete(const Element* element) +{ + if (!element->hasTagName(HTMLNames::inputTag)) + return Default; + + const HTMLInputElement* inputElement = static_cast<const HTMLInputElement*>(element); + if (inputElement->fastHasAttribute(HTMLNames::autocompleteAttr)) { + if (equalIgnoringCase(inputElement->fastGetAttribute(HTMLNames::autocompleteAttr), "on")) + return On; + } + + return inputElement->shouldAutocomplete() ? Default : Off; +} + +bool matchesReservedStringPreventingAutocomplete(AtomicString& string) +{ + if (string.contains("email", false /* caseSensitive */) + || string.contains("user", false /* caseSensitive */) + || string.contains("name", false /* caseSensitive */) + || string.contains("login", false /* caseSensitive */)) + return true; + + return false; +} + +// This checks to see if an input element has a name or id attribute set to +// username or email. These are rough checks to avoid major sites that use +// login fields as input type=text and auto correction interfers with. +bool elementIdOrNameIndicatesNoAutocomplete(const Element* element) +{ + if (!element->hasTagName(HTMLNames::inputTag)) + return false; + + AtomicString idAttribute = element->getIdAttribute(); + if (matchesReservedStringPreventingAutocomplete(idAttribute)) + return true; + + if (element->fastHasAttribute(HTMLNames::nameAttr)) { + AtomicString nameAttribute = element->fastGetAttribute(HTMLNames::nameAttr); + if (matchesReservedStringPreventingAutocomplete(nameAttribute)) + return true; + } + + return false; +} + +IntPoint convertPointToFrame(const Frame* sourceFrame, const Frame* targetFrame, const IntPoint& point) +{ + ASSERT(sourceFrame && targetFrame); + if (sourceFrame == targetFrame) + return point; + + ASSERT(sourceFrame->view() && targetFrame->view()); + ASSERT(targetFrame->tree()); + + Frame* targetFrameParent = targetFrame->tree()->parent(); + IntRect targetFrameRect = targetFrame->view()->frameRect(); + + // Convert the target frame rect to source window content coordinates. This is only required + // if the parent frame is not the source. If the parent is the source, subframeRect + // is already in source content coordinates. + if (targetFrameParent != sourceFrame) + targetFrameRect = sourceFrame->view()->windowToContents(targetFrameParent->view()->contentsToWindow(targetFrameRect)); + + // Requested point is outside of target frame, return InvalidPoint. + if (!targetFrameRect.contains(point)) + return InvalidPoint; + + // Adjust the points to be relative to the target. + return targetFrame->view()->windowToContents(sourceFrame->view()->contentsToWindow(point)); +} + +VisibleSelection visibleSelectionForClosestActualWordStart(const VisibleSelection& selection) +{ + // VisibleSelection validation has a special case when the caret is at the end of a paragraph where + // it selects the paragraph marker. As well, if the position is at the end of a word, it will select + // only the space between words. We want to select an actual word so we move the selection to + // the start of the leftmost word if the character after the selection point is whitespace. + if (selection.selectionType() != VisibleSelection::RangeSelection && isWhitespace(selection.visibleStart().characterAfter())) { + VisibleSelection leftSelection(previousWordPosition(selection.start())); + bool leftSelectionIsOnWord = !isWhitespace(leftSelection.visibleStart().characterAfter()); + + VisibleSelection rangeSelection(endOfWord(leftSelection.start()), selection.visibleStart()); + int leftDistance = TextIterator::rangeLength(rangeSelection.toNormalizedRange().get()); + + VisibleSelection rightSelection(nextWordPosition(selection.start())); + rightSelection = previousWordPosition(rightSelection.start()); + bool rightSelectionIsOnWord = !isWhitespace(rightSelection.visibleStart().characterAfter()); + + rangeSelection = VisibleSelection(rightSelection.visibleStart(), selection.visibleStart()); + int rightDistance = TextIterator::rangeLength(rangeSelection.toNormalizedRange().get()); + + // Make sure we found an actual word. If not, return the original selection. + if (!leftSelectionIsOnWord && !rightSelectionIsOnWord) + return selection; + + if (!rightSelectionIsOnWord || (leftSelectionIsOnWord && leftDistance < rightDistance)) { + // Left is closer or right is invalid. + return leftSelection; + } + + // Right is closer or equal, or left was invalid. + return rightSelection; + } + + // No adjustment required. + return selection; +} + +} // DOMSupport +} // WebKit +} // BlackBerry |