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 | |
110 | namespace WebCore { |
111 | |
112 | using namespace HTMLNames; |
113 | |
114 | #if PLATFORM(IOS_FAMILY) |
115 | static const Seconds scrollFrequency { 1000_s / 60. }; |
116 | #endif |
117 | |
118 | DEFINE_DEBUG_ONLY_GLOBAL(WTF::RefCountedLeakCounter, frameCounter, ("Frame" )); |
119 | |
120 | static inline Frame* parentFromOwnerElement(HTMLFrameOwnerElement* ownerElement) |
121 | { |
122 | if (!ownerElement) |
123 | return 0; |
124 | return ownerElement->document().frame(); |
125 | } |
126 | |
127 | static inline float parentPageZoomFactor(Frame* frame) |
128 | { |
129 | Frame* parent = frame->tree().parent(); |
130 | if (!parent) |
131 | return 1; |
132 | return parent->pageZoomFactor(); |
133 | } |
134 | |
135 | static inline float parentTextZoomFactor(Frame* frame) |
136 | { |
137 | Frame* parent = frame->tree().parent(); |
138 | if (!parent) |
139 | return 1; |
140 | return parent->textZoomFactor(); |
141 | } |
142 | |
143 | Frame::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 | |
177 | void Frame::init() |
178 | { |
179 | m_loader->init(); |
180 | } |
181 | |
182 | Ref<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 | |
189 | Frame::~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 | |
209 | void Frame::addDestructionObserver(FrameDestructionObserver* observer) |
210 | { |
211 | m_destructionObservers.add(observer); |
212 | } |
213 | |
214 | void Frame::removeDestructionObserver(FrameDestructionObserver* observer) |
215 | { |
216 | m_destructionObservers.remove(observer); |
217 | } |
218 | |
219 | void 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 | |
248 | void 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) |
300 | void 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 | |
313 | int 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 | |
321 | static 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 | |
356 | String 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&. |
384 | String 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 = 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 = 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 | |
449 | static 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 | |
484 | String 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 | |
499 | void Frame::setSelectionChangeCallbacksDisabled(bool selectionChangeCallbacksDisabled) |
500 | { |
501 | m_selectionChangeCallbacksDisabled = selectionChangeCallbacksDisabled; |
502 | } |
503 | |
504 | bool Frame::selectionChangeCallbacksDisabled() const |
505 | { |
506 | return m_selectionChangeCallbacksDisabled; |
507 | } |
508 | #endif // PLATFORM(IOS_FAMILY) |
509 | |
510 | bool 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 | |
548 | void 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 | |
574 | bool 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 | |
581 | FloatSize Frame::(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 | |
601 | void 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 | |
615 | void 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 | |
632 | RenderView* Frame::contentRenderer() const |
633 | { |
634 | return document() ? document()->renderView() : nullptr; |
635 | } |
636 | |
637 | RenderWidget* 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 | |
652 | Frame* 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 | |
662 | void 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 | |
675 | void Frame::clearTimers() |
676 | { |
677 | clearTimers(m_view.get(), document()); |
678 | } |
679 | |
680 | void 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 | |
711 | void 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 | |
724 | String Frame::displayStringModifiedByEncoding(const String& str) const |
725 | { |
726 | return document() ? document()->displayStringModifiedByEncoding(str) : str; |
727 | } |
728 | |
729 | VisiblePosition 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 | |
744 | Document* 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 | |
757 | RefPtr<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 | |
786 | void 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 | |
829 | DOMWindow* Frame::window() const |
830 | { |
831 | return document() ? document()->domWindow() : nullptr; |
832 | } |
833 | |
834 | AbstractDOMWindow* Frame::virtualWindow() const |
835 | { |
836 | return window(); |
837 | } |
838 | |
839 | String 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 | |
849 | String Frame::trackedRepaintRectsAsText() const |
850 | { |
851 | if (!m_view) |
852 | return String(); |
853 | return m_view->trackedRepaintRectsAsText(); |
854 | } |
855 | |
856 | void Frame::setPageZoomFactor(float factor) |
857 | { |
858 | setPageAndTextZoomFactors(factor, m_textZoomFactor); |
859 | } |
860 | |
861 | void Frame::setTextZoomFactor(float factor) |
862 | { |
863 | setPageAndTextZoomFactors(m_pageZoomFactor, factor); |
864 | } |
865 | |
866 | void 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 | |
909 | float 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 | |
920 | void 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 | |
935 | void 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 | |
957 | void 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 | |
966 | bool 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 | |
983 | bool Frame::isAlwaysOnLoggingAllowed() const |
984 | { |
985 | return page() && page()->isAlwaysOnLoggingAllowed(); |
986 | } |
987 | |
988 | void Frame::dropChildren() |
989 | { |
990 | ASSERT(isMainFrame()); |
991 | while (Frame* child = tree().firstChild()) |
992 | tree().removeChild(*child); |
993 | } |
994 | |
995 | void Frame::selfOnlyRef() |
996 | { |
997 | ASSERT(isMainFrame()); |
998 | if (m_selfOnlyRefCount++) |
999 | return; |
1000 | |
1001 | ref(); |
1002 | } |
1003 | |
1004 | void 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 | |