/* -*- 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/. */
// RangePaintInfo is used to paint ranges to offscreen buffers struct RangePaintInfo {
nsDisplayListBuilder mBuilder;
nsDisplayList mList;
// offset of builder's reference frame to the root frame
nsPoint mRootOffset;
// Resolution at which the items are normally painted. So if we're painting // these items in a range separately from the "full display list", we may want // to paint them at this resolution. float mResolution = 1.0;
#ifdef DEBUG // Set the environment variable GECKO_VERIFY_REFLOW_FLAGS to one or // more of the following flags (comma separated) for handy debug // output. static VerifyReflowFlags gVerifyReflowFlags;
staticvoid ShowVerifyReflowFlags() {
printf("Here are the available GECKO_VERIFY_REFLOW_FLAGS:\n"); const VerifyReflowFlagData* flag = gFlags; const VerifyReflowFlagData* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS; while (flag < limit) {
printf(" %s\n", flag->name);
++flag;
}
printf("Note: GECKO_VERIFY_REFLOW_FLAGS is a comma separated list of flag\n");
printf("names (no whitespace)\n");
} #endif
//======================================================================== //======================================================================== //======================================================================== #ifdef MOZ_REFLOW_PERF class ReflowCountMgr;
staticconstchar kGrandTotalsStr[] = "Grand Totals";
// Counting Class class ReflowCounter { public: explicit ReflowCounter(ReflowCountMgr* aMgr = nullptr);
~ReflowCounter();
// The upper bound on the amount of time to spend reflowing, in // microseconds. When this bound is exceeded and reflow commands are // still queued up, a reflow event is posted. The idea is for reflow // to not hog the processor beyond the time specifed in // gMaxRCProcessingTime. This data member is initialized from the // layout.reflow.timeslice pref. #define NS_MAX_REFLOW_TIME 1000000 static int32_t gMaxRCProcessingTime = -1;
// ---------------------------------------------------------------------------- // // NOTE(emilio): It'd be nice for this to assert that our document isn't in the // bfcache, but font pref changes don't care about that, and maybe / probably // shouldn't. #ifdef DEBUG # define ASSERT_REFLOW_SCHEDULED_STATE() \
{ \ if (ObservingStyleFlushes()) { \
MOZ_ASSERT( \
mDocument->GetBFCacheEntry() || \
mPresContext->RefreshDriver()->IsStyleFlushObserver(this), \ "Unexpected state"); \
} else { \
MOZ_ASSERT(!mPresContext->RefreshDriver()->IsStyleFlushObserver(this), \ "Unexpected state"); \
} \
} #else # define ASSERT_REFLOW_SCHEDULED_STATE() /* nothing */ #endif
class nsAutoCauseReflowNotifier { public:
MOZ_CAN_RUN_SCRIPT explicit nsAutoCauseReflowNotifier(PresShell* aPresShell)
: mPresShell(aPresShell) {
mPresShell->WillCauseReflow();
}
MOZ_CAN_RUN_SCRIPT ~nsAutoCauseReflowNotifier() { // This check should not be needed. Currently the only place that seem // to need it is the code that deals with bug 337586. if (!mPresShell->mHaveShutDown) {
RefPtr<PresShell> presShell(mPresShell);
presShell->DidCauseReflow();
} else {
nsContentUtils::RemoveScriptBlocker();
}
}
PresShell* mPresShell;
};
class MOZ_STACK_CLASS nsPresShellEventCB : public EventDispatchingCallback { public: explicit nsPresShellEventCB(PresShell* aPresShell) : mPresShell(aPresShell) {}
MOZ_CAN_RUN_SCRIPT virtualvoid HandleEvent(EventChainPostVisitor& aVisitor) override { if (aVisitor.mPresContext && aVisitor.mEvent->mClass != eBasicEventClass) { if (aVisitor.mEvent->mMessage == eMouseDown ||
aVisitor.mEvent->mMessage == eMouseUp) { // Mouse-up and mouse-down events call nsIFrame::HandlePress/Release // which call GetContentOffsetsFromPoint which requires up-to-date // layout. Bring layout up-to-date now so that GetCurrentEventFrame() // below will return a real frame and we don't have to worry about // destroying it by flushing later.
MOZ_KnownLive(mPresShell)->FlushPendingNotifications(FlushType::Layout);
} elseif (aVisitor.mEvent->mMessage == eWheel &&
aVisitor.mEventStatus != nsEventStatus_eConsumeNoDefault) {
nsIFrame* frame = mPresShell->GetCurrentEventFrame(); if (frame) { // chrome (including addons) should be able to know if content // handles both D3E "wheel" event and legacy mouse scroll events. // We should dispatch legacy mouse events before dispatching the // "wheel" event into system group.
RefPtr<EventStateManager> esm =
aVisitor.mPresContext->EventStateManager();
esm->DispatchLegacyMouseScrollEvents(
frame, aVisitor.mEvent->AsWheelEvent(), &aVisitor.mEventStatus);
}
}
nsIFrame* frame = mPresShell->GetCurrentEventFrame(); if (!frame && (aVisitor.mEvent->mMessage == eMouseUp ||
aVisitor.mEvent->mMessage == eTouchEnd)) { // Redirect BUTTON_UP and TOUCH_END events to the root frame to ensure // that capturing is released.
frame = mPresShell->GetRootFrame();
} if (frame) {
frame->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent->AsGUIEvent(),
&aVisitor.mEventStatus);
}
}
}
RefPtr<PresShell> mPresShell;
};
class nsBeforeFirstPaintDispatcher : public Runnable { public: explicit nsBeforeFirstPaintDispatcher(Document* aDocument)
: mozilla::Runnable("nsBeforeFirstPaintDispatcher"),
mDocument(aDocument) {}
// Fires the "before-first-paint" event so that interested parties (right now, // the mobile browser) are aware of it.
NS_IMETHOD Run() override {
nsCOMPtr<nsIObserverService> observerService =
mozilla::services::GetObserverService(); if (observerService) {
observerService->NotifyObservers(ToSupports(mDocument), "before-first-paint", nullptr);
} return NS_OK;
}
private:
RefPtr<Document> mDocument;
};
// This is a helper class to track whether the targeted frame is destroyed after // dispatching pointer events. In that case, we need the original targeted // content so that we can dispatch the mouse events to it. class MOZ_RAII AutoPointerEventTargetUpdater final { public:
AutoPointerEventTargetUpdater(PresShell* aShell, WidgetEvent* aEvent,
nsIFrame* aFrame, nsIContent* aTargetContent,
nsIContent** aOutTargetContent) {
MOZ_ASSERT(aEvent); if (!aOutTargetContent || aEvent->mClass != ePointerEventClass) { // Make the destructor happy.
mOutTargetContent = nullptr; return;
}
MOZ_ASSERT(aShell);
MOZ_ASSERT_IF(aFrame && aFrame->GetContent(),
aShell->GetDocument() == aFrame->GetContent()->OwnerDoc());
mShell = aShell;
mWeakFrame = aFrame;
mOutTargetContent = aOutTargetContent;
mFromTouch = aEvent->AsPointerEvent()->mFromTouchEvent; // Touch event target may have no frame, e.g., removed from the DOM
MOZ_ASSERT_IF(!mFromTouch, aFrame); // The frame may be a text frame, but the event target should be an element // node. Therefore, refer aTargetContent first, then, if we have only a // frame, we should use inclusive ancestor of the content.
mOriginalPointerEventTarget =
aShell->mPointerEventTarget = [&]() -> nsIContent* {
nsIContent* const target =
aTargetContent ? aTargetContent
: (aFrame ? aFrame->GetContent() : nullptr); if (MOZ_UNLIKELY(!target)) { return nullptr;
} if (target->IsElement() ||
!IsForbiddenDispatchingToNonElementContent(aEvent->mMessage)) { return target;
} return target->GetInclusiveFlattenedTreeAncestorElement();
}();
}
~AutoPointerEventTargetUpdater() { if (!mOutTargetContent || !mShell || mWeakFrame.IsAlive()) { return;
} if (mFromTouch) { // If the source event is a touch event, the touch event target should // always be same target as preceding ePointerDown. Therefore, we should // always set it back to the original event target.
mOriginalPointerEventTarget.swap(*mOutTargetContent);
} else { // If the source event is not a touch event (must be a mouse event in // this case), the event should be fired on the closest inclusive ancestor // of the pointer event target which is still connected. The mutations // are tracked by PresShell::ContentRemoved. Therefore, we should set it.
mShell->mPointerEventTarget.swap(*mOutTargetContent);
}
}
#ifdef DEBUG // MouseLocation logs the mouse location and when/where enqueued synthesized // mouse move is flushed. If you don't need all mouse location recording at // eMouseMove, you can use MouseLocation:3,sync. Then, it's logged once per // 50 times. Otherwise, if you need to log all eMouseMove locations, you can // use MouseLocation:5,sync.
LazyLogModule gLogMouseLocation("MouseLocation"); #endif
/* static */ bool PresShell::AccessibleCaretEnabled(nsIDocShell* aDocShell) { // If the pref forces it on, then enable it. if (StaticPrefs::layout_accessiblecaret_enabled()) { returntrue;
} // If the touch pref is on, and touch events are enabled (this depends // on the specific device running), then enable it. if (StaticPrefs::layout_accessiblecaret_enabled_on_touch() &&
dom::TouchEvent::PrefEnabled(aDocShell)) { returntrue;
} // Otherwise, disabled. returnfalse;
}
NS_INTERFACE_TABLE_HEAD(PresShell)
NS_INTERFACE_TABLE_BEGIN // In most cases, PresShell should be treated as concrete class, but need to // QI for weak reference. Therefore, the case needed by do_QueryReferent() // should be tested first.
NS_INTERFACE_TABLE_ENTRY(PresShell, PresShell)
NS_INTERFACE_TABLE_ENTRY(PresShell, nsIDocumentObserver)
NS_INTERFACE_TABLE_ENTRY(PresShell, nsISelectionController)
NS_INTERFACE_TABLE_ENTRY(PresShell, nsISelectionDisplay)
NS_INTERFACE_TABLE_ENTRY(PresShell, nsIObserver)
NS_INTERFACE_TABLE_ENTRY(PresShell, nsISupportsWeakReference)
NS_INTERFACE_TABLE_ENTRY(PresShell, nsIMutationObserver)
NS_INTERFACE_TABLE_ENTRY_AMBIGUOUS(PresShell, nsISupports, nsIObserver)
NS_INTERFACE_TABLE_END
NS_INTERFACE_TABLE_TO_MAP_SEGUE
NS_INTERFACE_MAP_END
PresShell::~PresShell() {
MOZ_RELEASE_ASSERT(!mForbiddenToFlush, "Flag should only be set temporarily, while doing things " "that shouldn't cause destruction");
MOZ_LOG(gLog, LogLevel::Debug, ("PresShell::~PresShell this=%p", this));
if (!mHaveShutDown) {
MOZ_ASSERT_UNREACHABLE("Someone did not call PresShell::Destroy()");
Destroy();
}
NS_ASSERTION(mCurrentEventTargetStack.IsEmpty(), "Huh, event content left on the stack in pres shell dtor!");
NS_ASSERTION(mFirstCallbackEventRequest == nullptr &&
mLastCallbackEventRequest == nullptr, "post-reflow queues not empty. This means we're leaking");
MOZ_ASSERT(!mAllocatedPointers || mAllocatedPointers->IsEmpty(), "Some pres arena objects were not freed");
mFrameConstructor = nullptr;
}
/** * Initialize the presentation shell. Create view manager and style * manager. * Note this can't be merged into our constructor because caret initialization * calls AddRef() on us.
*/ void PresShell::Init(nsPresContext* aPresContext, nsViewManager* aViewManager) {
MOZ_ASSERT(mDocument);
MOZ_ASSERT(aPresContext);
MOZ_ASSERT(aViewManager);
MOZ_ASSERT(!mViewManager, "already initialized");
mViewManager = aViewManager;
// mDocument is now set. It might have a display document whose "need layout/ // style" flush flags are not set, but ours will be set. To keep these // consistent, call the flag setting functions to propagate those flags up // to the display document.
SetNeedLayoutFlush();
SetNeedStyleFlush();
// The document viewer owns both view manager and pres shell.
mViewManager->SetPresShell(this);
// Bind the context to the presentation shell. // FYI: We cannot initialize mPresContext in the constructor because we // cannot call AttachPresShell() in it and once we initialize // mPresContext, other objects may refer refresh driver or restyle // manager via mPresContext and that causes hitting MOZ_ASSERT in some // places. Therefore, we should initialize mPresContext here with // const_cast hack since we want to guarantee that mPresContext lives // as long as the PresShell. const_cast<RefPtr<nsPresContext>&>(mPresContext) = aPresContext;
mPresContext->AttachPresShell(this);
mPresContext->InitFontCache();
// FIXME(emilio, bug 1544185): Some Android code somehow depends on the shell // being eagerly registered as a style flush observer. This shouldn't be // needed otherwise.
EnsureStyleFlush();
constbool accessibleCaretEnabled =
AccessibleCaretEnabled(mDocument->GetDocShell()); if (accessibleCaretEnabled) { // Need to happen before nsFrameSelection has been set up.
mAccessibleCaretEventHub = new AccessibleCaretEventHub(this);
mAccessibleCaretEventHub->Init();
}
mSelection = new nsFrameSelection(this, accessibleCaretEnabled);
// Important: this has to happen after the selection has been set up #ifdef SHOW_CARET // make the caret
mCaret = new nsCaret();
mCaret->Init(this);
mOriginalCaret = mCaret;
// SetCaretEnabled(true); // make it show in browser windows #endif // set up selection to be displayed in document // Don't enable selection for print media
nsPresContext::nsPresContextType type = mPresContext->Type(); if (type != nsPresContext::eContext_PrintPreview &&
type != nsPresContext::eContext_Print) {
SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
}
if (gMaxRCProcessingTime == -1) {
gMaxRCProcessingTime =
Preferences::GetInt("layout.reflow.timeslice", NS_MAX_REFLOW_TIME);
}
if (nsStyleSheetService* ss = nsStyleSheetService::GetInstance()) {
ss->RegisterPresShell(this);
}
if (mDocument->HasAnimationController()) {
SMILAnimationController* animCtrl = mDocument->GetAnimationController();
animCtrl->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
}
for (DocumentTimeline* timelines : mDocument->Timelines()) {
timelines->UpdateLastRefreshDriverTime();
}
// Get our activeness from the docShell.
ActivenessMaybeChanged();
// Setup our font inflation preferences.
mFontSizeInflationEmPerLine = StaticPrefs::font_size_inflation_emPerLine();
mFontSizeInflationMinTwips = StaticPrefs::font_size_inflation_minTwips();
mFontSizeInflationLineThreshold =
StaticPrefs::font_size_inflation_lineThreshold();
mFontSizeInflationForceEnabled =
StaticPrefs::font_size_inflation_forceEnabled();
mFontSizeInflationDisabledInMasterProcess =
StaticPrefs::font_size_inflation_disabledInMasterProcess(); // We'll compute the font size inflation state in Initialize(), when we know // the document type.
mTouchManager.Init(this, mDocument);
if (mPresContext->IsRootContentDocumentCrossProcess()) {
mZoomConstraintsClient = new ZoomConstraintsClient();
mZoomConstraintsClient->Init(this, mDocument);
// We call this to create mMobileViewportManager, if it is needed.
MaybeRecreateMobileViewportManager(false);
}
if (nsCOMPtr<nsIDocShell> docShell = mPresContext->GetDocShell()) { if (BrowsingContext* bc = docShell->GetBrowsingContext()) {
mUnderHiddenEmbedderElement = bc->IsUnderHiddenEmbedderElement();
}
}
}
void PresShell::Destroy() { // Do not add code before this line please! if (mHaveShutDown) { return;
}
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(), "destroy called on presshell while scripts not blocked");
[[maybe_unused]] nsIURI* uri = mDocument->GetDocumentURI();
AUTO_PROFILER_LABEL_DYNAMIC_NSCSTRING_RELEVANT_FOR_JS( "Layout tree destruction", LAYOUT_Destroy,
uri ? uri->GetSpecOrDefault() : "N/A"_ns);
// Try to determine if the page is the user had a meaningful opportunity to // zoom this page. This is not 100% accurate but should be "good enough" for // telemetry purposes. auto isUserZoomablePage = [&]() -> bool { if (mIsFirstPaint) { // Page was never painted, so it wasn't zoomable by the user. We get a // handful of these "transient" presShells. returnfalse;
} if (!mPresContext->IsRootContentDocumentCrossProcess()) { // Not a root content document, so APZ doesn't support zooming it. returnfalse;
} if (InRDMPane()) { // Responsive design mode is a special case that we want to ignore here. returnfalse;
} if (mDocument && mDocument->IsInitialDocument()) { // Ignore initial about:blank page loads returnfalse;
} if (XRE_IsContentProcess() &&
IsExtensionRemoteType(ContentChild::GetSingleton()->GetRemoteType())) { // Also omit presShells from the extension process because they sometimes // can't be zoomed by the user. returnfalse;
} // Otherwise assume the page is user-zoomable. returntrue;
}; if (isUserZoomablePage()) {
Telemetry::Accumulate(Telemetry::APZ_ZOOM_ACTIVITY,
IsResolutionUpdatedByApz());
}
// dump out cumulative text perf metrics
gfxTextPerfMetrics* tp; if (mPresContext && (tp = mPresContext->GetTextPerfMetrics())) {
tp->Accumulate(); if (tp->cumulative.numChars > 0) {
LogTextPerfStats(tp, this, tp->cumulative, 0.0, eLog_totals, nullptr);
}
} if (mPresContext) { if (gfxUserFontSet* fs = mPresContext->GetUserFontSet()) {
uint32_t fontCount;
uint64_t fontSize;
fs->GetLoadStatistics(fontCount, fontSize);
Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE, fontCount);
Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE,
uint32_t(fontSize / 1024));
} else {
Telemetry::Accumulate(Telemetry::WEBFONT_PER_PAGE, 0);
Telemetry::Accumulate(Telemetry::WEBFONT_SIZE_PER_PAGE, 0);
}
}
if (mContentToScrollTo) {
mContentToScrollTo->RemoveProperty(nsGkAtoms::scrolling);
mContentToScrollTo = nullptr;
}
if (mPresContext) { // We need to notify the destroying the nsPresContext to ESM for // suppressing to use from ESM.
mPresContext->EventStateManager()->NotifyDestroyPresContext(mPresContext);
}
if (nsStyleSheetService* ss = nsStyleSheetService::GetInstance()) {
ss->UnregisterPresShell(this);
}
{
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService(); if (os) {
os->RemoveObserver(this, "memory-pressure");
os->RemoveObserver(this, NS_WIDGET_WAKE_OBSERVER_TOPIC); if (XRE_IsParentProcess()) {
os->RemoveObserver(this, "sessionstore-one-or-no-tab-restored");
}
os->RemoveObserver(this, "font-info-updated");
os->RemoveObserver(this, "internal-look-and-feel-changed");
}
}
// If our paint suppression timer is still active, kill it.
CancelPaintSuppressionTimer();
if (mOriginalCaret) {
mOriginalCaret->Terminate();
} if (mCaret && mCaret != mOriginalCaret) {
mCaret->Terminate();
}
mCaret = mOriginalCaret = nullptr;
mFocusedFrameSelection = nullptr;
if (mSelection) {
RefPtr<nsFrameSelection> frameSelection = mSelection;
frameSelection->DisconnectFromPresShell();
}
mIsDestroying = true;
// We can't release all the event content in // mCurrentEventContentStack here since there might be code on the // stack that will release the event content too. Double release // bad!
// The frames will be torn down, so remove them from the current // event frame stack (since they'd be dangling references if we'd // leave them in) and null out the mCurrentEventFrame pointer as // well.
mCurrentEventTarget.ClearFrame();
for (EventTargetInfo& eventTargetInfo : mCurrentEventTargetStack) {
eventTargetInfo.ClearFrame();
}
if (mViewManager) { // Clear the view manager's weak pointer back to |this| in case it // was leaked.
mViewManager->SetPresShell(nullptr);
mViewManager = nullptr;
}
// This shell must be removed from the document before the frame // hierarchy is torn down to avoid finding deleted frames through // this presshell while the frames are being torn down if (mDocument) {
NS_ASSERTION(mDocument->GetPresShell() == this, "Wrong shell?");
mDocument->ClearServoRestyleRoot();
mDocument->DeletePresShell();
if (mDocument->HasAnimationController()) {
mDocument->GetAnimationController()->NotifyRefreshDriverDestroying(rd);
}
}
if (mPresContext) {
rd->CancelPendingAnimationEvents(mPresContext->AnimationEventDispatcher());
}
// Revoke any pending events. We need to do this and cancel pending reflows // before we destroy the frame constructor, since apparently frame destruction // sometimes spins the event queue when plug-ins are involved(!). // XXXmats is this still needed now that plugins are gone?
StopObservingRefreshDriver();
mObservingStyleFlushes = false;
if (rd->GetPresContext() == GetPresContext()) {
rd->RevokeViewManagerFlush();
rd->ClearHasScheduleFlush();
}
// Destroy the frame constructor. This will destroy the frame hierarchy
mFrameConstructor->WillDestroyFrameTree();
NS_WARNING_ASSERTION(!mAutoWeakFrames && mWeakFrames.IsEmpty(), "Weak frames alive after destroying FrameManager"); while (mAutoWeakFrames) {
mAutoWeakFrames->Clear(this);
} const nsTArray<WeakFrame*> weakFrames = ToArray(mWeakFrames); for (WeakFrame* weakFrame : weakFrames) {
weakFrame->Clear(this);
}
// Terminate AccessibleCaretEventHub after tearing down the frame tree so that // we don't need to remove caret element's frame in // AccessibleCaret::RemoveCaretElement(). if (mAccessibleCaretEventHub) {
mAccessibleCaretEventHub->Terminate();
mAccessibleCaretEventHub = nullptr;
}
if (mPresContext) { // We hold a reference to the pres context, and it holds a weak link back // to us. To avoid the pres context having a dangling reference, set its // pres shell to nullptr
mPresContext->DetachPresShell();
}
mHaveShutDown = true;
mTouchManager.Destroy();
}
void PresShell::StopObservingRefreshDriver() {
nsRefreshDriver* rd = mPresContext->RefreshDriver(); if (mResizeEventPending) {
rd->RemoveResizeEventFlushObserver(this);
} if (mObservingStyleFlushes) {
rd->RemoveStyleFlushObserver(this);
}
}
void PresShell::StartObservingRefreshDriver() {
nsRefreshDriver* rd = mPresContext->RefreshDriver(); if (mResizeEventPending) {
rd->AddResizeEventFlushObserver(this);
} if (mObservingStyleFlushes) {
rd->AddStyleFlushObserver(this);
}
}
void PresShell::AddUserSheet(StyleSheet* aSheet) { // Make sure this does what nsDocumentViewer::CreateStyleSet does wrt // ordering. We want this new sheet to come after all the existing stylesheet // service sheets (which are at the start), but before other user sheets; see // nsIStyleSheetService.idl for the ordering.
// Search for the place to insert the new user sheet. Since all of the // stylesheet service provided user sheets should be at the start of the style // set's list, and aSheet should be at the end of userSheets. Given that, we // can find the right place to insert the new sheet based on the length of // userSheets.
MOZ_ASSERT(aSheet);
MOZ_ASSERT(userSheets.LastElement() == aSheet);
size_t index = userSheets.Length() - 1;
// Assert that all of userSheets (except for the last, new element) matches up // with what's in the style set. for (size_t i = 0; i < index; ++i) {
MOZ_ASSERT(StyleSet()->SheetAt(StyleOrigin::User, i) == userSheets[i]);
}
void PresShell::AddAgentSheet(StyleSheet* aSheet) { // Make sure this does what nsDocumentViewer::CreateStyleSet does // wrt ordering.
StyleSet()->AppendStyleSheet(*aSheet);
mDocument->ApplicableStylesChanged();
}
void PresShell::AddAuthorSheet(StyleSheet* aSheet) { // Document specific "additional" Author sheets should be stronger than the // ones added with the StyleSheetService.
StyleSheet* firstAuthorSheet = mDocument->GetFirstAdditionalAuthorSheet(); if (firstAuthorSheet) {
StyleSet()->InsertStyleSheetBefore(*aSheet, *firstAuthorSheet);
} else {
StyleSet()->AppendStyleSheet(*aSheet);
}
mDocument->ApplicableStylesChanged();
}
bool PresShell::NeedsFocusFixUp() const { if (NS_WARN_IF(!mDocument)) { returnfalse;
}
nsIContent* currentFocus = mDocument->GetUnretargetedFocusedContent(
Document::IncludeChromeOnly::Yes); if (!currentFocus) { returnfalse;
}
// If focus target is an area element with one or more shapes that are // focusable areas. if (auto* area = HTMLAreaElement::FromNode(currentFocus)) { if (nsFocusManager::IsAreaElementFocusable(*area)) { returnfalse;
}
}
nsIFrame* f = currentFocus->GetPrimaryFrame(); if (f && f->IsFocusable()) { returnfalse;
}
bool PresShell::FixUpFocus() { if (!NeedsFocusFixUp()) { returnfalse;
}
RefPtr fm = nsFocusManager::GetFocusManager();
nsCOMPtr<nsPIDOMWindowOuter> window = mDocument->GetWindow(); if (NS_WARN_IF(!window)) { returnfalse;
}
fm->ClearFocus(window); returntrue;
}
void PresShell::SelectionWillTakeFocus() { if (mSelection) {
FrameSelectionWillTakeFocus(*mSelection);
}
}
void PresShell::SelectionWillLoseFocus() { // Do nothing, the main selection is the default focused selection.
}
// Selection repainting code relies on selection offsets being properly // adjusted (see bug 1626291), so we need to wait until the DOM is finished // notifying. staticvoid RepaintNormalSelectionWhenSafe(nsFrameSelection& aFrameSelection) { if (nsContentUtils::IsSafeToRunScript()) {
aFrameSelection.RepaintSelection(SelectionType::eNormal); return;
}
// Note that importantly we don't defer changing the DisplaySelection. That'd // be potentially racy with other code that may change it.
nsContentUtils::AddScriptRunner(NS_NewRunnableFunction( "RepaintNormalSelectionWhenSafe",
[sel = RefPtr<nsFrameSelection>(&aFrameSelection)] {
sel->RepaintSelection(SelectionType::eNormal);
}));
}
// Do nothing, the main selection is the default focused selection. if (&aFrameSelection == mSelection) { return;
}
RefPtr<nsFrameSelection> old = std::move(mFocusedFrameSelection);
MOZ_ASSERT(!mFocusedFrameSelection);
if (old->GetDisplaySelection() != nsISelectionController::SELECTION_HIDDEN) {
old->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
RepaintNormalSelectionWhenSafe(*old);
}
if (mSelection) {
FrameSelectionWillTakeFocus(*mSelection);
}
}
void PresShell::FrameSelectionWillTakeFocus(nsFrameSelection& aFrameSelection) { if (mFocusedFrameSelection == &aFrameSelection) { #ifdef XP_MACOSX // FIXME: Mac needs to update the global selection cache, even if the // document's focused selection doesn't change, and this is currently done // from RepaintSelection. Maybe we should move part of the global selection // handling here, or something of that sort, unclear.
RepaintNormalSelectionWhenSafe(aFrameSelection); #endif return;
}
RefPtr<nsFrameSelection> old = std::move(mFocusedFrameSelection);
mFocusedFrameSelection = &aFrameSelection;
if (old &&
old->GetDisplaySelection() != nsISelectionController::SELECTION_HIDDEN) {
old->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
RepaintNormalSelectionWhenSafe(*old);
}
if (aFrameSelection.GetDisplaySelection() !=
nsISelectionController::SELECTION_ON) {
aFrameSelection.SetDisplaySelection(nsISelectionController::SELECTION_ON);
RepaintNormalSelectionWhenSafe(aFrameSelection);
}
}
// Make shell be a document observer void PresShell::BeginObservingDocument() { if (mDocument && !mIsDestroying) {
mIsObservingDocument = true; if (mIsDocumentGone) {
NS_WARNING( "Adding a presshell that was disconnected from the document " "as a document observer? Sounds wrong...");
mIsDocumentGone = false;
}
}
}
// Make shell stop being a document observer void PresShell::EndObservingDocument() { // XXXbz do we need to tell the frame constructor that the document // is gone, perhaps? Except for printing it's NOT gone, sometimes.
mIsDocumentGone = true;
mIsObservingDocument = false;
}
// Ensure the pres context doesn't think it has changed, since we haven't even // started layout. This avoids spurious restyles / reflows afterwards. // // Note that this is very intentionally before setting mDidInitialize so it // doesn't notify the document, or run media query change events.
mPresContext->FlushPendingMediaFeatureValuesChanged();
MOZ_DIAGNOSTIC_ASSERT(!mIsDestroying);
mDidInitialize = true;
#ifdef DEBUG if (VerifyReflowFlags::NoisyCommands & gVerifyReflowFlags) { if (mDocument) {
nsIURI* uri = mDocument->GetDocumentURI(); if (uri) {
printf("*** PresShell::Initialize (this=%p, url='%s')\n", (void*)this,
uri->GetSpecOrDefault().get());
}
}
} #endif
// Get the root frame from the frame constructor. // XXXbz it would be nice to move this somewhere else... like frame manager // Init(), say. But we need to make sure our views are all set up by the // time we do this!
MOZ_ASSERT(!mFrameConstructor->GetRootFrame(), "How did that happen, exactly?");
ViewportFrame* rootFrame;
{
nsAutoScriptBlocker scriptBlocker;
rootFrame = mFrameConstructor->ConstructRootFrame();
mFrameConstructor->SetRootFrame(rootFrame);
}
NS_ENSURE_STATE(!mHaveShutDown);
if (!rootFrame) { return NS_ERROR_OUT_OF_MEMORY;
}
if (Element* root = mDocument->GetRootElement()) {
{
nsAutoCauseReflowNotifier reflowNotifier(this); // Have the style sheet processor construct frame for the root // content object down
mFrameConstructor->ContentInserted(
root, nsCSSFrameConstructor::InsertionKind::Sync);
} // Something in mFrameConstructor->ContentInserted may have caused // Destroy() to get called, bug 337586. Or, nsAutoCauseReflowNotifier // (which sets up a script blocker) going out of scope may have killed us // too
NS_ENSURE_STATE(!mHaveShutDown);
}
if (mDocument->HasAutoFocusCandidates()) {
mDocument->ScheduleFlushAutoFocusCandidates();
}
NS_ASSERTION(rootFrame, "How did that happen?");
// Note: when the frame was created above it had the NS_FRAME_IS_DIRTY bit // set, but XBL processing could have caused a reflow which clears it. if (MOZ_LIKELY(rootFrame->HasAnyStateBits(NS_FRAME_IS_DIRTY))) { // Unset the DIRTY bits so that FrameNeedsReflow() will work right.
rootFrame->RemoveStateBits(NS_FRAME_IS_DIRTY | NS_FRAME_HAS_DIRTY_CHILDREN);
NS_ASSERTION(!mDirtyRoots.Contains(rootFrame), "Why is the root in mDirtyRoots already?");
FrameNeedsReflow(rootFrame, IntrinsicDirty::None, NS_FRAME_IS_DIRTY);
NS_ASSERTION(mDirtyRoots.Contains(rootFrame), "Should be in mDirtyRoots now");
NS_ASSERTION(mObservingStyleFlushes, "Why no reflow scheduled?");
}
// Restore our root scroll position now if we're getting here after EndLoad // got called, since this is our one chance to do it. Note that we need not // have reflowed for this to work; when the scrollframe is finally reflowed // it'll pick up the position we store in it here. if (!mDocumentLoading) {
RestoreRootScrollPosition();
}
// For printing, we just immediately unsuppress. if (!mPresContext->IsPaginated()) { // Kick off a one-shot timer based off our pref value. When this timer // fires, if painting is still locked down, then we will go ahead and // trigger a full invalidate and allow painting to proceed normally.
mPaintingSuppressed = true; // Don't suppress painting if the document isn't loading.
Document::ReadyState readyState = mDocument->GetReadyStateEnum(); if (readyState != Document::READYSTATE_COMPLETE) {
mPaintSuppressionTimer = NS_NewTimer();
} if (!mPaintSuppressionTimer) {
mPaintingSuppressed = false;
} else { // Initialize the timer.
mPaintSuppressionTimer->SetTarget(GetMainThreadSerialEventTarget());
InitPaintSuppressionTimer(); if (mHasTriedFastUnsuppress) { // Someone tried to unsuppress painting before Initialize was called so // unsuppress painting rather soon.
mHasTriedFastUnsuppress = false;
TryUnsuppressPaintingSoon();
MOZ_ASSERT(mHasTriedFastUnsuppress);
}
}
}
// If we get here and painting is not suppressed, we still want to run the // unsuppression logic, so set mShouldUnsuppressPainting to true. if (!mPaintingSuppressed) {
mShouldUnsuppressPainting = true;
}
if (!mDidInitialize || !IsPaintingSuppressed() || !XRE_IsContentProcess()) { return;
}
if (!mDocument->IsInitialDocument() &&
mDocument->DidHitCompleteSheetCache() &&
mPresContext->IsRootContentDocumentCrossProcess()) { // Try to unsuppress faster on a top level page if it uses stylesheet // cache, since that hints that many resources can be painted sooner than // in a cold page load case.
NS_DispatchToCurrentThreadQueue(
NS_NewRunnableFunction("PresShell::TryUnsuppressPaintingSoon",
[self = RefPtr{this}]() -> void { if (self->IsPaintingSuppressed()) {
PROFILER_MARKER_UNTYPED( "Fast paint unsuppression", GRAPHICS);
self->UnsuppressPainting();
}
}),
EventQueuePriority::Control);
}
}
void PresShell::RefreshZoomConstraintsForScreenSizeChange() { if (mZoomConstraintsClient) {
mZoomConstraintsClient->ScreenSizeChanged();
}
}
void PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight,
ResizeReflowOptions aOptions) { if (mZoomConstraintsClient) { // If we have a ZoomConstraintsClient and the available screen area // changed, then we might need to disable double-tap-to-zoom, so notify // the ZCC to update itself.
mZoomConstraintsClient->ScreenSizeChanged();
} if (UsesMobileViewportSizing()) { // If we are using mobile viewport sizing, request a reflow from the MVM. // It can recompute the final CSS viewport and trigger a call to // ResizeReflowIgnoreOverride if it changed. We don't force adjusting
--> --------------------
--> maximum size reached
--> --------------------
¤ Dauer der Verarbeitung: 0.40 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.