/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ /* vim: set ts=8 sts=2 et sw=2 tw=80: */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
/* container for a document and its presentation */
/** editor Implementation of the FocusListener interface */ class nsDocViewerFocusListener final : public nsIDOMEventListener { public: explicit nsDocViewerFocusListener(nsDocumentViewer* aDocViewer)
: mDocViewer(aDocViewer) {}
/** * Mutation observer for use until we hand ourselves over to our SHEntry.
*/ class BFCachePreventionObserver final : public nsStubMutationObserver { public: explicit BFCachePreventionObserver(Document* aDocument)
: mDocument(aDocument) {}
void BFCachePreventionObserver::Disconnect() { if (mDocument) {
mDocument->RemoveMutationObserver(this); // It will no longer tell us when it goes away, so make sure we're // not holding a dangling ref.
mDocument = nullptr;
}
}
void BFCachePreventionObserver::MutationHappened() {
MOZ_ASSERT(
mDocument, "How can we not have a document but be getting notified for mutations?");
mDocument->DisallowBFCaching();
Disconnect();
}
} // namespace viewer_detail
using viewer_detail::BFCachePreventionObserver;
//------------------------------------------------------------- class nsDocumentViewer final : public nsIDocumentViewer, public nsIDocumentViewerEdit, public nsIDocumentViewerPrint #ifdef NS_PRINTING
, public nsIWebBrowserPrint #endif
private: /** * Creates a view manager, root view, and widget for the root view, setting * mViewManager and mWindow. * @param aSize the initial size in appunits * @param aContainerView the container view to hook our root view up * to as a child, or null if this will be the root view manager
*/
nsresult MakeWindow(const nsSize& aSize, nsView* aContainerView);
/** * If aDoCreation is true, this creates the device context, creates a * prescontext if necessary, and calls MakeWindow. * * If aForceSetNewDocument is false, then SetNewDocument won't be * called if the window's current document is already mDocument.
*/
nsresult InitInternal(nsIWidget* aParentWidget, nsISupports* aState,
mozilla::dom::WindowGlobalChild* aActor, const LayoutDeviceIntRect& aBounds, bool aDoCreation, bool aNeedMakeCX = true, bool aForceSetNewDocument = true); /** * @param aDoInitialReflow set to true if you want to kick off the initial * reflow
*/
MOZ_CAN_RUN_SCRIPT_BOUNDARY
nsresult InitPresentationStuff(bool aDoInitialReflow);
// Whether we should attach to the top level widget. This is true if we // are sharing/recycling a single base widget and not creating multiple // child widgets. bool ShouldAttachToTopLevel();
protected: // Returns the current viewmanager. Might be null.
nsViewManager* GetViewManager();
void DetachFromTopLevelWidget();
// IMPORTANT: The ownership implicit in the following member // variables has been explicitly checked and set using nsCOMPtr // for owning pointers and raw COM interface pointers for weak // (ie, non owning) references. If you add any members to this // class, please make the ownership explicit (pinkerton, scc).
WeakPtr<nsDocShell> mContainer; // it owns me!
RefPtr<nsDeviceContext> mDeviceContext; // We create and own this baby
// the following six items are explicitly in this order // so they will be destroyed in the reverse order (pinkerton, scc)
nsCOMPtr<Document> mDocument;
nsCOMPtr<nsIWidget> mWindow; // may be null
RefPtr<nsViewManager> mViewManager;
RefPtr<nsPresContext> mPresContext;
RefPtr<PresShell> mPresShell;
nsCOMPtr<nsIDocumentViewer> mPreviousViewer;
nsCOMPtr<nsISHEntry> mSHEntry; // Observer that will prevent bfcaching if it gets notified. This // is non-null precisely when mSHEntry is non-null.
RefPtr<BFCachePreventionObserver> mBFCachePreventionObserver;
nsIWidget* mParentWidget; // purposely won't be ref counted. May be null bool mAttachedToParent; // view is attached to the parent widget
unsigned mStopped : 1; unsigned mLoaded : 1; unsigned mDeferredWindowClose : 1; // document management data // these items are specific to markup documents (html and xml) // may consider splitting these out into a subclass unsigned mIsSticky : 1; unsigned mInPermitUnload : 1; unsigned mInPermitUnloadPrompt : 1;
MOZ_RELEASE_ASSERT(mDestroyBlockedCount == 0);
NS_ASSERTION(!mPresShell && !mPresContext, "User did not call nsIDocumentViewer::Destroy"); if (mPresShell || mPresContext) { // Make sure we don't hand out a reference to the content viewer to // the SHEntry!
mSHEntry = nullptr;
Destroy();
}
if (mSelectionListener) {
mSelectionListener->Disconnect();
}
RemoveFocusListener();
// XXX(?) Revoke pending invalidate events
}
/* * This method is called by the Document Loader once a document has * been created for a particular data stream... The content viewer * must cache this document for later use when Init(...) is called. * * This method is also called when an out of band document.write() happens. * In that case, the document passed in is the same as the previous document.
*/ /* virtual */ void nsDocumentViewer::LoadStart(Document* aDocument) {
MOZ_ASSERT(aDocument);
nsresult nsDocumentViewer::InitPresentationStuff(bool aDoInitialReflow) { // We assert this because initializing the pres shell could otherwise cause // re-entrancy into nsDocumentViewer methods, which might cause a different // pres shell to be created. Callers of InitPresentationStuff should ensure // the call is appropriately bounded by an nsAutoScriptBlocker to decide // when it is safe for these re-entrant calls to be made.
MOZ_ASSERT(!nsContentUtils::IsSafeToRunScript(), "InitPresentationStuff must only be called when scripts are " "blocked");
#ifdef NS_PRINTING // When getting printed, either for print or print preview, the print job // takes care of setting up the presentation of the document. if (mPrintJob) { return NS_OK;
} #endif
NS_ASSERTION(!mPresShell, "Someone should have destroyed the presshell!");
// Now make the shell for the document
nsCOMPtr<Document> doc = mDocument;
RefPtr<nsPresContext> presContext = mPresContext;
RefPtr<nsViewManager> viewManager = mViewManager;
mPresShell = doc->CreatePresShell(presContext, viewManager); if (!mPresShell) { return NS_ERROR_FAILURE;
}
if (aDoInitialReflow) { // Since Initialize() will create frames for *all* items // that are currently in the document tree, we need to flush // any pending notifications to prevent the content sink from // duplicating layout frames for content it has added to the tree // but hasn't notified the document about. (Bug 154018) // // Note that we are flushing before we add mPresShell as an observer // to avoid bogus notifications.
mDocument->FlushPendingNotifications(FlushType::ContentAndNotify);
}
mViewManager->SetWindowDimensions(size.width, size.height);
mPresContext->SetInitialVisibleArea(nsRect(nsPoint(), size)); // We rely on the default zoom not being initialized until here.
mPresContext->RecomputeBrowsingContextDependentData();
}
if (mWindow && mDocument->IsTopLevelContentDocument()) { // Set initial safe area insets
LayoutDeviceIntMargin windowSafeAreaInsets;
LayoutDeviceIntRect windowRect = mWindow->GetScreenBounds(); if (nsCOMPtr<nsIScreen> screen = mWindow->GetWidgetScreen()) {
windowSafeAreaInsets = nsContentUtils::GetWindowSafeAreaInsets(
screen, mWindow->GetSafeAreaInsets(), windowRect);
}
mPresContext->SetSafeAreaInsets(windowSafeAreaInsets);
}
// now register ourselves as a selection listener, so that we get // called when the selection changes in the window if (!mSelectionListener) {
mSelectionListener = new nsDocViewerSelectionListener(this);
}
RefPtr<mozilla::dom::Selection> selection = GetDocumentSelection(); if (!selection) { return NS_ERROR_FAILURE;
}
static already_AddRefed<nsPresContext> CreatePresContext(
Document* aDocument, nsPresContext::nsPresContextType aType,
nsView* aContainerView) {
RefPtr<nsPresContext> result = aContainerView
? new nsPresContext(aDocument, aType)
: new nsRootPresContext(aDocument, aType);
return result.forget();
}
//----------------------------------------------- // This method can be used to initial the "presentation" // The aDoCreation indicates whether it should create // all the new objects or just initialize the existing ones
nsresult nsDocumentViewer::InitInternal(
nsIWidget* aParentWidget, nsISupports* aState, WindowGlobalChild* aActor, const LayoutDeviceIntRect& aBounds, bool aDoCreation, bool aNeedMakeCX /*= true*/, bool aForceSetNewDocument /* = true*/) { // We don't want any scripts to run here. That can cause flushing, // which can cause reentry into initialization of this document viewer, // which would be disastrous.
nsAutoScriptBlocker blockScripts;
mParentWidget = aParentWidget; // not ref counted
mBounds = aBounds;
// XXXbz this is a nasty hack to do with the fact that we create // presentations both in Init() and in Show()... Ideally we would only do // it in one place (Show()) and require that callers call init(), open(), // show() in that order or something. if (!mPresContext &&
(aParentWidget || containerView || mDocument->IsBeingUsedAsImage() ||
(mDocument->GetDisplayDocument() &&
mDocument->GetDisplayDocument()->GetPresShell()))) { // Create presentation context if (mIsPageMode) { // Presentation context already created in SetPageModeForTesting which // is calling this method
} else {
mPresContext = CreatePresContext(
mDocument, nsPresContext::eContext_Galley, containerView);
}
NS_ENSURE_TRUE(mPresContext, NS_ERROR_OUT_OF_MEMORY);
#ifdefined(NS_PRINTING) && defined(NS_PRINT_PREVIEW)
makeCX = !GetIsPrintPreview() &&
aNeedMakeCX; // needs to be true except when we are already in // PP or we are enabling/disabling paginated mode. #else
makeCX = true; #endif
}
if (mPresContext) { // Create the ViewManager and Root View...
// We must do this before we tell the script global object about // this new document since doing that will cause us to re-enter // into nsSubDocumentFrame code through reflows caused by // FlushPendingNotifications() calls down the road...
#ifdef NS_PRINT_PREVIEW if (mIsPageMode) { // I'm leaving this in a broken state for the moment; we should // be measuring/scaling with the print device context, not the // screen device context, but this is good enough to allow // printing reftests to work. double pageWidth = 0, pageHeight = 0;
mPresContext->GetPrintSettings()->GetEffectivePageSize(&pageWidth,
&pageHeight);
mPresContext->SetPageSize(
nsSize(mPresContext->CSSTwipsToAppUnits(NSToIntFloor(pageWidth)),
mPresContext->CSSTwipsToAppUnits(NSToIntFloor(pageHeight))));
mPresContext->SetIsRootPaginatedDocument(true);
mPresContext->SetPageScale(1.0f);
} #endif
} else { // Avoid leaking the old viewer. if (mPreviousViewer) {
mPreviousViewer->Destroy();
mPreviousViewer = nullptr;
}
}
}
nsCOMPtr<nsIInterfaceRequestor> requestor(mContainer); if (requestor) { // Set script-context-owner in the document
if (window) {
nsCOMPtr<Document> curDoc = window->GetExtantDoc(); if (aForceSetNewDocument || curDoc != mDocument) {
rv = window->SetNewDocument(mDocument, aState, false, aActor); if (NS_FAILED(rv)) {
Destroy(); return rv;
}
}
}
}
if (aDoCreation && mPresContext) { // The ViewManager and Root View was created above (in // MakeWindow())...
rv = InitPresentationStuff(!makeCX);
}
return rv;
}
void nsDocumentViewer::SetNavigationTiming(nsDOMNavigationTiming* timing) {
NS_ASSERTION(mDocument, "Must have a document to set navigation timing."); if (mDocument) {
mDocument->SetNavigationTiming(timing);
}
}
// // LoadComplete(aStatus) // // aStatus - The status returned from loading the document. // // This method is called by the container when the document has been // completely loaded. //
NS_IMETHODIMP
nsDocumentViewer::LoadComplete(nsresult aStatus) { /* We need to protect ourself against auto-destruction in case the window is closed while processing the OnLoad event. See bug http://bugzilla.mozilla.org/show_bug.cgi?id=78445 for more explanation.
*/
RefPtr<nsDocumentViewer> kungFuDeathGrip(this);
// Flush out layout so it's up-to-date by the time onload is called. // Note that this could destroy the window, so do this before // checking for our mDocument and its window. if (mPresShell && !mStopped) { // Hold strong ref because this could conceivably run script
RefPtr<PresShell> presShell = mPresShell;
presShell->FlushPendingNotifications(FlushType::Layout);
}
// First, get the window from the document...
nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
mLoaded = true;
// Now, fire either an OnLoad or OnError event to the document... bool restoring = false; // XXXbz imagelib kills off the document load for a full-page image with // NS_ERROR_PARSED_DATA_CACHED if it's in the cache. So we want to treat // that one as a success code; otherwise whether we fire onload for the image // will depend on whether it's cached! if (window &&
(NS_SUCCEEDED(aStatus) || aStatus == NS_ERROR_PARSED_DATA_CACHED)) { // If this code changes, the code in nsDocLoader::DocLoaderIsEmpty // that fires load events for document.open() cases might need to // be updated too.
nsEventStatus status = nsEventStatus_eIgnore;
WidgetEvent event(true, eLoad);
event.mFlags.mBubbles = false;
event.mFlags.mCancelable = false; // XXX Dispatching to |window|, but using |document| as the target.
event.mTarget = mDocument;
// If the document presentation is being restored, we don't want to fire // onload to the document content since that would likely confuse scripts // on the page.
// Unfortunately, docShell->GetRestoringDocument() might no longer be set // correctly. In particular, it can be false by now if someone took it upon // themselves to block onload from inside restoration and unblock it later. // But we can detect the restoring case very simply: by whether our // document's readyState is COMPLETE.
restoring =
(mDocument->GetReadyStateEnum() == Document::READYSTATE_COMPLETE); if (!restoring) {
NS_ASSERTION(
mDocument->GetReadyStateEnum() == Document::READYSTATE_INTERACTIVE || // test_stricttransportsecurity.html has old-style // docshell-generated about:blank docs reach this code!
(mDocument->GetReadyStateEnum() ==
Document::READYSTATE_UNINITIALIZED &&
NS_IsAboutBlank(mDocument->GetDocumentURI())), "Bad readystate"); #ifdef DEBUG bool docShellThinksWeAreRestoring;
docShell->GetRestoringDocument(&docShellThinksWeAreRestoring);
MOZ_ASSERT(!docShellThinksWeAreRestoring, "How can docshell think we are restoring if we don't have a " "READYSTATE_COMPLETE document?"); #endif// DEBUG
nsCOMPtr<Document> d = mDocument;
mDocument->SetReadyStateInternal(Document::READYSTATE_COMPLETE);
RefPtr<nsDOMNavigationTiming> timing(d->GetNavigationTiming()); if (timing) {
timing->NotifyLoadEventStart();
}
// Dispatch observer notification to notify observers document load is // complete.
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) {
nsIPrincipal* principal = d->NodePrincipal();
os->NotifyObservers(ToSupports(d),
principal->IsSystemPrincipal()
? "chrome-document-loaded"
: "content-document-loaded",
nullptr);
}
if (innerWindow) {
innerWindow->QueuePerformanceNavigationTiming();
}
}
} else { // XXX: Should fire error event to the document...
// If our load was explicitly aborted, then we want to set our // readyState to COMPLETE, and fire a readystatechange event. if (aStatus == NS_BINDING_ABORTED && mDocument) {
mDocument->NotifyAbortedLoad();
}
}
// Notify the document that it has been shown (regardless of whether // it was just loaded). Note: mDocument may be null now if the above // firing of onload caused the document to unload. Or, mDocument may not be // the "current active" document, if the above firing of onload caused our // docshell to navigate away. NOTE: In this latter scenario, it's likely that // we fired pagehide (when navigating away) without ever having fired // pageshow, and that's pretty broken... Fortunately, this should be rare. // (It requires us to spin the event loop in onload handler, e.g. via sync // XHR, in order for the navigation-away to happen before onload completes.) // We skip firing pageshow if we're currently handling unload, or if loading // was explicitly aborted. if (mDocument && mDocument->IsCurrentActiveDocument() &&
aStatus != NS_BINDING_ABORTED) { // Re-get window, since it might have changed during above firing of onload
window = mDocument->GetWindow(); if (window) {
nsIDocShell* docShell = window->GetDocShell(); bool isInUnload; if (docShell && NS_SUCCEEDED(docShell->GetIsInUnload(&isInUnload)) &&
!isInUnload) {
mDocument->OnPageShow(restoring, nullptr);
}
}
}
if (!mStopped) { if (mDocument) { // This is the final attempt to scroll to an anchor / text directive. // This is the last iteration of the algorithm described in the spec for // trying to scroll to a fragment. // https://html.spec.whatwg.org/#try-to-scroll-to-the-fragment
nsCOMPtr<Document> document = mDocument;
document->ScrollToRef();
}
// Now that the document has loaded, we can tell the presshell // to unsuppress painting. if (mPresShell) {
RefPtr<PresShell> presShell = mPresShell;
presShell->UnsuppressPainting(); // mPresShell could have been removed now, see bug 378682/421432 if (mPresShell) {
mPresShell->LoadComplete();
}
}
}
// https://wicg.github.io/scroll-to-text-fragment/#invoking-text-directives // Monkeypatching HTML § 7.4.6.3 Scrolling to a fragment: // 2.1 If the user agent has reason to believe the user is no longer // interested in scrolling to the fragment, then: // 2.1.1 Set pending text directives to null. // // Gecko's implementation differs from the spec (ie., it implements its // intention but doesn't follow step by step), therefore the mentioned steps // are not applied in the same manner. // However, this should be the right place to do this. if (mDocument) {
mDocument->FragmentDirective()->ClearUninvokedDirectives();
} if (mDocument && !restoring) {
mDocument->LoadEventFired();
}
// It's probably a good idea to GC soon since we have finished loading.
nsJSContext::PokeGC(
JS::GCReason::LOAD_END,
mDocument ? mDocument->GetWrapperPreserveColor() : nullptr);
#ifdef NS_PRINTING // Check to see if someone tried to print during the load if (window) { auto* outerWin = nsGlobalWindowOuter::Cast(window);
outerWin->StopDelayingPrintingUntilAfterLoad(); if (outerWin->DelayedPrintUntilAfterLoad()) { // We call into the inner because it ensures there's an active document // and such, and it also waits until the whole thing completes, which is // nice because it allows us to close if needed right here. if (RefPtr inner =
nsGlobalWindowInner::Cast(window->GetCurrentInnerWindow())) {
inner->Print(IgnoreErrors());
} if (outerWin->DelayedCloseForPrinting()) {
outerWin->Close();
}
} else {
MOZ_ASSERT(!outerWin->DelayedCloseForPrinting());
}
} #endif
NS_IMETHODIMP
nsDocumentViewer::PermitUnload(PermitUnloadAction aAction, bool* aPermitUnload) { // We're going to be running JS and nested event loops, which could cause our // DocShell to be destroyed. Make sure we stay alive until the end of the // function.
RefPtr<nsDocumentViewer> kungFuDeathGrip(this);
if (StaticPrefs::dom_disable_beforeunload()) {
aAction = eDontPromptAndUnload;
}
*aPermitUnload = true;
NS_ENSURE_STATE(mContainer);
RefPtr<BrowsingContext> bc = mContainer->GetBrowsingContext(); if (!bc) { return NS_OK;
}
// Per spec, we need to increase the ignore-opens-during-unload counter while // dispatching the "beforeunload" event on both the document we're currently // dispatching the event to and the document that we explicitly asked to // unload.
IgnoreOpensDuringUnload ignoreOpens(mDocument);
if (!foundOOPListener) { if (!foundBlocker) { return NS_OK;
} if (aAction != ePrompt) {
*aPermitUnload = aAction == eDontPromptAndUnload; return NS_OK;
}
}
// NB: we nullcheck mDocument because it might now be dead as a result of // the event being dispatched.
RefPtr<WindowGlobalChild> wgc(mDocument ? mDocument->GetWindowGlobalChild()
: nullptr); if (!wgc) { return NS_OK;
}
bool done = false;
wgc->SendCheckPermitUnload(
foundBlocker, aAction,
[&](bool aPermit) {
done = true;
*aPermitUnload = aPermit;
},
[&](auto) { // If the prompt aborted, we tell our consumer that it is not allowed // to unload the page. One reason that prompts abort is that the user // performed some action that caused the page to unload while our prompt // was active. In those cases we don't want our consumer to also unload // the page. // // XXX: Are there other cases where prompts can abort? Is it ok to // prevent unloading the page in those cases?
done = true;
*aPermitUnload = false;
});
// First, get the script global object from the document...
RefPtr<nsGlobalWindowOuter> window =
nsGlobalWindowOuter::Cast(mDocument->GetWindow()); if (!window) { // This is odd, but not fatal
NS_WARNING("window not set for document!"); return eAllowNavigation;
}
NS_ASSERTION(nsContentUtils::IsSafeToRunScript(), "This is unsafe");
// https://html.spec.whatwg.org/multipage/browsing-the-web.html#prompt-to-unload-a-document // Create an RAII object on mDocument that will increment the // should-ignore-opens-during-unload counter on initialization // and decrement it again when it goes out of score (regardless // of how we exit this function).
IgnoreOpensDuringUnload ignoreOpens(mDocument);
// Now, fire an BeforeUnload event to the document and see if it's ok // to unload...
nsPresContext* presContext = mDocument->GetPresContext(); auto event = MakeRefPtr<BeforeUnloadEvent>(mDocument, presContext, nullptr);
event->InitEvent(u"beforeunload"_ns, false, true);
// Dispatching to |window|, but using |document| as the target.
event->SetTarget(mDocument);
event->SetTrusted(true);
// In evil cases we might be destroyed while handling the // onbeforeunload event, don't let that happen. (see also bug#331040)
RefPtr<nsDocumentViewer> kungFuDeathGrip(this);
{ // Never permit popups from the beforeunload handler, no matter // how we get here.
AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
RefPtr<BrowsingContext> bc = mContainer->GetBrowsingContext();
NS_ASSERTION(bc, "should have a browsing context in document viewer");
// Never permit dialogs from the beforeunload handler
nsGlobalWindowOuter::TemporarilyDisableDialogs disableDialogs(bc);
// NB: we nullcheck mDocument because it might now be dead as a result of // the event being dispatched. if (window->AreDialogsEnabled() && mDocument &&
!(mDocument->GetSandboxFlags() & SANDBOXED_MODALS) &&
(!StaticPrefs::dom_require_user_interaction_for_beforeunload() ||
mDocument->UserHasInteracted()) &&
(event->WidgetEventPtr()->DefaultPrevented() || !text.IsEmpty())) { return eRequestBlockNavigation;
} return eAllowNavigation;
}
if (aIsUnload) { // Poke the GC. The window might be collectable garbage now.
nsJSContext::PokeGC(JS::GCReason::PAGE_HIDE,
mDocument->GetWrapperPreserveColor(),
TimeDuration::FromMilliseconds(
StaticPrefs::javascript_options_gc_delay() * 2));
}
mDocument->OnPageHide(!aIsUnload, nullptr);
// inform the window so that the focus state is reset.
NS_ENSURE_STATE(mDocument);
nsPIDOMWindowOuter* window = mDocument->GetWindow(); if (window) {
window->PageHidden(!aIsUnload);
}
if (aIsUnload) { // if Destroy() was called during OnPageHide(), mDocument is nullptr.
NS_ENSURE_STATE(mDocument);
// First, get the window from the document...
RefPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow();
if (!window) { // Fail if no window is available...
NS_WARNING("window not set for document!"); return NS_ERROR_NULL_POINTER;
}
// Now, fire an Unload event to the document...
nsEventStatus status = nsEventStatus_eIgnore;
WidgetEvent event(true, eUnload);
event.mFlags.mBubbles = false; // XXX Dispatching to |window|, but using |document| as the target.
event.mTarget = mDocument;
// Never permit popups from the unload handler, no matter how we get // here.
AutoPopupStatePusher popupStatePusher(PopupBlocker::openAbused, true);
RefPtr<nsPresContext> presContext = mPresContext; // MOZ_KnownLive due to bug 1506441
EventDispatcher::Dispatch(MOZ_KnownLive(nsGlobalWindowOuter::Cast(window)),
presContext, &event, nullptr, &status);
}
// look for open menupopups and close them after the unload event, in case // the unload event listeners open any new popups
nsContentUtils::HidePopupsInDocument(mDocument);
return NS_OK;
}
staticvoid AttachContainerRecurse(nsIDocShell* aShell) {
nsCOMPtr<nsIDocumentViewer> viewer;
aShell->GetDocViewer(getter_AddRefs(viewer)); if (viewer) {
viewer->SetIsHidden(false);
Document* doc = viewer->GetDocument(); if (doc) {
doc->SetContainer(static_cast<nsDocShell*>(aShell));
} if (PresShell* presShell = viewer->GetPresShell()) {
presShell->SetForwardingContainer(WeakPtr<nsDocShell>());
}
}
// Now recurse through the children
int32_t childCount;
aShell->GetInProcessChildCount(&childCount); for (int32_t i = 0; i < childCount; ++i) {
nsCOMPtr<nsIDocShellTreeItem> childItem;
aShell->GetInProcessChildAt(i, getter_AddRefs(childItem));
nsCOMPtr<nsIDocShell> shell = do_QueryInterface(childItem);
AttachContainerRecurse(shell);
}
}
// XXX re-enable image animations once that works correctly
PrepareToStartLoad();
// When loading a page from the bfcache with puppet widgets, we do the // widget attachment here (it is otherwise done in MakeWindow, which is // called for non-bfcache pages in the history, but not bfcache pages). // Attachment is necessary, since we get detached when another page // is browsed to. That is, if we are one page A, then when we go to // page B, we detach. So page A's view has no widget. If we then go // back to it, and it is in the bfcache, we will use that view, which // doesn't have a widget. The attach call here will properly attach us. if (nsIWidget::UsePuppetWidgets() && mPresContext &&
ShouldAttachToTopLevel()) { // If the old view is already attached to our parent, detach
DetachFromTopLevelWidget();
nsViewManager* vm = GetViewManager();
MOZ_ASSERT(vm, "no view manager");
nsView* v = vm->GetRootView();
MOZ_ASSERT(v, "no root view");
MOZ_ASSERT(mParentWidget, "no mParentWidget to set");
v->AttachToTopLevelWidget(mParentWidget);
mAttachedToParent = true;
}
return NS_OK;
}
NS_IMETHODIMP
nsDocumentViewer::Close(nsISHEntry* aSHEntry) { // All callers are supposed to call close to break circular // references. If we do this stuff in the destructor, the // destructor might never be called (especially if we're being // used from JS.
mSHEntry = aSHEntry;
// Close is also needed to disable scripts during paint suppression, // since we transfer the existing global object to the new document // that is loaded. In the future, the global object may become a proxy // for an object that can be switched in and out so that we don't need // to disable scripts during paint suppression.
if (!mDocument) { return NS_OK;
}
if (mSHEntry) { if (mBFCachePreventionObserver) {
mBFCachePreventionObserver->Disconnect();
}
mBFCachePreventionObserver = new BFCachePreventionObserver(mDocument);
mDocument->AddMutationObserver(mBFCachePreventionObserver);
}
#ifdef NS_PRINTING // A Close was called while we were printing // so don't clear the ScriptGlobalObject // or clear the mDocument below if (mPrintJob && !mClosingWhilePrinting) {
mClosingWhilePrinting = true;
} else #endif
{ // out of band cleanup of docshell
mDocument->SetScriptGlobalObject(nullptr);
if (!mSHEntry && mDocument) {
mDocument->RemovedFromDocShell();
}
}
RemoveFocusListener(); return NS_OK;
}
staticvoid DetachContainerRecurse(nsIDocShell* aShell) { // Unhook this docshell's presentation
aShell->SynchronizeLayoutHistoryState();
nsCOMPtr<nsIDocumentViewer> viewer;
aShell->GetDocViewer(getter_AddRefs(viewer)); if (viewer) { if (Document* doc = viewer->GetDocument()) {
doc->SetContainer(nullptr);
} if (PresShell* presShell = viewer->GetPresShell()) { auto weakShell = static_cast<nsDocShell*>(aShell);
presShell->SetForwardingContainer(weakShell);
}
}
// Now recurse through the children
int32_t childCount;
aShell->GetInProcessChildCount(&childCount); for (int32_t i = 0; i < childCount; ++i) {
nsCOMPtr<nsIDocShellTreeItem> childItem;
aShell->GetInProcessChildAt(i, getter_AddRefs(childItem));
nsCOMPtr<nsIDocShell> shell = do_QueryInterface(childItem);
DetachContainerRecurse(shell);
}
}
NS_IMETHODIMP
nsDocumentViewer::Destroy() { // Don't let the document get unloaded while we are printing. // this could happen if we hit the back button during printing. // We also keep the viewer from being cached in session history, since // we require all documents there to be sanitized. if (mDestroyBlockedCount != 0) { return NS_OK;
}
#ifdef NS_PRINTING // Here is where we check to see if the document was still being prepared // for printing when it was asked to be destroy from someone externally // This usually happens if the document is unloaded while the user is in the // Print Dialog // // So we flip the bool to remember that the document is going away // and we can clean up and abort later after returning from the Print Dialog if (mPrintJob && mPrintJob->CheckBeforeDestroy()) { return NS_OK;
} #endif
// We want to make sure to disconnect mBFCachePreventionObserver before we // Sanitize() below. if (mBFCachePreventionObserver) {
mBFCachePreventionObserver->Disconnect();
mBFCachePreventionObserver = nullptr;
}
if (mSHEntry && mDocument && !mDocument->IsBFCachingAllowed()) { // Just drop the SHEntry now and pretend like we never even tried to bfcache // this viewer. This should only happen when someone calls // DisallowBFCaching() after CanSavePresentation() already ran. Ensure that // the SHEntry has no viewer and its state is synced up. We want to do this // via a stack reference, in case those calls mess with our members.
MOZ_LOG(gPageCacheLog, LogLevel::Debug,
("BFCache not allowed, dropping SHEntry"));
nsCOMPtr<nsISHEntry> shEntry = std::move(mSHEntry);
shEntry->SetDocumentViewer(nullptr);
shEntry->SyncPresentationState();
}
// If we were told to put ourselves into session history instead of destroy // the presentation, do that now. if (mSHEntry) { if (mPresShell) {
mPresShell->Freeze();
}
// Make sure the presentation isn't torn down by Hide().
mSHEntry->SetSticky(mIsSticky);
mIsSticky = true;
// Remove our root view from the view hierarchy. if (mPresShell) {
nsViewManager* vm = mPresShell->GetViewManager(); if (vm) {
nsView* rootView = vm->GetRootView();
if (rootView) {
nsView* rootViewParent = rootView->GetParent(); if (rootViewParent) {
nsView* subdocview = rootViewParent->GetParent(); if (subdocview) {
nsIFrame* f = subdocview->GetFrame(); if (f) {
nsSubDocumentFrame* s = do_QueryFrame(f); if (s) {
s->ClearDisplayItems();
}
}
}
nsViewManager* parentVM = rootViewParent->GetViewManager(); if (parentVM) {
parentVM->RemoveChild(rootView);
}
}
}
}
}
Hide();
// This is after Hide() so that the user doesn't see the inputs clear. if (mDocument) {
mDocument->Sanitize();
}
// Reverse ownership. Do this *after* calling sanitize so that sanitize // doesn't cause mutations that make the SHEntry drop the presentation
// Grab a reference to mSHEntry before calling into things like // SyncPresentationState that might mess with our members.
nsCOMPtr<nsISHEntry> shEntry =
std::move(mSHEntry); // we'll need this below
MOZ_LOG(gPageCacheLog, LogLevel::Debug,
("Storing content viewer into cache entry"));
shEntry->SetDocumentViewer(this);
// Always sync the presentation state. That way even if someone screws up // and shEntry has no window state at this point we'll be ok; we just won't // cache ourselves.
shEntry->SyncPresentationState(); // XXX Synchronize layout history state to parent once bfcache is supported // in session-history-in-parent.
// Shut down accessibility for the document before we start to tear it down. #ifdef ACCESSIBILITY if (mPresShell) {
a11y::DocAccessible* docAcc = mPresShell->GetDocAccessible(); if (docAcc) {
docAcc->Shutdown();
}
} #endif
// Break the link from the document/presentation to the docshell, so that // link traversals cannot affect the currently-loaded document. // When the presentation is restored, Open() and InitInternal() will reset // these pointers to their original values.
if (mDocument) {
mDocument->SetContainer(nullptr);
} if (mPresShell) {
mPresShell->SetForwardingContainer(mContainer);
}
// Do the same for our children. Note that we need to get the child // docshells from the SHEntry now; the docshell will have cleared them.
nsCOMPtr<nsIDocShellTreeItem> item;
int32_t itemIndex = 0; while (NS_SUCCEEDED(
shEntry->ChildShellAt(itemIndex++, getter_AddRefs(item))) &&
item) {
nsCOMPtr<nsIDocShell> shell = do_QueryInterface(item);
DetachContainerRecurse(shell);
}
return NS_OK;
}
// The document was not put in the bfcache
// Protect against pres shell destruction running scripts and re-entrantly // creating a new presentation.
nsAutoScriptBlocker scriptBlocker;
if (mPresShell) {
DestroyPresShell();
} if (mDocument) {
mDocument->Destroy();
mDocument = nullptr;
}
// All callers are supposed to call destroy to break circular // references. If we do this stuff in the destructor, the // destructor might never be called (especially if we're being // used from JS.
#ifdef NS_PRINTING if (mPrintJob) {
RefPtr<nsPrintJob> printJob = std::move(mPrintJob); # ifdef NS_PRINT_PREVIEW if (printJob->CreatedForPrintPreview()) {
printJob->FinishPrintPreview();
} # endif
printJob->Destroy();
MOZ_ASSERT(!mPrintJob, "mPrintJob shouldn't be recreated while destroying it");
} #endif
// Avoid leaking the old viewer. if (mPreviousViewer) {
mPreviousViewer->Destroy();
mPreviousViewer = nullptr;
}
NS_IMETHODIMP
nsDocumentViewer::Stop(void) {
NS_ASSERTION(mDocument, "Stop called too early or too late"); if (mDocument) {
mDocument->StopDocumentLoad();
}
mStopped = true;
if (!mLoaded && mPresShell) { // Well, we might as well paint what we have so far.
RefPtr<PresShell> presShell = mPresShell; // bug 378682
presShell->UnsuppressPainting();
}
nsresult nsDocumentViewer::SetDocument(Document* aDocument) { // Assumptions: // // 1) this document viewer has been initialized with a call to Init(). // 2) the stylesheets associated with the document have been added // to the document.
// XXX Right now, this method assumes that the layout of the current // document hasn't started yet. More cleanup will probably be // necessary to make this method work for the case when layout *has* // occurred for the current document. // That work can happen when and if it is needed.
// Set new container
aDocument->SetContainer(mContainer);
if (mDocument != aDocument) { if (aForceReuseInnerWindow) { // Transfer the navigation timing information to the new document, since // we're keeping the same inner and hence should really have the same // timing information.
aDocument->SetNavigationTiming(mDocument->GetNavigationTiming());
}
// Clear the list of old child docshells. Child docshells for the new // document will be constructed as frames are created. if (!aDocument->IsStaticDocument()) {
nsCOMPtr<nsIDocShell> node(mContainer); if (node) {
int32_t count;
node->GetInProcessChildCount(&count); for (int32_t i = 0; i < count; ++i) {
nsCOMPtr<nsIDocShellTreeItem> child;
node->GetInProcessChildAt(0, getter_AddRefs(child));
node->RemoveChild(child);
}
}
}
// Replace the old document with the new one. Do this only when // the new document really is a new document.
mDocument = aDocument;
// Set the script global object on the new document
nsCOMPtr<nsPIDOMWindowOuter> window =
mContainer ? mContainer->GetWindow() : nullptr; if (window) {
nsresult rv =
window->SetNewDocument(aDocument, nullptr, aForceReuseInnerWindow); if (NS_FAILED(rv)) {
Destroy(); return rv;
}
}
}
void nsDocumentViewer::SetPreviousViewer(nsIDocumentViewer* aViewer) { // NOTE: |Show| sets |mPreviousViewer| to null without calling this // function.
if (aViewer) {
NS_ASSERTION(!mPreviousViewer, "can't set previous viewer when there already is one");
// In a multiple chaining situation (which occurs when running a thrashing // test like i-bench or jrgm's tests with no delay), we can build up a // whole chain of viewers. In order to avoid this, we always set our // previous viewer to the MOST previous viewer in the chain, and then dump // the intermediate link from the chain. This ensures that at most only 2 // documents are alive and undestroyed at any given time (the one that is // showing and the one that is loading with painting suppressed). It's very // important that if this ever gets changed the code before the // RestorePresentation call in nsDocShell::InternalLoad be changed // accordingly. // // Make sure we hold a strong ref to prevViewer here, since we'll // tell aViewer to drop it.
nsCOMPtr<nsIDocumentViewer> prevViewer = aViewer->GetPreviousViewer(); if (prevViewer) {
aViewer->SetPreviousViewer(nullptr);
aViewer->Destroy(); return SetPreviousViewer(prevViewer);
}
}
if (mWindow && !mAttachedToParent) { // Resize the widget, but don't trigger repaint. Layout will generate // repaint requests during reflow.
mWindow->Resize(aBounds.x, aBounds.y, aBounds.width, aBounds.height, false);
} elseif (mPresContext && mViewManager) { // Ensure presContext's deviceContext is up to date, as we sometimes get // here before a resolution-change notification has been fully handled // during display configuration changes, especially when there are lots // of windows/widgets competing to handle the notifications. // (See bug 1154125.) if (mPresContext->DeviceContext()->CheckDPIChange()) {
mPresContext->UIResolutionChangedSync();
}
int32_t p2a = mPresContext->AppUnitsPerDevPixel();
nscoord width = NSIntPixelsToAppUnits(mBounds.width, p2a);
nscoord height = NSIntPixelsToAppUnits(mBounds.height, p2a);
nsView* rootView = mViewManager->GetRootView(); if (boundsChanged && rootView) {
nsRect viewDims = rootView->GetDimensions(); // If the view/frame tree and prescontext visible area already has the new // size but we did not, then it's likely that we got reflowed in response // to a call to GetContentSize. Thus there is a disconnect between the // size on the document viewer/docshell/containing widget and view // tree/frame tree/prescontext visible area). SetWindowDimensions compares // to the root view dimenstions to determine if it needs to do anything; // if they are the same as the new size it won't do anything, but we still // need to invalidate because what we want to draw to the screen has // changed. if (viewDims.width == width && viewDims.height == height) { if (nsIFrame* f = rootView->GetFrame()) {
f->InvalidateFrame();
// Forcibly refresh the viewport sizes even if the view size is not // changed since it is possible that the |mBounds| change means that // the software keyboard appeared/disappeared. In such cases we might // need to fire visual viewport events.
mPresShell->RefreshViewportSize();
}
}
}
// If there's a previous viewer, it's the one that's actually showing, // so be sure to resize it as well so it paints over the right area. // This may slow down the performance of the new page load, but resize // during load is also probably a relatively unusual condition // relating to things being hidden while something is loaded. It so // happens that Firefox does this a good bit with its infobar, and it // looks ugly if we don't do this. if (mPreviousViewer) {
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.22 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung ist noch experimentell.