layout/base/nsPresContext.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial