void DumpRenderTreeSupportQt::clearFrameName(QWebFrame* frame) { Frame* coreFrame = QWebFramePrivate::core(frame); coreFrame->tree()->clearName(); }
FrameTree::~FrameTree() { for (Frame* child = firstChild(); child; child = child->tree().nextSibling()) child->setView(nullptr); }
void PaintLayerCompositor::updateIfNeededRecursive() { FrameView* view = m_layoutView.frameView(); if (view->shouldThrottleRendering()) return; for (Frame* child = m_layoutView.frameView()->frame().tree().firstChild(); child; child = child->tree().nextSibling()) { if (!child->isLocalFrame()) continue; LocalFrame* localFrame = toLocalFrame(child); // It's possible for trusted Pepper plugins to force hit testing in situations where // the frame tree is in an inconsistent state, such as in the middle of frame detach. // TODO(bbudge) Remove this check when trusted Pepper plugins are gone. if (localFrame->document()->isActive()) localFrame->contentLayoutObject()->compositor()->updateIfNeededRecursive(); } TRACE_EVENT0("blink", "PaintLayerCompositor::updateIfNeededRecursive"); ASSERT(!m_layoutView.needsLayout()); ScriptForbiddenScope forbidScript; // FIXME: enableCompositingModeIfNeeded can trigger a CompositingUpdateRebuildTree, // which asserts that it's not InCompositingUpdate. enableCompositingModeIfNeeded(); if (m_needsUpdateDescendantDependentFlags) { updateDescendantDependentFlagsForEntireSubtree(*rootLayer()); m_needsUpdateDescendantDependentFlags = false; } m_layoutView.commitPendingSelection(); lifecycle().advanceTo(DocumentLifecycle::InCompositingUpdate); updateIfNeeded(); lifecycle().advanceTo(DocumentLifecycle::CompositingClean); DocumentAnimations::updateCompositorAnimations(m_layoutView.document()); m_layoutView.frameView()->scrollableArea()->updateCompositorScrollAnimations(); if (const FrameView::ScrollableAreaSet* animatingScrollableAreas = m_layoutView.frameView()->animatingScrollableAreas()) { for (ScrollableArea* scrollableArea : *animatingScrollableAreas) scrollableArea->updateCompositorScrollAnimations(); } #if ENABLE(ASSERT) ASSERT(lifecycle().state() == DocumentLifecycle::CompositingClean); assertNoUnresolvedDirtyBits(); for (Frame* child = m_layoutView.frameView()->frame().tree().firstChild(); child; child = child->tree().nextSibling()) { if (!child->isLocalFrame()) continue; LocalFrame* localFrame = toLocalFrame(child); if (localFrame->shouldThrottleRendering()) continue; localFrame->contentLayoutObject()->compositor()->assertNoUnresolvedDirtyBits(); } #endif }
void InspectorResourceContentLoader::start() { m_started = true; Vector<Document*> documents; for (Frame* frame = m_inspectedFrame; frame; frame = frame->tree().traverseNext(m_inspectedFrame)) { if (!frame->isLocalFrame()) continue; LocalFrame* localFrame = toLocalFrame(frame); documents.append(localFrame->document()); documents.appendVector(InspectorPageAgent::importsForFrame(localFrame)); } for (Document* document : documents) { HashSet<String> urlsToFetch; ResourceRequest resourceRequest; HistoryItem* item = document->frame() ? document->frame()->loader().currentItem() : nullptr; if (item) { resourceRequest = FrameLoader::resourceRequestFromHistoryItem(item, ReturnCacheDataDontLoad); } else { resourceRequest = document->url(); resourceRequest.setCachePolicy(ReturnCacheDataDontLoad); } resourceRequest.setRequestContext(WebURLRequest::RequestContextInternal); if (!resourceRequest.url().string().isEmpty()) { urlsToFetch.add(resourceRequest.url().string()); FetchRequest request(resourceRequest, FetchInitiatorTypeNames::internal); ResourcePtr<Resource> resource = RawResource::fetch(request, document->fetcher()); if (resource) { // Prevent garbage collection by holding a reference to this resource. m_resources.append(resource.get()); ResourceClient* resourceClient = new ResourceClient(this); m_pendingResourceClients.add(resourceClient); resourceClient->waitForResource(resource.get()); } } WillBeHeapVector<RawPtrWillBeMember<CSSStyleSheet> > styleSheets; InspectorCSSAgent::collectAllDocumentStyleSheets(document, styleSheets); for (CSSStyleSheet* styleSheet : styleSheets) { if (styleSheet->isInline() || !styleSheet->contents()->loadCompleted()) continue; String url = styleSheet->baseURL().string(); if (url.isEmpty() || urlsToFetch.contains(url)) continue; urlsToFetch.add(url); FetchRequest request(ResourceRequest(url), FetchInitiatorTypeNames::internal); request.mutableResourceRequest().setRequestContext(WebURLRequest::RequestContextInternal); ResourcePtr<Resource> resource = CSSStyleSheetResource::fetch(request, document->fetcher()); if (!resource) continue; // Prevent garbage collection by holding a reference to this resource. m_resources.append(resource.get()); ResourceClient* resourceClient = new ResourceClient(this); m_pendingResourceClients.add(resourceClient); resourceClient->waitForResource(resource.get()); } } m_allRequestsStarted = true; checkDone(); }
static unsigned logCanCacheFrameDecision(Frame* frame, int indentLevel) { #ifdef NDEBUG UNUSED_PARAM(indentLevel); #endif PCLOG("+---"); if (!frame->loader()->documentLoader()) { PCLOG(" -There is no DocumentLoader object"); return 1 << NoDocumentLoader; } KURL currentURL = frame->loader()->documentLoader()->url(); KURL newURL = frame->loader()->provisionalDocumentLoader() ? frame->loader()->provisionalDocumentLoader()->url() : KURL(); if (!newURL.isEmpty()) PCLOG(" Determining if frame can be cached navigating from (", currentURL.string(), ") to (", newURL.string(), "):"); else PCLOG(" Determining if subframe with URL (", currentURL.string(), ") can be cached:"); unsigned rejectReasons = 0; if (!frame->loader()->documentLoader()->mainDocumentError().isNull()) { PCLOG(" -Main document has an error"); rejectReasons |= 1 << MainDocumentError; } if (frame->loader()->documentLoader()->substituteData().isValid() && frame->loader()->documentLoader()->substituteData().failingURL().isEmpty()) { PCLOG(" -Frame is an error page"); rejectReasons |= 1 << IsErrorPage; } if (frame->loader()->subframeLoader()->containsPlugins() && !frame->page()->settings()->pageCacheSupportsPlugins()) { PCLOG(" -Frame contains plugins"); rejectReasons |= 1 << HasPlugins; } if (frame->document()->url().protocolIs("https") && (frame->loader()->documentLoader()->response().cacheControlContainsNoCache() || frame->loader()->documentLoader()->response().cacheControlContainsNoStore())) { PCLOG(" -Frame is HTTPS, and cache control prohibits caching or storing"); rejectReasons |= 1 << IsHttpsAndCacheControlled; } if (frame->document()->domWindow() && frame->document()->domWindow()->hasEventListeners(eventNames().unloadEvent)) { PCLOG(" -Frame has an unload event listener"); rejectReasons |= 1 << HasUnloadListener; } #if ENABLE(SQL_DATABASE) if (DatabaseManager::manager().hasOpenDatabases(frame->document())) { PCLOG(" -Frame has open database handles"); rejectReasons |= 1 << HasDatabaseHandles; } #endif #if ENABLE(SHARED_WORKERS) if (SharedWorkerRepository::hasSharedWorkers(frame->document())) { PCLOG(" -Frame has associated SharedWorkers"); rejectReasons |= 1 << HasSharedWorkers; } #endif if (!frame->loader()->history()->currentItem()) { PCLOG(" -No current history item"); rejectReasons |= 1 << NoHistoryItem; } if (frame->loader()->quickRedirectComing()) { PCLOG(" -Quick redirect is coming"); rejectReasons |= 1 << QuickRedirectComing; } if (frame->loader()->documentLoader()->isLoadingInAPISense()) { PCLOG(" -DocumentLoader is still loading in API sense"); rejectReasons |= 1 << IsLoadingInAPISense; } if (frame->loader()->documentLoader()->isStopping()) { PCLOG(" -DocumentLoader is in the middle of stopping"); rejectReasons |= 1 << IsStopping; } if (!frame->document()->canSuspendActiveDOMObjects()) { PCLOG(" -The document cannot suspect its active DOM Objects"); rejectReasons |= 1 << CannotSuspendActiveDOMObjects; } if (!frame->loader()->documentLoader()->applicationCacheHost()->canCacheInPageCache()) { PCLOG(" -The DocumentLoader uses an application cache"); rejectReasons |= 1 << DocumentLoaderUsesApplicationCache; } if (!frame->loader()->client()->canCachePage()) { PCLOG(" -The client says this frame cannot be cached"); rejectReasons |= 1 << ClientDeniesCaching; } HistogramSupport::histogramEnumeration("PageCache.FrameCacheable", !rejectReasons, 2); int reasonCount = 0; for (int i = 0; i < NumberOfReasonsFramesCannotBeInPageCache; ++i) { if (rejectReasons & (1 << i)) { ++reasonCount; HistogramSupport::histogramEnumeration("PageCache.FrameRejectReason", i, NumberOfReasonsFramesCannotBeInPageCache); } } HistogramSupport::histogramEnumeration("PageCache.FrameRejectReasonCount", reasonCount, 1 + NumberOfReasonsFramesCannotBeInPageCache); for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) rejectReasons |= logCanCacheFrameDecision(child, indentLevel + 1); PCLOG(rejectReasons ? " Frame CANNOT be cached" : " Frame CAN be cached"); PCLOG("+---"); return rejectReasons; }
static bool logCanCacheFrameDecision(Frame* frame, int indentLevel) { // Only bother logging for frames that have actually loaded and have content. if (frame->loader()->stateMachine()->creatingInitialEmptyDocument()) return false; KURL currentURL = frame->loader()->documentLoader() ? frame->loader()->documentLoader()->url() : KURL(); if (currentURL.isEmpty()) return false; PCLOG("+---"); KURL newURL = frame->loader()->provisionalDocumentLoader() ? frame->loader()->provisionalDocumentLoader()->url() : KURL(); if (!newURL.isEmpty()) PCLOG(" Determining if frame can be cached navigating from (", currentURL.string(), ") to (", newURL.string(), "):"); else PCLOG(" Determining if subframe with URL (", currentURL.string(), ") can be cached:"); bool cannotCache = false; do { if (!frame->loader()->documentLoader()) { PCLOG(" -There is no DocumentLoader object"); cannotCache = true; break; } if (!frame->loader()->documentLoader()->mainDocumentError().isNull()) { PCLOG(" -Main document has an error"); cannotCache = true; } if (frame->loader()->subframeLoader()->containsPlugins()) { PCLOG(" -Frame contains plugins"); cannotCache = true; } if (frame->document()->url().protocolIs("https")) { PCLOG(" -Frame is HTTPS"); cannotCache = true; } if (frame->domWindow() && frame->domWindow()->hasEventListeners(eventNames().unloadEvent)) { PCLOG(" -Frame has an unload event listener"); cannotCache = true; } #if ENABLE(DATABASE) if (frame->document()->hasOpenDatabases()) { PCLOG(" -Frame has open database handles"); cannotCache = true; } #endif #if ENABLE(SHARED_WORKERS) if (SharedWorkerRepository::hasSharedWorkers(frame->document())) { PCLOG(" -Frame has associated SharedWorkers"); cannotCache = true; } #endif if (frame->document()->usingGeolocation()) { PCLOG(" -Frame uses Geolocation"); cannotCache = true; } if (!frame->loader()->history()->currentItem()) { PCLOG(" -No current history item"); cannotCache = true; } if (frame->loader()->quickRedirectComing()) { PCLOG(" -Quick redirect is coming"); cannotCache = true; } if (frame->loader()->documentLoader()->isLoadingInAPISense()) { PCLOG(" -DocumentLoader is still loading in API sense"); cannotCache = true; } if (frame->loader()->documentLoader()->isStopping()) { PCLOG(" -DocumentLoader is in the middle of stopping"); cannotCache = true; } if (!frame->document()->canSuspendActiveDOMObjects()) { PCLOG(" -The document cannot suspect its active DOM Objects"); cannotCache = true; } #if ENABLE(OFFLINE_WEB_APPLICATIONS) if (!frame->loader()->documentLoader()->applicationCacheHost()->canCacheInPageCache()) { PCLOG(" -The DocumentLoader uses an application cache"); cannotCache = true; } #endif if (!frame->loader()->client()->canCachePage()) { PCLOG(" -The client says this frame cannot be cached"); cannotCache = true; } } while (false); for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) if (!logCanCacheFrameDecision(child, indentLevel + 1)) cannotCache = true; PCLOG(cannotCache ? " Frame CANNOT be cached" : " Frame CAN be cached"); PCLOG("+---"); return !cannotCache; }
void ProgressTracker::progressStarted(Frame& frame) { LOG(Progress, "Progress started (%p) - frame %p(\"%s\"), value %f, tracked frames %d, originating frame %p", this, &frame, frame.tree().uniqueName().string().utf8().data(), m_progressValue, m_numProgressTrackedFrames, m_originatingProgressFrame.get()); m_client.willChangeEstimatedProgress(); if (!m_numProgressTrackedFrames || m_originatingProgressFrame == &frame) { reset(); m_progressValue = initialProgressValue; m_originatingProgressFrame = &frame; m_progressHeartbeatTimer.startRepeating(progressHeartbeatInterval); m_originatingProgressFrame->loader().loadProgressingStatusChanged(); bool isMainFrame = !m_originatingProgressFrame->tree().parent(); auto elapsedTimeSinceMainLoadComplete = std::chrono::steady_clock::now() - m_mainLoadCompletionTime; static const auto subframePartOfMainLoadThreshold = std::chrono::seconds(1); m_isMainLoad = isMainFrame || elapsedTimeSinceMainLoadComplete < subframePartOfMainLoadThreshold; m_client.progressStarted(*m_originatingProgressFrame); } m_numProgressTrackedFrames++; m_client.didChangeEstimatedProgress(); InspectorInstrumentation::frameStartedLoading(frame); }
static unsigned logCanCacheFrameDecision(Frame& frame, DiagnosticLoggingClient& diagnosticLoggingClient, unsigned indentLevel) { PCLOG("+---"); if (!frame.loader().documentLoader()) { PCLOG(" -There is no DocumentLoader object"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::noDocumentLoaderKey()); return 1 << NoDocumentLoader; } URL currentURL = frame.loader().documentLoader()->url(); URL newURL = frame.loader().provisionalDocumentLoader() ? frame.loader().provisionalDocumentLoader()->url() : URL(); if (!newURL.isEmpty()) PCLOG(" Determining if frame can be cached navigating from (", currentURL.string(), ") to (", newURL.string(), "):"); else PCLOG(" Determining if subframe with URL (", currentURL.string(), ") can be cached:"); unsigned rejectReasons = 0; if (!frame.loader().documentLoader()->mainDocumentError().isNull()) { PCLOG(" -Main document has an error"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::mainDocumentErrorKey()); if (frame.loader().documentLoader()->mainDocumentError().isCancellation() && frame.loader().documentLoader()->subresourceLoadersArePageCacheAcceptable()) PCLOG(" -But, it was a cancellation and all loaders during the cancelation were loading images or XHR."); else rejectReasons |= 1 << MainDocumentError; } if (frame.loader().documentLoader()->substituteData().isValid() && frame.loader().documentLoader()->substituteData().failingURL().isEmpty()) { PCLOG(" -Frame is an error page"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::isErrorPageKey()); rejectReasons |= 1 << IsErrorPage; } if (frame.loader().subframeLoader().containsPlugins() && !frame.page()->settings().pageCacheSupportsPlugins()) { PCLOG(" -Frame contains plugins"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::hasPluginsKey()); rejectReasons |= 1 << HasPlugins; } if (frame.isMainFrame() && frame.document()->url().protocolIs("https") && frame.loader().documentLoader()->response().cacheControlContainsNoStore()) { PCLOG(" -Frame is HTTPS, and cache control prohibits storing"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::httpsNoStoreKey()); rejectReasons |= 1 << IsHttpsAndCacheControlled; } if (!frame.loader().history().currentItem()) { PCLOG(" -No current history item"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::noCurrentHistoryItemKey()); rejectReasons |= 1 << NoHistoryItem; } if (frame.loader().quickRedirectComing()) { PCLOG(" -Quick redirect is coming"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::quirkRedirectComingKey()); rejectReasons |= 1 << QuickRedirectComing; } if (frame.loader().documentLoader()->isLoadingInAPISense()) { PCLOG(" -DocumentLoader is still loading in API sense"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::loadingAPISenseKey()); rejectReasons |= 1 << IsLoadingInAPISense; } if (frame.loader().documentLoader()->isStopping()) { PCLOG(" -DocumentLoader is in the middle of stopping"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::documentLoaderStoppingKey()); rejectReasons |= 1 << IsStopping; } Vector<ActiveDOMObject*> unsuspendableObjects; if (!frame.document()->canSuspendActiveDOMObjectsForPageCache(&unsuspendableObjects)) { PCLOG(" -The document cannot suspend its active DOM Objects"); for (auto* activeDOMObject : unsuspendableObjects) { PCLOG(" - Unsuspendable: ", activeDOMObject->activeDOMObjectName()); diagnosticLoggingClient.logDiagnosticMessageWithValue(DiagnosticLoggingKeys::pageCacheKey(), DiagnosticLoggingKeys::unsuspendableDOMObjectKey(), activeDOMObject->activeDOMObjectName(), ShouldSample::Yes); UNUSED_PARAM(activeDOMObject); } logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::cannotSuspendActiveDOMObjectsKey()); rejectReasons |= 1 << CannotSuspendActiveDOMObjects; } if (!frame.loader().documentLoader()->applicationCacheHost()->canCacheInPageCache()) { PCLOG(" -The DocumentLoader uses an application cache"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::applicationCacheKey()); rejectReasons |= 1 << DocumentLoaderUsesApplicationCache; } if (!frame.loader().client().canCachePage()) { PCLOG(" -The client says this frame cannot be cached"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::deniedByClientKey()); rejectReasons |= 1 << ClientDeniesCaching; } for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling()) rejectReasons |= logCanCacheFrameDecision(*child, diagnosticLoggingClient, indentLevel + 1); PCLOG(rejectReasons ? " Frame CANNOT be cached" : " Frame CAN be cached"); PCLOG("+---"); return rejectReasons; }
RefPtr<LegacyWebArchive> LegacyWebArchive::create(const String& markupString, Frame& frame, const Vector<Node*>& nodes, std::function<bool (Frame&)> frameFilter) { const ResourceResponse& response = frame.loader().documentLoader()->response(); URL responseURL = response.url(); // it's possible to have a response without a URL here // <rdar://problem/5454935> if (responseURL.isNull()) responseURL = URL(ParsedURLString, emptyString()); RefPtr<ArchiveResource> mainResource = ArchiveResource::create(utf8Buffer(markupString), responseURL, response.mimeType(), "UTF-8", frame.tree().uniqueName()); if (!mainResource) return nullptr; Vector<RefPtr<LegacyWebArchive>> subframeArchives; Vector<RefPtr<ArchiveResource>> subresources; HashSet<URL> uniqueSubresources; size_t nodesSize = nodes.size(); for (size_t i = 0; i < nodesSize; ++i) { Node& node = *nodes[i]; Frame* childFrame; if ((is<HTMLFrameElementBase>(node) || is<HTMLObjectElement>(node)) && (childFrame = downcast<HTMLFrameOwnerElement>(node).contentFrame())) { if (frameFilter && !frameFilter(*childFrame)) continue; if (RefPtr<LegacyWebArchive> subframeArchive = create(*childFrame->document(), frameFilter)) subframeArchives.append(WTFMove(subframeArchive)); else LOG_ERROR("Unabled to archive subframe %s", childFrame->tree().uniqueName().string().utf8().data()); } else { ListHashSet<URL> subresourceURLs; node.getSubresourceURLs(subresourceURLs); DocumentLoader* documentLoader = frame.loader().documentLoader(); for (const auto& subresourceURL : subresourceURLs) { if (uniqueSubresources.contains(subresourceURL)) continue; uniqueSubresources.add(subresourceURL); if (RefPtr<ArchiveResource> resource = documentLoader->subresource(subresourceURL)) { subresources.append(WTFMove(resource)); continue; } ResourceRequest request(subresourceURL); #if ENABLE(CACHE_PARTITIONING) request.setDomainForCachePartition(frame.document()->topOrigin()->domainForCachePartition()); #endif CachedResource* cachedResource = MemoryCache::singleton().resourceForRequest(request, frame.page()->sessionID()); if (cachedResource) { if (RefPtr<ArchiveResource> resource = ArchiveResource::create(cachedResource->resourceBuffer(), subresourceURL, cachedResource->response())) { subresources.append(WTFMove(resource)); continue; } } // FIXME: should do something better than spew to console here LOG_ERROR("Failed to archive subresource for %s", subresourceURL.string().utf8().data()); } } } // Add favicon if one exists for this page, if we are archiving the entire page. if (nodesSize && nodes[0]->isDocumentNode() && iconDatabase().isEnabled()) { const String& iconURL = iconDatabase().synchronousIconURLForPageURL(responseURL); if (!iconURL.isEmpty() && iconDatabase().synchronousIconDataKnownForIconURL(iconURL)) { if (Image* iconImage = iconDatabase().synchronousIconForPageURL(responseURL, IntSize(16, 16))) { if (RefPtr<ArchiveResource> resource = ArchiveResource::create(iconImage->data(), URL(ParsedURLString, iconURL), "image/x-icon", "", "")) subresources.append(resource.release()); } } } return create(WTFMove(mainResource), WTFMove(subresources), WTFMove(subframeArchives)); }
FrameTree::~FrameTree() { for (Frame* child = firstChild(); child; child = child->tree()->nextSibling()) child->setView(0); }
CachedFrame::CachedFrame(Frame& frame) : CachedFrameBase(frame) { #ifndef NDEBUG cachedFrameCounter.increment(); #endif ASSERT(m_document); ASSERT(m_documentLoader); ASSERT(m_view); if (frame.page()->focusController().focusedFrame() == &frame) frame.page()->focusController().setFocusedFrame(&frame.mainFrame()); // Custom scrollbar renderers will get reattached when the document comes out of the page cache m_view->detachCustomScrollbars(); m_document->setInPageCache(true); frame.loader().stopLoading(UnloadEventPolicyUnloadAndPageHide); // Create the CachedFrames for all Frames in the FrameTree. for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling()) m_childFrames.append(std::make_unique<CachedFrame>(*child)); // Active DOM objects must be suspended before we cache the frame script data, // but after we've fired the pagehide event, in case that creates more objects. // Suspending must also happen after we've recursed over child frames, in case // those create more objects. m_document->documentWillSuspendForPageCache(); m_document->suspendScriptedAnimationControllerCallbacks(); m_document->suspendActiveDOMObjects(ActiveDOMObject::PageCache); m_cachedFrameScriptData = std::make_unique<ScriptCachedFrameData>(frame); m_document->domWindow()->suspendForPageCache(); frame.loader().client().savePlatformDataToCachedFrame(this); if (m_isComposited && PageCache::singleton().shouldClearBackingStores()) frame.view()->clearBackingStores(); // documentWillSuspendForPageCache() can set up a layout timer on the FrameView, so clear timers after that. frame.clearTimers(); // Deconstruct the FrameTree, to restore it later. // We do this for two reasons: // 1 - We reuse the main frame, so when it navigates to a new page load it needs to start with a blank FrameTree. // 2 - It's much easier to destroy a CachedFrame while it resides in the PageCache if it is disconnected from its parent. for (unsigned i = 0; i < m_childFrames.size(); ++i) frame.tree().removeChild(&m_childFrames[i]->view()->frame()); if (!m_isMainFrame) frame.page()->decrementSubframeCount(); frame.loader().client().didSaveToPageCache(); #ifndef NDEBUG if (m_isMainFrame) LOG(PageCache, "Finished creating CachedFrame for main frame url '%s' and DocumentLoader %p\n", m_url.string().utf8().data(), m_documentLoader.get()); else LOG(PageCache, "Finished creating CachedFrame for child frame with url '%s' and DocumentLoader %p\n", m_url.string().utf8().data(), m_documentLoader.get()); #endif #if PLATFORM(IOS) if (m_isMainFrame) { if (DOMWindow* domWindow = m_document->domWindow()) { if (domWindow->scrollEventListenerCount() && frame.page()) frame.page()->chrome().client().setNeedsScrollNotifications(&frame, false); } } #endif ASSERT_WITH_SECURITY_IMPLICATION(!m_documentLoader->isLoading()); }
void ProgressTracker::progressStarted(Frame& frame) { LOG(Progress, "Progress started (%p) - frame %p(\"%s\"), value %f, tracked frames %d, originating frame %p", this, &frame, frame.tree().uniqueName().string().utf8().data(), m_progressValue, m_numProgressTrackedFrames, m_originatingProgressFrame.get()); m_client.willChangeEstimatedProgress(); if (!m_numProgressTrackedFrames || m_originatingProgressFrame == &frame) { reset(); m_progressValue = initialProgressValue; m_originatingProgressFrame = &frame; m_progressHeartbeatTimer.startRepeating(progressHeartbeatInterval); m_originatingProgressFrame->loader().loadProgressingStatusChanged(); m_client.progressStarted(*m_originatingProgressFrame); } m_numProgressTrackedFrames++; m_client.didChangeEstimatedProgress(); InspectorInstrumentation::frameStartedLoading(frame); }
void PageSerializer::serializeFrame(Frame* frame) { Document* document = frame->document(); KURL url = document->url(); if (!url.isValid() || url.isBlankURL()) { // For blank frames we generate a fake URL so they can be referenced by their containing frame. url = urlForBlankFrame(frame); } if (m_resourceURLs.contains(url)) { // FIXME: We could have 2 frame with the same URL but which were dynamically changed and have now // different content. So we should serialize both and somehow rename the frame src in the containing // frame. Arg! return; } Vector<Node*> nodes; SerializerMarkupAccumulator accumulator(this, document, &nodes); TextEncoding textEncoding(document->charset()); CString data; if (!textEncoding.isValid()) { // FIXME: iframes used as images trigger this. We should deal with them correctly. return; } String text = accumulator.serializeNodes(document->documentElement(), 0, IncludeNode); CString frameHTML = textEncoding.encode(text.characters(), text.length(), EntitiesForUnencodables); m_resources->append(Resource(url, document->suggestedMIMEType(), SharedBuffer::create(frameHTML.data(), frameHTML.length()))); m_resourceURLs.add(url); for (Vector<Node*>::iterator iter = nodes.begin(); iter != nodes.end(); ++iter) { Node* node = *iter; if (!node->isElementNode()) continue; Element* element = toElement(node); // We have to process in-line style as it might contain some resources (typically background images). if (element->isStyledElement()) retrieveResourcesForProperties(static_cast<StyledElement*>(element)->inlineStyle(), document); if (element->hasTagName(HTMLNames::imgTag)) { HTMLImageElement* imageElement = static_cast<HTMLImageElement*>(element); KURL url = document->completeURL(imageElement->getAttribute(HTMLNames::srcAttr)); CachedImage* cachedImage = imageElement->cachedImage(); addImageToResources(cachedImage, imageElement->renderer(), url); } else if (element->hasTagName(HTMLNames::linkTag)) { HTMLLinkElement* linkElement = static_cast<HTMLLinkElement*>(element); if (CSSStyleSheet* sheet = linkElement->sheet()) { KURL url = document->completeURL(linkElement->getAttribute(HTMLNames::hrefAttr)); serializeCSSStyleSheet(sheet, url); ASSERT(m_resourceURLs.contains(url)); } } else if (element->hasTagName(HTMLNames::styleTag)) { HTMLStyleElement* styleElement = static_cast<HTMLStyleElement*>(element); if (CSSStyleSheet* sheet = styleElement->sheet()) serializeCSSStyleSheet(sheet, KURL()); } } for (Frame* childFrame = frame->tree()->firstChild(); childFrame; childFrame = childFrame->tree()->nextSibling()) serializeFrame(childFrame); }
static void setNeedsReapplyStylesInAllFrames(Page* page) { for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) frame->setNeedsReapplyStyles(); }
void ProgressTracker::progressCompleted(Frame& frame) { LOG(Progress, "Progress completed (%p) - frame %p(\"%s\"), value %f, tracked frames %d, originating frame %p", this, &frame, frame.tree().uniqueName().string().utf8().data(), m_progressValue, m_numProgressTrackedFrames, m_originatingProgressFrame.get()); if (m_numProgressTrackedFrames <= 0) return; m_client.willChangeEstimatedProgress(); m_numProgressTrackedFrames--; if (!m_numProgressTrackedFrames || m_originatingProgressFrame == &frame) finalProgressComplete(); m_client.didChangeEstimatedProgress(); }
static unsigned logCanCacheFrameDecision(Frame* frame, int indentLevel) { PCLOG("+---"); if (!frame->loader().documentLoader()) { PCLOG(" -There is no DocumentLoader object"); return 1 << NoDocumentLoader; } URL currentURL = frame->loader().documentLoader()->url(); URL newURL = frame->loader().provisionalDocumentLoader() ? frame->loader().provisionalDocumentLoader()->url() : URL(); if (!newURL.isEmpty()) PCLOG(" Determining if frame can be cached navigating from (", currentURL.string(), ") to (", newURL.string(), "):"); else PCLOG(" Determining if subframe with URL (", currentURL.string(), ") can be cached:"); unsigned rejectReasons = 0; if (!frame->loader().documentLoader()->mainDocumentError().isNull()) { PCLOG(" -Main document has an error"); #if !PLATFORM(IOS) rejectReasons |= 1 << MainDocumentError; #else if (frame->loader().documentLoader()->mainDocumentError().isCancellation() && frame->loader().documentLoader()->subresourceLoadersArePageCacheAcceptable()) PCLOG(" -But, it was a cancellation and all loaders during the cancel were loading images."); else rejectReasons |= 1 << MainDocumentError; #endif } if (frame->loader().documentLoader()->substituteData().isValid() && frame->loader().documentLoader()->substituteData().failingURL().isEmpty()) { PCLOG(" -Frame is an error page"); rejectReasons |= 1 << IsErrorPage; } if (frame->loader().subframeLoader().containsPlugins() && !frame->page()->settings().pageCacheSupportsPlugins()) { PCLOG(" -Frame contains plugins"); rejectReasons |= 1 << HasPlugins; } if (frame->document()->url().protocolIs("https") && (frame->loader().documentLoader()->response().cacheControlContainsNoCache() || frame->loader().documentLoader()->response().cacheControlContainsNoStore())) { PCLOG(" -Frame is HTTPS, and cache control prohibits caching or storing"); rejectReasons |= 1 << IsHttpsAndCacheControlled; } if (frame->document()->domWindow() && frame->document()->domWindow()->hasEventListeners(eventNames().unloadEvent)) { PCLOG(" -Frame has an unload event listener"); #if !PLATFORM(IOS) rejectReasons |= 1 << HasUnloadListener; #else // iOS allows pages with unload event listeners to enter the page cache. PCLOG(" -BUT iOS allows these pages to be cached."); #endif } #if ENABLE(SQL_DATABASE) if (DatabaseManager::manager().hasOpenDatabases(frame->document())) { PCLOG(" -Frame has open database handles"); rejectReasons |= 1 << HasDatabaseHandles; } #endif #if ENABLE(SHARED_WORKERS) if (SharedWorkerRepository::hasSharedWorkers(frame->document())) { PCLOG(" -Frame has associated SharedWorkers"); rejectReasons |= 1 << HasSharedWorkers; } #endif if (!frame->loader().history().currentItem()) { PCLOG(" -No current history item"); rejectReasons |= 1 << NoHistoryItem; } if (frame->loader().quickRedirectComing()) { PCLOG(" -Quick redirect is coming"); rejectReasons |= 1 << QuickRedirectComing; } if (frame->loader().documentLoader()->isLoadingInAPISense()) { PCLOG(" -DocumentLoader is still loading in API sense"); rejectReasons |= 1 << IsLoadingInAPISense; } if (frame->loader().documentLoader()->isStopping()) { PCLOG(" -DocumentLoader is in the middle of stopping"); rejectReasons |= 1 << IsStopping; } if (!frame->document()->canSuspendActiveDOMObjects()) { PCLOG(" -The document cannot suspect its active DOM Objects"); rejectReasons |= 1 << CannotSuspendActiveDOMObjects; } if (!frame->loader().documentLoader()->applicationCacheHost()->canCacheInPageCache()) { PCLOG(" -The DocumentLoader uses an application cache"); rejectReasons |= 1 << DocumentLoaderUsesApplicationCache; } if (!frame->loader().client().canCachePage()) { PCLOG(" -The client says this frame cannot be cached"); rejectReasons |= 1 << ClientDeniesCaching; } for (Frame* child = frame->tree().firstChild(); child; child = child->tree().nextSibling()) rejectReasons |= logCanCacheFrameDecision(child, indentLevel + 1); PCLOG(rejectReasons ? " Frame CANNOT be cached" : " Frame CAN be cached"); PCLOG("+---"); return rejectReasons; }
CachedFrame::CachedFrame(Frame* frame) : CachedFrameBase(frame) { #ifndef NDEBUG cachedFrameCounter().increment(); #endif ASSERT(m_document); ASSERT(m_documentLoader); ASSERT(m_view); if (frame->page()->focusController()->focusedFrame() == frame) frame->page()->focusController()->setFocusedFrame(frame->page()->mainFrame()); // Custom scrollbar renderers will get reattached when the document comes out of the page cache m_view->detachCustomScrollbars(); m_document->setInPageCache(true); frame->loader()->stopLoading(UnloadEventPolicyUnloadAndPageHide); // Create the CachedFrames for all Frames in the FrameTree. for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) m_childFrames.append(CachedFrame::create(child)); // Active DOM objects must be suspended before we cache the frame script data, // but after we've fired the pagehide event, in case that creates more objects. // Suspending must also happen after we've recursed over child frames, in case // those create more objects. m_document->documentWillSuspendForPageCache(); m_document->suspendScriptedAnimationControllerCallbacks(); m_document->suspendActiveDOMObjects(ActiveDOMObject::DocumentWillBecomeInactive); m_cachedFrameScriptData = adoptPtr(new ScriptCachedFrameData(frame)); m_document->domWindow()->suspendForPageCache(); frame->loader()->client()->savePlatformDataToCachedFrame(this); #if USE(ACCELERATED_COMPOSITING) if (m_isComposited && pageCache()->shouldClearBackingStores()) frame->view()->clearBackingStores(); #endif // documentWillSuspendForPageCache() can set up a layout timer on the FrameView, so clear timers after that. frame->clearTimers(); // Deconstruct the FrameTree, to restore it later. // We do this for two reasons: // 1 - We reuse the main frame, so when it navigates to a new page load it needs to start with a blank FrameTree. // 2 - It's much easier to destroy a CachedFrame while it resides in the PageCache if it is disconnected from its parent. for (unsigned i = 0; i < m_childFrames.size(); ++i) frame->tree()->removeChild(m_childFrames[i]->view()->frame()); if (!m_isMainFrame) frame->page()->decrementSubframeCount(); frame->loader()->client()->didSaveToPageCache(); #ifndef NDEBUG if (m_isMainFrame) LOG(PageCache, "Finished creating CachedFrame for main frame url '%s' and DocumentLoader %p\n", m_url.string().utf8().data(), m_documentLoader.get()); else LOG(PageCache, "Finished creating CachedFrame for child frame with url '%s' and DocumentLoader %p\n", m_url.string().utf8().data(), m_documentLoader.get()); #endif }
void FullscreenElementStack::requestFullScreenForElement(Element* element, unsigned short flags, FullScreenCheckType checkType) { // The Mozilla Full Screen API <https://wiki.mozilla.org/Gecko:FullScreenAPI> has different requirements // for full screen mode, and do not have the concept of a full screen element stack. bool inLegacyMozillaMode = (flags & Element::LEGACY_MOZILLA_REQUEST); do { if (!element) element = document()->documentElement(); // 1. If any of the following conditions are true, terminate these steps and queue a task to fire // an event named fullscreenerror with its bubbles attribute set to true on the context object's // node document: // The context object is not in a document. if (!element->inDocument()) break; // The context object's node document, or an ancestor browsing context's document does not have // the fullscreen enabled flag set. if (checkType == EnforceIFrameAllowFullScreenRequirement && !fullScreenIsAllowedForElement(element)) break; // The context object's node document fullscreen element stack is not empty and its top element // is not an ancestor of the context object. (NOTE: Ignore this requirement if the request was // made via the legacy Mozilla-style API.) if (!m_fullScreenElementStack.isEmpty() && !inLegacyMozillaMode) { Element* lastElementOnStack = m_fullScreenElementStack.last().get(); if (lastElementOnStack == element || !lastElementOnStack->contains(element)) break; } // A descendant browsing context's document has a non-empty fullscreen element stack. bool descendentHasNonEmptyStack = false; for (Frame* descendant = document()->frame() ? document()->frame()->tree().traverseNext() : 0; descendant; descendant = descendant->tree().traverseNext()) { if (fullscreenElementFrom(descendant->document())) { descendentHasNonEmptyStack = true; break; } } if (descendentHasNonEmptyStack && !inLegacyMozillaMode) break; // This algorithm is not allowed to show a pop-up: // An algorithm is allowed to show a pop-up if, in the task in which the algorithm is running, either: // - an activation behavior is currently being processed whose click event was trusted, or // - the event listener for a trusted click event is being handled. // FIXME: Does this need to null-check settings()? if (!UserGestureIndicator::processingUserGesture() && (!element->isMediaElement() || document()->settings()->mediaFullscreenRequiresUserGesture())) break; // There is a previously-established user preference, security risk, or platform limitation. if (!document()->settings() || !document()->settings()->fullScreenEnabled()) break; // 2. Let doc be element's node document. (i.e. "this") Document* currentDoc = document(); // 3. Let docs be all doc's ancestor browsing context's documents (if any) and doc. Deque<Document*> docs; do { docs.prepend(currentDoc); currentDoc = currentDoc->ownerElement() ? ¤tDoc->ownerElement()->document() : 0; } while (currentDoc); // 4. For each document in docs, run these substeps: Deque<Document*>::iterator current = docs.begin(), following = docs.begin(); do { ++following; // 1. Let following document be the document after document in docs, or null if there is no // such document. Document* currentDoc = *current; Document* followingDoc = following != docs.end() ? *following : 0; // 2. If following document is null, push context object on document's fullscreen element // stack, and queue a task to fire an event named fullscreenchange with its bubbles attribute // set to true on the document. if (!followingDoc) { from(currentDoc)->pushFullscreenElementStack(element); addDocumentToFullScreenChangeEventQueue(currentDoc); continue; } // 3. Otherwise, if document's fullscreen element stack is either empty or its top element // is not following document's browsing context container, Element* topElement = fullscreenElementFrom(currentDoc); if (!topElement || topElement != followingDoc->ownerElement()) { // ...push following document's browsing context container on document's fullscreen element // stack, and queue a task to fire an event named fullscreenchange with its bubbles attribute // set to true on document. from(currentDoc)->pushFullscreenElementStack(followingDoc->ownerElement()); addDocumentToFullScreenChangeEventQueue(currentDoc); continue; } // 4. Otherwise, do nothing for this document. It stays the same. } while (++current != docs.end()); // 5. Return, and run the remaining steps asynchronously. // 6. Optionally, perform some animation. m_areKeysEnabledInFullScreen = flags & Element::ALLOW_KEYBOARD_INPUT; document()->frameHost()->chrome().client().enterFullScreenForElement(element); // 7. Optionally, display a message indicating how the user can exit displaying the context object fullscreen. return; } while (0); m_fullScreenErrorEventTargetQueue.append(element ? element : document()->documentElement()); m_fullScreenChangeDelayTimer.startOneShot(0); }
static void setLoadsImagesAutomaticallyInAllFrames(Page* page) { for (Frame* frame = page->mainFrame(); frame; frame = frame->tree()->traverseNext()) frame->document()->cachedResourceLoader()->setAutoLoadImages(page->settings()->loadsImagesAutomatically()); }