1/*
2 * Copyright (C) 1998, 1999 Torben Weis <weis@kde.org>
3 * 1999 Lars Knoll <knoll@kde.org>
4 * 1999 Antti Koivisto <koivisto@kde.org>
5 * 2000 Simon Hausmann <hausmann@kde.org>
6 * 2000 Stefan Schimanski <1Stein@gmx.de>
7 * 2001 George Staikos <staikos@kde.org>
8 * Copyright (C) 2004-2019 Apple Inc. All rights reserved.
9 * Copyright (C) 2005 Alexey Proskuryakov <ap@nypop.com>
10 * Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies)
11 * Copyright (C) 2008 Eric Seidel <eric@webkit.org>
12 * Copyright (C) 2008 Google Inc.
13 *
14 * This library is free software; you can redistribute it and/or
15 * modify it under the terms of the GNU Library General Public
16 * License as published by the Free Software Foundation; either
17 * version 2 of the License, or (at your option) any later version.
18 *
19 * This library is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
22 * Library General Public License for more details.
23 *
24 * You should have received a copy of the GNU Library General Public License
25 * along with this library; see the file COPYING.LIB. If not, write to
26 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
27 * Boston, MA 02110-1301, USA.
28 */
29
30#include "config.h"
31#include "Frame.h"
32
33#include "ApplyStyleCommand.h"
34#include "BackForwardController.h"
35#include "CSSAnimationController.h"
36#include "CSSComputedStyleDeclaration.h"
37#include "CSSPropertyNames.h"
38#include "CachedCSSStyleSheet.h"
39#include "CachedResourceLoader.h"
40#include "Chrome.h"
41#include "ChromeClient.h"
42#include "DOMWindow.h"
43#include "DocumentTimeline.h"
44#include "DocumentType.h"
45#include "Editing.h"
46#include "Editor.h"
47#include "EditorClient.h"
48#include "Event.h"
49#include "EventHandler.h"
50#include "EventNames.h"
51#include "FloatQuad.h"
52#include "FocusController.h"
53#include "FrameDestructionObserver.h"
54#include "FrameLoader.h"
55#include "FrameLoaderClient.h"
56#include "FrameSelection.h"
57#include "FrameView.h"
58#include "GraphicsContext.h"
59#include "GraphicsLayer.h"
60#include "HTMLFormControlElement.h"
61#include "HTMLFormElement.h"
62#include "HTMLFrameElementBase.h"
63#include "HTMLNames.h"
64#include "HTMLTableCellElement.h"
65#include "HTMLTableRowElement.h"
66#include "HitTestResult.h"
67#include "ImageBuffer.h"
68#include "InspectorInstrumentation.h"
69#include "JSWindowProxy.h"
70#include "Logging.h"
71#include "NavigationScheduler.h"
72#include "Navigator.h"
73#include "NodeList.h"
74#include "NodeTraversal.h"
75#include "Page.h"
76#include "PageCache.h"
77#include "ProcessWarming.h"
78#include "RenderLayerCompositor.h"
79#include "RenderTableCell.h"
80#include "RenderText.h"
81#include "RenderTextControl.h"
82#include "RenderTheme.h"
83#include "RenderView.h"
84#include "RenderWidget.h"
85#include "RuntimeEnabledFeatures.h"
86#include "SVGDocument.h"
87#include "SVGDocumentExtensions.h"
88#include "ScriptController.h"
89#include "ScriptSourceCode.h"
90#include "ScrollingCoordinator.h"
91#include "Settings.h"
92#include "StyleProperties.h"
93#include "StyleScope.h"
94#include "TextNodeTraversal.h"
95#include "TextResourceDecoder.h"
96#include "UserContentController.h"
97#include "UserContentURLPattern.h"
98#include "UserGestureIndicator.h"
99#include "UserScript.h"
100#include "UserTypingGestureIndicator.h"
101#include "VisibleUnits.h"
102#include "markup.h"
103#include "npruntime_impl.h"
104#include "runtime_root.h"
105#include <JavaScriptCore/RegularExpression.h>
106#include <wtf/RefCountedLeakCounter.h>
107#include <wtf/StdLibExtras.h>
108#include <wtf/text/StringBuilder.h>
109
110namespace WebCore {
111
112using namespace HTMLNames;
113
114#if PLATFORM(IOS_FAMILY)
115static const Seconds scrollFrequency { 1000_s / 60. };
116#endif
117
118DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, frameCounter, ("Frame"));
119
120static inline Frame* parentFromOwnerElement(HTMLFrameOwnerElement* ownerElement)
121{
122 if (!ownerElement)
123 return 0;
124 return ownerElement->document().frame();
125}
126
127static inline float parentPageZoomFactor(Frame* frame)
128{
129 Frame* parent = frame->tree().parent();
130 if (!parent)
131 return 1;
132 return parent->pageZoomFactor();
133}
134
135static inline float parentTextZoomFactor(Frame* frame)
136{
137 Frame* parent = frame->tree().parent();
138 if (!parent)
139 return 1;
140 return parent->textZoomFactor();
141}
142
143Frame::Frame(Page& page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient& frameLoaderClient)
144 : m_mainFrame(ownerElement ? page.mainFrame() : *this)
145 , m_page(&page)
146 , m_settings(&page.settings())
147 , m_treeNode(*this, parentFromOwnerElement(ownerElement))
148 , m_loader(makeUniqueRef<FrameLoader>(*this, frameLoaderClient))
149 , m_navigationScheduler(makeUniqueRef<NavigationScheduler>(*this))
150 , m_ownerElement(ownerElement)
151 , m_script(makeUniqueRef<ScriptController>(*this))
152 , m_editor(makeUniqueRef<Editor>(*this))
153 , m_selection(makeUniqueRef<FrameSelection>(this))
154 , m_animationController(makeUniqueRef<CSSAnimationController>(*this))
155 , m_pageZoomFactor(parentPageZoomFactor(this))
156 , m_textZoomFactor(parentTextZoomFactor(this))
157 , m_eventHandler(makeUniqueRef<EventHandler>(*this))
158{
159 ProcessWarming::initializeNames();
160
161 if (ownerElement) {
162 m_mainFrame.selfOnlyRef();
163 page.incrementSubframeCount();
164 ownerElement->setContentFrame(this);
165 }
166
167#ifndef NDEBUG
168 frameCounter.increment();
169#endif
170
171 // Pause future ActiveDOMObjects if this frame is being created while the page is in a paused state.
172 Frame* parent = parentFromOwnerElement(ownerElement);
173 if (parent && parent->activeDOMObjectsAndAnimationsSuspended())
174 suspendActiveDOMObjectsAndAnimations();
175}
176
177void Frame::init()
178{
179 m_loader->init();
180}
181
182Ref<Frame> Frame::create(Page* page, HTMLFrameOwnerElement* ownerElement, FrameLoaderClient* client)
183{
184 ASSERT(page);
185 ASSERT(client);
186 return adoptRef(*new Frame(*page, ownerElement, *client));
187}
188
189Frame::~Frame()
190{
191 setView(nullptr);
192 loader().cancelAndClear();
193
194 // FIXME: We should not be doing all this work inside the destructor
195
196#ifndef NDEBUG
197 frameCounter.decrement();
198#endif
199
200 disconnectOwnerElement();
201
202 while (auto* destructionObserver = m_destructionObservers.takeAny())
203 destructionObserver->frameDestroyed();
204
205 if (!isMainFrame())
206 m_mainFrame.selfOnlyDeref();
207}
208
209void Frame::addDestructionObserver(FrameDestructionObserver* observer)
210{
211 m_destructionObservers.add(observer);
212}
213
214void Frame::removeDestructionObserver(FrameDestructionObserver* observer)
215{
216 m_destructionObservers.remove(observer);
217}
218
219void Frame::setView(RefPtr<FrameView>&& view)
220{
221 // We the custom scroll bars as early as possible to prevent m_doc->detach()
222 // from messing with the view such that its scroll bars won't be torn down.
223 // FIXME: We should revisit this.
224 if (m_view)
225 m_view->prepareForDetach();
226
227 // Prepare for destruction now, so any unload event handlers get run and the DOMWindow is
228 // notified. If we wait until the view is destroyed, then things won't be hooked up enough for
229 // these calls to work.
230 if (!view && m_doc && m_doc->pageCacheState() != Document::InPageCache)
231 m_doc->prepareForDestruction();
232
233 if (m_view)
234 m_view->layoutContext().unscheduleLayout();
235
236 m_eventHandler->clear();
237
238 RELEASE_ASSERT(!m_doc || !m_doc->hasLivingRenderTree());
239
240 m_view = WTFMove(view);
241
242 // Only one form submission is allowed per view of a part.
243 // Since this part may be getting reused as a result of being
244 // pulled from the back/forward cache, reset this flag.
245 loader().resetMultipleFormSubmissionProtection();
246}
247
248void Frame::setDocument(RefPtr<Document>&& newDocument)
249{
250 ASSERT(!newDocument || newDocument->frame() == this);
251
252 if (m_documentIsBeingReplaced)
253 return;
254
255 m_documentIsBeingReplaced = true;
256
257 if (isMainFrame()) {
258 if (m_page)
259 m_page->didChangeMainDocument();
260 m_loader->client().dispatchDidChangeMainDocument();
261
262 // We want to generate the same unique names whenever a page is loaded to avoid making layout tests
263 // flaky and for things like form state restoration to work. To achieve this, we reset our frame
264 // identifier generator every time the page is navigated.
265 tree().resetFrameIdentifiers();
266 }
267
268#if ENABLE(ATTACHMENT_ELEMENT)
269 if (m_doc) {
270 for (auto& attachment : m_doc->attachmentElementsByIdentifier().values())
271 editor().didRemoveAttachmentElement(attachment);
272 }
273#endif
274
275 if (m_doc && m_doc->pageCacheState() != Document::InPageCache)
276 m_doc->prepareForDestruction();
277
278 m_doc = newDocument.copyRef();
279 ASSERT(!m_doc || m_doc->domWindow());
280 ASSERT(!m_doc || m_doc->domWindow()->frame() == this);
281
282 // Don't use m_doc because it can be overwritten and we want to guarantee
283 // that the document is not destroyed during this function call.
284 if (newDocument)
285 newDocument->didBecomeCurrentDocumentInFrame();
286
287#if ENABLE(ATTACHMENT_ELEMENT)
288 if (m_doc) {
289 for (auto& attachment : m_doc->attachmentElementsByIdentifier().values())
290 editor().didInsertAttachmentElement(attachment);
291 }
292#endif
293
294 InspectorInstrumentation::frameDocumentUpdated(*this);
295
296 m_documentIsBeingReplaced = false;
297}
298
299#if ENABLE(ORIENTATION_EVENTS)
300void Frame::orientationChanged()
301{
302 Vector<Ref<Frame>> frames;
303 for (Frame* frame = this; frame; frame = frame->tree().traverseNext())
304 frames.append(*frame);
305
306 auto newOrientation = orientation();
307 for (auto& frame : frames) {
308 if (Document* document = frame->document())
309 document->orientationChanged(newOrientation);
310 }
311}
312
313int Frame::orientation() const
314{
315 if (m_page)
316 return m_page->chrome().client().deviceOrientation();
317 return 0;
318}
319#endif // ENABLE(ORIENTATION_EVENTS)
320
321static JSC::Yarr::RegularExpression createRegExpForLabels(const Vector<String>& labels)
322{
323 // REVIEW- version of this call in FrameMac.mm caches based on the NSArray ptrs being
324 // the same across calls. We can't do that.
325
326 static NeverDestroyed<JSC::Yarr::RegularExpression> wordRegExp("\\w");
327 StringBuilder pattern;
328 pattern.append('(');
329 unsigned int numLabels = labels.size();
330 unsigned int i;
331 for (i = 0; i < numLabels; i++) {
332 String label = labels[i];
333
334 bool startsWithWordChar = false;
335 bool endsWithWordChar = false;
336 if (label.length()) {
337 startsWithWordChar = wordRegExp.get().match(label.substring(0, 1)) >= 0;
338 endsWithWordChar = wordRegExp.get().match(label.substring(label.length() - 1, 1)) >= 0;
339 }
340
341 if (i)
342 pattern.append('|');
343 // Search for word boundaries only if label starts/ends with "word characters".
344 // If we always searched for word boundaries, this wouldn't work for languages
345 // such as Japanese.
346 if (startsWithWordChar)
347 pattern.appendLiteral("\\b");
348 pattern.append(label);
349 if (endsWithWordChar)
350 pattern.appendLiteral("\\b");
351 }
352 pattern.append(')');
353 return JSC::Yarr::RegularExpression(pattern.toString(), JSC::Yarr::TextCaseInsensitive);
354}
355
356String Frame::searchForLabelsAboveCell(const JSC::Yarr::RegularExpression& regExp, HTMLTableCellElement* cell, size_t* resultDistanceFromStartOfCell)
357{
358 HTMLTableCellElement* aboveCell = cell->cellAbove();
359 if (aboveCell) {
360 // search within the above cell we found for a match
361 size_t lengthSearched = 0;
362 for (Text* textNode = TextNodeTraversal::firstWithin(*aboveCell); textNode; textNode = TextNodeTraversal::next(*textNode, aboveCell)) {
363 if (!textNode->renderer() || textNode->renderer()->style().visibility() != Visibility::Visible)
364 continue;
365 // For each text chunk, run the regexp
366 String nodeString = textNode->data();
367 int pos = regExp.searchRev(nodeString);
368 if (pos >= 0) {
369 if (resultDistanceFromStartOfCell)
370 *resultDistanceFromStartOfCell = lengthSearched;
371 return nodeString.substring(pos, regExp.matchedLength());
372 }
373 lengthSearched += nodeString.length();
374 }
375 }
376
377 // Any reason in practice to search all cells in that are above cell?
378 if (resultDistanceFromStartOfCell)
379 *resultDistanceFromStartOfCell = notFound;
380 return String();
381}
382
383// FIXME: This should take an Element&.
384String Frame::searchForLabelsBeforeElement(const Vector<String>& labels, Element* element, size_t* resultDistance, bool* resultIsInCellAbove)
385{
386 ASSERT(element);
387 JSC::Yarr::RegularExpression regExp = createRegExpForLabels(labels);
388 // We stop searching after we've seen this many chars
389 const unsigned int charsSearchedThreshold = 500;
390 // This is the absolute max we search. We allow a little more slop than
391 // charsSearchedThreshold, to make it more likely that we'll search whole nodes.
392 const unsigned int maxCharsSearched = 600;
393 // If the starting element is within a table, the cell that contains it
394 HTMLTableCellElement* startingTableCell = nullptr;
395 bool searchedCellAbove = false;
396
397 if (resultDistance)
398 *resultDistance = notFound;
399 if (resultIsInCellAbove)
400 *resultIsInCellAbove = false;
401
402 // walk backwards in the node tree, until another element, or form, or end of tree
403 int unsigned lengthSearched = 0;
404 Node* n;
405 for (n = NodeTraversal::previous(*element); n && lengthSearched < charsSearchedThreshold; n = NodeTraversal::previous(*n)) {
406 // We hit another form element or the start of the form - bail out
407 if (is<HTMLFormElement>(*n) || is<HTMLFormControlElement>(*n))
408 break;
409
410 if (n->hasTagName(tdTag) && !startingTableCell)
411 startingTableCell = downcast<HTMLTableCellElement>(n);
412 else if (is<HTMLTableRowElement>(*n) && startingTableCell) {
413 String result = searchForLabelsAboveCell(regExp, startingTableCell, resultDistance);
414 if (!result.isEmpty()) {
415 if (resultIsInCellAbove)
416 *resultIsInCellAbove = true;
417 return result;
418 }
419 searchedCellAbove = true;
420 } else if (n->isTextNode() && n->renderer() && n->renderer()->style().visibility() == Visibility::Visible) {
421 // For each text chunk, run the regexp
422 String nodeString = n->nodeValue();
423 // add 100 for slop, to make it more likely that we'll search whole nodes
424 if (lengthSearched + nodeString.length() > maxCharsSearched)
425 nodeString = nodeString.right(charsSearchedThreshold - lengthSearched);
426 int pos = regExp.searchRev(nodeString);
427 if (pos >= 0) {
428 if (resultDistance)
429 *resultDistance = lengthSearched;
430 return nodeString.substring(pos, regExp.matchedLength());
431 }
432 lengthSearched += nodeString.length();
433 }
434 }
435
436 // If we started in a cell, but bailed because we found the start of the form or the
437 // previous element, we still might need to search the row above us for a label.
438 if (startingTableCell && !searchedCellAbove) {
439 String result = searchForLabelsAboveCell(regExp, startingTableCell, resultDistance);
440 if (!result.isEmpty()) {
441 if (resultIsInCellAbove)
442 *resultIsInCellAbove = true;
443 return result;
444 }
445 }
446 return String();
447}
448
449static String matchLabelsAgainstString(const Vector<String>& labels, const String& stringToMatch)
450{
451 if (stringToMatch.isEmpty())
452 return String();
453
454 String mutableStringToMatch = stringToMatch;
455
456 // Make numbers and _'s in field names behave like word boundaries, e.g., "address2"
457 replace(mutableStringToMatch, JSC::Yarr::RegularExpression("\\d"), " ");
458 mutableStringToMatch.replace('_', ' ');
459
460 JSC::Yarr::RegularExpression regExp = createRegExpForLabels(labels);
461 // Use the largest match we can find in the whole string
462 int pos;
463 int length;
464 int bestPos = -1;
465 int bestLength = -1;
466 int start = 0;
467 do {
468 pos = regExp.match(mutableStringToMatch, start);
469 if (pos != -1) {
470 length = regExp.matchedLength();
471 if (length >= bestLength) {
472 bestPos = pos;
473 bestLength = length;
474 }
475 start = pos + 1;
476 }
477 } while (pos != -1);
478
479 if (bestPos != -1)
480 return mutableStringToMatch.substring(bestPos, bestLength);
481 return String();
482}
483
484String Frame::matchLabelsAgainstElement(const Vector<String>& labels, Element* element)
485{
486 // Match against the name element, then against the id element if no match is found for the name element.
487 // See 7538330 for one popular site that benefits from the id element check.
488 // FIXME: This code is mirrored in FrameMac.mm. It would be nice to make the Mac code call the platform-agnostic
489 // code, which would require converting the NSArray of NSStrings to a Vector of Strings somewhere along the way.
490 String resultFromNameAttribute = matchLabelsAgainstString(labels, element->getNameAttribute());
491 if (!resultFromNameAttribute.isEmpty())
492 return resultFromNameAttribute;
493
494 return matchLabelsAgainstString(labels, element->attributeWithoutSynchronization(idAttr));
495}
496
497#if PLATFORM(IOS_FAMILY)
498
499void Frame::setSelectionChangeCallbacksDisabled(bool selectionChangeCallbacksDisabled)
500{
501 m_selectionChangeCallbacksDisabled = selectionChangeCallbacksDisabled;
502}
503
504bool Frame::selectionChangeCallbacksDisabled() const
505{
506 return m_selectionChangeCallbacksDisabled;
507}
508#endif // PLATFORM(IOS_FAMILY)
509
510bool Frame::requestDOMPasteAccess()
511{
512 if (m_settings->javaScriptCanAccessClipboard() && m_settings->DOMPasteAllowed())
513 return true;
514
515 if (!m_settings->domPasteAccessRequestsEnabled() || !m_doc)
516 return false;
517
518 auto gestureToken = UserGestureIndicator::currentUserGesture();
519 if (!gestureToken || !gestureToken->processingUserGesture())
520 return false;
521
522 switch (gestureToken->domPasteAccessPolicy()) {
523 case DOMPasteAccessPolicy::Granted:
524 return true;
525 case DOMPasteAccessPolicy::Denied:
526 return false;
527 case DOMPasteAccessPolicy::NotRequestedYet: {
528 auto* client = m_editor->client();
529 if (!client)
530 return false;
531
532 auto response = client->requestDOMPasteAccess(m_doc->originIdentifierForPasteboard());
533 gestureToken->didRequestDOMPasteAccess(response);
534 switch (response) {
535 case DOMPasteAccessResponse::GrantedForCommand:
536 case DOMPasteAccessResponse::GrantedForGesture:
537 return true;
538 case DOMPasteAccessResponse::DeniedForGesture:
539 return false;
540 }
541 }
542 }
543
544 ASSERT_NOT_REACHED();
545 return false;
546}
547
548void Frame::setPrinting(bool printing, const FloatSize& pageSize, const FloatSize& originalPageSize, float maximumShrinkRatio, AdjustViewSizeOrNot shouldAdjustViewSize)
549{
550 if (!view())
551 return;
552 // In setting printing, we should not validate resources already cached for the document.
553 // See https://bugs.webkit.org/show_bug.cgi?id=43704
554 ResourceCacheValidationSuppressor validationSuppressor(m_doc->cachedResourceLoader());
555
556 m_doc->setPrinting(printing);
557 auto& frameView = *view();
558 frameView.adjustMediaTypeForPrinting(printing);
559
560 m_doc->styleScope().didChangeStyleSheetEnvironment();
561 if (shouldUsePrintingLayout())
562 frameView.forceLayoutForPagination(pageSize, originalPageSize, maximumShrinkRatio, shouldAdjustViewSize);
563 else {
564 frameView.forceLayout();
565 if (shouldAdjustViewSize == AdjustViewSize)
566 frameView.adjustViewSize();
567 }
568
569 // Subframes of the one we're printing don't lay out to the page size.
570 for (RefPtr<Frame> child = tree().firstChild(); child; child = child->tree().nextSibling())
571 child->setPrinting(printing, FloatSize(), FloatSize(), 0, shouldAdjustViewSize);
572}
573
574bool Frame::shouldUsePrintingLayout() const
575{
576 // Only top frame being printed should be fit to page size.
577 // Subframes should be constrained by parents only.
578 return m_doc->printing() && (!tree().parent() || !tree().parent()->m_doc->printing());
579}
580
581FloatSize Frame::resizePageRectsKeepingRatio(const FloatSize& originalSize, const FloatSize& expectedSize)
582{
583 FloatSize resultSize;
584 if (!contentRenderer())
585 return FloatSize();
586
587 if (contentRenderer()->style().isHorizontalWritingMode()) {
588 ASSERT(fabs(originalSize.width()) > std::numeric_limits<float>::epsilon());
589 float ratio = originalSize.height() / originalSize.width();
590 resultSize.setWidth(floorf(expectedSize.width()));
591 resultSize.setHeight(floorf(resultSize.width() * ratio));
592 } else {
593 ASSERT(fabs(originalSize.height()) > std::numeric_limits<float>::epsilon());
594 float ratio = originalSize.width() / originalSize.height();
595 resultSize.setHeight(floorf(expectedSize.height()));
596 resultSize.setWidth(floorf(resultSize.height() * ratio));
597 }
598 return resultSize;
599}
600
601void Frame::injectUserScripts(UserScriptInjectionTime injectionTime)
602{
603 if (!m_page)
604 return;
605
606 if (loader().stateMachine().creatingInitialEmptyDocument() && !settings().shouldInjectUserScriptsInInitialEmptyDocument())
607 return;
608
609 m_page->userContentProvider().forEachUserScript([this, protectedThis = makeRef(*this), injectionTime](DOMWrapperWorld& world, const UserScript& script) {
610 if (script.injectionTime() == injectionTime)
611 injectUserScriptImmediately(world, script);
612 });
613}
614
615void Frame::injectUserScriptImmediately(DOMWrapperWorld& world, const UserScript& script)
616{
617 auto* document = this->document();
618 if (!document)
619 return;
620 if (script.injectedFrames() == InjectInTopFrameOnly && !isMainFrame())
621 return;
622 if (!UserContentURLPattern::matchesPatterns(document->url(), script.whitelist(), script.blacklist()))
623 return;
624 if (!m_script->shouldAllowUserAgentScripts(*document))
625 return;
626
627 document->setAsRunningUserScripts();
628 loader().client().willInjectUserScript(world);
629 m_script->evaluateInWorld(ScriptSourceCode(script.source(), URL(script.url())), world);
630}
631
632RenderView* Frame::contentRenderer() const
633{
634 return document() ? document()->renderView() : nullptr;
635}
636
637RenderWidget* Frame::ownerRenderer() const
638{
639 auto* ownerElement = m_ownerElement;
640 if (!ownerElement)
641 return nullptr;
642 auto* object = ownerElement->renderer();
643 // FIXME: If <object> is ever fixed to disassociate itself from frames
644 // that it has started but canceled, then this can turn into an ASSERT
645 // since m_ownerElement would be nullptr when the load is canceled.
646 // https://bugs.webkit.org/show_bug.cgi?id=18585
647 if (!is<RenderWidget>(object))
648 return nullptr;
649 return downcast<RenderWidget>(object);
650}
651
652Frame* Frame::frameForWidget(const Widget& widget)
653{
654 if (auto* renderer = RenderWidget::find(widget))
655 return renderer->frameOwnerElement().document().frame();
656
657 // Assume all widgets are either a FrameView or owned by a RenderWidget.
658 // FIXME: That assumption is not right for scroll bars!
659 return &downcast<FrameView>(widget).frame();
660}
661
662void Frame::clearTimers(FrameView *view, Document *document)
663{
664 if (view) {
665 view->layoutContext().unscheduleLayout();
666 if (RuntimeEnabledFeatures::sharedFeatures().webAnimationsCSSIntegrationEnabled()) {
667 if (auto* timeline = document->existingTimeline())
668 timeline->suspendAnimations();
669 } else
670 view->frame().animation().suspendAnimationsForDocument(document);
671 view->frame().eventHandler().stopAutoscrollTimer();
672 }
673}
674
675void Frame::clearTimers()
676{
677 clearTimers(m_view.get(), document());
678}
679
680void Frame::willDetachPage()
681{
682 if (Frame* parent = tree().parent())
683 parent->loader().checkLoadComplete();
684
685 for (auto& observer : m_destructionObservers)
686 observer->willDetachPage();
687
688 // FIXME: It's unclear as to why this is called more than once, but it is,
689 // so page() could be NULL.
690 if (page() && page()->focusController().focusedFrame() == this)
691 page()->focusController().setFocusedFrame(nullptr);
692
693 if (page() && page()->scrollingCoordinator() && m_view)
694 page()->scrollingCoordinator()->willDestroyScrollableArea(*m_view);
695
696 script().clearScriptObjects();
697 script().updatePlatformScriptObjects();
698
699 // We promise that the Frame is always connected to a Page while the render tree is live.
700 //
701 // The render tree can be torn down in a few different ways, but the two important ones are:
702 //
703 // - When calling Frame::setView() with a null FrameView*. This is always done before calling
704 // Frame::willDetachPage (this function.) Hence the assertion below.
705 //
706 // - When adding a document to the page cache, the tree is torn down before instantiating
707 // the CachedPage+CachedFrame object tree.
708 ASSERT(!document() || !document()->renderView());
709}
710
711void Frame::disconnectOwnerElement()
712{
713 if (m_ownerElement) {
714 m_ownerElement->clearContentFrame();
715 if (m_page)
716 m_page->decrementSubframeCount();
717 }
718 m_ownerElement = nullptr;
719
720 if (auto* document = this->document())
721 document->frameWasDisconnectedFromOwner();
722}
723
724String Frame::displayStringModifiedByEncoding(const String& str) const
725{
726 return document() ? document()->displayStringModifiedByEncoding(str) : str;
727}
728
729VisiblePosition Frame::visiblePositionForPoint(const IntPoint& framePoint) const
730{
731 HitTestResult result = eventHandler().hitTestResultAtPoint(framePoint, HitTestRequest::ReadOnly | HitTestRequest::Active);
732 Node* node = result.innerNonSharedNode();
733 if (!node)
734 return VisiblePosition();
735 auto renderer = node->renderer();
736 if (!renderer)
737 return VisiblePosition();
738 VisiblePosition visiblePos = renderer->positionForPoint(result.localPoint(), nullptr);
739 if (visiblePos.isNull())
740 visiblePos = firstPositionInOrBeforeNode(node);
741 return visiblePos;
742}
743
744Document* Frame::documentAtPoint(const IntPoint& point)
745{
746 if (!view())
747 return nullptr;
748
749 IntPoint pt = view()->windowToContents(point);
750 HitTestResult result = HitTestResult(pt);
751
752 if (contentRenderer())
753 result = eventHandler().hitTestResultAtPoint(pt);
754 return result.innerNode() ? &result.innerNode()->document() : 0;
755}
756
757RefPtr<Range> Frame::rangeForPoint(const IntPoint& framePoint)
758{
759 VisiblePosition position = visiblePositionForPoint(framePoint);
760 if (position.isNull())
761 return nullptr;
762
763 Position deepPosition = position.deepEquivalent();
764 Text* containerText = deepPosition.containerText();
765 if (!containerText || !containerText->renderer() || containerText->renderer()->style().userSelect() == UserSelect::None)
766 return nullptr;
767
768 VisiblePosition previous = position.previous();
769 if (previous.isNotNull()) {
770 RefPtr<Range> previousCharacterRange = makeRange(previous, position);
771 LayoutRect rect = editor().firstRectForRange(previousCharacterRange.get());
772 if (rect.contains(framePoint))
773 return previousCharacterRange;
774 }
775
776 VisiblePosition next = position.next();
777 if (RefPtr<Range> nextCharacterRange = makeRange(position, next)) {
778 LayoutRect rect = editor().firstRectForRange(nextCharacterRange.get());
779 if (rect.contains(framePoint))
780 return nextCharacterRange;
781 }
782
783 return nullptr;
784}
785
786void Frame::createView(const IntSize& viewportSize, const Optional<Color>& backgroundColor,
787 const IntSize& fixedLayoutSize, const IntRect& fixedVisibleContentRect,
788 bool useFixedLayout, ScrollbarMode horizontalScrollbarMode, bool horizontalLock,
789 ScrollbarMode verticalScrollbarMode, bool verticalLock)
790{
791 ASSERT(m_page);
792
793 bool isMainFrame = this->isMainFrame();
794
795 if (isMainFrame && view())
796 view()->setParentVisible(false);
797
798 setView(nullptr);
799
800 RefPtr<FrameView> frameView;
801 if (isMainFrame) {
802 frameView = FrameView::create(*this, viewportSize);
803 frameView->setFixedLayoutSize(fixedLayoutSize);
804#if USE(COORDINATED_GRAPHICS)
805 frameView->setFixedVisibleContentRect(fixedVisibleContentRect);
806#else
807 UNUSED_PARAM(fixedVisibleContentRect);
808#endif
809 frameView->setUseFixedLayout(useFixedLayout);
810 } else
811 frameView = FrameView::create(*this);
812
813 frameView->setScrollbarModes(horizontalScrollbarMode, verticalScrollbarMode, horizontalLock, verticalLock);
814
815 setView(frameView.copyRef());
816
817 frameView->updateBackgroundRecursively(backgroundColor);
818
819 if (isMainFrame)
820 frameView->setParentVisible(true);
821
822 if (ownerRenderer())
823 ownerRenderer()->setWidget(frameView);
824
825 if (HTMLFrameOwnerElement* owner = ownerElement())
826 view()->setCanHaveScrollbars(owner->scrollingMode() != ScrollbarAlwaysOff);
827}
828
829DOMWindow* Frame::window() const
830{
831 return document() ? document()->domWindow() : nullptr;
832}
833
834AbstractDOMWindow* Frame::virtualWindow() const
835{
836 return window();
837}
838
839String Frame::layerTreeAsText(LayerTreeFlags flags) const
840{
841 document()->updateLayout();
842
843 if (!contentRenderer())
844 return String();
845
846 return contentRenderer()->compositor().layerTreeAsText(flags);
847}
848
849String Frame::trackedRepaintRectsAsText() const
850{
851 if (!m_view)
852 return String();
853 return m_view->trackedRepaintRectsAsText();
854}
855
856void Frame::setPageZoomFactor(float factor)
857{
858 setPageAndTextZoomFactors(factor, m_textZoomFactor);
859}
860
861void Frame::setTextZoomFactor(float factor)
862{
863 setPageAndTextZoomFactors(m_pageZoomFactor, factor);
864}
865
866void Frame::setPageAndTextZoomFactors(float pageZoomFactor, float textZoomFactor)
867{
868 if (m_pageZoomFactor == pageZoomFactor && m_textZoomFactor == textZoomFactor)
869 return;
870
871 Page* page = this->page();
872 if (!page)
873 return;
874
875 Document* document = this->document();
876 if (!document)
877 return;
878
879 m_editor->dismissCorrectionPanelAsIgnored();
880
881 // Respect SVGs zoomAndPan="disabled" property in standalone SVG documents.
882 // FIXME: How to handle compound documents + zoomAndPan="disabled"? Needs SVG WG clarification.
883 if (is<SVGDocument>(*document) && !downcast<SVGDocument>(*document).zoomAndPanEnabled())
884 return;
885
886 if (m_pageZoomFactor != pageZoomFactor) {
887 if (FrameView* view = this->view()) {
888 // Update the scroll position when doing a full page zoom, so the content stays in relatively the same position.
889 LayoutPoint scrollPosition = view->scrollPosition();
890 float percentDifference = (pageZoomFactor / m_pageZoomFactor);
891 view->setScrollPosition(IntPoint(scrollPosition.x() * percentDifference, scrollPosition.y() * percentDifference));
892 }
893 }
894
895 m_pageZoomFactor = pageZoomFactor;
896 m_textZoomFactor = textZoomFactor;
897
898 document->resolveStyle(Document::ResolveStyleType::Rebuild);
899
900 for (RefPtr<Frame> child = tree().firstChild(); child; child = child->tree().nextSibling())
901 child->setPageAndTextZoomFactors(m_pageZoomFactor, m_textZoomFactor);
902
903 if (FrameView* view = this->view()) {
904 if (document->renderView() && document->renderView()->needsLayout() && view->didFirstLayout())
905 view->layoutContext().layout();
906 }
907}
908
909float Frame::frameScaleFactor() const
910{
911 Page* page = this->page();
912
913 // Main frame is scaled with respect to he container but inner frames are not scaled with respect to the main frame.
914 if (!page || &page->mainFrame() != this || settings().delegatesPageScaling())
915 return 1;
916
917 return page->pageScaleFactor();
918}
919
920void Frame::suspendActiveDOMObjectsAndAnimations()
921{
922 bool wasSuspended = activeDOMObjectsAndAnimationsSuspended();
923
924 m_activeDOMObjectsAndAnimationsSuspendedCount++;
925
926 if (wasSuspended)
927 return;
928
929 // FIXME: Suspend/resume calls will not match if the frame is navigated, and gets a new document.
930 clearTimers(); // Suspends animations and pending relayouts.
931 if (m_doc)
932 m_doc->suspendScheduledTasks(ReasonForSuspension::PageWillBeSuspended);
933}
934
935void Frame::resumeActiveDOMObjectsAndAnimations()
936{
937 if (!activeDOMObjectsAndAnimationsSuspended())
938 return;
939
940 m_activeDOMObjectsAndAnimationsSuspendedCount--;
941
942 if (activeDOMObjectsAndAnimationsSuspended())
943 return;
944
945 if (!m_doc)
946 return;
947
948 // FIXME: Suspend/resume calls will not match if the frame is navigated, and gets a new document.
949 m_doc->resumeScheduledTasks(ReasonForSuspension::PageWillBeSuspended);
950
951 // Frame::clearTimers() suspended animations and pending relayouts.
952 animation().resumeAnimationsForDocument(m_doc.get());
953 if (m_view)
954 m_view->layoutContext().scheduleLayout();
955}
956
957void Frame::deviceOrPageScaleFactorChanged()
958{
959 for (RefPtr<Frame> child = tree().firstChild(); child; child = child->tree().nextSibling())
960 child->deviceOrPageScaleFactorChanged();
961
962 if (RenderView* root = contentRenderer())
963 root->compositor().deviceOrPageScaleFactorChanged();
964}
965
966bool Frame::isURLAllowed(const URL& url) const
967{
968 // We allow one level of self-reference because some sites depend on that,
969 // but we don't allow more than one.
970 if (m_page->subframeCount() >= Page::maxNumberOfFrames)
971 return false;
972 bool foundSelfReference = false;
973 for (const Frame* frame = this; frame; frame = frame->tree().parent()) {
974 if (equalIgnoringFragmentIdentifier(frame->document()->url(), url)) {
975 if (foundSelfReference)
976 return false;
977 foundSelfReference = true;
978 }
979 }
980 return true;
981}
982
983bool Frame::isAlwaysOnLoggingAllowed() const
984{
985 return page() && page()->isAlwaysOnLoggingAllowed();
986}
987
988void Frame::dropChildren()
989{
990 ASSERT(isMainFrame());
991 while (Frame* child = tree().firstChild())
992 tree().removeChild(*child);
993}
994
995void Frame::selfOnlyRef()
996{
997 ASSERT(isMainFrame());
998 if (m_selfOnlyRefCount++)
999 return;
1000
1001 ref();
1002}
1003
1004void Frame::selfOnlyDeref()
1005{
1006 ASSERT(isMainFrame());
1007 ASSERT(m_selfOnlyRefCount);
1008 if (--m_selfOnlyRefCount)
1009 return;
1010
1011 if (hasOneRef())
1012 dropChildren();
1013
1014 deref();
1015}
1016
1017} // namespace WebCore
1018