michael@0: /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: /* a presentation of a document, part 1 */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/DebugOnly.h" michael@0: #include "mozilla/EventDispatcher.h" michael@0: #include "mozilla/EventStateManager.h" michael@0: michael@0: #include "base/basictypes.h" michael@0: michael@0: #include "nsCOMPtr.h" michael@0: #include "nsPresContext.h" michael@0: #include "nsIPresShell.h" michael@0: #include "nsDocShell.h" michael@0: #include "nsIContentViewer.h" michael@0: #include "nsPIDOMWindow.h" michael@0: #include "nsStyleSet.h" michael@0: #include "nsIContent.h" michael@0: #include "nsIFrame.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIPrintSettings.h" michael@0: #include "nsILanguageAtomService.h" michael@0: #include "mozilla/LookAndFeel.h" michael@0: #include "nsIInterfaceRequestorUtils.h" michael@0: #include "nsIWeakReferenceUtils.h" michael@0: #include "nsAutoPtr.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "nsFrameManager.h" michael@0: #include "nsLayoutUtils.h" michael@0: #include "nsViewManager.h" michael@0: #include "RestyleManager.h" michael@0: #include "nsCSSRuleProcessor.h" michael@0: #include "nsRuleNode.h" michael@0: #include "gfxPlatform.h" michael@0: #include "nsCSSRules.h" michael@0: #include "nsFontFaceLoader.h" michael@0: #include "mozilla/EventListenerManager.h" michael@0: #include "prenv.h" michael@0: #include "nsObjectFrame.h" michael@0: #include "nsTransitionManager.h" michael@0: #include "nsAnimationManager.h" michael@0: #include "mozilla/MemoryReporting.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "nsIMessageManager.h" michael@0: #include "mozilla/dom/MediaQueryList.h" michael@0: #include "nsSMILAnimationController.h" michael@0: #include "mozilla/css/ImageLoader.h" michael@0: #include "mozilla/dom/TabChild.h" michael@0: #include "nsRefreshDriver.h" michael@0: #include "Layers.h" michael@0: #include "nsIDOMEvent.h" michael@0: #include "gfxPrefs.h" michael@0: #include "nsString.h" michael@0: #include "nsUnicharUtils.h" michael@0: michael@0: #include "nsContentUtils.h" michael@0: #include "nsCxPusher.h" michael@0: #include "nsPIWindowRoot.h" michael@0: #include "mozilla/Preferences.h" michael@0: michael@0: // Needed for Start/Stop of Image Animation michael@0: #include "imgIContainer.h" michael@0: #include "nsIImageLoadingContent.h" michael@0: michael@0: #include "nsCSSParser.h" michael@0: #include "nsBidiUtils.h" michael@0: #include "nsServiceManagerUtils.h" michael@0: michael@0: #include "URL.h" michael@0: michael@0: using namespace mozilla; michael@0: using namespace mozilla::dom; michael@0: using namespace mozilla::layers; michael@0: michael@0: uint8_t gNotifySubDocInvalidationData; michael@0: michael@0: /** michael@0: * Layer UserData for ContainerLayers that want to be notified michael@0: * of local invalidations of them and their descendant layers. michael@0: * Pass a callback to ComputeDifferences to have these called. michael@0: */ michael@0: class ContainerLayerPresContext : public LayerUserData { michael@0: public: michael@0: nsPresContext* mPresContext; michael@0: }; michael@0: michael@0: namespace { michael@0: michael@0: class CharSetChangingRunnable : public nsRunnable michael@0: { michael@0: public: michael@0: CharSetChangingRunnable(nsPresContext* aPresContext, michael@0: const nsCString& aCharSet) michael@0: : mPresContext(aPresContext), michael@0: mCharSet(aCharSet) michael@0: { michael@0: } michael@0: michael@0: NS_IMETHOD Run() michael@0: { michael@0: mPresContext->DoChangeCharSet(mCharSet); michael@0: return NS_OK; michael@0: } michael@0: michael@0: private: michael@0: nsRefPtr mPresContext; michael@0: nsCString mCharSet; michael@0: }; michael@0: michael@0: } // anonymous namespace michael@0: michael@0: nscolor michael@0: nsPresContext::MakeColorPref(const nsString& aColor) michael@0: { michael@0: nsCSSParser parser; michael@0: nsCSSValue value; michael@0: if (!parser.ParseColorString(aColor, nullptr, 0, value)) { michael@0: // Any better choices? michael@0: return NS_RGB(0, 0, 0); michael@0: } michael@0: michael@0: nscolor color; michael@0: return nsRuleNode::ComputeColor(value, this, nullptr, color) michael@0: ? color michael@0: : NS_RGB(0, 0, 0); michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::IsDOMPaintEventPending() michael@0: { michael@0: if (mFireAfterPaintEvents) { michael@0: return true; michael@0: } michael@0: if (GetDisplayRootPresContext()->GetRootPresContext()->mRefreshDriver->ViewManagerFlushIsPending()) { michael@0: // Since we're promising that there will be a MozAfterPaint event michael@0: // fired, we record an empty invalidation in case display list michael@0: // invalidation doesn't invalidate anything further. michael@0: NotifyInvalidation(nsRect(0, 0, 0, 0), 0); michael@0: NS_ASSERTION(mFireAfterPaintEvents, "Why aren't we planning to fire the event?"); michael@0: return true; michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: void michael@0: nsPresContext::PrefChangedCallback(const char* aPrefName, void* instance_data) michael@0: { michael@0: nsRefPtr presContext = michael@0: static_cast(instance_data); michael@0: michael@0: NS_ASSERTION(nullptr != presContext, "bad instance data"); michael@0: if (nullptr != presContext) { michael@0: presContext->PreferenceChanged(aPrefName); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::PrefChangedUpdateTimerCallback(nsITimer *aTimer, void *aClosure) michael@0: { michael@0: nsPresContext* presContext = (nsPresContext*)aClosure; michael@0: NS_ASSERTION(presContext != nullptr, "bad instance data"); michael@0: if (presContext) michael@0: presContext->UpdateAfterPreferencesChanged(); michael@0: } michael@0: michael@0: static bool michael@0: IsVisualCharset(const nsCString& aCharset) michael@0: { michael@0: if (aCharset.LowerCaseEqualsLiteral("ibm862") // Hebrew michael@0: || aCharset.LowerCaseEqualsLiteral("iso-8859-8") ) { // Hebrew michael@0: return true; // visual text type michael@0: } michael@0: else { michael@0: return false; // logical text type michael@0: } michael@0: } michael@0: michael@0: // NOTE! nsPresContext::operator new() zeroes out all members, so don't michael@0: // bother initializing members to 0. michael@0: michael@0: nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType) michael@0: : mType(aType), mDocument(aDocument), mBaseMinFontSize(0), michael@0: mTextZoom(1.0), mFullZoom(1.0), mLastFontInflationScreenWidth(-1.0), michael@0: mPageSize(-1, -1), mPPScale(1.0f), michael@0: mViewportStyleOverflow(NS_STYLE_OVERFLOW_AUTO, NS_STYLE_OVERFLOW_AUTO), michael@0: mImageAnimationModePref(imgIContainer::kNormalAnimMode), michael@0: mAllInvalidated(false), michael@0: mPaintFlashing(false), mPaintFlashingInitialized(false) michael@0: { michael@0: // NOTE! nsPresContext::operator new() zeroes out all members, so don't michael@0: // bother initializing members to 0. michael@0: michael@0: mDoScaledTwips = true; michael@0: michael@0: SetBackgroundImageDraw(true); // always draw the background michael@0: SetBackgroundColorDraw(true); michael@0: michael@0: mBackgroundColor = NS_RGB(0xFF, 0xFF, 0xFF); michael@0: michael@0: mUseDocumentColors = true; michael@0: mUseDocumentFonts = true; michael@0: michael@0: // the minimum font-size is unconstrained by default michael@0: michael@0: mLinkColor = NS_RGB(0x00, 0x00, 0xEE); michael@0: mActiveLinkColor = NS_RGB(0xEE, 0x00, 0x00); michael@0: mVisitedLinkColor = NS_RGB(0x55, 0x1A, 0x8B); michael@0: mUnderlineLinks = true; michael@0: mSendAfterPaintToContent = false; michael@0: michael@0: mFocusTextColor = mDefaultColor; michael@0: mFocusBackgroundColor = mBackgroundColor; michael@0: mFocusRingWidth = 1; michael@0: michael@0: mBodyTextColor = mDefaultColor; michael@0: michael@0: if (aType == eContext_Galley) { michael@0: mMedium = nsGkAtoms::screen; michael@0: } else { michael@0: mMedium = nsGkAtoms::print; michael@0: mPaginated = true; michael@0: } michael@0: mMediaEmulated = mMedium; michael@0: michael@0: if (!IsDynamic()) { michael@0: mImageAnimationMode = imgIContainer::kDontAnimMode; michael@0: mNeverAnimate = true; michael@0: } else { michael@0: mImageAnimationMode = imgIContainer::kNormalAnimMode; michael@0: mNeverAnimate = false; michael@0: } michael@0: NS_ASSERTION(mDocument, "Null document"); michael@0: mUserFontSet = nullptr; michael@0: mUserFontSetDirty = true; michael@0: michael@0: // if text perf logging enabled, init stats struct michael@0: PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textperf); michael@0: if (log && log->level >= PR_LOG_WARNING) { michael@0: mTextPerf = new gfxTextPerfMetrics(); michael@0: } michael@0: michael@0: PR_INIT_CLIST(&mDOMMediaQueryLists); michael@0: } michael@0: michael@0: nsPresContext::~nsPresContext() michael@0: { michael@0: NS_PRECONDITION(!mShell, "Presshell forgot to clear our mShell pointer"); michael@0: SetShell(nullptr); michael@0: michael@0: NS_ABORT_IF_FALSE(PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists), michael@0: "must not have media query lists left"); michael@0: michael@0: // Disconnect the refresh driver *after* the transition manager, which michael@0: // needs it. michael@0: if (mRefreshDriver && mRefreshDriver->PresContext() == this) { michael@0: mRefreshDriver->Disconnect(); michael@0: } michael@0: michael@0: if (mEventManager) { michael@0: // unclear if these are needed, but can't hurt michael@0: mEventManager->NotifyDestroyPresContext(this); michael@0: mEventManager->SetPresContext(nullptr); michael@0: } michael@0: michael@0: if (mPrefChangedTimer) michael@0: { michael@0: mPrefChangedTimer->Cancel(); michael@0: mPrefChangedTimer = nullptr; michael@0: } michael@0: michael@0: // Unregister preference callbacks michael@0: Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, michael@0: "font.", michael@0: this); michael@0: Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, michael@0: "browser.display.", michael@0: this); michael@0: Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, michael@0: "browser.underline_anchors", michael@0: this); michael@0: Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, michael@0: "browser.anchor_color", michael@0: this); michael@0: Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, michael@0: "browser.active_color", michael@0: this); michael@0: Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, michael@0: "browser.visited_color", michael@0: this); michael@0: Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, michael@0: "image.animation_mode", michael@0: this); michael@0: Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, michael@0: "bidi.", michael@0: this); michael@0: Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, michael@0: "dom.send_after_paint_to_content", michael@0: this); michael@0: Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, michael@0: "gfx.font_rendering.", michael@0: this); michael@0: Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, michael@0: "layout.css.dpi", michael@0: this); michael@0: Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, michael@0: "layout.css.devPixelsPerPx", michael@0: this); michael@0: Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, michael@0: "nglayout.debug.paint_flashing", michael@0: this); michael@0: Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback, michael@0: "nglayout.debug.paint_flashing_chrome", michael@0: this); michael@0: } michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPresContext) michael@0: NS_INTERFACE_MAP_ENTRY(nsISupports) michael@0: NS_INTERFACE_MAP_ENTRY(nsIObserver) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPresContext) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsPresContext, LastRelease()) michael@0: michael@0: void michael@0: nsPresContext::LastRelease() michael@0: { michael@0: if (IsRoot()) { michael@0: static_cast(this)->CancelDidPaintTimer(); michael@0: } michael@0: } michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument); michael@0: // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventManager); michael@0: // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom michael@0: michael@0: // We own only the items in mDOMMediaQueryLists that have listeners; michael@0: // this reference is managed by their AddListener and RemoveListener michael@0: // methods. michael@0: for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists); michael@0: l != &tmp->mDOMMediaQueryLists; l = PR_NEXT_LINK(l)) { michael@0: MediaQueryList *mql = static_cast(l); michael@0: if (mql->HasListeners()) { michael@0: NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item"); michael@0: cb.NoteXPCOMChild(mql); michael@0: } michael@0: } michael@0: michael@0: // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTheme); // a service michael@0: // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLangService); // a service michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrintSettings); michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrefChangedTimer); michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext); // worth bothering? michael@0: if (tmp->mEventManager) { michael@0: // unclear if these are needed, but can't hurt michael@0: tmp->mEventManager->NotifyDestroyPresContext(tmp); michael@0: tmp->mEventManager->SetPresContext(nullptr); michael@0: tmp->mEventManager = nullptr; michael@0: } michael@0: michael@0: // We own only the items in mDOMMediaQueryLists that have listeners; michael@0: // this reference is managed by their AddListener and RemoveListener michael@0: // methods. michael@0: for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists); michael@0: l != &tmp->mDOMMediaQueryLists; ) { michael@0: PRCList *next = PR_NEXT_LINK(l); michael@0: MediaQueryList *mql = static_cast(l); michael@0: mql->RemoveAllListeners(); michael@0: l = next; michael@0: } michael@0: michael@0: // NS_RELEASE(tmp->mLanguage); // an atom michael@0: michael@0: // NS_IMPL_CYCLE_COLLECTION_UNLINK(mTheme); // a service michael@0: // NS_IMPL_CYCLE_COLLECTION_UNLINK(mLangService); // a service michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrintSettings); michael@0: if (tmp->mPrefChangedTimer) michael@0: { michael@0: tmp->mPrefChangedTimer->Cancel(); michael@0: tmp->mPrefChangedTimer = nullptr; michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: michael@0: michael@0: #define MAKE_FONT_PREF_KEY(_pref, _s0, _s1) \ michael@0: _pref.Assign(_s0); \ michael@0: _pref.Append(_s1); michael@0: michael@0: static const char* const kGenericFont[] = { michael@0: ".variable.", michael@0: ".fixed.", michael@0: ".serif.", michael@0: ".sans-serif.", michael@0: ".monospace.", michael@0: ".cursive.", michael@0: ".fantasy." michael@0: }; michael@0: michael@0: // whether no native theme service exists; michael@0: // if this gets set to true, we'll stop asking for it. michael@0: static bool sNoTheme = false; michael@0: michael@0: // Set to true when LookAndFeelChanged needs to be called. This is used michael@0: // because the look and feel is a service, so there's no need to notify it from michael@0: // more than one prescontext. michael@0: static bool sLookAndFeelChanged; michael@0: michael@0: // Set to true when ThemeChanged needs to be called on mTheme. This is used michael@0: // because mTheme is a service, so there's no need to notify it from more than michael@0: // one prescontext. michael@0: static bool sThemeChanged; michael@0: michael@0: const nsPresContext::LangGroupFontPrefs* michael@0: nsPresContext::GetFontPrefsForLang(nsIAtom *aLanguage) const michael@0: { michael@0: // Get language group for aLanguage: michael@0: michael@0: nsresult rv = NS_OK; michael@0: nsIAtom *langGroupAtom = nullptr; michael@0: if (!aLanguage) { michael@0: aLanguage = mLanguage; michael@0: } michael@0: if (aLanguage && mLangService) { michael@0: langGroupAtom = mLangService->GetLanguageGroup(aLanguage, &rv); michael@0: } michael@0: if (NS_FAILED(rv) || !langGroupAtom) { michael@0: langGroupAtom = nsGkAtoms::x_western; // Assume x-western is safe... michael@0: } michael@0: michael@0: // Look for cached prefs for this lang group. michael@0: // Most documents will only use one (or very few) language groups. Rather michael@0: // than have the overhead of a hash lookup, we simply look along what will michael@0: // typically be a very short (usually of length 1) linked list. There are 31 michael@0: // language groups, so in the worst case scenario we'll need to traverse 31 michael@0: // link items. michael@0: michael@0: LangGroupFontPrefs *prefs = michael@0: const_cast(&mLangGroupFontPrefs); michael@0: if (prefs->mLangGroup) { // if initialized michael@0: DebugOnly count = 0; michael@0: for (;;) { michael@0: NS_ASSERTION(++count < 35, "Lang group count exceeded!!!"); michael@0: if (prefs->mLangGroup == langGroupAtom) { michael@0: return prefs; michael@0: } michael@0: if (!prefs->mNext) { michael@0: break; michael@0: } michael@0: prefs = prefs->mNext; michael@0: } michael@0: michael@0: // nothing cached, so go on and fetch the prefs for this lang group: michael@0: prefs = prefs->mNext = new LangGroupFontPrefs; michael@0: } michael@0: michael@0: prefs->mLangGroup = langGroupAtom; michael@0: michael@0: /* Fetch the font prefs to be used -- see bug 61883 for details. michael@0: Not all prefs are needed upfront. Some are fallback prefs intended michael@0: for the GFX font sub-system... michael@0: michael@0: 1) unit : assumed to be the same for all language groups ------------- michael@0: font.size.unit = px | pt XXX could be folded in the size... bug 90440 michael@0: michael@0: 2) attributes for generic fonts -------------------------------------- michael@0: font.default.[langGroup] = serif | sans-serif - fallback generic font michael@0: font.name.[generic].[langGroup] = current user' selected font on the pref dialog michael@0: font.name-list.[generic].[langGroup] = fontname1, fontname2, ... [factory pre-built list] michael@0: font.size.[generic].[langGroup] = integer - settable by the user michael@0: font.size-adjust.[generic].[langGroup] = "float" - settable by the user michael@0: font.minimum-size.[langGroup] = integer - settable by the user michael@0: */ michael@0: michael@0: nsAutoCString langGroup; michael@0: langGroupAtom->ToUTF8String(langGroup); michael@0: michael@0: prefs->mDefaultVariableFont.size = CSSPixelsToAppUnits(16); michael@0: prefs->mDefaultFixedFont.size = CSSPixelsToAppUnits(13); michael@0: michael@0: nsAutoCString pref; michael@0: michael@0: // get the current applicable font-size unit michael@0: enum {eUnit_unknown = -1, eUnit_px, eUnit_pt}; michael@0: int32_t unit = eUnit_px; michael@0: michael@0: nsAdoptingCString cvalue = michael@0: Preferences::GetCString("font.size.unit"); michael@0: michael@0: if (!cvalue.IsEmpty()) { michael@0: if (cvalue.Equals("px")) { michael@0: unit = eUnit_px; michael@0: } michael@0: else if (cvalue.Equals("pt")) { michael@0: unit = eUnit_pt; michael@0: } michael@0: else { michael@0: // XXX should really send this warning to the user (Error Console?). michael@0: // And just default to unit = eUnit_px? michael@0: NS_WARNING("unexpected font-size unit -- expected: 'px' or 'pt'"); michael@0: unit = eUnit_unknown; michael@0: } michael@0: } michael@0: michael@0: // get font.minimum-size.[langGroup] michael@0: michael@0: MAKE_FONT_PREF_KEY(pref, "font.minimum-size.", langGroup); michael@0: michael@0: int32_t size = Preferences::GetInt(pref.get()); michael@0: if (unit == eUnit_px) { michael@0: prefs->mMinimumFontSize = CSSPixelsToAppUnits(size); michael@0: } michael@0: else if (unit == eUnit_pt) { michael@0: prefs->mMinimumFontSize = CSSPointsToAppUnits(size); michael@0: } michael@0: michael@0: nsFont* fontTypes[] = { michael@0: &prefs->mDefaultVariableFont, michael@0: &prefs->mDefaultFixedFont, michael@0: &prefs->mDefaultSerifFont, michael@0: &prefs->mDefaultSansSerifFont, michael@0: &prefs->mDefaultMonospaceFont, michael@0: &prefs->mDefaultCursiveFont, michael@0: &prefs->mDefaultFantasyFont michael@0: }; michael@0: static_assert(MOZ_ARRAY_LENGTH(fontTypes) == eDefaultFont_COUNT, michael@0: "FontTypes array count is not correct"); michael@0: michael@0: // Get attributes specific to each generic font. We do not get the user's michael@0: // generic-font-name-to-specific-family-name preferences because its the michael@0: // generic name that should be fed into the cascade. It is up to the GFX michael@0: // code to look up the font prefs to convert generic names to specific michael@0: // family names as necessary. michael@0: nsAutoCString generic_dot_langGroup; michael@0: for (uint32_t eType = 0; eType < ArrayLength(fontTypes); ++eType) { michael@0: generic_dot_langGroup.Assign(kGenericFont[eType]); michael@0: generic_dot_langGroup.Append(langGroup); michael@0: michael@0: nsFont* font = fontTypes[eType]; michael@0: michael@0: // set the default variable font (the other fonts are seen as 'generic' fonts michael@0: // in GFX and will be queried there when hunting for alternative fonts) michael@0: if (eType == eDefaultFont_Variable) { michael@0: MAKE_FONT_PREF_KEY(pref, "font.name.variable.", langGroup); michael@0: michael@0: nsAdoptingString value = Preferences::GetString(pref.get()); michael@0: if (!value.IsEmpty()) { michael@0: prefs->mDefaultVariableFont.name.Assign(value); michael@0: } michael@0: else { michael@0: MAKE_FONT_PREF_KEY(pref, "font.default.", langGroup); michael@0: value = Preferences::GetString(pref.get()); michael@0: if (!value.IsEmpty()) { michael@0: prefs->mDefaultVariableFont.name.Assign(value); michael@0: } michael@0: } michael@0: } michael@0: else { michael@0: if (eType == eDefaultFont_Monospace) { michael@0: // This takes care of the confusion whereby people often expect "monospace" michael@0: // to have the same default font-size as "-moz-fixed" (this tentative michael@0: // size may be overwritten with the specific value for "monospace" when michael@0: // "font.size.monospace.[langGroup]" is read -- see below) michael@0: prefs->mDefaultMonospaceFont.size = prefs->mDefaultFixedFont.size; michael@0: } michael@0: else if (eType != eDefaultFont_Fixed) { michael@0: // all the other generic fonts are initialized with the size of the michael@0: // variable font, but their specific size can supersede later -- see below michael@0: font->size = prefs->mDefaultVariableFont.size; michael@0: } michael@0: } michael@0: michael@0: // Bug 84398: for spec purists, a different font-size only applies to the michael@0: // .variable. and .fixed. fonts and the other fonts should get |font-size-adjust|. michael@0: // The problem is that only GfxWin has the support for |font-size-adjust|. So for michael@0: // parity, we enable the ability to set a different font-size on all platforms. michael@0: michael@0: // get font.size.[generic].[langGroup] michael@0: // size=0 means 'Auto', i.e., generic fonts retain the size of the variable font michael@0: MAKE_FONT_PREF_KEY(pref, "font.size", generic_dot_langGroup); michael@0: size = Preferences::GetInt(pref.get()); michael@0: if (size > 0) { michael@0: if (unit == eUnit_px) { michael@0: font->size = CSSPixelsToAppUnits(size); michael@0: } michael@0: else if (unit == eUnit_pt) { michael@0: font->size = CSSPointsToAppUnits(size); michael@0: } michael@0: } michael@0: michael@0: // get font.size-adjust.[generic].[langGroup] michael@0: // XXX only applicable on GFX ports that handle |font-size-adjust| michael@0: MAKE_FONT_PREF_KEY(pref, "font.size-adjust", generic_dot_langGroup); michael@0: cvalue = Preferences::GetCString(pref.get()); michael@0: if (!cvalue.IsEmpty()) { michael@0: font->sizeAdjust = (float)atof(cvalue.get()); michael@0: } michael@0: michael@0: #ifdef DEBUG_rbs michael@0: printf("%s Family-list:%s size:%d sizeAdjust:%.2f\n", michael@0: generic_dot_langGroup.get(), michael@0: NS_ConvertUTF16toUTF8(font->name).get(), font->size, michael@0: font->sizeAdjust); michael@0: #endif michael@0: } michael@0: michael@0: return prefs; michael@0: } michael@0: michael@0: void michael@0: nsPresContext::GetDocumentColorPreferences() michael@0: { michael@0: // Make sure the preferences are initialized. In the normal run, michael@0: // they would already be, because gfxPlatform would have been created, michael@0: // but in some reference tests, that is not the case. michael@0: gfxPrefs::GetSingleton(); michael@0: michael@0: int32_t useAccessibilityTheme = 0; michael@0: bool usePrefColors = true; michael@0: bool isChromeDocShell = false; michael@0: michael@0: nsIDocument* doc = mDocument->GetDisplayDocument(); michael@0: if (doc && doc->GetDocShell()) { michael@0: isChromeDocShell = nsIDocShellTreeItem::typeChrome == michael@0: doc->GetDocShell()->ItemType(); michael@0: } else { michael@0: nsCOMPtr docShell(mContainer); michael@0: if (docShell) { michael@0: isChromeDocShell = nsIDocShellTreeItem::typeChrome == docShell->ItemType(); michael@0: } michael@0: } michael@0: michael@0: mIsChromeOriginImage = mDocument->IsBeingUsedAsImage() && michael@0: IsChromeURI(mDocument->GetDocumentURI()); michael@0: michael@0: if (isChromeDocShell || mIsChromeOriginImage) { michael@0: usePrefColors = false; michael@0: } else { michael@0: useAccessibilityTheme = michael@0: LookAndFeel::GetInt(LookAndFeel::eIntID_UseAccessibilityTheme, 0); michael@0: usePrefColors = !useAccessibilityTheme; michael@0: } michael@0: if (usePrefColors) { michael@0: usePrefColors = michael@0: !Preferences::GetBool("browser.display.use_system_colors", false); michael@0: } michael@0: michael@0: if (usePrefColors) { michael@0: nsAdoptingString colorStr = michael@0: Preferences::GetString("browser.display.foreground_color"); michael@0: michael@0: if (!colorStr.IsEmpty()) { michael@0: mDefaultColor = MakeColorPref(colorStr); michael@0: } michael@0: michael@0: colorStr = Preferences::GetString("browser.display.background_color"); michael@0: michael@0: if (!colorStr.IsEmpty()) { michael@0: mBackgroundColor = MakeColorPref(colorStr); michael@0: } michael@0: } michael@0: else { michael@0: mDefaultColor = michael@0: LookAndFeel::GetColor(LookAndFeel::eColorID_WindowForeground, michael@0: NS_RGB(0x00, 0x00, 0x00)); michael@0: mBackgroundColor = michael@0: LookAndFeel::GetColor(LookAndFeel::eColorID_WindowBackground, michael@0: NS_RGB(0xFF, 0xFF, 0xFF)); michael@0: } michael@0: michael@0: // Wherever we got the default background color from, ensure it is michael@0: // opaque. michael@0: mBackgroundColor = NS_ComposeColors(NS_RGB(0xFF, 0xFF, 0xFF), michael@0: mBackgroundColor); michael@0: michael@0: mUseDocumentColors = !useAccessibilityTheme && michael@0: Preferences::GetBool("browser.display.use_document_colors", michael@0: mUseDocumentColors); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::GetUserPreferences() michael@0: { michael@0: if (!GetPresShell()) { michael@0: // No presshell means nothing to do here. We'll do this when we michael@0: // get a presshell. michael@0: return; michael@0: } michael@0: michael@0: mAutoQualityMinFontSizePixelsPref = michael@0: Preferences::GetInt("browser.display.auto_quality_min_font_size"); michael@0: michael@0: // * document colors michael@0: GetDocumentColorPreferences(); michael@0: michael@0: mSendAfterPaintToContent = michael@0: Preferences::GetBool("dom.send_after_paint_to_content", michael@0: mSendAfterPaintToContent); michael@0: michael@0: // * link colors michael@0: mUnderlineLinks = michael@0: Preferences::GetBool("browser.underline_anchors", mUnderlineLinks); michael@0: michael@0: nsAdoptingString colorStr = Preferences::GetString("browser.anchor_color"); michael@0: michael@0: if (!colorStr.IsEmpty()) { michael@0: mLinkColor = MakeColorPref(colorStr); michael@0: } michael@0: michael@0: colorStr = Preferences::GetString("browser.active_color"); michael@0: michael@0: if (!colorStr.IsEmpty()) { michael@0: mActiveLinkColor = MakeColorPref(colorStr); michael@0: } michael@0: michael@0: colorStr = Preferences::GetString("browser.visited_color"); michael@0: michael@0: if (!colorStr.IsEmpty()) { michael@0: mVisitedLinkColor = MakeColorPref(colorStr); michael@0: } michael@0: michael@0: mUseFocusColors = michael@0: Preferences::GetBool("browser.display.use_focus_colors", mUseFocusColors); michael@0: michael@0: mFocusTextColor = mDefaultColor; michael@0: mFocusBackgroundColor = mBackgroundColor; michael@0: michael@0: colorStr = Preferences::GetString("browser.display.focus_text_color"); michael@0: michael@0: if (!colorStr.IsEmpty()) { michael@0: mFocusTextColor = MakeColorPref(colorStr); michael@0: } michael@0: michael@0: colorStr = Preferences::GetString("browser.display.focus_background_color"); michael@0: michael@0: if (!colorStr.IsEmpty()) { michael@0: mFocusBackgroundColor = MakeColorPref(colorStr); michael@0: } michael@0: michael@0: mFocusRingWidth = michael@0: Preferences::GetInt("browser.display.focus_ring_width", mFocusRingWidth); michael@0: michael@0: mFocusRingOnAnything = michael@0: Preferences::GetBool("browser.display.focus_ring_on_anything", michael@0: mFocusRingOnAnything); michael@0: michael@0: mFocusRingStyle = michael@0: Preferences::GetInt("browser.display.focus_ring_style", mFocusRingStyle); michael@0: michael@0: mBodyTextColor = mDefaultColor; michael@0: michael@0: // * use fonts? michael@0: mUseDocumentFonts = michael@0: Preferences::GetInt("browser.display.use_document_fonts") != 0; michael@0: michael@0: mMaxFonts = michael@0: Preferences::GetInt("browser.display.max_font_count", -1); michael@0: michael@0: mMaxFontAttempts = michael@0: Preferences::GetInt("browser.display.max_font_attempts", -1); michael@0: michael@0: mPrefScrollbarSide = Preferences::GetInt("layout.scrollbar.side"); michael@0: michael@0: ResetCachedFontPrefs(); michael@0: michael@0: // * image animation michael@0: const nsAdoptingCString& animatePref = michael@0: Preferences::GetCString("image.animation_mode"); michael@0: if (animatePref.Equals("normal")) michael@0: mImageAnimationModePref = imgIContainer::kNormalAnimMode; michael@0: else if (animatePref.Equals("none")) michael@0: mImageAnimationModePref = imgIContainer::kDontAnimMode; michael@0: else if (animatePref.Equals("once")) michael@0: mImageAnimationModePref = imgIContainer::kLoopOnceAnimMode; michael@0: else // dynamic change to invalid value should act like it does initially michael@0: mImageAnimationModePref = imgIContainer::kNormalAnimMode; michael@0: michael@0: uint32_t bidiOptions = GetBidi(); michael@0: michael@0: int32_t prefInt = michael@0: Preferences::GetInt(IBMBIDI_TEXTDIRECTION_STR, michael@0: GET_BIDI_OPTION_DIRECTION(bidiOptions)); michael@0: SET_BIDI_OPTION_DIRECTION(bidiOptions, prefInt); michael@0: mPrefBidiDirection = prefInt; michael@0: michael@0: prefInt = michael@0: Preferences::GetInt(IBMBIDI_TEXTTYPE_STR, michael@0: GET_BIDI_OPTION_TEXTTYPE(bidiOptions)); michael@0: SET_BIDI_OPTION_TEXTTYPE(bidiOptions, prefInt); michael@0: michael@0: prefInt = michael@0: Preferences::GetInt(IBMBIDI_NUMERAL_STR, michael@0: GET_BIDI_OPTION_NUMERAL(bidiOptions)); michael@0: SET_BIDI_OPTION_NUMERAL(bidiOptions, prefInt); michael@0: michael@0: prefInt = michael@0: Preferences::GetInt(IBMBIDI_SUPPORTMODE_STR, michael@0: GET_BIDI_OPTION_SUPPORT(bidiOptions)); michael@0: SET_BIDI_OPTION_SUPPORT(bidiOptions, prefInt); michael@0: michael@0: // We don't need to force reflow: either we are initializing a new michael@0: // prescontext or we are being called from UpdateAfterPreferencesChanged() michael@0: // which triggers a reflow anyway. michael@0: SetBidi(bidiOptions, false); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::InvalidateThebesLayers() michael@0: { michael@0: if (!mShell) michael@0: return; michael@0: nsIFrame* rootFrame = mShell->FrameManager()->GetRootFrame(); michael@0: if (rootFrame) { michael@0: // FrameLayerBuilder caches invalidation-related values that depend on the michael@0: // appunits-per-dev-pixel ratio, so ensure that all ThebesLayer drawing michael@0: // is completely flushed. michael@0: rootFrame->InvalidateFrameSubtree(); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::AppUnitsPerDevPixelChanged() michael@0: { michael@0: InvalidateThebesLayers(); michael@0: michael@0: if (mDeviceContext) { michael@0: mDeviceContext->FlushFontCache(); michael@0: } michael@0: michael@0: if (HasCachedStyleData()) { michael@0: // All cached style data must be recomputed. michael@0: MediaFeatureValuesChanged(eAlwaysRebuildStyle, NS_STYLE_HINT_REFLOW); michael@0: } michael@0: michael@0: mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel(); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::PreferenceChanged(const char* aPrefName) michael@0: { michael@0: nsDependentCString prefName(aPrefName); michael@0: if (prefName.EqualsLiteral("layout.css.dpi") || michael@0: prefName.EqualsLiteral("layout.css.devPixelsPerPx")) { michael@0: int32_t oldAppUnitsPerDevPixel = AppUnitsPerDevPixel(); michael@0: if (mDeviceContext->CheckDPIChange() && mShell) { michael@0: nsCOMPtr shell = mShell; michael@0: // Re-fetch the view manager's window dimensions in case there's a deferred michael@0: // resize which hasn't affected our mVisibleArea yet michael@0: nscoord oldWidthAppUnits, oldHeightAppUnits; michael@0: nsRefPtr vm = shell->GetViewManager(); michael@0: if (!vm) { michael@0: return; michael@0: } michael@0: vm->GetWindowDimensions(&oldWidthAppUnits, &oldHeightAppUnits); michael@0: float oldWidthDevPixels = oldWidthAppUnits/oldAppUnitsPerDevPixel; michael@0: float oldHeightDevPixels = oldHeightAppUnits/oldAppUnitsPerDevPixel; michael@0: michael@0: nscoord width = NSToCoordRound(oldWidthDevPixels*AppUnitsPerDevPixel()); michael@0: nscoord height = NSToCoordRound(oldHeightDevPixels*AppUnitsPerDevPixel()); michael@0: vm->SetWindowDimensions(width, height); michael@0: michael@0: AppUnitsPerDevPixelChanged(); michael@0: } michael@0: return; michael@0: } michael@0: if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("font."))) { michael@0: // Changes to font family preferences don't change anything in the michael@0: // computed style data, so the style system won't generate a reflow michael@0: // hint for us. We need to do that manually. michael@0: michael@0: // FIXME We could probably also handle changes to michael@0: // browser.display.auto_quality_min_font_size here, but that michael@0: // probably also requires clearing the text run cache, so don't michael@0: // bother (yet, anyway). michael@0: mPrefChangePendingNeedsReflow = true; michael@0: } michael@0: if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("bidi."))) { michael@0: // Changes to bidi prefs need to trigger a reflow (see bug 443629) michael@0: mPrefChangePendingNeedsReflow = true; michael@0: michael@0: // Changes to bidi.numeral also needs to empty the text run cache. michael@0: // This is handled in gfxTextRunWordCache.cpp. michael@0: } michael@0: if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("gfx.font_rendering."))) { michael@0: // Changes to font_rendering prefs need to trigger a reflow michael@0: mPrefChangePendingNeedsReflow = true; michael@0: } michael@0: // we use a zero-delay timer to coalesce multiple pref updates michael@0: if (!mPrefChangedTimer) michael@0: { michael@0: mPrefChangedTimer = do_CreateInstance("@mozilla.org/timer;1"); michael@0: if (!mPrefChangedTimer) michael@0: return; michael@0: mPrefChangedTimer->InitWithFuncCallback(nsPresContext::PrefChangedUpdateTimerCallback, (void*)this, 0, nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: if (prefName.EqualsLiteral("nglayout.debug.paint_flashing") || michael@0: prefName.EqualsLiteral("nglayout.debug.paint_flashing_chrome")) { michael@0: mPaintFlashingInitialized = false; michael@0: return; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::UpdateAfterPreferencesChanged() michael@0: { michael@0: mPrefChangedTimer = nullptr; michael@0: michael@0: nsCOMPtr docShell(mContainer); michael@0: if (docShell && nsIDocShellTreeItem::typeChrome == docShell->ItemType()) { michael@0: return; michael@0: } michael@0: michael@0: // Initialize our state from the user preferences michael@0: GetUserPreferences(); michael@0: michael@0: // update the presShell: tell it to set the preference style rules up michael@0: if (mShell) { michael@0: mShell->SetPreferenceStyleRules(true); michael@0: } michael@0: michael@0: InvalidateThebesLayers(); michael@0: mDeviceContext->FlushFontCache(); michael@0: michael@0: nsChangeHint hint = nsChangeHint(0); michael@0: michael@0: if (mPrefChangePendingNeedsReflow) { michael@0: NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW); michael@0: } michael@0: michael@0: RebuildAllStyleData(hint); michael@0: } michael@0: michael@0: nsresult michael@0: nsPresContext::Init(nsDeviceContext* aDeviceContext) michael@0: { michael@0: NS_ASSERTION(!mInitialized, "attempt to reinit pres context"); michael@0: NS_ENSURE_ARG(aDeviceContext); michael@0: michael@0: mDeviceContext = aDeviceContext; michael@0: michael@0: if (mDeviceContext->SetPixelScale(mFullZoom)) michael@0: mDeviceContext->FlushFontCache(); michael@0: mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel(); michael@0: michael@0: mEventManager = new mozilla::EventStateManager(); michael@0: michael@0: mTransitionManager = new nsTransitionManager(this); michael@0: michael@0: mAnimationManager = new nsAnimationManager(this); michael@0: michael@0: // FIXME: Why is mozilla:: needed? michael@0: mRestyleManager = new mozilla::RestyleManager(this); michael@0: michael@0: if (mDocument->GetDisplayDocument()) { michael@0: NS_ASSERTION(mDocument->GetDisplayDocument()->GetShell() && michael@0: mDocument->GetDisplayDocument()->GetShell()->GetPresContext(), michael@0: "Why are we being initialized?"); michael@0: mRefreshDriver = mDocument->GetDisplayDocument()->GetShell()-> michael@0: GetPresContext()->RefreshDriver(); michael@0: } else { michael@0: nsIDocument* parent = mDocument->GetParentDocument(); michael@0: // Unfortunately, sometimes |parent| here has no presshell because michael@0: // printing screws up things. Assert that in other cases it does, michael@0: // but whenever the shell is null just fall back on using our own michael@0: // refresh driver. michael@0: NS_ASSERTION(!parent || mDocument->IsStaticDocument() || parent->GetShell(), michael@0: "How did we end up with a presshell if our parent doesn't " michael@0: "have one?"); michael@0: if (parent && parent->GetShell()) { michael@0: NS_ASSERTION(parent->GetShell()->GetPresContext(), michael@0: "How did we get a presshell?"); michael@0: michael@0: // We don't have our container set yet at this point michael@0: nsCOMPtr ourItem = mDocument->GetDocShell(); michael@0: if (ourItem) { michael@0: nsCOMPtr parentItem; michael@0: ourItem->GetSameTypeParent(getter_AddRefs(parentItem)); michael@0: if (parentItem) { michael@0: Element* containingElement = michael@0: parent->FindContentForSubDocument(mDocument); michael@0: if (!containingElement->IsXUL() || michael@0: !containingElement-> michael@0: HasAttr(kNameSpaceID_None, michael@0: nsGkAtoms::forceOwnRefreshDriver)) { michael@0: mRefreshDriver = parent->GetShell()->GetPresContext()->RefreshDriver(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: if (!mRefreshDriver) { michael@0: mRefreshDriver = new nsRefreshDriver(this); michael@0: } michael@0: } michael@0: michael@0: // Initialise refresh tick counters for OMTA michael@0: mLastStyleUpdateForAllAnimations = michael@0: mLastUpdateThrottledAnimationStyle = michael@0: mLastUpdateThrottledTransitionStyle = mRefreshDriver->MostRecentRefresh(); michael@0: michael@0: mLangService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID); michael@0: michael@0: // Register callbacks so we're notified when the preferences change michael@0: Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, michael@0: "font.", michael@0: this); michael@0: Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, michael@0: "browser.display.", michael@0: this); michael@0: Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, michael@0: "browser.underline_anchors", michael@0: this); michael@0: Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, michael@0: "browser.anchor_color", michael@0: this); michael@0: Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, michael@0: "browser.active_color", michael@0: this); michael@0: Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, michael@0: "browser.visited_color", michael@0: this); michael@0: Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, michael@0: "image.animation_mode", michael@0: this); michael@0: Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, michael@0: "bidi.", michael@0: this); michael@0: Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, michael@0: "dom.send_after_paint_to_content", michael@0: this); michael@0: Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, michael@0: "gfx.font_rendering.", michael@0: this); michael@0: Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, michael@0: "layout.css.dpi", michael@0: this); michael@0: Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, michael@0: "layout.css.devPixelsPerPx", michael@0: this); michael@0: Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, michael@0: "nglayout.debug.paint_flashing", michael@0: this); michael@0: Preferences::RegisterCallback(nsPresContext::PrefChangedCallback, michael@0: "nglayout.debug.paint_flashing_chrome", michael@0: this); michael@0: michael@0: nsresult rv = mEventManager->Init(); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: mEventManager->SetPresContext(this); michael@0: michael@0: #ifdef DEBUG michael@0: mInitialized = true; michael@0: #endif michael@0: michael@0: mBorderWidthTable[NS_STYLE_BORDER_WIDTH_THIN] = CSSPixelsToAppUnits(1); michael@0: mBorderWidthTable[NS_STYLE_BORDER_WIDTH_MEDIUM] = CSSPixelsToAppUnits(3); michael@0: mBorderWidthTable[NS_STYLE_BORDER_WIDTH_THICK] = CSSPixelsToAppUnits(5); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: // Note: We don't hold a reference on the shell; it has a reference to michael@0: // us michael@0: void michael@0: nsPresContext::SetShell(nsIPresShell* aShell) michael@0: { michael@0: if (mUserFontSet) { michael@0: // Clear out user font set if we have one michael@0: mUserFontSet->Destroy(); michael@0: NS_RELEASE(mUserFontSet); michael@0: } michael@0: michael@0: if (mShell) { michael@0: // Remove ourselves as the charset observer from the shell's doc, because michael@0: // this shell may be going away for good. michael@0: nsIDocument *doc = mShell->GetDocument(); michael@0: if (doc) { michael@0: doc->RemoveCharSetObserver(this); michael@0: } michael@0: } michael@0: michael@0: mShell = aShell; michael@0: michael@0: if (mShell) { michael@0: nsIDocument *doc = mShell->GetDocument(); michael@0: NS_ASSERTION(doc, "expect document here"); michael@0: if (doc) { michael@0: // Have to update PresContext's mDocument before calling any other methods. michael@0: mDocument = doc; michael@0: } michael@0: // Initialize our state from the user preferences, now that we michael@0: // have a presshell, and hence a document. michael@0: GetUserPreferences(); michael@0: michael@0: if (doc) { michael@0: nsIURI *docURI = doc->GetDocumentURI(); michael@0: michael@0: if (IsDynamic() && docURI) { michael@0: bool isChrome = false; michael@0: bool isRes = false; michael@0: docURI->SchemeIs("chrome", &isChrome); michael@0: docURI->SchemeIs("resource", &isRes); michael@0: michael@0: if (!isChrome && !isRes) michael@0: mImageAnimationMode = mImageAnimationModePref; michael@0: else michael@0: mImageAnimationMode = imgIContainer::kNormalAnimMode; michael@0: } michael@0: michael@0: if (mLangService) { michael@0: doc->AddCharSetObserver(this); michael@0: UpdateCharSet(doc->GetDocumentCharacterSet()); michael@0: } michael@0: } michael@0: } else { michael@0: if (mTransitionManager) { michael@0: mTransitionManager->Disconnect(); michael@0: mTransitionManager = nullptr; michael@0: } michael@0: if (mAnimationManager) { michael@0: mAnimationManager->Disconnect(); michael@0: mAnimationManager = nullptr; michael@0: } michael@0: if (mRestyleManager) { michael@0: mRestyleManager->Disconnect(); michael@0: mRestyleManager = nullptr; michael@0: } michael@0: michael@0: if (IsRoot()) { michael@0: // Have to cancel our plugin geometry timer, because the michael@0: // callback for that depends on a non-null presshell. michael@0: static_cast(this)->CancelApplyPluginGeometryTimer(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::DoChangeCharSet(const nsCString& aCharSet) michael@0: { michael@0: UpdateCharSet(aCharSet); michael@0: mDeviceContext->FlushFontCache(); michael@0: RebuildAllStyleData(NS_STYLE_HINT_REFLOW); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::UpdateCharSet(const nsCString& aCharSet) michael@0: { michael@0: if (mLangService) { michael@0: mLanguage = mLangService->LookupCharSet(aCharSet.get()); michael@0: // this will be a language group (or script) code rather than a true language code michael@0: michael@0: // bug 39570: moved from nsLanguageAtomService::LookupCharSet() michael@0: if (mLanguage == nsGkAtoms::Unicode) { michael@0: mLanguage = mLangService->GetLocaleLanguage(); michael@0: } michael@0: ResetCachedFontPrefs(); michael@0: } michael@0: michael@0: switch (GET_BIDI_OPTION_TEXTTYPE(GetBidi())) { michael@0: michael@0: case IBMBIDI_TEXTTYPE_LOGICAL: michael@0: SetVisualMode(false); michael@0: break; michael@0: michael@0: case IBMBIDI_TEXTTYPE_VISUAL: michael@0: SetVisualMode(true); michael@0: break; michael@0: michael@0: case IBMBIDI_TEXTTYPE_CHARSET: michael@0: default: michael@0: SetVisualMode(IsVisualCharset(aCharSet)); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: nsPresContext::Observe(nsISupports* aSubject, michael@0: const char* aTopic, michael@0: const char16_t* aData) michael@0: { michael@0: if (!nsCRT::strcmp(aTopic, "charset")) { michael@0: nsRefPtr runnable = michael@0: new CharSetChangingRunnable(this, NS_LossyConvertUTF16toASCII(aData)); michael@0: return NS_DispatchToCurrentThread(runnable); michael@0: } michael@0: michael@0: NS_WARNING("unrecognized topic in nsPresContext::Observe"); michael@0: return NS_ERROR_FAILURE; michael@0: } michael@0: michael@0: nsPresContext* michael@0: nsPresContext::GetParentPresContext() michael@0: { michael@0: nsIPresShell* shell = GetPresShell(); michael@0: if (shell) { michael@0: nsViewManager* viewManager = shell->GetViewManager(); michael@0: if (viewManager) { michael@0: nsView* view = viewManager->GetRootView(); michael@0: if (view) { michael@0: view = view->GetParent(); // anonymous inner view michael@0: if (view) { michael@0: view = view->GetParent(); // subdocumentframe's view michael@0: if (view) { michael@0: nsIFrame* f = view->GetFrame(); michael@0: if (f) { michael@0: return f->PresContext(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: nsPresContext* michael@0: nsPresContext::GetToplevelContentDocumentPresContext() michael@0: { michael@0: if (IsChrome()) michael@0: return nullptr; michael@0: nsPresContext* pc = this; michael@0: for (;;) { michael@0: nsPresContext* parent = pc->GetParentPresContext(); michael@0: if (!parent || parent->IsChrome()) michael@0: return pc; michael@0: pc = parent; michael@0: } michael@0: } michael@0: michael@0: nsIWidget* michael@0: nsPresContext::GetNearestWidget(nsPoint* aOffset) michael@0: { michael@0: NS_ENSURE_TRUE(mShell, nullptr); michael@0: nsIFrame* frame = mShell->GetRootFrame(); michael@0: NS_ENSURE_TRUE(frame, nullptr); michael@0: return frame->GetView()->GetNearestWidget(aOffset); michael@0: } michael@0: michael@0: nsIWidget* michael@0: nsPresContext::GetRootWidget() michael@0: { michael@0: NS_ENSURE_TRUE(mShell, nullptr); michael@0: nsViewManager* vm = mShell->GetViewManager(); michael@0: if (!vm) { michael@0: return nullptr; michael@0: } michael@0: nsCOMPtr widget; michael@0: vm->GetRootWidget(getter_AddRefs(widget)); michael@0: return widget.get(); michael@0: } michael@0: michael@0: // We may want to replace this with something faster, maybe caching the root prescontext michael@0: nsRootPresContext* michael@0: nsPresContext::GetRootPresContext() michael@0: { michael@0: nsPresContext* pc = this; michael@0: for (;;) { michael@0: nsPresContext* parent = pc->GetParentPresContext(); michael@0: if (!parent) michael@0: break; michael@0: pc = parent; michael@0: } michael@0: return pc->IsRoot() ? static_cast(pc) : nullptr; michael@0: } michael@0: michael@0: nsRootPresContext* michael@0: nsPresContext::GetDisplayRootPresContext() michael@0: { michael@0: nsPresContext* pc = this; michael@0: for (;;) { michael@0: nsPresContext* parent = pc->GetParentPresContext(); michael@0: if (!parent) { michael@0: // Not sure if this is always strictly the parent, but it works for GetRootPresContext michael@0: // where the current pres context has no frames. michael@0: nsIDocument *doc = pc->Document(); michael@0: if (doc) { michael@0: doc = doc->GetParentDocument(); michael@0: if (doc) { michael@0: nsIPresShell* shell = doc->GetShell(); michael@0: if (shell) { michael@0: parent = shell->GetPresContext(); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: if (!parent || parent == pc) michael@0: break; michael@0: pc = parent; michael@0: } michael@0: return pc->IsRoot() ? static_cast(pc) : nullptr; michael@0: } michael@0: michael@0: void michael@0: nsPresContext::CompatibilityModeChanged() michael@0: { michael@0: if (!mShell) michael@0: return; michael@0: michael@0: // enable/disable the QuirkSheet michael@0: mShell->StyleSet()-> michael@0: EnableQuirkStyleSheet(CompatibilityMode() == eCompatibility_NavQuirks); michael@0: } michael@0: michael@0: // Helper function for setting Anim Mode on image michael@0: static void SetImgAnimModeOnImgReq(imgIRequest* aImgReq, uint16_t aMode) michael@0: { michael@0: if (aImgReq) { michael@0: nsCOMPtr imgCon; michael@0: aImgReq->GetImage(getter_AddRefs(imgCon)); michael@0: if (imgCon) { michael@0: imgCon->SetAnimationMode(aMode); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // IMPORTANT: Assumption is that all images for a Presentation michael@0: // have the same Animation Mode (pavlov said this was OK) michael@0: // michael@0: // Walks content and set the animation mode michael@0: // this is a way to turn on/off image animations michael@0: void nsPresContext::SetImgAnimations(nsIContent *aParent, uint16_t aMode) michael@0: { michael@0: nsCOMPtr imgContent(do_QueryInterface(aParent)); michael@0: if (imgContent) { michael@0: nsCOMPtr imgReq; michael@0: imgContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST, michael@0: getter_AddRefs(imgReq)); michael@0: SetImgAnimModeOnImgReq(imgReq, aMode); michael@0: } michael@0: michael@0: uint32_t count = aParent->GetChildCount(); michael@0: for (uint32_t i = 0; i < count; ++i) { michael@0: SetImgAnimations(aParent->GetChildAt(i), aMode); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::SetSMILAnimations(nsIDocument *aDoc, uint16_t aNewMode, michael@0: uint16_t aOldMode) michael@0: { michael@0: if (aDoc->HasAnimationController()) { michael@0: nsSMILAnimationController* controller = aDoc->GetAnimationController(); michael@0: switch (aNewMode) michael@0: { michael@0: case imgIContainer::kNormalAnimMode: michael@0: case imgIContainer::kLoopOnceAnimMode: michael@0: if (aOldMode == imgIContainer::kDontAnimMode) michael@0: controller->Resume(nsSMILTimeContainer::PAUSE_USERPREF); michael@0: break; michael@0: michael@0: case imgIContainer::kDontAnimMode: michael@0: if (aOldMode != imgIContainer::kDontAnimMode) michael@0: controller->Pause(nsSMILTimeContainer::PAUSE_USERPREF); michael@0: break; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::SetImageAnimationModeInternal(uint16_t aMode) michael@0: { michael@0: NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode || michael@0: aMode == imgIContainer::kDontAnimMode || michael@0: aMode == imgIContainer::kLoopOnceAnimMode, "Wrong Animation Mode is being set!"); michael@0: michael@0: // Image animation mode cannot be changed when rendering to a printer. michael@0: if (!IsDynamic()) michael@0: return; michael@0: michael@0: // Now walk the content tree and set the animation mode michael@0: // on all the images. michael@0: if (mShell != nullptr) { michael@0: nsIDocument *doc = mShell->GetDocument(); michael@0: if (doc) { michael@0: doc->StyleImageLoader()->SetAnimationMode(aMode); michael@0: michael@0: Element *rootElement = doc->GetRootElement(); michael@0: if (rootElement) { michael@0: SetImgAnimations(rootElement, aMode); michael@0: } michael@0: SetSMILAnimations(doc, aMode, mImageAnimationMode); michael@0: } michael@0: } michael@0: michael@0: mImageAnimationMode = aMode; michael@0: } michael@0: michael@0: void michael@0: nsPresContext::SetImageAnimationModeExternal(uint16_t aMode) michael@0: { michael@0: SetImageAnimationModeInternal(aMode); michael@0: } michael@0: michael@0: const nsFont* michael@0: nsPresContext::GetDefaultFont(uint8_t aFontID, nsIAtom *aLanguage) const michael@0: { michael@0: const LangGroupFontPrefs *prefs = GetFontPrefsForLang(aLanguage); michael@0: michael@0: const nsFont *font; michael@0: switch (aFontID) { michael@0: // Special (our default variable width font and fixed width font) michael@0: case kPresContext_DefaultVariableFont_ID: michael@0: font = &prefs->mDefaultVariableFont; michael@0: break; michael@0: case kPresContext_DefaultFixedFont_ID: michael@0: font = &prefs->mDefaultFixedFont; michael@0: break; michael@0: // CSS michael@0: case kGenericFont_serif: michael@0: font = &prefs->mDefaultSerifFont; michael@0: break; michael@0: case kGenericFont_sans_serif: michael@0: font = &prefs->mDefaultSansSerifFont; michael@0: break; michael@0: case kGenericFont_monospace: michael@0: font = &prefs->mDefaultMonospaceFont; michael@0: break; michael@0: case kGenericFont_cursive: michael@0: font = &prefs->mDefaultCursiveFont; michael@0: break; michael@0: case kGenericFont_fantasy: michael@0: font = &prefs->mDefaultFantasyFont; michael@0: break; michael@0: default: michael@0: font = nullptr; michael@0: NS_ERROR("invalid arg"); michael@0: break; michael@0: } michael@0: return font; michael@0: } michael@0: michael@0: PRBool michael@0: nsPresContext::FontUseCountReached(const nsFont &font) { michael@0: if (mMaxFonts < 0) { michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: for (PRUint32 i = 0; i < mFontsUsed.Length(); i++) { michael@0: if (mFontsUsed[i].name.Equals(font.name, michael@0: nsCaseInsensitiveStringComparator()) michael@0: // XXX: Style is sometimes filled with garbage?? michael@0: /*&& mFontsUsed[i].style == font.style*/) { michael@0: // seen it before: OK michael@0: return PR_FALSE; michael@0: } michael@0: } michael@0: michael@0: if (mFontsUsed.Length() >= (unsigned)mMaxFonts) { michael@0: return PR_TRUE; michael@0: } michael@0: michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: PRBool michael@0: nsPresContext::FontAttemptCountReached(const nsFont &font) { michael@0: if (mMaxFontAttempts < 0) { michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: for (PRUint32 i = 0; i < mFontsTried.Length(); i++) { michael@0: if (mFontsTried[i].name.Equals(font.name, michael@0: nsCaseInsensitiveStringComparator()) michael@0: // XXX: Style is sometimes filled with garbage?? michael@0: /*&& mFontsTried[i].style == font.style*/) { michael@0: // seen it before: OK michael@0: return PR_FALSE; michael@0: } michael@0: } michael@0: michael@0: if (mFontsTried.Length() >= (unsigned)mMaxFontAttempts) { michael@0: return PR_TRUE; michael@0: } michael@0: michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: void michael@0: nsPresContext::AddFontUse(const nsFont &font) { michael@0: if (mMaxFonts < 0) { michael@0: return; michael@0: } michael@0: michael@0: for (PRUint32 i = 0; i < mFontsUsed.Length(); i++) { michael@0: if (mFontsUsed[i].name.Equals(font.name, michael@0: nsCaseInsensitiveStringComparator()) michael@0: // XXX: Style is sometimes filled with garbage?? michael@0: /*&& mFontsUsed[i].style == font.style*/) { michael@0: // seen it before: OK michael@0: return; michael@0: } michael@0: } michael@0: michael@0: if (mFontsUsed.Length() >= (unsigned)mMaxFonts) { michael@0: return; michael@0: } michael@0: michael@0: mFontsUsed.AppendElement(font); michael@0: return; michael@0: } michael@0: michael@0: void michael@0: nsPresContext::AddFontAttempt(const nsFont &font) { michael@0: if (mMaxFontAttempts < 0) { michael@0: return; michael@0: } michael@0: michael@0: for (PRUint32 i = 0; i < mFontsTried.Length(); i++) { michael@0: if (mFontsTried[i].name.Equals(font.name, michael@0: nsCaseInsensitiveStringComparator()) michael@0: // XXX: Style is sometimes filled with garbage?? michael@0: /*&& mFontsTried[i].style == font.style*/) { michael@0: // seen it before: OK michael@0: return; michael@0: } michael@0: } michael@0: michael@0: if (mFontsTried.Length() >= (unsigned)mMaxFontAttempts) { michael@0: return; michael@0: } michael@0: michael@0: mFontsTried.AppendElement(font); michael@0: return; michael@0: } michael@0: michael@0: void michael@0: nsPresContext::SetFullZoom(float aZoom) michael@0: { michael@0: if (!mShell || mFullZoom == aZoom) { michael@0: return; michael@0: } michael@0: michael@0: // Re-fetch the view manager's window dimensions in case there's a deferred michael@0: // resize which hasn't affected our mVisibleArea yet michael@0: nscoord oldWidthAppUnits, oldHeightAppUnits; michael@0: mShell->GetViewManager()->GetWindowDimensions(&oldWidthAppUnits, &oldHeightAppUnits); michael@0: float oldWidthDevPixels = oldWidthAppUnits / float(mCurAppUnitsPerDevPixel); michael@0: float oldHeightDevPixels = oldHeightAppUnits / float(mCurAppUnitsPerDevPixel); michael@0: mDeviceContext->SetPixelScale(aZoom); michael@0: michael@0: NS_ASSERTION(!mSupressResizeReflow, "two zooms happening at the same time? impossible!"); michael@0: mSupressResizeReflow = true; michael@0: michael@0: mFullZoom = aZoom; michael@0: mShell->GetViewManager()-> michael@0: SetWindowDimensions(NSToCoordRound(oldWidthDevPixels * AppUnitsPerDevPixel()), michael@0: NSToCoordRound(oldHeightDevPixels * AppUnitsPerDevPixel())); michael@0: michael@0: AppUnitsPerDevPixelChanged(); michael@0: michael@0: mSupressResizeReflow = false; michael@0: } michael@0: michael@0: float michael@0: nsPresContext::ScreenWidthInchesForFontInflation(bool* aChanged) michael@0: { michael@0: if (aChanged) { michael@0: *aChanged = false; michael@0: } michael@0: michael@0: nsDeviceContext *dx = DeviceContext(); michael@0: nsRect clientRect; michael@0: dx->GetClientRect(clientRect); // FIXME: GetClientRect looks expensive michael@0: float deviceWidthInches = michael@0: float(clientRect.width) / float(dx->AppUnitsPerPhysicalInch()); michael@0: michael@0: if (mLastFontInflationScreenWidth == -1.0) { michael@0: mLastFontInflationScreenWidth = deviceWidthInches; michael@0: } michael@0: michael@0: if (deviceWidthInches != mLastFontInflationScreenWidth && aChanged) { michael@0: *aChanged = true; michael@0: mLastFontInflationScreenWidth = deviceWidthInches; michael@0: } michael@0: michael@0: return deviceWidthInches; michael@0: } michael@0: michael@0: void michael@0: nsPresContext::SetContainer(nsIDocShell* aDocShell) michael@0: { michael@0: if (aDocShell) { michael@0: mContainer = static_cast(aDocShell)->asWeakPtr(); michael@0: } else { michael@0: mContainer = WeakPtr(); michael@0: } michael@0: UpdateIsChrome(); michael@0: if (mContainer) { michael@0: GetDocumentColorPreferences(); michael@0: } michael@0: } michael@0: michael@0: nsISupports* michael@0: nsPresContext::GetContainerWeakInternal() const michael@0: { michael@0: return static_cast(mContainer); michael@0: } michael@0: michael@0: nsISupports* michael@0: nsPresContext::GetContainerWeakExternal() const michael@0: { michael@0: return GetContainerWeakInternal(); michael@0: } michael@0: michael@0: nsIDocShell* michael@0: nsPresContext::GetDocShell() const michael@0: { michael@0: return mContainer; michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsPresContext::Detach() michael@0: { michael@0: SetContainer(nullptr); michael@0: SetLinkHandler(nullptr); michael@0: if (mShell) { michael@0: mShell->CancelInvalidatePresShellIfHidden(); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::ThrottledTransitionStyleIsUpToDate() const michael@0: { michael@0: return michael@0: mLastUpdateThrottledTransitionStyle == mRefreshDriver->MostRecentRefresh(); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::TickLastUpdateThrottledTransitionStyle() michael@0: { michael@0: mLastUpdateThrottledTransitionStyle = mRefreshDriver->MostRecentRefresh(); michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::ThrottledAnimationStyleIsUpToDate() const michael@0: { michael@0: return michael@0: mLastUpdateThrottledAnimationStyle == mRefreshDriver->MostRecentRefresh(); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::TickLastUpdateThrottledAnimationStyle() michael@0: { michael@0: mLastUpdateThrottledAnimationStyle = mRefreshDriver->MostRecentRefresh(); michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::StyleUpdateForAllAnimationsIsUpToDate() michael@0: { michael@0: return mLastStyleUpdateForAllAnimations == mRefreshDriver->MostRecentRefresh(); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::TickLastStyleUpdateForAllAnimations() michael@0: { michael@0: mLastStyleUpdateForAllAnimations = mRefreshDriver->MostRecentRefresh(); michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::BidiEnabledExternal() const michael@0: { michael@0: return BidiEnabledInternal(); michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::BidiEnabledInternal() const michael@0: { michael@0: return Document()->GetBidiEnabled(); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::SetBidiEnabled() const michael@0: { michael@0: if (mShell) { michael@0: nsIDocument *doc = mShell->GetDocument(); michael@0: if (doc) { michael@0: doc->SetBidiEnabled(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::SetBidi(uint32_t aSource, bool aForceRestyle) michael@0: { michael@0: // Don't do all this stuff unless the options have changed. michael@0: if (aSource == GetBidi()) { michael@0: return; michael@0: } michael@0: michael@0: NS_ASSERTION(!(aForceRestyle && (GetBidi() == 0)), michael@0: "ForceReflow on new prescontext"); michael@0: michael@0: Document()->SetBidiOptions(aSource); michael@0: if (IBMBIDI_TEXTDIRECTION_RTL == GET_BIDI_OPTION_DIRECTION(aSource) michael@0: || IBMBIDI_NUMERAL_HINDI == GET_BIDI_OPTION_NUMERAL(aSource)) { michael@0: SetBidiEnabled(); michael@0: } michael@0: if (IBMBIDI_TEXTTYPE_VISUAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) { michael@0: SetVisualMode(true); michael@0: } michael@0: else if (IBMBIDI_TEXTTYPE_LOGICAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) { michael@0: SetVisualMode(false); michael@0: } michael@0: else { michael@0: nsIDocument* doc = mShell->GetDocument(); michael@0: if (doc) { michael@0: SetVisualMode(IsVisualCharset(doc->GetDocumentCharacterSet())); michael@0: } michael@0: } michael@0: if (aForceRestyle && mShell) { michael@0: // Reconstruct the root document element's frame and its children, michael@0: // because we need to trigger frame reconstruction for direction change. michael@0: RebuildUserFontSet(); michael@0: mShell->ReconstructFrames(); michael@0: } michael@0: } michael@0: michael@0: uint32_t michael@0: nsPresContext::GetBidi() const michael@0: { michael@0: return Document()->GetBidiOptions(); michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::IsTopLevelWindowInactive() michael@0: { michael@0: nsCOMPtr treeItem(mContainer); michael@0: if (!treeItem) michael@0: return false; michael@0: michael@0: nsCOMPtr rootItem; michael@0: treeItem->GetRootTreeItem(getter_AddRefs(rootItem)); michael@0: nsCOMPtr domWindow(do_GetInterface(rootItem)); michael@0: michael@0: return domWindow && !domWindow->IsActive(); michael@0: } michael@0: michael@0: nsITheme* michael@0: nsPresContext::GetTheme() michael@0: { michael@0: if (!sNoTheme && !mTheme) { michael@0: mTheme = do_GetService("@mozilla.org/chrome/chrome-native-theme;1"); michael@0: if (!mTheme) michael@0: sNoTheme = true; michael@0: } michael@0: michael@0: return mTheme; michael@0: } michael@0: michael@0: void michael@0: nsPresContext::ThemeChanged() michael@0: { michael@0: if (!mPendingThemeChanged) { michael@0: sLookAndFeelChanged = true; michael@0: sThemeChanged = true; michael@0: michael@0: nsCOMPtr ev = michael@0: NS_NewRunnableMethod(this, &nsPresContext::ThemeChangedInternal); michael@0: if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { michael@0: mPendingThemeChanged = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::ThemeChangedInternal() michael@0: { michael@0: mPendingThemeChanged = false; michael@0: michael@0: // Tell the theme that it changed, so it can flush any handles to stale theme michael@0: // data. michael@0: if (mTheme && sThemeChanged) { michael@0: mTheme->ThemeChanged(); michael@0: sThemeChanged = false; michael@0: } michael@0: michael@0: // Clear all cached LookAndFeel colors. michael@0: if (sLookAndFeelChanged) { michael@0: LookAndFeel::Refresh(); michael@0: sLookAndFeelChanged = false; michael@0: } michael@0: michael@0: // This will force the system metrics to be generated the next time they're used michael@0: nsCSSRuleProcessor::FreeSystemMetrics(); michael@0: michael@0: // Changes to system metrics can change media queries on them. michael@0: // Changes in theme can change system colors (whose changes are michael@0: // properly reflected in computed style data), system fonts (whose michael@0: // changes are not), and -moz-appearance (whose changes likewise are michael@0: // not), so we need to reflow. michael@0: MediaFeatureValuesChanged(eAlwaysRebuildStyle, NS_STYLE_HINT_REFLOW); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::SysColorChanged() michael@0: { michael@0: if (!mPendingSysColorChanged) { michael@0: sLookAndFeelChanged = true; michael@0: nsCOMPtr ev = michael@0: NS_NewRunnableMethod(this, &nsPresContext::SysColorChangedInternal); michael@0: if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { michael@0: mPendingSysColorChanged = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::SysColorChangedInternal() michael@0: { michael@0: mPendingSysColorChanged = false; michael@0: michael@0: if (sLookAndFeelChanged) { michael@0: // Don't use the cached values for the system colors michael@0: LookAndFeel::Refresh(); michael@0: sLookAndFeelChanged = false; michael@0: } michael@0: michael@0: // Reset default background and foreground colors for the document since michael@0: // they may be using system colors michael@0: GetDocumentColorPreferences(); michael@0: michael@0: // The system color values are computed to colors in the style data, michael@0: // so normal style data comparison is sufficient here. michael@0: RebuildAllStyleData(nsChangeHint(0)); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::UIResolutionChanged() michael@0: { michael@0: if (!mPendingUIResolutionChanged) { michael@0: nsCOMPtr ev = michael@0: NS_NewRunnableMethod(this, &nsPresContext::UIResolutionChangedInternal); michael@0: if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { michael@0: mPendingUIResolutionChanged = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: /*static*/ bool michael@0: nsPresContext::UIResolutionChangedSubdocumentCallback(nsIDocument* aDocument, michael@0: void* aData) michael@0: { michael@0: nsIPresShell* shell = aDocument->GetShell(); michael@0: if (shell) { michael@0: nsPresContext* pc = shell->GetPresContext(); michael@0: if (pc) { michael@0: pc->UIResolutionChangedInternal(); michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: nsPresContext::UIResolutionChangedInternal() michael@0: { michael@0: mPendingUIResolutionChanged = false; michael@0: michael@0: mDeviceContext->CheckDPIChange(); michael@0: if (mCurAppUnitsPerDevPixel != AppUnitsPerDevPixel()) { michael@0: AppUnitsPerDevPixelChanged(); michael@0: } michael@0: michael@0: mDocument->EnumerateSubDocuments(UIResolutionChangedSubdocumentCallback, michael@0: nullptr); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::EmulateMedium(const nsAString& aMediaType) michael@0: { michael@0: nsIAtom* previousMedium = Medium(); michael@0: mIsEmulatingMedia = true; michael@0: michael@0: nsAutoString mediaType; michael@0: nsContentUtils::ASCIIToLower(aMediaType, mediaType); michael@0: michael@0: mMediaEmulated = do_GetAtom(mediaType); michael@0: if (mMediaEmulated != previousMedium && mShell) { michael@0: MediaFeatureValuesChanged(eRebuildStyleIfNeeded, nsChangeHint(0)); michael@0: } michael@0: } michael@0: michael@0: void nsPresContext::StopEmulatingMedium() michael@0: { michael@0: nsIAtom* previousMedium = Medium(); michael@0: mIsEmulatingMedia = false; michael@0: if (Medium() != previousMedium) { michael@0: MediaFeatureValuesChanged(eRebuildStyleIfNeeded, nsChangeHint(0)); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::RebuildAllStyleData(nsChangeHint aExtraHint) michael@0: { michael@0: if (!mShell) { michael@0: // We must have been torn down. Nothing to do here. michael@0: return; michael@0: } michael@0: michael@0: mUsesRootEMUnits = false; michael@0: mUsesViewportUnits = false; michael@0: RebuildUserFontSet(); michael@0: michael@0: RestyleManager()->RebuildAllStyleData(aExtraHint); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint) michael@0: { michael@0: if (!mShell) { michael@0: // We must have been torn down. Nothing to do here. michael@0: return; michael@0: } michael@0: RestyleManager()->PostRebuildAllStyleDataEvent(aExtraHint); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::MediaFeatureValuesChanged(StyleRebuildType aShouldRebuild, michael@0: nsChangeHint aChangeHint) michael@0: { michael@0: NS_ASSERTION(aShouldRebuild == eAlwaysRebuildStyle || aChangeHint == 0, michael@0: "If you don't know if we need a rebuild, how can you provide a hint?"); michael@0: michael@0: mPendingMediaFeatureValuesChanged = false; michael@0: michael@0: // MediumFeaturesChanged updates the applied rules, so it always gets called. michael@0: bool mediaFeaturesDidChange = mShell ? mShell->StyleSet()->MediumFeaturesChanged(this) michael@0: : false; michael@0: michael@0: if (aShouldRebuild == eAlwaysRebuildStyle || michael@0: mediaFeaturesDidChange || michael@0: (mUsesViewportUnits && mPendingViewportChange)) { michael@0: RebuildAllStyleData(aChangeHint); michael@0: } michael@0: michael@0: mPendingViewportChange = false; michael@0: michael@0: if (!nsContentUtils::IsSafeToRunScript()) { michael@0: NS_ABORT_IF_FALSE(mDocument->IsBeingUsedAsImage(), michael@0: "How did we get here? Are we failing to notify " michael@0: "listeners that we should notify?"); michael@0: return; michael@0: } michael@0: michael@0: // Media query list listeners should be notified from a queued task michael@0: // (in HTML5 terms), although we also want to notify them on certain michael@0: // flushes. (We're already running off an event.) michael@0: // michael@0: // Note that we do this after the new style from media queries in michael@0: // style sheets has been computed. michael@0: michael@0: if (!PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists)) { michael@0: // We build a list of all the notifications we're going to send michael@0: // before we send any of them. (The spec says the notifications michael@0: // should be a queued task, so any removals that happen during the michael@0: // notifications shouldn't affect what gets notified.) Furthermore, michael@0: // we hold strong pointers to everything we're going to make michael@0: // notification calls to, since each notification involves calling michael@0: // arbitrary script that might otherwise destroy these objects, or, michael@0: // for that matter, |this|. michael@0: // michael@0: // Note that we intentionally send the notifications to media query michael@0: // list in the order they were created and, for each list, to the michael@0: // listeners in the order added. michael@0: MediaQueryList::NotifyList notifyList; michael@0: for (PRCList *l = PR_LIST_HEAD(&mDOMMediaQueryLists); michael@0: l != &mDOMMediaQueryLists; l = PR_NEXT_LINK(l)) { michael@0: MediaQueryList *mql = static_cast(l); michael@0: mql->MediumFeaturesChanged(notifyList); michael@0: } michael@0: michael@0: if (!notifyList.IsEmpty()) { michael@0: nsPIDOMWindow *win = mDocument->GetInnerWindow(); michael@0: nsCOMPtr et = do_QueryInterface(win); michael@0: nsCxPusher pusher; michael@0: michael@0: for (uint32_t i = 0, i_end = notifyList.Length(); i != i_end; ++i) { michael@0: if (pusher.RePush(et)) { michael@0: nsAutoMicroTask mt; michael@0: MediaQueryList::HandleChangeData &d = notifyList[i]; michael@0: ErrorResult result; michael@0: d.callback->Call(*d.mql, result); michael@0: } michael@0: } michael@0: } michael@0: michael@0: // NOTE: When |notifyList| goes out of scope, our destructor could run. michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::PostMediaFeatureValuesChangedEvent() michael@0: { michael@0: // FIXME: We should probably replace this event with use of michael@0: // nsRefreshDriver::AddStyleFlushObserver (except the pres shell would michael@0: // need to track whether it's been added). michael@0: if (!mPendingMediaFeatureValuesChanged) { michael@0: nsCOMPtr ev = michael@0: NS_NewRunnableMethod(this, &nsPresContext::HandleMediaFeatureValuesChangedEvent); michael@0: if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { michael@0: mPendingMediaFeatureValuesChanged = true; michael@0: mDocument->SetNeedStyleFlush(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::HandleMediaFeatureValuesChangedEvent() michael@0: { michael@0: // Null-check mShell in case the shell has been destroyed (and the michael@0: // event is the only thing holding the pres context alive). michael@0: if (mPendingMediaFeatureValuesChanged && mShell) { michael@0: MediaFeatureValuesChanged(eRebuildStyleIfNeeded); michael@0: } michael@0: } michael@0: michael@0: already_AddRefed michael@0: nsPresContext::MatchMedia(const nsAString& aMediaQueryList) michael@0: { michael@0: nsRefPtr result = new MediaQueryList(this, aMediaQueryList); michael@0: michael@0: // Insert the new item at the end of the linked list. michael@0: PR_INSERT_BEFORE(result, &mDOMMediaQueryLists); michael@0: michael@0: return result.forget(); michael@0: } michael@0: michael@0: nsCompatibility michael@0: nsPresContext::CompatibilityMode() const michael@0: { michael@0: return Document()->GetCompatibilityMode(); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::SetPaginatedScrolling(bool aPaginated) michael@0: { michael@0: if (mType == eContext_PrintPreview || mType == eContext_PageLayout) michael@0: mCanPaginatedScroll = aPaginated; michael@0: } michael@0: michael@0: void michael@0: nsPresContext::SetPrintSettings(nsIPrintSettings *aPrintSettings) michael@0: { michael@0: if (mMedium == nsGkAtoms::print) michael@0: mPrintSettings = aPrintSettings; michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::EnsureVisible() michael@0: { michael@0: nsCOMPtr docShell(mContainer); michael@0: if (docShell) { michael@0: nsCOMPtr cv; michael@0: docShell->GetContentViewer(getter_AddRefs(cv)); michael@0: // Make sure this is the content viewer we belong with michael@0: if (cv) { michael@0: nsRefPtr currentPresContext; michael@0: cv->GetPresContext(getter_AddRefs(currentPresContext)); michael@0: if (currentPresContext == this) { michael@0: // OK, this is us. We want to call Show() on the content viewer. michael@0: nsresult result = cv->Show(); michael@0: if (NS_SUCCEEDED(result)) { michael@0: return true; michael@0: } michael@0: } michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: #ifdef MOZ_REFLOW_PERF michael@0: void michael@0: nsPresContext::CountReflows(const char * aName, nsIFrame * aFrame) michael@0: { michael@0: if (mShell) { michael@0: mShell->CountReflows(aName, aFrame); michael@0: } michael@0: } michael@0: #endif michael@0: michael@0: void michael@0: nsPresContext::UpdateIsChrome() michael@0: { michael@0: mIsChrome = mContainer && michael@0: nsIDocShellTreeItem::typeChrome == mContainer->ItemType(); michael@0: } michael@0: michael@0: /* virtual */ bool michael@0: nsPresContext::HasAuthorSpecifiedRules(nsIFrame *aFrame, uint32_t ruleTypeMask) const michael@0: { michael@0: return michael@0: nsRuleNode::HasAuthorSpecifiedRules(aFrame->StyleContext(), michael@0: ruleTypeMask, michael@0: UseDocumentColors()); michael@0: } michael@0: michael@0: gfxUserFontSet* michael@0: nsPresContext::GetUserFontSetInternal() michael@0: { michael@0: // We want to initialize the user font set lazily the first time the michael@0: // user asks for it, rather than building it too early and forcing michael@0: // rule cascade creation. Thus we try to enforce the invariant that michael@0: // we *never* build the user font set until the first call to michael@0: // GetUserFontSet. However, once it's been requested, we can't wait michael@0: // for somebody to call GetUserFontSet in order to rebuild it (see michael@0: // comments below in RebuildUserFontSet for why). michael@0: #ifdef DEBUG michael@0: bool userFontSetGottenBefore = mGetUserFontSetCalled; michael@0: #endif michael@0: // Set mGetUserFontSetCalled up front, so that FlushUserFontSet will actually michael@0: // flush. michael@0: mGetUserFontSetCalled = true; michael@0: if (mUserFontSetDirty) { michael@0: // If this assertion fails, and there have actually been changes to michael@0: // @font-face rules, then we will call StyleChangeReflow in michael@0: // FlushUserFontSet. If we're in the middle of reflow, michael@0: // that's a bad thing to do, and the caller was responsible for michael@0: // flushing first. If we're not (e.g., in frame construction), it's michael@0: // ok. michael@0: NS_ASSERTION(!userFontSetGottenBefore || !mShell->IsReflowLocked(), michael@0: "FlushUserFontSet should have been called first"); michael@0: FlushUserFontSet(); michael@0: } michael@0: michael@0: return mUserFontSet; michael@0: } michael@0: michael@0: gfxUserFontSet* michael@0: nsPresContext::GetUserFontSetExternal() michael@0: { michael@0: return GetUserFontSetInternal(); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::FlushUserFontSet() michael@0: { michael@0: if (!mShell) { michael@0: return; // we've been torn down michael@0: } michael@0: michael@0: if (!mGetUserFontSetCalled) { michael@0: return; // No one cares about this font set yet, but we want to be careful michael@0: // to not unset our mUserFontSetDirty bit, so when someone really michael@0: // does we'll create it. michael@0: } michael@0: michael@0: if (mUserFontSetDirty) { michael@0: if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) { michael@0: nsTArray rules; michael@0: if (!mShell->StyleSet()->AppendFontFaceRules(this, rules)) { michael@0: if (mUserFontSet) { michael@0: mUserFontSet->Destroy(); michael@0: NS_RELEASE(mUserFontSet); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: bool changed = false; michael@0: michael@0: if (rules.Length() == 0) { michael@0: if (mUserFontSet) { michael@0: mUserFontSet->Destroy(); michael@0: NS_RELEASE(mUserFontSet); michael@0: changed = true; michael@0: } michael@0: } else { michael@0: if (!mUserFontSet) { michael@0: mUserFontSet = new nsUserFontSet(this); michael@0: NS_ADDREF(mUserFontSet); michael@0: } michael@0: changed = mUserFontSet->UpdateRules(rules); michael@0: } michael@0: michael@0: // We need to enqueue a style change reflow (for later) to michael@0: // reflect that we're modifying @font-face rules. (However, michael@0: // without a reflow, nothing will happen to start any downloads michael@0: // that are needed.) michael@0: if (changed) { michael@0: UserFontSetUpdated(); michael@0: } michael@0: } michael@0: michael@0: mUserFontSetDirty = false; michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::RebuildUserFontSet() michael@0: { michael@0: if (!mGetUserFontSetCalled) { michael@0: // We want to lazily build the user font set the first time it's michael@0: // requested (so we don't force creation of rule cascades too michael@0: // early), so don't do anything now. michael@0: return; michael@0: } michael@0: michael@0: mUserFontSetDirty = true; michael@0: mDocument->SetNeedStyleFlush(); michael@0: michael@0: // Somebody has already asked for the user font set, so we need to michael@0: // post an event to rebuild it. Setting the user font set to be dirty michael@0: // and lazily rebuilding it isn't sufficient, since it is only the act michael@0: // of rebuilding it that will trigger the style change reflow that michael@0: // calls GetUserFontSet. (This reflow causes rebuilding of text runs, michael@0: // which starts font loads, whose completion causes another style michael@0: // change reflow). michael@0: if (!mPostedFlushUserFontSet) { michael@0: nsCOMPtr ev = michael@0: NS_NewRunnableMethod(this, &nsPresContext::HandleRebuildUserFontSet); michael@0: if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) { michael@0: mPostedFlushUserFontSet = true; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::UserFontSetUpdated() michael@0: { michael@0: if (!mShell) michael@0: return; michael@0: michael@0: // Changes to the set of available fonts can cause updates to layout by: michael@0: // michael@0: // 1. Changing the font used for text, which changes anything that michael@0: // depends on text measurement, including line breaking and michael@0: // intrinsic widths, and any other parts of layout that depend on michael@0: // font metrics. This requires a style change reflow to update. michael@0: // michael@0: // 2. Changing the value of the 'ex' and 'ch' units in style data, michael@0: // which also depend on font metrics. Updating this information michael@0: // requires rebuilding the rule tree from the top, avoiding the michael@0: // reuse of cached data even when no style rules have changed. michael@0: michael@0: PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::EnsureSafeToHandOutCSSRules() michael@0: { michael@0: nsCSSStyleSheet::EnsureUniqueInnerResult res = michael@0: mShell->StyleSet()->EnsureUniqueInnerOnCSSSheets(); michael@0: if (res == nsCSSStyleSheet::eUniqueInner_AlreadyUnique) { michael@0: // Nothing to do. michael@0: return; michael@0: } michael@0: michael@0: MOZ_ASSERT(res == nsCSSStyleSheet::eUniqueInner_ClonedInner); michael@0: RebuildAllStyleData(nsChangeHint(0)); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::FireDOMPaintEvent(nsInvalidateRequestList* aList) michael@0: { michael@0: nsPIDOMWindow* ourWindow = mDocument->GetWindow(); michael@0: if (!ourWindow) michael@0: return; michael@0: michael@0: nsCOMPtr dispatchTarget = do_QueryInterface(ourWindow); michael@0: nsCOMPtr eventTarget = dispatchTarget; michael@0: if (!IsChrome() && !mSendAfterPaintToContent) { michael@0: // Don't tell the window about this event, it should not know that michael@0: // something happened in a subdocument. Tell only the chrome event handler. michael@0: // (Events sent to the window get propagated to the chrome event handler michael@0: // automatically.) michael@0: dispatchTarget = do_QueryInterface(ourWindow->GetParentTarget()); michael@0: if (!dispatchTarget) { michael@0: return; michael@0: } michael@0: } michael@0: // Events sent to the window get propagated to the chrome event handler michael@0: // automatically. michael@0: nsCOMPtr event; michael@0: // This will empty our list in case dispatching the event causes more damage michael@0: // (hopefully it won't, or we're likely to get an infinite loop! At least michael@0: // it won't be blocking app execution though). michael@0: NS_NewDOMNotifyPaintEvent(getter_AddRefs(event), eventTarget, this, nullptr, michael@0: NS_AFTERPAINT, aList); michael@0: if (!event) { michael@0: return; michael@0: } michael@0: michael@0: // Even if we're not telling the window about the event (so eventTarget is michael@0: // the chrome event handler, not the window), the window is still michael@0: // logically the event target. michael@0: event->SetTarget(eventTarget); michael@0: event->SetTrusted(true); michael@0: EventDispatcher::DispatchDOMEvent(dispatchTarget, nullptr, event, this, michael@0: nullptr); michael@0: } michael@0: michael@0: static bool michael@0: MayHavePaintEventListenerSubdocumentCallback(nsIDocument* aDocument, void* aData) michael@0: { michael@0: bool *result = static_cast(aData); michael@0: nsIPresShell* shell = aDocument->GetShell(); michael@0: if (shell) { michael@0: nsPresContext* pc = shell->GetPresContext(); michael@0: if (pc) { michael@0: *result = pc->MayHavePaintEventListenerInSubDocument(); michael@0: michael@0: // If we found a paint event listener, then we can stop enumerating michael@0: // sub documents. michael@0: return !*result; michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: static bool michael@0: MayHavePaintEventListener(nsPIDOMWindow* aInnerWindow) michael@0: { michael@0: if (!aInnerWindow) michael@0: return false; michael@0: if (aInnerWindow->HasPaintEventListeners()) michael@0: return true; michael@0: michael@0: EventTarget* parentTarget = aInnerWindow->GetParentTarget(); michael@0: if (!parentTarget) michael@0: return false; michael@0: michael@0: EventListenerManager* manager = nullptr; michael@0: if ((manager = parentTarget->GetExistingListenerManager()) && michael@0: manager->MayHavePaintEventListener()) { michael@0: return true; michael@0: } michael@0: michael@0: nsCOMPtr node; michael@0: if (parentTarget != aInnerWindow->GetChromeEventHandler()) { michael@0: nsCOMPtr mm = michael@0: do_QueryInterface(parentTarget); michael@0: if (mm) { michael@0: node = mm->GetOwnerContent(); michael@0: } michael@0: } michael@0: michael@0: if (!node) { michael@0: node = do_QueryInterface(parentTarget); michael@0: } michael@0: if (node) michael@0: return MayHavePaintEventListener(node->OwnerDoc()->GetInnerWindow()); michael@0: michael@0: nsCOMPtr window = do_QueryInterface(parentTarget); michael@0: if (window) michael@0: return MayHavePaintEventListener(window); michael@0: michael@0: nsCOMPtr root = do_QueryInterface(parentTarget); michael@0: EventTarget* tabChildGlobal; michael@0: return root && michael@0: (tabChildGlobal = root->GetParentTarget()) && michael@0: (manager = tabChildGlobal->GetExistingListenerManager()) && michael@0: manager->MayHavePaintEventListener(); michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::MayHavePaintEventListener() michael@0: { michael@0: return ::MayHavePaintEventListener(mDocument->GetInnerWindow()); michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::MayHavePaintEventListenerInSubDocument() michael@0: { michael@0: if (MayHavePaintEventListener()) { michael@0: return true; michael@0: } michael@0: michael@0: bool result = false; michael@0: mDocument->EnumerateSubDocuments(MayHavePaintEventListenerSubdocumentCallback, &result); michael@0: return result; michael@0: } michael@0: michael@0: void michael@0: nsPresContext::NotifyInvalidation(uint32_t aFlags) michael@0: { michael@0: nsIFrame* rootFrame = PresShell()->FrameManager()->GetRootFrame(); michael@0: NotifyInvalidation(rootFrame->GetVisualOverflowRect(), aFlags); michael@0: mAllInvalidated = true; michael@0: } michael@0: michael@0: void michael@0: nsPresContext::NotifyInvalidation(const nsIntRect& aRect, uint32_t aFlags) michael@0: { michael@0: nsRect rect(DevPixelsToAppUnits(aRect.x), michael@0: DevPixelsToAppUnits(aRect.y), michael@0: DevPixelsToAppUnits(aRect.width), michael@0: DevPixelsToAppUnits(aRect.height)); michael@0: NotifyInvalidation(rect, aFlags); michael@0: } michael@0: michael@0: void michael@0: nsPresContext::NotifyInvalidation(const nsRect& aRect, uint32_t aFlags) michael@0: { michael@0: MOZ_ASSERT(GetContainerWeak(), "Invalidation in detached pres context"); michael@0: michael@0: // If there is no paint event listener, then we don't need to fire michael@0: // the asynchronous event. We don't even need to record invalidation. michael@0: // MayHavePaintEventListener is pretty cheap and we could make it michael@0: // even cheaper by providing a more efficient michael@0: // nsPIDOMWindow::GetListenerManager. michael@0: michael@0: if (mAllInvalidated) { michael@0: return; michael@0: } michael@0: michael@0: nsPresContext* pc; michael@0: for (pc = this; pc; pc = pc->GetParentPresContext()) { michael@0: if (pc->mFireAfterPaintEvents) michael@0: break; michael@0: pc->mFireAfterPaintEvents = true; michael@0: } michael@0: if (!pc) { michael@0: nsRootPresContext* rpc = GetRootPresContext(); michael@0: if (rpc) { michael@0: rpc->EnsureEventualDidPaintEvent(); michael@0: } michael@0: } michael@0: michael@0: nsInvalidateRequestList::Request* request = michael@0: mInvalidateRequestsSinceLastPaint.mRequests.AppendElement(); michael@0: if (!request) michael@0: return; michael@0: michael@0: request->mRect = aRect; michael@0: request->mFlags = aFlags; michael@0: } michael@0: michael@0: /* static */ void michael@0: nsPresContext::NotifySubDocInvalidation(ContainerLayer* aContainer, michael@0: const nsIntRegion& aRegion) michael@0: { michael@0: ContainerLayerPresContext *data = michael@0: static_cast( michael@0: aContainer->GetUserData(&gNotifySubDocInvalidationData)); michael@0: if (!data) { michael@0: return; michael@0: } michael@0: michael@0: nsIntPoint topLeft = aContainer->GetVisibleRegion().GetBounds().TopLeft(); michael@0: michael@0: nsIntRegionRectIterator iter(aRegion); michael@0: while (const nsIntRect* r = iter.Next()) { michael@0: nsIntRect rect = *r; michael@0: //PresContext coordinate space is relative to the start of our visible michael@0: // region. Is this really true? This feels like the wrong way to get the right michael@0: // answer. michael@0: rect.MoveBy(-topLeft); michael@0: data->mPresContext->NotifyInvalidation(rect, 0); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::SetNotifySubDocInvalidationData(ContainerLayer* aContainer) michael@0: { michael@0: ContainerLayerPresContext* pres = new ContainerLayerPresContext; michael@0: pres->mPresContext = this; michael@0: aContainer->SetUserData(&gNotifySubDocInvalidationData, pres); michael@0: } michael@0: michael@0: /* static */ void michael@0: nsPresContext::ClearNotifySubDocInvalidationData(ContainerLayer* aContainer) michael@0: { michael@0: aContainer->SetUserData(&gNotifySubDocInvalidationData, nullptr); michael@0: } michael@0: michael@0: struct NotifyDidPaintSubdocumentCallbackClosure { michael@0: uint32_t mFlags; michael@0: bool mNeedsAnotherDidPaintNotification; michael@0: }; michael@0: static bool michael@0: NotifyDidPaintSubdocumentCallback(nsIDocument* aDocument, void* aData) michael@0: { michael@0: NotifyDidPaintSubdocumentCallbackClosure* closure = michael@0: static_cast(aData); michael@0: nsIPresShell* shell = aDocument->GetShell(); michael@0: if (shell) { michael@0: nsPresContext* pc = shell->GetPresContext(); michael@0: if (pc) { michael@0: pc->NotifyDidPaintForSubtree(closure->mFlags); michael@0: if (pc->IsDOMPaintEventPending()) { michael@0: closure->mNeedsAnotherDidPaintNotification = true; michael@0: } michael@0: } michael@0: } michael@0: return true; michael@0: } michael@0: michael@0: class DelayedFireDOMPaintEvent : public nsRunnable { michael@0: public: michael@0: DelayedFireDOMPaintEvent(nsPresContext* aPresContext, michael@0: nsInvalidateRequestList* aList) michael@0: : mPresContext(aPresContext) michael@0: { michael@0: MOZ_ASSERT(mPresContext->GetContainerWeak(), michael@0: "DOMPaintEvent requested for a detached pres context"); michael@0: mList.TakeFrom(aList); michael@0: } michael@0: NS_IMETHOD Run() MOZ_OVERRIDE michael@0: { michael@0: // The pres context might have been detached during the delay - michael@0: // that's fine, just don't fire the event. michael@0: if (mPresContext->GetContainerWeak()) { michael@0: mPresContext->FireDOMPaintEvent(&mList); michael@0: } michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsRefPtr mPresContext; michael@0: nsInvalidateRequestList mList; michael@0: }; michael@0: michael@0: void michael@0: nsPresContext::NotifyDidPaintForSubtree(uint32_t aFlags) michael@0: { michael@0: if (IsRoot()) { michael@0: static_cast(this)->CancelDidPaintTimer(); michael@0: michael@0: if (!mFireAfterPaintEvents) { michael@0: return; michael@0: } michael@0: } michael@0: // Non-root prescontexts fire MozAfterPaint to all their descendants michael@0: // unconditionally, even if no invalidations have been collected. This is michael@0: // because we don't want to eat the cost of collecting invalidations for michael@0: // every subdocument (which would require putting every subdocument in its michael@0: // own layer). michael@0: michael@0: if (aFlags & nsIPresShell::PAINT_LAYERS) { michael@0: mUndeliveredInvalidateRequestsBeforeLastPaint.TakeFrom( michael@0: &mInvalidateRequestsSinceLastPaint); michael@0: mAllInvalidated = false; michael@0: } michael@0: if (aFlags & nsIPresShell::PAINT_COMPOSITE) { michael@0: nsCOMPtr ev = michael@0: new DelayedFireDOMPaintEvent(this, &mUndeliveredInvalidateRequestsBeforeLastPaint); michael@0: nsContentUtils::AddScriptRunner(ev); michael@0: } michael@0: michael@0: NotifyDidPaintSubdocumentCallbackClosure closure = { aFlags, false }; michael@0: mDocument->EnumerateSubDocuments(NotifyDidPaintSubdocumentCallback, &closure); michael@0: michael@0: if (!closure.mNeedsAnotherDidPaintNotification && michael@0: mInvalidateRequestsSinceLastPaint.IsEmpty() && michael@0: mUndeliveredInvalidateRequestsBeforeLastPaint.IsEmpty()) { michael@0: // Nothing more to do for the moment. michael@0: mFireAfterPaintEvents = false; michael@0: } else { michael@0: if (IsRoot()) { michael@0: static_cast(this)->EnsureEventualDidPaintEvent(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::HasCachedStyleData() michael@0: { michael@0: return mShell && mShell->StyleSet()->HasCachedStyleData(); michael@0: } michael@0: michael@0: static bool sGotInterruptEnv = false; michael@0: enum InterruptMode { michael@0: ModeRandom, michael@0: ModeCounter, michael@0: ModeEvent michael@0: }; michael@0: // Controlled by the GECKO_REFLOW_INTERRUPT_MODE env var; allowed values are michael@0: // "random" (except on Windows) or "counter". If neither is used, the mode is michael@0: // ModeEvent. michael@0: static InterruptMode sInterruptMode = ModeEvent; michael@0: #ifndef XP_WIN michael@0: // Used for the "random" mode. Controlled by the GECKO_REFLOW_INTERRUPT_SEED michael@0: // env var. michael@0: static uint32_t sInterruptSeed = 1; michael@0: #endif michael@0: // Used for the "counter" mode. This is the number of unskipped interrupt michael@0: // checks that have to happen before we interrupt. Controlled by the michael@0: // GECKO_REFLOW_INTERRUPT_FREQUENCY env var. michael@0: static uint32_t sInterruptMaxCounter = 10; michael@0: // Used for the "counter" mode. This counts up to sInterruptMaxCounter and is michael@0: // then reset to 0. michael@0: static uint32_t sInterruptCounter; michael@0: // Number of interrupt checks to skip before really trying to interrupt. michael@0: // Controlled by the GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP env var. michael@0: static uint32_t sInterruptChecksToSkip = 200; michael@0: // Number of milliseconds that a reflow should be allowed to run for before we michael@0: // actually allow interruption. Controlled by the michael@0: // GECKO_REFLOW_MIN_NOINTERRUPT_DURATION env var. Can't be initialized here, michael@0: // because TimeDuration/TimeStamp is not safe to use in static constructors.. michael@0: static TimeDuration sInterruptTimeout; michael@0: michael@0: static void GetInterruptEnv() michael@0: { michael@0: char *ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_MODE"); michael@0: if (ev) { michael@0: #ifndef XP_WIN michael@0: if (PL_strcasecmp(ev, "random") == 0) { michael@0: ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_SEED"); michael@0: if (ev) { michael@0: sInterruptSeed = atoi(ev); michael@0: } michael@0: srandom(sInterruptSeed); michael@0: sInterruptMode = ModeRandom; michael@0: } else michael@0: #endif michael@0: if (PL_strcasecmp(ev, "counter") == 0) { michael@0: ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_FREQUENCY"); michael@0: if (ev) { michael@0: sInterruptMaxCounter = atoi(ev); michael@0: } michael@0: sInterruptCounter = 0; michael@0: sInterruptMode = ModeCounter; michael@0: } michael@0: } michael@0: ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP"); michael@0: if (ev) { michael@0: sInterruptChecksToSkip = atoi(ev); michael@0: } michael@0: michael@0: ev = PR_GetEnv("GECKO_REFLOW_MIN_NOINTERRUPT_DURATION"); michael@0: int duration_ms = ev ? atoi(ev) : 100; michael@0: sInterruptTimeout = TimeDuration::FromMilliseconds(duration_ms); michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::HavePendingInputEvent() michael@0: { michael@0: switch (sInterruptMode) { michael@0: #ifndef XP_WIN michael@0: case ModeRandom: michael@0: return (random() & 1); michael@0: #endif michael@0: case ModeCounter: michael@0: if (sInterruptCounter < sInterruptMaxCounter) { michael@0: ++sInterruptCounter; michael@0: return false; michael@0: } michael@0: sInterruptCounter = 0; michael@0: return true; michael@0: default: michael@0: case ModeEvent: { michael@0: nsIFrame* f = PresShell()->GetRootFrame(); michael@0: if (f) { michael@0: nsIWidget* w = f->GetNearestWidget(); michael@0: if (w) { michael@0: return w->HasPendingInputEvent(); michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsPresContext::ReflowStarted(bool aInterruptible) michael@0: { michael@0: #ifdef NOISY_INTERRUPTIBLE_REFLOW michael@0: if (!aInterruptible) { michael@0: printf("STARTING NONINTERRUPTIBLE REFLOW\n"); michael@0: } michael@0: #endif michael@0: // We don't support interrupting in paginated contexts, since page michael@0: // sequences only handle initial reflow michael@0: mInterruptsEnabled = aInterruptible && !IsPaginated() && michael@0: nsLayoutUtils::InterruptibleReflowEnabled(); michael@0: michael@0: // Don't set mHasPendingInterrupt based on HavePendingInputEvent() here. If michael@0: // we ever change that, then we need to update the code in michael@0: // PresShell::DoReflow to only add the just-reflown root to dirty roots if michael@0: // it's actually dirty. Otherwise we can end up adding a root that has no michael@0: // interruptible descendants, just because we detected an interrupt at reflow michael@0: // start. michael@0: mHasPendingInterrupt = false; michael@0: michael@0: mInterruptChecksToSkip = sInterruptChecksToSkip; michael@0: michael@0: if (mInterruptsEnabled) { michael@0: mReflowStartTime = TimeStamp::Now(); michael@0: } michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::CheckForInterrupt(nsIFrame* aFrame) michael@0: { michael@0: if (mHasPendingInterrupt) { michael@0: mShell->FrameNeedsToContinueReflow(aFrame); michael@0: return true; michael@0: } michael@0: michael@0: if (!sGotInterruptEnv) { michael@0: sGotInterruptEnv = true; michael@0: GetInterruptEnv(); michael@0: } michael@0: michael@0: if (!mInterruptsEnabled) { michael@0: return false; michael@0: } michael@0: michael@0: if (mInterruptChecksToSkip > 0) { michael@0: --mInterruptChecksToSkip; michael@0: return false; michael@0: } michael@0: mInterruptChecksToSkip = sInterruptChecksToSkip; michael@0: michael@0: // Don't interrupt if it's been less than sInterruptTimeout since we started michael@0: // the reflow. michael@0: mHasPendingInterrupt = michael@0: TimeStamp::Now() - mReflowStartTime > sInterruptTimeout && michael@0: HavePendingInputEvent() && michael@0: !IsChrome(); michael@0: if (mHasPendingInterrupt) { michael@0: #ifdef NOISY_INTERRUPTIBLE_REFLOW michael@0: printf("*** DETECTED pending interrupt (time=%lld)\n", PR_Now()); michael@0: #endif /* NOISY_INTERRUPTIBLE_REFLOW */ michael@0: mShell->FrameNeedsToContinueReflow(aFrame); michael@0: } michael@0: return mHasPendingInterrupt; michael@0: } michael@0: michael@0: nsIFrame* michael@0: nsPresContext::GetPrimaryFrameFor(nsIContent* aContent) michael@0: { michael@0: NS_PRECONDITION(aContent, "Don't do that"); michael@0: if (GetPresShell() && michael@0: GetPresShell()->GetDocument() == aContent->GetCurrentDoc()) { michael@0: return aContent->GetPrimaryFrame(); michael@0: } michael@0: return nullptr; michael@0: } michael@0: michael@0: michael@0: size_t michael@0: nsPresContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: return mPropertyTable.SizeOfExcludingThis(aMallocSizeOf); michael@0: mLangGroupFontPrefs.SizeOfExcludingThis(aMallocSizeOf); michael@0: michael@0: // Measurement of other members may be added later if DMD finds it is michael@0: // worthwhile. michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::IsRootContentDocument() michael@0: { michael@0: // We are a root content document if: we are not a resource doc, we are michael@0: // not chrome, and we either have no parent or our parent is chrome. michael@0: if (mDocument->IsResourceDoc()) { michael@0: return false; michael@0: } michael@0: if (IsChrome()) { michael@0: return false; michael@0: } michael@0: // We may not have a root frame, so use views. michael@0: nsView* view = PresShell()->GetViewManager()->GetRootView(); michael@0: if (!view) { michael@0: return false; michael@0: } michael@0: view = view->GetParent(); // anonymous inner view michael@0: if (!view) { michael@0: return true; michael@0: } michael@0: view = view->GetParent(); // subdocumentframe's view michael@0: if (!view) { michael@0: return true; michael@0: } michael@0: michael@0: nsIFrame* f = view->GetFrame(); michael@0: return (f && f->PresContext()->IsChrome()); michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::IsCrossProcessRootContentDocument() michael@0: { michael@0: if (!IsRootContentDocument()) { michael@0: return false; michael@0: } michael@0: michael@0: if (XRE_GetProcessType() == GeckoProcessType_Default) { michael@0: return true; michael@0: } michael@0: michael@0: TabChild* tabChild = TabChild::GetFrom(mShell); michael@0: return (tabChild && tabChild->IsRootContentDocument()); michael@0: } michael@0: michael@0: bool nsPresContext::GetPaintFlashing() const michael@0: { michael@0: if (!mPaintFlashingInitialized) { michael@0: bool pref = Preferences::GetBool("nglayout.debug.paint_flashing"); michael@0: if (!pref && IsChrome()) { michael@0: pref = Preferences::GetBool("nglayout.debug.paint_flashing_chrome"); michael@0: } michael@0: mPaintFlashing = pref; michael@0: mPaintFlashingInitialized = true; michael@0: } michael@0: return mPaintFlashing; michael@0: } michael@0: michael@0: int32_t michael@0: nsPresContext::AppUnitsPerDevPixel() const michael@0: { michael@0: return mDeviceContext->AppUnitsPerDevPixel(); michael@0: } michael@0: michael@0: nscoord michael@0: nsPresContext::GfxUnitsToAppUnits(gfxFloat aGfxUnits) const michael@0: { michael@0: return mDeviceContext->GfxUnitsToAppUnits(aGfxUnits); michael@0: } michael@0: michael@0: gfxFloat michael@0: nsPresContext::AppUnitsToGfxUnits(nscoord aAppUnits) const michael@0: { michael@0: return mDeviceContext->AppUnitsToGfxUnits(aAppUnits); michael@0: } michael@0: michael@0: bool michael@0: nsPresContext::IsDeviceSizePageSize() michael@0: { michael@0: bool isDeviceSizePageSize = false; michael@0: nsCOMPtr docShell(mContainer); michael@0: if (docShell) { michael@0: isDeviceSizePageSize = docShell->GetDeviceSizeIsPageSize(); michael@0: } michael@0: return isDeviceSizePageSize; michael@0: } michael@0: michael@0: nsRootPresContext::nsRootPresContext(nsIDocument* aDocument, michael@0: nsPresContextType aType) michael@0: : nsPresContext(aDocument, aType), michael@0: mDOMGeneration(0) michael@0: { michael@0: } michael@0: michael@0: nsRootPresContext::~nsRootPresContext() michael@0: { michael@0: NS_ASSERTION(mRegisteredPlugins.Count() == 0, michael@0: "All plugins should have been unregistered"); michael@0: CancelDidPaintTimer(); michael@0: CancelApplyPluginGeometryTimer(); michael@0: } michael@0: michael@0: /* virtual */ void michael@0: nsRootPresContext::Detach() michael@0: { michael@0: CancelDidPaintTimer(); michael@0: // XXXmats maybe also CancelApplyPluginGeometryTimer(); ? michael@0: nsPresContext::Detach(); michael@0: } michael@0: michael@0: void michael@0: nsRootPresContext::RegisterPluginForGeometryUpdates(nsIContent* aPlugin) michael@0: { michael@0: mRegisteredPlugins.PutEntry(aPlugin); michael@0: } michael@0: michael@0: void michael@0: nsRootPresContext::UnregisterPluginForGeometryUpdates(nsIContent* aPlugin) michael@0: { michael@0: mRegisteredPlugins.RemoveEntry(aPlugin); michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: SetPluginHidden(nsRefPtrHashKey* aEntry, void* userArg) michael@0: { michael@0: nsIFrame* root = static_cast(userArg); michael@0: nsObjectFrame* f = static_cast(aEntry->GetKey()->GetPrimaryFrame()); michael@0: if (!f) { michael@0: NS_WARNING("Null frame in SetPluginHidden"); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: if (!nsLayoutUtils::IsAncestorFrameCrossDoc(root, f)) { michael@0: // f is not managed by this frame so we should ignore it. michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: f->SetEmptyWidgetConfiguration(); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: nsRootPresContext::ComputePluginGeometryUpdates(nsIFrame* aFrame, michael@0: nsDisplayListBuilder* aBuilder, michael@0: nsDisplayList* aList) michael@0: { michael@0: if (mRegisteredPlugins.Count() == 0) { michael@0: return; michael@0: } michael@0: michael@0: // Initially make the next state for each plugin descendant of aFrame be michael@0: // "hidden". Plugins that are visible will have their next state set to michael@0: // unhidden by nsDisplayPlugin::ComputeVisibility. michael@0: mRegisteredPlugins.EnumerateEntries(SetPluginHidden, aFrame); michael@0: michael@0: nsIFrame* rootFrame = FrameManager()->GetRootFrame(); michael@0: michael@0: if (rootFrame && aBuilder->ContainsPluginItem()) { michael@0: aBuilder->SetForPluginGeometry(); michael@0: aBuilder->SetAccurateVisibleRegions(); michael@0: // Merging and flattening has already been done and we should not do it michael@0: // again. nsDisplayScroll(Info)Layer doesn't support trying to flatten michael@0: // again. michael@0: aBuilder->SetAllowMergingAndFlattening(false); michael@0: nsRegion region = rootFrame->GetVisualOverflowRectRelativeToSelf(); michael@0: // nsDisplayPlugin::ComputeVisibility will automatically set a non-hidden michael@0: // widget configuration for the plugin, if it's visible. michael@0: aList->ComputeVisibilityForRoot(aBuilder, ®ion); michael@0: } michael@0: michael@0: #ifdef XP_MACOSX michael@0: // We control painting of Mac plugins, so just apply geometry updates now. michael@0: // This is not happening during a paint event. michael@0: ApplyPluginGeometryUpdates(); michael@0: #else michael@0: InitApplyPluginGeometryTimer(); michael@0: #endif michael@0: } michael@0: michael@0: static void michael@0: ApplyPluginGeometryUpdatesCallback(nsITimer *aTimer, void *aClosure) michael@0: { michael@0: static_cast(aClosure)->ApplyPluginGeometryUpdates(); michael@0: } michael@0: michael@0: void michael@0: nsRootPresContext::InitApplyPluginGeometryTimer() michael@0: { michael@0: if (mApplyPluginGeometryTimer) { michael@0: return; michael@0: } michael@0: michael@0: // We'll apply the plugin geometry updates during the next compositing paint in this michael@0: // presContext (either from nsPresShell::WillPaintWindow or from michael@0: // nsPresShell::DidPaintWindow, depending on the platform). But paints might michael@0: // get optimized away if the old plugin geometry covers the invalid region, michael@0: // so set a backup timer to do this too. We want to make sure this michael@0: // won't fire before our normal paint notifications, if those would michael@0: // update the geometry, so set it for double the refresh driver interval. michael@0: mApplyPluginGeometryTimer = do_CreateInstance("@mozilla.org/timer;1"); michael@0: if (mApplyPluginGeometryTimer) { michael@0: mApplyPluginGeometryTimer-> michael@0: InitWithFuncCallback(ApplyPluginGeometryUpdatesCallback, this, michael@0: nsRefreshDriver::DefaultInterval() * 2, michael@0: nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: } michael@0: michael@0: void michael@0: nsRootPresContext::CancelApplyPluginGeometryTimer() michael@0: { michael@0: if (mApplyPluginGeometryTimer) { michael@0: mApplyPluginGeometryTimer->Cancel(); michael@0: mApplyPluginGeometryTimer = nullptr; michael@0: } michael@0: } michael@0: michael@0: static bool michael@0: HasOverlap(const nsIntPoint& aOffset1, const nsTArray& aClipRects1, michael@0: const nsIntPoint& aOffset2, const nsTArray& aClipRects2) michael@0: { michael@0: nsIntPoint offsetDelta = aOffset1 - aOffset2; michael@0: for (uint32_t i = 0; i < aClipRects1.Length(); ++i) { michael@0: for (uint32_t j = 0; j < aClipRects2.Length(); ++j) { michael@0: if ((aClipRects1[i] + offsetDelta).Intersects(aClipRects2[j])) michael@0: return true; michael@0: } michael@0: } michael@0: return false; michael@0: } michael@0: michael@0: /** michael@0: * Given a list of plugin windows to move to new locations, sort the list michael@0: * so that for each window move, the window moves to a location that michael@0: * does not intersect other windows. This minimizes flicker and repainting. michael@0: * It's not always possible to do this perfectly, since in general michael@0: * we might have cycles. But we do our best. michael@0: * We need to take into account that windows are clipped to particular michael@0: * regions and the clip regions change as the windows are moved. michael@0: */ michael@0: static void michael@0: SortConfigurations(nsTArray* aConfigurations) michael@0: { michael@0: if (aConfigurations->Length() > 10) { michael@0: // Give up, we don't want to get bogged down here michael@0: return; michael@0: } michael@0: michael@0: nsTArray pluginsToMove; michael@0: pluginsToMove.SwapElements(*aConfigurations); michael@0: michael@0: // Our algorithm is quite naive. At each step we try to identify michael@0: // a window that can be moved to its new location that won't overlap michael@0: // any other windows at the new location. If there is no such michael@0: // window, we just move the last window in the list anyway. michael@0: while (!pluginsToMove.IsEmpty()) { michael@0: // Find a window whose destination does not overlap any other window michael@0: uint32_t i; michael@0: for (i = 0; i + 1 < pluginsToMove.Length(); ++i) { michael@0: nsIWidget::Configuration* config = &pluginsToMove[i]; michael@0: bool foundOverlap = false; michael@0: for (uint32_t j = 0; j < pluginsToMove.Length(); ++j) { michael@0: if (i == j) michael@0: continue; michael@0: nsIntRect bounds; michael@0: pluginsToMove[j].mChild->GetBounds(bounds); michael@0: nsAutoTArray clipRects; michael@0: pluginsToMove[j].mChild->GetWindowClipRegion(&clipRects); michael@0: if (HasOverlap(bounds.TopLeft(), clipRects, michael@0: config->mBounds.TopLeft(), michael@0: config->mClipRegion)) { michael@0: foundOverlap = true; michael@0: break; michael@0: } michael@0: } michael@0: if (!foundOverlap) michael@0: break; michael@0: } michael@0: // Note that we always move the last plugin in pluginsToMove, if we michael@0: // can't find any other plugin to move michael@0: aConfigurations->AppendElement(pluginsToMove[i]); michael@0: pluginsToMove.RemoveElementAt(i); michael@0: } michael@0: } michael@0: michael@0: static PLDHashOperator michael@0: PluginDidSetGeometryEnumerator(nsRefPtrHashKey* aEntry, void* userArg) michael@0: { michael@0: nsObjectFrame* f = static_cast(aEntry->GetKey()->GetPrimaryFrame()); michael@0: if (!f) { michael@0: NS_WARNING("Null frame in PluginDidSetGeometryEnumerator"); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: f->DidSetWidgetGeometry(); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: struct PluginGetGeometryUpdateClosure { michael@0: nsTArray mConfigurations; michael@0: }; michael@0: static PLDHashOperator michael@0: PluginGetGeometryUpdate(nsRefPtrHashKey* aEntry, void* userArg) michael@0: { michael@0: PluginGetGeometryUpdateClosure* closure = michael@0: static_cast(userArg); michael@0: nsObjectFrame* f = static_cast(aEntry->GetKey()->GetPrimaryFrame()); michael@0: if (!f) { michael@0: NS_WARNING("Null frame in GetPluginGeometryUpdate"); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: f->GetWidgetConfiguration(&closure->mConfigurations); michael@0: return PL_DHASH_NEXT; michael@0: } michael@0: michael@0: void michael@0: nsRootPresContext::ApplyPluginGeometryUpdates() michael@0: { michael@0: CancelApplyPluginGeometryTimer(); michael@0: michael@0: PluginGetGeometryUpdateClosure closure; michael@0: mRegisteredPlugins.EnumerateEntries(PluginGetGeometryUpdate, &closure); michael@0: // Walk mRegisteredPlugins and ask each plugin for its configuration michael@0: if (!closure.mConfigurations.IsEmpty()) { michael@0: nsIWidget* widget = closure.mConfigurations[0].mChild->GetParent(); michael@0: NS_ASSERTION(widget, "Plugins must have a parent window"); michael@0: SortConfigurations(&closure.mConfigurations); michael@0: widget->ConfigureChildren(closure.mConfigurations); michael@0: } michael@0: mRegisteredPlugins.EnumerateEntries(PluginDidSetGeometryEnumerator, nullptr); michael@0: } michael@0: michael@0: static void michael@0: NotifyDidPaintForSubtreeCallback(nsITimer *aTimer, void *aClosure) michael@0: { michael@0: nsPresContext* presContext = (nsPresContext*)aClosure; michael@0: nsAutoScriptBlocker blockScripts; michael@0: // This is a fallback if we don't get paint events for some reason michael@0: // so we'll just pretend both layer painting and compositing happened. michael@0: presContext->NotifyDidPaintForSubtree( michael@0: nsIPresShell::PAINT_LAYERS | nsIPresShell::PAINT_COMPOSITE); michael@0: } michael@0: michael@0: void michael@0: nsRootPresContext::EnsureEventualDidPaintEvent() michael@0: { michael@0: if (mNotifyDidPaintTimer) michael@0: return; michael@0: mNotifyDidPaintTimer = do_CreateInstance("@mozilla.org/timer;1"); michael@0: if (!mNotifyDidPaintTimer) michael@0: return; michael@0: mNotifyDidPaintTimer->InitWithFuncCallback(NotifyDidPaintForSubtreeCallback, michael@0: (void*)this, 100, nsITimer::TYPE_ONE_SHOT); michael@0: } michael@0: michael@0: void michael@0: nsRootPresContext::AddWillPaintObserver(nsIRunnable* aRunnable) michael@0: { michael@0: if (!mWillPaintFallbackEvent.IsPending()) { michael@0: mWillPaintFallbackEvent = new RunWillPaintObservers(this); michael@0: NS_DispatchToMainThread(mWillPaintFallbackEvent.get()); michael@0: } michael@0: mWillPaintObservers.AppendElement(aRunnable); michael@0: } michael@0: michael@0: /** michael@0: * Run all runnables that need to get called before the next paint. michael@0: */ michael@0: void michael@0: nsRootPresContext::FlushWillPaintObservers() michael@0: { michael@0: mWillPaintFallbackEvent = nullptr; michael@0: nsTArray > observers; michael@0: observers.SwapElements(mWillPaintObservers); michael@0: for (uint32_t i = 0; i < observers.Length(); ++i) { michael@0: observers[i]->Run(); michael@0: } michael@0: } michael@0: michael@0: size_t michael@0: nsRootPresContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const michael@0: { michael@0: return nsPresContext::SizeOfExcludingThis(aMallocSizeOf); michael@0: michael@0: // Measurement of the following members may be added later if DMD finds it is michael@0: // worthwhile: michael@0: // - mNotifyDidPaintTimer michael@0: // - mRegisteredPlugins michael@0: // - mWillPaintObservers michael@0: // - mWillPaintFallbackEvent michael@0: }