Node.appendChild(null) / replaceChild(null, null) / removeChild(null) / insertBefore...
[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 "AnimationController.h"
31 #import "BlockExceptions.h"
32 #import "DOMCSSStyleDeclarationInternal.h"
33 #import "DOMCore.h"
34 #import "DOMInternal.h"
35 #import "DOMNodeInternal.h"
36 #import "DOMWindow.h"
37 #import "Document.h"
38 #import "DocumentMarkerController.h"
39 #import "Editor.h"
40 #import "EditorClient.h"
41 #import "EventHandler.h"
42 #import "EventNames.h"
43 #import "FormController.h"
44 #import "FrameSelection.h"
45 #import "FrameView.h"
46 #import "HTMLAreaElement.h"
47 #import "HTMLDocument.h"
48 #import "HTMLElement.h"
49 #import "HTMLNames.h"
50 #import "HTMLObjectElement.h"
51 #import "HitTestRequest.h"
52 #import "HitTestResult.h"
53 #import "JSDOMWindowBase.h"
54 #import "MainFrame.h"
55 #import "NodeRenderStyle.h"
56 #import "NodeTraversal.h"
57 #import "Page.h"
58 #import "PageTransitionEvent.h"
59 #import "PlatformScreen.h"
60 #import "PropertySetCSSStyleDeclaration.h"
61 #import "RenderLayer.h"
62 #import "RenderLayerCompositor.h"
63 #import "RenderTextControl.h"
64 #import "RenderView.h"
65 #import "RenderedDocumentMarker.h"
66 #import "TextBoundaries.h"
67 #import "TextIterator.h"
68 #import "VisiblePosition.h"
69 #import "VisibleUnits.h"
70 #import "WAKWindow.h"
71 #import "WebCoreSystemInterface.h"
72 #import <runtime/JSLock.h>
73
74 using namespace WebCore::HTMLNames;
75 using namespace WTF::Unicode;
76
77 using JSC::JSLockHolder;
78
79 namespace WebCore {
80
81 // Create <html><body (style="...")></body></html> doing minimal amount of work.
82 void Frame::initWithSimpleHTMLDocument(const String& style, const URL& url)
83 {
84     m_loader.initForSynthesizedDocument(url);
85
86     RefPtr<HTMLDocument> document = HTMLDocument::createSynthesizedDocument(this, url);
87     document->setCompatibilityMode(DocumentCompatibilityMode::LimitedQuirksMode);
88     document->createDOMWindow();
89     setDocument(document);
90
91     ExceptionCode ec;
92     RefPtr<Element> rootElement = document->createElementNS(xhtmlNamespaceURI, ASCIILiteral("html"), ec);
93
94     RefPtr<Element> body = document->createElementNS(xhtmlNamespaceURI, ASCIILiteral("body"), ec);
95     if (!style.isEmpty())
96         body->setAttribute(HTMLNames::styleAttr, style);
97
98     rootElement->appendChild(body.releaseNonNull(), ec);
99     document->appendChild(rootElement.releaseNonNull(), ec);
100 }
101
102 const ViewportArguments& Frame::viewportArguments() const
103 {
104     return m_viewportArguments;
105 }
106
107 void Frame::setViewportArguments(const ViewportArguments& arguments)
108 {
109     m_viewportArguments = arguments;
110 }
111
112 // FIXME: Extract the common code in indexCountOfWordPrecedingSelection() and wordsInCurrentParagraph() into a shared function.
113 int Frame::indexCountOfWordPrecedingSelection(NSString *word) const
114 {
115     int result = -1;
116
117     if (!page() || page()->selection().isNone())
118         return result;
119
120     RefPtr<Range> searchRange(rangeOfContents(*document()));
121     VisiblePosition start(page()->selection().start(), page()->selection().affinity());
122     VisiblePosition oneBeforeStart = start.previous();
123
124     setEnd(searchRange.get(), oneBeforeStart.isNotNull() ? oneBeforeStart : start);
125
126     if (searchRange->collapsed())
127         return result;
128
129     WordAwareIterator it(*searchRange);
130     while (!it.atEnd()) {
131         StringView text = it.text();
132         int length = text.length();
133         if (length > 1 || !isSpaceOrNewline(text[0])) {
134             int startOfWordBoundary = 0;
135             for (int i = 1; i < length; i++) {
136                 if (isSpaceOrNewline(text[i]) || text[i] == 0xA0) {
137                     int wordLength = i - startOfWordBoundary;
138                     RetainPtr<NSString> chunk = text.substring(startOfWordBoundary, wordLength).createNSStringWithoutCopying();
139                     if ([chunk isEqualToString:word])
140                         ++result;
141                     startOfWordBoundary += wordLength + 1;
142                 }
143             }
144             if (startOfWordBoundary < length) {
145                 RetainPtr<NSString> chunk = text.substring(startOfWordBoundary, length - startOfWordBoundary).createNSStringWithoutCopying();
146                 if ([chunk isEqualToString:word])
147                     ++result;
148             }
149         }
150         it.advance();
151     }
152
153     return result + 1;
154 }
155
156 // FIXME: Extract the common code in indexCountOfWordPrecedingSelection() and wordsInCurrentParagraph() into a shared function.
157 NSArray *Frame::wordsInCurrentParagraph() const
158 {
159     document()->updateLayout();
160
161     if (!page() || !page()->selection().isCaret())
162         return nil;
163
164     VisiblePosition position(page()->selection().start(), page()->selection().affinity());
165     VisiblePosition end(position);
166     if (!isStartOfParagraph(end)) {
167         VisiblePosition previous = end.previous();
168         UChar c(previous.characterAfter());
169         if (!iswpunct(c) && !isSpaceOrNewline(c) && c != 0xA0)
170             end = startOfWord(end);
171     }
172     VisiblePosition start(startOfParagraph(end));
173
174     RefPtr<Range> searchRange(rangeOfContents(*document()));
175     setStart(searchRange.get(), start);
176     setEnd(searchRange.get(), end);
177
178     if (searchRange->collapsed())
179         return nil;
180
181     NSMutableArray *words = [NSMutableArray array];
182
183     WordAwareIterator it(*searchRange);
184     while (!it.atEnd()) {
185         StringView text = it.text();
186         int length = text.length();
187         if (length > 1 || !isSpaceOrNewline(text[0])) {
188             int startOfWordBoundary = 0;
189             for (int i = 1; i < length; i++) {
190                 if (isSpaceOrNewline(text[i]) || text[i] == 0xA0) {
191                     int wordLength = i - startOfWordBoundary;
192                     if (wordLength > 0) {
193                         RetainPtr<NSString> chunk = text.substring(startOfWordBoundary, wordLength).createNSString();
194                         [words addObject:chunk.get()];
195                     }
196                     startOfWordBoundary += wordLength + 1;
197                 }
198             }
199             if (startOfWordBoundary < length) {
200                 RetainPtr<NSString> chunk = text.substring(startOfWordBoundary, length - startOfWordBoundary).createNSString();
201                 [words addObject:chunk.get()];
202             }
203         }
204         it.advance();
205     }
206
207     if ([words count] > 0 && isEndOfParagraph(position) && !isStartOfParagraph(position)) {
208         VisiblePosition previous = position.previous();
209         UChar c(previous.characterAfter());
210         if (!isSpaceOrNewline(c) && c != 0xA0)
211             [words removeLastObject];
212     }
213
214     return words;
215 }
216
217 #define CHECK_FONT_SIZE 0
218 #define RECT_LOGGING 0
219
220 CGRect Frame::renderRectForPoint(CGPoint point, bool* isReplaced, float* fontSize) const
221 {
222     *isReplaced = false;
223     *fontSize = 0;
224
225     if (!m_doc || !m_doc->renderBox())
226         return CGRectZero;
227
228     // FIXME: why this layer check?
229     RenderLayer* layer = m_doc->renderBox()->layer();
230     if (!layer)
231         return CGRectZero;
232
233     HitTestResult result = eventHandler().hitTestResultAtPoint(IntPoint(roundf(point.x), roundf(point.y)));
234
235     Node* node = result.innerNode();
236     if (!node)
237         return CGRectZero;
238
239     RenderObject* hitRenderer = node->renderer();
240     RenderObject* renderer = hitRenderer;
241 #if RECT_LOGGING
242     printf("\n%f %f\n", point.x, point.y);
243 #endif
244     while (renderer && !renderer->isBody() && !renderer->isRoot()) {
245 #if RECT_LOGGING
246         CGRect rect = renderer->absoluteBoundingBoxRect(true);
247         if (renderer->node()) {
248             const char *nodeName = renderer->node()->nodeName().ascii().data();
249             printf("%s %f %f %f %f\n", nodeName, rect.origin.x, rect.origin.y, rect.size.width, rect.size.height);
250         }
251 #endif
252         if (renderer->isRenderBlock() || renderer->isInlineBlockOrInlineTable() || renderer->isReplaced()) {
253             *isReplaced = renderer->isReplaced();
254 #if CHECK_FONT_SIZE
255             for (RenderObject* textRenderer = hitRenderer; textRenderer; textRenderer = textRenderer->traverseNext(hitRenderer)) {
256                 if (textRenderer->isText()) {
257                     *fontSize = textRenderer->font(true).pixelSize();
258                     break;
259                 }
260             }
261 #endif
262             IntRect targetRect = renderer->absoluteBoundingBoxRect(true);
263             for (Widget* currView = &(renderer->view().frameView()); currView && currView != view(); currView = currView->parent())
264                 targetRect = currView->convertToContainingView(targetRect);
265
266             return targetRect;
267         }
268         renderer = renderer->parent();
269     }
270
271     return CGRectZero;
272 }
273
274 static Node* ancestorRespondingToScrollWheelEvents(const HitTestResult& hitTestResult, Node* terminationNode, IntRect* nodeBounds)
275 {
276     if (nodeBounds)
277         *nodeBounds = IntRect();
278
279     Node* scrollingAncestor = nullptr;
280     for (Node* node = hitTestResult.innerNode(); node && node != terminationNode && !node->hasTagName(HTMLNames::bodyTag); node = node->parentNode()) {
281         RenderObject* renderer = node->renderer();
282         if (!renderer)
283             continue;
284
285         if ((renderer->isTextField() || renderer->isTextArea()) && downcast<RenderTextControl>(*renderer).canScroll()) {
286             scrollingAncestor = node;
287             continue;
288         }
289
290         RenderStyle& style = renderer->style();
291
292         if (renderer->hasOverflowClip() &&
293             (style.overflowY() == OAUTO || style.overflowY() == OSCROLL || style.overflowY() == OOVERLAY ||
294              style.overflowX() == OAUTO || style.overflowX() == OSCROLL || style.overflowX() == OOVERLAY))
295             scrollingAncestor = node;
296     }
297
298     return scrollingAncestor;
299 }
300
301 static Node* ancestorRespondingToClickEvents(const HitTestResult& hitTestResult, Node* terminationNode, IntRect* nodeBounds)
302 {
303     bool bodyHasBeenReached = false;
304     bool pointerCursorStillValid = true;
305
306     if (nodeBounds)
307         *nodeBounds = IntRect();
308
309     Node* pointerCursorNode = nullptr;
310     for (Node* node = hitTestResult.innerNode(); node && node != terminationNode; node = node->parentNode()) {
311         ASSERT(!node->isInShadowTree());
312
313         // We only accept pointer nodes before reaching the body tag.
314         if (node->hasTagName(HTMLNames::bodyTag)) {
315 #if USE(UIKIT_EDITING)
316             // Make sure we cover the case of an empty editable body.
317             if (!pointerCursorNode && node->isContentEditable())
318                 pointerCursorNode = node;
319 #endif
320             bodyHasBeenReached = true;
321             pointerCursorStillValid = false;
322         }
323
324         // If we already have a pointer, and we reach a table, don't accept it.
325         if (pointerCursorNode && (node->hasTagName(HTMLNames::tableTag) || node->hasTagName(HTMLNames::tbodyTag)))
326             pointerCursorStillValid = false;
327
328         // If we haven't reached the body, and we are still paying attention to pointer cursors, and the node has a pointer cursor...
329         if (pointerCursorStillValid && node->renderStyle() && node->renderStyle()->cursor() == CursorPointer)
330             pointerCursorNode = node;
331         // We want the lowest unbroken chain of pointer cursors.
332         else if (pointerCursorNode)
333             pointerCursorStillValid = false;
334
335         if (node->willRespondToMouseClickEvents() || node->willRespondToMouseMoveEvents()) {
336             // If we're at the body or higher, use the pointer cursor node (which may be null).
337             if (bodyHasBeenReached)
338                 node = pointerCursorNode;
339
340             // If we are interested about the frame, use it.
341             if (nodeBounds) {
342                 // 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.
343                 if (node == hitTestResult.innerNode() && node != hitTestResult.innerNonSharedNode() && is<HTMLAreaElement>(*node))
344                     *nodeBounds = snappedIntRect(downcast<HTMLAreaElement>(*node).computeRect(hitTestResult.innerNonSharedNode()->renderer()));
345                 else if (node && node->renderer())
346                     *nodeBounds = node->renderer()->absoluteBoundingBoxRect(true);
347             }
348
349             return node;
350         }
351     }
352
353     return nullptr;
354 }
355
356 void Frame::betterApproximateNode(const IntPoint& testPoint, NodeQualifier nodeQualifierFunction, Node*& best, Node* failedNode, IntPoint& bestPoint, IntRect& bestRect, const IntRect& testRect)
357 {
358     IntRect candidateRect;
359     Node* candidate = nodeQualifierFunction(eventHandler().hitTestResultAtPoint(testPoint), failedNode, &candidateRect);
360
361     // Bail if we have no candidate, or the candidate is already equal to our current best node,
362     // or our candidate is the avoidedNode and there is a current best node.
363     if (!candidate || candidate == best)
364         return;
365
366     // The document should never be considered the best alternative.
367     if (candidate->isDocumentNode())
368         return;
369
370     if (best) {
371         IntRect bestIntersect = intersection(testRect, bestRect);
372         IntRect candidateIntersect = intersection(testRect, candidateRect);
373         // if the candidate intersection is smaller than the current best intersection, bail.
374         if (candidateIntersect.width() * candidateIntersect.height() <= bestIntersect.width() * bestIntersect.height())
375             return;
376     }
377
378     // At this point we either don't have a previous best, or our current candidate has a better intersection.
379     best = candidate;
380     bestPoint = testPoint;
381     bestRect = candidateRect;
382 }
383
384 bool Frame::hitTestResultAtViewportLocation(const FloatPoint& viewportLocation, HitTestResult& hitTestResult, IntPoint& center)
385 {
386     if (!m_doc || !m_doc->renderView())
387         return false;
388
389     FrameView* view = m_view.get();
390     if (!view)
391         return false;
392
393     center = view->windowToContents(roundedIntPoint(viewportLocation));
394     hitTestResult = eventHandler().hitTestResultAtPoint(center);
395     return true;
396 }
397
398 Node* Frame::qualifyingNodeAtViewportLocation(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation, NodeQualifier nodeQualifierFunction, bool shouldApproximate)
399 {
400     adjustedViewportLocation = viewportLocation;
401
402     IntPoint testCenter;
403     HitTestResult candidateInfo;
404     if (!hitTestResultAtViewportLocation(viewportLocation, candidateInfo, testCenter))
405         return nullptr;
406
407     IntPoint bestPoint = testCenter;
408
409     // We have the candidate node at the location, check whether it or one of its ancestors passes
410     // the qualifier function, which typically checks if the node responds to a particular event type.
411     Node* approximateNode = nodeQualifierFunction(candidateInfo, 0, 0);
412
413 #if USE(UIKIT_EDITING)
414     if (approximateNode && approximateNode->isContentEditable()) {
415         // If we are in editable content, we look for the root editable element.
416         approximateNode = approximateNode->rootEditableElement();
417         // If we have a focusable node, there is no need to approximate.
418         if (approximateNode)
419             shouldApproximate = false;
420     }
421 #endif
422
423
424     float scale = page() ? page()->pageScaleFactor() : 1;
425     float ppiFactor = screenPPIFactor();
426
427     static const float unscaledSearchRadius = 15;
428     int searchRadius = static_cast<int>(unscaledSearchRadius * ppiFactor / scale);
429
430     if (approximateNode && shouldApproximate) {
431         const float testOffsets[] = {
432             -.3f, -.3f,
433             -.6f, -.6f,
434             +.3f, +.3f,
435             -.9f, -.9f,
436         };
437
438         Node* originalApproximateNode = approximateNode;
439         for (unsigned n = 0; n < WTF_ARRAY_LENGTH(testOffsets); n += 2) {
440             IntSize testOffset(testOffsets[n] * searchRadius, testOffsets[n + 1] * searchRadius);
441             IntPoint testPoint = testCenter + testOffset;
442
443             HitTestResult candidateInfo = eventHandler().hitTestResultAtPoint(testPoint);
444             Node* candidateNode = nodeQualifierFunction(candidateInfo, 0, 0);
445             if (candidateNode && candidateNode->isDescendantOf(originalApproximateNode)) {
446                 approximateNode = candidateNode;
447                 bestPoint = testPoint;
448                 break;
449             }
450         }
451     } else if (!approximateNode && shouldApproximate) {
452         // Grab the closest parent element of our failed candidate node.
453         Node* candidate = candidateInfo.innerNode();
454         Node* failedNode = candidate;
455
456         while (candidate && !candidate->isElementNode())
457             candidate = candidate->parentNode();
458
459         if (candidate)
460             failedNode = candidate;
461
462         // The center point was tested earlier.
463         const float testOffsets[] = {
464             -.3f, -.3f,
465             +.3f, -.3f,
466             -.3f, +.3f,
467             +.3f, +.3f,
468             -.6f, -.6f,
469             +.6f, -.6f,
470             -.6f, +.6f,
471             +.6f, +.6f,
472             -1.f, 0,
473             +1.f, 0,
474             0, +1.f,
475             0, -1.f,
476         };
477         IntRect bestFrame;
478         IntRect testRect(testCenter, IntSize());
479         testRect.inflate(searchRadius);
480         int currentTestRadius = 0;
481         for (unsigned n = 0; n < WTF_ARRAY_LENGTH(testOffsets); n += 2) {
482             IntSize testOffset(testOffsets[n] * searchRadius, testOffsets[n + 1] * searchRadius);
483             IntPoint testPoint = testCenter + testOffset;
484             int testRadius = std::max(abs(testOffset.width()), abs(testOffset.height()));
485             if (testRadius > currentTestRadius) {
486                 // Bail out with the best match within a radius
487                 currentTestRadius = testRadius;
488                 if (approximateNode)
489                     break;
490             }
491             betterApproximateNode(testPoint, nodeQualifierFunction, approximateNode, failedNode, bestPoint, bestFrame, testRect);
492         }
493     }
494
495     if (approximateNode) {
496         IntPoint p = m_view->contentsToWindow(bestPoint);
497         adjustedViewportLocation = p;
498 #if USE(UIKIT_EDITING)
499         if (approximateNode->isContentEditable()) {
500             // When in editable content, look for the root editable node again,
501             // since this could be the node found with the approximation.
502             approximateNode = approximateNode->rootEditableElement();
503         }
504 #endif
505     }
506
507     return approximateNode;
508 }
509
510 Node* Frame::deepestNodeAtLocation(const FloatPoint& viewportLocation)
511 {
512     IntPoint center;
513     HitTestResult hitTestResult;
514     if (!hitTestResultAtViewportLocation(viewportLocation, hitTestResult, center))
515         return nullptr;
516
517     return hitTestResult.innerNode();
518 }
519
520 Node* Frame::nodeRespondingToClickEvents(const FloatPoint& viewportLocation, FloatPoint& adjustedViewportLocation)
521 {
522     return qualifyingNodeAtViewportLocation(viewportLocation, adjustedViewportLocation, &ancestorRespondingToClickEvents, true);
523 }
524
525 Node* Frame::nodeRespondingToScrollWheelEvents(const FloatPoint& viewportLocation)
526 {
527     FloatPoint adjustedViewportLocation;
528     return qualifyingNodeAtViewportLocation(viewportLocation, adjustedViewportLocation, &ancestorRespondingToScrollWheelEvents, false);
529 }
530
531 int Frame::preferredHeight() const
532 {
533     Document* document = this->document();
534     if (!document)
535         return 0;
536
537     document->updateLayout();
538
539     auto* body = document->bodyOrFrameset();
540     if (!body)
541         return 0;
542
543     RenderObject* renderer = body->renderer();
544     if (!is<RenderBlock>(renderer))
545         return 0;
546
547     RenderBlock& block = downcast<RenderBlock>(*renderer);
548     return block.height() + block.marginTop() + block.marginBottom();
549 }
550
551 int Frame::innerLineHeight(DOMNode* domNode) const
552 {
553     if (!domNode)
554         return 0;
555
556     Document* document = this->document();
557     if (!document)
558         return 0;
559
560     document->updateLayout();
561
562     Node* node = core(domNode);
563     if (!node)
564         return 0;
565
566     RenderObject* renderer = node->renderer();
567     if (!renderer)
568         return 0;
569
570     return renderer->innerLineHeight();
571 }
572
573 void Frame::updateLayout() const
574 {
575     Document* document = this->document();
576     if (!document)
577         return;
578
579     document->updateLayout();
580
581     if (FrameView* view = this->view())
582         view->adjustViewSize();
583 }
584
585 NSRect Frame::caretRect() const
586 {
587     VisibleSelection visibleSelection = selection().selection();
588     if (visibleSelection.isNone())
589         return CGRectZero;
590     return visibleSelection.isCaret() ? selection().absoluteCaretBounds() : VisiblePosition(visibleSelection.end()).absoluteCaretBounds();
591 }
592
593 NSRect Frame::rectForScrollToVisible() const
594 {
595     VisibleSelection selection(this->selection().selection());
596
597     if (selection.isNone())
598         return CGRectZero;
599
600     if (selection.isCaret())
601         return caretRect();
602
603     return unionRect(selection.visibleStart().absoluteCaretBounds(), selection.visibleEnd().absoluteCaretBounds());
604 }
605
606 DOMCSSStyleDeclaration* Frame::styleAtSelectionStart() const
607 {
608     RefPtr<EditingStyle> editingStyle = EditingStyle::styleAtSelectionStart(selection().selection());
609     if (!editingStyle)
610         return nullptr;
611     PropertySetCSSStyleDeclaration* propertySetCSSStyleDeclaration = new PropertySetCSSStyleDeclaration(editingStyle->style());
612     // The auto-generated code for DOMCSSStyleDeclaration derefs its pointer when it is deallocated.
613     return kit(static_cast<CSSStyleDeclaration*>(propertySetCSSStyleDeclaration));
614 }
615
616 unsigned Frame::formElementsCharacterCount() const
617 {
618     Document* document = this->document();
619     if (!document)
620         return 0;
621     return document->formController().formElementsCharacterCount();
622 }
623
624 void Frame::setTimersPaused(bool paused)
625 {
626     JSLockHolder lock(JSDOMWindowBase::commonVM());
627     setTimersPausedInternal(paused);
628 }
629
630 void Frame::setTimersPausedInternal(bool paused)
631 {
632     if (paused) {
633         ++m_timersPausedCount;
634         if (m_timersPausedCount == 1) {
635             clearTimers();
636             if (document())
637                 document()->suspendScheduledTasks(ActiveDOMObject::DocumentWillBePaused);
638         }
639     } else {
640         --m_timersPausedCount;
641         ASSERT(m_timersPausedCount >= 0);
642         if (m_timersPausedCount == 0) {
643             if (document())
644                 document()->resumeScheduledTasks(ActiveDOMObject::DocumentWillBePaused);
645
646             // clearTimers() suspended animations and pending relayouts, reschedule if needed.
647             animation().resumeAnimationsForDocument(document());
648
649             if (view())
650                 view()->scheduleRelayout();
651         }
652     }
653
654     // We need to make sure all subframes' states are up to date.
655     for (Frame* frame = tree().firstChild(); frame; frame = frame->tree().nextSibling())
656         frame->setTimersPausedInternal(paused);
657 }
658
659 void Frame::dispatchPageHideEventBeforePause()
660 {
661     ASSERT(isMainFrame());
662     if (!isMainFrame())
663         return;
664
665     for (Frame* frame = this; frame; frame = frame->tree().traverseNext(this))
666         frame->document()->domWindow()->dispatchEvent(PageTransitionEvent::create(eventNames().pagehideEvent, true), document());
667 }
668
669 void Frame::dispatchPageShowEventBeforeResume()
670 {
671     ASSERT(isMainFrame());
672     if (!isMainFrame())
673         return;
674
675     for (Frame* frame = this; frame; frame = frame->tree().traverseNext(this))
676         frame->document()->domWindow()->dispatchEvent(PageTransitionEvent::create(eventNames().pageshowEvent, true), document());
677 }
678
679 void Frame::setRangedSelectionBaseToCurrentSelection()
680 {
681     m_rangedSelectionBase = selection().selection();
682 }
683
684 void Frame::setRangedSelectionBaseToCurrentSelectionStart()
685 {
686     const VisibleSelection& visibleSelection = selection().selection();
687     m_rangedSelectionBase = VisibleSelection(visibleSelection.start(), visibleSelection.affinity());
688 }
689
690 void Frame::setRangedSelectionBaseToCurrentSelectionEnd()
691 {
692     const VisibleSelection& visibleSelection = selection().selection();
693     m_rangedSelectionBase = VisibleSelection(visibleSelection.end(), visibleSelection.affinity());
694 }
695
696 VisibleSelection Frame::rangedSelectionBase() const
697 {
698     return m_rangedSelectionBase;
699 }
700
701 void Frame::clearRangedSelectionInitialExtent()
702 {
703     m_rangedSelectionInitialExtent = VisibleSelection();
704 }
705
706 void Frame::setRangedSelectionInitialExtentToCurrentSelectionStart()
707 {
708     const VisibleSelection& visibleSelection = selection().selection();
709     m_rangedSelectionInitialExtent = VisibleSelection(visibleSelection.start(), visibleSelection.affinity());
710 }
711
712 void Frame::setRangedSelectionInitialExtentToCurrentSelectionEnd()
713 {
714     const VisibleSelection& visibleSelection = selection().selection();
715     m_rangedSelectionInitialExtent = VisibleSelection(visibleSelection.end(), visibleSelection.affinity());
716 }
717
718 VisibleSelection Frame::rangedSelectionInitialExtent() const
719 {
720     return m_rangedSelectionInitialExtent;
721 }
722
723 void Frame::recursiveSetUpdateAppearanceEnabled(bool enabled)
724 {
725     selection().setUpdateAppearanceEnabled(enabled);
726     for (Frame* child = tree().firstChild(); child; child = child->tree().nextSibling())
727         child->recursiveSetUpdateAppearanceEnabled(enabled);
728 }
729
730 // FIXME: Break this function up into pieces with descriptive function names so that it's easier to follow.
731 NSArray *Frame::interpretationsForCurrentRoot() const
732 {
733     if (!document())
734         return nil;
735
736     auto* root = selection().selection().selectionType() == VisibleSelection::NoSelection ? document()->bodyOrFrameset() : selection().selection().rootEditableElement();
737     unsigned rootChildCount = root->countChildNodes();
738     RefPtr<Range> rangeOfRootContents = Range::create(*document(), createLegacyEditingPosition(root, 0), createLegacyEditingPosition(root, rootChildCount));
739
740     auto markersInRoot = document()->markers().markersInRange(rangeOfRootContents.get(), DocumentMarker::DictationPhraseWithAlternatives);
741
742     // There are no phrases with alternatives, so there is just one interpretation.
743     if (markersInRoot.isEmpty())
744         return [NSArray arrayWithObject:plainText(rangeOfRootContents.get())];
745
746     // The number of interpretations will be i1 * i2 * ... * iN, where iX is the number of interpretations for the Xth phrase with alternatives.
747     size_t interpretationsCount = 1;
748
749     for (auto* marker : markersInRoot)
750         interpretationsCount *= marker->alternatives().size() + 1;
751
752     Vector<Vector<UChar>> interpretations;
753     interpretations.grow(interpretationsCount);
754
755     Position precedingTextStartPosition = createLegacyEditingPosition(root, 0);
756
757     unsigned combinationsSoFar = 1;
758
759     Node* pastLastNode = rangeOfRootContents->pastLastNode();
760     for (Node* node = rangeOfRootContents->firstNode(); node != pastLastNode; node = NodeTraversal::next(*node)) {
761         for (auto* marker : document()->markers().markersFor(node, DocumentMarker::MarkerTypes(DocumentMarker::DictationPhraseWithAlternatives))) {
762             // First, add text that precede the marker.
763             if (precedingTextStartPosition != createLegacyEditingPosition(node, marker->startOffset())) {
764                 RefPtr<Range> precedingTextRange = Range::create(*document(), precedingTextStartPosition, createLegacyEditingPosition(node, marker->startOffset()));
765                 String precedingText = plainText(precedingTextRange.get());
766                 if (!precedingText.isEmpty()) {
767                     for (auto& interpretation : interpretations)
768                         append(interpretation, precedingText);
769                 }
770             }
771
772             RefPtr<Range> rangeForMarker = Range::create(*document(), createLegacyEditingPosition(node, marker->startOffset()), createLegacyEditingPosition(node, marker->endOffset()));
773             String visibleTextForMarker = plainText(rangeForMarker.get());
774             size_t interpretationsCountForCurrentMarker = marker->alternatives().size() + 1;
775             for (size_t i = 0; i < interpretationsCount; ++i) {
776                 // Determine text for the ith interpretation. It will either be the visible text, or one of its
777                 // alternatives stored in the marker.
778
779                 size_t indexOfInterpretationForCurrentMarker = (i / combinationsSoFar) % interpretationsCountForCurrentMarker;
780                 if (!indexOfInterpretationForCurrentMarker)
781                     append(interpretations[i], visibleTextForMarker);
782                 else
783                     append(interpretations[i], marker->alternatives().at(i % marker->alternatives().size()));
784             }
785
786             combinationsSoFar *= interpretationsCountForCurrentMarker;
787
788             precedingTextStartPosition = createLegacyEditingPosition(node, marker->endOffset());
789         }
790     }
791
792     // Finally, add any text after the last marker.
793     RefPtr<Range> afterLastMarkerRange = Range::create(*document(), precedingTextStartPosition, createLegacyEditingPosition(root, rootChildCount));
794     String textAfterLastMarker = plainText(afterLastMarkerRange.get());
795     if (!textAfterLastMarker.isEmpty()) {
796         for (auto& interpretation : interpretations)
797             append(interpretation, textAfterLastMarker);
798     }
799
800     NSMutableArray *result = [NSMutableArray array];
801     for (auto& interpretation : interpretations)
802         [result addObject:adoptNS([[NSString alloc] initWithCharacters:interpretation.data() length:interpretation.size()]).get()];
803
804     return result;
805 }
806
807 static bool anyFrameHasTiledLayers(Frame* rootFrame)
808 {
809     for (Frame* frame = rootFrame; frame; frame = frame->tree().traverseNext(rootFrame)) {
810         if (frame->containsTiledBackingLayers())
811             return true;
812     }
813     return false;
814 }
815
816 void Frame::viewportOffsetChanged(ViewportOffsetChangeType changeType)
817 {
818     if (changeType == IncrementalScrollOffset) {
819         if (anyFrameHasTiledLayers(this)) {
820             if (RenderView* root = contentRenderer())
821                 root->compositor().didChangeVisibleRect();
822         }
823     }
824
825     if (changeType == CompletedScrollOffset) {
826         if (RenderView* root = contentRenderer())
827             root->compositor().updateCompositingLayers(CompositingUpdateOnScroll);
828     }
829 }
830
831 bool Frame::containsTiledBackingLayers() const
832 {
833     if (RenderView* root = contentRenderer())
834         return root->compositor().hasNonMainLayersWithTiledBacking();
835
836     return false;
837 }
838
839 void Frame::overflowScrollPositionChangedForNode(const IntPoint& position, Node* node, bool isUserScroll)
840 {
841     RenderObject* renderer = node->renderer();
842     if (!renderer || !renderer->hasLayer())
843         return;
844
845     RenderLayer& layer = *downcast<RenderBoxModelObject>(*renderer).layer();
846
847     layer.setIsUserScroll(isUserScroll);
848     layer.scrollToOffsetWithoutAnimation(position);
849     layer.setIsUserScroll(false);
850     layer.didEndScroll(); // FIXME: Should we always call this?
851 }
852
853 void Frame::resetAllGeolocationPermission()
854 {
855     if (document()->domWindow())
856         document()->domWindow()->resetAllGeolocationPermission();
857
858     for (Frame* child = tree().firstChild(); child; child = child->tree().nextSibling())
859         child->resetAllGeolocationPermission();
860 }
861
862 } // namespace WebCore
863 #endif // PLATFORM(IOS)