void InspectorApplicationCacheAgent::getFramesWithManifests( ErrorString*, std::unique_ptr< protocol::Array<protocol::ApplicationCache::FrameWithManifest>>* result) { *result = protocol::Array<protocol::ApplicationCache::FrameWithManifest>::create(); for (LocalFrame* frame : *m_inspectedFrames) { DocumentLoader* documentLoader = frame->loader().documentLoader(); if (!documentLoader) return; ApplicationCacheHost* host = documentLoader->applicationCacheHost(); ApplicationCacheHost::CacheInfo info = host->applicationCacheInfo(); String manifestURL = info.m_manifest.getString(); if (!manifestURL.isEmpty()) { std::unique_ptr<protocol::ApplicationCache::FrameWithManifest> value = protocol::ApplicationCache::FrameWithManifest::create() .setFrameId(IdentifiersFactory::frameId(frame)) .setManifestURL(manifestURL) .setStatus(static_cast<int>(host->getStatus())) .build(); (*result)->addItem(std::move(value)); } } }
bool PageCache::canCachePageContainingThisFrame(Frame& frame) { for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling()) { if (!canCachePageContainingThisFrame(*child)) return false; } FrameLoader& frameLoader = frame.loader(); DocumentLoader* documentLoader = frameLoader.documentLoader(); Document* document = frame.document(); return documentLoader && (documentLoader->mainDocumentError().isNull() || (documentLoader->mainDocumentError().isCancellation() && documentLoader->subresourceLoadersArePageCacheAcceptable())) // Do not cache error pages (these can be recognized as pages with substitute data or unreachable URLs). && !(documentLoader->substituteData().isValid() && !documentLoader->substituteData().failingURL().isEmpty()) && (!frameLoader.subframeLoader().containsPlugins() || frame.page()->settings().pageCacheSupportsPlugins()) && !(frame.isMainFrame() && document->url().protocolIs("https") && documentLoader->response().cacheControlContainsNoStore()) && frameLoader.history().currentItem() && !frameLoader.quickRedirectComing() && !documentLoader->isLoading() && !documentLoader->isStopping() && document->canSuspendActiveDOMObjectsForPageCache() // FIXME: We should investigating caching frames that have an associated // application cache. <rdar://problem/5917899> tracks that work. && documentLoader->applicationCacheHost()->canCacheInPageCache() && frameLoader.client().canCachePage(); }
bool PageCache::canCachePageContainingThisFrame(Frame* frame) { for (Frame* child = frame->tree()->firstChild(); child; child = child->tree()->nextSibling()) { if (!canCachePageContainingThisFrame(child)) return false; } FrameLoader* frameLoader = frame->loader(); DocumentLoader* documentLoader = frameLoader->documentLoader(); Document* document = frame->document(); return documentLoader && documentLoader->mainDocumentError().isNull() // Do not cache error pages (these can be recognized as pages with substitute data or unreachable URLs). && !(documentLoader->substituteData().isValid() && !documentLoader->substituteData().failingURL().isEmpty()) && (!frameLoader->subframeLoader()->containsPlugins() || frame->page()->settings()->pageCacheSupportsPlugins()) && (!document->url().protocolIs("https") || (!documentLoader->response().cacheControlContainsNoCache() && !documentLoader->response().cacheControlContainsNoStore())) && (!document->domWindow() || !document->domWindow()->hasEventListeners(eventNames().unloadEvent)) #if ENABLE(SQL_DATABASE) && !DatabaseManager::manager().hasOpenDatabases(document) #endif #if ENABLE(SHARED_WORKERS) && !SharedWorkerRepository::hasSharedWorkers(document) #endif && frameLoader->history()->currentItem() && !frameLoader->quickRedirectComing() && !documentLoader->isLoadingInAPISense() && !documentLoader->isStopping() && document->canSuspendActiveDOMObjects() // FIXME: We should investigating caching frames that have an associated // application cache. <rdar://problem/5917899> tracks that work. && documentLoader->applicationCacheHost()->canCacheInPageCache() && frameLoader->client()->canCachePage(); }
void ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame* frame) { if (!frame->settings().offlineWebApplicationCacheEnabled()) return; if (!frame->document()->securityOrigin()->canAccessApplicationCache(frame->tree().top().document()->securityOrigin())) return; DocumentLoader* documentLoader = frame->loader().documentLoader(); ASSERT(!documentLoader->applicationCacheHost()->applicationCache()); ApplicationCache* mainResourceCache = documentLoader->applicationCacheHost()->mainResourceApplicationCache(); if (mainResourceCache) { mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext); } }
void InspectorApplicationCacheAgent::getManifestForFrame(ErrorString* errorString, const String& frameId, String* manifestURL) { DocumentLoader* documentLoader = assertFrameWithDocumentLoader(errorString, frameId); if (!documentLoader) return; ApplicationCacheHost::CacheInfo info = documentLoader->applicationCacheHost()->applicationCacheInfo(); *manifestURL = info.m_manifest.string(); }
void HTMLHtmlElement::insertedByParser() { // When parsing a fragment, its dummy document has a null parser. if (!document()->parser() || !document()->parser()->documentWasLoadedAsPartOfNavigation()) return; if (!document()->frame()) return; DocumentLoader* documentLoader = document()->frame()->loader()->documentLoader(); if (!documentLoader) return; const AtomicString& manifest = getAttribute(manifestAttr); if (manifest.isEmpty()) documentLoader->applicationCacheHost()->selectCacheWithoutManifest(); else documentLoader->applicationCacheHost()->selectCacheWithManifest(document()->completeURL(manifest)); }
void InspectorApplicationCacheAgent::getApplicationCaches(RefPtr<InspectorValue>* applicationCaches) { DocumentLoader* documentLoader = m_inspectorController->inspectedPage()->mainFrame()->loader()->documentLoader(); if (documentLoader) { ApplicationCacheHost* host = documentLoader->applicationCacheHost(); ApplicationCacheHost::CacheInfo info = host->applicationCacheInfo(); ApplicationCacheHost::ResourceInfoList resources; host->fillResourceList(&resources); *applicationCaches = buildObjectForApplicationCache(resources, info); } }
void InspectorApplicationCacheAgent::getApplicationCaches(ErrorString*, RefPtr<InspectorObject>* applicationCaches) { DocumentLoader* documentLoader = m_inspectedPage->mainFrame()->loader()->documentLoader(); if (!documentLoader) return; ApplicationCacheHost* host = documentLoader->applicationCacheHost(); ApplicationCacheHost::CacheInfo info = host->applicationCacheInfo(); ApplicationCacheHost::ResourceInfoList resources; host->fillResourceList(&resources); *applicationCaches = buildObjectForApplicationCache(resources, info); }
void ApplicationCacheGroup::selectCacheWithoutManifestURL(Frame* frame) { if (!frame->settings().offlineWebApplicationCacheEnabled()) return; DocumentLoader* documentLoader = frame->loader().documentLoader(); ASSERT(!documentLoader->applicationCacheHost()->applicationCache()); // Don't access anything on disk if private browsing is enabled. if (frame->page()->usesEphemeralSession() || !frame->document()->securityOrigin()->canAccessApplicationCache(frame->tree().top().document()->securityOrigin())) { postListenerTask(ApplicationCacheHost::CHECKING_EVENT, documentLoader); postListenerTask(ApplicationCacheHost::ERROR_EVENT, documentLoader); return; } ApplicationCache* mainResourceCache = documentLoader->applicationCacheHost()->mainResourceApplicationCache(); if (mainResourceCache) { mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext); } }
Response InspectorApplicationCacheAgent::getManifestForFrame( const String& frameId, String* manifestURL) { DocumentLoader* documentLoader = nullptr; Response response = assertFrameWithDocumentLoader(frameId, documentLoader); if (!response.isSuccess()) return response; ApplicationCacheHost::CacheInfo info = documentLoader->applicationCacheHost()->applicationCacheInfo(); *manifestURL = info.m_manifest.getString(); return Response::OK(); }
void InspectorApplicationCacheAgent::updateApplicationCacheStatus(LocalFrame* frame) { DocumentLoader* documentLoader = frame->loader().documentLoader(); if (!documentLoader) return; ApplicationCacheHost* host = documentLoader->applicationCacheHost(); ApplicationCacheHost::Status status = host->status(); ApplicationCacheHost::CacheInfo info = host->applicationCacheInfo(); String manifestURL = info.m_manifest.string(); m_frontend->applicationCacheStatusUpdated(m_pageAgent->frameId(frame), manifestURL, static_cast<int>(status)); }
void InspectorApplicationCacheAgent::getApplicationCacheForFrame(ErrorString* errorString, const String& frameId, RefPtr<TypeBuilder::ApplicationCache::ApplicationCache>& applicationCache) { DocumentLoader* documentLoader = assertFrameWithDocumentLoader(errorString, frameId); if (!documentLoader) return; ApplicationCacheHost* host = documentLoader->applicationCacheHost(); ApplicationCacheHost::CacheInfo info = host->applicationCacheInfo(); ApplicationCacheHost::ResourceInfoList resources; host->fillResourceList(&resources); applicationCache = buildObjectForApplicationCache(resources, info); }
void InspectorApplicationCacheAgent::updateApplicationCacheStatus(LocalFrame* frame) { DocumentLoader* documentLoader = frame->loader().documentLoader(); if (!documentLoader) return; ApplicationCacheHost* host = documentLoader->applicationCacheHost(); ApplicationCacheHost::Status status = host->getStatus(); ApplicationCacheHost::CacheInfo info = host->applicationCacheInfo(); String manifestURL = info.m_manifest.getString(); String frameId = IdentifiersFactory::frameId(frame); frontend()->applicationCacheStatusUpdated(frameId, manifestURL, static_cast<int>(status)); }
void HTMLHtmlElement::insertedIntoDocument() { HTMLElement::insertedIntoDocument(); if (!document()->parsing()) return; if (!document()->frame()) return; DocumentLoader* documentLoader = document()->frame()->loader()->documentLoader(); if (!documentLoader) return; // Check the manifest attribute // FIXME: Revisit this when we get a clarification from whatwg on how to handle empty // manifest attributes. As spec'd, and coded here, the system will initiate an update // passing in the document url as the manifest url. That's not a good thing. AtomicString manifest = getAttribute(manifestAttr); if (manifest.isNull()) documentLoader->applicationCacheHost()->selectCacheWithoutManifest(); else documentLoader->applicationCacheHost()->selectCacheWithManifest(document()->completeURL(manifest)); }
Response InspectorApplicationCacheAgent::getApplicationCacheForFrame( const String& frameId, std::unique_ptr<protocol::ApplicationCache::ApplicationCache>* applicationCache) { DocumentLoader* documentLoader = nullptr; Response response = assertFrameWithDocumentLoader(frameId, documentLoader); if (!response.isSuccess()) return response; ApplicationCacheHost* host = documentLoader->applicationCacheHost(); ApplicationCacheHost::CacheInfo info = host->applicationCacheInfo(); ApplicationCacheHost::ResourceInfoList resources; host->fillResourceList(&resources); *applicationCache = buildObjectForApplicationCache(resources, info); return Response::OK(); }
void InspectorApplicationCacheAgent::getFramesWithManifests(ErrorString*, RefPtr<TypeBuilder::Array<TypeBuilder::ApplicationCache::FrameWithManifest> >& result) { result = TypeBuilder::Array<TypeBuilder::ApplicationCache::FrameWithManifest>::create(); for (LocalFrame* frame : *m_inspectedFrames) { DocumentLoader* documentLoader = frame->loader().documentLoader(); if (!documentLoader) return; ApplicationCacheHost* host = documentLoader->applicationCacheHost(); ApplicationCacheHost::CacheInfo info = host->applicationCacheInfo(); String manifestURL = info.m_manifest.string(); if (!manifestURL.isEmpty()) { RefPtr<TypeBuilder::ApplicationCache::FrameWithManifest> value = TypeBuilder::ApplicationCache::FrameWithManifest::create() .setFrameId(IdentifiersFactory::frameId(frame)) .setManifestURL(manifestURL) .setStatus(static_cast<int>(host->status())); result->addItem(value); } } }
void InspectorApplicationCacheAgent::getFramesWithManifests(ErrorString*, RefPtr<TypeBuilder::Array<TypeBuilder::ApplicationCache::FrameWithManifest>>& result) { result = TypeBuilder::Array<TypeBuilder::ApplicationCache::FrameWithManifest>::create(); Frame* mainFrame = m_pageAgent->mainFrame(); for (Frame* frame = mainFrame; frame; frame = frame->tree().traverseNext(mainFrame)) { DocumentLoader* documentLoader = frame->loader().documentLoader(); if (!documentLoader) continue; ApplicationCacheHost* host = documentLoader->applicationCacheHost(); ApplicationCacheHost::CacheInfo info = host->applicationCacheInfo(); String manifestURL = info.m_manifest.string(); if (!manifestURL.isEmpty()) { RefPtr<TypeBuilder::ApplicationCache::FrameWithManifest> value = TypeBuilder::ApplicationCache::FrameWithManifest::create() .setFrameId(m_pageAgent->frameId(frame)) .setManifestURL(manifestURL) .setStatus(static_cast<int>(host->status())); result->addItem(value); } } }
void ApplicationCacheGroup::selectCache(Frame* frame, const URL& passedManifestURL) { ASSERT(frame && frame->page()); if (!frame->settings().offlineWebApplicationCacheEnabled()) return; DocumentLoader* documentLoader = frame->loader().documentLoader(); ASSERT(!documentLoader->applicationCacheHost()->applicationCache()); if (passedManifestURL.isNull()) { selectCacheWithoutManifestURL(frame); return; } // Don't access anything on disk if private browsing is enabled. if (frame->page()->usesEphemeralSession() || !frame->document()->securityOrigin()->canAccessApplicationCache(frame->tree().top().document()->securityOrigin())) { postListenerTask(ApplicationCacheHost::CHECKING_EVENT, documentLoader); postListenerTask(ApplicationCacheHost::ERROR_EVENT, documentLoader); return; } URL manifestURL(passedManifestURL); if (manifestURL.hasFragmentIdentifier()) manifestURL.removeFragmentIdentifier(); ApplicationCache* mainResourceCache = documentLoader->applicationCacheHost()->mainResourceApplicationCache(); if (mainResourceCache) { if (manifestURL == mainResourceCache->group()->m_manifestURL) { // The cache may have gotten obsoleted after we've loaded from it, but before we parsed the document and saw cache manifest. if (mainResourceCache->group()->isObsolete()) return; mainResourceCache->group()->associateDocumentLoaderWithCache(documentLoader, mainResourceCache); mainResourceCache->group()->update(frame, ApplicationCacheUpdateWithBrowsingContext); } else { // The main resource was loaded from cache, so the cache must have an entry for it. Mark it as foreign. URL resourceURL(documentLoader->responseURL()); if (resourceURL.hasFragmentIdentifier()) resourceURL.removeFragmentIdentifier(); ApplicationCacheResource* resource = mainResourceCache->resourceForURL(resourceURL); bool inStorage = resource->storageID(); resource->addType(ApplicationCacheResource::Foreign); if (inStorage) frame->page()->applicationCacheStorage().storeUpdatedType(resource, mainResourceCache); // Restart the current navigation from the top of the navigation algorithm, undoing any changes that were made // as part of the initial load. // The navigation will not result in the same resource being loaded, because "foreign" entries are never picked during navigation. frame->navigationScheduler().scheduleLocationChange(frame->document(), frame->document()->securityOrigin(), documentLoader->url(), frame->loader().referrer()); } return; } // The resource was loaded from the network, check if it is a HTTP/HTTPS GET. const ResourceRequest& request = frame->loader().activeDocumentLoader()->request(); if (!ApplicationCache::requestIsHTTPOrHTTPSGet(request)) return; // Check that the resource URL has the same scheme/host/port as the manifest URL. if (!protocolHostAndPortAreEqual(manifestURL, request.url())) return; ApplicationCacheGroup* group = frame->page()->applicationCacheStorage().findOrCreateCacheGroup(manifestURL); documentLoader->applicationCacheHost()->setCandidateApplicationCacheGroup(group); group->m_pendingMasterResourceLoaders.add(documentLoader); group->m_downloadingPendingMasterResourceLoadersCount++; ASSERT(!group->m_cacheBeingUpdated || group->m_updateStatus != Idle); group->update(frame, ApplicationCacheUpdateWithBrowsingContext); }
static bool canCacheFrame(Frame& frame, DiagnosticLoggingClient& diagnosticLoggingClient, unsigned indentLevel) { PCLOG("+---"); FrameLoader& frameLoader = frame.loader(); // Prevent page caching if a subframe is still in provisional load stage. // We only do this check for subframes because the main frame is reused when navigating to a new page. if (!frame.isMainFrame() && frameLoader.state() == FrameStateProvisional) { PCLOG(" -Frame is in provisional load stage"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::provisionalLoadKey()); return false; } DocumentLoader* documentLoader = frameLoader.documentLoader(); if (!documentLoader) { PCLOG(" -There is no DocumentLoader object"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::noDocumentLoaderKey()); return false; } URL currentURL = documentLoader->url(); URL newURL = frameLoader.provisionalDocumentLoader() ? frameLoader.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:"); bool isCacheable = true; if (!documentLoader->mainDocumentError().isNull()) { PCLOG(" -Main document has an error"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::mainDocumentErrorKey()); if (documentLoader->mainDocumentError().isCancellation() && documentLoader->subresourceLoadersArePageCacheAcceptable()) PCLOG(" -But, it was a cancellation and all loaders during the cancelation were loading images or XHR."); else isCacheable = false; } // Do not cache error pages (these can be recognized as pages with substitute data or unreachable URLs). if (documentLoader->substituteData().isValid() && !documentLoader->substituteData().failingURL().isEmpty()) { PCLOG(" -Frame is an error page"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::isErrorPageKey()); isCacheable = false; } if (frameLoader.subframeLoader().containsPlugins() && !frame.page()->settings().pageCacheSupportsPlugins()) { PCLOG(" -Frame contains plugins"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::hasPluginsKey()); isCacheable = false; } if (frame.isMainFrame() && frame.document() && frame.document()->url().protocolIs("https") && documentLoader->response().cacheControlContainsNoStore()) { PCLOG(" -Frame is HTTPS, and cache control prohibits storing"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::httpsNoStoreKey()); isCacheable = false; } if (frame.isMainFrame() && !frameLoader.history().currentItem()) { PCLOG(" -Main frame has no current history item"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::noCurrentHistoryItemKey()); isCacheable = false; } if (frameLoader.quickRedirectComing()) { PCLOG(" -Quick redirect is coming"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::quirkRedirectComingKey()); isCacheable = false; } if (documentLoader->isLoading()) { PCLOG(" -DocumentLoader is still loading"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::isLoadingKey()); isCacheable = false; } if (documentLoader->isStopping()) { PCLOG(" -DocumentLoader is in the middle of stopping"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::documentLoaderStoppingKey()); isCacheable = false; } Vector<ActiveDOMObject*> unsuspendableObjects; if (frame.document() && !frame.document()->canSuspendActiveDOMObjectsForDocumentSuspension(&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()); isCacheable = false; } // FIXME: We should investigating caching frames that have an associated // application cache. <rdar://problem/5917899> tracks that work. if (!documentLoader->applicationCacheHost()->canCacheInPageCache()) { PCLOG(" -The DocumentLoader uses an application cache"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::applicationCacheKey()); isCacheable = false; } if (!frameLoader.client().canCachePage()) { PCLOG(" -The client says this frame cannot be cached"); logPageCacheFailureDiagnosticMessage(diagnosticLoggingClient, DiagnosticLoggingKeys::deniedByClientKey()); isCacheable = false; } for (Frame* child = frame.tree().firstChild(); child; child = child->tree().nextSibling()) { if (!canCacheFrame(*child, diagnosticLoggingClient, indentLevel + 1)) isCacheable = false; } PCLOG(isCacheable ? " Frame CAN be cached" : " Frame CANNOT be cached"); PCLOG("+---"); return isCacheable; }