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