Merge mozilla-central and tracemonkey. (a=blockers)
[mozilla-central.git] / layout / base / nsPresContext.cpp
blob875222413d5c6659e6c43ff1f74758cfcee2c926
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
13 * License.
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
22 * Contributor(s):
23 * L. David Baron <dbaron@dbaron.org>, Mozilla Corporation
24 * Ehsan Akhgari <ehsan.akhgari@gmail.com>
26 * Alternatively, the contents of this file may be used under the terms of
27 * either of the GNU General Public License Version 2 or later (the "GPL"),
28 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 * in which case the provisions of the GPL or the LGPL are applicable instead
30 * of those above. If you wish to allow use of your version of this file only
31 * under the terms of either the GPL or the LGPL, and not to allow others to
32 * use your version of this file under the terms of the MPL, indicate your
33 * decision by deleting the provisions above and replace them with the notice
34 * and other provisions required by the GPL or the LGPL. If you do not delete
35 * the provisions above, a recipient may use your version of this file under
36 * the terms of any one of the MPL, the GPL or the LGPL.
38 * ***** END LICENSE BLOCK ***** */
40 /* a presentation of a document, part 1 */
42 #include "nsCOMPtr.h"
43 #include "nsPresContext.h"
44 #include "nsIPresShell.h"
45 #include "nsILinkHandler.h"
46 #include "nsIDocShellTreeItem.h"
47 #include "nsIDocShell.h"
48 #include "nsIContentViewer.h"
49 #include "nsIDocumentViewer.h"
50 #include "nsPIDOMWindow.h"
51 #include "nsStyleSet.h"
52 #include "nsImageLoader.h"
53 #include "nsIContent.h"
54 #include "nsIFrame.h"
55 #include "nsIRenderingContext.h"
56 #include "nsIURL.h"
57 #include "nsIDocument.h"
58 #include "nsStyleContext.h"
59 #include "nsILookAndFeel.h"
60 #include "nsWidgetsCID.h"
61 #include "nsIComponentManager.h"
62 #include "nsIURIContentListener.h"
63 #include "nsIInterfaceRequestor.h"
64 #include "nsIInterfaceRequestorUtils.h"
65 #include "nsIServiceManager.h"
66 #include "nsIDOMElement.h"
67 #include "nsContentPolicyUtils.h"
68 #include "nsIDOMWindow.h"
69 #include "nsXPIDLString.h"
70 #include "nsIWeakReferenceUtils.h"
71 #include "nsCSSRendering.h"
72 #include "prprf.h"
73 #include "nsContentPolicyUtils.h"
74 #include "nsIDOMDocument.h"
75 #include "nsAutoPtr.h"
76 #include "nsEventStateManager.h"
77 #include "nsThreadUtils.h"
78 #include "nsFrameManager.h"
79 #include "nsLayoutUtils.h"
80 #include "nsIViewManager.h"
81 #include "nsCSSFrameConstructor.h"
82 #include "nsCSSRuleProcessor.h"
83 #include "nsStyleChangeList.h"
84 #include "nsRuleNode.h"
85 #include "nsEventDispatcher.h"
86 #include "gfxUserFontSet.h"
87 #include "gfxPlatform.h"
88 #include "nsCSSRules.h"
89 #include "nsFontFaceLoader.h"
90 #include "nsIEventListenerManager.h"
91 #include "nsStyleStructInlines.h"
92 #include "nsIAppShell.h"
93 #include "prenv.h"
94 #include "nsIPrivateDOMEvent.h"
95 #include "nsIDOMEventTarget.h"
96 #include "nsObjectFrame.h"
97 #include "nsTransitionManager.h"
98 #include "mozilla/dom/Element.h"
99 #include "nsIFrameMessageManager.h"
101 #ifdef MOZ_SMIL
102 #include "nsSMILAnimationController.h"
103 #endif // MOZ_SMIL
105 #ifdef IBMBIDI
106 #include "nsBidiPresUtils.h"
107 #endif // IBMBIDI
109 #include "nsContentUtils.h"
110 #include "nsPIWindowRoot.h"
112 // Needed for Start/Stop of Image Animation
113 #include "imgIContainer.h"
114 #include "nsIImageLoadingContent.h"
116 //needed for resetting of image service color
117 #include "nsLayoutCID.h"
119 using mozilla::TimeDuration;
120 using mozilla::TimeStamp;
121 using namespace mozilla::dom;
123 static nscolor
124 MakeColorPref(const char *colstr)
126 PRUint32 red, green, blue;
127 nscolor colorref;
129 // 4.x stored RGB color values as a string rather than as an int,
130 // thus we need to do this conversion
131 PR_sscanf(colstr, "#%02x%02x%02x", &red, &green, &blue);
132 colorref = NS_RGB(red, green, blue);
133 return colorref;
137 nsPresContext::PrefChangedCallback(const char* aPrefName, void* instance_data)
139 nsPresContext* presContext = (nsPresContext*)instance_data;
141 NS_ASSERTION(nsnull != presContext, "bad instance data");
142 if (nsnull != presContext) {
143 presContext->PreferenceChanged(aPrefName);
145 return 0; // PREF_OK
149 void
150 nsPresContext::PrefChangedUpdateTimerCallback(nsITimer *aTimer, void *aClosure)
152 nsPresContext* presContext = (nsPresContext*)aClosure;
153 NS_ASSERTION(presContext != nsnull, "bad instance data");
154 if (presContext)
155 presContext->UpdateAfterPreferencesChanged();
158 #ifdef IBMBIDI
159 static PRBool
160 IsVisualCharset(const nsCString& aCharset)
162 if (aCharset.LowerCaseEqualsLiteral("ibm864") // Arabic//ahmed
163 || aCharset.LowerCaseEqualsLiteral("ibm862") // Hebrew
164 || aCharset.LowerCaseEqualsLiteral("iso-8859-8") ) { // Hebrew
165 return PR_TRUE; // visual text type
167 else {
168 return PR_FALSE; // logical text type
171 #endif // IBMBIDI
174 static PLDHashOperator
175 destroy_loads(const void * aKey, nsRefPtr<nsImageLoader>& aData, void* closure)
177 aData->Destroy();
178 return PL_DHASH_NEXT;
181 static NS_DEFINE_CID(kLookAndFeelCID, NS_LOOKANDFEEL_CID);
182 #include "nsContentCID.h"
184 // NOTE! nsPresContext::operator new() zeroes out all members, so don't
185 // bother initializing members to 0.
187 nsPresContext::nsPresContext(nsIDocument* aDocument, nsPresContextType aType)
188 : mType(aType), mDocument(aDocument), mTextZoom(1.0), mFullZoom(1.0),
189 mPageSize(-1, -1), mPPScale(1.0f),
190 mViewportStyleOverflow(NS_STYLE_OVERFLOW_AUTO, NS_STYLE_OVERFLOW_AUTO),
191 mImageAnimationModePref(imgIContainer::kNormalAnimMode),
192 // Font sizes default to zero; they will be set in GetFontPreferences
193 mDefaultVariableFont("serif", NS_FONT_STYLE_NORMAL, NS_FONT_VARIANT_NORMAL,
194 NS_FONT_WEIGHT_NORMAL, NS_FONT_STRETCH_NORMAL, 0, 0),
195 mDefaultFixedFont("monospace", NS_FONT_STYLE_NORMAL,
196 NS_FONT_VARIANT_NORMAL, NS_FONT_WEIGHT_NORMAL,
197 NS_FONT_STRETCH_NORMAL, 0, 0),
198 mDefaultSerifFont("serif", NS_FONT_STYLE_NORMAL, NS_FONT_VARIANT_NORMAL,
199 NS_FONT_WEIGHT_NORMAL, NS_FONT_STRETCH_NORMAL, 0, 0),
200 mDefaultSansSerifFont("sans-serif", NS_FONT_STYLE_NORMAL,
201 NS_FONT_VARIANT_NORMAL, NS_FONT_WEIGHT_NORMAL,
202 NS_FONT_STRETCH_NORMAL, 0, 0),
203 mDefaultMonospaceFont("monospace", NS_FONT_STYLE_NORMAL,
204 NS_FONT_VARIANT_NORMAL, NS_FONT_WEIGHT_NORMAL,
205 NS_FONT_STRETCH_NORMAL, 0, 0),
206 mDefaultCursiveFont("cursive", NS_FONT_STYLE_NORMAL,
207 NS_FONT_VARIANT_NORMAL, NS_FONT_WEIGHT_NORMAL,
208 NS_FONT_STRETCH_NORMAL, 0, 0),
209 mDefaultFantasyFont("fantasy", NS_FONT_STYLE_NORMAL,
210 NS_FONT_VARIANT_NORMAL, NS_FONT_WEIGHT_NORMAL,
211 NS_FONT_STRETCH_NORMAL, 0, 0)
213 // NOTE! nsPresContext::operator new() zeroes out all members, so don't
214 // bother initializing members to 0.
216 mDoScaledTwips = PR_TRUE;
218 SetBackgroundImageDraw(PR_TRUE); // always draw the background
219 SetBackgroundColorDraw(PR_TRUE);
221 mBackgroundColor = NS_RGB(0xFF, 0xFF, 0xFF);
223 mUseDocumentColors = PR_TRUE;
224 mUseDocumentFonts = PR_TRUE;
226 // the minimum font-size is unconstrained by default
228 mLinkColor = NS_RGB(0x00, 0x00, 0xEE);
229 mActiveLinkColor = NS_RGB(0xEE, 0x00, 0x00);
230 mVisitedLinkColor = NS_RGB(0x55, 0x1A, 0x8B);
231 mUnderlineLinks = PR_TRUE;
232 mSendAfterPaintToContent = PR_FALSE;
234 mFocusTextColor = mDefaultColor;
235 mFocusBackgroundColor = mBackgroundColor;
236 mFocusRingWidth = 1;
238 if (aType == eContext_Galley) {
239 mMedium = nsGkAtoms::screen;
240 } else {
241 mMedium = nsGkAtoms::print;
242 mPaginated = PR_TRUE;
245 if (!IsDynamic()) {
246 mImageAnimationMode = imgIContainer::kDontAnimMode;
247 mNeverAnimate = PR_TRUE;
248 } else {
249 mImageAnimationMode = imgIContainer::kNormalAnimMode;
250 mNeverAnimate = PR_FALSE;
252 NS_ASSERTION(mDocument, "Null document");
253 mUserFontSet = nsnull;
254 mUserFontSetDirty = PR_TRUE;
257 nsPresContext::~nsPresContext()
259 NS_PRECONDITION(!mShell, "Presshell forgot to clear our mShell pointer");
260 SetShell(nsnull);
262 // Disconnect the refresh driver *after* the transition manager, which
263 // needs it.
264 if (mRefreshDriver && mRefreshDriver->PresContext() == this) {
265 mRefreshDriver->Disconnect();
268 if (mEventManager) {
269 // unclear if these are needed, but can't hurt
270 mEventManager->NotifyDestroyPresContext(this);
271 mEventManager->SetPresContext(nsnull);
273 NS_RELEASE(mEventManager);
276 if (mPrefChangedTimer)
278 mPrefChangedTimer->Cancel();
279 mPrefChangedTimer = nsnull;
282 // Unregister preference callbacks
283 nsContentUtils::UnregisterPrefCallback("font.",
284 nsPresContext::PrefChangedCallback,
285 this);
286 nsContentUtils::UnregisterPrefCallback("browser.display.",
287 nsPresContext::PrefChangedCallback,
288 this);
289 nsContentUtils::UnregisterPrefCallback("browser.underline_anchors",
290 nsPresContext::PrefChangedCallback,
291 this);
292 nsContentUtils::UnregisterPrefCallback("browser.anchor_color",
293 nsPresContext::PrefChangedCallback,
294 this);
295 nsContentUtils::UnregisterPrefCallback("browser.active_color",
296 nsPresContext::PrefChangedCallback,
297 this);
298 nsContentUtils::UnregisterPrefCallback("browser.visited_color",
299 nsPresContext::PrefChangedCallback,
300 this);
301 nsContentUtils::UnregisterPrefCallback("image.animation_mode",
302 nsPresContext::PrefChangedCallback,
303 this);
304 #ifdef IBMBIDI
305 nsContentUtils::UnregisterPrefCallback("bidi.", PrefChangedCallback, this);
306 #endif // IBMBIDI
307 nsContentUtils::UnregisterPrefCallback("dom.send_after_paint_to_content",
308 nsPresContext::PrefChangedCallback,
309 this);
310 nsContentUtils::UnregisterPrefCallback("gfx.font_rendering.",
311 nsPresContext::PrefChangedCallback,
312 this);
313 nsContentUtils::UnregisterPrefCallback("layout.css.dpi",
314 nsPresContext::PrefChangedCallback,
315 this);
316 nsContentUtils::UnregisterPrefCallback("layout.css.devPixelsPerPx",
317 nsPresContext::PrefChangedCallback,
318 this);
320 NS_IF_RELEASE(mDeviceContext);
321 NS_IF_RELEASE(mLookAndFeel);
322 NS_IF_RELEASE(mLanguage);
325 NS_IMPL_CYCLE_COLLECTION_CLASS(nsPresContext)
327 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsPresContext)
328 NS_INTERFACE_MAP_ENTRY(nsISupports)
329 NS_INTERFACE_MAP_ENTRY(nsIObserver)
330 NS_INTERFACE_MAP_END
332 NS_IMPL_CYCLE_COLLECTING_ADDREF(nsPresContext)
333 NS_IMPL_CYCLE_COLLECTING_RELEASE(nsPresContext)
335 static PLDHashOperator
336 TraverseImageLoader(const void * aKey, nsRefPtr<nsImageLoader>& aData,
337 void* aClosure)
339 nsCycleCollectionTraversalCallback *cb =
340 static_cast<nsCycleCollectionTraversalCallback*>(aClosure);
342 cb->NoteXPCOMChild(aData);
344 return PL_DHASH_NEXT;
347 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsPresContext)
348 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mDocument);
349 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mDeviceContext); // worth bothering?
350 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mEventManager);
351 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLookAndFeel); // a service
352 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_RAWPTR(mLanguage); // an atom
354 for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i)
355 tmp->mImageLoaders[i].Enumerate(TraverseImageLoader, &cb);
357 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mTheme); // a service
358 // NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mLangService); // a service
359 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPrintSettings);
360 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_NSCOMPTR(mPrefChangedTimer);
361 if (tmp->mBidiUtils)
362 tmp->mBidiUtils->Traverse(cb);
363 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
365 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsPresContext)
366 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mDocument);
367 NS_RELEASE(tmp->mDeviceContext); // worth bothering?
368 if (tmp->mEventManager) {
369 // unclear if these are needed, but can't hurt
370 tmp->mEventManager->NotifyDestroyPresContext(tmp);
371 tmp->mEventManager->SetPresContext(nsnull);
373 NS_RELEASE(tmp->mEventManager);
376 // NS_RELEASE(tmp->mLookAndFeel); // a service
377 // NS_RELEASE(tmp->mLanguage); // an atom
379 // NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mTheme); // a service
380 // NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mLangService); // a service
381 NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mPrintSettings);
382 if (tmp->mPrefChangedTimer)
384 tmp->mPrefChangedTimer->Cancel();
385 tmp->mPrefChangedTimer = nsnull;
387 if (tmp->mBidiUtils)
388 tmp->mBidiUtils->Unlink();
389 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
392 #define MAKE_FONT_PREF_KEY(_pref, _s0, _s1) \
393 _pref.Assign(_s0); \
394 _pref.Append(_s1);
396 static const char* const kGenericFont[] = {
397 ".variable.",
398 ".fixed.",
399 ".serif.",
400 ".sans-serif.",
401 ".monospace.",
402 ".cursive.",
403 ".fantasy."
406 // whether no native theme service exists;
407 // if this gets set to true, we'll stop asking for it.
408 static PRBool sNoTheme = PR_FALSE;
410 // Set to true when LookAndFeelChanged needs to be called. This is used
411 // because the look and feel is a service, so there's no need to notify it from
412 // more than one prescontext.
413 static PRBool sLookAndFeelChanged;
415 // Set to true when ThemeChanged needs to be called on mTheme. This is used
416 // because mTheme is a service, so there's no need to notify it from more than
417 // one prescontext.
418 static PRBool sThemeChanged;
420 void
421 nsPresContext::GetFontPreferences()
423 /* Fetch the font prefs to be used -- see bug 61883 for details.
424 Not all prefs are needed upfront. Some are fallback prefs intended
425 for the GFX font sub-system...
427 1) unit : assumed to be the same for all language groups -------------
428 font.size.unit = px | pt XXX could be folded in the size... bug 90440
430 2) attributes for generic fonts --------------------------------------
431 font.default = serif | sans-serif - fallback generic font
432 font.name.[generic].[langGroup] = current user' selected font on the pref dialog
433 font.name-list.[generic].[langGroup] = fontname1, fontname2, ... [factory pre-built list]
434 font.size.[generic].[langGroup] = integer - settable by the user
435 font.size-adjust.[generic].[langGroup] = "float" - settable by the user
436 font.minimum-size.[langGroup] = integer - settable by the user
439 mDefaultVariableFont.size = CSSPixelsToAppUnits(16);
440 mDefaultFixedFont.size = CSSPixelsToAppUnits(13);
442 // the font prefs are based on langGroup, not actual language
443 nsCAutoString langGroup;
444 if (mLanguage && mLangService) {
445 nsresult rv;
446 nsIAtom *group = mLangService->GetLanguageGroup(mLanguage, &rv);
447 if (NS_SUCCEEDED(rv) && group) {
448 group->ToUTF8String(langGroup);
450 else {
451 langGroup.AssignLiteral("x-western"); // Assume x-western is safe...
454 else {
455 langGroup.AssignLiteral("x-western"); // Assume x-western is safe...
458 nsCAutoString pref;
460 // get the current applicable font-size unit
461 enum {eUnit_unknown = -1, eUnit_px, eUnit_pt};
462 PRInt32 unit = eUnit_px;
464 nsAdoptingCString cvalue =
465 nsContentUtils::GetCharPref("font.size.unit");
467 if (!cvalue.IsEmpty()) {
468 if (cvalue.Equals("px")) {
469 unit = eUnit_px;
471 else if (cvalue.Equals("pt")) {
472 unit = eUnit_pt;
474 else {
475 NS_WARNING("unexpected font-size unit -- expected: 'px' or 'pt'");
476 unit = eUnit_unknown;
480 // get font.minimum-size.[langGroup]
482 pref.Assign("font.minimum-size.");
483 pref.Append(langGroup);
485 PRInt32 size = nsContentUtils::GetIntPref(pref.get());
486 if (unit == eUnit_px) {
487 mMinimumFontSize = CSSPixelsToAppUnits(size);
489 else if (unit == eUnit_pt) {
490 mMinimumFontSize = CSSPointsToAppUnits(size);
493 // get attributes specific to each generic font
494 nsCAutoString generic_dot_langGroup;
495 for (PRInt32 eType = eDefaultFont_Variable; eType < eDefaultFont_COUNT; ++eType) {
496 generic_dot_langGroup.Assign(kGenericFont[eType]);
497 generic_dot_langGroup.Append(langGroup);
499 nsFont* font;
500 switch (eType) {
501 case eDefaultFont_Variable: font = &mDefaultVariableFont; break;
502 case eDefaultFont_Fixed: font = &mDefaultFixedFont; break;
503 case eDefaultFont_Serif: font = &mDefaultSerifFont; break;
504 case eDefaultFont_SansSerif: font = &mDefaultSansSerifFont; break;
505 case eDefaultFont_Monospace: font = &mDefaultMonospaceFont; break;
506 case eDefaultFont_Cursive: font = &mDefaultCursiveFont; break;
507 case eDefaultFont_Fantasy: font = &mDefaultFantasyFont; break;
510 // set the default variable font (the other fonts are seen as 'generic' fonts
511 // in GFX and will be queried there when hunting for alternative fonts)
512 if (eType == eDefaultFont_Variable) {
513 MAKE_FONT_PREF_KEY(pref, "font.name", generic_dot_langGroup);
515 nsAdoptingString value =
516 nsContentUtils::GetStringPref(pref.get());
517 if (!value.IsEmpty()) {
518 font->name.Assign(value);
520 else {
521 MAKE_FONT_PREF_KEY(pref, "font.default.", langGroup);
522 value = nsContentUtils::GetStringPref(pref.get());
523 if (!value.IsEmpty()) {
524 mDefaultVariableFont.name.Assign(value);
528 else {
529 if (eType == eDefaultFont_Monospace) {
530 // This takes care of the confusion whereby people often expect "monospace"
531 // to have the same default font-size as "-moz-fixed" (this tentative
532 // size may be overwritten with the specific value for "monospace" when
533 // "font.size.monospace.[langGroup]" is read -- see below)
534 font->size = mDefaultFixedFont.size;
536 else if (eType != eDefaultFont_Fixed) {
537 // all the other generic fonts are initialized with the size of the
538 // variable font, but their specific size can supersede later -- see below
539 font->size = mDefaultVariableFont.size;
543 // Bug 84398: for spec purists, a different font-size only applies to the
544 // .variable. and .fixed. fonts and the other fonts should get |font-size-adjust|.
545 // The problem is that only GfxWin has the support for |font-size-adjust|. So for
546 // parity, we enable the ability to set a different font-size on all platforms.
548 // get font.size.[generic].[langGroup]
549 // size=0 means 'Auto', i.e., generic fonts retain the size of the variable font
550 MAKE_FONT_PREF_KEY(pref, "font.size", generic_dot_langGroup);
551 size = nsContentUtils::GetIntPref(pref.get());
552 if (size > 0) {
553 if (unit == eUnit_px) {
554 font->size = CSSPixelsToAppUnits(size);
556 else if (unit == eUnit_pt) {
557 font->size = CSSPointsToAppUnits(size);
561 // get font.size-adjust.[generic].[langGroup]
562 // XXX only applicable on GFX ports that handle |font-size-adjust|
563 MAKE_FONT_PREF_KEY(pref, "font.size-adjust", generic_dot_langGroup);
564 cvalue = nsContentUtils::GetCharPref(pref.get());
565 if (!cvalue.IsEmpty()) {
566 font->sizeAdjust = (float)atof(cvalue.get());
569 #ifdef DEBUG_rbs
570 printf("%s Family-list:%s size:%d sizeAdjust:%.2f\n",
571 generic_dot_langGroup.get(),
572 NS_ConvertUTF16toUTF8(font->name).get(), font->size,
573 font->sizeAdjust);
574 #endif
578 void
579 nsPresContext::GetDocumentColorPreferences()
581 PRInt32 useAccessibilityTheme = 0;
582 PRBool usePrefColors = PR_TRUE;
583 nsCOMPtr<nsIDocShellTreeItem> docShell(do_QueryReferent(mContainer));
584 if (docShell) {
585 PRInt32 docShellType;
586 docShell->GetItemType(&docShellType);
587 if (nsIDocShellTreeItem::typeChrome == docShellType) {
588 usePrefColors = PR_FALSE;
590 else {
591 mLookAndFeel->GetMetric(nsILookAndFeel::eMetric_UseAccessibilityTheme, useAccessibilityTheme);
592 usePrefColors = !useAccessibilityTheme;
596 if (usePrefColors) {
597 usePrefColors =
598 !nsContentUtils::GetBoolPref("browser.display.use_system_colors",
599 PR_FALSE);
602 if (usePrefColors) {
603 nsAdoptingCString colorStr =
604 nsContentUtils::GetCharPref("browser.display.foreground_color");
606 if (!colorStr.IsEmpty()) {
607 mDefaultColor = MakeColorPref(colorStr);
610 colorStr =
611 nsContentUtils::GetCharPref("browser.display.background_color");
613 if (!colorStr.IsEmpty()) {
614 mBackgroundColor = MakeColorPref(colorStr);
617 else {
618 mDefaultColor = NS_RGB(0x00, 0x00, 0x00);
619 mBackgroundColor = NS_RGB(0xFF, 0xFF, 0xFF);
620 mLookAndFeel->GetColor(nsILookAndFeel::eColor_WindowForeground,
621 mDefaultColor);
622 mLookAndFeel->GetColor(nsILookAndFeel::eColor_WindowBackground,
623 mBackgroundColor);
626 // Wherever we got the default background color from, ensure it is
627 // opaque.
628 mBackgroundColor = NS_ComposeColors(NS_RGB(0xFF, 0xFF, 0xFF),
629 mBackgroundColor);
631 mUseDocumentColors = !useAccessibilityTheme &&
632 nsContentUtils::GetBoolPref("browser.display.use_document_colors",
633 mUseDocumentColors);
636 void
637 nsPresContext::GetUserPreferences()
639 if (!GetPresShell()) {
640 // No presshell means nothing to do here. We'll do this when we
641 // get a presshell.
642 return;
645 mFontScaler =
646 nsContentUtils::GetIntPref("browser.display.base_font_scaler",
647 mFontScaler);
650 mAutoQualityMinFontSizePixelsPref =
651 nsContentUtils::GetIntPref("browser.display.auto_quality_min_font_size");
653 // * document colors
654 GetDocumentColorPreferences();
656 mSendAfterPaintToContent =
657 nsContentUtils::GetBoolPref("dom.send_after_paint_to_content",
658 mSendAfterPaintToContent);
660 // * link colors
661 mUnderlineLinks =
662 nsContentUtils::GetBoolPref("browser.underline_anchors", mUnderlineLinks);
664 nsAdoptingCString colorStr =
665 nsContentUtils::GetCharPref("browser.anchor_color");
667 if (!colorStr.IsEmpty()) {
668 mLinkColor = MakeColorPref(colorStr);
671 colorStr =
672 nsContentUtils::GetCharPref("browser.active_color");
674 if (!colorStr.IsEmpty()) {
675 mActiveLinkColor = MakeColorPref(colorStr);
678 colorStr = nsContentUtils::GetCharPref("browser.visited_color");
680 if (!colorStr.IsEmpty()) {
681 mVisitedLinkColor = MakeColorPref(colorStr);
684 mUseFocusColors =
685 nsContentUtils::GetBoolPref("browser.display.use_focus_colors",
686 mUseFocusColors);
688 mFocusTextColor = mDefaultColor;
689 mFocusBackgroundColor = mBackgroundColor;
691 colorStr = nsContentUtils::GetCharPref("browser.display.focus_text_color");
693 if (!colorStr.IsEmpty()) {
694 mFocusTextColor = MakeColorPref(colorStr);
697 colorStr =
698 nsContentUtils::GetCharPref("browser.display.focus_background_color");
700 if (!colorStr.IsEmpty()) {
701 mFocusBackgroundColor = MakeColorPref(colorStr);
704 mFocusRingWidth =
705 nsContentUtils::GetIntPref("browser.display.focus_ring_width",
706 mFocusRingWidth);
708 mFocusRingOnAnything =
709 nsContentUtils::GetBoolPref("browser.display.focus_ring_on_anything",
710 mFocusRingOnAnything);
712 mFocusRingStyle =
713 nsContentUtils::GetIntPref("browser.display.focus_ring_style",
714 mFocusRingStyle);
715 // * use fonts?
716 mUseDocumentFonts =
717 nsContentUtils::GetIntPref("browser.display.use_document_fonts") != 0;
719 // * replace backslashes with Yen signs? (bug 245770)
720 mEnableJapaneseTransform =
721 nsContentUtils::GetBoolPref("layout.enable_japanese_specific_transform");
723 mPrefScrollbarSide =
724 nsContentUtils::GetIntPref("layout.scrollbar.side");
726 GetFontPreferences();
728 // * image animation
729 const nsAdoptingCString& animatePref =
730 nsContentUtils::GetCharPref("image.animation_mode");
731 if (animatePref.Equals("normal"))
732 mImageAnimationModePref = imgIContainer::kNormalAnimMode;
733 else if (animatePref.Equals("none"))
734 mImageAnimationModePref = imgIContainer::kDontAnimMode;
735 else if (animatePref.Equals("once"))
736 mImageAnimationModePref = imgIContainer::kLoopOnceAnimMode;
737 else // dynamic change to invalid value should act like it does initially
738 mImageAnimationModePref = imgIContainer::kNormalAnimMode;
740 PRUint32 bidiOptions = GetBidi();
742 PRInt32 prefInt =
743 nsContentUtils::GetIntPref(IBMBIDI_TEXTDIRECTION_STR,
744 GET_BIDI_OPTION_DIRECTION(bidiOptions));
745 SET_BIDI_OPTION_DIRECTION(bidiOptions, prefInt);
746 mPrefBidiDirection = prefInt;
748 prefInt =
749 nsContentUtils::GetIntPref(IBMBIDI_TEXTTYPE_STR,
750 GET_BIDI_OPTION_TEXTTYPE(bidiOptions));
751 SET_BIDI_OPTION_TEXTTYPE(bidiOptions, prefInt);
753 prefInt =
754 nsContentUtils::GetIntPref(IBMBIDI_NUMERAL_STR,
755 GET_BIDI_OPTION_NUMERAL(bidiOptions));
756 SET_BIDI_OPTION_NUMERAL(bidiOptions, prefInt);
758 prefInt =
759 nsContentUtils::GetIntPref(IBMBIDI_SUPPORTMODE_STR,
760 GET_BIDI_OPTION_SUPPORT(bidiOptions));
761 SET_BIDI_OPTION_SUPPORT(bidiOptions, prefInt);
763 prefInt =
764 nsContentUtils::GetIntPref(IBMBIDI_CHARSET_STR,
765 GET_BIDI_OPTION_CHARACTERSET(bidiOptions));
766 SET_BIDI_OPTION_CHARACTERSET(bidiOptions, prefInt);
768 // We don't need to force reflow: either we are initializing a new
769 // prescontext or we are being called from UpdateAfterPreferencesChanged()
770 // which triggers a reflow anyway.
771 SetBidi(bidiOptions, PR_FALSE);
774 void
775 nsPresContext::PreferenceChanged(const char* aPrefName)
777 nsDependentCString prefName(aPrefName);
778 if (prefName.EqualsLiteral("layout.css.dpi") ||
779 prefName.EqualsLiteral("layout.css.devPixelsPerPx")) {
780 PRInt32 oldAppUnitsPerDevPixel = AppUnitsPerDevPixel();
781 if (mDeviceContext->CheckDPIChange() && mShell) {
782 mDeviceContext->FlushFontCache();
784 // Re-fetch the view manager's window dimensions in case there's a deferred
785 // resize which hasn't affected our mVisibleArea yet
786 nscoord oldWidthAppUnits, oldHeightAppUnits;
787 nsIViewManager* vm = mShell->GetViewManager();
788 vm->GetWindowDimensions(&oldWidthAppUnits, &oldHeightAppUnits);
789 float oldWidthDevPixels = oldWidthAppUnits/oldAppUnitsPerDevPixel;
790 float oldHeightDevPixels = oldHeightAppUnits/oldAppUnitsPerDevPixel;
792 nscoord width = NSToCoordRound(oldWidthDevPixels*AppUnitsPerDevPixel());
793 nscoord height = NSToCoordRound(oldHeightDevPixels*AppUnitsPerDevPixel());
794 vm->SetWindowDimensions(width, height);
796 MediaFeatureValuesChanged(PR_TRUE);
797 RebuildAllStyleData(NS_STYLE_HINT_REFLOW);
799 return;
801 if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("font."))) {
802 // Changes to font family preferences don't change anything in the
803 // computed style data, so the style system won't generate a reflow
804 // hint for us. We need to do that manually.
806 // FIXME We could probably also handle changes to
807 // browser.display.auto_quality_min_font_size here, but that
808 // probably also requires clearing the text run cache, so don't
809 // bother (yet, anyway).
810 mPrefChangePendingNeedsReflow = PR_TRUE;
812 if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("bidi."))) {
813 // Changes to bidi prefs need to trigger a reflow (see bug 443629)
814 mPrefChangePendingNeedsReflow = PR_TRUE;
816 // Changes to bidi.numeral also needs to empty the text run cache.
817 // This is handled in gfxTextRunWordCache.cpp.
819 if (StringBeginsWith(prefName, NS_LITERAL_CSTRING("gfx.font_rendering."))) {
820 // Changes to font_rendering prefs need to trigger a reflow
821 mPrefChangePendingNeedsReflow = PR_TRUE;
823 // we use a zero-delay timer to coalesce multiple pref updates
824 if (!mPrefChangedTimer)
826 mPrefChangedTimer = do_CreateInstance("@mozilla.org/timer;1");
827 if (!mPrefChangedTimer)
828 return;
829 mPrefChangedTimer->InitWithFuncCallback(nsPresContext::PrefChangedUpdateTimerCallback, (void*)this, 0, nsITimer::TYPE_ONE_SHOT);
833 void
834 nsPresContext::UpdateAfterPreferencesChanged()
836 mPrefChangedTimer = nsnull;
838 nsCOMPtr<nsIDocShellTreeItem> docShell(do_QueryReferent(mContainer));
839 if (docShell) {
840 PRInt32 docShellType;
841 docShell->GetItemType(&docShellType);
842 if (nsIDocShellTreeItem::typeChrome == docShellType)
843 return;
846 // Initialize our state from the user preferences
847 GetUserPreferences();
849 // update the presShell: tell it to set the preference style rules up
850 if (mShell) {
851 mShell->SetPreferenceStyleRules(PR_TRUE);
854 mDeviceContext->FlushFontCache();
856 nsChangeHint hint = nsChangeHint(0);
858 if (mPrefChangePendingNeedsReflow) {
859 NS_UpdateHint(hint, NS_STYLE_HINT_REFLOW);
862 RebuildAllStyleData(hint);
865 nsresult
866 nsPresContext::Init(nsIDeviceContext* aDeviceContext)
868 NS_ASSERTION(!mInitialized, "attempt to reinit pres context");
869 NS_ENSURE_ARG(aDeviceContext);
871 mDeviceContext = aDeviceContext;
872 NS_ADDREF(mDeviceContext);
874 if (mDeviceContext->SetPixelScale(mFullZoom))
875 mDeviceContext->FlushFontCache();
876 mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel();
878 for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i)
879 if (!mImageLoaders[i].Init())
880 return NS_ERROR_OUT_OF_MEMORY;
882 // Get the look and feel service here; default colors will be initialized
883 // from calling GetUserPreferences() when we get a presshell.
884 nsresult rv = CallGetService(kLookAndFeelCID, &mLookAndFeel);
885 if (NS_FAILED(rv)) {
886 NS_ERROR("LookAndFeel service must be implemented for this toolkit");
887 return rv;
890 mEventManager = new nsEventStateManager();
891 if (!mEventManager)
892 return NS_ERROR_OUT_OF_MEMORY;
894 NS_ADDREF(mEventManager);
896 mTransitionManager = new nsTransitionManager(this);
897 if (!mTransitionManager)
898 return NS_ERROR_OUT_OF_MEMORY;
900 if (mDocument->GetDisplayDocument()) {
901 NS_ASSERTION(mDocument->GetDisplayDocument()->GetShell() &&
902 mDocument->GetDisplayDocument()->GetShell()->GetPresContext(),
903 "Why are we being initialized?");
904 mRefreshDriver = mDocument->GetDisplayDocument()->GetShell()->
905 GetPresContext()->RefreshDriver();
906 } else {
907 nsIDocument* parent = mDocument->GetParentDocument();
908 // Unfortunately, sometimes |parent| here has no presshell because
909 // printing screws up things. Assert that in other cases it does,
910 // but whenever the shell is null just fall back on using our own
911 // refresh driver.
912 NS_ASSERTION(!parent || mDocument->IsStaticDocument() || parent->GetShell(),
913 "How did we end up with a presshell if our parent doesn't "
914 "have one?");
915 if (parent && parent->GetShell()) {
916 NS_ASSERTION(parent->GetShell()->GetPresContext(),
917 "How did we get a presshell?");
919 // We don't have our container set yet at this point
920 nsCOMPtr<nsISupports> ourContainer = mDocument->GetContainer();
922 nsCOMPtr<nsIDocShellTreeItem> ourItem = do_QueryInterface(ourContainer);
923 if (ourItem) {
924 nsCOMPtr<nsIDocShellTreeItem> parentItem;
925 ourItem->GetSameTypeParent(getter_AddRefs(parentItem));
926 if (parentItem) {
927 mRefreshDriver = parent->GetShell()->GetPresContext()->RefreshDriver();
932 if (!mRefreshDriver) {
933 mRefreshDriver = new nsRefreshDriver(this);
934 if (!mRefreshDriver)
935 return NS_ERROR_OUT_OF_MEMORY;
939 mLangService = do_GetService(NS_LANGUAGEATOMSERVICE_CONTRACTID);
941 // Register callbacks so we're notified when the preferences change
942 nsContentUtils::RegisterPrefCallback("font.",
943 nsPresContext::PrefChangedCallback,
944 this);
945 nsContentUtils::RegisterPrefCallback("browser.display.",
946 nsPresContext::PrefChangedCallback,
947 this);
948 nsContentUtils::RegisterPrefCallback("browser.underline_anchors",
949 nsPresContext::PrefChangedCallback,
950 this);
951 nsContentUtils::RegisterPrefCallback("browser.anchor_color",
952 nsPresContext::PrefChangedCallback,
953 this);
954 nsContentUtils::RegisterPrefCallback("browser.active_color",
955 nsPresContext::PrefChangedCallback,
956 this);
957 nsContentUtils::RegisterPrefCallback("browser.visited_color",
958 nsPresContext::PrefChangedCallback,
959 this);
960 nsContentUtils::RegisterPrefCallback("image.animation_mode",
961 nsPresContext::PrefChangedCallback,
962 this);
963 #ifdef IBMBIDI
964 nsContentUtils::RegisterPrefCallback("bidi.", PrefChangedCallback,
965 this);
966 #endif
967 nsContentUtils::RegisterPrefCallback("dom.send_after_paint_to_content",
968 nsPresContext::PrefChangedCallback,
969 this);
970 nsContentUtils::RegisterPrefCallback("gfx.font_rendering.", PrefChangedCallback,
971 this);
972 nsContentUtils::RegisterPrefCallback("layout.css.dpi",
973 nsPresContext::PrefChangedCallback,
974 this);
975 nsContentUtils::RegisterPrefCallback("layout.css.devPixelsPerPx",
976 nsPresContext::PrefChangedCallback,
977 this);
979 rv = mEventManager->Init();
980 NS_ENSURE_SUCCESS(rv, rv);
982 mEventManager->SetPresContext(this);
984 #ifdef DEBUG
985 mInitialized = PR_TRUE;
986 #endif
988 mBorderWidthTable[NS_STYLE_BORDER_WIDTH_THIN] = CSSPixelsToAppUnits(1);
989 mBorderWidthTable[NS_STYLE_BORDER_WIDTH_MEDIUM] = CSSPixelsToAppUnits(3);
990 mBorderWidthTable[NS_STYLE_BORDER_WIDTH_THICK] = CSSPixelsToAppUnits(5);
992 return NS_OK;
995 // Note: We don't hold a reference on the shell; it has a reference to
996 // us
997 void
998 nsPresContext::SetShell(nsIPresShell* aShell)
1000 if (mUserFontSet) {
1001 // Clear out user font set if we have one
1002 mUserFontSet->Destroy();
1003 NS_RELEASE(mUserFontSet);
1006 if (mShell) {
1007 // Remove ourselves as the charset observer from the shell's doc, because
1008 // this shell may be going away for good.
1009 nsIDocument *doc = mShell->GetDocument();
1010 if (doc) {
1011 doc->RemoveCharSetObserver(this);
1015 mShell = aShell;
1017 if (mShell) {
1018 nsIDocument *doc = mShell->GetDocument();
1019 NS_ASSERTION(doc, "expect document here");
1020 if (doc) {
1021 // Have to update PresContext's mDocument before calling any other methods.
1022 mDocument = doc;
1024 // Initialize our state from the user preferences, now that we
1025 // have a presshell, and hence a document.
1026 GetUserPreferences();
1028 if (doc) {
1029 nsIURI *docURI = doc->GetDocumentURI();
1031 if (IsDynamic() && docURI) {
1032 PRBool isChrome = PR_FALSE;
1033 PRBool isRes = PR_FALSE;
1034 docURI->SchemeIs("chrome", &isChrome);
1035 docURI->SchemeIs("resource", &isRes);
1037 if (!isChrome && !isRes)
1038 mImageAnimationMode = mImageAnimationModePref;
1039 else
1040 mImageAnimationMode = imgIContainer::kNormalAnimMode;
1043 if (mLangService) {
1044 doc->AddCharSetObserver(this);
1045 UpdateCharSet(doc->GetDocumentCharacterSet());
1048 } else {
1049 // Destroy image loaders now that the presshell is going away.
1050 // This is important since imageloaders can have pointers to frames and
1051 // we don't want those pointers to outlive the destruction of the frame
1052 // arena.
1053 for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i) {
1054 mImageLoaders[i].Enumerate(destroy_loads, nsnull);
1055 mImageLoaders[i].Clear();
1058 if (mTransitionManager) {
1059 mTransitionManager->Disconnect();
1060 mTransitionManager = nsnull;
1065 void
1066 nsPresContext::UpdateCharSet(const nsAFlatCString& aCharSet)
1068 if (mLangService) {
1069 NS_IF_RELEASE(mLanguage);
1070 mLanguage = mLangService->LookupCharSet(aCharSet.get()).get(); // addrefs
1071 // this will be a language group (or script) code rather than a true language code
1073 // bug 39570: moved from nsLanguageAtomService::LookupCharSet()
1074 #if !defined(XP_BEOS)
1075 if (mLanguage == nsGkAtoms::Unicode) {
1076 NS_RELEASE(mLanguage);
1077 NS_IF_ADDREF(mLanguage = mLangService->GetLocaleLanguage());
1079 #endif
1080 GetFontPreferences();
1082 #ifdef IBMBIDI
1083 //ahmed
1085 switch (GET_BIDI_OPTION_TEXTTYPE(GetBidi())) {
1087 case IBMBIDI_TEXTTYPE_LOGICAL:
1088 SetVisualMode(PR_FALSE);
1089 break;
1091 case IBMBIDI_TEXTTYPE_VISUAL:
1092 SetVisualMode(PR_TRUE);
1093 break;
1095 case IBMBIDI_TEXTTYPE_CHARSET:
1096 default:
1097 SetVisualMode(IsVisualCharset(aCharSet));
1099 #endif // IBMBIDI
1102 NS_IMETHODIMP
1103 nsPresContext::Observe(nsISupports* aSubject,
1104 const char* aTopic,
1105 const PRUnichar* aData)
1107 if (!nsCRT::strcmp(aTopic, "charset")) {
1108 UpdateCharSet(NS_LossyConvertUTF16toASCII(aData));
1109 mDeviceContext->FlushFontCache();
1110 RebuildAllStyleData(NS_STYLE_HINT_REFLOW);
1111 return NS_OK;
1114 NS_WARNING("unrecognized topic in nsPresContext::Observe");
1115 return NS_ERROR_FAILURE;
1118 static nsPresContext*
1119 GetParentPresContext(nsPresContext* aPresContext)
1121 nsIPresShell* shell = aPresContext->GetPresShell();
1122 if (shell) {
1123 nsIFrame* rootFrame = shell->FrameManager()->GetRootFrame();
1124 if (rootFrame) {
1125 nsIFrame* f = nsLayoutUtils::GetCrossDocParentFrame(rootFrame);
1126 if (f)
1127 return f->PresContext();
1130 return nsnull;
1133 // We may want to replace this with something faster, maybe caching the root prescontext
1134 nsRootPresContext*
1135 nsPresContext::GetRootPresContext()
1137 nsPresContext* pc = this;
1138 for (;;) {
1139 nsPresContext* parent = GetParentPresContext(pc);
1140 if (!parent)
1141 break;
1142 pc = parent;
1144 return pc->IsRoot() ? static_cast<nsRootPresContext*>(pc) : nsnull;
1147 void
1148 nsPresContext::CompatibilityModeChanged()
1150 if (!mShell)
1151 return;
1153 // enable/disable the QuirkSheet
1154 mShell->StyleSet()->
1155 EnableQuirkStyleSheet(CompatibilityMode() == eCompatibility_NavQuirks);
1158 // Helper function for setting Anim Mode on image
1159 static void SetImgAnimModeOnImgReq(imgIRequest* aImgReq, PRUint16 aMode)
1161 if (aImgReq) {
1162 nsCOMPtr<imgIContainer> imgCon;
1163 aImgReq->GetImage(getter_AddRefs(imgCon));
1164 if (imgCon) {
1165 imgCon->SetAnimationMode(aMode);
1170 // Enumeration call back for HashTable
1171 static PLDHashOperator
1172 set_animation_mode(const void * aKey, nsRefPtr<nsImageLoader>& aData, void* closure)
1174 for (nsImageLoader *loader = aData; loader;
1175 loader = loader->GetNextLoader()) {
1176 imgIRequest* imgReq = loader->GetRequest();
1177 SetImgAnimModeOnImgReq(imgReq, (PRUint16)NS_PTR_TO_INT32(closure));
1179 return PL_DHASH_NEXT;
1182 // IMPORTANT: Assumption is that all images for a Presentation
1183 // have the same Animation Mode (pavlov said this was OK)
1185 // Walks content and set the animation mode
1186 // this is a way to turn on/off image animations
1187 void nsPresContext::SetImgAnimations(nsIContent *aParent, PRUint16 aMode)
1189 nsCOMPtr<nsIImageLoadingContent> imgContent(do_QueryInterface(aParent));
1190 if (imgContent) {
1191 nsCOMPtr<imgIRequest> imgReq;
1192 imgContent->GetRequest(nsIImageLoadingContent::CURRENT_REQUEST,
1193 getter_AddRefs(imgReq));
1194 SetImgAnimModeOnImgReq(imgReq, aMode);
1197 PRUint32 count = aParent->GetChildCount();
1198 for (PRUint32 i = 0; i < count; ++i) {
1199 SetImgAnimations(aParent->GetChildAt(i), aMode);
1203 #ifdef MOZ_SMIL
1204 void
1205 nsPresContext::SetSMILAnimations(nsIDocument *aDoc, PRUint16 aNewMode,
1206 PRUint16 aOldMode)
1208 if (aDoc->HasAnimationController()) {
1209 nsSMILAnimationController* controller = aDoc->GetAnimationController();
1210 switch (aNewMode)
1212 case imgIContainer::kNormalAnimMode:
1213 case imgIContainer::kLoopOnceAnimMode:
1214 if (aOldMode == imgIContainer::kDontAnimMode)
1215 controller->Resume(nsSMILTimeContainer::PAUSE_USERPREF);
1216 break;
1218 case imgIContainer::kDontAnimMode:
1219 if (aOldMode != imgIContainer::kDontAnimMode)
1220 controller->Pause(nsSMILTimeContainer::PAUSE_USERPREF);
1221 break;
1225 #endif // MOZ_SMIL
1227 void
1228 nsPresContext::SetImageAnimationModeInternal(PRUint16 aMode)
1230 NS_ASSERTION(aMode == imgIContainer::kNormalAnimMode ||
1231 aMode == imgIContainer::kDontAnimMode ||
1232 aMode == imgIContainer::kLoopOnceAnimMode, "Wrong Animation Mode is being set!");
1234 // Image animation mode cannot be changed when rendering to a printer.
1235 if (!IsDynamic())
1236 return;
1238 // Set the mode on the image loaders.
1239 for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i)
1240 mImageLoaders[i].Enumerate(set_animation_mode, NS_INT32_TO_PTR(aMode));
1242 // Now walk the content tree and set the animation mode
1243 // on all the images.
1244 if (mShell != nsnull) {
1245 nsIDocument *doc = mShell->GetDocument();
1246 if (doc) {
1247 Element *rootElement = doc->GetRootElement();
1248 if (rootElement) {
1249 SetImgAnimations(rootElement, aMode);
1252 #ifdef MOZ_SMIL
1253 SetSMILAnimations(doc, aMode, mImageAnimationMode);
1254 #endif // MOZ_SMIL
1258 mImageAnimationMode = aMode;
1261 void
1262 nsPresContext::SetImageAnimationModeExternal(PRUint16 aMode)
1264 SetImageAnimationModeInternal(aMode);
1267 already_AddRefed<nsIFontMetrics>
1268 nsPresContext::GetMetricsFor(const nsFont& aFont, PRBool aUseUserFontSet)
1270 nsIFontMetrics* metrics = nsnull;
1271 mDeviceContext->GetMetricsFor(aFont, mLanguage,
1272 aUseUserFontSet ? GetUserFontSet() : nsnull,
1273 metrics);
1274 return metrics;
1277 const nsFont*
1278 nsPresContext::GetDefaultFont(PRUint8 aFontID) const
1280 const nsFont *font;
1281 switch (aFontID) {
1282 // Special (our default variable width font and fixed width font)
1283 case kPresContext_DefaultVariableFont_ID:
1284 font = &mDefaultVariableFont;
1285 break;
1286 case kPresContext_DefaultFixedFont_ID:
1287 font = &mDefaultFixedFont;
1288 break;
1289 // CSS
1290 case kGenericFont_serif:
1291 font = &mDefaultSerifFont;
1292 break;
1293 case kGenericFont_sans_serif:
1294 font = &mDefaultSansSerifFont;
1295 break;
1296 case kGenericFont_monospace:
1297 font = &mDefaultMonospaceFont;
1298 break;
1299 case kGenericFont_cursive:
1300 font = &mDefaultCursiveFont;
1301 break;
1302 case kGenericFont_fantasy:
1303 font = &mDefaultFantasyFont;
1304 break;
1305 default:
1306 font = nsnull;
1307 NS_ERROR("invalid arg");
1308 break;
1310 return font;
1313 void
1314 nsPresContext::SetFullZoom(float aZoom)
1316 if (!mShell || mFullZoom == aZoom) {
1317 return;
1319 // Re-fetch the view manager's window dimensions in case there's a deferred
1320 // resize which hasn't affected our mVisibleArea yet
1321 nscoord oldWidthAppUnits, oldHeightAppUnits;
1322 mShell->GetViewManager()->GetWindowDimensions(&oldWidthAppUnits, &oldHeightAppUnits);
1323 float oldWidthDevPixels = oldWidthAppUnits / float(mCurAppUnitsPerDevPixel);
1324 float oldHeightDevPixels = oldHeightAppUnits / float(mCurAppUnitsPerDevPixel);
1325 if (mDeviceContext->SetPixelScale(aZoom)) {
1326 mDeviceContext->FlushFontCache();
1329 NS_ASSERTION(!mSupressResizeReflow, "two zooms happening at the same time? impossible!");
1330 mSupressResizeReflow = PR_TRUE;
1332 mFullZoom = aZoom;
1333 mShell->GetViewManager()->
1334 SetWindowDimensions(NSToCoordRound(oldWidthDevPixels * AppUnitsPerDevPixel()),
1335 NSToCoordRound(oldHeightDevPixels * AppUnitsPerDevPixel()));
1336 if (HasCachedStyleData()) {
1337 MediaFeatureValuesChanged(PR_TRUE);
1338 RebuildAllStyleData(NS_STYLE_HINT_REFLOW);
1341 mSupressResizeReflow = PR_FALSE;
1343 mCurAppUnitsPerDevPixel = AppUnitsPerDevPixel();
1346 void
1347 nsPresContext::SetImageLoaders(nsIFrame* aTargetFrame,
1348 ImageLoadType aType,
1349 nsImageLoader* aImageLoaders)
1351 NS_ASSERTION(mShell || !aImageLoaders,
1352 "Shouldn't add new image loader after the shell is gone");
1354 nsRefPtr<nsImageLoader> oldLoaders;
1355 mImageLoaders[aType].Get(aTargetFrame, getter_AddRefs(oldLoaders));
1357 if (aImageLoaders) {
1358 mImageLoaders[aType].Put(aTargetFrame, aImageLoaders);
1359 } else if (oldLoaders) {
1360 mImageLoaders[aType].Remove(aTargetFrame);
1363 if (oldLoaders)
1364 oldLoaders->Destroy();
1367 void
1368 nsPresContext::SetupBackgroundImageLoaders(nsIFrame* aFrame,
1369 const nsStyleBackground* aStyleBackground)
1371 nsRefPtr<nsImageLoader> loaders;
1372 NS_FOR_VISIBLE_BACKGROUND_LAYERS_BACK_TO_FRONT(i, aStyleBackground) {
1373 if (aStyleBackground->mLayers[i].mImage.GetType() == eStyleImageType_Image) {
1374 PRUint32 actions = nsImageLoader::ACTION_REDRAW_ON_DECODE;
1375 imgIRequest *image = aStyleBackground->mLayers[i].mImage.GetImageData();
1376 loaders = nsImageLoader::Create(aFrame, image, actions, loaders);
1379 SetImageLoaders(aFrame, BACKGROUND_IMAGE, loaders);
1382 void
1383 nsPresContext::SetupBorderImageLoaders(nsIFrame* aFrame,
1384 const nsStyleBorder* aStyleBorder)
1386 PRUint32 actions = nsImageLoader::ACTION_REDRAW_ON_LOAD;
1387 if (aStyleBorder->ImageBorderDiffers())
1388 actions |= nsImageLoader::ACTION_REFLOW_ON_LOAD;
1389 nsRefPtr<nsImageLoader> loader =
1390 nsImageLoader::Create(aFrame, aStyleBorder->GetBorderImage(),
1391 actions, nsnull);
1392 SetImageLoaders(aFrame, BORDER_IMAGE, loader);
1395 void
1396 nsPresContext::StopImagesFor(nsIFrame* aTargetFrame)
1398 for (PRUint32 i = 0; i < IMAGE_LOAD_TYPE_COUNT; ++i)
1399 SetImageLoaders(aTargetFrame, ImageLoadType(i), nsnull);
1402 void
1403 nsPresContext::SetContainer(nsISupports* aHandler)
1405 mContainer = do_GetWeakReference(aHandler);
1406 InvalidateIsChromeCache();
1407 if (mContainer) {
1408 GetDocumentColorPreferences();
1412 already_AddRefed<nsISupports>
1413 nsPresContext::GetContainerInternal() const
1415 nsISupports *result = nsnull;
1416 if (mContainer)
1417 CallQueryReferent(mContainer.get(), &result);
1419 return result;
1422 already_AddRefed<nsISupports>
1423 nsPresContext::GetContainerExternal() const
1425 return GetContainerInternal();
1428 #ifdef IBMBIDI
1429 void
1430 nsPresContext::SetBidiEnabled() const
1432 if (mShell) {
1433 nsIDocument *doc = mShell->GetDocument();
1434 if (doc) {
1435 doc->SetBidiEnabled();
1440 nsBidiPresUtils*
1441 nsPresContext::GetBidiUtils()
1443 if (!mBidiUtils)
1444 mBidiUtils = new nsBidiPresUtils;
1446 return mBidiUtils;
1449 void
1450 nsPresContext::SetBidi(PRUint32 aSource, PRBool aForceRestyle)
1452 // Don't do all this stuff unless the options have changed.
1453 if (aSource == GetBidi()) {
1454 return;
1457 NS_ASSERTION(!(aForceRestyle && (GetBidi() == 0)),
1458 "ForceReflow on new prescontext");
1460 Document()->SetBidiOptions(aSource);
1461 if (IBMBIDI_TEXTDIRECTION_RTL == GET_BIDI_OPTION_DIRECTION(aSource)
1462 || IBMBIDI_NUMERAL_HINDI == GET_BIDI_OPTION_NUMERAL(aSource)) {
1463 SetBidiEnabled();
1465 if (IBMBIDI_TEXTTYPE_VISUAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) {
1466 SetVisualMode(PR_TRUE);
1468 else if (IBMBIDI_TEXTTYPE_LOGICAL == GET_BIDI_OPTION_TEXTTYPE(aSource)) {
1469 SetVisualMode(PR_FALSE);
1471 else {
1472 nsIDocument* doc = mShell->GetDocument();
1473 if (doc) {
1474 SetVisualMode(IsVisualCharset(doc->GetDocumentCharacterSet()));
1477 if (aForceRestyle && mShell) {
1478 // Reconstruct the root document element's frame and its children,
1479 // because we need to trigger frame reconstruction for direction change.
1480 RebuildUserFontSet();
1481 mShell->ReconstructFrames();
1485 PRUint32
1486 nsPresContext::GetBidi() const
1488 return Document()->GetBidiOptions();
1491 PRUint32
1492 nsPresContext::GetBidiMemoryUsed()
1494 if (!mBidiUtils)
1495 return 0;
1497 return mBidiUtils->EstimateMemoryUsed();
1500 #endif //IBMBIDI
1502 PRBool
1503 nsPresContext::IsTopLevelWindowInactive()
1505 nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryReferent(mContainer));
1506 if (!treeItem)
1507 return PR_FALSE;
1509 nsCOMPtr<nsIDocShellTreeItem> rootItem;
1510 treeItem->GetRootTreeItem(getter_AddRefs(rootItem));
1511 nsCOMPtr<nsPIDOMWindow> domWindow(do_GetInterface(rootItem));
1513 return domWindow && !domWindow->IsActive();
1516 nsITheme*
1517 nsPresContext::GetTheme()
1519 if (!sNoTheme && !mTheme) {
1520 mTheme = do_GetService("@mozilla.org/chrome/chrome-native-theme;1");
1521 if (!mTheme)
1522 sNoTheme = PR_TRUE;
1525 return mTheme;
1528 void
1529 nsPresContext::ThemeChanged()
1531 if (!mPendingThemeChanged) {
1532 sLookAndFeelChanged = PR_TRUE;
1533 sThemeChanged = PR_TRUE;
1535 nsCOMPtr<nsIRunnable> ev =
1536 NS_NewRunnableMethod(this, &nsPresContext::ThemeChangedInternal);
1537 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
1538 mPendingThemeChanged = PR_TRUE;
1543 void
1544 nsPresContext::ThemeChangedInternal()
1546 mPendingThemeChanged = PR_FALSE;
1548 // Tell the theme that it changed, so it can flush any handles to stale theme
1549 // data.
1550 if (mTheme && sThemeChanged) {
1551 mTheme->ThemeChanged();
1552 sThemeChanged = PR_FALSE;
1555 // Clear all cached nsILookAndFeel colors.
1556 if (mLookAndFeel && sLookAndFeelChanged) {
1557 mLookAndFeel->LookAndFeelChanged();
1558 sLookAndFeelChanged = PR_FALSE;
1561 // This will force the system metrics to be generated the next time they're used
1562 nsCSSRuleProcessor::FreeSystemMetrics();
1564 // Changes to system metrics can change media queries on them.
1565 MediaFeatureValuesChanged(PR_TRUE);
1567 // Changes in theme can change system colors (whose changes are
1568 // properly reflected in computed style data), system fonts (whose
1569 // changes are not), and -moz-appearance (whose changes likewise are
1570 // not), so we need to reflow.
1571 RebuildAllStyleData(NS_STYLE_HINT_REFLOW);
1574 void
1575 nsPresContext::SysColorChanged()
1577 if (!mPendingSysColorChanged) {
1578 sLookAndFeelChanged = PR_TRUE;
1579 nsCOMPtr<nsIRunnable> ev =
1580 NS_NewRunnableMethod(this, &nsPresContext::SysColorChangedInternal);
1581 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
1582 mPendingSysColorChanged = PR_TRUE;
1587 void
1588 nsPresContext::SysColorChangedInternal()
1590 mPendingSysColorChanged = PR_FALSE;
1592 if (mLookAndFeel && sLookAndFeelChanged) {
1593 // Don't use the cached values for the system colors
1594 mLookAndFeel->LookAndFeelChanged();
1595 sLookAndFeelChanged = PR_FALSE;
1598 // Reset default background and foreground colors for the document since
1599 // they may be using system colors
1600 GetDocumentColorPreferences();
1602 // The system color values are computed to colors in the style data,
1603 // so normal style data comparison is sufficient here.
1604 RebuildAllStyleData(nsChangeHint(0));
1607 void
1608 nsPresContext::RebuildAllStyleData(nsChangeHint aExtraHint)
1610 if (!mShell) {
1611 // We must have been torn down. Nothing to do here.
1612 return;
1615 RebuildUserFontSet();
1617 mShell->FrameConstructor()->RebuildAllStyleData(aExtraHint);
1620 void
1621 nsPresContext::PostRebuildAllStyleDataEvent(nsChangeHint aExtraHint)
1623 if (!mShell) {
1624 // We must have been torn down. Nothing to do here.
1625 return;
1627 mShell->FrameConstructor()->PostRebuildAllStyleDataEvent(aExtraHint);
1630 void
1631 nsPresContext::MediaFeatureValuesChanged(PRBool aCallerWillRebuildStyleData)
1633 mPendingMediaFeatureValuesChanged = PR_FALSE;
1634 if (mShell &&
1635 mShell->StyleSet()->MediumFeaturesChanged(this) &&
1636 !aCallerWillRebuildStyleData) {
1637 RebuildAllStyleData(nsChangeHint(0));
1641 void
1642 nsPresContext::PostMediaFeatureValuesChangedEvent()
1644 if (!mPendingMediaFeatureValuesChanged) {
1645 nsCOMPtr<nsIRunnable> ev =
1646 NS_NewRunnableMethod(this, &nsPresContext::HandleMediaFeatureValuesChangedEvent);
1647 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
1648 mPendingMediaFeatureValuesChanged = PR_TRUE;
1653 void
1654 nsPresContext::HandleMediaFeatureValuesChangedEvent()
1656 // Null-check mShell in case the shell has been destroyed (and the
1657 // event is the only thing holding the pres context alive).
1658 if (mPendingMediaFeatureValuesChanged && mShell) {
1659 MediaFeatureValuesChanged(PR_FALSE);
1663 void
1664 nsPresContext::SetPaginatedScrolling(PRBool aPaginated)
1666 if (mType == eContext_PrintPreview || mType == eContext_PageLayout)
1667 mCanPaginatedScroll = aPaginated;
1670 void
1671 nsPresContext::SetPrintSettings(nsIPrintSettings *aPrintSettings)
1673 if (mMedium == nsGkAtoms::print)
1674 mPrintSettings = aPrintSettings;
1677 PRBool
1678 nsPresContext::EnsureVisible()
1680 nsCOMPtr<nsIDocShell> docShell(do_QueryReferent(mContainer));
1681 if (docShell) {
1682 nsCOMPtr<nsIContentViewer> cv;
1683 docShell->GetContentViewer(getter_AddRefs(cv));
1684 // Make sure this is the content viewer we belong with
1685 nsCOMPtr<nsIDocumentViewer> docV(do_QueryInterface(cv));
1686 if (docV) {
1687 nsRefPtr<nsPresContext> currentPresContext;
1688 docV->GetPresContext(getter_AddRefs(currentPresContext));
1689 if (currentPresContext == this) {
1690 // OK, this is us. We want to call Show() on the content viewer.
1691 cv->Show();
1692 return PR_TRUE;
1696 return PR_FALSE;
1699 #ifdef MOZ_REFLOW_PERF
1700 void
1701 nsPresContext::CountReflows(const char * aName, nsIFrame * aFrame)
1703 if (mShell) {
1704 mShell->CountReflows(aName, aFrame);
1707 #endif
1709 PRBool
1710 nsPresContext::IsChromeSlow() const
1712 PRBool isChrome = PR_FALSE;
1713 nsCOMPtr<nsISupports> container = GetContainer();
1714 if (container) {
1715 nsresult result;
1716 nsCOMPtr<nsIDocShellTreeItem> docShell(do_QueryInterface(container, &result));
1717 if (NS_SUCCEEDED(result) && docShell) {
1718 PRInt32 docShellType;
1719 result = docShell->GetItemType(&docShellType);
1720 if (NS_SUCCEEDED(result)) {
1721 isChrome = nsIDocShellTreeItem::typeChrome == docShellType;
1725 mIsChrome = isChrome;
1726 mIsChromeIsCached = PR_TRUE;
1727 return mIsChrome;
1730 void
1731 nsPresContext::InvalidateIsChromeCacheExternal()
1733 InvalidateIsChromeCacheInternal();
1736 /* virtual */ PRBool
1737 nsPresContext::HasAuthorSpecifiedRules(nsIFrame *aFrame, PRUint32 ruleTypeMask) const
1739 return
1740 nsRuleNode::HasAuthorSpecifiedRules(aFrame->GetStyleContext(),
1741 ruleTypeMask,
1742 UseDocumentColors());
1745 static void
1746 InsertFontFaceRule(nsCSSFontFaceRule *aRule, gfxUserFontSet* aFontSet,
1747 PRUint8 aSheetType)
1749 NS_ABORT_IF_FALSE(aRule->GetType() == nsICSSRule::FONT_FACE_RULE,
1750 "InsertFontFaceRule passed a non-fontface CSS rule");
1752 // aRule->List();
1754 nsAutoString fontfamily;
1755 nsCSSValue val;
1757 PRUint32 unit;
1758 PRUint32 weight = NS_STYLE_FONT_WEIGHT_NORMAL;
1759 PRUint32 stretch = NS_STYLE_FONT_STRETCH_NORMAL;
1760 PRUint32 italicStyle = FONT_STYLE_NORMAL;
1761 nsString featureSettings, languageOverride;
1763 // set up family name
1764 aRule->GetDesc(eCSSFontDesc_Family, val);
1765 unit = val.GetUnit();
1766 if (unit == eCSSUnit_String) {
1767 val.GetStringValue(fontfamily);
1768 } else {
1769 NS_ASSERTION(unit == eCSSUnit_Null,
1770 "@font-face family name has unexpected unit");
1771 // If there is no family name, this rule cannot contribute a
1772 // usable font, so there is no point in processing it further.
1773 return;
1776 // set up weight
1777 aRule->GetDesc(eCSSFontDesc_Weight, val);
1778 unit = val.GetUnit();
1779 if (unit == eCSSUnit_Integer || unit == eCSSUnit_Enumerated) {
1780 weight = val.GetIntValue();
1781 } else if (unit == eCSSUnit_Normal) {
1782 weight = NS_STYLE_FONT_WEIGHT_NORMAL;
1783 } else {
1784 NS_ASSERTION(unit == eCSSUnit_Null,
1785 "@font-face weight has unexpected unit");
1788 // set up stretch
1789 aRule->GetDesc(eCSSFontDesc_Stretch, val);
1790 unit = val.GetUnit();
1791 if (unit == eCSSUnit_Enumerated) {
1792 stretch = val.GetIntValue();
1793 } else if (unit == eCSSUnit_Normal) {
1794 stretch = NS_STYLE_FONT_STRETCH_NORMAL;
1795 } else {
1796 NS_ASSERTION(unit == eCSSUnit_Null,
1797 "@font-face stretch has unexpected unit");
1800 // set up font style
1801 aRule->GetDesc(eCSSFontDesc_Style, val);
1802 unit = val.GetUnit();
1803 if (unit == eCSSUnit_Enumerated) {
1804 italicStyle = val.GetIntValue();
1805 } else if (unit == eCSSUnit_Normal) {
1806 italicStyle = FONT_STYLE_NORMAL;
1807 } else {
1808 NS_ASSERTION(unit == eCSSUnit_Null,
1809 "@font-face style has unexpected unit");
1812 // set up font features
1813 aRule->GetDesc(eCSSFontDesc_FontFeatureSettings, val);
1814 unit = val.GetUnit();
1815 if (unit == eCSSUnit_Normal) {
1816 // empty feature string
1817 } else if (unit == eCSSUnit_String) {
1818 val.GetStringValue(featureSettings);
1819 } else {
1820 NS_ASSERTION(unit == eCSSUnit_Null,
1821 "@font-face font-feature-settings has unexpected unit");
1824 // set up font language override
1825 aRule->GetDesc(eCSSFontDesc_FontLanguageOverride, val);
1826 unit = val.GetUnit();
1827 if (unit == eCSSUnit_Normal) {
1828 // empty feature string
1829 } else if (unit == eCSSUnit_String) {
1830 val.GetStringValue(languageOverride);
1831 } else {
1832 NS_ASSERTION(unit == eCSSUnit_Null,
1833 "@font-face font-language-override has unexpected unit");
1836 // set up src array
1837 nsTArray<gfxFontFaceSrc> srcArray;
1839 aRule->GetDesc(eCSSFontDesc_Src, val);
1840 unit = val.GetUnit();
1841 if (unit == eCSSUnit_Array) {
1842 nsCSSValue::Array *srcArr = val.GetArrayValue();
1843 size_t numSrc = srcArr->Count();
1845 for (size_t i = 0; i < numSrc; i++) {
1846 val = srcArr->Item(i);
1847 unit = val.GetUnit();
1848 gfxFontFaceSrc *face = srcArray.AppendElements(1);
1849 if (!face)
1850 return;
1852 switch (unit) {
1854 case eCSSUnit_Local_Font:
1855 val.GetStringValue(face->mLocalName);
1856 face->mIsLocal = PR_TRUE;
1857 face->mURI = nsnull;
1858 face->mFormatFlags = 0;
1859 break;
1860 case eCSSUnit_URL:
1861 face->mIsLocal = PR_FALSE;
1862 face->mURI = val.GetURLValue();
1863 NS_ASSERTION(face->mURI, "null url in @font-face rule");
1864 face->mReferrer = val.GetURLStructValue()->mReferrer;
1865 face->mOriginPrincipal = val.GetURLStructValue()->mOriginPrincipal;
1866 NS_ASSERTION(face->mOriginPrincipal, "null origin principal in @font-face rule");
1868 // agent and user stylesheets are treated slightly differently,
1869 // the same-site origin check and access control headers are
1870 // enforced against the sheet principal rather than the document
1871 // principal to allow user stylesheets to include @font-face rules
1872 face->mUseOriginPrincipal = (aSheetType == nsStyleSet::eUserSheet ||
1873 aSheetType == nsStyleSet::eAgentSheet);
1875 face->mLocalName.Truncate();
1876 face->mFormatFlags = 0;
1877 while (i + 1 < numSrc && (val = srcArr->Item(i+1),
1878 val.GetUnit() == eCSSUnit_Font_Format)) {
1879 nsDependentString valueString(val.GetStringBufferValue());
1880 if (valueString.LowerCaseEqualsASCII("woff")) {
1881 face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_WOFF;
1882 } else if (valueString.LowerCaseEqualsASCII("opentype")) {
1883 face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_OPENTYPE;
1884 } else if (valueString.LowerCaseEqualsASCII("truetype")) {
1885 face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE;
1886 } else if (valueString.LowerCaseEqualsASCII("truetype-aat")) {
1887 face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_TRUETYPE_AAT;
1888 } else if (valueString.LowerCaseEqualsASCII("embedded-opentype")) {
1889 face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_EOT;
1890 } else if (valueString.LowerCaseEqualsASCII("svg")) {
1891 face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_SVG;
1892 } else {
1893 // unknown format specified, mark to distinguish from the
1894 // case where no format hints are specified
1895 face->mFormatFlags |= gfxUserFontSet::FLAG_FORMAT_UNKNOWN;
1897 i++;
1899 break;
1900 default:
1901 NS_ASSERTION(unit == eCSSUnit_Local_Font || unit == eCSSUnit_URL,
1902 "strange unit type in font-face src array");
1903 break;
1906 } else {
1907 NS_ASSERTION(unit == eCSSUnit_Null, "@font-face src has unexpected unit");
1910 if (!fontfamily.IsEmpty() && srcArray.Length() > 0) {
1911 aFontSet->AddFontFace(fontfamily, srcArray, weight, stretch, italicStyle,
1912 featureSettings, languageOverride);
1916 gfxUserFontSet*
1917 nsPresContext::GetUserFontSetInternal()
1919 // We want to initialize the user font set lazily the first time the
1920 // user asks for it, rather than building it too early and forcing
1921 // rule cascade creation. Thus we try to enforce the invariant that
1922 // we *never* build the user font set until the first call to
1923 // GetUserFontSet. However, once it's been requested, we can't wait
1924 // for somebody to call GetUserFontSet in order to rebuild it (see
1925 // comments below in RebuildUserFontSet for why).
1926 #ifdef DEBUG
1927 PRBool userFontSetGottenBefore = mGetUserFontSetCalled;
1928 #endif
1929 // Set mGetUserFontSetCalled up front, so that FlushUserFontSet will actually
1930 // flush.
1931 mGetUserFontSetCalled = PR_TRUE;
1932 if (mUserFontSetDirty) {
1933 // If this assertion fails, and there have actually been changes to
1934 // @font-face rules, then we will call StyleChangeReflow in
1935 // FlushUserFontSet. If we're in the middle of reflow,
1936 // that's a bad thing to do, and the caller was responsible for
1937 // flushing first. If we're not (e.g., in frame construction), it's
1938 // ok.
1939 NS_ASSERTION(!userFontSetGottenBefore || !mShell->IsReflowLocked(),
1940 "FlushUserFontSet should have been called first");
1941 FlushUserFontSet();
1944 return mUserFontSet;
1947 gfxUserFontSet*
1948 nsPresContext::GetUserFontSetExternal()
1950 return GetUserFontSetInternal();
1953 void
1954 nsPresContext::FlushUserFontSet()
1956 if (!mShell)
1957 return; // we've been torn down
1959 if (!mGetUserFontSetCalled) {
1960 return; // No one cares about this font set yet, but we want to be careful
1961 // to not unset our mUserFontSetDirty bit, so when someone really
1962 // does we'll create it.
1965 if (mUserFontSetDirty) {
1966 if (gfxPlatform::GetPlatform()->DownloadableFontsEnabled()) {
1967 nsRefPtr<gfxUserFontSet> oldUserFontSet = mUserFontSet;
1969 nsTArray<nsFontFaceRuleContainer> rules;
1970 if (!mShell->StyleSet()->AppendFontFaceRules(this, rules))
1971 return;
1973 PRBool differ;
1974 if (rules.Length() == mFontFaceRules.Length()) {
1975 differ = PR_FALSE;
1976 for (PRUint32 i = 0, i_end = rules.Length(); i < i_end; ++i) {
1977 if (rules[i].mRule != mFontFaceRules[i].mRule ||
1978 rules[i].mSheetType != mFontFaceRules[i].mSheetType) {
1979 differ = PR_TRUE;
1980 break;
1983 } else {
1984 differ = PR_TRUE;
1987 // Only rebuild things if the set of @font-face rules is different.
1988 if (differ) {
1989 if (mUserFontSet) {
1990 mUserFontSet->Destroy();
1991 NS_RELEASE(mUserFontSet);
1994 if (rules.Length() > 0) {
1995 nsUserFontSet *fs = new nsUserFontSet(this);
1996 if (!fs)
1997 return;
1998 mUserFontSet = fs;
1999 NS_ADDREF(mUserFontSet);
2001 for (PRUint32 i = 0, i_end = rules.Length(); i < i_end; ++i) {
2002 InsertFontFaceRule(rules[i].mRule, fs, rules[i].mSheetType);
2007 #ifdef DEBUG
2008 PRBool success =
2009 #endif
2010 rules.SwapElements(mFontFaceRules);
2011 NS_ASSERTION(success, "should never fail given both are heap arrays");
2013 if (mGetUserFontSetCalled && oldUserFontSet != mUserFontSet) {
2014 // If we've changed, created, or destroyed a user font set, we
2015 // need to trigger a style change reflow.
2016 // We need to enqueue a style change reflow (for later) to
2017 // reflect that we're dropping @font-face rules. (However,
2018 // without a reflow, nothing will happen to start any downloads
2019 // that are needed.)
2020 UserFontSetUpdated();
2024 mUserFontSetDirty = PR_FALSE;
2028 void
2029 nsPresContext::RebuildUserFontSet()
2031 if (!mGetUserFontSetCalled) {
2032 // We want to lazily build the user font set the first time it's
2033 // requested (so we don't force creation of rule cascades too
2034 // early), so don't do anything now.
2035 return;
2038 mUserFontSetDirty = PR_TRUE;
2040 // Somebody has already asked for the user font set, so we need to
2041 // post an event to rebuild it. Setting the user font set to be dirty
2042 // and lazily rebuilding it isn't sufficient, since it is only the act
2043 // of rebuilding it that will trigger the style change reflow that
2044 // calls GetUserFontSet. (This reflow causes rebuilding of text runs,
2045 // which starts font loads, whose completion causes another style
2046 // change reflow).
2047 if (!mPostedFlushUserFontSet) {
2048 nsCOMPtr<nsIRunnable> ev =
2049 NS_NewRunnableMethod(this, &nsPresContext::HandleRebuildUserFontSet);
2050 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(ev))) {
2051 mPostedFlushUserFontSet = PR_TRUE;
2056 void
2057 nsPresContext::UserFontSetUpdated()
2059 if (!mShell)
2060 return;
2062 // Changes to the set of available fonts can cause updates to layout by:
2064 // 1. Changing the font used for text, which changes anything that
2065 // depends on text measurement, including line breaking and
2066 // intrinsic widths, and any other parts of layout that depend on
2067 // font metrics. This requires a style change reflow to update.
2069 // 2. Changing the value of the 'ex' and 'ch' units in style data,
2070 // which also depend on font metrics. Updating this information
2071 // requires rebuilding the rule tree from the top, avoiding the
2072 // reuse of cached data even when no style rules have changed.
2074 PostRebuildAllStyleDataEvent(NS_STYLE_HINT_REFLOW);
2077 PRBool
2078 nsPresContext::EnsureSafeToHandOutCSSRules()
2080 nsCSSStyleSheet::EnsureUniqueInnerResult res =
2081 mShell->StyleSet()->EnsureUniqueInnerOnCSSSheets();
2082 if (res == nsCSSStyleSheet::eUniqueInner_AlreadyUnique) {
2083 // Nothing to do.
2084 return PR_TRUE;
2086 if (res == nsCSSStyleSheet::eUniqueInner_CloneFailed) {
2087 return PR_FALSE;
2090 NS_ABORT_IF_FALSE(res == nsCSSStyleSheet::eUniqueInner_ClonedInner,
2091 "unexpected result");
2092 RebuildAllStyleData(nsChangeHint(0));
2093 return PR_TRUE;
2096 void
2097 nsPresContext::FireDOMPaintEvent()
2099 nsPIDOMWindow* ourWindow = mDocument->GetWindow();
2100 if (!ourWindow)
2101 return;
2103 nsCOMPtr<nsIDOMEventTarget> dispatchTarget = do_QueryInterface(ourWindow);
2104 nsCOMPtr<nsIDOMEventTarget> eventTarget = dispatchTarget;
2105 if (!IsChrome()) {
2106 PRBool notifyContent = mSendAfterPaintToContent;
2108 if (notifyContent) {
2109 // If the pref is set, we still don't post events when they're
2110 // entirely cross-doc.
2111 notifyContent = PR_FALSE;
2112 for (PRUint32 i = 0; i < mInvalidateRequests.mRequests.Length(); ++i) {
2113 if (!(mInvalidateRequests.mRequests[i].mFlags &
2114 nsIFrame::INVALIDATE_CROSS_DOC)) {
2115 notifyContent = PR_TRUE;
2119 if (!notifyContent) {
2120 // Don't tell the window about this event, it should not know that
2121 // something happened in a subdocument. Tell only the chrome event handler.
2122 // (Events sent to the window get propagated to the chrome event handler
2123 // automatically.)
2124 dispatchTarget = do_QueryInterface(ourWindow->GetParentTarget());
2125 if (!dispatchTarget) {
2126 return;
2130 // Events sent to the window get propagated to the chrome event handler
2131 // automatically.
2132 nsCOMPtr<nsIDOMEvent> event;
2133 // This will empty our list in case dispatching the event causes more damage
2134 // (hopefully it won't, or we're likely to get an infinite loop! At least
2135 // it won't be blocking app execution though).
2136 NS_NewDOMNotifyPaintEvent(getter_AddRefs(event), this, nsnull,
2137 NS_AFTERPAINT,
2138 &mInvalidateRequests);
2139 nsCOMPtr<nsIPrivateDOMEvent> pEvent = do_QueryInterface(event);
2140 if (!pEvent) return;
2142 // Even if we're not telling the window about the event (so eventTarget is
2143 // the chrome event handler, not the window), the window is still
2144 // logically the event target.
2145 pEvent->SetTarget(eventTarget);
2146 pEvent->SetTrusted(PR_TRUE);
2147 nsEventDispatcher::DispatchDOMEvent(dispatchTarget, nsnull, event, this, nsnull);
2150 static PRBool
2151 MayHavePaintEventListener(nsPIDOMWindow* aInnerWindow)
2153 if (!aInnerWindow)
2154 return PR_FALSE;
2155 if (aInnerWindow->HasPaintEventListeners())
2156 return PR_TRUE;
2158 nsPIDOMEventTarget* parentTarget = aInnerWindow->GetParentTarget();
2159 if (!parentTarget)
2160 return PR_FALSE;
2162 nsIEventListenerManager* manager = nsnull;
2163 if ((manager = parentTarget->GetListenerManager(PR_FALSE)) &&
2164 manager->MayHavePaintEventListener()) {
2165 return PR_TRUE;
2168 nsCOMPtr<nsINode> node;
2169 if (parentTarget != aInnerWindow->GetChromeEventHandler()) {
2170 nsCOMPtr<nsIInProcessContentFrameMessageManager> mm =
2171 do_QueryInterface(parentTarget);
2172 if (mm) {
2173 node = mm->GetOwnerContent();
2177 if (!node) {
2178 node = do_QueryInterface(parentTarget);
2180 if (node)
2181 return MayHavePaintEventListener(node->GetOwnerDoc()->GetInnerWindow());
2183 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(parentTarget);
2184 if (window)
2185 return MayHavePaintEventListener(window);
2187 nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(parentTarget);
2188 nsPIDOMEventTarget* tabChildGlobal;
2189 return root &&
2190 (tabChildGlobal = root->GetParentTarget()) &&
2191 (manager = tabChildGlobal->GetListenerManager(PR_FALSE)) &&
2192 manager->MayHavePaintEventListener();
2195 PRBool
2196 nsPresContext::MayHavePaintEventListener()
2198 return ::MayHavePaintEventListener(mDocument->GetInnerWindow());
2201 void
2202 nsPresContext::NotifyInvalidation(const nsRect& aRect, PRUint32 aFlags)
2204 // If there is no paint event listener, then we don't need to fire
2205 // the asynchronous event. We don't even need to record invalidation.
2206 // MayHavePaintEventListener is pretty cheap and we could make it
2207 // even cheaper by providing a more efficient
2208 // nsPIDOMWindow::GetListenerManager.
2209 if (aRect.IsEmpty() || !MayHavePaintEventListener())
2210 return;
2212 nsPresContext* pc;
2213 for (pc = this; pc; pc = GetParentPresContext(pc)) {
2214 if (pc->mFireAfterPaintEvents)
2215 break;
2216 pc->mFireAfterPaintEvents = PR_TRUE;
2218 if (!pc) {
2219 nsRootPresContext* rpc = GetRootPresContext();
2220 if (rpc) {
2221 rpc->EnsureEventualDidPaintEvent();
2225 nsInvalidateRequestList::Request* request =
2226 mInvalidateRequests.mRequests.AppendElement();
2227 if (!request)
2228 return;
2230 request->mRect = aRect;
2231 request->mFlags = aFlags;
2234 static PRBool
2235 NotifyDidPaintSubdocumentCallback(nsIDocument* aDocument, void* aData)
2237 nsIPresShell* shell = aDocument->GetShell();
2238 if (shell) {
2239 nsPresContext* pc = shell->GetPresContext();
2240 if (pc) {
2241 pc->NotifyDidPaintForSubtree();
2244 return PR_TRUE;
2247 void
2248 nsPresContext::NotifyDidPaintForSubtree()
2250 if (!mFireAfterPaintEvents)
2251 return;
2252 mFireAfterPaintEvents = PR_FALSE;
2254 if (IsRoot()) {
2255 static_cast<nsRootPresContext*>(this)->CancelDidPaintTimer();
2258 if (!mInvalidateRequests.mRequests.IsEmpty()) {
2259 nsCOMPtr<nsIRunnable> ev =
2260 NS_NewRunnableMethod(this, &nsPresContext::FireDOMPaintEvent);
2261 nsContentUtils::AddScriptRunner(ev);
2264 mDocument->EnumerateSubDocuments(NotifyDidPaintSubdocumentCallback, nsnull);
2267 PRBool
2268 nsPresContext::HasCachedStyleData()
2270 return mShell && mShell->StyleSet()->HasCachedStyleData();
2273 static PRBool sGotInterruptEnv = PR_FALSE;
2274 enum InterruptMode {
2275 ModeRandom,
2276 ModeCounter,
2277 ModeEvent
2279 // Controlled by the GECKO_REFLOW_INTERRUPT_MODE env var; allowed values are
2280 // "random" (except on Windows) or "counter". If neither is used, the mode is
2281 // ModeEvent.
2282 static InterruptMode sInterruptMode = ModeEvent;
2283 // Used for the "random" mode. Controlled by the GECKO_REFLOW_INTERRUPT_SEED
2284 // env var.
2285 static PRUint32 sInterruptSeed = 1;
2286 // Used for the "counter" mode. This is the number of unskipped interrupt
2287 // checks that have to happen before we interrupt. Controlled by the
2288 // GECKO_REFLOW_INTERRUPT_FREQUENCY env var.
2289 static PRUint32 sInterruptMaxCounter = 10;
2290 // Used for the "counter" mode. This counts up to sInterruptMaxCounter and is
2291 // then reset to 0.
2292 static PRUint32 sInterruptCounter;
2293 // Number of interrupt checks to skip before really trying to interrupt.
2294 // Controlled by the GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP env var.
2295 static PRUint32 sInterruptChecksToSkip = 200;
2296 // Number of milliseconds that a reflow should be allowed to run for before we
2297 // actually allow interruption. Controlled by the
2298 // GECKO_REFLOW_MIN_NOINTERRUPT_DURATION env var.
2299 static TimeDuration sInterruptTimeout = TimeDuration::FromMilliseconds(100);
2301 static void GetInterruptEnv()
2303 char *ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_MODE");
2304 if (ev) {
2305 #ifndef XP_WIN
2306 if (PL_strcasecmp(ev, "random") == 0) {
2307 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_SEED");
2308 if (ev) {
2309 sInterruptSeed = atoi(ev);
2311 srandom(sInterruptSeed);
2312 sInterruptMode = ModeRandom;
2313 } else
2314 #endif
2315 if (PL_strcasecmp(ev, "counter") == 0) {
2316 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_FREQUENCY");
2317 if (ev) {
2318 sInterruptMaxCounter = atoi(ev);
2320 sInterruptCounter = 0;
2321 sInterruptMode = ModeCounter;
2324 ev = PR_GetEnv("GECKO_REFLOW_INTERRUPT_CHECKS_TO_SKIP");
2325 if (ev) {
2326 sInterruptChecksToSkip = atoi(ev);
2329 ev = PR_GetEnv("GECKO_REFLOW_MIN_NOINTERRUPT_DURATION");
2330 if (ev) {
2331 sInterruptTimeout = TimeDuration::FromMilliseconds(atoi(ev));
2335 PRBool
2336 nsPresContext::HavePendingInputEvent()
2338 switch (sInterruptMode) {
2339 #ifndef XP_WIN
2340 case ModeRandom:
2341 return (random() & 1);
2342 #endif
2343 case ModeCounter:
2344 if (sInterruptCounter < sInterruptMaxCounter) {
2345 ++sInterruptCounter;
2346 return PR_FALSE;
2348 sInterruptCounter = 0;
2349 return PR_TRUE;
2350 default:
2351 case ModeEvent: {
2352 nsIFrame* f = PresShell()->GetRootFrame();
2353 if (f) {
2354 nsIWidget* w = f->GetNearestWidget();
2355 if (w) {
2356 return w->HasPendingInputEvent();
2359 return PR_FALSE;
2364 void
2365 nsPresContext::ReflowStarted(PRBool aInterruptible)
2367 #ifdef NOISY_INTERRUPTIBLE_REFLOW
2368 if (!aInterruptible) {
2369 printf("STARTING NONINTERRUPTIBLE REFLOW\n");
2371 #endif
2372 // We don't support interrupting in paginated contexts, since page
2373 // sequences only handle initial reflow
2374 mInterruptsEnabled = aInterruptible && !IsPaginated();
2376 // Don't set mHasPendingInterrupt based on HavePendingInputEvent() here. If
2377 // we ever change that, then we need to update the code in
2378 // PresShell::DoReflow to only add the just-reflown root to dirty roots if
2379 // it's actually dirty. Otherwise we can end up adding a root that has no
2380 // interruptible descendants, just because we detected an interrupt at reflow
2381 // start.
2382 mHasPendingInterrupt = PR_FALSE;
2384 mInterruptChecksToSkip = sInterruptChecksToSkip;
2386 if (mInterruptsEnabled) {
2387 mReflowStartTime = TimeStamp::Now();
2391 PRBool
2392 nsPresContext::CheckForInterrupt(nsIFrame* aFrame)
2394 if (mHasPendingInterrupt) {
2395 mShell->FrameNeedsToContinueReflow(aFrame);
2396 return PR_TRUE;
2399 if (!sGotInterruptEnv) {
2400 sGotInterruptEnv = PR_TRUE;
2401 GetInterruptEnv();
2404 if (!mInterruptsEnabled) {
2405 return PR_FALSE;
2408 if (mInterruptChecksToSkip > 0) {
2409 --mInterruptChecksToSkip;
2410 return PR_FALSE;
2412 mInterruptChecksToSkip = sInterruptChecksToSkip;
2414 // Don't interrupt if it's been less than sInterruptTimeout since we started
2415 // the reflow.
2416 mHasPendingInterrupt =
2417 TimeStamp::Now() - mReflowStartTime > sInterruptTimeout &&
2418 HavePendingInputEvent() &&
2419 !IsChrome();
2420 if (mHasPendingInterrupt) {
2421 #ifdef NOISY_INTERRUPTIBLE_REFLOW
2422 printf("*** DETECTED pending interrupt (time=%lld)\n", PR_Now());
2423 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
2424 mShell->FrameNeedsToContinueReflow(aFrame);
2426 return mHasPendingInterrupt;
2429 PRBool
2430 nsPresContext::IsRootContentDocument()
2432 // We are a root content document if: we are not a resource doc, we are
2433 // not chrome, and we either have no parent or our parent is chrome.
2434 if (mDocument->IsResourceDoc()) {
2435 return PR_FALSE;
2437 if (IsChrome()) {
2438 return PR_FALSE;
2440 // We may not have a root frame, so use views.
2441 nsIViewManager* vm = PresShell()->GetViewManager();
2442 nsIView* view = nsnull;
2443 if (NS_FAILED(vm->GetRootView(view)) || !view) {
2444 return PR_FALSE;
2446 view = view->GetParent(); // anonymous inner view
2447 if (!view) {
2448 return PR_TRUE;
2450 view = view->GetParent(); // subdocumentframe's view
2451 if (!view) {
2452 return PR_TRUE;
2455 nsIFrame* f = static_cast<nsIFrame*>(view->GetClientData());
2456 return (f && f->PresContext()->IsChrome());
2459 nsRootPresContext::nsRootPresContext(nsIDocument* aDocument,
2460 nsPresContextType aType)
2461 : nsPresContext(aDocument, aType),
2462 mUpdatePluginGeometryForFrame(nsnull),
2463 mDOMGeneration(0),
2464 mNeedsToUpdatePluginGeometry(PR_FALSE)
2466 mRegisteredPlugins.Init();
2469 nsRootPresContext::~nsRootPresContext()
2471 NS_ASSERTION(mRegisteredPlugins.Count() == 0,
2472 "All plugins should have been unregistered");
2473 CancelDidPaintTimer();
2476 void
2477 nsRootPresContext::RegisterPluginForGeometryUpdates(nsObjectFrame* aPlugin)
2479 mRegisteredPlugins.PutEntry(aPlugin);
2482 void
2483 nsRootPresContext::UnregisterPluginForGeometryUpdates(nsObjectFrame* aPlugin)
2485 mRegisteredPlugins.RemoveEntry(aPlugin);
2488 struct PluginGeometryClosure {
2489 nsIFrame* mRootFrame;
2490 PRInt32 mRootAPD;
2491 nsIFrame* mChangedSubtree;
2492 nsRect mChangedRect;
2493 nsTHashtable<nsPtrHashKey<nsObjectFrame> > mAffectedPlugins;
2494 nsRect mAffectedPluginBounds;
2495 nsTArray<nsIWidget::Configuration>* mOutputConfigurations;
2497 static PLDHashOperator
2498 PluginBoundsEnumerator(nsPtrHashKey<nsObjectFrame>* aEntry, void* userArg)
2500 PluginGeometryClosure* closure = static_cast<PluginGeometryClosure*>(userArg);
2501 nsObjectFrame* f = aEntry->GetKey();
2502 nsRect fBounds = f->GetContentRect() +
2503 f->GetParent()->GetOffsetToCrossDoc(closure->mRootFrame);
2504 PRInt32 APD = f->PresContext()->AppUnitsPerDevPixel();
2505 fBounds = fBounds.ConvertAppUnitsRoundOut(APD, closure->mRootAPD);
2506 // We're identifying the plugins that may have been affected by changes
2507 // to the frame subtree rooted at aChangedRoot. Any plugin that overlaps
2508 // the overflow area of aChangedRoot could have its clip region affected
2509 // because it might be covered (or uncovered) by changes to the subtree.
2510 // Plugins in the subtree might have changed position and/or size, and
2511 // they might not be in aChangedRoot's overflow area (because they're
2512 // being clipped by an ancestor in the subtree).
2513 if (fBounds.Intersects(closure->mChangedRect) ||
2514 nsLayoutUtils::IsAncestorFrameCrossDoc(closure->mChangedSubtree, f)) {
2515 closure->mAffectedPluginBounds.UnionRect(
2516 closure->mAffectedPluginBounds, fBounds);
2517 closure->mAffectedPlugins.PutEntry(f);
2519 return PL_DHASH_NEXT;
2522 static PLDHashOperator
2523 PluginHideEnumerator(nsPtrHashKey<nsObjectFrame>* aEntry, void* userArg)
2525 PluginGeometryClosure* closure = static_cast<PluginGeometryClosure*>(userArg);
2526 nsObjectFrame* f = aEntry->GetKey();
2527 f->GetEmptyClipConfiguration(closure->mOutputConfigurations);
2528 return PL_DHASH_NEXT;
2531 static void
2532 RecoverPluginGeometry(nsDisplayListBuilder* aBuilder,
2533 nsDisplayList* aList, PluginGeometryClosure* aClosure)
2535 for (nsDisplayItem* i = aList->GetBottom(); i; i = i->GetAbove()) {
2536 switch (i->GetType()) {
2537 case nsDisplayItem::TYPE_PLUGIN: {
2538 nsDisplayPlugin* displayPlugin = static_cast<nsDisplayPlugin*>(i);
2539 nsObjectFrame* f = static_cast<nsObjectFrame*>(
2540 displayPlugin->GetUnderlyingFrame());
2541 // Ignore plugins which aren't supposed to be affected by this
2542 // operation --- their bounds will not have been included in the
2543 // display list computations so the visible region computed for them
2544 // would be incorrect
2545 nsPtrHashKey<nsObjectFrame>* entry =
2546 aClosure->mAffectedPlugins.GetEntry(f);
2547 if (entry) {
2548 displayPlugin->GetWidgetConfiguration(aBuilder,
2549 aClosure->mOutputConfigurations);
2550 // we've dealt with this plugin now
2551 aClosure->mAffectedPlugins.RawRemoveEntry(entry);
2553 break;
2555 default: {
2556 nsDisplayList* sublist = i->GetList();
2557 if (sublist) {
2558 RecoverPluginGeometry(aBuilder, sublist, aClosure);
2560 break;
2566 #ifdef DEBUG
2567 #include <stdio.h>
2569 static PRBool gDumpPluginList = PR_FALSE;
2570 #endif
2572 void
2573 nsRootPresContext::GetPluginGeometryUpdates(nsIFrame* aChangedSubtree,
2574 nsTArray<nsIWidget::Configuration>* aConfigurations)
2576 if (mRegisteredPlugins.Count() == 0)
2577 return;
2579 PluginGeometryClosure closure;
2580 closure.mRootFrame = mShell->FrameManager()->GetRootFrame();
2581 closure.mRootAPD = closure.mRootFrame->PresContext()->AppUnitsPerDevPixel();
2582 closure.mChangedSubtree = aChangedSubtree;
2583 closure.mChangedRect = aChangedSubtree->GetVisualOverflowRect() +
2584 aChangedSubtree->GetOffsetToCrossDoc(closure.mRootFrame);
2585 PRInt32 subtreeAPD = aChangedSubtree->PresContext()->AppUnitsPerDevPixel();
2586 closure.mChangedRect =
2587 closure.mChangedRect.ConvertAppUnitsRoundOut(subtreeAPD, closure.mRootAPD);
2588 closure.mAffectedPlugins.Init();
2589 closure.mOutputConfigurations = aConfigurations;
2590 // Fill in closure.mAffectedPlugins and closure.mAffectedPluginBounds
2591 mRegisteredPlugins.EnumerateEntries(PluginBoundsEnumerator, &closure);
2593 nsRect bounds;
2594 if (bounds.IntersectRect(closure.mAffectedPluginBounds,
2595 closure.mRootFrame->GetRect())) {
2596 nsDisplayListBuilder builder(closure.mRootFrame,
2597 nsDisplayListBuilder::PLUGIN_GEOMETRY, PR_FALSE);
2598 builder.SetAccurateVisibleRegions();
2599 nsDisplayList list;
2601 builder.EnterPresShell(closure.mRootFrame, bounds);
2602 closure.mRootFrame->BuildDisplayListForStackingContext(
2603 &builder, bounds, &list);
2604 builder.LeavePresShell(closure.mRootFrame, bounds);
2606 #ifdef DEBUG
2607 if (gDumpPluginList) {
2608 fprintf(stderr, "Plugins --- before optimization (bounds %d,%d,%d,%d):\n",
2609 bounds.x, bounds.y, bounds.width, bounds.height);
2610 nsFrame::PrintDisplayList(&builder, list);
2612 #endif
2614 nsRegion visibleRegion(bounds);
2615 list.ComputeVisibilityForRoot(&builder, &visibleRegion);
2617 #ifdef DEBUG
2618 if (gDumpPluginList) {
2619 fprintf(stderr, "Plugins --- after optimization:\n");
2620 nsFrame::PrintDisplayList(&builder, list);
2622 #endif
2624 RecoverPluginGeometry(&builder, &list, &closure);
2625 list.DeleteAll();
2628 // Plugins that we didn't find in the display list are not visible
2629 closure.mAffectedPlugins.EnumerateEntries(PluginHideEnumerator, &closure);
2632 static PRBool
2633 HasOverlap(const nsIntPoint& aOffset1, const nsTArray<nsIntRect>& aClipRects1,
2634 const nsIntPoint& aOffset2, const nsTArray<nsIntRect>& aClipRects2)
2636 nsIntPoint offsetDelta = aOffset1 - aOffset2;
2637 for (PRUint32 i = 0; i < aClipRects1.Length(); ++i) {
2638 for (PRUint32 j = 0; j < aClipRects2.Length(); ++j) {
2639 if ((aClipRects1[i] + offsetDelta).Intersects(aClipRects2[j]))
2640 return PR_TRUE;
2643 return PR_FALSE;
2647 * Given a list of plugin windows to move to new locations, sort the list
2648 * so that for each window move, the window moves to a location that
2649 * does not intersect other windows. This minimizes flicker and repainting.
2650 * It's not always possible to do this perfectly, since in general
2651 * we might have cycles. But we do our best.
2652 * We need to take into account that windows are clipped to particular
2653 * regions and the clip regions change as the windows are moved.
2655 static void
2656 SortConfigurations(nsTArray<nsIWidget::Configuration>* aConfigurations)
2658 if (aConfigurations->Length() > 10) {
2659 // Give up, we don't want to get bogged down here
2660 return;
2663 nsTArray<nsIWidget::Configuration> pluginsToMove;
2664 pluginsToMove.SwapElements(*aConfigurations);
2666 // Our algorithm is quite naive. At each step we try to identify
2667 // a window that can be moved to its new location that won't overlap
2668 // any other windows at the new location. If there is no such
2669 // window, we just move the last window in the list anyway.
2670 while (!pluginsToMove.IsEmpty()) {
2671 // Find a window whose destination does not overlap any other window
2672 PRUint32 i;
2673 for (i = 0; i + 1 < pluginsToMove.Length(); ++i) {
2674 nsIWidget::Configuration* config = &pluginsToMove[i];
2675 PRBool foundOverlap = PR_FALSE;
2676 for (PRUint32 j = 0; j < pluginsToMove.Length(); ++j) {
2677 if (i == j)
2678 continue;
2679 nsIntRect bounds;
2680 pluginsToMove[j].mChild->GetBounds(bounds);
2681 nsAutoTArray<nsIntRect,1> clipRects;
2682 pluginsToMove[j].mChild->GetWindowClipRegion(&clipRects);
2683 if (HasOverlap(bounds.TopLeft(), clipRects,
2684 config->mBounds.TopLeft(),
2685 config->mClipRegion)) {
2686 foundOverlap = PR_TRUE;
2687 break;
2690 if (!foundOverlap)
2691 break;
2693 // Note that we always move the last plugin in pluginsToMove, if we
2694 // can't find any other plugin to move
2695 aConfigurations->AppendElement(pluginsToMove[i]);
2696 pluginsToMove.RemoveElementAt(i);
2700 void
2701 nsRootPresContext::UpdatePluginGeometry()
2703 if (!mNeedsToUpdatePluginGeometry)
2704 return;
2705 mNeedsToUpdatePluginGeometry = PR_FALSE;
2707 nsIFrame* f = mUpdatePluginGeometryForFrame;
2708 if (f) {
2709 mUpdatePluginGeometryForFrame->PresContext()->
2710 SetContainsUpdatePluginGeometryFrame(PR_FALSE);
2711 mUpdatePluginGeometryForFrame = nsnull;
2712 } else {
2713 f = FrameManager()->GetRootFrame();
2716 nsTArray<nsIWidget::Configuration> configurations;
2717 GetPluginGeometryUpdates(f, &configurations);
2718 if (configurations.IsEmpty())
2719 return;
2720 SortConfigurations(&configurations);
2721 nsIWidget* widget = FrameManager()->GetRootFrame()->GetNearestWidget();
2722 NS_ASSERTION(widget, "Plugins must have a parent window");
2723 widget->ConfigureChildren(configurations);
2724 DidApplyPluginGeometryUpdates();
2727 void
2728 nsRootPresContext::SynchronousPluginGeometryUpdate()
2730 if (!mNeedsToUpdatePluginGeometry) {
2731 // Nothing to do
2732 return;
2735 // Force synchronous paint
2736 nsIPresShell* shell = GetPresShell();
2737 if (!shell)
2738 return;
2739 nsIFrame* rootFrame = shell->GetRootFrame();
2740 if (!rootFrame)
2741 return;
2742 nsCOMPtr<nsIWidget> widget = rootFrame->GetNearestWidget();
2743 if (!widget)
2744 return;
2745 // Force synchronous paint of a single pixel, just to force plugin
2746 // updates to be flushed. Doing plugin updates during paint is the best
2747 // way to ensure that plugin updates are in sync with our content.
2748 widget->Invalidate(nsIntRect(0,0,1,1), PR_TRUE);
2750 // Update plugin geometry just in case that invalidate didn't work
2751 // (e.g. if none of the widget is visible, it might not have processed
2752 // a paint event). Normally this won't need to do anything.
2753 UpdatePluginGeometry();
2756 void
2757 nsRootPresContext::RequestUpdatePluginGeometry(nsIFrame* aFrame)
2759 if (mRegisteredPlugins.Count() == 0)
2760 return;
2762 if (!mNeedsToUpdatePluginGeometry) {
2763 mNeedsToUpdatePluginGeometry = PR_TRUE;
2765 // Dispatch a Gecko event to ensure plugin geometry gets updated
2766 // XXX this really should be done through the refresh driver, once
2767 // all painting happens in the refresh driver
2768 nsCOMPtr<nsIRunnable> event =
2769 NS_NewRunnableMethod(this, &nsRootPresContext::SynchronousPluginGeometryUpdate);
2770 NS_DispatchToMainThread(event);
2772 mUpdatePluginGeometryForFrame = aFrame;
2773 mUpdatePluginGeometryForFrame->PresContext()->
2774 SetContainsUpdatePluginGeometryFrame(PR_TRUE);
2775 } else {
2776 if (!mUpdatePluginGeometryForFrame ||
2777 aFrame == mUpdatePluginGeometryForFrame)
2778 return;
2779 mUpdatePluginGeometryForFrame->PresContext()->
2780 SetContainsUpdatePluginGeometryFrame(PR_FALSE);
2781 mUpdatePluginGeometryForFrame = nsnull;
2785 static PLDHashOperator
2786 PluginDidSetGeometryEnumerator(nsPtrHashKey<nsObjectFrame>* aEntry, void* userArg)
2788 nsObjectFrame* f = aEntry->GetKey();
2789 f->DidSetWidgetGeometry();
2790 return PL_DHASH_NEXT;
2793 void
2794 nsRootPresContext::DidApplyPluginGeometryUpdates()
2796 mRegisteredPlugins.EnumerateEntries(PluginDidSetGeometryEnumerator, nsnull);
2799 void
2800 nsRootPresContext::RootForgetUpdatePluginGeometryFrame(nsIFrame* aFrame)
2802 if (aFrame == mUpdatePluginGeometryForFrame) {
2803 mUpdatePluginGeometryForFrame->PresContext()->
2804 SetContainsUpdatePluginGeometryFrame(PR_FALSE);
2805 mUpdatePluginGeometryForFrame = nsnull;
2809 static void
2810 NotifyDidPaintForSubtreeCallback(nsITimer *aTimer, void *aClosure)
2812 nsPresContext* presContext = (nsPresContext*)aClosure;
2813 nsAutoScriptBlocker blockScripts;
2814 presContext->NotifyDidPaintForSubtree();
2817 void
2818 nsRootPresContext::EnsureEventualDidPaintEvent()
2820 if (mNotifyDidPaintTimer)
2821 return;
2822 mNotifyDidPaintTimer = do_CreateInstance("@mozilla.org/timer;1");
2823 if (!mNotifyDidPaintTimer)
2824 return;
2825 mNotifyDidPaintTimer->InitWithFuncCallback(NotifyDidPaintForSubtreeCallback,
2826 (void*)this, 100, nsITimer::TYPE_ONE_SHOT);