Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 /* a presentation of a document, part 1 */
8 #include "mozilla/ArrayUtils.h"
9 #include "mozilla/DebugOnly.h"
10 #include "mozilla/EventDispatcher.h"
11 #include "mozilla/EventStateManager.h"
13 #include "base/basictypes.h"
15 #include "nsCOMPtr.h"
16 #include "nsPresContext.h"
17 #include "nsIPresShell.h"
18 #include "nsDocShell.h"
19 #include "nsIContentViewer.h"
20 #include "nsPIDOMWindow.h"
21 #include "nsStyleSet.h"
22 #include "nsIContent.h"
23 #include "nsIFrame.h"
24 #include "nsIDocument.h"
25 #include "nsIPrintSettings.h"
26 #include "nsILanguageAtomService.h"
27 #include "mozilla/LookAndFeel.h"
28 #include "nsIInterfaceRequestorUtils.h"
29 #include "nsIWeakReferenceUtils.h"
30 #include "nsAutoPtr.h"
31 #include "nsThreadUtils.h"
32 #include "nsFrameManager.h"
33 #include "nsLayoutUtils.h"
34 #include "nsViewManager.h"
35 #include "RestyleManager.h"
36 #include "nsCSSRuleProcessor.h"
37 #include "nsRuleNode.h"
38 #include "gfxPlatform.h"
39 #include "nsCSSRules.h"
40 #include "nsFontFaceLoader.h"
41 #include "mozilla/EventListenerManager.h"
42 #include "prenv.h"
43 #include "nsObjectFrame.h"
44 #include "nsTransitionManager.h"
45 #include "nsAnimationManager.h"
46 #include "mozilla/MemoryReporting.h"
47 #include "mozilla/dom/Element.h"
48 #include "nsIMessageManager.h"
49 #include "mozilla/dom/MediaQueryList.h"
50 #include "nsSMILAnimationController.h"
51 #include "mozilla/css/ImageLoader.h"
52 #include "mozilla/dom/TabChild.h"
53 #include "nsRefreshDriver.h"
54 #include "Layers.h"
55 #include "nsIDOMEvent.h"
56 #include "gfxPrefs.h"
57 #include "nsString.h"
58 #include "nsUnicharUtils.h"
60 #include "nsContentUtils.h"
61 #include "nsCxPusher.h"
62 #include "nsPIWindowRoot.h"
63 #include "mozilla/Preferences.h"
65 // Needed for Start/Stop of Image Animation
66 #include "imgIContainer.h"
67 #include "nsIImageLoadingContent.h"
69 #include "nsCSSParser.h"
70 #include "nsBidiUtils.h"
71 #include "nsServiceManagerUtils.h"
73 #include "URL.h"
75 using namespace mozilla;
76 using namespace mozilla::dom;
77 using namespace mozilla::layers;
79 uint8_t gNotifySubDocInvalidationData;
81 /**
82 * Layer UserData for ContainerLayers that want to be notified
83 * of local invalidations of them and their descendant layers.
84 * Pass a callback to ComputeDifferences to have these called.
85 */
86 class ContainerLayerPresContext : public LayerUserData {
87 public:
88 nsPresContext* mPresContext;
89 };
91 namespace {
93 class CharSetChangingRunnable : public nsRunnable
94 {
95 public:
96 CharSetChangingRunnable(nsPresContext* aPresContext,
97 const nsCString& aCharSet)
98 : mPresContext(aPresContext),
99 mCharSet(aCharSet)
100 {
101 }
103 NS_IMETHOD Run()
104 {
105 mPresContext->DoChangeCharSet(mCharSet);
106 return NS_OK;
107 }
109 private:
110 nsRefPtr<nsPresContext> mPresContext;
111 nsCString mCharSet;
112 };
114 } // anonymous namespace
116 nscolor
117 nsPresContext::MakeColorPref(const nsString& aColor)
118 {
119 nsCSSParser parser;
120 nsCSSValue value;
121 if (!parser.ParseColorString(aColor, nullptr, 0, value)) {
122 // Any better choices?
123 return NS_RGB(0, 0, 0);
124 }
126 nscolor color;
127 return nsRuleNode::ComputeColor(value, this, nullptr, color)
128 ? color
129 : NS_RGB(0, 0, 0);
130 }
132 bool
133 nsPresContext::IsDOMPaintEventPending()
134 {
135 if (mFireAfterPaintEvents) {
136 return true;
137 }
138 if (GetDisplayRootPresContext()->GetRootPresContext()->mRefreshDriver->ViewManagerFlushIsPending()) {
139 // Since we're promising that there will be a MozAfterPaint event
140 // fired, we record an empty invalidation in case display list
141 // invalidation doesn't invalidate anything further.
142 NotifyInvalidation(nsRect(0, 0, 0, 0), 0);
143 NS_ASSERTION(mFireAfterPaintEvents, "Why aren't we planning to fire the event?");
144 return true;
145 }
146 return false;
147 }
149 void
150 nsPresContext::PrefChangedCallback(const char* aPrefName, void* instance_data)
151 {
152 nsRefPtr<nsPresContext> presContext =
153 static_cast<nsPresContext*>(instance_data);
155 NS_ASSERTION(nullptr != presContext, "bad instance data");
156 if (nullptr != presContext) {
157 presContext->PreferenceChanged(aPrefName);
158 }
159 }
161 void
162 nsPresContext::PrefChangedUpdateTimerCallback(nsITimer *aTimer, void *aClosure)
163 {
164 nsPresContext* presContext = (nsPresContext*)aClosure;
165 NS_ASSERTION(presContext != nullptr, "bad instance data");
166 if (presContext)
167 presContext->UpdateAfterPreferencesChanged();
168 }
170 static bool
171 IsVisualCharset(const nsCString& aCharset)
172 {
173 if (aCharset.LowerCaseEqualsLiteral("ibm862") // Hebrew
174 || aCharset.LowerCaseEqualsLiteral("iso-8859-8") ) { // Hebrew
175 return true; // visual text type
176 }
177 else {
178 return false; // logical text type
179 }
180 }
182 // NOTE! nsPresContext::operator new() zeroes out all members, so don't
183 // bother initializing members to 0.
185 nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType)
186 : mType(aType), mDocument(aDocument), mBaseMinFontSize(0),
187 mTextZoom(1.0), mFullZoom(1.0), mLastFontInflationScreenWidth(-1.0),
188 mPageSize(-1, -1), mPPScale(1.0f),
189 mViewportStyleOverflow(NS_STYLE_OVERFLOW_AUTO, NS_STYLE_OVERFLOW_AUTO),
190 mImageAnimationModePref(imgIContainer::kNormalAnimMode),
191 mAllInvalidated(false),
192 mPaintFlashing(false), mPaintFlashingInitialized(false)
193 {
194 // NOTE! nsPresContext::operator new() zeroes out all members, so don't
195 // bother initializing members to 0.
197 mDoScaledTwips = true;
199 SetBackgroundImageDraw(true); // always draw the background
200 SetBackgroundColorDraw(true);
202 mBackgroundColor = NS_RGB(0xFF, 0xFF, 0xFF);
204 mUseDocumentColors = true;
205 mUseDocumentFonts = true;
207 // the minimum font-size is unconstrained by default
209 mLinkColor = NS_RGB(0x00, 0x00, 0xEE);
210 mActiveLinkColor = NS_RGB(0xEE, 0x00, 0x00);
211 mVisitedLinkColor = NS_RGB(0x55, 0x1A, 0x8B);
212 mUnderlineLinks = true;
213 mSendAfterPaintToContent = false;
215 mFocusTextColor = mDefaultColor;
216 mFocusBackgroundColor = mBackgroundColor;
217 mFocusRingWidth = 1;
219 mBodyTextColor = mDefaultColor;
221 if (aType == eContext_Galley) {
222 mMedium = nsGkAtoms::screen;
223 } else {
224 mMedium = nsGkAtoms::print;
225 mPaginated = true;
226 }
227 mMediaEmulated = mMedium;
229 if (!IsDynamic()) {
230 mImageAnimationMode = imgIContainer::kDontAnimMode;
231 mNeverAnimate = true;
232 } else {
233 mImageAnimationMode = imgIContainer::kNormalAnimMode;
234 mNeverAnimate = false;
235 }
236 NS_ASSERTION(mDocument, "Null document");
237 mUserFontSet = nullptr;
238 mUserFontSetDirty = true;
240 // if text perf logging enabled, init stats struct
241 PRLogModuleInfo *log = gfxPlatform::GetLog(eGfxLog_textperf);
242 if (log && log->level >= PR_LOG_WARNING) {
243 mTextPerf = new gfxTextPerfMetrics();
244 }
246 PR_INIT_CLIST(&mDOMMediaQueryLists);
247 }
249 nsPresContext::~nsPresContext()
250 {
251 NS_PRECONDITION(!mShell, "Presshell forgot to clear our mShell pointer");
252 SetShell(nullptr);
254 NS_ABORT_IF_FALSE(PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists),
255 "must not have media query lists left");
257 // Disconnect the refresh driver *after* the transition manager, which
258 // needs it.
259 if (mRefreshDriver && mRefreshDriver->PresContext() == this) {
260 mRefreshDriver->Disconnect();
261 }
263 if (mEventManager) {
264 // unclear if these are needed, but can't hurt
265 mEventManager->NotifyDestroyPresContext(this);
266 mEventManager->SetPresContext(nullptr);
267 }
269 if (mPrefChangedTimer)
270 {
271 mPrefChangedTimer->Cancel();
272 mPrefChangedTimer = nullptr;
273 }
275 // Unregister preference callbacks
276 Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
277 "font.",
278 this);
279 Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
280 "browser.display.",
281 this);
282 Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
283 "browser.underline_anchors",
284 this);
285 Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
286 "browser.anchor_color",
287 this);
288 Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
289 "browser.active_color",
290 this);
291 Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
292 "browser.visited_color",
293 this);
294 Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
295 "image.animation_mode",
296 this);
297 Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
298 "bidi.",
299 this);
300 Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
301 "dom.send_after_paint_to_content",
302 this);
303 Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
304 "gfx.font_rendering.",
305 this);
306 Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
307 "layout.css.dpi",
308 this);
309 Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
310 "layout.css.devPixelsPerPx",
311 this);
312 Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
313 "nglayout.debug.paint_flashing",
314 this);
315 Preferences::UnregisterCallback(nsPresContext::PrefChangedCallback,
316 "nglayout.debug.paint_flashing_chrome",
317 this);
318 }
320 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPresContext)
321 NS_INTERFACE_MAP_ENTRY(nsISupports)
322 NS_INTERFACE_MAP_ENTRY(nsIObserver)
323 NS_INTERFACE_MAP_END
325 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPresContext)
326 NS_IMPL_CYCLE_COLLECTING_RELEASE_WITH_LAST_RELEASE(nsPresContext, LastRelease())
328 void
329 nsPresContext::LastRelease()
330 {
331 if (IsRoot()) {
332 static_cast<nsRootPresContext*>(this)->CancelDidPaintTimer();
333 }
334 }
336 NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext)
338 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext)
339 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDocument);
340 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // not xpcom
341 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mEventManager);
342 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom
344 // We own only the items in mDOMMediaQueryLists that have listeners;
345 // this reference is managed by their AddListener and RemoveListener
346 // methods.
347 for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists);
348 l != &tmp->mDOMMediaQueryLists; l = PR_NEXT_LINK(l)) {
349 MediaQueryList *mql = static_cast<MediaQueryList*>(l);
350 if (mql->HasListeners()) {
351 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb, "mDOMMediaQueryLists item");
352 cb.NoteXPCOMChild(mql);
353 }
354 }
356 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mTheme); // a service
357 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mLangService); // a service
358 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrintSettings);
359 NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mPrefChangedTimer);
360 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
362 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext)
363 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDocument);
364 NS_IMPL_CYCLE_COLLECTION_UNLINK(mDeviceContext); // worth bothering?
365 if (tmp->mEventManager) {
366 // unclear if these are needed, but can't hurt
367 tmp->mEventManager->NotifyDestroyPresContext(tmp);
368 tmp->mEventManager->SetPresContext(nullptr);
369 tmp->mEventManager = nullptr;
370 }
372 // We own only the items in mDOMMediaQueryLists that have listeners;
373 // this reference is managed by their AddListener and RemoveListener
374 // methods.
375 for (PRCList *l = PR_LIST_HEAD(&tmp->mDOMMediaQueryLists);
376 l != &tmp->mDOMMediaQueryLists; ) {
377 PRCList *next = PR_NEXT_LINK(l);
378 MediaQueryList *mql = static_cast<MediaQueryList*>(l);
379 mql->RemoveAllListeners();
380 l = next;
381 }
383 // NS_RELEASE(tmp->mLanguage); // an atom
385 // NS_IMPL_CYCLE_COLLECTION_UNLINK(mTheme); // a service
386 // NS_IMPL_CYCLE_COLLECTION_UNLINK(mLangService); // a service
387 NS_IMPL_CYCLE_COLLECTION_UNLINK(mPrintSettings);
388 if (tmp->mPrefChangedTimer)
389 {
390 tmp->mPrefChangedTimer->Cancel();
391 tmp->mPrefChangedTimer = nullptr;
392 }
393 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
396 #define MAKE_FONT_PREF_KEY(_pref, _s0, _s1) \
397 _pref.Assign(_s0); \
398 _pref.Append(_s1);
400 static const char* const kGenericFont[] = {
401 ".variable.",
402 ".fixed.",
403 ".serif.",
404 ".sans-serif.",
405 ".monospace.",
406 ".cursive.",
407 ".fantasy."
408 };
410 // whether no native theme service exists;
411 // if this gets set to true, we'll stop asking for it.
412 static bool sNoTheme = false;
414 // Set to true when LookAndFeelChanged needs to be called. This is used
415 // because the look and feel is a service, so there's no need to notify it from
416 // more than one prescontext.
417 static bool sLookAndFeelChanged;
419 // Set to true when ThemeChanged needs to be called on mTheme. This is used
420 // because mTheme is a service, so there's no need to notify it from more than
421 // one prescontext.
422 static bool sThemeChanged;
424 const nsPresContext::LangGroupFontPrefs*
425 nsPresContext::GetFontPrefsForLang(nsIAtom *aLanguage) const
426 {
427 // Get language group for aLanguage:
429 nsresult rv = NS_OK;
430 nsIAtom *langGroupAtom = nullptr;
431 if (!aLanguage) {
432 aLanguage = mLanguage;
433 }
434 if (aLanguage && mLangService) {
435 langGroupAtom = mLangService->GetLanguageGroup(aLanguage, &rv);
436 }
437 if (NS_FAILED(rv) || !langGroupAtom) {
438 langGroupAtom = nsGkAtoms::x_western; // Assume x-western is safe...
439 }
441 // Look for cached prefs for this lang group.
442 // Most documents will only use one (or very few) language groups. Rather
443 // than have the overhead of a hash lookup, we simply look along what will
444 // typically be a very short (usually of length 1) linked list. There are 31
445 // language groups, so in the worst case scenario we'll need to traverse 31
446 // link items.
448 LangGroupFontPrefs *prefs =
449 const_cast<LangGroupFontPrefs*>(&mLangGroupFontPrefs);
450 if (prefs->mLangGroup) { // if initialized
451 DebugOnly<uint32_t> count = 0;
452 for (;;) {
453 NS_ASSERTION(++count < 35, "Lang group count exceeded!!!");
454 if (prefs->mLangGroup == langGroupAtom) {
455 return prefs;
456 }
457 if (!prefs->mNext) {
458 break;
459 }
460 prefs = prefs->mNext;
461 }
463 // nothing cached, so go on and fetch the prefs for this lang group:
464 prefs = prefs->mNext = new LangGroupFontPrefs;
465 }
467 prefs->mLangGroup = langGroupAtom;
469 /* Fetch the font prefs to be used -- see bug 61883 for details.
470 Not all prefs are needed upfront. Some are fallback prefs intended
471 for the GFX font sub-system...
473 1) unit : assumed to be the same for all language groups -------------
474 font.size.unit = px | pt XXX could be folded in the size... bug 90440
476 2) attributes for generic fonts --------------------------------------
477 font.default.[langGroup] = serif | sans-serif - fallback generic font
478 font.name.[generic].[langGroup] = current user' selected font on the pref dialog
479 font.name-list.[generic].[langGroup] = fontname1, fontname2, ... [factory pre-built list]
480 font.size.[generic].[langGroup] = integer - settable by the user
481 font.size-adjust.[generic].[langGroup] = "float" - settable by the user
482 font.minimum-size.[langGroup] = integer - settable by the user
483 */
485 nsAutoCString langGroup;
486 langGroupAtom->ToUTF8String(langGroup);
488 prefs->mDefaultVariableFont.size = CSSPixelsToAppUnits(16);
489 prefs->mDefaultFixedFont.size = CSSPixelsToAppUnits(13);
491 nsAutoCString pref;
493 // get the current applicable font-size unit
494 enum {eUnit_unknown = -1, eUnit_px, eUnit_pt};
495 int32_t unit = eUnit_px;
497 nsAdoptingCString cvalue =
498 Preferences::GetCString("font.size.unit");
500 if (!cvalue.IsEmpty()) {
501 if (cvalue.Equals("px")) {
502 unit = eUnit_px;
503 }
504 else if (cvalue.Equals("pt")) {
505 unit = eUnit_pt;
506 }
507 else {
508 // XXX should really send this warning to the user (Error Console?).
509 // And just default to unit = eUnit_px?
510 NS_WARNING("unexpected font-size unit -- expected: 'px' or 'pt'");
511 unit = eUnit_unknown;
512 }
513 }
515 // get font.minimum-size.[langGroup]
517 MAKE_FONT_PREF_KEY(pref, "font.minimum-size.", langGroup);
519 int32_t size = Preferences::GetInt(pref.get());
520 if (unit == eUnit_px) {
521 prefs->mMinimumFontSize = CSSPixelsToAppUnits(size);
522 }
523 else if (unit == eUnit_pt) {
524 prefs->mMinimumFontSize = CSSPointsToAppUnits(size);
525 }
527 nsFont* fontTypes[] = {
528 &prefs->mDefaultVariableFont,
529 &prefs->mDefaultFixedFont,
530 &prefs->mDefaultSerifFont,
531 &prefs->mDefaultSansSerifFont,
532 &prefs->mDefaultMonospaceFont,
533 &prefs->mDefaultCursiveFont,
534 &prefs->mDefaultFantasyFont
535 };
536 static_assert(MOZ_ARRAY_LENGTH(fontTypes) == eDefaultFont_COUNT,
537 "FontTypes array count is not correct");
539 // Get attributes specific to each generic font. We do not get the user's
540 // generic-font-name-to-specific-family-name preferences because its the
541 // generic name that should be fed into the cascade. It is up to the GFX
542 // code to look up the font prefs to convert generic names to specific
543 // family names as necessary.
544 nsAutoCString generic_dot_langGroup;
545 for (uint32_t eType = 0; eType < ArrayLength(fontTypes); ++eType) {
546 generic_dot_langGroup.Assign(kGenericFont[eType]);
547 generic_dot_langGroup.Append(langGroup);
549 nsFont* font = fontTypes[eType];
551 // set the default variable font (the other fonts are seen as 'generic' fonts
552 // in GFX and will be queried there when hunting for alternative fonts)
553 if (eType == eDefaultFont_Variable) {
554 MAKE_FONT_PREF_KEY(pref, "font.name.variable.", langGroup);
556 nsAdoptingString value = Preferences::GetString(pref.get());
557 if (!value.IsEmpty()) {
558 prefs->mDefaultVariableFont.name.Assign(value);
559 }
560 else {
561 MAKE_FONT_PREF_KEY(pref, "font.default.", langGroup);
562 value = Preferences::GetString(pref.get());
563 if (!value.IsEmpty()) {
564 prefs->mDefaultVariableFont.name.Assign(value);
565 }
566 }
567 }
568 else {
569 if (eType == eDefaultFont_Monospace) {
570 // This takes care of the confusion whereby people often expect "monospace"
571 // to have the same default font-size as "-moz-fixed" (this tentative
572 // size may be overwritten with the specific value for "monospace" when
573 // "font.size.monospace.[langGroup]" is read -- see below)
574 prefs->mDefaultMonospaceFont.size = prefs->mDefaultFixedFont.size;
575 }
576 else if (eType != eDefaultFont_Fixed) {
577 // all the other generic fonts are initialized with the size of the
578 // variable font, but their specific size can supersede later -- see below
579 font->size = prefs->mDefaultVariableFont.size;
580 }
581 }
583 // Bug 84398: for spec purists, a different font-size only applies to the
584 // .variable. and .fixed. fonts and the other fonts should get |font-size-adjust|.
585 // The problem is that only GfxWin has the support for |font-size-adjust|. So for
586 // parity, we enable the ability to set a different font-size on all platforms.
588 // get font.size.[generic].[langGroup]
589 // size=0 means 'Auto', i.e., generic fonts retain the size of the variable font
590 MAKE_FONT_PREF_KEY(pref, "font.size", generic_dot_langGroup);
591 size = Preferences::GetInt(pref.get());
592 if (size > 0) {
593 if (unit == eUnit_px) {
594 font->size = CSSPixelsToAppUnits(size);
595 }
596 else if (unit == eUnit_pt) {
597 font->size = CSSPointsToAppUnits(size);
598 }
599 }
601 // get font.size-adjust.[generic].[langGroup]
602 // XXX only applicable on GFX ports that handle |font-size-adjust|
603 MAKE_FONT_PREF_KEY(pref, "font.size-adjust", generic_dot_langGroup);
604 cvalue = Preferences::GetCString(pref.get());
605 if (!cvalue.IsEmpty()) {
606 font->sizeAdjust = (float)atof(cvalue.get());
607 }
609 #ifdef DEBUG_rbs
610 printf("%s Family-list:%s size:%d sizeAdjust:%.2f\n",
611 generic_dot_langGroup.get(),
612 NS_ConvertUTF16toUTF8(font->name).get(), font->size,
613 font->sizeAdjust);
614 #endif
615 }
617 return prefs;
618 }
620 void
621 nsPresContext::GetDocumentColorPreferences()
622 {
623 // Make sure the preferences are initialized. In the normal run,
624 // they would already be, because gfxPlatform would have been created,
625 // but in some reference tests, that is not the case.
626 gfxPrefs::GetSingleton();
628 int32_t useAccessibilityTheme = 0;
629 bool usePrefColors = true;
630 bool isChromeDocShell = false;
632 nsIDocument* doc = mDocument->GetDisplayDocument();
633 if (doc && doc->GetDocShell()) {
634 isChromeDocShell = nsIDocShellTreeItem::typeChrome ==
635 doc->GetDocShell()->ItemType();
636 } else {
637 nsCOMPtr<nsIDocShellTreeItem> docShell(mContainer);
638 if (docShell) {
639 isChromeDocShell = nsIDocShellTreeItem::typeChrome == docShell->ItemType();
640 }
641 }
643 mIsChromeOriginImage = mDocument->IsBeingUsedAsImage() &&
644 IsChromeURI(mDocument->GetDocumentURI());
646 if (isChromeDocShell || mIsChromeOriginImage) {
647 usePrefColors = false;
648 } else {
649 useAccessibilityTheme =
650 LookAndFeel::GetInt(LookAndFeel::eIntID_UseAccessibilityTheme, 0);
651 usePrefColors = !useAccessibilityTheme;
652 }
653 if (usePrefColors) {
654 usePrefColors =
655 !Preferences::GetBool("browser.display.use_system_colors", false);
656 }
658 if (usePrefColors) {
659 nsAdoptingString colorStr =
660 Preferences::GetString("browser.display.foreground_color");
662 if (!colorStr.IsEmpty()) {
663 mDefaultColor = MakeColorPref(colorStr);
664 }
666 colorStr = Preferences::GetString("browser.display.background_color");
668 if (!colorStr.IsEmpty()) {
669 mBackgroundColor = MakeColorPref(colorStr);
670 }
671 }
672 else {
673 mDefaultColor =
674 LookAndFeel::GetColor(LookAndFeel::eColorID_WindowForeground,
675 NS_RGB(0x00, 0x00, 0x00));
676 mBackgroundColor =
677 LookAndFeel::GetColor(LookAndFeel::eColorID_WindowBackground,
678 NS_RGB(0xFF, 0xFF, 0xFF));
679 }
681 // Wherever we got the default background color from, ensure it is
682 // opaque.
683 mBackgroundColor = NS_ComposeColors(NS_RGB(0xFF, 0xFF, 0xFF),
684 mBackgroundColor);
686 mUseDocumentColors = !useAccessibilityTheme &&
687 Preferences::GetBool("browser.display.use_document_colors",
688 mUseDocumentColors);
689 }
691 void
692 nsPresContext::GetUserPreferences()
693 {
694 if (!GetPresShell()) {
695 // No presshell means nothing to do here. We'll do this when we
696 // get a presshell.
697 return;
698 }
700 mAutoQualityMinFontSizePixelsPref =
701 Preferences::GetInt("browser.display.auto_quality_min_font_size");
703 // * document colors
704 GetDocumentColorPreferences();
706 mSendAfterPaintToContent =
707 Preferences::GetBool("dom.send_after_paint_to_content",
708 mSendAfterPaintToContent);
710 // * link colors
711 mUnderlineLinks =
712 Preferences::GetBool("browser.underline_anchors", mUnderlineLinks);
714 nsAdoptingString colorStr = Preferences::GetString("browser.anchor_color");
716 if (!colorStr.IsEmpty()) {
717 mLinkColor = MakeColorPref(colorStr);
718 }
720 colorStr = Preferences::GetString("browser.active_color");
722 if (!colorStr.IsEmpty()) {
723 mActiveLinkColor = MakeColorPref(colorStr);
724 }
726 colorStr = Preferences::GetString("browser.visited_color");
728 if (!colorStr.IsEmpty()) {
729 mVisitedLinkColor = MakeColorPref(colorStr);
730 }
732 mUseFocusColors =
733 Preferences::GetBool("browser.display.use_focus_colors", mUseFocusColors);
735 mFocusTextColor = mDefaultColor;
736 mFocusBackgroundColor = mBackgroundColor;
738 colorStr = Preferences::GetString("browser.display.focus_text_color");
740 if (!colorStr.IsEmpty()) {
741 mFocusTextColor = MakeColorPref(colorStr);
742 }
744 colorStr = Preferences::GetString("browser.display.focus_background_color");
746 if (!colorStr.IsEmpty()) {
747 mFocusBackgroundColor = MakeColorPref(colorStr);
748 }
750 mFocusRingWidth =
751 Preferences::GetInt("browser.display.focus_ring_width", mFocusRingWidth);
753 mFocusRingOnAnything =
754 Preferences::GetBool("browser.display.focus_ring_on_anything",
755 mFocusRingOnAnything);
757 mFocusRingStyle =
758 Preferences::GetInt("browser.display.focus_ring_style", mFocusRingStyle);
760 mBodyTextColor = mDefaultColor;
762 // * use fonts?
763 mUseDocumentFonts =
764 Preferences::GetInt("browser.display.use_document_fonts") != 0;
766 mMaxFonts =
767 Preferences::GetInt("browser.display.max_font_count", -1);
769 mMaxFontAttempts =
770 Preferences::GetInt("browser.display.max_font_attempts", -1);
772 mPrefScrollbarSide = Preferences::GetInt("layout.scrollbar.side");
774 ResetCachedFontPrefs();
776 // * image animation
777 const nsAdoptingCString& animatePref =
778 Preferences::GetCString("image.animation_mode");
779 if (animatePref.Equals("normal"))
780 mImageAnimationModePref = imgIContainer::kNormalAnimMode;
781 else if (animatePref.Equals("none"))
782 mImageAnimationModePref = imgIContainer::kDontAnimMode;
783 else if (animatePref.Equals("once"))
784 mImageAnimationModePref = imgIContainer::kLoopOnceAnimMode;
785 else // dynamic change to invalid value should act like it does initially
786 mImageAnimationModePref = imgIContainer::kNormalAnimMode;
788 uint32_t bidiOptions = GetBidi();
790 int32_t prefInt =
791 Preferences::GetInt(IBMBIDI_TEXTDIRECTION_STR,
792 GET_BIDI_OPTION_DIRECTION(bidiOptions));
793 SET_BIDI_OPTION_DIRECTION(bidiOptions, prefInt);
794 mPrefBidiDirection = prefInt;
796 prefInt =
797 Preferences::GetInt(IBMBIDI_TEXTTYPE_STR,
798 GET_BIDI_OPTION_TEXTTYPE(bidiOptions));
799 SET_BIDI_OPTION_TEXTTYPE(bidiOptions, prefInt);
801 prefInt =
802 Preferences::GetInt(IBMBIDI_NUMERAL_STR,
803 GET_BIDI_OPTION_NUMERAL(bidiOptions));
804 SET_BIDI_OPTION_NUMERAL(bidiOptions, prefInt);
806 prefInt =
807 Preferences::GetInt(IBMBIDI_SUPPORTMODE_STR,
808 GET_BIDI_OPTION_SUPPORT(bidiOptions));
809 SET_BIDI_OPTION_SUPPORT(bidiOptions, prefInt);
811 // We don't need to force reflow: either we are initializing a new
812 // prescontext or we are being called from UpdateAfterPreferencesChanged()
813 // which triggers a reflow anyway.
814 SetBidi(bidiOptions, false);
815 }
817 void
818 nsPresContext::InvalidateThebesLayers()
819 {
820 if (!mShell)
821 return;
822 nsIFrame* rootFrame = mShell->FrameManager()->GetRootFrame();
823 if (rootFrame) {
824 // FrameLayerBuilder caches invalidation-related values that depend on the
825 // appunits-per-dev-pixel ratio, so ensure that all ThebesLayer drawing
826 // is completely flushed.
827 rootFrame->InvalidateFrameSubtree();
828 }
829 }
831 void
832 nsPresContext::AppUnitsPerDevPixelChanged()
833 {
834 InvalidateThebesLayers();
836 if (mDeviceContext) {
837 mDeviceContext->FlushFontCache();
838 }
840 if (HasCachedStyleData()) {
841 // All cached style data must be recomputed.
842 MediaFeatureValuesChanged(eAlwaysRebuildStyle, NS_STYLE_HINT_REFLOW);
843 }
845 mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel();
846 }
848 void
849 nsPresContext::PreferenceChanged(const char* aPrefName)
850 {
851 nsDependentCString prefName(aPrefName);
852 if (prefName.EqualsLiteral("layout.css.dpi") ||
853 prefName.EqualsLiteral("layout.css.devPixelsPerPx")) {
854 int32_t oldAppUnitsPerDevPixel = AppUnitsPerDevPixel();
855 if (mDeviceContext->CheckDPIChange() && mShell) {
856 nsCOMPtr<nsIPresShell> shell = mShell;
857 // Re-fetch the view manager's window dimensions in case there's a deferred
858 // resize which hasn't affected our mVisibleArea yet
859 nscoord oldWidthAppUnits, oldHeightAppUnits;
860 nsRefPtr<nsViewManager> vm = shell->GetViewManager();
861 if (!vm) {
862 return;
863 }
864 vm->GetWindowDimensions(&oldWidthAppUnits, &oldHeightAppUnits);
865 float oldWidthDevPixels = oldWidthAppUnits/oldAppUnitsPerDevPixel;
866 float oldHeightDevPixels = oldHeightAppUnits/oldAppUnitsPerDevPixel;
868 nscoord width = NSToCoordRound(oldWidthDevPixels*AppUnitsPerDevPixel());
869 nscoord height = NSToCoordRound(oldHeightDevPixels*AppUnitsPerDevPixel());
870 vm->SetWindowDimensions(width, height);
872 AppUnitsPerDevPixelChanged();
873 }
874 return;
875 }
876 if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("font."))) {
877 // Changes to font family preferences don't change anything in the
878 // computed style data, so the style system won't generate a reflow
879 // hint for us. We need to do that manually.
881 // FIXME We could probably also handle changes to
882 // browser.display.auto_quality_min_font_size here, but that
883 // probably also requires clearing the text run cache, so don't
884 // bother (yet, anyway).
885 mPrefChangePendingNeedsReflow = true;
886 }
887 if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("bidi."))) {
888 // Changes to bidi prefs need to trigger a reflow (see bug 443629)
889 mPrefChangePendingNeedsReflow = true;
891 // Changes to bidi.numeral also needs to empty the text run cache.
892 // This is handled in gfxTextRunWordCache.cpp.
893 }
894 if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("gfx.font_rendering."))) {
895 // Changes to font_rendering prefs need to trigger a reflow
896 mPrefChangePendingNeedsReflow = true;
897 }
898 // we use a zero-delay timer to coalesce multiple pref updates
899 if (!mPrefChangedTimer)
900 {
901 mPrefChangedTimer = do_CreateInstance("@mozilla.org/timer;1");
902 if (!mPrefChangedTimer)
903 return;
904 mPrefChangedTimer->InitWithFuncCallback(nsPresContext::PrefChangedUpdateTimerCallback, (void*)this, 0, nsITimer::TYPE_ONE_SHOT);
905 }
906 if (prefName.EqualsLiteral("nglayout.debug.paint_flashing") ||
907 prefName.EqualsLiteral("nglayout.debug.paint_flashing_chrome")) {
908 mPaintFlashingInitialized = false;
909 return;
910 }
911 }
913 void
914 nsPresContext::UpdateAfterPreferencesChanged()
915 {
916 mPrefChangedTimer = nullptr;
918 nsCOMPtr<nsIDocShellTreeItem> docShell(mContainer);
919 if (docShell && nsIDocShellTreeItem::typeChrome == docShell->ItemType()) {
920 return;
921 }
923 // Initialize our state from the user preferences
924 GetUserPreferences();
926 // update the presShell: tell it to set the preference style rules up
927 if (mShell) {
928 mShell->SetPreferenceStyleRules(true);
929 }
931 InvalidateThebesLayers();
932 mDeviceContext->FlushFontCache();
934 nsChangeHint hint = nsChangeHint(0);
936 if (mPrefChangePendingNeedsReflow) {
937 NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW);
938 }
940 RebuildAllStyleData(hint);
941 }
943 nsresult
944 nsPresContext::Init(nsDeviceContext* aDeviceContext)
945 {
946 NS_ASSERTION(!mInitialized, "attempt to reinit pres context");
947 NS_ENSURE_ARG(aDeviceContext);
949 mDeviceContext = aDeviceContext;
951 if (mDeviceContext->SetPixelScale(mFullZoom))
952 mDeviceContext->FlushFontCache();
953 mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel();
955 mEventManager = new mozilla::EventStateManager();
957 mTransitionManager = new nsTransitionManager(this);
959 mAnimationManager = new nsAnimationManager(this);
961 // FIXME: Why is mozilla:: needed?
962 mRestyleManager = new mozilla::RestyleManager(this);
964 if (mDocument->GetDisplayDocument()) {
965 NS_ASSERTION(mDocument->GetDisplayDocument()->GetShell() &&
966 mDocument->GetDisplayDocument()->GetShell()->GetPresContext(),
967 "Why are we being initialized?");
968 mRefreshDriver = mDocument->GetDisplayDocument()->GetShell()->
969 GetPresContext()->RefreshDriver();
970 } else {
971 nsIDocument* parent = mDocument->GetParentDocument();
972 // Unfortunately, sometimes |parent| here has no presshell because
973 // printing screws up things. Assert that in other cases it does,
974 // but whenever the shell is null just fall back on using our own
975 // refresh driver.
976 NS_ASSERTION(!parent || mDocument->IsStaticDocument() || parent->GetShell(),
977 "How did we end up with a presshell if our parent doesn't "
978 "have one?");
979 if (parent && parent->GetShell()) {
980 NS_ASSERTION(parent->GetShell()->GetPresContext(),
981 "How did we get a presshell?");
983 // We don't have our container set yet at this point
984 nsCOMPtr<nsIDocShellTreeItem> ourItem = mDocument->GetDocShell();
985 if (ourItem) {
986 nsCOMPtr<nsIDocShellTreeItem> parentItem;
987 ourItem->GetSameTypeParent(getter_AddRefs(parentItem));
988 if (parentItem) {
989 Element* containingElement =
990 parent->FindContentForSubDocument(mDocument);
991 if (!containingElement->IsXUL() ||
992 !containingElement->
993 HasAttr(kNameSpaceID_None,
994 nsGkAtoms::forceOwnRefreshDriver)) {
995 mRefreshDriver = parent->GetShell()->GetPresContext()->RefreshDriver();
996 }
997 }
998 }
999 }
1001 if (!mRefreshDriver) {
1002 mRefreshDriver = new nsRefreshDriver(this);
1003 }
1004 }
1006 // Initialise refresh tick counters for OMTA
1007 mLastStyleUpdateForAllAnimations =
1008 mLastUpdateThrottledAnimationStyle =
1009 mLastUpdateThrottledTransitionStyle = mRefreshDriver->MostRecentRefresh();
1011 mLangService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
1013 // Register callbacks so we're notified when the preferences change
1014 Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
1015 "font.",
1016 this);
1017 Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
1018 "browser.display.",
1019 this);
1020 Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
1021 "browser.underline_anchors",
1022 this);
1023 Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
1024 "browser.anchor_color",
1025 this);
1026 Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
1027 "browser.active_color",
1028 this);
1029 Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
1030 "browser.visited_color",
1031 this);
1032 Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
1033 "image.animation_mode",
1034 this);
1035 Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
1036 "bidi.",
1037 this);
1038 Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
1039 "dom.send_after_paint_to_content",
1040 this);
1041 Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
1042 "gfx.font_rendering.",
1043 this);
1044 Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
1045 "layout.css.dpi",
1046 this);
1047 Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
1048 "layout.css.devPixelsPerPx",
1049 this);
1050 Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
1051 "nglayout.debug.paint_flashing",
1052 this);
1053 Preferences::RegisterCallback(nsPresContext::PrefChangedCallback,
1054 "nglayout.debug.paint_flashing_chrome",
1055 this);
1057 nsresult rv = mEventManager->Init();
1058 NS_ENSURE_SUCCESS(rv, rv);
1060 mEventManager->SetPresContext(this);
1062 #ifdef DEBUG
1063 mInitialized = true;
1064 #endif
1066 mBorderWidthTable[NS_STYLE_BORDER_WIDTH_THIN] = CSSPixelsToAppUnits(1);
1067 mBorderWidthTable[NS_STYLE_BORDER_WIDTH_MEDIUM] = CSSPixelsToAppUnits(3);
1068 mBorderWidthTable[NS_STYLE_BORDER_WIDTH_THICK] = CSSPixelsToAppUnits(5);
1070 return NS_OK;
1071 }
1073 // Note: We don't hold a reference on the shell; it has a reference to
1074 // us
1075 void
1076 nsPresContext::SetShell(nsIPresShell* aShell)
1077 {
1078 if (mUserFontSet) {
1079 // Clear out user font set if we have one
1080 mUserFontSet->Destroy();
1081 NS_RELEASE(mUserFontSet);
1082 }
1084 if (mShell) {
1085 // Remove ourselves as the charset observer from the shell's doc, because
1086 // this shell may be going away for good.
1087 nsIDocument *doc = mShell->GetDocument();
1088 if (doc) {
1089 doc->RemoveCharSetObserver(this);
1090 }
1091 }
1093 mShell = aShell;
1095 if (mShell) {
1096 nsIDocument *doc = mShell->GetDocument();
1097 NS_ASSERTION(doc, "expect document here");
1098 if (doc) {
1099 // Have to update PresContext's mDocument before calling any other methods.
1100 mDocument = doc;
1101 }
1102 // Initialize our state from the user preferences, now that we
1103 // have a presshell, and hence a document.
1104 GetUserPreferences();
1106 if (doc) {
1107 nsIURI *docURI = doc->GetDocumentURI();
1109 if (IsDynamic() && docURI) {
1110 bool isChrome = false;
1111 bool isRes = false;
1112 docURI->SchemeIs("chrome", &isChrome);
1113 docURI->SchemeIs("resource", &isRes);
1115 if (!isChrome && !isRes)
1116 mImageAnimationMode = mImageAnimationModePref;
1117 else
1118 mImageAnimationMode = imgIContainer::kNormalAnimMode;
1119 }
1121 if (mLangService) {
1122 doc->AddCharSetObserver(this);
1123 UpdateCharSet(doc->GetDocumentCharacterSet());
1124 }
1125 }
1126 } else {
1127 if (mTransitionManager) {
1128 mTransitionManager->Disconnect();
1129 mTransitionManager = nullptr;
1130 }
1131 if (mAnimationManager) {
1132 mAnimationManager->Disconnect();
1133 mAnimationManager = nullptr;
1134 }
1135 if (mRestyleManager) {
1136 mRestyleManager->Disconnect();
1137 mRestyleManager = nullptr;
1138 }
1140 if (IsRoot()) {
1141 // Have to cancel our plugin geometry timer, because the
1142 // callback for that depends on a non-null presshell.
1143 static_cast<nsRootPresContext*>(this)->CancelApplyPluginGeometryTimer();
1144 }
1145 }
1146 }
1148 void
1149 nsPresContext::DoChangeCharSet(const nsCString& aCharSet)
1150 {
1151 UpdateCharSet(aCharSet);
1152 mDeviceContext->FlushFontCache();
1153 RebuildAllStyleData(NS_STYLE_HINT_REFLOW);
1154 }
1156 void
1157 nsPresContext::UpdateCharSet(const nsCString& aCharSet)
1158 {
1159 if (mLangService) {
1160 mLanguage = mLangService->LookupCharSet(aCharSet.get());
1161 // this will be a language group (or script) code rather than a true language code
1163 // bug 39570: moved from nsLanguageAtomService::LookupCharSet()
1164 if (mLanguage == nsGkAtoms::Unicode) {
1165 mLanguage = mLangService->GetLocaleLanguage();
1166 }
1167 ResetCachedFontPrefs();
1168 }
1170 switch (GET_BIDI_OPTION_TEXTTYPE(GetBidi())) {
1172 case IBMBIDI_TEXTTYPE_LOGICAL:
1173 SetVisualMode(false);
1174 break;
1176 case IBMBIDI_TEXTTYPE_VISUAL:
1177 SetVisualMode(true);
1178 break;
1180 case IBMBIDI_TEXTTYPE_CHARSET:
1181 default:
1182 SetVisualMode(IsVisualCharset(aCharSet));
1183 }
1184 }
1186 NS_IMETHODIMP
1187 nsPresContext::Observe(nsISupports* aSubject,
1188 const char* aTopic,
1189 const char16_t* aData)
1190 {
1191 if (!nsCRT::strcmp(aTopic, "charset")) {
1192 nsRefPtr<CharSetChangingRunnable> runnable =
1193 new CharSetChangingRunnable(this, NS_LossyConvertUTF16toASCII(aData));
1194 return NS_DispatchToCurrentThread(runnable);
1195 }
1197 NS_WARNING("unrecognized topic in nsPresContext::Observe");
1198 return NS_ERROR_FAILURE;
1199 }
1201 nsPresContext*
1202 nsPresContext::GetParentPresContext()
1203 {
1204 nsIPresShell* shell = GetPresShell();
1205 if (shell) {
1206 nsViewManager* viewManager = shell->GetViewManager();
1207 if (viewManager) {
1208 nsView* view = viewManager->GetRootView();
1209 if (view) {
1210 view = view->GetParent(); // anonymous inner view
1211 if (view) {
1212 view = view->GetParent(); // subdocumentframe's view
1213 if (view) {
1214 nsIFrame* f = view->GetFrame();
1215 if (f) {
1216 return f->PresContext();
1217 }
1218 }
1219 }
1220 }
1221 }
1222 }
1223 return nullptr;
1224 }
1226 nsPresContext*
1227 nsPresContext::GetToplevelContentDocumentPresContext()
1228 {
1229 if (IsChrome())
1230 return nullptr;
1231 nsPresContext* pc = this;
1232 for (;;) {
1233 nsPresContext* parent = pc->GetParentPresContext();
1234 if (!parent || parent->IsChrome())
1235 return pc;
1236 pc = parent;
1237 }
1238 }
1240 nsIWidget*
1241 nsPresContext::GetNearestWidget(nsPoint* aOffset)
1242 {
1243 NS_ENSURE_TRUE(mShell, nullptr);
1244 nsIFrame* frame = mShell->GetRootFrame();
1245 NS_ENSURE_TRUE(frame, nullptr);
1246 return frame->GetView()->GetNearestWidget(aOffset);
1247 }
1249 nsIWidget*
1250 nsPresContext::GetRootWidget()
1251 {
1252 NS_ENSURE_TRUE(mShell, nullptr);
1253 nsViewManager* vm = mShell->GetViewManager();
1254 if (!vm) {
1255 return nullptr;
1256 }
1257 nsCOMPtr<nsIWidget> widget;
1258 vm->GetRootWidget(getter_AddRefs(widget));
1259 return widget.get();
1260 }
1262 // We may want to replace this with something faster, maybe caching the root prescontext
1263 nsRootPresContext*
1264 nsPresContext::GetRootPresContext()
1265 {
1266 nsPresContext* pc = this;
1267 for (;;) {
1268 nsPresContext* parent = pc->GetParentPresContext();
1269 if (!parent)
1270 break;
1271 pc = parent;
1272 }
1273 return pc->IsRoot() ? static_cast<nsRootPresContext*>(pc) : nullptr;
1274 }
1276 nsRootPresContext*
1277 nsPresContext::GetDisplayRootPresContext()
1278 {
1279 nsPresContext* pc = this;
1280 for (;;) {
1281 nsPresContext* parent = pc->GetParentPresContext();
1282 if (!parent) {
1283 // Not sure if this is always strictly the parent, but it works for GetRootPresContext
1284 // where the current pres context has no frames.
1285 nsIDocument *doc = pc->Document();
1286 if (doc) {
1287 doc = doc->GetParentDocument();
1288 if (doc) {
1289 nsIPresShell* shell = doc->GetShell();
1290 if (shell) {
1291 parent = shell->GetPresContext();
1292 }
1293 }
1294 }
1295 }
1296 if (!parent || parent == pc)
1297 break;
1298 pc = parent;
1299 }
1300 return pc->IsRoot() ? static_cast<nsRootPresContext*>(pc) : nullptr;
1301 }
1303 void
1304 nsPresContext::CompatibilityModeChanged()
1305 {
1306 if (!mShell)
1307 return;
1309 // enable/disable the QuirkSheet
1310 mShell->StyleSet()->
1311 EnableQuirkStyleSheet(CompatibilityMode() == eCompatibility_NavQuirks);
1312 }
1314 // Helper function for setting Anim Mode on image
1315 static void SetImgAnimModeOnImgReq(imgIRequest* aImgReq, uint16_t aMode)
1316 {
1317 if (aImgReq) {
1318 nsCOMPtr<imgIContainer> imgCon;
1319 aImgReq->GetImage(getter_AddRefs(imgCon));
1320 if (imgCon) {
1321 imgCon->SetAnimationMode(aMode);
1322 }
1323 }
1324 }
1326 // IMPORTANT: Assumption is that all images for a Presentation
1327 // have the same Animation Mode (pavlov said this was OK)
1328 //
1329 // Walks content and set the animation mode
1330 // this is a way to turn on/off image animations
1331 void nsPresContext::SetImgAnimations(nsIContent *aParent, uint16_t aMode)
1332 {
1333 nsCOMPtr<nsIImageLoadingContent> imgContent(do_QueryInterface(aParent));
1334 if (imgContent) {
1335 nsCOMPtr<imgIRequest> imgReq;
1336 imgContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
1337 getter_AddRefs(imgReq));
1338 SetImgAnimModeOnImgReq(imgReq, aMode);
1339 }
1341 uint32_t count = aParent->GetChildCount();
1342 for (uint32_t i = 0; i < count; ++i) {
1343 SetImgAnimations(aParent->GetChildAt(i), aMode);
1344 }
1345 }
1347 void
1348 nsPresContext::SetSMILAnimations(nsIDocument *aDoc, uint16_t aNewMode,
1349 uint16_t aOldMode)
1350 {
1351 if (aDoc->HasAnimationController()) {
1352 nsSMILAnimationController* controller = aDoc->GetAnimationController();
1353 switch (aNewMode)
1354 {
1355 case imgIContainer::kNormalAnimMode:
1356 case imgIContainer::kLoopOnceAnimMode:
1357 if (aOldMode == imgIContainer::kDontAnimMode)
1358 controller->Resume(nsSMILTimeContainer::PAUSE_USERPREF);
1359 break;
1361 case imgIContainer::kDontAnimMode:
1362 if (aOldMode != imgIContainer::kDontAnimMode)
1363 controller->Pause(nsSMILTimeContainer::PAUSE_USERPREF);
1364 break;
1365 }
1366 }
1367 }
1369 void
1370 nsPresContext::SetImageAnimationModeInternal(uint16_t aMode)
1371 {
1372 NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
1373 aMode == imgIContainer::kDontAnimMode ||
1374 aMode == imgIContainer::kLoopOnceAnimMode, "Wrong Animation Mode is being set!");
1376 // Image animation mode cannot be changed when rendering to a printer.
1377 if (!IsDynamic())
1378 return;
1380 // Now walk the content tree and set the animation mode
1381 // on all the images.
1382 if (mShell != nullptr) {
1383 nsIDocument *doc = mShell->GetDocument();
1384 if (doc) {
1385 doc->StyleImageLoader()->SetAnimationMode(aMode);
1387 Element *rootElement = doc->GetRootElement();
1388 if (rootElement) {
1389 SetImgAnimations(rootElement, aMode);
1390 }
1391 SetSMILAnimations(doc, aMode, mImageAnimationMode);
1392 }
1393 }
1395 mImageAnimationMode = aMode;
1396 }
1398 void
1399 nsPresContext::SetImageAnimationModeExternal(uint16_t aMode)
1400 {
1401 SetImageAnimationModeInternal(aMode);
1402 }
1404 const nsFont*
1405 nsPresContext::GetDefaultFont(uint8_t aFontID, nsIAtom *aLanguage) const
1406 {
1407 const LangGroupFontPrefs *prefs = GetFontPrefsForLang(aLanguage);
1409 const nsFont *font;
1410 switch (aFontID) {
1411 // Special (our default variable width font and fixed width font)
1412 case kPresContext_DefaultVariableFont_ID:
1413 font = &prefs->mDefaultVariableFont;
1414 break;
1415 case kPresContext_DefaultFixedFont_ID:
1416 font = &prefs->mDefaultFixedFont;
1417 break;
1418 // CSS
1419 case kGenericFont_serif:
1420 font = &prefs->mDefaultSerifFont;
1421 break;
1422 case kGenericFont_sans_serif:
1423 font = &prefs->mDefaultSansSerifFont;
1424 break;
1425 case kGenericFont_monospace:
1426 font = &prefs->mDefaultMonospaceFont;
1427 break;
1428 case kGenericFont_cursive:
1429 font = &prefs->mDefaultCursiveFont;
1430 break;
1431 case kGenericFont_fantasy:
1432 font = &prefs->mDefaultFantasyFont;
1433 break;
1434 default:
1435 font = nullptr;
1436 NS_ERROR("invalid arg");
1437 break;
1438 }
1439 return font;
1440 }
1442 PRBool
1443 nsPresContext::FontUseCountReached(const nsFont &font) {
1444 if (mMaxFonts < 0) {
1445 return PR_FALSE;
1446 }
1448 for (PRUint32 i = 0; i < mFontsUsed.Length(); i++) {
1449 if (mFontsUsed[i].name.Equals(font.name,
1450 nsCaseInsensitiveStringComparator())
1451 // XXX: Style is sometimes filled with garbage??
1452 /*&& mFontsUsed[i].style == font.style*/) {
1453 // seen it before: OK
1454 return PR_FALSE;
1455 }
1456 }
1458 if (mFontsUsed.Length() >= (unsigned)mMaxFonts) {
1459 return PR_TRUE;
1460 }
1462 return PR_FALSE;
1463 }
1465 PRBool
1466 nsPresContext::FontAttemptCountReached(const nsFont &font) {
1467 if (mMaxFontAttempts < 0) {
1468 return PR_FALSE;
1469 }
1471 for (PRUint32 i = 0; i < mFontsTried.Length(); i++) {
1472 if (mFontsTried[i].name.Equals(font.name,
1473 nsCaseInsensitiveStringComparator())
1474 // XXX: Style is sometimes filled with garbage??
1475 /*&& mFontsTried[i].style == font.style*/) {
1476 // seen it before: OK
1477 return PR_FALSE;
1478 }
1479 }
1481 if (mFontsTried.Length() >= (unsigned)mMaxFontAttempts) {
1482 return PR_TRUE;
1483 }
1485 return PR_FALSE;
1486 }
1488 void
1489 nsPresContext::AddFontUse(const nsFont &font) {
1490 if (mMaxFonts < 0) {
1491 return;
1492 }
1494 for (PRUint32 i = 0; i < mFontsUsed.Length(); i++) {
1495 if (mFontsUsed[i].name.Equals(font.name,
1496 nsCaseInsensitiveStringComparator())
1497 // XXX: Style is sometimes filled with garbage??
1498 /*&& mFontsUsed[i].style == font.style*/) {
1499 // seen it before: OK
1500 return;
1501 }
1502 }
1504 if (mFontsUsed.Length() >= (unsigned)mMaxFonts) {
1505 return;
1506 }
1508 mFontsUsed.AppendElement(font);
1509 return;
1510 }
1512 void
1513 nsPresContext::AddFontAttempt(const nsFont &font) {
1514 if (mMaxFontAttempts < 0) {
1515 return;
1516 }
1518 for (PRUint32 i = 0; i < mFontsTried.Length(); i++) {
1519 if (mFontsTried[i].name.Equals(font.name,
1520 nsCaseInsensitiveStringComparator())
1521 // XXX: Style is sometimes filled with garbage??
1522 /*&& mFontsTried[i].style == font.style*/) {
1523 // seen it before: OK
1524 return;
1525 }
1526 }
1528 if (mFontsTried.Length() >= (unsigned)mMaxFontAttempts) {
1529 return;
1530 }
1532 mFontsTried.AppendElement(font);
1533 return;
1534 }
1536 void
1537 nsPresContext::SetFullZoom(float aZoom)
1538 {
1539 if (!mShell || mFullZoom == aZoom) {
1540 return;
1541 }
1543 // Re-fetch the view manager's window dimensions in case there's a deferred
1544 // resize which hasn't affected our mVisibleArea yet
1545 nscoord oldWidthAppUnits, oldHeightAppUnits;
1546 mShell->GetViewManager()->GetWindowDimensions(&oldWidthAppUnits, &oldHeightAppUnits);
1547 float oldWidthDevPixels = oldWidthAppUnits / float(mCurAppUnitsPerDevPixel);
1548 float oldHeightDevPixels = oldHeightAppUnits / float(mCurAppUnitsPerDevPixel);
1549 mDeviceContext->SetPixelScale(aZoom);
1551 NS_ASSERTION(!mSupressResizeReflow, "two zooms happening at the same time? impossible!");
1552 mSupressResizeReflow = true;
1554 mFullZoom = aZoom;
1555 mShell->GetViewManager()->
1556 SetWindowDimensions(NSToCoordRound(oldWidthDevPixels * AppUnitsPerDevPixel()),
1557 NSToCoordRound(oldHeightDevPixels * AppUnitsPerDevPixel()));
1559 AppUnitsPerDevPixelChanged();
1561 mSupressResizeReflow = false;
1562 }
1564 float
1565 nsPresContext::ScreenWidthInchesForFontInflation(bool* aChanged)
1566 {
1567 if (aChanged) {
1568 *aChanged = false;
1569 }
1571 nsDeviceContext *dx = DeviceContext();
1572 nsRect clientRect;
1573 dx->GetClientRect(clientRect); // FIXME: GetClientRect looks expensive
1574 float deviceWidthInches =
1575 float(clientRect.width) / float(dx->AppUnitsPerPhysicalInch());
1577 if (mLastFontInflationScreenWidth == -1.0) {
1578 mLastFontInflationScreenWidth = deviceWidthInches;
1579 }
1581 if (deviceWidthInches != mLastFontInflationScreenWidth && aChanged) {
1582 *aChanged = true;
1583 mLastFontInflationScreenWidth = deviceWidthInches;
1584 }
1586 return deviceWidthInches;
1587 }
1589 void
1590 nsPresContext::SetContainer(nsIDocShell* aDocShell)
1591 {
1592 if (aDocShell) {
1593 mContainer = static_cast<nsDocShell*>(aDocShell)->asWeakPtr();
1594 } else {
1595 mContainer = WeakPtr<nsDocShell>();
1596 }
1597 UpdateIsChrome();
1598 if (mContainer) {
1599 GetDocumentColorPreferences();
1600 }
1601 }
1603 nsISupports*
1604 nsPresContext::GetContainerWeakInternal() const
1605 {
1606 return static_cast<nsIDocShell*>(mContainer);
1607 }
1609 nsISupports*
1610 nsPresContext::GetContainerWeakExternal() const
1611 {
1612 return GetContainerWeakInternal();
1613 }
1615 nsIDocShell*
1616 nsPresContext::GetDocShell() const
1617 {
1618 return mContainer;
1619 }
1621 /* virtual */ void
1622 nsPresContext::Detach()
1623 {
1624 SetContainer(nullptr);
1625 SetLinkHandler(nullptr);
1626 if (mShell) {
1627 mShell->CancelInvalidatePresShellIfHidden();
1628 }
1629 }
1631 bool
1632 nsPresContext::ThrottledTransitionStyleIsUpToDate() const
1633 {
1634 return
1635 mLastUpdateThrottledTransitionStyle == mRefreshDriver->MostRecentRefresh();
1636 }
1638 void
1639 nsPresContext::TickLastUpdateThrottledTransitionStyle()
1640 {
1641 mLastUpdateThrottledTransitionStyle = mRefreshDriver->MostRecentRefresh();
1642 }
1644 bool
1645 nsPresContext::ThrottledAnimationStyleIsUpToDate() const
1646 {
1647 return
1648 mLastUpdateThrottledAnimationStyle == mRefreshDriver->MostRecentRefresh();
1649 }
1651 void
1652 nsPresContext::TickLastUpdateThrottledAnimationStyle()
1653 {
1654 mLastUpdateThrottledAnimationStyle = mRefreshDriver->MostRecentRefresh();
1655 }
1657 bool
1658 nsPresContext::StyleUpdateForAllAnimationsIsUpToDate()
1659 {
1660 return mLastStyleUpdateForAllAnimations == mRefreshDriver->MostRecentRefresh();
1661 }
1663 void
1664 nsPresContext::TickLastStyleUpdateForAllAnimations()
1665 {
1666 mLastStyleUpdateForAllAnimations = mRefreshDriver->MostRecentRefresh();
1667 }
1669 bool
1670 nsPresContext::BidiEnabledExternal() const
1671 {
1672 return BidiEnabledInternal();
1673 }
1675 bool
1676 nsPresContext::BidiEnabledInternal() const
1677 {
1678 return Document()->GetBidiEnabled();
1679 }
1681 void
1682 nsPresContext::SetBidiEnabled() const
1683 {
1684 if (mShell) {
1685 nsIDocument *doc = mShell->GetDocument();
1686 if (doc) {
1687 doc->SetBidiEnabled();
1688 }
1689 }
1690 }
1692 void
1693 nsPresContext::SetBidi(uint32_t aSource, bool aForceRestyle)
1694 {
1695 // Don't do all this stuff unless the options have changed.
1696 if (aSource == GetBidi()) {
1697 return;
1698 }
1700 NS_ASSERTION(!(aForceRestyle && (GetBidi() == 0)),
1701 "ForceReflow on new prescontext");
1703 Document()->SetBidiOptions(aSource);
1704 if (IBMBIDI_TEXTDIRECTION_RTL == GET_BIDI_OPTION_DIRECTION(aSource)
1705 || IBMBIDI_NUMERAL_HINDI == GET_BIDI_OPTION_NUMERAL(aSource)) {
1706 SetBidiEnabled();
1707 }
1708 if (IBMBIDI_TEXTTYPE_VISUAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) {
1709 SetVisualMode(true);
1710 }
1711 else if (IBMBIDI_TEXTTYPE_LOGICAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) {
1712 SetVisualMode(false);
1713 }
1714 else {
1715 nsIDocument* doc = mShell->GetDocument();
1716 if (doc) {
1717 SetVisualMode(IsVisualCharset(doc->GetDocumentCharacterSet()));
1718 }
1719 }
1720 if (aForceRestyle && mShell) {
1721 // Reconstruct the root document element's frame and its children,
1722 // because we need to trigger frame reconstruction for direction change.
1723 RebuildUserFontSet();
1724 mShell->ReconstructFrames();
1725 }
1726 }
1728 uint32_t
1729 nsPresContext::GetBidi() const
1730 {
1731 return Document()->GetBidiOptions();
1732 }
1734 bool
1735 nsPresContext::IsTopLevelWindowInactive()
1736 {
1737 nsCOMPtr<nsIDocShellTreeItem> treeItem(mContainer);
1738 if (!treeItem)
1739 return false;
1741 nsCOMPtr<nsIDocShellTreeItem> rootItem;
1742 treeItem->GetRootTreeItem(getter_AddRefs(rootItem));
1743 nsCOMPtr<nsPIDOMWindow> domWindow(do_GetInterface(rootItem));
1745 return domWindow && !domWindow->IsActive();
1746 }
1748 nsITheme*
1749 nsPresContext::GetTheme()
1750 {
1751 if (!sNoTheme && !mTheme) {
1752 mTheme = do_GetService("@mozilla.org/chrome/chrome-native-theme;1");
1753 if (!mTheme)
1754 sNoTheme = true;
1755 }
1757 return mTheme;
1758 }
1760 void
1761 nsPresContext::ThemeChanged()
1762 {
1763 if (!mPendingThemeChanged) {
1764 sLookAndFeelChanged = true;
1765 sThemeChanged = true;
1767 nsCOMPtr<nsIRunnable> ev =
1768 NS_NewRunnableMethod(this, &nsPresContext::ThemeChangedInternal);
1769 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
1770 mPendingThemeChanged = true;
1771 }
1772 }
1773 }
1775 void
1776 nsPresContext::ThemeChangedInternal()
1777 {
1778 mPendingThemeChanged = false;
1780 // Tell the theme that it changed, so it can flush any handles to stale theme
1781 // data.
1782 if (mTheme && sThemeChanged) {
1783 mTheme->ThemeChanged();
1784 sThemeChanged = false;
1785 }
1787 // Clear all cached LookAndFeel colors.
1788 if (sLookAndFeelChanged) {
1789 LookAndFeel::Refresh();
1790 sLookAndFeelChanged = false;
1791 }
1793 // This will force the system metrics to be generated the next time they're used
1794 nsCSSRuleProcessor::FreeSystemMetrics();
1796 // Changes to system metrics can change media queries on them.
1797 // Changes in theme can change system colors (whose changes are
1798 // properly reflected in computed style data), system fonts (whose
1799 // changes are not), and -moz-appearance (whose changes likewise are
1800 // not), so we need to reflow.
1801 MediaFeatureValuesChanged(eAlwaysRebuildStyle, NS_STYLE_HINT_REFLOW);
1802 }
1804 void
1805 nsPresContext::SysColorChanged()
1806 {
1807 if (!mPendingSysColorChanged) {
1808 sLookAndFeelChanged = true;
1809 nsCOMPtr<nsIRunnable> ev =
1810 NS_NewRunnableMethod(this, &nsPresContext::SysColorChangedInternal);
1811 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
1812 mPendingSysColorChanged = true;
1813 }
1814 }
1815 }
1817 void
1818 nsPresContext::SysColorChangedInternal()
1819 {
1820 mPendingSysColorChanged = false;
1822 if (sLookAndFeelChanged) {
1823 // Don't use the cached values for the system colors
1824 LookAndFeel::Refresh();
1825 sLookAndFeelChanged = false;
1826 }
1828 // Reset default background and foreground colors for the document since
1829 // they may be using system colors
1830 GetDocumentColorPreferences();
1832 // The system color values are computed to colors in the style data,
1833 // so normal style data comparison is sufficient here.
1834 RebuildAllStyleData(nsChangeHint(0));
1835 }
1837 void
1838 nsPresContext::UIResolutionChanged()
1839 {
1840 if (!mPendingUIResolutionChanged) {
1841 nsCOMPtr<nsIRunnable> ev =
1842 NS_NewRunnableMethod(this, &nsPresContext::UIResolutionChangedInternal);
1843 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
1844 mPendingUIResolutionChanged = true;
1845 }
1846 }
1847 }
1849 /*static*/ bool
1850 nsPresContext::UIResolutionChangedSubdocumentCallback(nsIDocument* aDocument,
1851 void* aData)
1852 {
1853 nsIPresShell* shell = aDocument->GetShell();
1854 if (shell) {
1855 nsPresContext* pc = shell->GetPresContext();
1856 if (pc) {
1857 pc->UIResolutionChangedInternal();
1858 }
1859 }
1860 return true;
1861 }
1863 void
1864 nsPresContext::UIResolutionChangedInternal()
1865 {
1866 mPendingUIResolutionChanged = false;
1868 mDeviceContext->CheckDPIChange();
1869 if (mCurAppUnitsPerDevPixel != AppUnitsPerDevPixel()) {
1870 AppUnitsPerDevPixelChanged();
1871 }
1873 mDocument->EnumerateSubDocuments(UIResolutionChangedSubdocumentCallback,
1874 nullptr);
1875 }
1877 void
1878 nsPresContext::EmulateMedium(const nsAString& aMediaType)
1879 {
1880 nsIAtom* previousMedium = Medium();
1881 mIsEmulatingMedia = true;
1883 nsAutoString mediaType;
1884 nsContentUtils::ASCIIToLower(aMediaType, mediaType);
1886 mMediaEmulated = do_GetAtom(mediaType);
1887 if (mMediaEmulated != previousMedium && mShell) {
1888 MediaFeatureValuesChanged(eRebuildStyleIfNeeded, nsChangeHint(0));
1889 }
1890 }
1892 void nsPresContext::StopEmulatingMedium()
1893 {
1894 nsIAtom* previousMedium = Medium();
1895 mIsEmulatingMedia = false;
1896 if (Medium() != previousMedium) {
1897 MediaFeatureValuesChanged(eRebuildStyleIfNeeded, nsChangeHint(0));
1898 }
1899 }
1901 void
1902 nsPresContext::RebuildAllStyleData(nsChangeHint aExtraHint)
1903 {
1904 if (!mShell) {
1905 // We must have been torn down. Nothing to do here.
1906 return;
1907 }
1909 mUsesRootEMUnits = false;
1910 mUsesViewportUnits = false;
1911 RebuildUserFontSet();
1913 RestyleManager()->RebuildAllStyleData(aExtraHint);
1914 }
1916 void
1917 nsPresContext::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint)
1918 {
1919 if (!mShell) {
1920 // We must have been torn down. Nothing to do here.
1921 return;
1922 }
1923 RestyleManager()->PostRebuildAllStyleDataEvent(aExtraHint);
1924 }
1926 void
1927 nsPresContext::MediaFeatureValuesChanged(StyleRebuildType aShouldRebuild,
1928 nsChangeHint aChangeHint)
1929 {
1930 NS_ASSERTION(aShouldRebuild == eAlwaysRebuildStyle || aChangeHint == 0,
1931 "If you don't know if we need a rebuild, how can you provide a hint?");
1933 mPendingMediaFeatureValuesChanged = false;
1935 // MediumFeaturesChanged updates the applied rules, so it always gets called.
1936 bool mediaFeaturesDidChange = mShell ? mShell->StyleSet()->MediumFeaturesChanged(this)
1937 : false;
1939 if (aShouldRebuild == eAlwaysRebuildStyle ||
1940 mediaFeaturesDidChange ||
1941 (mUsesViewportUnits && mPendingViewportChange)) {
1942 RebuildAllStyleData(aChangeHint);
1943 }
1945 mPendingViewportChange = false;
1947 if (!nsContentUtils::IsSafeToRunScript()) {
1948 NS_ABORT_IF_FALSE(mDocument->IsBeingUsedAsImage(),
1949 "How did we get here? Are we failing to notify "
1950 "listeners that we should notify?");
1951 return;
1952 }
1954 // Media query list listeners should be notified from a queued task
1955 // (in HTML5 terms), although we also want to notify them on certain
1956 // flushes. (We're already running off an event.)
1957 //
1958 // Note that we do this after the new style from media queries in
1959 // style sheets has been computed.
1961 if (!PR_CLIST_IS_EMPTY(&mDOMMediaQueryLists)) {
1962 // We build a list of all the notifications we're going to send
1963 // before we send any of them. (The spec says the notifications
1964 // should be a queued task, so any removals that happen during the
1965 // notifications shouldn't affect what gets notified.) Furthermore,
1966 // we hold strong pointers to everything we're going to make
1967 // notification calls to, since each notification involves calling
1968 // arbitrary script that might otherwise destroy these objects, or,
1969 // for that matter, |this|.
1970 //
1971 // Note that we intentionally send the notifications to media query
1972 // list in the order they were created and, for each list, to the
1973 // listeners in the order added.
1974 MediaQueryList::NotifyList notifyList;
1975 for (PRCList *l = PR_LIST_HEAD(&mDOMMediaQueryLists);
1976 l != &mDOMMediaQueryLists; l = PR_NEXT_LINK(l)) {
1977 MediaQueryList *mql = static_cast<MediaQueryList*>(l);
1978 mql->MediumFeaturesChanged(notifyList);
1979 }
1981 if (!notifyList.IsEmpty()) {
1982 nsPIDOMWindow *win = mDocument->GetInnerWindow();
1983 nsCOMPtr<EventTarget> et = do_QueryInterface(win);
1984 nsCxPusher pusher;
1986 for (uint32_t i = 0, i_end = notifyList.Length(); i != i_end; ++i) {
1987 if (pusher.RePush(et)) {
1988 nsAutoMicroTask mt;
1989 MediaQueryList::HandleChangeData &d = notifyList[i];
1990 ErrorResult result;
1991 d.callback->Call(*d.mql, result);
1992 }
1993 }
1994 }
1996 // NOTE: When |notifyList| goes out of scope, our destructor could run.
1997 }
1998 }
2000 void
2001 nsPresContext::PostMediaFeatureValuesChangedEvent()
2002 {
2003 // FIXME: We should probably replace this event with use of
2004 // nsRefreshDriver::AddStyleFlushObserver (except the pres shell would
2005 // need to track whether it's been added).
2006 if (!mPendingMediaFeatureValuesChanged) {
2007 nsCOMPtr<nsIRunnable> ev =
2008 NS_NewRunnableMethod(this, &nsPresContext::HandleMediaFeatureValuesChangedEvent);
2009 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
2010 mPendingMediaFeatureValuesChanged = true;
2011 mDocument->SetNeedStyleFlush();
2012 }
2013 }
2014 }
2016 void
2017 nsPresContext::HandleMediaFeatureValuesChangedEvent()
2018 {
2019 // Null-check mShell in case the shell has been destroyed (and the
2020 // event is the only thing holding the pres context alive).
2021 if (mPendingMediaFeatureValuesChanged && mShell) {
2022 MediaFeatureValuesChanged(eRebuildStyleIfNeeded);
2023 }
2024 }
2026 already_AddRefed<MediaQueryList>
2027 nsPresContext::MatchMedia(const nsAString& aMediaQueryList)
2028 {
2029 nsRefPtr<MediaQueryList> result = new MediaQueryList(this, aMediaQueryList);
2031 // Insert the new item at the end of the linked list.
2032 PR_INSERT_BEFORE(result, &mDOMMediaQueryLists);
2034 return result.forget();
2035 }
2037 nsCompatibility
2038 nsPresContext::CompatibilityMode() const
2039 {
2040 return Document()->GetCompatibilityMode();
2041 }
2043 void
2044 nsPresContext::SetPaginatedScrolling(bool aPaginated)
2045 {
2046 if (mType == eContext_PrintPreview || mType == eContext_PageLayout)
2047 mCanPaginatedScroll = aPaginated;
2048 }
2050 void
2051 nsPresContext::SetPrintSettings(nsIPrintSettings *aPrintSettings)
2052 {
2053 if (mMedium == nsGkAtoms::print)
2054 mPrintSettings = aPrintSettings;
2055 }
2057 bool
2058 nsPresContext::EnsureVisible()
2059 {
2060 nsCOMPtr<nsIDocShell> docShell(mContainer);
2061 if (docShell) {
2062 nsCOMPtr<nsIContentViewer> cv;
2063 docShell->GetContentViewer(getter_AddRefs(cv));
2064 // Make sure this is the content viewer we belong with
2065 if (cv) {
2066 nsRefPtr<nsPresContext> currentPresContext;
2067 cv->GetPresContext(getter_AddRefs(currentPresContext));
2068 if (currentPresContext == this) {
2069 // OK, this is us. We want to call Show() on the content viewer.
2070 nsresult result = cv->Show();
2071 if (NS_SUCCEEDED(result)) {
2072 return true;
2073 }
2074 }
2075 }
2076 }
2077 return false;
2078 }
2080 #ifdef MOZ_REFLOW_PERF
2081 void
2082 nsPresContext::CountReflows(const char * aName, nsIFrame * aFrame)
2083 {
2084 if (mShell) {
2085 mShell->CountReflows(aName, aFrame);
2086 }
2087 }
2088 #endif
2090 void
2091 nsPresContext::UpdateIsChrome()
2092 {
2093 mIsChrome = mContainer &&
2094 nsIDocShellTreeItem::typeChrome == mContainer->ItemType();
2095 }
2097 /* virtual */ bool
2098 nsPresContext::HasAuthorSpecifiedRules(nsIFrame *aFrame, uint32_t ruleTypeMask) const
2099 {
2100 return
2101 nsRuleNode::HasAuthorSpecifiedRules(aFrame->StyleContext(),
2102 ruleTypeMask,
2103 UseDocumentColors());
2104 }
2106 gfxUserFontSet*
2107 nsPresContext::GetUserFontSetInternal()
2108 {
2109 // We want to initialize the user font set lazily the first time the
2110 // user asks for it, rather than building it too early and forcing
2111 // rule cascade creation. Thus we try to enforce the invariant that
2112 // we *never* build the user font set until the first call to
2113 // GetUserFontSet. However, once it's been requested, we can't wait
2114 // for somebody to call GetUserFontSet in order to rebuild it (see
2115 // comments below in RebuildUserFontSet for why).
2116 #ifdef DEBUG
2117 bool userFontSetGottenBefore = mGetUserFontSetCalled;
2118 #endif
2119 // Set mGetUserFontSetCalled up front, so that FlushUserFontSet will actually
2120 // flush.
2121 mGetUserFontSetCalled = true;
2122 if (mUserFontSetDirty) {
2123 // If this assertion fails, and there have actually been changes to
2124 // @font-face rules, then we will call StyleChangeReflow in
2125 // FlushUserFontSet. If we're in the middle of reflow,
2126 // that's a bad thing to do, and the caller was responsible for
2127 // flushing first. If we're not (e.g., in frame construction), it's
2128 // ok.
2129 NS_ASSERTION(!userFontSetGottenBefore || !mShell->IsReflowLocked(),
2130 "FlushUserFontSet should have been called first");
2131 FlushUserFontSet();
2132 }
2134 return mUserFontSet;
2135 }
2137 gfxUserFontSet*
2138 nsPresContext::GetUserFontSetExternal()
2139 {
2140 return GetUserFontSetInternal();
2141 }
2143 void
2144 nsPresContext::FlushUserFontSet()
2145 {
2146 if (!mShell) {
2147 return; // we've been torn down
2148 }
2150 if (!mGetUserFontSetCalled) {
2151 return; // No one cares about this font set yet, but we want to be careful
2152 // to not unset our mUserFontSetDirty bit, so when someone really
2153 // does we'll create it.
2154 }
2156 if (mUserFontSetDirty) {
2157 if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
2158 nsTArray<nsFontFaceRuleContainer> rules;
2159 if (!mShell->StyleSet()->AppendFontFaceRules(this, rules)) {
2160 if (mUserFontSet) {
2161 mUserFontSet->Destroy();
2162 NS_RELEASE(mUserFontSet);
2163 }
2164 return;
2165 }
2167 bool changed = false;
2169 if (rules.Length() == 0) {
2170 if (mUserFontSet) {
2171 mUserFontSet->Destroy();
2172 NS_RELEASE(mUserFontSet);
2173 changed = true;
2174 }
2175 } else {
2176 if (!mUserFontSet) {
2177 mUserFontSet = new nsUserFontSet(this);
2178 NS_ADDREF(mUserFontSet);
2179 }
2180 changed = mUserFontSet->UpdateRules(rules);
2181 }
2183 // We need to enqueue a style change reflow (for later) to
2184 // reflect that we're modifying @font-face rules. (However,
2185 // without a reflow, nothing will happen to start any downloads
2186 // that are needed.)
2187 if (changed) {
2188 UserFontSetUpdated();
2189 }
2190 }
2192 mUserFontSetDirty = false;
2193 }
2194 }
2196 void
2197 nsPresContext::RebuildUserFontSet()
2198 {
2199 if (!mGetUserFontSetCalled) {
2200 // We want to lazily build the user font set the first time it's
2201 // requested (so we don't force creation of rule cascades too
2202 // early), so don't do anything now.
2203 return;
2204 }
2206 mUserFontSetDirty = true;
2207 mDocument->SetNeedStyleFlush();
2209 // Somebody has already asked for the user font set, so we need to
2210 // post an event to rebuild it. Setting the user font set to be dirty
2211 // and lazily rebuilding it isn't sufficient, since it is only the act
2212 // of rebuilding it that will trigger the style change reflow that
2213 // calls GetUserFontSet. (This reflow causes rebuilding of text runs,
2214 // which starts font loads, whose completion causes another style
2215 // change reflow).
2216 if (!mPostedFlushUserFontSet) {
2217 nsCOMPtr<nsIRunnable> ev =
2218 NS_NewRunnableMethod(this, &nsPresContext::HandleRebuildUserFontSet);
2219 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
2220 mPostedFlushUserFontSet = true;
2221 }
2222 }
2223 }
2225 void
2226 nsPresContext::UserFontSetUpdated()
2227 {
2228 if (!mShell)
2229 return;
2231 // Changes to the set of available fonts can cause updates to layout by:
2232 //
2233 // 1. Changing the font used for text, which changes anything that
2234 // depends on text measurement, including line breaking and
2235 // intrinsic widths, and any other parts of layout that depend on
2236 // font metrics. This requires a style change reflow to update.
2237 //
2238 // 2. Changing the value of the 'ex' and 'ch' units in style data,
2239 // which also depend on font metrics. Updating this information
2240 // requires rebuilding the rule tree from the top, avoiding the
2241 // reuse of cached data even when no style rules have changed.
2243 PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW);
2244 }
2246 void
2247 nsPresContext::EnsureSafeToHandOutCSSRules()
2248 {
2249 nsCSSStyleSheet::EnsureUniqueInnerResult res =
2250 mShell->StyleSet()->EnsureUniqueInnerOnCSSSheets();
2251 if (res == nsCSSStyleSheet::eUniqueInner_AlreadyUnique) {
2252 // Nothing to do.
2253 return;
2254 }
2256 MOZ_ASSERT(res == nsCSSStyleSheet::eUniqueInner_ClonedInner);
2257 RebuildAllStyleData(nsChangeHint(0));
2258 }
2260 void
2261 nsPresContext::FireDOMPaintEvent(nsInvalidateRequestList* aList)
2262 {
2263 nsPIDOMWindow* ourWindow = mDocument->GetWindow();
2264 if (!ourWindow)
2265 return;
2267 nsCOMPtr<EventTarget> dispatchTarget = do_QueryInterface(ourWindow);
2268 nsCOMPtr<EventTarget> eventTarget = dispatchTarget;
2269 if (!IsChrome() && !mSendAfterPaintToContent) {
2270 // Don't tell the window about this event, it should not know that
2271 // something happened in a subdocument. Tell only the chrome event handler.
2272 // (Events sent to the window get propagated to the chrome event handler
2273 // automatically.)
2274 dispatchTarget = do_QueryInterface(ourWindow->GetParentTarget());
2275 if (!dispatchTarget) {
2276 return;
2277 }
2278 }
2279 // Events sent to the window get propagated to the chrome event handler
2280 // automatically.
2281 nsCOMPtr<nsIDOMEvent> event;
2282 // This will empty our list in case dispatching the event causes more damage
2283 // (hopefully it won't, or we're likely to get an infinite loop! At least
2284 // it won't be blocking app execution though).
2285 NS_NewDOMNotifyPaintEvent(getter_AddRefs(event), eventTarget, this, nullptr,
2286 NS_AFTERPAINT, aList);
2287 if (!event) {
2288 return;
2289 }
2291 // Even if we're not telling the window about the event (so eventTarget is
2292 // the chrome event handler, not the window), the window is still
2293 // logically the event target.
2294 event->SetTarget(eventTarget);
2295 event->SetTrusted(true);
2296 EventDispatcher::DispatchDOMEvent(dispatchTarget, nullptr, event, this,
2297 nullptr);
2298 }
2300 static bool
2301 MayHavePaintEventListenerSubdocumentCallback(nsIDocument* aDocument, void* aData)
2302 {
2303 bool *result = static_cast<bool*>(aData);
2304 nsIPresShell* shell = aDocument->GetShell();
2305 if (shell) {
2306 nsPresContext* pc = shell->GetPresContext();
2307 if (pc) {
2308 *result = pc->MayHavePaintEventListenerInSubDocument();
2310 // If we found a paint event listener, then we can stop enumerating
2311 // sub documents.
2312 return !*result;
2313 }
2314 }
2315 return true;
2316 }
2318 static bool
2319 MayHavePaintEventListener(nsPIDOMWindow* aInnerWindow)
2320 {
2321 if (!aInnerWindow)
2322 return false;
2323 if (aInnerWindow->HasPaintEventListeners())
2324 return true;
2326 EventTarget* parentTarget = aInnerWindow->GetParentTarget();
2327 if (!parentTarget)
2328 return false;
2330 EventListenerManager* manager = nullptr;
2331 if ((manager = parentTarget->GetExistingListenerManager()) &&
2332 manager->MayHavePaintEventListener()) {
2333 return true;
2334 }
2336 nsCOMPtr<nsINode> node;
2337 if (parentTarget != aInnerWindow->GetChromeEventHandler()) {
2338 nsCOMPtr<nsIInProcessContentFrameMessageManager> mm =
2339 do_QueryInterface(parentTarget);
2340 if (mm) {
2341 node = mm->GetOwnerContent();
2342 }
2343 }
2345 if (!node) {
2346 node = do_QueryInterface(parentTarget);
2347 }
2348 if (node)
2349 return MayHavePaintEventListener(node->OwnerDoc()->GetInnerWindow());
2351 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(parentTarget);
2352 if (window)
2353 return MayHavePaintEventListener(window);
2355 nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(parentTarget);
2356 EventTarget* tabChildGlobal;
2357 return root &&
2358 (tabChildGlobal = root->GetParentTarget()) &&
2359 (manager = tabChildGlobal->GetExistingListenerManager()) &&
2360 manager->MayHavePaintEventListener();
2361 }
2363 bool
2364 nsPresContext::MayHavePaintEventListener()
2365 {
2366 return ::MayHavePaintEventListener(mDocument->GetInnerWindow());
2367 }
2369 bool
2370 nsPresContext::MayHavePaintEventListenerInSubDocument()
2371 {
2372 if (MayHavePaintEventListener()) {
2373 return true;
2374 }
2376 bool result = false;
2377 mDocument->EnumerateSubDocuments(MayHavePaintEventListenerSubdocumentCallback, &result);
2378 return result;
2379 }
2381 void
2382 nsPresContext::NotifyInvalidation(uint32_t aFlags)
2383 {
2384 nsIFrame* rootFrame = PresShell()->FrameManager()->GetRootFrame();
2385 NotifyInvalidation(rootFrame->GetVisualOverflowRect(), aFlags);
2386 mAllInvalidated = true;
2387 }
2389 void
2390 nsPresContext::NotifyInvalidation(const nsIntRect& aRect, uint32_t aFlags)
2391 {
2392 nsRect rect(DevPixelsToAppUnits(aRect.x),
2393 DevPixelsToAppUnits(aRect.y),
2394 DevPixelsToAppUnits(aRect.width),
2395 DevPixelsToAppUnits(aRect.height));
2396 NotifyInvalidation(rect, aFlags);
2397 }
2399 void
2400 nsPresContext::NotifyInvalidation(const nsRect& aRect, uint32_t aFlags)
2401 {
2402 MOZ_ASSERT(GetContainerWeak(), "Invalidation in detached pres context");
2404 // If there is no paint event listener, then we don't need to fire
2405 // the asynchronous event. We don't even need to record invalidation.
2406 // MayHavePaintEventListener is pretty cheap and we could make it
2407 // even cheaper by providing a more efficient
2408 // nsPIDOMWindow::GetListenerManager.
2410 if (mAllInvalidated) {
2411 return;
2412 }
2414 nsPresContext* pc;
2415 for (pc = this; pc; pc = pc->GetParentPresContext()) {
2416 if (pc->mFireAfterPaintEvents)
2417 break;
2418 pc->mFireAfterPaintEvents = true;
2419 }
2420 if (!pc) {
2421 nsRootPresContext* rpc = GetRootPresContext();
2422 if (rpc) {
2423 rpc->EnsureEventualDidPaintEvent();
2424 }
2425 }
2427 nsInvalidateRequestList::Request* request =
2428 mInvalidateRequestsSinceLastPaint.mRequests.AppendElement();
2429 if (!request)
2430 return;
2432 request->mRect = aRect;
2433 request->mFlags = aFlags;
2434 }
2436 /* static */ void
2437 nsPresContext::NotifySubDocInvalidation(ContainerLayer* aContainer,
2438 const nsIntRegion& aRegion)
2439 {
2440 ContainerLayerPresContext *data =
2441 static_cast<ContainerLayerPresContext*>(
2442 aContainer->GetUserData(&gNotifySubDocInvalidationData));
2443 if (!data) {
2444 return;
2445 }
2447 nsIntPoint topLeft = aContainer->GetVisibleRegion().GetBounds().TopLeft();
2449 nsIntRegionRectIterator iter(aRegion);
2450 while (const nsIntRect* r = iter.Next()) {
2451 nsIntRect rect = *r;
2452 //PresContext coordinate space is relative to the start of our visible
2453 // region. Is this really true? This feels like the wrong way to get the right
2454 // answer.
2455 rect.MoveBy(-topLeft);
2456 data->mPresContext->NotifyInvalidation(rect, 0);
2457 }
2458 }
2460 void
2461 nsPresContext::SetNotifySubDocInvalidationData(ContainerLayer* aContainer)
2462 {
2463 ContainerLayerPresContext* pres = new ContainerLayerPresContext;
2464 pres->mPresContext = this;
2465 aContainer->SetUserData(&gNotifySubDocInvalidationData, pres);
2466 }
2468 /* static */ void
2469 nsPresContext::ClearNotifySubDocInvalidationData(ContainerLayer* aContainer)
2470 {
2471 aContainer->SetUserData(&gNotifySubDocInvalidationData, nullptr);
2472 }
2474 struct NotifyDidPaintSubdocumentCallbackClosure {
2475 uint32_t mFlags;
2476 bool mNeedsAnotherDidPaintNotification;
2477 };
2478 static bool
2479 NotifyDidPaintSubdocumentCallback(nsIDocument* aDocument, void* aData)
2480 {
2481 NotifyDidPaintSubdocumentCallbackClosure* closure =
2482 static_cast<NotifyDidPaintSubdocumentCallbackClosure*>(aData);
2483 nsIPresShell* shell = aDocument->GetShell();
2484 if (shell) {
2485 nsPresContext* pc = shell->GetPresContext();
2486 if (pc) {
2487 pc->NotifyDidPaintForSubtree(closure->mFlags);
2488 if (pc->IsDOMPaintEventPending()) {
2489 closure->mNeedsAnotherDidPaintNotification = true;
2490 }
2491 }
2492 }
2493 return true;
2494 }
2496 class DelayedFireDOMPaintEvent : public nsRunnable {
2497 public:
2498 DelayedFireDOMPaintEvent(nsPresContext* aPresContext,
2499 nsInvalidateRequestList* aList)
2500 : mPresContext(aPresContext)
2501 {
2502 MOZ_ASSERT(mPresContext->GetContainerWeak(),
2503 "DOMPaintEvent requested for a detached pres context");
2504 mList.TakeFrom(aList);
2505 }
2506 NS_IMETHOD Run() MOZ_OVERRIDE
2507 {
2508 // The pres context might have been detached during the delay -
2509 // that's fine, just don't fire the event.
2510 if (mPresContext->GetContainerWeak()) {
2511 mPresContext->FireDOMPaintEvent(&mList);
2512 }
2513 return NS_OK;
2514 }
2516 nsRefPtr<nsPresContext> mPresContext;
2517 nsInvalidateRequestList mList;
2518 };
2520 void
2521 nsPresContext::NotifyDidPaintForSubtree(uint32_t aFlags)
2522 {
2523 if (IsRoot()) {
2524 static_cast<nsRootPresContext*>(this)->CancelDidPaintTimer();
2526 if (!mFireAfterPaintEvents) {
2527 return;
2528 }
2529 }
2530 // Non-root prescontexts fire MozAfterPaint to all their descendants
2531 // unconditionally, even if no invalidations have been collected. This is
2532 // because we don't want to eat the cost of collecting invalidations for
2533 // every subdocument (which would require putting every subdocument in its
2534 // own layer).
2536 if (aFlags & nsIPresShell::PAINT_LAYERS) {
2537 mUndeliveredInvalidateRequestsBeforeLastPaint.TakeFrom(
2538 &mInvalidateRequestsSinceLastPaint);
2539 mAllInvalidated = false;
2540 }
2541 if (aFlags & nsIPresShell::PAINT_COMPOSITE) {
2542 nsCOMPtr<nsIRunnable> ev =
2543 new DelayedFireDOMPaintEvent(this, &mUndeliveredInvalidateRequestsBeforeLastPaint);
2544 nsContentUtils::AddScriptRunner(ev);
2545 }
2547 NotifyDidPaintSubdocumentCallbackClosure closure = { aFlags, false };
2548 mDocument->EnumerateSubDocuments(NotifyDidPaintSubdocumentCallback, &closure);
2550 if (!closure.mNeedsAnotherDidPaintNotification &&
2551 mInvalidateRequestsSinceLastPaint.IsEmpty() &&
2552 mUndeliveredInvalidateRequestsBeforeLastPaint.IsEmpty()) {
2553 // Nothing more to do for the moment.
2554 mFireAfterPaintEvents = false;
2555 } else {
2556 if (IsRoot()) {
2557 static_cast<nsRootPresContext*>(this)->EnsureEventualDidPaintEvent();
2558 }
2559 }
2560 }
2562 bool
2563 nsPresContext::HasCachedStyleData()
2564 {
2565 return mShell && mShell->StyleSet()->HasCachedStyleData();
2566 }
2568 static bool sGotInterruptEnv = false;
2569 enum InterruptMode {
2570 ModeRandom,
2571 ModeCounter,
2572 ModeEvent
2573 };
2574 // Controlled by the GECKO_REFLOW_INTERRUPT_MODE env var; allowed values are
2575 // "random" (except on Windows) or "counter". If neither is used, the mode is
2576 // ModeEvent.
2577 static InterruptMode sInterruptMode = ModeEvent;
2578 #ifndef XP_WIN
2579 // Used for the "random" mode. Controlled by the GECKO_REFLOW_INTERRUPT_SEED
2580 // env var.
2581 static uint32_t sInterruptSeed = 1;
2582 #endif
2583 // Used for the "counter" mode. This is the number of unskipped interrupt
2584 // checks that have to happen before we interrupt. Controlled by the
2585 // GECKO_REFLOW_INTERRUPT_FREQUENCY env var.
2586 static uint32_t sInterruptMaxCounter = 10;
2587 // Used for the "counter" mode. This counts up to sInterruptMaxCounter and is
2588 // then reset to 0.
2589 static uint32_t sInterruptCounter;
2590 // Number of interrupt checks to skip before really trying to interrupt.
2591 // Controlled by the GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP env var.
2592 static uint32_t sInterruptChecksToSkip = 200;
2593 // Number of milliseconds that a reflow should be allowed to run for before we
2594 // actually allow interruption. Controlled by the
2595 // GECKO_REFLOW_MIN_NOINTERRUPT_DURATION env var. Can't be initialized here,
2596 // because TimeDuration/TimeStamp is not safe to use in static constructors..
2597 static TimeDuration sInterruptTimeout;
2599 static void GetInterruptEnv()
2600 {
2601 char *ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_MODE");
2602 if (ev) {
2603 #ifndef XP_WIN
2604 if (PL_strcasecmp(ev, "random") == 0) {
2605 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_SEED");
2606 if (ev) {
2607 sInterruptSeed = atoi(ev);
2608 }
2609 srandom(sInterruptSeed);
2610 sInterruptMode = ModeRandom;
2611 } else
2612 #endif
2613 if (PL_strcasecmp(ev, "counter") == 0) {
2614 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_FREQUENCY");
2615 if (ev) {
2616 sInterruptMaxCounter = atoi(ev);
2617 }
2618 sInterruptCounter = 0;
2619 sInterruptMode = ModeCounter;
2620 }
2621 }
2622 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP");
2623 if (ev) {
2624 sInterruptChecksToSkip = atoi(ev);
2625 }
2627 ev = PR_GetEnv("GECKO_REFLOW_MIN_NOINTERRUPT_DURATION");
2628 int duration_ms = ev ? atoi(ev) : 100;
2629 sInterruptTimeout = TimeDuration::FromMilliseconds(duration_ms);
2630 }
2632 bool
2633 nsPresContext::HavePendingInputEvent()
2634 {
2635 switch (sInterruptMode) {
2636 #ifndef XP_WIN
2637 case ModeRandom:
2638 return (random() & 1);
2639 #endif
2640 case ModeCounter:
2641 if (sInterruptCounter < sInterruptMaxCounter) {
2642 ++sInterruptCounter;
2643 return false;
2644 }
2645 sInterruptCounter = 0;
2646 return true;
2647 default:
2648 case ModeEvent: {
2649 nsIFrame* f = PresShell()->GetRootFrame();
2650 if (f) {
2651 nsIWidget* w = f->GetNearestWidget();
2652 if (w) {
2653 return w->HasPendingInputEvent();
2654 }
2655 }
2656 return false;
2657 }
2658 }
2659 }
2661 void
2662 nsPresContext::ReflowStarted(bool aInterruptible)
2663 {
2664 #ifdef NOISY_INTERRUPTIBLE_REFLOW
2665 if (!aInterruptible) {
2666 printf("STARTING NONINTERRUPTIBLE REFLOW\n");
2667 }
2668 #endif
2669 // We don't support interrupting in paginated contexts, since page
2670 // sequences only handle initial reflow
2671 mInterruptsEnabled = aInterruptible && !IsPaginated() &&
2672 nsLayoutUtils::InterruptibleReflowEnabled();
2674 // Don't set mHasPendingInterrupt based on HavePendingInputEvent() here. If
2675 // we ever change that, then we need to update the code in
2676 // PresShell::DoReflow to only add the just-reflown root to dirty roots if
2677 // it's actually dirty. Otherwise we can end up adding a root that has no
2678 // interruptible descendants, just because we detected an interrupt at reflow
2679 // start.
2680 mHasPendingInterrupt = false;
2682 mInterruptChecksToSkip = sInterruptChecksToSkip;
2684 if (mInterruptsEnabled) {
2685 mReflowStartTime = TimeStamp::Now();
2686 }
2687 }
2689 bool
2690 nsPresContext::CheckForInterrupt(nsIFrame* aFrame)
2691 {
2692 if (mHasPendingInterrupt) {
2693 mShell->FrameNeedsToContinueReflow(aFrame);
2694 return true;
2695 }
2697 if (!sGotInterruptEnv) {
2698 sGotInterruptEnv = true;
2699 GetInterruptEnv();
2700 }
2702 if (!mInterruptsEnabled) {
2703 return false;
2704 }
2706 if (mInterruptChecksToSkip > 0) {
2707 --mInterruptChecksToSkip;
2708 return false;
2709 }
2710 mInterruptChecksToSkip = sInterruptChecksToSkip;
2712 // Don't interrupt if it's been less than sInterruptTimeout since we started
2713 // the reflow.
2714 mHasPendingInterrupt =
2715 TimeStamp::Now() - mReflowStartTime > sInterruptTimeout &&
2716 HavePendingInputEvent() &&
2717 !IsChrome();
2718 if (mHasPendingInterrupt) {
2719 #ifdef NOISY_INTERRUPTIBLE_REFLOW
2720 printf("*** DETECTED pending interrupt (time=%lld)\n", PR_Now());
2721 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
2722 mShell->FrameNeedsToContinueReflow(aFrame);
2723 }
2724 return mHasPendingInterrupt;
2725 }
2727 nsIFrame*
2728 nsPresContext::GetPrimaryFrameFor(nsIContent* aContent)
2729 {
2730 NS_PRECONDITION(aContent, "Don't do that");
2731 if (GetPresShell() &&
2732 GetPresShell()->GetDocument() == aContent->GetCurrentDoc()) {
2733 return aContent->GetPrimaryFrame();
2734 }
2735 return nullptr;
2736 }
2739 size_t
2740 nsPresContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
2741 {
2742 return mPropertyTable.SizeOfExcludingThis(aMallocSizeOf);
2743 mLangGroupFontPrefs.SizeOfExcludingThis(aMallocSizeOf);
2745 // Measurement of other members may be added later if DMD finds it is
2746 // worthwhile.
2747 }
2749 bool
2750 nsPresContext::IsRootContentDocument()
2751 {
2752 // We are a root content document if: we are not a resource doc, we are
2753 // not chrome, and we either have no parent or our parent is chrome.
2754 if (mDocument->IsResourceDoc()) {
2755 return false;
2756 }
2757 if (IsChrome()) {
2758 return false;
2759 }
2760 // We may not have a root frame, so use views.
2761 nsView* view = PresShell()->GetViewManager()->GetRootView();
2762 if (!view) {
2763 return false;
2764 }
2765 view = view->GetParent(); // anonymous inner view
2766 if (!view) {
2767 return true;
2768 }
2769 view = view->GetParent(); // subdocumentframe's view
2770 if (!view) {
2771 return true;
2772 }
2774 nsIFrame* f = view->GetFrame();
2775 return (f && f->PresContext()->IsChrome());
2776 }
2778 bool
2779 nsPresContext::IsCrossProcessRootContentDocument()
2780 {
2781 if (!IsRootContentDocument()) {
2782 return false;
2783 }
2785 if (XRE_GetProcessType() == GeckoProcessType_Default) {
2786 return true;
2787 }
2789 TabChild* tabChild = TabChild::GetFrom(mShell);
2790 return (tabChild && tabChild->IsRootContentDocument());
2791 }
2793 bool nsPresContext::GetPaintFlashing() const
2794 {
2795 if (!mPaintFlashingInitialized) {
2796 bool pref = Preferences::GetBool("nglayout.debug.paint_flashing");
2797 if (!pref && IsChrome()) {
2798 pref = Preferences::GetBool("nglayout.debug.paint_flashing_chrome");
2799 }
2800 mPaintFlashing = pref;
2801 mPaintFlashingInitialized = true;
2802 }
2803 return mPaintFlashing;
2804 }
2806 int32_t
2807 nsPresContext::AppUnitsPerDevPixel() const
2808 {
2809 return mDeviceContext->AppUnitsPerDevPixel();
2810 }
2812 nscoord
2813 nsPresContext::GfxUnitsToAppUnits(gfxFloat aGfxUnits) const
2814 {
2815 return mDeviceContext->GfxUnitsToAppUnits(aGfxUnits);
2816 }
2818 gfxFloat
2819 nsPresContext::AppUnitsToGfxUnits(nscoord aAppUnits) const
2820 {
2821 return mDeviceContext->AppUnitsToGfxUnits(aAppUnits);
2822 }
2824 bool
2825 nsPresContext::IsDeviceSizePageSize()
2826 {
2827 bool isDeviceSizePageSize = false;
2828 nsCOMPtr<nsIDocShell> docShell(mContainer);
2829 if (docShell) {
2830 isDeviceSizePageSize = docShell->GetDeviceSizeIsPageSize();
2831 }
2832 return isDeviceSizePageSize;
2833 }
2835 nsRootPresContext::nsRootPresContext(nsIDocument* aDocument,
2836 nsPresContextType aType)
2837 : nsPresContext(aDocument, aType),
2838 mDOMGeneration(0)
2839 {
2840 }
2842 nsRootPresContext::~nsRootPresContext()
2843 {
2844 NS_ASSERTION(mRegisteredPlugins.Count() == 0,
2845 "All plugins should have been unregistered");
2846 CancelDidPaintTimer();
2847 CancelApplyPluginGeometryTimer();
2848 }
2850 /* virtual */ void
2851 nsRootPresContext::Detach()
2852 {
2853 CancelDidPaintTimer();
2854 // XXXmats maybe also CancelApplyPluginGeometryTimer(); ?
2855 nsPresContext::Detach();
2856 }
2858 void
2859 nsRootPresContext::RegisterPluginForGeometryUpdates(nsIContent* aPlugin)
2860 {
2861 mRegisteredPlugins.PutEntry(aPlugin);
2862 }
2864 void
2865 nsRootPresContext::UnregisterPluginForGeometryUpdates(nsIContent* aPlugin)
2866 {
2867 mRegisteredPlugins.RemoveEntry(aPlugin);
2868 }
2870 static PLDHashOperator
2871 SetPluginHidden(nsRefPtrHashKey<nsIContent>* aEntry, void* userArg)
2872 {
2873 nsIFrame* root = static_cast<nsIFrame*>(userArg);
2874 nsObjectFrame* f = static_cast<nsObjectFrame*>(aEntry->GetKey()->GetPrimaryFrame());
2875 if (!f) {
2876 NS_WARNING("Null frame in SetPluginHidden");
2877 return PL_DHASH_NEXT;
2878 }
2879 if (!nsLayoutUtils::IsAncestorFrameCrossDoc(root, f)) {
2880 // f is not managed by this frame so we should ignore it.
2881 return PL_DHASH_NEXT;
2882 }
2883 f->SetEmptyWidgetConfiguration();
2884 return PL_DHASH_NEXT;
2885 }
2887 void
2888 nsRootPresContext::ComputePluginGeometryUpdates(nsIFrame* aFrame,
2889 nsDisplayListBuilder* aBuilder,
2890 nsDisplayList* aList)
2891 {
2892 if (mRegisteredPlugins.Count() == 0) {
2893 return;
2894 }
2896 // Initially make the next state for each plugin descendant of aFrame be
2897 // "hidden". Plugins that are visible will have their next state set to
2898 // unhidden by nsDisplayPlugin::ComputeVisibility.
2899 mRegisteredPlugins.EnumerateEntries(SetPluginHidden, aFrame);
2901 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
2903 if (rootFrame && aBuilder->ContainsPluginItem()) {
2904 aBuilder->SetForPluginGeometry();
2905 aBuilder->SetAccurateVisibleRegions();
2906 // Merging and flattening has already been done and we should not do it
2907 // again. nsDisplayScroll(Info)Layer doesn't support trying to flatten
2908 // again.
2909 aBuilder->SetAllowMergingAndFlattening(false);
2910 nsRegion region = rootFrame->GetVisualOverflowRectRelativeToSelf();
2911 // nsDisplayPlugin::ComputeVisibility will automatically set a non-hidden
2912 // widget configuration for the plugin, if it's visible.
2913 aList->ComputeVisibilityForRoot(aBuilder, ®ion);
2914 }
2916 #ifdef XP_MACOSX
2917 // We control painting of Mac plugins, so just apply geometry updates now.
2918 // This is not happening during a paint event.
2919 ApplyPluginGeometryUpdates();
2920 #else
2921 InitApplyPluginGeometryTimer();
2922 #endif
2923 }
2925 static void
2926 ApplyPluginGeometryUpdatesCallback(nsITimer *aTimer, void *aClosure)
2927 {
2928 static_cast<nsRootPresContext*>(aClosure)->ApplyPluginGeometryUpdates();
2929 }
2931 void
2932 nsRootPresContext::InitApplyPluginGeometryTimer()
2933 {
2934 if (mApplyPluginGeometryTimer) {
2935 return;
2936 }
2938 // We'll apply the plugin geometry updates during the next compositing paint in this
2939 // presContext (either from nsPresShell::WillPaintWindow or from
2940 // nsPresShell::DidPaintWindow, depending on the platform). But paints might
2941 // get optimized away if the old plugin geometry covers the invalid region,
2942 // so set a backup timer to do this too. We want to make sure this
2943 // won't fire before our normal paint notifications, if those would
2944 // update the geometry, so set it for double the refresh driver interval.
2945 mApplyPluginGeometryTimer = do_CreateInstance("@mozilla.org/timer;1");
2946 if (mApplyPluginGeometryTimer) {
2947 mApplyPluginGeometryTimer->
2948 InitWithFuncCallback(ApplyPluginGeometryUpdatesCallback, this,
2949 nsRefreshDriver::DefaultInterval() * 2,
2950 nsITimer::TYPE_ONE_SHOT);
2951 }
2952 }
2954 void
2955 nsRootPresContext::CancelApplyPluginGeometryTimer()
2956 {
2957 if (mApplyPluginGeometryTimer) {
2958 mApplyPluginGeometryTimer->Cancel();
2959 mApplyPluginGeometryTimer = nullptr;
2960 }
2961 }
2963 static bool
2964 HasOverlap(const nsIntPoint& aOffset1, const nsTArray<nsIntRect>& aClipRects1,
2965 const nsIntPoint& aOffset2, const nsTArray<nsIntRect>& aClipRects2)
2966 {
2967 nsIntPoint offsetDelta = aOffset1 - aOffset2;
2968 for (uint32_t i = 0; i < aClipRects1.Length(); ++i) {
2969 for (uint32_t j = 0; j < aClipRects2.Length(); ++j) {
2970 if ((aClipRects1[i] + offsetDelta).Intersects(aClipRects2[j]))
2971 return true;
2972 }
2973 }
2974 return false;
2975 }
2977 /**
2978 * Given a list of plugin windows to move to new locations, sort the list
2979 * so that for each window move, the window moves to a location that
2980 * does not intersect other windows. This minimizes flicker and repainting.
2981 * It's not always possible to do this perfectly, since in general
2982 * we might have cycles. But we do our best.
2983 * We need to take into account that windows are clipped to particular
2984 * regions and the clip regions change as the windows are moved.
2985 */
2986 static void
2987 SortConfigurations(nsTArray<nsIWidget::Configuration>* aConfigurations)
2988 {
2989 if (aConfigurations->Length() > 10) {
2990 // Give up, we don't want to get bogged down here
2991 return;
2992 }
2994 nsTArray<nsIWidget::Configuration> pluginsToMove;
2995 pluginsToMove.SwapElements(*aConfigurations);
2997 // Our algorithm is quite naive. At each step we try to identify
2998 // a window that can be moved to its new location that won't overlap
2999 // any other windows at the new location. If there is no such
3000 // window, we just move the last window in the list anyway.
3001 while (!pluginsToMove.IsEmpty()) {
3002 // Find a window whose destination does not overlap any other window
3003 uint32_t i;
3004 for (i = 0; i + 1 < pluginsToMove.Length(); ++i) {
3005 nsIWidget::Configuration* config = &pluginsToMove[i];
3006 bool foundOverlap = false;
3007 for (uint32_t j = 0; j < pluginsToMove.Length(); ++j) {
3008 if (i == j)
3009 continue;
3010 nsIntRect bounds;
3011 pluginsToMove[j].mChild->GetBounds(bounds);
3012 nsAutoTArray<nsIntRect,1> clipRects;
3013 pluginsToMove[j].mChild->GetWindowClipRegion(&clipRects);
3014 if (HasOverlap(bounds.TopLeft(), clipRects,
3015 config->mBounds.TopLeft(),
3016 config->mClipRegion)) {
3017 foundOverlap = true;
3018 break;
3019 }
3020 }
3021 if (!foundOverlap)
3022 break;
3023 }
3024 // Note that we always move the last plugin in pluginsToMove, if we
3025 // can't find any other plugin to move
3026 aConfigurations->AppendElement(pluginsToMove[i]);
3027 pluginsToMove.RemoveElementAt(i);
3028 }
3029 }
3031 static PLDHashOperator
3032 PluginDidSetGeometryEnumerator(nsRefPtrHashKey<nsIContent>* aEntry, void* userArg)
3033 {
3034 nsObjectFrame* f = static_cast<nsObjectFrame*>(aEntry->GetKey()->GetPrimaryFrame());
3035 if (!f) {
3036 NS_WARNING("Null frame in PluginDidSetGeometryEnumerator");
3037 return PL_DHASH_NEXT;
3038 }
3039 f->DidSetWidgetGeometry();
3040 return PL_DHASH_NEXT;
3041 }
3043 struct PluginGetGeometryUpdateClosure {
3044 nsTArray<nsIWidget::Configuration> mConfigurations;
3045 };
3046 static PLDHashOperator
3047 PluginGetGeometryUpdate(nsRefPtrHashKey<nsIContent>* aEntry, void* userArg)
3048 {
3049 PluginGetGeometryUpdateClosure* closure =
3050 static_cast<PluginGetGeometryUpdateClosure*>(userArg);
3051 nsObjectFrame* f = static_cast<nsObjectFrame*>(aEntry->GetKey()->GetPrimaryFrame());
3052 if (!f) {
3053 NS_WARNING("Null frame in GetPluginGeometryUpdate");
3054 return PL_DHASH_NEXT;
3055 }
3056 f->GetWidgetConfiguration(&closure->mConfigurations);
3057 return PL_DHASH_NEXT;
3058 }
3060 void
3061 nsRootPresContext::ApplyPluginGeometryUpdates()
3062 {
3063 CancelApplyPluginGeometryTimer();
3065 PluginGetGeometryUpdateClosure closure;
3066 mRegisteredPlugins.EnumerateEntries(PluginGetGeometryUpdate, &closure);
3067 // Walk mRegisteredPlugins and ask each plugin for its configuration
3068 if (!closure.mConfigurations.IsEmpty()) {
3069 nsIWidget* widget = closure.mConfigurations[0].mChild->GetParent();
3070 NS_ASSERTION(widget, "Plugins must have a parent window");
3071 SortConfigurations(&closure.mConfigurations);
3072 widget->ConfigureChildren(closure.mConfigurations);
3073 }
3074 mRegisteredPlugins.EnumerateEntries(PluginDidSetGeometryEnumerator, nullptr);
3075 }
3077 static void
3078 NotifyDidPaintForSubtreeCallback(nsITimer *aTimer, void *aClosure)
3079 {
3080 nsPresContext* presContext = (nsPresContext*)aClosure;
3081 nsAutoScriptBlocker blockScripts;
3082 // This is a fallback if we don't get paint events for some reason
3083 // so we'll just pretend both layer painting and compositing happened.
3084 presContext->NotifyDidPaintForSubtree(
3085 nsIPresShell::PAINT_LAYERS | nsIPresShell::PAINT_COMPOSITE);
3086 }
3088 void
3089 nsRootPresContext::EnsureEventualDidPaintEvent()
3090 {
3091 if (mNotifyDidPaintTimer)
3092 return;
3093 mNotifyDidPaintTimer = do_CreateInstance("@mozilla.org/timer;1");
3094 if (!mNotifyDidPaintTimer)
3095 return;
3096 mNotifyDidPaintTimer->InitWithFuncCallback(NotifyDidPaintForSubtreeCallback,
3097 (void*)this, 100, nsITimer::TYPE_ONE_SHOT);
3098 }
3100 void
3101 nsRootPresContext::AddWillPaintObserver(nsIRunnable* aRunnable)
3102 {
3103 if (!mWillPaintFallbackEvent.IsPending()) {
3104 mWillPaintFallbackEvent = new RunWillPaintObservers(this);
3105 NS_DispatchToMainThread(mWillPaintFallbackEvent.get());
3106 }
3107 mWillPaintObservers.AppendElement(aRunnable);
3108 }
3110 /**
3111 * Run all runnables that need to get called before the next paint.
3112 */
3113 void
3114 nsRootPresContext::FlushWillPaintObservers()
3115 {
3116 mWillPaintFallbackEvent = nullptr;
3117 nsTArray<nsCOMPtr<nsIRunnable> > observers;
3118 observers.SwapElements(mWillPaintObservers);
3119 for (uint32_t i = 0; i < observers.Length(); ++i) {
3120 observers[i]->Run();
3121 }
3122 }
3124 size_t
3125 nsRootPresContext::SizeOfExcludingThis(MallocSizeOf aMallocSizeOf) const
3126 {
3127 return nsPresContext::SizeOfExcludingThis(aMallocSizeOf);
3129 // Measurement of the following members may be added later if DMD finds it is
3130 // worthwhile:
3131 // - mNotifyDidPaintTimer
3132 // - mRegisteredPlugins
3133 // - mWillPaintObservers
3134 // - mWillPaintFallbackEvent
3135 }