[BlackBerry] Read "data-blackberry-text-selection-handle-position" attribute from...
[WebKit-https.git] / Source / WebKit / blackberry / WebKitSupport / DOMSupport.cpp
1 /*
2  * Copyright (C) 2011, 2012 Research In Motion Limited. All rights reserved.
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Lesser General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Lesser General Public License for more details.
13  *
14  * You should have received a copy of the GNU Lesser General Public
15  * License along with this library; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
17  */
18
19 #include "config.h"
20 #include "DOMSupport.h"
21
22 #include "FloatQuad.h"
23 #include "Frame.h"
24 #include "FrameView.h"
25 #include "HTMLFormElement.h"
26 #include "HTMLInputElement.h"
27 #include "HTMLNames.h"
28 #include "HTMLTextAreaElement.h"
29 #include "Node.h"
30 #include "Range.h"
31 #include "RenderObject.h"
32 #include "RenderText.h"
33 #include "RenderTextControl.h"
34 #include "TextIterator.h"
35 #include "VisiblePosition.h"
36 #include "VisibleSelection.h"
37
38 #include "htmlediting.h"
39 #include "visible_units.h"
40
41 #include <limits>
42
43 #include <wtf/text/WTFString.h>
44
45 using WTF::Vector;
46
47 using namespace WebCore;
48
49 namespace BlackBerry {
50 namespace WebKit {
51 namespace DOMSupport {
52
53 void visibleTextQuads(const VisibleSelection& selection, Vector<FloatQuad>& quads)
54 {
55     if (!selection.isRange())
56         return;
57
58     // Make sure that both start and end have valid nodes associated otherwise
59     // this can crash. See PR 220628.
60     if (!selection.start().anchorNode() || !selection.end().anchorNode())
61         return;
62
63     visibleTextQuads(*(selection.firstRange()), quads, true /* useSelectionHeight */);
64 }
65
66 void visibleTextQuads(const Range& range, Vector<FloatQuad>& quads, bool useSelectionHeight)
67 {
68     // Range::textQuads includes hidden text, which we don't want.
69     // To work around this, this is a copy of it which skips hidden elements.
70     Node* startContainer = range.startContainer();
71     Node* endContainer = range.endContainer();
72
73     if (!startContainer || !endContainer)
74         return;
75
76     Node* stopNode = range.pastLastNode();
77     for (Node* node = range.firstNode(); node && node != stopNode; node = node->traverseNextNode()) {
78         RenderObject* r = node->renderer();
79         if (!r || !r->isText())
80             continue;
81
82         if (r->style()->visibility() != VISIBLE)
83             continue;
84
85         RenderText* renderText = toRenderText(r);
86         int startOffset = node == startContainer ? range.startOffset() : 0;
87         int endOffset = node == endContainer ? range.endOffset() : std::numeric_limits<int>::max();
88         renderText->absoluteQuadsForRange(quads, startOffset, endOffset, useSelectionHeight);
89     }
90 }
91
92 bool isTextInputElement(Element* element)
93 {
94     return element->isTextFormControl()
95            || element->hasTagName(HTMLNames::textareaTag)
96            || element->isContentEditable();
97 }
98
99 bool isPasswordElement(const Element* element)
100 {
101     return element && element->hasTagName(HTMLNames::inputTag)
102            && static_cast<const HTMLInputElement*>(element)->isPasswordField();
103 }
104
105 WTF::String inputElementText(Element* element)
106 {
107     if (!element)
108         return WTF::String();
109
110     WTF::String elementText;
111     if (element->hasTagName(HTMLNames::inputTag)) {
112         const HTMLInputElement* inputElement = static_cast<const HTMLInputElement*>(element);
113         elementText = inputElement->value();
114     } else if (element->hasTagName(HTMLNames::textareaTag)) {
115         const HTMLTextAreaElement* inputElement = static_cast<const HTMLTextAreaElement*>(element);
116         elementText = inputElement->value();
117     } else if (element->isContentEditable()) {
118         RefPtr<Range> rangeForNode = rangeOfContents(element);
119         elementText = rangeForNode.get()->text();
120     }
121     return elementText;
122 }
123
124 WTF::String webWorksContext(const WebCore::Element* element)
125 {
126     if (!element)
127         return WTF::String();
128
129     DEFINE_STATIC_LOCAL(QualifiedName, webworksContextAttr, (nullAtom, "data-webworks-context", nullAtom));
130     if (element->fastHasAttribute(webworksContextAttr))
131         return element->fastGetAttribute(webworksContextAttr);
132
133     return WTF::String();
134 }
135
136 bool isElementTypePlugin(const Element* element)
137 {
138     if (!element)
139         return false;
140
141     if (element->hasTagName(HTMLNames::objectTag)
142         || element->hasTagName(HTMLNames::embedTag)
143         || element->hasTagName(HTMLNames::appletTag))
144         return true;
145
146     return false;
147 }
148
149 HTMLTextFormControlElement* toTextControlElement(Node* node)
150 {
151     if (!(node && node->isElementNode()))
152         return 0;
153
154     Element* element = static_cast<Element*>(node);
155     if (!element->isFormControlElement())
156         return 0;
157
158     HTMLFormControlElement* formElement = static_cast<HTMLFormControlElement*>(element);
159     if (!formElement->isTextFormControl())
160         return 0;
161
162     return static_cast<HTMLTextFormControlElement*>(formElement);
163 }
164
165 bool isPopupInputField(const Element* element)
166 {
167     return isDateTimeInputField(element) || isColorInputField(element);
168 }
169
170 bool isDateTimeInputField(const Element* element)
171 {
172     if (!element->hasTagName(HTMLNames::inputTag))
173         return false;
174
175     const HTMLInputElement* inputElement = static_cast<const HTMLInputElement*>(element);
176
177     // The following types have popup's.
178     if (inputElement->isDateField()
179         || inputElement->isDateTimeField()
180         || inputElement->isDateTimeLocalField()
181         || inputElement->isTimeField()
182         || inputElement->isMonthField())
183             return true;
184
185     return false;
186 }
187
188 bool isColorInputField(const Element* element)
189 {
190     if (!element->hasTagName(HTMLNames::inputTag))
191         return false;
192
193     const HTMLInputElement* inputElement = static_cast<const HTMLInputElement*>(element);
194
195 #if ENABLE(INPUT_TYPE_COLOR)
196     if (inputElement->isColorControl())
197         return true;
198 #endif
199
200     return false;
201 }
202
203 // This is a Tristate return to allow us to override name matching when
204 // the attribute is expressly requested for a field. Default indicates
205 // that the setting is On which is the default but not expressly requested
206 // for the element being checked. On indicates that it is directly added
207 // to the element.
208 AttributeState elementSupportsAutocorrect(const Element* element)
209 {
210     DEFINE_STATIC_LOCAL(QualifiedName, autocorrectAttr, (nullAtom, "autocorrect", nullAtom));
211     return elementAttributeState(element, autocorrectAttr);
212 }
213
214 AttributeState elementSupportsAutocomplete(const Element* element)
215 {
216     return elementAttributeState(element, HTMLNames::autocompleteAttr);
217 }
218
219 AttributeState elementSupportsSpellCheck(const Element* element)
220 {
221     return elementAttributeState(element, HTMLNames::spellcheckAttr);
222 }
223
224 AttributeState elementAttributeState(const Element* element, const QualifiedName& attributeName)
225 {
226     // First we check the input item itself. If the attribute is not defined,
227     // we check its parent form.
228     if (element->fastHasAttribute(attributeName)) {
229         AtomicString attributeString = element->fastGetAttribute(attributeName);
230         if (equalIgnoringCase(attributeString, "off"))
231             return Off;
232         if (equalIgnoringCase(attributeString, "on"))
233             return On;
234         // If we haven't returned, it wasn't set properly. Check the form for an explicit setting
235         // because the attribute was provided, but invalid.
236     }
237     if (element->isFormControlElement()) {
238         const HTMLFormControlElement* formElement = static_cast<const HTMLFormControlElement*>(element);
239         if (formElement->form() && formElement->form()->fastHasAttribute(attributeName)) {
240             AtomicString attributeString = formElement->form()->fastGetAttribute(attributeName);
241             if (equalIgnoringCase(attributeString, "off"))
242                 return Off;
243             if (equalIgnoringCase(attributeString, "on"))
244                 return On;
245         }
246     }
247
248     return Default;
249 }
250
251 bool elementHasContinuousSpellCheckingEnabled(const PassRefPtr<WebCore::Element> element)
252 {
253     return element && element->document()->frame() && element->document()->frame()->editor()->isContinuousSpellCheckingEnabled();
254 }
255
256 // Check if this is an input field that will be focused & require input support.
257 bool isTextBasedContentEditableElement(Element* element)
258 {
259     if (!element)
260         return false;
261
262     if (element->isReadOnlyNode() || !element->isEnabledFormControl())
263         return false;
264
265     if (isPopupInputField(element))
266         return false;
267
268     return element->isTextFormControl() || element->isContentEditable();
269 }
270
271 IntRect transformedBoundingBoxForRange(const Range& range)
272 {
273     // Based on Range::boundingBox, which does not handle transforms, and
274     // RenderObject::absoluteBoundingBoxRect, which does.
275     IntRect result;
276     Vector<FloatQuad> quads;
277     visibleTextQuads(range, quads);
278     const size_t n = quads.size();
279     for (size_t i = 0; i < n; ++i)
280         result.unite(quads[i].enclosingBoundingBox());
281
282     return result;
283 }
284
285 VisibleSelection visibleSelectionForInputElement(Element* element)
286 {
287     return visibleSelectionForRangeInputElement(element, 0, inputElementText(element).length());
288 }
289
290 VisibleSelection visibleSelectionForRangeInputElement(Element* element, int start, int end)
291 {
292     if (DOMSupport::toTextControlElement(element)) {
293         RenderTextControl* textRender = toRenderTextControl(element->renderer());
294         if (!textRender)
295             return VisibleSelection();
296
297         VisiblePosition startPosition = textRender->visiblePositionForIndex(start);
298         VisiblePosition endPosition;
299         if (start == end)
300             endPosition = startPosition;
301         else
302             endPosition = textRender->visiblePositionForIndex(end);
303
304         return VisibleSelection(startPosition, endPosition);
305     }
306
307     // Must be content editable, generate the range.
308     RefPtr<Range> selectionRange = TextIterator::rangeFromLocationAndLength(element, start, end - start);
309
310     if (!selectionRange)
311         return VisibleSelection();
312
313     if (start == end)
314         return VisibleSelection(selectionRange.get()->startPosition(), DOWNSTREAM);
315
316     VisiblePosition visibleStart(selectionRange->startPosition(), DOWNSTREAM);
317     VisiblePosition visibleEnd(selectionRange->endPosition(), SEL_DEFAULT_AFFINITY);
318
319     return VisibleSelection(visibleStart, visibleEnd);
320 }
321
322 static bool matchesReservedStringEmail(const AtomicString& string)
323 {
324     return string.contains("email", false /* caseSensitive */);
325 }
326
327 static bool matchesReservedStringUrl(const AtomicString& string)
328 {
329     return equalIgnoringCase("url", string);
330 }
331
332 bool elementIdOrNameIndicatesEmail(const HTMLInputElement* inputElement)
333 {
334     if (!inputElement)
335         return false;
336
337     if (matchesReservedStringEmail(inputElement->getIdAttribute()))
338         return true;
339
340     if (inputElement->fastHasAttribute(HTMLNames::nameAttr)) {
341         if (matchesReservedStringEmail(inputElement->fastGetAttribute(HTMLNames::nameAttr)))
342             return true;
343     }
344
345     return false;
346 }
347
348 bool elementIdOrNameIndicatesUrl(const HTMLInputElement* inputElement)
349 {
350     if (!inputElement)
351         return false;
352
353     if (matchesReservedStringUrl(inputElement->getIdAttribute()))
354         return true;
355
356     if (inputElement->fastHasAttribute(HTMLNames::nameAttr)) {
357         if (matchesReservedStringUrl(inputElement->fastGetAttribute(HTMLNames::nameAttr)))
358             return true;
359     }
360
361     return false;
362 }
363
364 static bool matchesReservedStringPreventingAutocomplete(const AtomicString& string)
365 {
366     if (matchesReservedStringEmail(string)
367         || matchesReservedStringUrl(string)
368         || string.contains("user", false /* caseSensitive */)
369         || string.contains("name", false /* caseSensitive */)
370         || string.contains("login", false /* caseSensitive */))
371         return true;
372
373     return false;
374 }
375
376 // This checks to see if an input element has a name or id attribute set to
377 // username or email. These are rough checks to avoid major sites that use
378 // login fields as input type=text and auto correction interfers with.
379 bool elementIdOrNameIndicatesNoAutocomplete(const Element* element)
380 {
381     if (!element->hasTagName(HTMLNames::inputTag))
382         return false;
383
384     AtomicString idAttribute = element->getIdAttribute();
385     if (matchesReservedStringPreventingAutocomplete(idAttribute))
386         return true;
387
388     if (element->fastHasAttribute(HTMLNames::nameAttr)) {
389         AtomicString nameAttribute = element->fastGetAttribute(HTMLNames::nameAttr);
390         if (matchesReservedStringPreventingAutocomplete(nameAttribute))
391             return true;
392     }
393
394     return false;
395 }
396
397 bool elementPatternIndicatesNumber(const HTMLInputElement* inputElement)
398 {
399     return elementPatternMatches("[0-9]", inputElement);
400 }
401
402 bool elementPatternIndicatesHexadecimal(const HTMLInputElement* inputElement)
403 {
404     return elementPatternMatches("[0-9a-fA-F]", inputElement);
405 }
406
407 bool elementPatternMatches(const char* pattern, const HTMLInputElement* inputElement)
408 {
409     WTF::String patternString(pattern);
410     if (!inputElement || patternString.isEmpty())
411         return false;
412
413     if (inputElement->fastHasAttribute(HTMLNames::patternAttr)) {
414         WTF::String patternAttribute = inputElement->fastGetAttribute(HTMLNames::patternAttr);
415         if (patternAttribute.startsWith(patternString)) {
416             // The pattern is for hexadecimal, make sure nothing else is permitted.
417
418             // Check if it was an exact match.
419             if (patternAttribute.length() == patternString.length())
420                 return true;
421
422             // Check for *
423             if (patternAttribute.length() == patternString.length() + 1 && patternAttribute[patternString.length()] == '*')
424                 return true;
425
426             // Is the regex specifying a character count?
427             if (patternAttribute[patternString.length()] != '{' || !patternAttribute.endsWith("}"))
428                 return false;
429
430             // Make sure the number in the regex is actually a number.
431             unsigned count = 0;
432             patternString = patternString + "{%d}";
433             return (sscanf(patternAttribute.latin1().data(), patternString.latin1().data() + '\0', &count) == 1) && count > 0;
434         }
435     }
436     return false;
437 }
438
439 IntPoint convertPointToFrame(const Frame* sourceFrame, const Frame* targetFrame, const IntPoint& point, const bool clampToTargetFrame)
440 {
441     ASSERT(sourceFrame && targetFrame);
442     if (sourceFrame == targetFrame)
443         return point;
444
445     ASSERT(sourceFrame->view() && targetFrame->view());
446     ASSERT(targetFrame->tree());
447
448     Frame* targetFrameParent = targetFrame->tree()->parent();
449     IntRect targetFrameRect = targetFrame->view()->frameRect();
450     IntPoint targetPoint = point;
451
452     // Convert the target frame rect to source window content coordinates. This is only required
453     // if the parent frame is not the source. If the parent is the source, subframeRect
454     // is already in source content coordinates.
455     if (targetFrameParent != sourceFrame)
456         targetFrameRect = sourceFrame->view()->windowToContents(targetFrameParent->view()->contentsToWindow(targetFrameRect));
457
458     // Requested point is outside of target frame, return InvalidPoint.
459     if (clampToTargetFrame && !targetFrameRect.contains(targetPoint))
460         targetPoint = IntPoint(targetPoint.x() < targetFrameRect.x() ? targetFrameRect.x() : std::min(targetPoint.x(), targetFrameRect.maxX()),
461                 targetPoint.y() < targetFrameRect.y() ? targetFrameRect.y() : std::min(targetPoint.y(), targetFrameRect.maxY()));
462     else if (!targetFrameRect.contains(targetPoint))
463         return InvalidPoint;
464
465     // Adjust the points to be relative to the target.
466     return targetFrame->view()->windowToContents(sourceFrame->view()->contentsToWindow(targetPoint));
467 }
468
469 VisibleSelection visibleSelectionForClosestActualWordStart(const VisibleSelection& selection)
470 {
471     // VisibleSelection validation has a special case when the caret is at the end of a paragraph where
472     // it selects the paragraph marker. As well, if the position is at the end of a word, it will select
473     // only the space between words. We want to select an actual word so we move the selection to
474     // the start of the leftmost word if the character after the selection point is whitespace.
475
476     if (selection.selectionType() != VisibleSelection::RangeSelection) {
477         int leftDistance = 0;
478         int rightDistance = 0;
479
480         VisibleSelection leftSelection(previousWordPosition(selection.start()));
481         bool leftSelectionIsOnWord = !isWhitespace(leftSelection.visibleStart().characterAfter()) && leftSelection.start().containerNode() == selection.start().containerNode();
482         if (leftSelectionIsOnWord) {
483             VisibleSelection rangeSelection(endOfWord(leftSelection.start()), selection.visibleStart());
484             leftDistance = TextIterator::rangeLength(rangeSelection.toNormalizedRange().get());
485         }
486
487         VisibleSelection rightSelection = previousWordPosition(nextWordPosition(selection.start()));
488         bool rightSelectionIsOnWord = !isWhitespace(rightSelection.visibleStart().characterAfter()) && rightSelection.start().containerNode() == selection.start().containerNode();
489         if (rightSelectionIsOnWord) {
490             VisibleSelection rangeSelection = VisibleSelection(rightSelection.visibleStart(), selection.visibleStart());
491             rightDistance = TextIterator::rangeLength(rangeSelection.toNormalizedRange().get());
492         }
493
494         // Make sure we found an actual word. If not, return the original selection.
495         if (!leftSelectionIsOnWord && !rightSelectionIsOnWord)
496             return selection;
497
498         if (!rightSelectionIsOnWord || (leftSelectionIsOnWord && leftDistance <= rightDistance)) {
499             // Left is closer or right is invalid.
500             return leftSelection;
501         }
502
503         // Right is closer or equal, or left was invalid.
504         return rightSelection;
505     }
506
507     // No adjustment required.
508     return selection;
509 }
510
511 int offsetFromStartOfBlock(const VisiblePosition offset)
512 {
513     RefPtr<Range> range = makeRange(startOfBlock(offset), offset);
514     if (!range)
515         return -1;
516
517     return range->text().latin1().length();
518 }
519
520 VisibleSelection visibleSelectionForFocusedBlock(Element* element)
521 {
522     int textLength = inputElementText(element).length();
523
524     if (DOMSupport::toTextControlElement(element)) {
525         RenderTextControl* textRender = toRenderTextControl(element->renderer());
526         if (!textRender)
527             return VisibleSelection();
528
529         VisiblePosition startPosition = textRender->visiblePositionForIndex(0);
530         VisiblePosition endPosition;
531
532         if (textLength)
533             endPosition = textRender->visiblePositionForIndex(textLength);
534         else
535             endPosition = startPosition;
536         return VisibleSelection(startPosition, endPosition);
537     }
538
539     // Must be content editable, generate the range.
540     RefPtr<Range> selectionRange = TextIterator::rangeFromLocationAndLength(element, 0, textLength);
541
542     if (!selectionRange)
543         return VisibleSelection();
544
545     if (!textLength)
546         return VisibleSelection(selectionRange->startPosition(), DOWNSTREAM);
547
548     return VisibleSelection(selectionRange->startPosition(), selectionRange->endPosition());
549 }
550
551 // This function is copied from WebCore/page/Page.cpp.
552 Frame* incrementFrame(Frame* curr, bool forward, bool wrapFlag)
553 {
554     return forward
555         ? curr->tree()->traverseNextWithWrap(wrapFlag)
556         : curr->tree()->traversePreviousWithWrap(wrapFlag);
557 }
558
559 PassRefPtr<Range> trimWhitespaceFromRange(PassRefPtr<Range> range)
560 {
561     return trimWhitespaceFromRange(VisiblePosition(range->startPosition()), VisiblePosition(range->endPosition()));
562 }
563
564 PassRefPtr<Range> trimWhitespaceFromRange(VisiblePosition startPosition, VisiblePosition endPosition)
565 {
566     if (startPosition == endPosition || isRangeTextAllWhitespace(startPosition, endPosition))
567         return 0;
568
569     while (isWhitespace(startPosition.characterAfter()))
570         startPosition = startPosition.next();
571
572     while (isWhitespace(endPosition.characterBefore()))
573         endPosition = endPosition.previous();
574
575     return makeRange(startPosition, endPosition);
576 }
577
578 bool isRangeTextAllWhitespace(VisiblePosition startPosition, VisiblePosition endPosition)
579 {
580     while (isWhitespace(startPosition.characterAfter())) {
581         startPosition = startPosition.next();
582
583         if (startPosition == endPosition)
584             return true;
585     }
586
587     return false;
588 }
589
590 bool isFixedPositionOrHasFixedPositionAncestor(RenderObject* renderer)
591 {
592     RenderObject* currentRenderer = renderer;
593     while (currentRenderer) {
594
595         if (currentRenderer->isOutOfFlowPositioned() && currentRenderer->style()->position() == FixedPosition)
596             return true;
597
598         currentRenderer = currentRenderer->parent();
599     }
600
601     return false;
602 }
603
604 Element* selectionContainerElement(const VisibleSelection& selection)
605 {
606     if (!selection.isRange())
607         return 0;
608
609     Node* startContainer = 0;
610     if (selection.firstRange())
611         startContainer = selection.firstRange()->startContainer();
612
613     if (!startContainer)
614         return 0;
615
616     Element* element = 0;
617     if (startContainer->isInShadowTree())
618         element = startContainer->shadowHost();
619     else if (startContainer->isElementNode())
620         element = static_cast<Element*>(startContainer);
621     else
622         element = startContainer->parentElement();
623
624     return element;
625 }
626
627 BlackBerry::Platform::RequestedHandlePosition elementHandlePositionAttribute(const WebCore::Element* element)
628 {
629     BlackBerry::Platform::RequestedHandlePosition position = BlackBerry::Platform::SmartPlacement;
630     if (!element)
631         return position;
632
633     DEFINE_STATIC_LOCAL(QualifiedName, qualifiedAttrNameForHandlePosition, (nullAtom, "data-blackberry-text-selection-handle-position", nullAtom));
634     AtomicString attributeString;
635     if (element->fastHasAttribute(qualifiedAttrNameForHandlePosition))
636         attributeString = element->fastGetAttribute(qualifiedAttrNameForHandlePosition);
637
638     if (attributeString.isNull() || attributeString.isEmpty())
639         return position;
640
641     if (equalIgnoringCase(attributeString, "above"))
642         position = BlackBerry::Platform::Above;
643     else if (equalIgnoringCase(attributeString, "below"))
644         position = BlackBerry::Platform::Below;
645     return position;
646 }
647
648 } // DOMSupport
649 } // WebKit
650 } // BlackBerry