42c8b31ef6bffa6828575844d891044fc699e8b3
[WebKit-https.git] / Source / WebCore / page / ios / FrameIOS.mm
1 /*
2  * Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012, 2013 Apple Inc. All rights reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1.  Redistributions of source code must retain the above copyright
8  *     notice, this list of conditions and the following disclaimer.
9  * 2.  Redistributions in binary form must reproduce the above copyright
10  *     notice, this list of conditions and the following disclaimer in the
11  *     documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
15  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16  * DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS BE LIABLE FOR ANY
17  * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
18  * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
19  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
20  * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
21  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
22  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  */
24
25 #import "config.h"
26 #import "Frame.h"
27
28 #if PLATFORM(IOS)
29
30 #import "CSSAnimationController.h"
31 #import "CommonVM.h"
32 #import "DOMWindow.h"
33 #import "Document.h"
34 #import "DocumentMarkerController.h"
35 #import "Editor.h"
36 #import "EditorClient.h"
37 #import "EventHandler.h"
38 #import "EventNames.h"
39 #import "FormController.h"
40 #import "FrameSelection.h"
41 #import "FrameView.h"
42 #import "HTMLAreaElement.h"
43 #import "HTMLBodyElement.h"
44 #import "HTMLDocument.h"
45 #import "HTMLHtmlElement.h"
46 #import "HTMLNames.h"
47 #import "HTMLObjectElement.h"
48 #import "HitTestRequest.h"
49 #import "HitTestResult.h"
50 #import "MainFrame.h"
51 #import "NodeRenderStyle.h"
52 #import "NodeTraversal.h"
53 #import "Page.h"
54 #import "PageTransitionEvent.h"
55 #import "PlatformScreen.h"
56 #import "PropertySetCSSStyleDeclaration.h"
57 #import "RenderLayer.h"
58 #import "RenderLayerCompositor.h"
59 #import "RenderTextControl.h"
60 #import "RenderView.h"
61 #import "RenderedDocumentMarker.h"
62 #import "ShadowRoot.h"
63 #import "TextBoundaries.h"
64 #import "TextIterator.h"
65 #import "VisiblePosition.h"
66 #import "VisibleUnits.h"
67 #import "WAKWindow.h"
68 #import "WebCoreSystemInterface.h"
69 #import <runtime/JSLock.h>
70 #import <wtf/BlockObjCExceptions.h>
71
72 using namespace WebCore::HTMLNames;
73 using namespace WTF::Unicode;
74
75 using JSC::JSLockHolder;
76
77 namespace WebCore {
78
79 // Create <html><body (style="...")></body></html> doing minimal amount of work.
80 void Frame::initWithSimpleHTMLDocument(const String& style, const URL& url)
81 {
82     m_loader->initForSynthesizedDocument(url);
83
84     RefPtr<HTMLDocument> document = HTMLDocument::createSynthesizedDocument(this, url);
85     document->setCompatibilityMode(DocumentCompatibilityMode::LimitedQuirksMode);
86     document->createDOMWindow();
87     setDocument(document);
88
89     auto rootElement = HTMLHtmlElement::create(*document);
90
91     auto body = HTMLBodyElement::create(*document);
92     if (!style.isEmpty())
93         body->setAttribute(HTMLNames::styleAttr, style);
94
95     rootElement->appendChild(body);
96     document->appendChild(rootElement);
97 }
98
99 const ViewportArguments& Frame::viewportArguments() const
100 {
101     return m_viewportArguments;
102 }
103
104 void Frame::setViewportArguments(const ViewportArguments& arguments)
105 {
106     m_viewportArguments = arguments;
107 }
108
109 NSArray *Frame::wordsInCurrentParagraph() const
110 {
111     document()->updateLayout();
112
113     if (!page() || !page()->selection().isCaret())
114         return nil;
115
116     VisiblePosition position(page()->selection().start(), page()->selection().affinity());
117     VisiblePosition end(position);
118     if (!isStartOfParagraph(end)) {
119         VisiblePosition previous = end.previous();
120         UChar c(previous.characterAfter());
121         if (!iswpunct(c) && !isSpaceOrNewline(c) && c != 0xA0)
122             end = startOfWord(end);
123     }
124     VisiblePosition start(startOfParagraph(end));
125
126     RefPtr<Range> searchRange(rangeOfContents(*document()));
127     setStart(searchRange.get(), start);
128     setEnd(searchRange.get(), end);
129
130     if (searchRange->collapsed())
131         return nil;
132
133     NSMutableArray *words = [NSMutableArray array];
134
135     WordAwareIterator it(*searchRange);
136     while (!it.atEnd()) {
137         StringView text = it.text();
138         int length = text.length();
139         if (length > 1 || !isSpaceOrNewline(text[0])) {
140             int startOfWordBoundary = 0;
141             for (int i = 1; i < length; i++) {
142                 if (isSpaceOrNewline(text[i]) || text[i] == 0xA0) {
143                     int wordLength = i - startOfWordBoundary;
144                     if (wordLength > 0) {
145                         RetainPtr<NSString> chunk = text.substring(startOfWordBoundary, wordLength).createNSString();
146                         [words addObject:chunk.get()];
147                     }
148                     startOfWordBoundary += wordLength + 1;
149                 }
150             }
151             if (startOfWordBoundary < length) {
152                 RetainPtr<NSString> chunk = text.substring(startOfWordBoundary, length - startOfWordBoundary).createNSString();
153                 [words addObject:chunk.get()];
154             }
155         }
156         it.advance();
157     }
158
159     if ([words count] > 0 && isEndOfParagraph(position) && !isStartOfParagraph(position)) {
160         VisiblePosition previous = position.previous();
161         UChar c(previous.characterAfter());
162         if (!isSpaceOrNewline(c) && c != 0xA0)
163             [words removeLastObject];
164     }
165
166     return words;
167 }
168
169 #define CHECK_FONT_SIZE 0
170 #define RECT_LOGGING 0
171
172 CGRect Frame::renderRectForPoint(CGPoint point, bool* isReplaced, float* fontSize) const
173 {
174     *isReplaced = false;
175     *fontSize = 0;
176
177     if (!m_doc || !m_doc->renderBox())
178         return CGRectZero;
179
180     // FIXME: why this layer check?
181     RenderLayer* layer = m_doc->renderBox()->layer();
182     if (!layer)
183         return CGRectZero;
184
185     HitTestResult result = eventHandler().hitTestResultAtPoint(IntPoint(roundf(point.x), roundf(point.y)));
186
187     Node* node = result.innerNode();
188     if (!node)
189         return CGRectZero;
190
191     RenderObject* hitRenderer = node->renderer();
192     RenderObject* renderer = hitRenderer;
193 #if RECT_LOGGING
194     printf("\n%f %f\n", point.x, point.y);
195 #endif
196     while (renderer && !renderer->isBody() && !renderer->isDocumentElementRenderer()) {
197 #if RECT_LOGGING
198         CGRect rect = renderer->absoluteBoundingBoxRect(true);
199         if (renderer->node()) {
200             const char *nodeName = renderer->node()->nodeName().ascii().data();
201             printf("%s %f %f %f %f\n", nodeName, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
202         }
203 #endif
204         if (renderer->isRenderBlock() || renderer->isInlineBlockOrInlineTable() || renderer->isReplaced()) {
205             *isReplaced = renderer->isReplaced();
206 #if CHECK_FONT_SIZE
207             for (RenderObject* textRenderer = hitRenderer; textRenderer; textRenderer = textRenderer->traverseNext(hitRenderer)) {
208                 if (textRenderer->isText()) {
209                     *fontSize = textRenderer->font(true).pixelSize();
210                     break;
211                 }
212             }
213 #endif
214             IntRect targetRect = renderer->absoluteBoundingBoxRect(true);
215             for (Widget* currView = &(renderer->view().frameView()); currView && currView != view(); currView = currView->parent())
216                 targetRect = currView->convertToContainingView(targetRect);
217
218             return targetRect;
219         }
220         renderer = renderer->parent();
221     }
222
223     return CGRectZero;
224 }
225
226 static Node* ancestorRespondingToScrollWheelEvents(const HitTestResult& hitTestResult, Node* terminationNode, IntRect* nodeBounds)
227 {
228     if (nodeBounds)
229         *nodeBounds = IntRect();
230
231     Node* scrollingAncestor = nullptr;
232     for (Node* node = hitTestResult.innerNode(); node && node != terminationNode && !node->hasTagName(HTMLNames::bodyTag); node = node->parentNode()) {
233         RenderObject* renderer = node->renderer();
234         if (!renderer)
235             continue;
236
237         if ((renderer->isTextField() || renderer->isTextArea()) && downcast<RenderTextControl>(*renderer).canScroll()) {
238             scrollingAncestor = node;
239             continue;
240         }
241
242         auto& style = renderer->style();
243
244         if (renderer->hasOverflowClip() &&
245             (style.overflowY() == OAUTO || style.overflowY() == OSCROLL || style.overflowY() == OOVERLAY ||
246              style.overflowX() == OAUTO || style.overflowX() == OSCROLL || style.overflowX() == OOVERLAY))
247             scrollingAncestor = node;
248     }
249
250     return scrollingAncestor;
251 }
252
253 static Node* ancestorRespondingToClickEvents(const HitTestResult& hitTestResult, Node* terminationNode, IntRect* nodeBounds)
254 {
255     bool bodyHasBeenReached = false;
256     bool pointerCursorStillValid = true;
257
258     if (nodeBounds)
259         *nodeBounds = IntRect();
260
261     Node* pointerCursorNode = nullptr;
262     for (Node* node = hitTestResult.innerNode(); node && node != terminationNode; node = node->parentInComposedTree()) {
263         // We only accept pointer nodes before reaching the body tag.
264         if (node->hasTagName(HTMLNames::bodyTag)) {
265 #if USE(UIKIT_EDITING)
266             // Make sure we cover the case of an empty editable body.
267             if (!pointerCursorNode && node->isContentEditable())
268                 pointerCursorNode = node;
269 #endif
270             bodyHasBeenReached = true;
271             pointerCursorStillValid = false;
272         }
273
274         // If we already have a pointer, and we reach a table, don't accept it.
275         if (pointerCursorNode && (node->hasTagName(HTMLNames::tableTag) || node->hasTagName(HTMLNames::tbodyTag)))
276             pointerCursorStillValid = false;
277
278         // If we haven't reached the body, and we are still paying attention to pointer cursors, and the node has a pointer cursor...
279         if (pointerCursorStillValid && node->renderStyle() && node->renderStyle()->cursor() == CursorPointer)
280             pointerCursorNode = node;
281         // We want the lowest unbroken chain of pointer cursors.
282         else if (pointerCursorNode)
283             pointerCursorStillValid = false;
284
285         if (node->willRespondToMouseClickEvents() || node->willRespondToMouseMoveEvents() || (is<Element>(*node) && downcast<Element>(*node).isMouseFocusable())) {
286             // If we're at the body or higher, use the pointer cursor node (which may be null).
287             if (bodyHasBeenReached)
288                 node = pointerCursorNode;
289
290             // If we are interested about the frame, use it.
291             if (nodeBounds) {
292                 // This is a check to see whether this node is an area element.  The only way this can happen is if this is the first check.
293                 if (node == hitTestResult.innerNode() && node != hitTestResult.innerNonSharedNode() && is<HTMLAreaElement>(*node))
294                     *nodeBounds = snappedIntRect(downcast<HTMLAreaElement>(*node).computeRect(hitTestResult.innerNonSharedNode()->renderer()));
295                 else if (node && node->renderer())
296                     *nodeBounds = node->renderer()->absoluteBoundingBoxRect(true);
297             }
298
299             return node;
300         }
301     }
302
303     return nullptr;
304 }
305
306 void Frame::betterApproximateNode(const IntPoint& testPoint, NodeQualifier nodeQualifierFunction, Node*& best, Node* failedNode, IntPoint& bestPoint, IntRect& bestRect, const IntRect& testRect)
307 {
308     IntRect candidateRect;
309     Node* candidate = nodeQualifierFunction(eventHandler().hitTestResultAtPoint(testPoint), failedNode, &candidateRect);
310
311     // Bail if we have no candidate, or the candidate is already equal to our current best node,
312     // or our candidate is the avoidedNode and there is a current best node.
313     if (!candidate || candidate == best)
314         return;
315
316     // The document should never be considered the best alternative.
317     if (candidate->isDocumentNode())
318         return;
319
320     if (best) {
321         IntRect bestIntersect = intersection(testRect, bestRect);
322         IntRect candidateIntersect = intersection(testRect, candidateRect);
323         // if the candidate intersection is smaller than the current best intersection, bail.
324         if (candidateIntersect.width() * candidateIntersect.height() <= bestIntersect.width() * bestIntersect.height())
325             return;
326     }
327
328     // At this point we either don't have a previous best, or our current candidate has a better intersection.
329     best = candidate;
330     bestPoint = testPoint;
331     bestRect = candidateRect;
332 }
333
334 bool Frame::hitTestResultAtViewportLocation(const FloatPoint& viewportLocation, HitTestResult& hitTestResult, IntPoint& center)
335 {
336     if (!m_doc || !m_doc->renderView())
337         return false;
338
339     FrameView* view = m_view.get();
340     if (!view)
341         return false;
342
343     center = view->windowToContents(roundedIntPoint(viewportLocation));
344     hitTestResult = eventHandler().hitTestResultAtPoint(center);
345     return true;
346 }
347
348 Node* Frame::qualifyingNodeAtViewportLocation(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, NodeQualifier nodeQualifierFunction, bool shouldApproximate)
349 {
350     adjustedViewportLocation = viewportLocation;
351
352     IntPoint testCenter;
353     HitTestResult candidateInfo;
354     if (!hitTestResultAtViewportLocation(viewportLocation, candidateInfo, testCenter))
355         return nullptr;
356
357     IntPoint bestPoint = testCenter;
358
359     // We have the candidate node at the location, check whether it or one of its ancestors passes
360     // the qualifier function, which typically checks if the node responds to a particular event type.
361     Node* approximateNode = nodeQualifierFunction(candidateInfo, 0, 0);
362
363 #if USE(UIKIT_EDITING)
364     if (approximateNode && approximateNode->isContentEditable()) {
365         // If we are in editable content, we look for the root editable element.
366         approximateNode = approximateNode->rootEditableElement();
367         // If we have a focusable node, there is no need to approximate.
368         if (approximateNode)
369             shouldApproximate = false;
370     }
371 #endif
372
373
374     float scale = page() ? page()->pageScaleFactor() : 1;
375     float ppiFactor = screenPPIFactor();
376
377     static const float unscaledSearchRadius = 15;
378     int searchRadius = static_cast<int>(unscaledSearchRadius * ppiFactor / scale);
379
380     if (approximateNode && shouldApproximate) {
381         const float testOffsets[] = {
382             -.3f, -.3f,
383             -.6f, -.6f,
384             +.3f, +.3f,
385             -.9f, -.9f,
386         };
387
388         Node* originalApproximateNode = approximateNode;
389         for (unsigned n = 0; n < WTF_ARRAY_LENGTH(testOffsets); n += 2) {
390             IntSize testOffset(testOffsets[n] * searchRadius, testOffsets[n + 1] * searchRadius);
391             IntPoint testPoint = testCenter + testOffset;
392
393             HitTestResult candidateInfo = eventHandler().hitTestResultAtPoint(testPoint);
394             Node* candidateNode = nodeQualifierFunction(candidateInfo, 0, 0);
395             if (candidateNode && candidateNode->isDescendantOf(originalApproximateNode)) {
396                 approximateNode = candidateNode;
397                 bestPoint = testPoint;
398                 break;
399             }
400         }
401     } else if (!approximateNode && shouldApproximate) {
402         // Grab the closest parent element of our failed candidate node.
403         Node* candidate = candidateInfo.innerNode();
404         Node* failedNode = candidate;
405
406         while (candidate && !candidate->isElementNode())
407             candidate = candidate->parentInComposedTree();
408
409         if (candidate)
410             failedNode = candidate;
411
412         // The center point was tested earlier.
413         const float testOffsets[] = {
414             -.3f, -.3f,
415             +.3f, -.3f,
416             -.3f, +.3f,
417             +.3f, +.3f,
418             -.6f, -.6f,
419             +.6f, -.6f,
420             -.6f, +.6f,
421             +.6f, +.6f,
422             -1.f, 0,
423             +1.f, 0,
424             0, +1.f,
425             0, -1.f,
426         };
427         IntRect bestFrame;
428         IntRect testRect(testCenter, IntSize());
429         testRect.inflate(searchRadius);
430         int currentTestRadius = 0;
431         for (unsigned n = 0; n < WTF_ARRAY_LENGTH(testOffsets); n += 2) {
432             IntSize testOffset(testOffsets[n] * searchRadius, testOffsets[n + 1] * searchRadius);
433             IntPoint testPoint = testCenter + testOffset;
434             int testRadius = std::max(abs(testOffset.width()), abs(testOffset.height()));
435             if (testRadius > currentTestRadius) {
436                 // Bail out with the best match within a radius
437                 currentTestRadius = testRadius;
438                 if (approximateNode)
439                     break;
440             }
441             betterApproximateNode(testPoint, nodeQualifierFunction, approximateNode, failedNode, bestPoint, bestFrame, testRect);
442         }
443     }
444
445     if (approximateNode) {
446         IntPoint p = m_view->contentsToWindow(bestPoint);
447         adjustedViewportLocation = p;
448 #if USE(UIKIT_EDITING)
449         if (approximateNode->isContentEditable()) {
450             // When in editable content, look for the root editable node again,
451             // since this could be the node found with the approximation.
452             approximateNode = approximateNode->rootEditableElement();
453         }
454 #endif
455     }
456
457     return approximateNode;
458 }
459
460 Node* Frame::deepestNodeAtLocation(const FloatPoint& viewportLocation)
461 {
462     IntPoint center;
463     HitTestResult hitTestResult;
464     if (!hitTestResultAtViewportLocation(viewportLocation, hitTestResult, center))
465         return nullptr;
466
467     return hitTestResult.innerNode();
468 }
469
470 Node* Frame::nodeRespondingToClickEvents(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation)
471 {
472     return qualifyingNodeAtViewportLocation(viewportLocation, adjustedViewportLocation, &ancestorRespondingToClickEvents, true);
473 }
474
475 Node* Frame::nodeRespondingToScrollWheelEvents(const FloatPoint& viewportLocation)
476 {
477     FloatPoint adjustedViewportLocation;
478     return qualifyingNodeAtViewportLocation(viewportLocation, adjustedViewportLocation, &ancestorRespondingToScrollWheelEvents, false);
479 }
480
481 int Frame::preferredHeight() const
482 {
483     Document* document = this->document();
484     if (!document)
485         return 0;
486
487     document->updateLayout();
488
489     auto* body = document->bodyOrFrameset();
490     if (!body)
491         return 0;
492
493     RenderObject* renderer = body->renderer();
494     if (!is<RenderBlock>(renderer))
495         return 0;
496
497     RenderBlock& block = downcast<RenderBlock>(*renderer);
498     return block.height() + block.marginTop() + block.marginBottom();
499 }
500
501 void Frame::updateLayout() const
502 {
503     Document* document = this->document();
504     if (!document)
505         return;
506
507     document->updateLayout();
508
509     if (FrameView* view = this->view())
510         view->adjustViewSize();
511 }
512
513 NSRect Frame::caretRect()
514 {
515     VisibleSelection visibleSelection = selection().selection();
516     if (visibleSelection.isNone())
517         return CGRectZero;
518     return visibleSelection.isCaret() ? selection().absoluteCaretBounds() : VisiblePosition(visibleSelection.end()).absoluteCaretBounds();
519 }
520
521 NSRect Frame::rectForScrollToVisible()
522 {
523     VisibleSelection selection(this->selection().selection());
524
525     if (selection.isNone())
526         return CGRectZero;
527
528     if (selection.isCaret())
529         return caretRect();
530
531     return unionRect(selection.visibleStart().absoluteCaretBounds(), selection.visibleEnd().absoluteCaretBounds());
532 }
533
534 unsigned Frame::formElementsCharacterCount() const
535 {
536     Document* document = this->document();
537     if (!document)
538         return 0;
539     return document->formController().formElementsCharacterCount();
540 }
541
542 void Frame::setTimersPaused(bool paused)
543 {
544     if (!m_page)
545         return;
546     JSLockHolder lock(commonVM());
547     if (paused)
548         m_page->suspendActiveDOMObjectsAndAnimations();
549     else
550         m_page->resumeActiveDOMObjectsAndAnimations();
551 }
552
553 void Frame::dispatchPageHideEventBeforePause()
554 {
555     ASSERT(isMainFrame());
556     if (!isMainFrame())
557         return;
558
559     for (Frame* frame = this; frame; frame = frame->tree().traverseNext(this))
560         frame->document()->domWindow()->dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, true), document());
561 }
562
563 void Frame::dispatchPageShowEventBeforeResume()
564 {
565     ASSERT(isMainFrame());
566     if (!isMainFrame())
567         return;
568
569     for (Frame* frame = this; frame; frame = frame->tree().traverseNext(this))
570         frame->document()->domWindow()->dispatchEvent(PageTransitionEvent::create(eventNames().pageshowEvent, true), document());
571 }
572
573 void Frame::setRangedSelectionBaseToCurrentSelection()
574 {
575     m_rangedSelectionBase = selection().selection();
576 }
577
578 void Frame::setRangedSelectionBaseToCurrentSelectionStart()
579 {
580     const VisibleSelection& visibleSelection = selection().selection();
581     m_rangedSelectionBase = VisibleSelection(visibleSelection.start(), visibleSelection.affinity());
582 }
583
584 void Frame::setRangedSelectionBaseToCurrentSelectionEnd()
585 {
586     const VisibleSelection& visibleSelection = selection().selection();
587     m_rangedSelectionBase = VisibleSelection(visibleSelection.end(), visibleSelection.affinity());
588 }
589
590 VisibleSelection Frame::rangedSelectionBase() const
591 {
592     return m_rangedSelectionBase;
593 }
594
595 void Frame::clearRangedSelectionInitialExtent()
596 {
597     m_rangedSelectionInitialExtent = VisibleSelection();
598 }
599
600 void Frame::setRangedSelectionInitialExtentToCurrentSelectionStart()
601 {
602     const VisibleSelection& visibleSelection = selection().selection();
603     m_rangedSelectionInitialExtent = VisibleSelection(visibleSelection.start(), visibleSelection.affinity());
604 }
605
606 void Frame::setRangedSelectionInitialExtentToCurrentSelectionEnd()
607 {
608     const VisibleSelection& visibleSelection = selection().selection();
609     m_rangedSelectionInitialExtent = VisibleSelection(visibleSelection.end(), visibleSelection.affinity());
610 }
611
612 VisibleSelection Frame::rangedSelectionInitialExtent() const
613 {
614     return m_rangedSelectionInitialExtent;
615 }
616
617 void Frame::recursiveSetUpdateAppearanceEnabled(bool enabled)
618 {
619     selection().setUpdateAppearanceEnabled(enabled);
620     for (Frame* child = tree().firstChild(); child; child = child->tree().nextSibling())
621         child->recursiveSetUpdateAppearanceEnabled(enabled);
622 }
623
624 // FIXME: Break this function up into pieces with descriptive function names so that it's easier to follow.
625 NSArray *Frame::interpretationsForCurrentRoot() const
626 {
627     if (!document())
628         return nil;
629
630     auto* root = selection().selection().selectionType() == VisibleSelection::NoSelection ? document()->bodyOrFrameset() : selection().selection().rootEditableElement();
631     unsigned rootChildCount = root->countChildNodes();
632     auto rangeOfRootContents = Range::create(*document(), createLegacyEditingPosition(root, 0), createLegacyEditingPosition(root, rootChildCount));
633
634     auto markersInRoot = document()->markers().markersInRange(rangeOfRootContents, DocumentMarker::DictationPhraseWithAlternatives);
635
636     // There are no phrases with alternatives, so there is just one interpretation.
637     if (markersInRoot.isEmpty())
638         return [NSArray arrayWithObject:plainText(rangeOfRootContents.ptr())];
639
640     // The number of interpretations will be i1 * i2 * ... * iN, where iX is the number of interpretations for the Xth phrase with alternatives.
641     size_t interpretationsCount = 1;
642
643     for (auto* marker : markersInRoot)
644         interpretationsCount *= marker->alternatives().size() + 1;
645
646     Vector<Vector<UChar>> interpretations;
647     interpretations.grow(interpretationsCount);
648
649     Position precedingTextStartPosition = createLegacyEditingPosition(root, 0);
650
651     unsigned combinationsSoFar = 1;
652
653     Node* pastLastNode = rangeOfRootContents->pastLastNode();
654     for (Node* node = rangeOfRootContents->firstNode(); node != pastLastNode; node = NodeTraversal::next(*node)) {
655         for (auto* marker : document()->markers().markersFor(node, DocumentMarker::DictationPhraseWithAlternatives)) {
656             // First, add text that precede the marker.
657             if (precedingTextStartPosition != createLegacyEditingPosition(node, marker->startOffset())) {
658                 RefPtr<Range> precedingTextRange = Range::create(*document(), precedingTextStartPosition, createLegacyEditingPosition(node, marker->startOffset()));
659                 String precedingText = plainText(precedingTextRange.get());
660                 if (!precedingText.isEmpty()) {
661                     for (auto& interpretation : interpretations)
662                         append(interpretation, precedingText);
663                 }
664             }
665
666             RefPtr<Range> rangeForMarker = Range::create(*document(), createLegacyEditingPosition(node, marker->startOffset()), createLegacyEditingPosition(node, marker->endOffset()));
667             String visibleTextForMarker = plainText(rangeForMarker.get());
668             size_t interpretationsCountForCurrentMarker = marker->alternatives().size() + 1;
669             for (size_t i = 0; i < interpretationsCount; ++i) {
670                 // Determine text for the ith interpretation. It will either be the visible text, or one of its
671                 // alternatives stored in the marker.
672
673                 size_t indexOfInterpretationForCurrentMarker = (i / combinationsSoFar) % interpretationsCountForCurrentMarker;
674                 if (!indexOfInterpretationForCurrentMarker)
675                     append(interpretations[i], visibleTextForMarker);
676                 else
677                     append(interpretations[i], marker->alternatives().at(i % marker->alternatives().size()));
678             }
679
680             combinationsSoFar *= interpretationsCountForCurrentMarker;
681
682             precedingTextStartPosition = createLegacyEditingPosition(node, marker->endOffset());
683         }
684     }
685
686     // Finally, add any text after the last marker.
687     RefPtr<Range> afterLastMarkerRange = Range::create(*document(), precedingTextStartPosition, createLegacyEditingPosition(root, rootChildCount));
688     String textAfterLastMarker = plainText(afterLastMarkerRange.get());
689     if (!textAfterLastMarker.isEmpty()) {
690         for (auto& interpretation : interpretations)
691             append(interpretation, textAfterLastMarker);
692     }
693
694     NSMutableArray *result = [NSMutableArray array];
695     for (auto& interpretation : interpretations)
696         [result addObject:adoptNS([[NSString alloc] initWithCharacters:interpretation.data() length:interpretation.size()]).get()];
697
698     return result;
699 }
700
701 static bool anyFrameHasTiledLayers(Frame* rootFrame)
702 {
703     for (Frame* frame = rootFrame; frame; frame = frame->tree().traverseNext(rootFrame)) {
704         if (frame->containsTiledBackingLayers())
705             return true;
706     }
707     return false;
708 }
709
710 void Frame::viewportOffsetChanged(ViewportOffsetChangeType changeType)
711 {
712     if (changeType == IncrementalScrollOffset) {
713         if (anyFrameHasTiledLayers(this)) {
714             if (RenderView* root = contentRenderer())
715                 root->compositor().didChangeVisibleRect();
716         }
717     }
718
719     if (changeType == CompletedScrollOffset) {
720         if (RenderView* root = contentRenderer())
721             root->compositor().updateCompositingLayers(CompositingUpdateType::OnScroll);
722     }
723 }
724
725 bool Frame::containsTiledBackingLayers() const
726 {
727     if (RenderView* root = contentRenderer())
728         return root->compositor().hasNonMainLayersWithTiledBacking();
729
730     return false;
731 }
732
733 void Frame::overflowScrollPositionChangedForNode(const IntPoint& position, Node* node, bool isUserScroll)
734 {
735     RenderObject* renderer = node->renderer();
736     if (!renderer || !renderer->hasLayer())
737         return;
738
739     RenderLayer& layer = *downcast<RenderBoxModelObject>(*renderer).layer();
740
741     layer.setIsUserScroll(isUserScroll);
742     layer.scrollToOffsetWithoutAnimation(position);
743     layer.setIsUserScroll(false);
744     layer.didEndScroll(); // FIXME: Should we always call this?
745 }
746
747 void Frame::resetAllGeolocationPermission()
748 {
749     if (document()->domWindow())
750         document()->domWindow()->resetAllGeolocationPermission();
751
752     for (Frame* child = tree().firstChild(); child; child = child->tree().nextSibling())
753         child->resetAllGeolocationPermission();
754 }
755
756 } // namespace WebCore
757 #endif // PLATFORM(IOS)