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