Bug 575870 - Enable the firefox button on xp themed, classic, and aero basic. r=dao...
[mozilla-central.git] / layout / base / nsPresShell.cpp
blob7e6f03c83c7da0bed0ee396007730dba7ddd2f8f
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * vim: set ts=2 sw=2 et tw=78:
3 * ***** BEGIN LICENSE BLOCK *****
4 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 * The contents of this file are subject to the Mozilla Public License Version
7 * 1.1 (the "License"); you may not use this file except in compliance with
8 * the License. You may obtain a copy of the License at
9 * http://www.mozilla.org/MPL/
11 * Software distributed under the License is distributed on an "AS IS" basis,
12 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 * for the specific language governing rights and limitations under the
14 * License.
16 * The Original Code is mozilla.org code.
18 * The Initial Developer of the Original Code is
19 * Netscape Communications Corporation.
20 * Portions created by the Initial Developer are Copyright (C) 1998
21 * the Initial Developer. All Rights Reserved.
23 * Contributor(s):
24 * Steve Clark <buster@netscape.com>
25 * HÃ¥kan Waara <hwaara@chello.se>
26 * Dan Rosen <dr@netscape.com>
27 * Daniel Glazman <glazman@netscape.com>
28 * Mats Palmgren <matspal@gmail.com>
30 * Alternatively, the contents of this file may be used under the terms of
31 * either of the GNU General Public License Version 2 or later (the "GPL"),
32 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
33 * in which case the provisions of the GPL or the LGPL are applicable instead
34 * of those above. If you wish to allow use of your version of this file only
35 * under the terms of either the GPL or the LGPL, and not to allow others to
36 * use your version of this file under the terms of the MPL, indicate your
37 * decision by deleting the provisions above and replace them with the notice
38 * and other provisions required by the GPL or the LGPL. If you do not delete
39 * the provisions above, a recipient may use your version of this file under
40 * the terms of any one of the MPL, the GPL or the LGPL.
42 * ***** END LICENSE BLOCK *****
44 * This Original Code has been modified by IBM Corporation.
45 * Modifications made by IBM described herein are
46 * Copyright (c) International Business Machines
47 * Corporation, 2000
49 * Modifications to Mozilla code or documentation
50 * identified per MPL Section 3.3
52 * Date Modified by Description of modification
53 * 05/03/2000 IBM Corp. Observer events for reflow states
56 /* a presentation of a document, part 2 */
58 #include "nsIPresShell.h"
59 #include "nsPresContext.h"
60 #include "nsIContent.h"
61 #include "mozilla/dom/Element.h"
62 #include "nsIDocument.h"
63 #include "nsIDOMXULDocument.h"
64 #include "nsStubDocumentObserver.h"
65 #include "nsStyleSet.h"
66 #include "nsCSSStyleSheet.h" // XXX for UA sheet loading hack, can this go away please?
67 #include "nsIDOMCSSStyleSheet.h" // for Pref-related rule management (bugs 22963,20760,31816)
68 #include "nsINameSpaceManager.h" // for Pref-related rule management (bugs 22963,20760,31816)
69 #include "nsIServiceManager.h"
70 #include "nsFrame.h"
71 #include "nsIViewManager.h"
72 #include "nsCRT.h"
73 #include "nsCRTGlue.h"
74 #include "prlog.h"
75 #include "prmem.h"
76 #include "prprf.h"
77 #include "prinrval.h"
78 #include "nsTArray.h"
79 #include "nsCOMArray.h"
80 #include "nsHashtable.h"
81 #include "nsIViewObserver.h"
82 #include "nsContainerFrame.h"
83 #include "nsIDeviceContext.h"
84 #include "nsEventStateManager.h"
85 #include "nsDOMEvent.h"
86 #include "nsGUIEvent.h"
87 #include "nsHTMLParts.h"
88 #include "nsContentUtils.h"
89 #include "nsISelection.h"
90 #include "nsISelectionController.h"
91 #include "nsISelectionPrivate.h"
92 #include "nsLayoutCID.h"
93 #include "nsGkAtoms.h"
94 #include "nsIDOMRange.h"
95 #include "nsIDOMDocument.h"
96 #include "nsIDOMNode.h"
97 #include "nsIDOM3Node.h"
98 #include "nsIDOMNodeList.h"
99 #include "nsIDOMElement.h"
100 #include "nsRange.h"
101 #include "nsCSSPseudoElements.h"
102 #include "nsCOMPtr.h"
103 #include "nsAutoPtr.h"
104 #include "nsReadableUtils.h"
105 #include "nsUnicharUtils.h"
106 #include "nsWeakReference.h"
107 #include "nsIPageSequenceFrame.h"
108 #include "nsCaret.h"
109 #include "nsIDOMHTMLDocument.h"
110 #include "nsIXPointer.h"
111 #include "nsIDOMXMLDocument.h"
112 #include "nsIParser.h"
113 #include "nsParserCIID.h"
114 #include "nsFrameSelection.h"
115 #include "nsIDOMNSHTMLTextAreaElement.h"
116 #include "nsViewsCID.h"
117 #include "nsPresArena.h"
118 #include "nsFrameManager.h"
119 #include "nsXPCOM.h"
120 #include "nsISupportsPrimitives.h"
121 #include "nsILayoutHistoryState.h"
122 #include "nsILineIterator.h" // for ScrollContentIntoView
123 #include "nsWeakPtr.h"
124 #include "pldhash.h"
125 #include "nsIObserverService.h"
126 #include "nsIObserver.h"
127 #include "nsIDocShell.h" // for reflow observation
128 #include "nsIBaseWindow.h"
129 #include "nsLayoutErrors.h"
130 #include "nsLayoutUtils.h"
131 #include "nsCSSRendering.h"
132 // for |#ifdef DEBUG| code
133 #include "prenv.h"
134 #include "nsIAttribute.h"
135 #include "nsIGlobalHistory2.h"
136 #include "nsDisplayList.h"
137 #include "nsIRegion.h"
138 #include "nsRegion.h"
140 #ifdef MOZ_REFLOW_PERF
141 #include "nsIRenderingContext.h"
142 #include "nsIFontMetrics.h"
143 #endif
145 #include "nsIReflowCallback.h"
147 #include "nsPIDOMWindow.h"
148 #include "nsFocusManager.h"
149 #include "nsIPluginInstance.h"
150 #include "nsIObjectFrame.h"
151 #include "nsIObjectLoadingContent.h"
152 #include "nsNetUtil.h"
153 #include "nsEventDispatcher.h"
154 #include "nsThreadUtils.h"
155 #include "nsStyleSheetService.h"
156 #include "gfxImageSurface.h"
157 #include "gfxContext.h"
158 #ifdef MOZ_MEDIA
159 #include "nsHTMLMediaElement.h"
160 #endif
161 #ifdef MOZ_SMIL
162 #include "nsSMILAnimationController.h"
163 #endif
165 #include "nsRefreshDriver.h"
167 // Drag & Drop, Clipboard
168 #include "nsWidgetsCID.h"
169 #include "nsIClipboard.h"
170 #include "nsIClipboardHelper.h"
171 #include "nsIDocShellTreeItem.h"
172 #include "nsIURI.h"
173 #include "nsIScrollableFrame.h"
174 #include "prtime.h"
175 #include "prlong.h"
176 #include "nsIDragService.h"
177 #include "nsCopySupport.h"
178 #include "nsIDOMHTMLAnchorElement.h"
179 #include "nsIDOMHTMLAreaElement.h"
180 #include "nsIDOMHTMLLinkElement.h"
181 #include "nsITimer.h"
182 #ifdef ACCESSIBILITY
183 #include "nsIAccessibilityService.h"
184 #include "nsAccessible.h"
185 #endif
187 // For style data reconstruction
188 #include "nsStyleChangeList.h"
189 #include "nsCSSFrameConstructor.h"
190 #ifdef MOZ_XUL
191 #include "nsMenuFrame.h"
192 #include "nsTreeBodyFrame.h"
193 #include "nsIBoxObject.h"
194 #include "nsITreeBoxObject.h"
195 #include "nsMenuPopupFrame.h"
196 #include "nsITreeColumns.h"
197 #include "nsIDOMXULMultSelectCntrlEl.h"
198 #include "nsIDOMXULSelectCntrlItemEl.h"
199 #include "nsIDOMXULMenuListElement.h"
201 #endif
202 #include "nsPlaceholderFrame.h"
203 #include "nsCanvasFrame.h"
205 // Content viewer interfaces
206 #include "nsIContentViewer.h"
207 #include "imgIEncoder.h"
208 #include "gfxPlatform.h"
210 #include "mozilla/FunctionTimer.h"
212 #include "Layers.h"
214 #ifdef NS_FUNCTION_TIMER
215 #define NS_TIME_FUNCTION_DECLARE_DOCURL \
216 nsCAutoString docURL__("N/A"); \
217 nsIURI *uri__ = mDocument->GetDocumentURI(); \
218 if (uri__) uri__->GetSpec(docURL__);
219 #define NS_TIME_FUNCTION_WITH_DOCURL \
220 NS_TIME_FUNCTION_DECLARE_DOCURL \
221 NS_TIME_FUNCTION_MIN_FMT(1.0, \
222 "%s (line %d) (document: %s)", MOZ_FUNCTION_NAME, \
223 __LINE__, docURL__.get())
224 #else
225 #define NS_TIME_FUNCTION_WITH_DOCURL do{} while(0)
226 #endif
228 #include "nsContentCID.h"
229 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
231 /* for NS_MEMORY_REPORTER_IMPLEMENT */
232 #include "nsIMemoryReporter.h"
234 using namespace mozilla;
235 using namespace mozilla::dom;
236 using namespace mozilla::layers;
238 PRBool nsIPresShell::gIsAccessibilityActive = PR_FALSE;
239 CapturingContentInfo nsIPresShell::gCaptureInfo;
240 nsIContent* nsIPresShell::gKeyDownTarget;
242 // convert a color value to a string, in the CSS format #RRGGBB
243 // * - initially created for bugs 31816, 20760, 22963
244 static void ColorToString(nscolor aColor, nsAutoString &aString);
246 // Class ID's
247 static NS_DEFINE_CID(kFrameSelectionCID, NS_FRAMESELECTION_CID);
249 // RangePaintInfo is used to paint ranges to offscreen buffers
250 struct RangePaintInfo {
251 nsCOMPtr<nsIRange> mRange;
252 nsDisplayListBuilder mBuilder;
253 nsDisplayList mList;
255 // offset of builder's reference frame to the root frame
256 nsPoint mRootOffset;
258 RangePaintInfo(nsIRange* aRange, nsIFrame* aFrame)
259 : mRange(aRange), mBuilder(aFrame, PR_FALSE, PR_FALSE)
261 MOZ_COUNT_CTOR(RangePaintInfo);
264 ~RangePaintInfo()
266 mList.DeleteAll();
267 MOZ_COUNT_DTOR(RangePaintInfo);
271 #undef NOISY
273 // ----------------------------------------------------------------------
275 #ifdef NS_DEBUG
276 // Set the environment variable GECKO_VERIFY_REFLOW_FLAGS to one or
277 // more of the following flags (comma separated) for handy debug
278 // output.
279 static PRUint32 gVerifyReflowFlags;
281 struct VerifyReflowFlags {
282 const char* name;
283 PRUint32 bit;
286 static const VerifyReflowFlags gFlags[] = {
287 { "verify", VERIFY_REFLOW_ON },
288 { "reflow", VERIFY_REFLOW_NOISY },
289 { "all", VERIFY_REFLOW_ALL },
290 { "list-commands", VERIFY_REFLOW_DUMP_COMMANDS },
291 { "noisy-commands", VERIFY_REFLOW_NOISY_RC },
292 { "really-noisy-commands", VERIFY_REFLOW_REALLY_NOISY_RC },
293 { "resize", VERIFY_REFLOW_DURING_RESIZE_REFLOW },
296 #define NUM_VERIFY_REFLOW_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
298 static void
299 ShowVerifyReflowFlags()
301 printf("Here are the available GECKO_VERIFY_REFLOW_FLAGS:\n");
302 const VerifyReflowFlags* flag = gFlags;
303 const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
304 while (flag < limit) {
305 printf(" %s\n", flag->name);
306 ++flag;
308 printf("Note: GECKO_VERIFY_REFLOW_FLAGS is a comma separated list of flag\n");
309 printf("names (no whitespace)\n");
311 #endif
313 //========================================================================
314 //========================================================================
315 //========================================================================
316 #ifdef MOZ_REFLOW_PERF
317 class ReflowCountMgr;
319 static const char kGrandTotalsStr[] = "Grand Totals";
321 // Counting Class
322 class ReflowCounter {
323 public:
324 ReflowCounter(ReflowCountMgr * aMgr = nsnull);
325 ~ReflowCounter();
327 void ClearTotals();
328 void DisplayTotals(const char * aStr);
329 void DisplayDiffTotals(const char * aStr);
330 void DisplayHTMLTotals(const char * aStr);
332 void Add() { mTotal++; }
333 void Add(PRUint32 aTotal) { mTotal += aTotal; }
335 void CalcDiffInTotals();
336 void SetTotalsCache();
338 void SetMgr(ReflowCountMgr * aMgr) { mMgr = aMgr; }
340 PRUint32 GetTotal() { return mTotal; }
342 protected:
343 void DisplayTotals(PRUint32 aTotal, const char * aTitle);
344 void DisplayHTMLTotals(PRUint32 aTotal, const char * aTitle);
346 PRUint32 mTotal;
347 PRUint32 mCacheTotal;
349 ReflowCountMgr * mMgr; // weak reference (don't delete)
352 // Counting Class
353 class IndiReflowCounter {
354 public:
355 IndiReflowCounter(ReflowCountMgr * aMgr = nsnull)
356 : mFrame(nsnull),
357 mCount(0),
358 mMgr(aMgr),
359 mCounter(aMgr),
360 mHasBeenOutput(PR_FALSE)
362 virtual ~IndiReflowCounter() {}
364 nsAutoString mName;
365 nsIFrame * mFrame; // weak reference (don't delete)
366 PRInt32 mCount;
368 ReflowCountMgr * mMgr; // weak reference (don't delete)
370 ReflowCounter mCounter;
371 PRBool mHasBeenOutput;
375 //--------------------
376 // Manager Class
377 //--------------------
378 class ReflowCountMgr {
379 public:
380 ReflowCountMgr();
381 virtual ~ReflowCountMgr();
383 void ClearTotals();
384 void ClearGrandTotals();
385 void DisplayTotals(const char * aStr);
386 void DisplayHTMLTotals(const char * aStr);
387 void DisplayDiffsInTotals(const char * aStr);
389 void Add(const char * aName, nsIFrame * aFrame);
390 ReflowCounter * LookUp(const char * aName);
392 void PaintCount(const char * aName, nsIRenderingContext* aRenderingContext, nsPresContext* aPresContext, nsIFrame * aFrame, PRUint32 aColor);
394 FILE * GetOutFile() { return mFD; }
396 PLHashTable * GetIndiFrameHT() { return mIndiFrameCounts; }
398 void SetPresContext(nsPresContext * aPresContext) { mPresContext = aPresContext; } // weak reference
399 void SetPresShell(nsIPresShell* aPresShell) { mPresShell= aPresShell; } // weak reference
401 void SetDumpFrameCounts(PRBool aVal) { mDumpFrameCounts = aVal; }
402 void SetDumpFrameByFrameCounts(PRBool aVal) { mDumpFrameByFrameCounts = aVal; }
403 void SetPaintFrameCounts(PRBool aVal) { mPaintFrameByFrameCounts = aVal; }
405 PRBool IsPaintingFrameCounts() { return mPaintFrameByFrameCounts; }
407 protected:
408 void DisplayTotals(PRUint32 aTotal, PRUint32 * aDupArray, char * aTitle);
409 void DisplayHTMLTotals(PRUint32 aTotal, PRUint32 * aDupArray, char * aTitle);
411 static PRIntn RemoveItems(PLHashEntry *he, PRIntn i, void *arg);
412 static PRIntn RemoveIndiItems(PLHashEntry *he, PRIntn i, void *arg);
413 void CleanUp();
415 // stdout Output Methods
416 static PRIntn DoSingleTotal(PLHashEntry *he, PRIntn i, void *arg);
417 static PRIntn DoSingleIndi(PLHashEntry *he, PRIntn i, void *arg);
419 void DoGrandTotals();
420 void DoIndiTotalsTree();
422 // HTML Output Methods
423 static PRIntn DoSingleHTMLTotal(PLHashEntry *he, PRIntn i, void *arg);
424 void DoGrandHTMLTotals();
426 // Zero Out the Totals
427 static PRIntn DoClearTotals(PLHashEntry *he, PRIntn i, void *arg);
429 // Displays the Diff Totals
430 static PRIntn DoDisplayDiffTotals(PLHashEntry *he, PRIntn i, void *arg);
432 PLHashTable * mCounts;
433 PLHashTable * mIndiFrameCounts;
434 FILE * mFD;
436 PRBool mDumpFrameCounts;
437 PRBool mDumpFrameByFrameCounts;
438 PRBool mPaintFrameByFrameCounts;
440 PRBool mCycledOnce;
442 // Root Frame for Individual Tracking
443 nsPresContext * mPresContext;
444 nsIPresShell* mPresShell;
446 // ReflowCountMgr gReflowCountMgr;
448 #endif
449 //========================================================================
451 // comment out to hide caret
452 #define SHOW_CARET
454 // The upper bound on the amount of time to spend reflowing, in
455 // microseconds. When this bound is exceeded and reflow commands are
456 // still queued up, a reflow event is posted. The idea is for reflow
457 // to not hog the processor beyond the time specifed in
458 // gMaxRCProcessingTime. This data member is initialized from the
459 // layout.reflow.timeslice pref.
460 #define NS_MAX_REFLOW_TIME 1000000
461 static PRInt32 gMaxRCProcessingTime = -1;
463 #define MARK_INCREMENT 50
464 #define BLOCK_INCREMENT 4044 /* a bit under 4096, for malloc overhead */
466 /**A block of memory that the stack will
467 * chop up and hand out
469 struct StackBlock {
471 // a block of memory. Note that this must be first so that it will
472 // be aligned.
473 char mBlock[BLOCK_INCREMENT];
475 // another block of memory that would only be created
476 // if our stack overflowed. Yes we have the ability
477 // to grow on a stack overflow
478 StackBlock* mNext;
480 StackBlock() : mNext(nsnull) { }
481 ~StackBlock() { }
484 /* we hold an array of marks. A push pushes a mark on the stack
485 * a pop pops it off.
487 struct StackMark {
488 // the block of memory we are currently handing out chunks of
489 StackBlock* mBlock;
491 // our current position in the memory
492 size_t mPos;
496 /* A stack arena allows a stack based interface to a block of memory.
497 * It should be used when you need to allocate some temporary memory that
498 * you will immediately return.
500 class StackArena {
501 public:
502 StackArena();
503 ~StackArena();
505 nsresult Init() { return mBlocks ? NS_OK : NS_ERROR_OUT_OF_MEMORY; }
507 // Memory management functions
508 void* Allocate(size_t aSize);
509 void Push();
510 void Pop();
512 PRUint32 Size() {
513 PRUint32 result = 0;
514 StackBlock *block = mBlocks;
515 while (block) {
516 result += sizeof(StackBlock);
517 block = block->mNext;
519 return result;
522 private:
523 // our current position in memory
524 size_t mPos;
526 // a list of memory block. Usually there is only one
527 // but if we overrun our stack size we can get more memory.
528 StackBlock* mBlocks;
530 // the current block of memory we are passing our chucks of
531 StackBlock* mCurBlock;
533 // our stack of mark where push has been called
534 StackMark* mMarks;
536 // the current top of the mark list
537 PRUint32 mStackTop;
539 // the size of the mark array
540 PRUint32 mMarkLength;
545 StackArena::StackArena()
547 mMarkLength = 0;
548 mMarks = nsnull;
550 // allocate our stack memory
551 mBlocks = new StackBlock();
552 mCurBlock = mBlocks;
554 mStackTop = 0;
555 mPos = 0;
558 StackArena::~StackArena()
560 // free up our data
561 delete[] mMarks;
562 while(mBlocks)
564 StackBlock* toDelete = mBlocks;
565 mBlocks = mBlocks->mNext;
566 delete toDelete;
570 void
571 StackArena::Push()
573 // Resize the mark array if we overrun it. Failure to allocate the
574 // mark array is not fatal; we just won't free to that mark. This
575 // allows callers not to worry about error checking.
576 if (mStackTop >= mMarkLength)
578 PRUint32 newLength = mStackTop + MARK_INCREMENT;
579 StackMark* newMarks = new StackMark[newLength];
580 if (newMarks) {
581 if (mMarkLength)
582 memcpy(newMarks, mMarks, sizeof(StackMark)*mMarkLength);
583 // Fill in any marks that we couldn't allocate during a prior call
584 // to Push().
585 for (; mMarkLength < mStackTop; ++mMarkLength) {
586 NS_NOTREACHED("should only hit this on out-of-memory");
587 newMarks[mMarkLength].mBlock = mCurBlock;
588 newMarks[mMarkLength].mPos = mPos;
590 delete [] mMarks;
591 mMarks = newMarks;
592 mMarkLength = newLength;
596 // set a mark at the top (if we can)
597 NS_ASSERTION(mStackTop < mMarkLength, "out of memory");
598 if (mStackTop < mMarkLength) {
599 mMarks[mStackTop].mBlock = mCurBlock;
600 mMarks[mStackTop].mPos = mPos;
603 mStackTop++;
606 void*
607 StackArena::Allocate(size_t aSize)
609 NS_ASSERTION(mStackTop > 0, "Allocate called without Push");
611 // make sure we are aligned. Beard said 8 was safer then 4.
612 // Round size to multiple of 8
613 aSize = PR_ROUNDUP(aSize, 8);
615 // if the size makes the stack overflow. Grab another block for the stack
616 if (mPos + aSize >= BLOCK_INCREMENT)
618 NS_ASSERTION(aSize <= BLOCK_INCREMENT,"Requested memory is greater that our block size!!");
619 if (mCurBlock->mNext == nsnull)
620 mCurBlock->mNext = new StackBlock();
622 mCurBlock = mCurBlock->mNext;
623 mPos = 0;
626 // return the chunk they need.
627 void *result = mCurBlock->mBlock + mPos;
628 mPos += aSize;
630 return result;
633 void
634 StackArena::Pop()
636 // pop off the mark
637 NS_ASSERTION(mStackTop > 0, "unmatched pop");
638 mStackTop--;
640 if (mStackTop >= mMarkLength) {
641 // We couldn't allocate the marks array at the time of the push, so
642 // we don't know where we're freeing to.
643 NS_NOTREACHED("out of memory");
644 if (mStackTop == 0) {
645 // But we do know if we've completely pushed the stack.
646 mCurBlock = mBlocks;
647 mPos = 0;
649 return;
652 #ifdef DEBUG
653 // Mark the "freed" memory with 0xdd to help with debugging of memory
654 // allocation problems.
656 StackBlock *block = mMarks[mStackTop].mBlock, *block_end = mCurBlock;
657 size_t pos = mMarks[mStackTop].mPos;
658 for (; block != block_end; block = block->mNext, pos = 0) {
659 memset(block->mBlock + pos, 0xdd, sizeof(block->mBlock) - pos);
661 memset(block->mBlock + pos, 0xdd, mPos - pos);
663 #endif
665 mCurBlock = mMarks[mStackTop].mBlock;
666 mPos = mMarks[mStackTop].mPos;
669 struct nsCallbackEventRequest
671 nsIReflowCallback* callback;
672 nsCallbackEventRequest* next;
675 // ----------------------------------------------------------------------------
676 #define ASSERT_REFLOW_SCHEDULED_STATE() \
677 NS_ASSERTION(mReflowScheduled == \
678 GetPresContext()->RefreshDriver()-> \
679 IsLayoutFlushObserver(this), "Unexpected state")
681 class nsPresShellEventCB;
682 class nsAutoCauseReflowNotifier;
684 class PresShell : public nsIPresShell, public nsIViewObserver,
685 public nsStubDocumentObserver,
686 public nsISelectionController, public nsIObserver,
687 public nsSupportsWeakReference
689 public:
690 PresShell();
692 NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
694 // nsISupports
695 NS_DECL_ISUPPORTS
697 // nsIPresShell
698 virtual NS_HIDDEN_(nsresult) Init(nsIDocument* aDocument,
699 nsPresContext* aPresContext,
700 nsIViewManager* aViewManager,
701 nsStyleSet* aStyleSet,
702 nsCompatibility aCompatMode);
703 virtual NS_HIDDEN_(void) Destroy();
705 virtual NS_HIDDEN_(void*) AllocateFrame(nsQueryFrame::FrameIID aCode,
706 size_t aSize);
707 virtual NS_HIDDEN_(void) FreeFrame(nsQueryFrame::FrameIID aCode,
708 void* aChunk);
710 virtual NS_HIDDEN_(void*) AllocateMisc(size_t aSize);
711 virtual NS_HIDDEN_(void) FreeMisc(size_t aSize, void* aChunk);
713 // Dynamic stack memory allocation
714 virtual NS_HIDDEN_(void) PushStackMemory();
715 virtual NS_HIDDEN_(void) PopStackMemory();
716 virtual NS_HIDDEN_(void*) AllocateStackMemory(size_t aSize);
718 virtual NS_HIDDEN_(nsresult) SetPreferenceStyleRules(PRBool aForceReflow);
720 NS_IMETHOD GetSelection(SelectionType aType, nsISelection** aSelection);
721 virtual nsISelection* GetCurrentSelection(SelectionType aType);
723 NS_IMETHOD SetDisplaySelection(PRInt16 aToggle);
724 NS_IMETHOD GetDisplaySelection(PRInt16 *aToggle);
725 NS_IMETHOD ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion, PRBool aIsSynchronous);
726 NS_IMETHOD RepaintSelection(SelectionType aType);
728 virtual NS_HIDDEN_(void) BeginObservingDocument();
729 virtual NS_HIDDEN_(void) EndObservingDocument();
730 virtual NS_HIDDEN_(nsresult) InitialReflow(nscoord aWidth, nscoord aHeight);
731 virtual NS_HIDDEN_(nsresult) ResizeReflow(nscoord aWidth, nscoord aHeight);
732 virtual NS_HIDDEN_(void) StyleChangeReflow();
733 virtual NS_HIDDEN_(nsIPageSequenceFrame*) GetPageSequenceFrame() const;
734 virtual NS_HIDDEN_(nsIFrame*) GetRealPrimaryFrameFor(nsIContent* aContent) const;
736 virtual NS_HIDDEN_(nsIFrame*) GetPlaceholderFrameFor(nsIFrame* aFrame) const;
737 virtual NS_HIDDEN_(void) FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
738 nsFrameState aBitToAdd);
739 virtual NS_HIDDEN_(void) FrameNeedsToContinueReflow(nsIFrame *aFrame);
740 virtual NS_HIDDEN_(void) CancelAllPendingReflows();
741 virtual NS_HIDDEN_(PRBool) IsSafeToFlush() const;
742 virtual NS_HIDDEN_(void) FlushPendingNotifications(mozFlushType aType);
745 * Recreates the frames for a node
747 virtual NS_HIDDEN_(nsresult) RecreateFramesFor(nsIContent* aContent);
750 * Post a callback that should be handled after reflow has finished.
752 virtual NS_HIDDEN_(nsresult) PostReflowCallback(nsIReflowCallback* aCallback);
753 virtual NS_HIDDEN_(void) CancelReflowCallback(nsIReflowCallback* aCallback);
755 virtual NS_HIDDEN_(void) ClearFrameRefs(nsIFrame* aFrame);
756 virtual NS_HIDDEN_(already_AddRefed<nsIRenderingContext>) GetReferenceRenderingContext();
757 virtual NS_HIDDEN_(nsresult) GoToAnchor(const nsAString& aAnchorName, PRBool aScroll);
758 virtual NS_HIDDEN_(nsresult) ScrollToAnchor();
760 virtual NS_HIDDEN_(nsresult) ScrollContentIntoView(nsIContent* aContent,
761 PRIntn aVPercent,
762 PRIntn aHPercent);
763 virtual PRBool ScrollFrameRectIntoView(nsIFrame* aFrame,
764 const nsRect& aRect,
765 PRIntn aVPercent,
766 PRIntn aHPercent,
767 PRUint32 aFlags);
768 virtual nsRectVisibility GetRectVisibility(nsIFrame *aFrame,
769 const nsRect &aRect,
770 nscoord aMinTwips) const;
772 virtual NS_HIDDEN_(void) SetIgnoreFrameDestruction(PRBool aIgnore);
773 virtual NS_HIDDEN_(void) NotifyDestroyingFrame(nsIFrame* aFrame);
775 virtual NS_HIDDEN_(nsresult) GetLinkLocation(nsIDOMNode* aNode, nsAString& aLocationString) const;
777 virtual NS_HIDDEN_(nsresult) CaptureHistoryState(nsILayoutHistoryState** aLayoutHistoryState, PRBool aLeavingPage);
779 virtual NS_HIDDEN_(void) UnsuppressPainting();
781 virtual nsresult GetAgentStyleSheets(nsCOMArray<nsIStyleSheet>& aSheets);
782 virtual nsresult SetAgentStyleSheets(const nsCOMArray<nsIStyleSheet>& aSheets);
784 virtual nsresult AddOverrideStyleSheet(nsIStyleSheet *aSheet);
785 virtual nsresult RemoveOverrideStyleSheet(nsIStyleSheet *aSheet);
787 virtual NS_HIDDEN_(nsresult) HandleEventWithTarget(nsEvent* aEvent, nsIFrame* aFrame,
788 nsIContent* aContent,
789 nsEventStatus* aStatus);
790 virtual NS_HIDDEN_(nsIFrame*) GetEventTargetFrame();
791 virtual NS_HIDDEN_(already_AddRefed<nsIContent>) GetEventTargetContent(nsEvent* aEvent);
794 virtual nsresult ReconstructFrames(void);
795 virtual void Freeze();
796 virtual void Thaw();
797 virtual void FireOrClearDelayedEvents(PRBool aFireEvents);
799 virtual nsIFrame* GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt);
801 virtual NS_HIDDEN_(nsresult) RenderDocument(const nsRect& aRect, PRUint32 aFlags,
802 nscolor aBackgroundColor,
803 gfxContext* aThebesContext);
805 virtual already_AddRefed<gfxASurface> RenderNode(nsIDOMNode* aNode,
806 nsIntRegion* aRegion,
807 nsIntPoint& aPoint,
808 nsIntRect* aScreenRect);
810 virtual already_AddRefed<gfxASurface> RenderSelection(nsISelection* aSelection,
811 nsIntPoint& aPoint,
812 nsIntRect* aScreenRect);
814 virtual already_AddRefed<nsPIDOMWindow> GetRootWindow();
816 virtual LayerManager* GetLayerManager();
818 //nsIViewObserver interface
820 NS_IMETHOD Paint(nsIView* aDisplayRoot,
821 nsIView* aViewToPaint,
822 nsIWidget* aWidget,
823 const nsRegion& aDirtyRegion,
824 const nsIntRegion& aIntDirtyRegion,
825 PRBool aPaintDefaultBackground,
826 PRBool aWillSendDidPaint);
827 NS_IMETHOD HandleEvent(nsIView* aView,
828 nsGUIEvent* aEvent,
829 nsEventStatus* aEventStatus);
830 virtual NS_HIDDEN_(nsresult) HandleDOMEventWithTarget(nsIContent* aTargetContent,
831 nsEvent* aEvent,
832 nsEventStatus* aStatus);
833 virtual NS_HIDDEN_(nsresult) HandleDOMEventWithTarget(nsIContent* aTargetContent,
834 nsIDOMEvent* aEvent,
835 nsEventStatus* aStatus);
836 NS_IMETHOD ResizeReflow(nsIView *aView, nscoord aWidth, nscoord aHeight);
837 NS_IMETHOD_(PRBool) IsVisible();
838 NS_IMETHOD_(PRBool) ShouldIgnoreInvalidation();
839 NS_IMETHOD_(void) WillPaint(PRBool aWillSendDidPaint);
840 NS_IMETHOD_(void) DidPaint();
841 NS_IMETHOD_(void) DispatchSynthMouseMove(nsGUIEvent *aEvent,
842 PRBool aFlushOnHoverChange);
843 NS_IMETHOD_(void) ClearMouseCapture(nsIView* aView);
845 // caret handling
846 virtual NS_HIDDEN_(already_AddRefed<nsCaret>) GetCaret() const;
847 virtual NS_HIDDEN_(void) MaybeInvalidateCaretPosition();
848 NS_IMETHOD SetCaretEnabled(PRBool aInEnable);
849 NS_IMETHOD SetCaretReadOnly(PRBool aReadOnly);
850 NS_IMETHOD GetCaretEnabled(PRBool *aOutEnabled);
851 NS_IMETHOD SetCaretVisibilityDuringSelection(PRBool aVisibility);
852 NS_IMETHOD GetCaretVisible(PRBool *_retval);
853 virtual void SetCaret(nsCaret *aNewCaret);
854 virtual void RestoreCaret();
856 NS_IMETHOD SetSelectionFlags(PRInt16 aInEnable);
857 NS_IMETHOD GetSelectionFlags(PRInt16 *aOutEnable);
859 // nsISelectionController
861 NS_IMETHOD CharacterMove(PRBool aForward, PRBool aExtend);
862 NS_IMETHOD CharacterExtendForDelete();
863 NS_IMETHOD CharacterExtendForBackspace();
864 NS_IMETHOD WordMove(PRBool aForward, PRBool aExtend);
865 NS_IMETHOD WordExtendForDelete(PRBool aForward);
866 NS_IMETHOD LineMove(PRBool aForward, PRBool aExtend);
867 NS_IMETHOD IntraLineMove(PRBool aForward, PRBool aExtend);
868 NS_IMETHOD PageMove(PRBool aForward, PRBool aExtend);
869 NS_IMETHOD ScrollPage(PRBool aForward);
870 NS_IMETHOD ScrollLine(PRBool aForward);
871 NS_IMETHOD ScrollHorizontal(PRBool aLeft);
872 NS_IMETHOD CompleteScroll(PRBool aForward);
873 NS_IMETHOD CompleteMove(PRBool aForward, PRBool aExtend);
874 NS_IMETHOD SelectAll();
875 NS_IMETHOD CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, PRBool *_retval);
877 // nsIDocumentObserver
878 virtual void BeginUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType);
879 virtual void EndUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType);
880 virtual void BeginLoad(nsIDocument* aDocument);
881 virtual void EndLoad(nsIDocument* aDocument);
882 virtual void ContentStatesChanged(nsIDocument* aDocument,
883 nsIContent* aContent1,
884 nsIContent* aContent2,
885 PRInt32 aStateMask);
886 virtual void DocumentStatesChanged(nsIDocument* aDocument,
887 PRInt32 aStateMask);
888 virtual void StyleSheetAdded(nsIDocument* aDocument,
889 nsIStyleSheet* aStyleSheet,
890 PRBool aDocumentSheet);
891 virtual void StyleSheetRemoved(nsIDocument* aDocument,
892 nsIStyleSheet* aStyleSheet,
893 PRBool aDocumentSheet);
894 virtual void StyleSheetApplicableStateChanged(nsIDocument* aDocument,
895 nsIStyleSheet* aStyleSheet,
896 PRBool aApplicable);
897 virtual void StyleRuleChanged(nsIDocument* aDocument,
898 nsIStyleSheet* aStyleSheet,
899 nsIStyleRule* aOldStyleRule,
900 nsIStyleRule* aNewStyleRule);
901 virtual void StyleRuleAdded(nsIDocument* aDocument,
902 nsIStyleSheet* aStyleSheet,
903 nsIStyleRule* aStyleRule);
904 virtual void StyleRuleRemoved(nsIDocument* aDocument,
905 nsIStyleSheet* aStyleSheet,
906 nsIStyleRule* aStyleRule);
908 // nsIMutationObserver
909 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
910 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
911 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
912 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
913 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
914 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
916 NS_DECL_NSIOBSERVER
918 #ifdef MOZ_REFLOW_PERF
919 virtual NS_HIDDEN_(void) DumpReflows();
920 virtual NS_HIDDEN_(void) CountReflows(const char * aName, nsIFrame * aFrame);
921 virtual NS_HIDDEN_(void) PaintCount(const char * aName,
922 nsIRenderingContext* aRenderingContext,
923 nsPresContext* aPresContext,
924 nsIFrame * aFrame,
925 PRUint32 aColor);
926 virtual NS_HIDDEN_(void) SetPaintFrameCount(PRBool aOn);
927 virtual PRBool IsPaintingFrameCounts();
928 #endif
930 #ifdef DEBUG
931 virtual void ListStyleContexts(nsIFrame *aRootFrame, FILE *out,
932 PRInt32 aIndent = 0);
934 virtual void ListStyleSheets(FILE *out, PRInt32 aIndent = 0);
935 virtual void VerifyStyleTree();
936 #endif
938 #ifdef PR_LOGGING
939 static PRLogModuleInfo* gLog;
940 #endif
942 virtual NS_HIDDEN_(void) DisableNonTestMouseEvents(PRBool aDisable);
944 virtual void UpdateCanvasBackground();
946 virtual nsresult AddCanvasBackgroundColorItem(nsDisplayListBuilder& aBuilder,
947 nsDisplayList& aList,
948 nsIFrame* aFrame,
949 const nsRect& aBounds,
950 nscolor aBackstopColor,
951 PRBool aForceDraw);
953 virtual nsresult AddPrintPreviewBackgroundItem(nsDisplayListBuilder& aBuilder,
954 nsDisplayList& aList,
955 nsIFrame* aFrame,
956 const nsRect& aBounds);
958 virtual nscolor ComputeBackstopColor(nsIView* aDisplayRoot);
960 virtual NS_HIDDEN_(nsresult) SetIsActive(PRBool aIsActive);
962 protected:
963 virtual ~PresShell();
965 void HandlePostedReflowCallbacks(PRBool aInterruptible);
966 void CancelPostedReflowCallbacks();
968 void UnsuppressAndInvalidate();
970 void WillCauseReflow() {
971 nsContentUtils::AddScriptBlocker();
972 ++mChangeNestCount;
974 nsresult DidCauseReflow();
975 friend class nsAutoCauseReflowNotifier;
977 void WillDoReflow();
978 void DidDoReflow(PRBool aInterruptible);
979 // ProcessReflowCommands returns whether we processed all our dirty roots
980 // without interruptions.
981 PRBool ProcessReflowCommands(PRBool aInterruptible);
982 // MaybeScheduleReflow checks if posting a reflow is needed, then checks if
983 // the last reflow was interrupted. In the interrupted case ScheduleReflow is
984 // called off a timer, otherwise it is called directly.
985 void MaybeScheduleReflow();
986 // Actually schedules a reflow. This should only be called by
987 // MaybeScheduleReflow and the reflow timer ScheduleReflowOffTimer
988 // sets up.
989 void ScheduleReflow();
991 // DoReflow returns whether the reflow finished without interruption
992 PRBool DoReflow(nsIFrame* aFrame, PRBool aInterruptible);
993 #ifdef DEBUG
994 void DoVerifyReflow();
995 void VerifyHasDirtyRootAncestor(nsIFrame* aFrame);
996 #endif
998 // Helper for ScrollContentIntoView
999 void DoScrollContentIntoView(nsIContent* aContent,
1000 PRIntn aVPercent,
1001 PRIntn aHPercent);
1003 friend class nsPresShellEventCB;
1005 PRBool mCaretEnabled;
1006 #ifdef NS_DEBUG
1007 nsresult CloneStyleSet(nsStyleSet* aSet, nsStyleSet** aResult);
1008 PRBool VerifyIncrementalReflow();
1009 PRBool mInVerifyReflow;
1010 void ShowEventTargetDebug();
1011 #endif
1014 * methods that manage rules that are used to implement the associated preferences
1015 * - initially created for bugs 31816, 20760, 22963
1017 nsresult ClearPreferenceStyleRules(void);
1018 nsresult CreatePreferenceStyleSheet(void);
1019 nsresult SetPrefLinkRules(void);
1020 nsresult SetPrefFocusRules(void);
1021 nsresult SetPrefNoScriptRule();
1022 nsresult SetPrefNoFramesRule(void);
1024 // methods for painting a range to an offscreen buffer
1026 // given a display list, clip the items within the list to
1027 // the range
1028 nsRect ClipListToRange(nsDisplayListBuilder *aBuilder,
1029 nsDisplayList* aList,
1030 nsIRange* aRange);
1032 // create a RangePaintInfo for the range aRange containing the
1033 // display list needed to paint the range to a surface
1034 RangePaintInfo* CreateRangePaintInfo(nsIDOMRange* aRange,
1035 nsRect& aSurfaceRect,
1036 PRBool aForPrimarySelection);
1039 * Paint the items to a new surface and return it.
1041 * aSelection - selection being painted, if any
1042 * aRegion - clip region, if any
1043 * aArea - area that the surface occupies, relative to the root frame
1044 * aPoint - reference point, typically the mouse position
1045 * aScreenRect - [out] set to the area of the screen the painted area should
1046 * be displayed at
1048 already_AddRefed<gfxASurface>
1049 PaintRangePaintInfo(nsTArray<nsAutoPtr<RangePaintInfo> >* aItems,
1050 nsISelection* aSelection,
1051 nsIntRegion* aRegion,
1052 nsRect aArea,
1053 nsIntPoint& aPoint,
1054 nsIntRect* aScreenRect);
1057 * Methods to handle changes to user and UA sheet lists that we get
1058 * notified about.
1060 void AddUserSheet(nsISupports* aSheet);
1061 void AddAgentSheet(nsISupports* aSheet);
1062 void RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet);
1064 // Hide a view if it is a popup
1065 void HideViewIfPopup(nsIView* aView);
1067 // Utility method to restore the root scrollframe state
1068 void RestoreRootScrollPosition();
1070 void MaybeReleaseCapturingContent()
1072 nsCOMPtr<nsFrameSelection> frameSelection = FrameSelection();
1073 if (frameSelection) {
1074 frameSelection->SetMouseDownState(PR_FALSE);
1076 if (gCaptureInfo.mContent &&
1077 gCaptureInfo.mContent->GetOwnerDoc() == mDocument) {
1078 SetCapturingContent(nsnull, 0);
1082 nsRefPtr<nsCSSStyleSheet> mPrefStyleSheet; // mStyleSet owns it but we
1083 // maintain a ref, may be null
1084 #ifdef DEBUG
1085 PRUint32 mUpdateCount;
1086 #endif
1087 // reflow roots that need to be reflowed, as both a queue and a hashtable
1088 nsTArray<nsIFrame*> mDirtyRoots;
1090 PRPackedBool mDocumentLoading;
1092 PRPackedBool mIgnoreFrameDestruction;
1093 PRPackedBool mHaveShutDown;
1095 // This is used to protect ourselves from triggering reflow while in the
1096 // middle of frame construction and the like... it really shouldn't be
1097 // needed, one hopes, but it is for now.
1098 PRUint32 mChangeNestCount;
1100 nsIFrame* mCurrentEventFrame;
1101 nsCOMPtr<nsIContent> mCurrentEventContent;
1102 nsTArray<nsIFrame*> mCurrentEventFrameStack;
1103 nsCOMArray<nsIContent> mCurrentEventContentStack;
1105 nsCOMPtr<nsIContent> mLastAnchorScrolledTo;
1106 nscoord mLastAnchorScrollPositionY;
1107 nsRefPtr<nsCaret> mCaret;
1108 nsRefPtr<nsCaret> mOriginalCaret;
1109 nsPresArena mFrameArena;
1110 StackArena mStackArena;
1111 nsCOMPtr<nsIDragService> mDragService;
1113 #ifdef DEBUG
1114 // The reflow root under which we're currently reflowing. Null when
1115 // not in reflow.
1116 nsIFrame* mCurrentReflowRoot;
1117 #endif
1119 // Set of frames that we should mark with NS_FRAME_HAS_DIRTY_CHILDREN after
1120 // we finish reflowing mCurrentReflowRoot.
1121 nsTHashtable< nsPtrHashKey<nsIFrame> > mFramesToDirty;
1123 // Information needed to properly handle scrolling content into view if the
1124 // pre-scroll reflow flush can be interrupted. mContentToScrollTo is
1125 // non-null between the initial scroll attempt and the first time we finish
1126 // processing all our dirty roots. mContentScrollVPosition and
1127 // mContentScrollHPosition are only used when it's non-null.
1128 nsCOMPtr<nsIContent> mContentToScrollTo;
1129 PRIntn mContentScrollVPosition;
1130 PRIntn mContentScrollHPosition;
1132 class nsDelayedEvent
1134 public:
1135 virtual ~nsDelayedEvent() {};
1136 virtual void Dispatch(PresShell* aShell) {}
1139 class nsDelayedInputEvent : public nsDelayedEvent
1141 public:
1142 virtual void Dispatch(PresShell* aShell)
1144 if (mEvent && mEvent->widget) {
1145 nsCOMPtr<nsIWidget> w = mEvent->widget;
1146 nsEventStatus status;
1147 w->DispatchEvent(mEvent, status);
1151 protected:
1152 void Init(nsInputEvent* aEvent)
1154 mEvent->time = aEvent->time;
1155 mEvent->refPoint = aEvent->refPoint;
1156 mEvent->isShift = aEvent->isShift;
1157 mEvent->isControl = aEvent->isControl;
1158 mEvent->isAlt = aEvent->isAlt;
1159 mEvent->isMeta = aEvent->isMeta;
1162 nsDelayedInputEvent()
1163 : nsDelayedEvent(), mEvent(nsnull) {}
1165 nsInputEvent* mEvent;
1168 class nsDelayedMouseEvent : public nsDelayedInputEvent
1170 public:
1171 nsDelayedMouseEvent(nsMouseEvent* aEvent) : nsDelayedInputEvent()
1173 mEvent = new nsMouseEvent(NS_IS_TRUSTED_EVENT(aEvent),
1174 aEvent->message,
1175 aEvent->widget,
1176 aEvent->reason,
1177 aEvent->context);
1178 if (mEvent) {
1179 Init(aEvent);
1180 static_cast<nsMouseEvent*>(mEvent)->clickCount = aEvent->clickCount;
1184 virtual ~nsDelayedMouseEvent()
1186 delete static_cast<nsMouseEvent*>(mEvent);
1190 class nsDelayedKeyEvent : public nsDelayedInputEvent
1192 public:
1193 nsDelayedKeyEvent(nsKeyEvent* aEvent) : nsDelayedInputEvent()
1195 mEvent = new nsKeyEvent(NS_IS_TRUSTED_EVENT(aEvent),
1196 aEvent->message,
1197 aEvent->widget);
1198 if (mEvent) {
1199 Init(aEvent);
1200 static_cast<nsKeyEvent*>(mEvent)->keyCode = aEvent->keyCode;
1201 static_cast<nsKeyEvent*>(mEvent)->charCode = aEvent->charCode;
1202 static_cast<nsKeyEvent*>(mEvent)->alternativeCharCodes =
1203 aEvent->alternativeCharCodes;
1204 static_cast<nsKeyEvent*>(mEvent)->isChar = aEvent->isChar;
1208 virtual ~nsDelayedKeyEvent()
1210 delete static_cast<nsKeyEvent*>(mEvent);
1214 PRPackedBool mNoDelayedMouseEvents;
1215 PRPackedBool mNoDelayedKeyEvents;
1216 nsTArray<nsAutoPtr<nsDelayedEvent> > mDelayedEvents;
1218 nsCallbackEventRequest* mFirstCallbackEventRequest;
1219 nsCallbackEventRequest* mLastCallbackEventRequest;
1221 PRPackedBool mIsDocumentGone; // We've been disconnected from the document.
1222 // We will refuse to paint the document until either
1223 // (a) our timer fires or (b) all frames are constructed.
1224 PRPackedBool mShouldUnsuppressPainting; // Indicates that it is safe to unlock painting once all pending
1225 // reflows have been processed.
1226 nsCOMPtr<nsITimer> mPaintSuppressionTimer; // This timer controls painting suppression. Until it fires
1227 // or all frames are constructed, we won't paint anything but
1228 // our <body> background and scrollbars.
1229 #define PAINTLOCK_EVENT_DELAY 250 // 250ms. This is actually
1230 // pref-controlled, but we use this
1231 // value if we fail to get the pref
1232 // for any reason.
1234 static void sPaintSuppressionCallback(nsITimer* aTimer, void* aPresShell); // A callback for the timer.
1236 // At least on Win32 and Mac after interupting a reflow we need to post
1237 // the resume reflow event off a timer to avoid event starvation because
1238 // posted messages are processed before other messages when the modal
1239 // moving/sizing loop is running, see bug 491700 for details.
1240 nsCOMPtr<nsITimer> mReflowContinueTimer;
1241 static void sReflowContinueCallback(nsITimer* aTimer, void* aPresShell);
1242 PRBool ScheduleReflowOffTimer();
1244 #ifdef MOZ_REFLOW_PERF
1245 ReflowCountMgr * mReflowCountMgr;
1246 #endif
1248 static PRBool sDisableNonTestMouseEvents;
1250 // false if a check should be done for key/ime events that should be
1251 // retargeted to the currently focused presshell
1252 static PRBool sDontRetargetEvents;
1254 private:
1256 PRBool InZombieDocument(nsIContent *aContent);
1257 already_AddRefed<nsIPresShell> GetParentPresShell();
1258 nsresult RetargetEventToParent(nsGUIEvent* aEvent,
1259 nsEventStatus* aEventStatus);
1261 //helper funcs for event handling
1262 protected:
1263 //protected because nsPresShellEventCB needs this.
1264 nsIFrame* GetCurrentEventFrame();
1265 private:
1266 void PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent);
1267 void PopCurrentEventInfo();
1268 nsresult HandleEventInternal(nsEvent* aEvent, nsIView* aView,
1269 nsEventStatus *aStatus);
1270 nsresult HandlePositionedEvent(nsIView* aView,
1271 nsIFrame* aTargetFrame,
1272 nsGUIEvent* aEvent,
1273 nsEventStatus* aEventStatus);
1274 // This returns the focused DOM window under our top level window.
1275 // I.e., when we are deactive, this returns the *last* focused DOM window.
1276 already_AddRefed<nsPIDOMWindow> GetFocusedDOMWindowInOurWindow();
1279 * This and the next two helper methods are used to target and position the
1280 * context menu when the keyboard shortcut is used to open it.
1282 * If another menu is open, the context menu is opened relative to the
1283 * active menuitem within the menu, or the menu itself if no item is active.
1284 * Otherwise, if the caret is visible, the menu is opened near the caret.
1285 * Otherwise, if a selectable list such as a listbox is focused, the
1286 * current item within the menu is opened relative to this item.
1287 * Otherwise, the context menu is opened at the topleft corner of the
1288 * view.
1290 * Returns true if the context menu event should fire and false if it should
1291 * not.
1293 PRBool AdjustContextMenuKeyEvent(nsMouseEvent* aEvent);
1296 PRBool PrepareToUseCaretPosition(nsIWidget* aEventWidget, nsIntPoint& aTargetPt);
1298 // Get the selected item and coordinates in device pixels relative to root
1299 // document's root view for element, first ensuring the element is onscreen
1300 void GetCurrentItemAndPositionForElement(nsIDOMElement *aCurrentEl,
1301 nsIContent **aTargetToUse,
1302 nsIntPoint& aTargetPt,
1303 nsIWidget *aRootWidget);
1305 void FireResizeEvent();
1306 static void AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell);
1307 nsRevocableEventPtr<nsRunnableMethod<PresShell> > mResizeEvent;
1308 nsCOMPtr<nsITimer> mAsyncResizeEventTimer;
1309 PRPackedBool mAsyncResizeTimerIsActive;
1310 PRPackedBool mInResize;
1312 private:
1313 #ifdef DEBUG
1314 // Ensure that every allocation from the PresArena is eventually freed.
1315 PRUint32 mPresArenaAllocCount;
1316 #endif
1318 public:
1320 PRUint32 EstimateMemoryUsed() {
1321 PRUint32 result = 0;
1323 result += sizeof(PresShell);
1324 result += mStackArena.Size();
1325 result += mFrameArena.Size();
1327 return result;
1330 static PLDHashOperator LiveShellSizeEnumerator(PresShellPtrKey *aEntry,
1331 void *userArg)
1333 PresShell *aShell = static_cast<PresShell*>(aEntry->GetKey());
1334 PRUint32 *val = (PRUint32*)userArg;
1335 *val += aShell->EstimateMemoryUsed();
1336 *val += aShell->mPresContext->EstimateMemoryUsed();
1337 return PL_DHASH_NEXT;
1340 static PLDHashOperator LiveShellBidiSizeEnumerator(PresShellPtrKey *aEntry,
1341 void *userArg)
1343 PresShell *aShell = static_cast<PresShell*>(aEntry->GetKey());
1344 PRUint32 *val = (PRUint32*)userArg;
1345 *val += aShell->mPresContext->GetBidiMemoryUsed();
1346 return PL_DHASH_NEXT;
1349 static PRUint32
1350 EstimateShellsMemory(nsTHashtable<PresShellPtrKey>::Enumerator aEnumerator)
1352 PRUint32 result = 0;
1353 sLiveShells->EnumerateEntries(aEnumerator, &result);
1354 return result;
1358 static PRInt64 SizeOfLayoutMemoryReporter(void *) {
1359 return EstimateShellsMemory(LiveShellSizeEnumerator);
1362 static PRInt64 SizeOfBidiMemoryReporter(void *) {
1363 return EstimateShellsMemory(LiveShellBidiSizeEnumerator);
1366 protected:
1367 void QueryIsActive();
1368 nsresult UpdateImageLockingState();
1371 class nsAutoCauseReflowNotifier
1373 public:
1374 nsAutoCauseReflowNotifier(PresShell* aShell)
1375 : mShell(aShell)
1377 mShell->WillCauseReflow();
1379 ~nsAutoCauseReflowNotifier()
1381 // This check should not be needed. Currently the only place that seem
1382 // to need it is the code that deals with bug 337586.
1383 if (!mShell->mHaveShutDown) {
1384 mShell->DidCauseReflow();
1386 else {
1387 nsContentUtils::RemoveScriptBlocker();
1391 PresShell* mShell;
1394 class NS_STACK_CLASS nsPresShellEventCB : public nsDispatchingCallback
1396 public:
1397 nsPresShellEventCB(PresShell* aPresShell) : mPresShell(aPresShell) {}
1399 virtual void HandleEvent(nsEventChainPostVisitor& aVisitor)
1401 if (aVisitor.mPresContext && aVisitor.mEvent->eventStructType != NS_EVENT) {
1402 nsIFrame* frame = mPresShell->GetCurrentEventFrame();
1403 if (frame) {
1404 frame->HandleEvent(aVisitor.mPresContext,
1405 (nsGUIEvent*) aVisitor.mEvent,
1406 &aVisitor.mEventStatus);
1411 nsRefPtr<PresShell> mPresShell;
1414 PRBool PresShell::sDisableNonTestMouseEvents = PR_FALSE;
1415 PRBool PresShell::sDontRetargetEvents = PR_FALSE;
1417 #ifdef PR_LOGGING
1418 PRLogModuleInfo* PresShell::gLog;
1419 #endif
1421 #ifdef NS_DEBUG
1422 static void
1423 VerifyStyleTree(nsPresContext* aPresContext, nsFrameManager* aFrameManager)
1425 if (nsFrame::GetVerifyStyleTreeEnable()) {
1426 nsIFrame* rootFrame = aFrameManager->GetRootFrame();
1427 aFrameManager->DebugVerifyStyleTree(rootFrame);
1430 #define VERIFY_STYLE_TREE ::VerifyStyleTree(mPresContext, FrameManager())
1431 #else
1432 #define VERIFY_STYLE_TREE
1433 #endif
1435 static PRBool gVerifyReflowEnabled;
1437 PRBool
1438 nsIPresShell::GetVerifyReflowEnable()
1440 #ifdef NS_DEBUG
1441 static PRBool firstTime = PR_TRUE;
1442 if (firstTime) {
1443 firstTime = PR_FALSE;
1444 char* flags = PR_GetEnv("GECKO_VERIFY_REFLOW_FLAGS");
1445 if (flags) {
1446 PRBool error = PR_FALSE;
1448 for (;;) {
1449 char* comma = PL_strchr(flags, ',');
1450 if (comma)
1451 *comma = '\0';
1453 PRBool found = PR_FALSE;
1454 const VerifyReflowFlags* flag = gFlags;
1455 const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
1456 while (flag < limit) {
1457 if (PL_strcasecmp(flag->name, flags) == 0) {
1458 gVerifyReflowFlags |= flag->bit;
1459 found = PR_TRUE;
1460 break;
1462 ++flag;
1465 if (! found)
1466 error = PR_TRUE;
1468 if (! comma)
1469 break;
1471 *comma = ',';
1472 flags = comma + 1;
1475 if (error)
1476 ShowVerifyReflowFlags();
1479 if (VERIFY_REFLOW_ON & gVerifyReflowFlags) {
1480 gVerifyReflowEnabled = PR_TRUE;
1482 printf("Note: verifyreflow is enabled");
1483 if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
1484 printf(" (noisy)");
1486 if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) {
1487 printf(" (all)");
1489 if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
1490 printf(" (show reflow commands)");
1492 if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
1493 printf(" (noisy reflow commands)");
1494 if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) {
1495 printf(" (REALLY noisy reflow commands)");
1498 printf("\n");
1501 #endif
1502 return gVerifyReflowEnabled;
1505 void
1506 nsIPresShell::SetVerifyReflowEnable(PRBool aEnabled)
1508 gVerifyReflowEnabled = aEnabled;
1511 /* virtual */ void
1512 nsIPresShell::AddWeakFrameExternal(nsWeakFrame* aWeakFrame)
1514 AddWeakFrameInternal(aWeakFrame);
1517 void
1518 nsIPresShell::AddWeakFrameInternal(nsWeakFrame* aWeakFrame)
1520 if (aWeakFrame->GetFrame()) {
1521 aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
1523 aWeakFrame->SetPreviousWeakFrame(mWeakFrames);
1524 mWeakFrames = aWeakFrame;
1527 /* virtual */ void
1528 nsIPresShell::RemoveWeakFrameExternal(nsWeakFrame* aWeakFrame)
1530 RemoveWeakFrameInternal(aWeakFrame);
1533 void
1534 nsIPresShell::RemoveWeakFrameInternal(nsWeakFrame* aWeakFrame)
1536 if (mWeakFrames == aWeakFrame) {
1537 mWeakFrames = aWeakFrame->GetPreviousWeakFrame();
1538 return;
1540 nsWeakFrame* nextWeak = mWeakFrames;
1541 while (nextWeak && nextWeak->GetPreviousWeakFrame() != aWeakFrame) {
1542 nextWeak = nextWeak->GetPreviousWeakFrame();
1544 if (nextWeak) {
1545 nextWeak->SetPreviousWeakFrame(aWeakFrame->GetPreviousWeakFrame());
1549 already_AddRefed<nsFrameSelection>
1550 nsIPresShell::FrameSelection()
1552 NS_IF_ADDREF(mSelection);
1553 return mSelection;
1556 //----------------------------------------------------------------------
1558 nsresult
1559 NS_NewPresShell(nsIPresShell** aInstancePtrResult)
1561 NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
1563 if (!aInstancePtrResult)
1564 return NS_ERROR_NULL_POINTER;
1566 *aInstancePtrResult = new PresShell();
1567 if (!*aInstancePtrResult)
1568 return NS_ERROR_OUT_OF_MEMORY;
1570 NS_ADDREF(*aInstancePtrResult);
1571 return NS_OK;
1574 nsTHashtable<PresShell::PresShellPtrKey> *nsIPresShell::sLiveShells = 0;
1576 NS_MEMORY_REPORTER_IMPLEMENT(LayoutPresShell,
1577 "layout/all",
1578 "Memory in use by layout PresShell, PresContext, and other related areas.",
1579 PresShell::SizeOfLayoutMemoryReporter,
1580 nsnull)
1582 NS_MEMORY_REPORTER_IMPLEMENT(LayoutBidi,
1583 "layout/bidi",
1584 "Memory in use by layout Bidi processor.",
1585 PresShell::SizeOfBidiMemoryReporter,
1586 nsnull)
1588 PresShell::PresShell()
1590 mSelection = nsnull;
1591 #ifdef MOZ_REFLOW_PERF
1592 mReflowCountMgr = new ReflowCountMgr();
1593 mReflowCountMgr->SetPresContext(mPresContext);
1594 mReflowCountMgr->SetPresShell(this);
1595 #endif
1596 #ifdef PR_LOGGING
1597 if (! gLog)
1598 gLog = PR_NewLogModule("PresShell");
1599 #endif
1600 mSelectionFlags = nsISelectionDisplay::DISPLAY_TEXT | nsISelectionDisplay::DISPLAY_IMAGES;
1601 mIsThemeSupportDisabled = PR_FALSE;
1602 mIsActive = PR_TRUE;
1603 mFrozen = PR_FALSE;
1604 #ifdef DEBUG
1605 mPresArenaAllocCount = 0;
1606 #endif
1608 static bool registeredReporter = false;
1609 if (!registeredReporter) {
1610 NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(LayoutPresShell));
1611 NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(LayoutBidi));
1612 registeredReporter = true;
1615 new (this) nsFrameManager();
1617 sLiveShells->PutEntry(this);
1620 NS_IMPL_ISUPPORTS8(PresShell, nsIPresShell, nsIDocumentObserver,
1621 nsIViewObserver, nsISelectionController,
1622 nsISelectionDisplay, nsIObserver, nsISupportsWeakReference,
1623 nsIMutationObserver)
1625 PresShell::~PresShell()
1627 sLiveShells->RemoveEntry(this);
1629 if (!mHaveShutDown) {
1630 NS_NOTREACHED("Someone did not call nsIPresShell::destroy");
1631 Destroy();
1634 NS_ASSERTION(mCurrentEventContentStack.Count() == 0,
1635 "Huh, event content left on the stack in pres shell dtor!");
1636 NS_ASSERTION(mFirstCallbackEventRequest == nsnull &&
1637 mLastCallbackEventRequest == nsnull,
1638 "post-reflow queues not empty. This means we're leaking");
1640 #ifdef DEBUG
1641 NS_ASSERTION(mPresArenaAllocCount == 0,
1642 "Some pres arena objects were not freed");
1643 #endif
1645 delete mStyleSet;
1646 delete mFrameConstructor;
1648 mCurrentEventContent = nsnull;
1650 NS_IF_RELEASE(mPresContext);
1651 NS_IF_RELEASE(mDocument);
1652 NS_IF_RELEASE(mSelection);
1656 * Initialize the presentation shell. Create view manager and style
1657 * manager.
1659 nsresult
1660 PresShell::Init(nsIDocument* aDocument,
1661 nsPresContext* aPresContext,
1662 nsIViewManager* aViewManager,
1663 nsStyleSet* aStyleSet,
1664 nsCompatibility aCompatMode)
1666 NS_TIME_FUNCTION_MIN(1.0);
1668 NS_PRECONDITION(nsnull != aDocument, "null ptr");
1669 NS_PRECONDITION(nsnull != aPresContext, "null ptr");
1670 NS_PRECONDITION(nsnull != aViewManager, "null ptr");
1671 nsresult result;
1673 if ((nsnull == aDocument) || (nsnull == aPresContext) ||
1674 (nsnull == aViewManager)) {
1675 return NS_ERROR_NULL_POINTER;
1677 if (mDocument) {
1678 NS_WARNING("PresShell double init'ed");
1679 return NS_ERROR_ALREADY_INITIALIZED;
1681 result = mStackArena.Init();
1682 NS_ENSURE_SUCCESS(result, result);
1684 if (!mFramesToDirty.Init()) {
1685 return NS_ERROR_OUT_OF_MEMORY;
1688 mDocument = aDocument;
1689 NS_ADDREF(mDocument);
1690 mViewManager = aViewManager;
1692 // Create our frame constructor.
1693 mFrameConstructor = new nsCSSFrameConstructor(mDocument, this);
1694 NS_ENSURE_TRUE(mFrameConstructor, NS_ERROR_OUT_OF_MEMORY);
1696 // The document viewer owns both view manager and pres shell.
1697 mViewManager->SetViewObserver(this);
1699 // Bind the context to the presentation shell.
1700 mPresContext = aPresContext;
1701 NS_ADDREF(mPresContext);
1702 aPresContext->SetShell(this);
1704 // Now we can initialize the style set.
1705 result = aStyleSet->Init(aPresContext);
1706 NS_ENSURE_SUCCESS(result, result);
1708 // From this point on, any time we return an error we need to make
1709 // sure to null out mStyleSet first, since an error return from this
1710 // method will cause the caller to delete the style set, so we don't
1711 // want to delete it in our destructor.
1712 mStyleSet = aStyleSet;
1714 // Notify our prescontext that it now has a compatibility mode. Note that
1715 // this MUST happen after we set up our style set but before we create any
1716 // frames.
1717 mPresContext->CompatibilityModeChanged();
1719 // setup the preference style rules (no forced reflow), and do it
1720 // before creating any frames.
1721 SetPreferenceStyleRules(PR_FALSE);
1723 result = CallCreateInstance(kFrameSelectionCID, &mSelection);
1724 if (NS_FAILED(result)) {
1725 mStyleSet = nsnull;
1726 return result;
1729 // Create and initialize the frame manager
1730 result = FrameManager()->Init(this, mStyleSet);
1731 if (NS_FAILED(result)) {
1732 NS_WARNING("Frame manager initialization failed");
1733 mStyleSet = nsnull;
1734 return result;
1737 mSelection->Init(this, nsnull);
1739 // Important: this has to happen after the selection has been set up
1740 #ifdef SHOW_CARET
1741 // make the caret
1742 nsresult err = NS_NewCaret(getter_AddRefs(mCaret));
1743 if (NS_SUCCEEDED(err))
1745 mCaret->Init(this);
1746 mOriginalCaret = mCaret;
1749 //SetCaretEnabled(PR_TRUE); // make it show in browser windows
1750 #endif
1751 //set up selection to be displayed in document
1752 // Don't enable selection for print media
1753 nsPresContext::nsPresContextType type = aPresContext->Type();
1754 if (type != nsPresContext::eContext_PrintPreview &&
1755 type != nsPresContext::eContext_Print)
1756 SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
1758 if (gMaxRCProcessingTime == -1) {
1759 gMaxRCProcessingTime =
1760 nsContentUtils::GetIntPref("layout.reflow.timeslice",
1761 NS_MAX_REFLOW_TIME);
1765 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1766 if (os) {
1767 os->AddObserver(this, "agent-sheet-added", PR_FALSE);
1768 os->AddObserver(this, "user-sheet-added", PR_FALSE);
1769 os->AddObserver(this, "agent-sheet-removed", PR_FALSE);
1770 os->AddObserver(this, "user-sheet-removed", PR_FALSE);
1771 #ifdef MOZ_XUL
1772 os->AddObserver(this, "chrome-flush-skin-caches", PR_FALSE);
1773 #endif
1774 #ifdef ACCESSIBILITY
1775 os->AddObserver(this, "a11y-init-or-shutdown", PR_FALSE);
1776 #endif
1780 // cache the drag service so we can check it during reflows
1781 mDragService = do_GetService("@mozilla.org/widget/dragservice;1");
1783 #ifdef MOZ_REFLOW_PERF
1784 if (mReflowCountMgr) {
1785 PRBool paintFrameCounts =
1786 nsContentUtils::GetBoolPref("layout.reflow.showframecounts");
1788 PRBool dumpFrameCounts =
1789 nsContentUtils::GetBoolPref("layout.reflow.dumpframecounts");
1791 PRBool dumpFrameByFrameCounts =
1792 nsContentUtils::GetBoolPref("layout.reflow.dumpframebyframecounts");
1794 mReflowCountMgr->SetDumpFrameCounts(dumpFrameCounts);
1795 mReflowCountMgr->SetDumpFrameByFrameCounts(dumpFrameByFrameCounts);
1796 mReflowCountMgr->SetPaintFrameCounts(paintFrameCounts);
1798 #endif
1800 #ifdef MOZ_SMIL
1801 if (mDocument->HasAnimationController()) {
1802 nsSMILAnimationController* animCtrl = mDocument->GetAnimationController();
1803 if (!animCtrl->IsPaused()) {
1804 animCtrl->StartSampling(GetPresContext()->RefreshDriver());
1807 #endif // MOZ_SMIL
1809 // Get our activeness from the docShell.
1810 QueryIsActive();
1812 return NS_OK;
1815 void
1816 PresShell::Destroy()
1818 NS_TIME_FUNCTION_MIN(1.0);
1820 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
1821 "destroy called on presshell while scripts not blocked");
1823 #ifdef MOZ_REFLOW_PERF
1824 DumpReflows();
1825 if (mReflowCountMgr) {
1826 delete mReflowCountMgr;
1827 mReflowCountMgr = nsnull;
1829 #endif
1831 if (mHaveShutDown)
1832 return;
1834 #ifdef ACCESSIBILITY
1835 if (gIsAccessibilityActive) {
1836 nsCOMPtr<nsIAccessibilityService> accService =
1837 do_GetService("@mozilla.org/accessibilityService;1");
1838 if (accService) {
1839 accService->PresShellDestroyed(this);
1842 #endif // ACCESSIBILITY
1844 MaybeReleaseCapturingContent();
1846 if (gKeyDownTarget && gKeyDownTarget->GetOwnerDoc() == mDocument) {
1847 NS_RELEASE(gKeyDownTarget);
1850 mContentToScrollTo = nsnull;
1852 if (mPresContext) {
1853 // We need to notify the destroying the nsPresContext to ESM for
1854 // suppressing to use from ESM.
1855 mPresContext->EventStateManager()->NotifyDestroyPresContext(mPresContext);
1859 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1860 if (os) {
1861 os->RemoveObserver(this, "agent-sheet-added");
1862 os->RemoveObserver(this, "user-sheet-added");
1863 os->RemoveObserver(this, "agent-sheet-removed");
1864 os->RemoveObserver(this, "user-sheet-removed");
1865 #ifdef MOZ_XUL
1866 os->RemoveObserver(this, "chrome-flush-skin-caches");
1867 #endif
1868 #ifdef ACCESSIBILITY
1869 os->RemoveObserver(this, "a11y-init-or-shutdown");
1870 #endif
1874 // If our paint suppression timer is still active, kill it.
1875 if (mPaintSuppressionTimer) {
1876 mPaintSuppressionTimer->Cancel();
1877 mPaintSuppressionTimer = nsnull;
1880 // Same for our reflow continuation timer
1881 if (mReflowContinueTimer) {
1882 mReflowContinueTimer->Cancel();
1883 mReflowContinueTimer = nsnull;
1886 if (mCaret) {
1887 mCaret->Terminate();
1888 mCaret = nsnull;
1891 if (mSelection) {
1892 mSelection->DisconnectFromPresShell();
1895 // release our pref style sheet, if we have one still
1896 ClearPreferenceStyleRules();
1898 mIsDestroying = PR_TRUE;
1900 // We can't release all the event content in
1901 // mCurrentEventContentStack here since there might be code on the
1902 // stack that will release the event content too. Double release
1903 // bad!
1905 // The frames will be torn down, so remove them from the current
1906 // event frame stack (since they'd be dangling references if we'd
1907 // leave them in) and null out the mCurrentEventFrame pointer as
1908 // well.
1910 mCurrentEventFrame = nsnull;
1912 PRInt32 i, count = mCurrentEventFrameStack.Length();
1913 for (i = 0; i < count; i++) {
1914 mCurrentEventFrameStack[i] = nsnull;
1917 mFramesToDirty.Clear();
1919 if (mViewManager) {
1920 // Clear the view manager's weak pointer back to |this| in case it
1921 // was leaked.
1922 mViewManager->SetViewObserver(nsnull);
1923 mViewManager = nsnull;
1926 mStyleSet->BeginShutdown(mPresContext);
1927 nsRefreshDriver* rd = GetPresContext()->RefreshDriver();
1929 // This shell must be removed from the document before the frame
1930 // hierarchy is torn down to avoid finding deleted frames through
1931 // this presshell while the frames are being torn down
1932 if (mDocument) {
1933 NS_ASSERTION(mDocument->GetShell() == this, "Wrong shell?");
1934 mDocument->DeleteShell();
1936 #ifdef MOZ_SMIL
1937 if (mDocument->HasAnimationController()) {
1938 mDocument->GetAnimationController()->StopSampling(rd);
1940 #endif // MOZ_SMIL
1943 // Revoke any pending events. We need to do this and cancel pending reflows
1944 // before we destroy the frame manager, since apparently frame destruction
1945 // sometimes spins the event queue when plug-ins are involved(!).
1946 rd->RemoveLayoutFlushObserver(this);
1947 mResizeEvent.Revoke();
1948 if (mAsyncResizeTimerIsActive) {
1949 mAsyncResizeEventTimer->Cancel();
1950 mAsyncResizeTimerIsActive = PR_FALSE;
1953 CancelAllPendingReflows();
1954 CancelPostedReflowCallbacks();
1956 // Destroy the frame manager. This will destroy the frame hierarchy
1957 mFrameConstructor->WillDestroyFrameTree();
1958 FrameManager()->Destroy();
1960 // Destroy all frame properties (whose destruction was suppressed
1961 // while destroying the frame tree, but which might contain more
1962 // frames within the properties.
1963 if (mPresContext) {
1964 // Clear out the prescontext's property table -- since our frame tree is
1965 // now dead, we shouldn't be looking up any more properties in that table.
1966 // We want to do this before we call SetShell() on the prescontext, so
1967 // property destructors can usefully call GetPresShell() on the
1968 // prescontext.
1969 mPresContext->PropertyTable()->DeleteAll();
1973 NS_WARN_IF_FALSE(!mWeakFrames, "Weak frames alive after destroying FrameManager");
1974 while (mWeakFrames) {
1975 mWeakFrames->Clear(this);
1978 // Let the style set do its cleanup.
1979 mStyleSet->Shutdown(mPresContext);
1981 if (mPresContext) {
1982 // We hold a reference to the pres context, and it holds a weak link back
1983 // to us. To avoid the pres context having a dangling reference, set its
1984 // pres shell to NULL
1985 mPresContext->SetShell(nsnull);
1987 // Clear the link handler (weak reference) as well
1988 mPresContext->SetLinkHandler(nsnull);
1991 mHaveShutDown = PR_TRUE;
1994 // Dynamic stack memory allocation
1995 /* virtual */ void
1996 PresShell::PushStackMemory()
1998 mStackArena.Push();
2001 /* virtual */ void
2002 PresShell::PopStackMemory()
2004 mStackArena.Pop();
2007 /* virtual */ void*
2008 PresShell::AllocateStackMemory(size_t aSize)
2010 return mStackArena.Allocate(aSize);
2013 void
2014 PresShell::FreeFrame(nsQueryFrame::FrameIID aCode, void* aPtr)
2016 #ifdef DEBUG
2017 mPresArenaAllocCount--;
2018 #endif
2019 if (PRESARENA_MUST_FREE_DURING_DESTROY || !mIsDestroying)
2020 mFrameArena.FreeByCode(aCode, aPtr);
2023 void*
2024 PresShell::AllocateFrame(nsQueryFrame::FrameIID aCode, size_t aSize)
2026 #ifdef DEBUG
2027 mPresArenaAllocCount++;
2028 #endif
2029 void* result = mFrameArena.AllocateByCode(aCode, aSize);
2031 if (result) {
2032 memset(result, 0, aSize);
2034 return result;
2037 void
2038 PresShell::FreeMisc(size_t aSize, void* aPtr)
2040 #ifdef DEBUG
2041 mPresArenaAllocCount--;
2042 #endif
2043 if (PRESARENA_MUST_FREE_DURING_DESTROY || !mIsDestroying)
2044 mFrameArena.FreeBySize(aSize, aPtr);
2047 void*
2048 PresShell::AllocateMisc(size_t aSize)
2050 #ifdef DEBUG
2051 mPresArenaAllocCount++;
2052 #endif
2053 return mFrameArena.AllocateBySize(aSize);
2056 void
2057 nsIPresShell::SetAuthorStyleDisabled(PRBool aStyleDisabled)
2059 if (aStyleDisabled != mStyleSet->GetAuthorStyleDisabled()) {
2060 mStyleSet->SetAuthorStyleDisabled(aStyleDisabled);
2061 ReconstructStyleData();
2065 PRBool
2066 nsIPresShell::GetAuthorStyleDisabled() const
2068 return mStyleSet->GetAuthorStyleDisabled();
2071 nsresult
2072 PresShell::SetPreferenceStyleRules(PRBool aForceReflow)
2074 NS_TIME_FUNCTION_MIN(1.0);
2076 if (!mDocument) {
2077 return NS_ERROR_NULL_POINTER;
2080 nsPIDOMWindow *window = mDocument->GetWindow();
2082 // If the document doesn't have a window there's no need to notify
2083 // its presshell about changes to preferences since the document is
2084 // in a state where it doesn't matter any more (see
2085 // DocumentViewerImpl::Close()).
2087 if (!window) {
2088 return NS_ERROR_NULL_POINTER;
2091 NS_PRECONDITION(mPresContext, "presContext cannot be null");
2092 if (mPresContext) {
2093 // first, make sure this is not a chrome shell
2094 if (nsContentUtils::IsInChromeDocshell(mDocument)) {
2095 return NS_OK;
2098 #ifdef DEBUG_attinasi
2099 printf("Setting Preference Style Rules:\n");
2100 #endif
2101 // if here, we need to create rules for the prefs
2102 // - this includes the background-color, the text-color,
2103 // the link color, the visited link color and the link-underlining
2105 // first clear any exising rules
2106 nsresult result = ClearPreferenceStyleRules();
2108 // now the link rules (must come after the color rules, or links will not be correct color!)
2109 // XXX - when there is both an override and agent pref stylesheet this won't matter,
2110 // as the color rules will be overrides and the links rules will be agent
2111 if (NS_SUCCEEDED(result)) {
2112 result = SetPrefLinkRules();
2114 if (NS_SUCCEEDED(result)) {
2115 result = SetPrefFocusRules();
2117 if (NS_SUCCEEDED(result)) {
2118 result = SetPrefNoScriptRule();
2120 if (NS_SUCCEEDED(result)) {
2121 result = SetPrefNoFramesRule();
2123 #ifdef DEBUG_attinasi
2124 printf( "Preference Style Rules set: error=%ld\n", (long)result);
2125 #endif
2127 // Note that this method never needs to force any calculation; the caller
2128 // will recalculate style if needed
2130 return result;
2133 return NS_ERROR_NULL_POINTER;
2136 nsresult PresShell::ClearPreferenceStyleRules(void)
2138 nsresult result = NS_OK;
2139 if (mPrefStyleSheet) {
2140 NS_ASSERTION(mStyleSet, "null styleset entirely unexpected!");
2141 if (mStyleSet) {
2142 // remove the sheet from the styleset:
2143 // - note that we have to check for success by comparing the count before and after...
2144 #ifdef NS_DEBUG
2145 PRInt32 numBefore = mStyleSet->SheetCount(nsStyleSet::eUserSheet);
2146 NS_ASSERTION(numBefore > 0, "no user stylesheets in styleset, but we have one!");
2147 #endif
2148 mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet);
2150 #ifdef DEBUG_attinasi
2151 NS_ASSERTION((numBefore - 1) == mStyleSet->GetNumberOfUserStyleSheets(),
2152 "Pref stylesheet was not removed");
2153 printf("PrefStyleSheet removed\n");
2154 #endif
2155 // clear the sheet pointer: it is strictly historical now
2156 mPrefStyleSheet = nsnull;
2159 return result;
2162 nsresult PresShell::CreatePreferenceStyleSheet(void)
2164 NS_TIME_FUNCTION_MIN(1.0);
2166 NS_ASSERTION(!mPrefStyleSheet, "prefStyleSheet already exists");
2167 nsresult result = NS_NewCSSStyleSheet(getter_AddRefs(mPrefStyleSheet));
2168 if (NS_SUCCEEDED(result)) {
2169 NS_ASSERTION(mPrefStyleSheet, "null but no error");
2170 nsCOMPtr<nsIURI> uri;
2171 result = NS_NewURI(getter_AddRefs(uri), "about:PreferenceStyleSheet", nsnull);
2172 if (NS_SUCCEEDED(result)) {
2173 NS_ASSERTION(uri, "null but no error");
2174 mPrefStyleSheet->SetURIs(uri, uri, uri);
2175 mPrefStyleSheet->SetComplete();
2176 PRUint32 index;
2177 result =
2178 mPrefStyleSheet->InsertRuleInternal(NS_LITERAL_STRING("@namespace url(http://www.w3.org/1999/xhtml);"),
2179 0, &index);
2180 if (NS_SUCCEEDED(result)) {
2181 mStyleSet->AppendStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet);
2186 #ifdef DEBUG_attinasi
2187 printf("CreatePrefStyleSheet completed: error=%ld\n",(long)result);
2188 #endif
2190 if (NS_FAILED(result)) {
2191 mPrefStyleSheet = nsnull;
2194 return result;
2197 // XXX We want these after the @namespace rule. Does order matter
2198 // for these rules, or can we call nsICSSStyleRule::StyleRuleCount()
2199 // and just "append"?
2200 static PRUint32 sInsertPrefSheetRulesAt = 1;
2202 nsresult
2203 PresShell::SetPrefNoScriptRule()
2205 NS_TIME_FUNCTION_MIN(1.0);
2207 nsresult rv = NS_OK;
2209 // also handle the case where print is done from print preview
2210 // see bug #342439 for more details
2211 nsIDocument* doc = mDocument;
2212 if (mPresContext->Type() == nsPresContext::eContext_PrintPreview ||
2213 mPresContext->Type() == nsPresContext::eContext_Print) {
2214 while (doc->GetOriginalDocument()) {
2215 doc = doc->GetOriginalDocument();
2219 PRBool scriptEnabled = doc->IsScriptEnabled();
2220 if (scriptEnabled) {
2221 if (!mPrefStyleSheet) {
2222 rv = CreatePreferenceStyleSheet();
2223 NS_ENSURE_SUCCESS(rv, rv);
2226 PRUint32 index = 0;
2227 mPrefStyleSheet->
2228 InsertRuleInternal(NS_LITERAL_STRING("noscript{display:none!important}"),
2229 sInsertPrefSheetRulesAt, &index);
2232 return rv;
2235 nsresult PresShell::SetPrefNoFramesRule(void)
2237 NS_TIME_FUNCTION_MIN(1.0);
2239 NS_ASSERTION(mPresContext,"null prescontext not allowed");
2240 if (!mPresContext) {
2241 return NS_ERROR_FAILURE;
2244 nsresult rv = NS_OK;
2246 if (!mPrefStyleSheet) {
2247 rv = CreatePreferenceStyleSheet();
2248 NS_ENSURE_SUCCESS(rv, rv);
2251 NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
2253 PRBool allowSubframes = PR_TRUE;
2254 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
2255 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
2256 if (docShell) {
2257 docShell->GetAllowSubframes(&allowSubframes);
2259 if (!allowSubframes) {
2260 PRUint32 index = 0;
2261 rv = mPrefStyleSheet->
2262 InsertRuleInternal(NS_LITERAL_STRING("noframes{display:block}"),
2263 sInsertPrefSheetRulesAt, &index);
2264 NS_ENSURE_SUCCESS(rv, rv);
2265 rv = mPrefStyleSheet->
2266 InsertRuleInternal(NS_LITERAL_STRING("frame, frameset, iframe {display:none!important}"),
2267 sInsertPrefSheetRulesAt, &index);
2269 return rv;
2272 nsresult PresShell::SetPrefLinkRules(void)
2274 NS_TIME_FUNCTION_MIN(1.0);
2276 NS_ASSERTION(mPresContext,"null prescontext not allowed");
2277 if (!mPresContext) {
2278 return NS_ERROR_FAILURE;
2281 nsresult rv = NS_OK;
2283 if (!mPrefStyleSheet) {
2284 rv = CreatePreferenceStyleSheet();
2285 NS_ENSURE_SUCCESS(rv, rv);
2288 NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
2290 // support default link colors:
2291 // this means the link colors need to be overridable,
2292 // which they are if we put them in the agent stylesheet,
2293 // though if using an override sheet this will cause authors grief still
2294 // In the agent stylesheet, they are !important when we are ignoring document colors
2296 nscolor linkColor(mPresContext->DefaultLinkColor());
2297 nscolor activeColor(mPresContext->DefaultActiveLinkColor());
2298 nscolor visitedColor(mPresContext->DefaultVisitedLinkColor());
2300 NS_NAMED_LITERAL_STRING(ruleClose, "}");
2301 PRUint32 index = 0;
2302 nsAutoString strColor;
2304 // insert a rule to color links: '*|*:link {color: #RRGGBB [!important];}'
2305 ColorToString(linkColor, strColor);
2306 rv = mPrefStyleSheet->
2307 InsertRuleInternal(NS_LITERAL_STRING("*|*:link{color:") +
2308 strColor + ruleClose,
2309 sInsertPrefSheetRulesAt, &index);
2310 NS_ENSURE_SUCCESS(rv, rv);
2312 // - visited links: '*|*:visited {color: #RRGGBB [!important];}'
2313 ColorToString(visitedColor, strColor);
2314 rv = mPrefStyleSheet->
2315 InsertRuleInternal(NS_LITERAL_STRING("*|*:visited{color:") +
2316 strColor + ruleClose,
2317 sInsertPrefSheetRulesAt, &index);
2318 NS_ENSURE_SUCCESS(rv, rv);
2320 // - active links: '*|*:-moz-any-link:active {color: #RRGGBB [!important];}'
2321 ColorToString(activeColor, strColor);
2322 rv = mPrefStyleSheet->
2323 InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link:active{color:") +
2324 strColor + ruleClose,
2325 sInsertPrefSheetRulesAt, &index);
2326 NS_ENSURE_SUCCESS(rv, rv);
2328 PRBool underlineLinks =
2329 mPresContext->GetCachedBoolPref(kPresContext_UnderlineLinks);
2331 if (underlineLinks) {
2332 // create a rule to make underlining happen
2333 // '*|*:-moz-any-link {text-decoration:[underline|none];}'
2334 // no need for important, we want these to be overridable
2335 // NOTE: these must go in the agent stylesheet or they cannot be
2336 // overridden by authors
2337 rv = mPrefStyleSheet->
2338 InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link{text-decoration:underline}"),
2339 sInsertPrefSheetRulesAt, &index);
2340 } else {
2341 rv = mPrefStyleSheet->
2342 InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link{text-decoration:none}"),
2343 sInsertPrefSheetRulesAt, &index);
2346 return rv;
2349 nsresult PresShell::SetPrefFocusRules(void)
2351 NS_TIME_FUNCTION_MIN(1.0);
2353 NS_ASSERTION(mPresContext,"null prescontext not allowed");
2354 nsresult result = NS_OK;
2356 if (!mPresContext)
2357 result = NS_ERROR_FAILURE;
2359 if (NS_SUCCEEDED(result) && !mPrefStyleSheet)
2360 result = CreatePreferenceStyleSheet();
2362 if (NS_SUCCEEDED(result)) {
2363 NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
2365 if (mPresContext->GetUseFocusColors()) {
2366 nscolor focusBackground(mPresContext->FocusBackgroundColor());
2367 nscolor focusText(mPresContext->FocusTextColor());
2369 // insert a rule to make focus the preferred color
2370 PRUint32 index = 0;
2371 nsAutoString strRule, strColor;
2373 ///////////////////////////////////////////////////////////////
2374 // - focus: '*:focus
2375 ColorToString(focusText,strColor);
2376 strRule.AppendLiteral("*:focus,*:focus>font {color: ");
2377 strRule.Append(strColor);
2378 strRule.AppendLiteral(" !important; background-color: ");
2379 ColorToString(focusBackground,strColor);
2380 strRule.Append(strColor);
2381 strRule.AppendLiteral(" !important; } ");
2382 // insert the rules
2383 result = mPrefStyleSheet->
2384 InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
2386 PRUint8 focusRingWidth = mPresContext->FocusRingWidth();
2387 PRBool focusRingOnAnything = mPresContext->GetFocusRingOnAnything();
2388 PRUint8 focusRingStyle = mPresContext->GetFocusRingStyle();
2390 if ((NS_SUCCEEDED(result) && focusRingWidth != 1 && focusRingWidth <= 4 ) || focusRingOnAnything) {
2391 PRUint32 index = 0;
2392 nsAutoString strRule;
2393 if (!focusRingOnAnything)
2394 strRule.AppendLiteral("*|*:link:focus, *|*:visited"); // If we only want focus rings on the normal things like links
2395 strRule.AppendLiteral(":focus {outline: "); // For example 3px dotted WindowText (maximum 4)
2396 strRule.AppendInt(focusRingWidth);
2397 if (focusRingStyle == 0) // solid
2398 strRule.AppendLiteral("px solid -moz-mac-focusring !important; -moz-outline-radius: 3px; outline-offset: 1px; } ");
2399 else // dotted
2400 strRule.AppendLiteral("px dotted WindowText !important; } ");
2401 // insert the rules
2402 result = mPrefStyleSheet->
2403 InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
2404 NS_ENSURE_SUCCESS(result, result);
2405 if (focusRingWidth != 1) {
2406 // If the focus ring width is different from the default, fix buttons with rings
2407 strRule.AssignLiteral("button::-moz-focus-inner, input[type=\"reset\"]::-moz-focus-inner,");
2408 strRule.AppendLiteral("input[type=\"button\"]::-moz-focus-inner, ");
2409 strRule.AppendLiteral("input[type=\"submit\"]::-moz-focus-inner { padding: 1px 2px 1px 2px; border: ");
2410 strRule.AppendInt(focusRingWidth);
2411 if (focusRingStyle == 0) // solid
2412 strRule.AppendLiteral("px solid transparent !important; } ");
2413 else
2414 strRule.AppendLiteral("px dotted transparent !important; } ");
2415 result = mPrefStyleSheet->
2416 InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
2417 NS_ENSURE_SUCCESS(result, result);
2419 strRule.AssignLiteral("button:focus::-moz-focus-inner, input[type=\"reset\"]:focus::-moz-focus-inner,");
2420 strRule.AppendLiteral("input[type=\"button\"]:focus::-moz-focus-inner, input[type=\"submit\"]:focus::-moz-focus-inner {");
2421 strRule.AppendLiteral("border-color: ButtonText !important; }");
2422 result = mPrefStyleSheet->
2423 InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
2427 return result;
2430 void
2431 PresShell::AddUserSheet(nsISupports* aSheet)
2433 // Make sure this does what DocumentViewerImpl::CreateStyleSet does wrt
2434 // ordering. We want this new sheet to come after all the existing stylesheet
2435 // service sheets, but before other user sheets; see nsIStyleSheetService.idl
2436 // for the ordering. Just remove and readd all the nsStyleSheetService
2437 // sheets.
2438 nsCOMPtr<nsIStyleSheetService> dummy =
2439 do_GetService(NS_STYLESHEETSERVICE_CONTRACTID);
2441 mStyleSet->BeginUpdate();
2443 nsStyleSheetService *sheetService = nsStyleSheetService::gInstance;
2444 nsCOMArray<nsIStyleSheet> & userSheets = *sheetService->UserStyleSheets();
2445 PRInt32 i;
2446 // Iterate forwards when removing so the searches for RemoveStyleSheet are as
2447 // short as possible.
2448 for (i = 0; i < userSheets.Count(); ++i) {
2449 mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, userSheets[i]);
2452 // Now iterate backwards, so that the order of userSheets will be the same as
2453 // the order of sheets from it in the style set.
2454 for (i = userSheets.Count() - 1; i >= 0; --i) {
2455 mStyleSet->PrependStyleSheet(nsStyleSet::eUserSheet, userSheets[i]);
2458 mStyleSet->EndUpdate();
2460 ReconstructStyleData();
2463 void
2464 PresShell::AddAgentSheet(nsISupports* aSheet)
2466 // Make sure this does what DocumentViewerImpl::CreateStyleSet does
2467 // wrt ordering.
2468 nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet);
2469 if (!sheet) {
2470 return;
2473 mStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet);
2474 ReconstructStyleData();
2477 void
2478 PresShell::RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet)
2480 nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet);
2481 if (!sheet) {
2482 return;
2485 mStyleSet->RemoveStyleSheet(aType, sheet);
2486 ReconstructStyleData();
2489 NS_IMETHODIMP
2490 PresShell::SetDisplaySelection(PRInt16 aToggle)
2492 mSelection->SetDisplaySelection(aToggle);
2493 return NS_OK;
2496 NS_IMETHODIMP
2497 PresShell::GetDisplaySelection(PRInt16 *aToggle)
2499 *aToggle = mSelection->GetDisplaySelection();
2500 return NS_OK;
2503 NS_IMETHODIMP
2504 PresShell::GetSelection(SelectionType aType, nsISelection **aSelection)
2506 if (!aSelection || !mSelection)
2507 return NS_ERROR_NULL_POINTER;
2509 *aSelection = mSelection->GetSelection(aType);
2511 if (!(*aSelection))
2512 return NS_ERROR_INVALID_ARG;
2514 NS_ADDREF(*aSelection);
2516 return NS_OK;
2519 nsISelection*
2520 PresShell::GetCurrentSelection(SelectionType aType)
2522 if (!mSelection)
2523 return nsnull;
2525 return mSelection->GetSelection(aType);
2528 NS_IMETHODIMP
2529 PresShell::ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion, PRBool aIsSynchronous)
2531 if (!mSelection)
2532 return NS_ERROR_NULL_POINTER;
2534 return mSelection->ScrollSelectionIntoView(aType, aRegion, aIsSynchronous);
2537 NS_IMETHODIMP
2538 PresShell::RepaintSelection(SelectionType aType)
2540 if (!mSelection)
2541 return NS_ERROR_NULL_POINTER;
2543 return mSelection->RepaintSelection(aType);
2546 // Make shell be a document observer
2547 void
2548 PresShell::BeginObservingDocument()
2550 if (mDocument && !mIsDestroying) {
2551 mDocument->AddObserver(this);
2552 if (mIsDocumentGone) {
2553 NS_WARNING("Adding a presshell that was disconnected from the document "
2554 "as a document observer? Sounds wrong...");
2555 mIsDocumentGone = PR_FALSE;
2560 // Make shell stop being a document observer
2561 void
2562 PresShell::EndObservingDocument()
2564 // XXXbz do we need to tell the frame constructor that the document
2565 // is gone, perhaps? Except for printing it's NOT gone, sometimes.
2566 mIsDocumentGone = PR_TRUE;
2567 if (mDocument) {
2568 mDocument->RemoveObserver(this);
2572 #ifdef DEBUG_kipp
2573 char* nsPresShell_ReflowStackPointerTop;
2574 #endif
2576 nsresult
2577 PresShell::InitialReflow(nscoord aWidth, nscoord aHeight)
2579 if (mIsDestroying) {
2580 return NS_OK;
2583 if (!mDocument) {
2584 // Nothing to do
2585 return NS_OK;
2588 NS_TIME_FUNCTION_WITH_DOCURL;
2590 NS_ASSERTION(!mDidInitialReflow, "Why are we being called?");
2592 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
2593 mDidInitialReflow = PR_TRUE;
2595 #ifdef NS_DEBUG
2596 if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
2597 if (mDocument) {
2598 nsIURI *uri = mDocument->GetDocumentURI();
2599 if (uri) {
2600 nsCAutoString url;
2601 uri->GetSpec(url);
2602 printf("*** PresShell::InitialReflow (this=%p, url='%s')\n", (void*)this, url.get());
2606 #endif
2608 if (mCaret)
2609 mCaret->EraseCaret();
2611 // XXX Do a full invalidate at the beginning so that invalidates along
2612 // the way don't have region accumulation issues?
2614 mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
2616 // Get the root frame from the frame manager
2617 // XXXbz it would be nice to move this somewhere else... like frame manager
2618 // Init(), say. But we need to make sure our views are all set up by the
2619 // time we do this!
2620 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
2621 NS_ASSERTION(!rootFrame, "How did that happen, exactly?");
2622 if (!rootFrame) {
2623 nsAutoScriptBlocker scriptBlocker;
2624 mFrameConstructor->BeginUpdate();
2625 mFrameConstructor->ConstructRootFrame(&rootFrame);
2626 FrameManager()->SetRootFrame(rootFrame);
2627 mFrameConstructor->EndUpdate();
2630 NS_ENSURE_STATE(!mHaveShutDown);
2632 if (!rootFrame) {
2633 return NS_ERROR_OUT_OF_MEMORY;
2636 Element *root = mDocument->GetRootElement();
2638 if (root) {
2640 nsAutoCauseReflowNotifier reflowNotifier(this);
2641 mFrameConstructor->BeginUpdate();
2643 // Have the style sheet processor construct frame for the root
2644 // content object down
2645 mFrameConstructor->ContentInserted(nsnull, root, nsnull, PR_FALSE);
2646 VERIFY_STYLE_TREE;
2648 // Something in mFrameConstructor->ContentInserted may have caused
2649 // Destroy() to get called, bug 337586.
2650 NS_ENSURE_STATE(!mHaveShutDown);
2652 mFrameConstructor->EndUpdate();
2655 // nsAutoScriptBlocker going out of scope may have killed us too
2656 NS_ENSURE_STATE(!mHaveShutDown);
2658 // Run the XBL binding constructors for any new frames we've constructed
2659 mDocument->BindingManager()->ProcessAttachedQueue();
2661 NS_TIME_FUNCTION_MARK("XBL binding constructors fired");
2663 // Constructors may have killed us too
2664 NS_ENSURE_STATE(!mHaveShutDown);
2666 // Now flush out pending restyles before we actually reflow, in
2667 // case XBL constructors changed styles somewhere.
2669 nsAutoScriptBlocker scriptBlocker;
2670 mFrameConstructor->CreateNeededFrames();
2671 mFrameConstructor->ProcessPendingRestyles();
2674 // And that might have run _more_ XBL constructors
2675 NS_ENSURE_STATE(!mHaveShutDown);
2678 NS_ASSERTION(rootFrame, "How did that happen?");
2680 // Note: Because the frame just got created, it has the NS_FRAME_IS_DIRTY
2681 // bit set. Unset it so that FrameNeedsReflow() will work right.
2682 NS_ASSERTION(!mDirtyRoots.Contains(rootFrame),
2683 "Why is the root in mDirtyRoots already?");
2685 rootFrame->RemoveStateBits(NS_FRAME_IS_DIRTY |
2686 NS_FRAME_HAS_DIRTY_CHILDREN);
2687 FrameNeedsReflow(rootFrame, eResize, NS_FRAME_IS_DIRTY);
2689 NS_ASSERTION(mDirtyRoots.Contains(rootFrame),
2690 "Should be in mDirtyRoots now");
2691 NS_ASSERTION(mReflowScheduled, "Why no reflow scheduled?");
2693 // Restore our root scroll position now if we're getting here after EndLoad
2694 // got called, since this is our one chance to do it. Note that we need not
2695 // have reflowed for this to work; when the scrollframe is finally reflowed
2696 // it'll puick up the position we store in it here.
2697 if (!mDocumentLoading) {
2698 RestoreRootScrollPosition();
2701 // For printing, we just immediately unsuppress.
2702 if (!mPresContext->IsPaginated()) {
2703 // Kick off a one-shot timer based off our pref value. When this timer
2704 // fires, if painting is still locked down, then we will go ahead and
2705 // trigger a full invalidate and allow painting to proceed normally.
2706 mPaintingSuppressed = PR_TRUE;
2707 mPaintSuppressionTimer = do_CreateInstance("@mozilla.org/timer;1");
2708 if (!mPaintSuppressionTimer)
2709 // Uh-oh. We must be out of memory. No point in keeping painting locked down.
2710 mPaintingSuppressed = PR_FALSE;
2711 else {
2712 // Initialize the timer.
2714 // Default to PAINTLOCK_EVENT_DELAY if we can't get the pref value.
2715 PRInt32 delay =
2716 nsContentUtils::GetIntPref("nglayout.initialpaint.delay",
2717 PAINTLOCK_EVENT_DELAY);
2719 mPaintSuppressionTimer->InitWithFuncCallback(sPaintSuppressionCallback,
2720 this, delay,
2721 nsITimer::TYPE_ONE_SHOT);
2725 return NS_OK; //XXX this needs to be real. MMP
2728 void
2729 PresShell::sPaintSuppressionCallback(nsITimer *aTimer, void* aPresShell)
2731 nsRefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
2732 if (self)
2733 self->UnsuppressPainting();
2736 void
2737 PresShell::AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell)
2739 static_cast<PresShell*>(aPresShell)->FireResizeEvent();
2742 nsresult
2743 PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight)
2745 NS_PRECONDITION(!mIsReflowing, "Shouldn't be in reflow here!");
2746 NS_PRECONDITION(aWidth != NS_UNCONSTRAINEDSIZE,
2747 "shouldn't use unconstrained widths anymore");
2749 // If we don't have a root frame yet, that means we haven't had our initial
2750 // reflow... If that's the case, and aWidth or aHeight is unconstrained,
2751 // ignore them altogether.
2752 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
2754 if (!rootFrame && aHeight == NS_UNCONSTRAINEDSIZE) {
2755 // We can't do the work needed for SizeToContent without a root
2756 // frame, and we want to return before setting the visible area.
2757 return NS_ERROR_NOT_AVAILABLE;
2760 mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
2762 // There isn't anything useful we can do if the initial reflow hasn't happened
2763 if (!rootFrame)
2764 return NS_OK;
2766 NS_ASSERTION(mViewManager, "Must have view manager");
2767 nsCOMPtr<nsIViewManager> viewManagerDeathGrip = mViewManager;
2768 // Take this ref after viewManager so it'll make sure to go away first
2769 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
2770 if (!GetPresContext()->SupressingResizeReflow())
2772 nsIViewManager::UpdateViewBatch batch(mViewManager);
2774 // Have to make sure that the content notifications are flushed before we
2775 // start messing with the frame model; otherwise we can get content doubling.
2776 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
2778 // Make sure style is up to date
2780 nsAutoScriptBlocker scriptBlocker;
2781 mFrameConstructor->CreateNeededFrames();
2782 mFrameConstructor->ProcessPendingRestyles();
2785 if (!mIsDestroying) {
2786 // XXX Do a full invalidate at the beginning so that invalidates along
2787 // the way don't have region accumulation issues?
2790 nsAutoCauseReflowNotifier crNotifier(this);
2791 WillDoReflow();
2793 // Kick off a top-down reflow
2794 AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
2796 mDirtyRoots.RemoveElement(rootFrame);
2797 DoReflow(rootFrame, PR_TRUE);
2800 DidDoReflow(PR_TRUE);
2803 batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
2806 if (aHeight == NS_UNCONSTRAINEDSIZE) {
2807 mPresContext->SetVisibleArea(
2808 nsRect(0, 0, aWidth, rootFrame->GetRect().height));
2811 if (!mIsDestroying && !mResizeEvent.IsPending() &&
2812 !mAsyncResizeTimerIsActive) {
2813 if (mInResize) {
2814 if (!mAsyncResizeEventTimer) {
2815 mAsyncResizeEventTimer = do_CreateInstance("@mozilla.org/timer;1");
2817 if (mAsyncResizeEventTimer) {
2818 mAsyncResizeTimerIsActive = PR_TRUE;
2819 mAsyncResizeEventTimer->InitWithFuncCallback(AsyncResizeEventCallback,
2820 this, 15,
2821 nsITimer::TYPE_ONE_SHOT);
2823 } else {
2824 nsRefPtr<nsRunnableMethod<PresShell> > resizeEvent =
2825 NS_NewRunnableMethod(this, &PresShell::FireResizeEvent);
2826 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(resizeEvent))) {
2827 mResizeEvent = resizeEvent;
2832 return NS_OK; //XXX this needs to be real. MMP
2835 void
2836 PresShell::FireResizeEvent()
2838 if (mAsyncResizeTimerIsActive) {
2839 mAsyncResizeTimerIsActive = PR_FALSE;
2840 mAsyncResizeEventTimer->Cancel();
2842 mResizeEvent.Revoke();
2844 if (mIsDocumentGone)
2845 return;
2847 //Send resize event from here.
2848 nsEvent event(PR_TRUE, NS_RESIZE_EVENT);
2849 nsEventStatus status = nsEventStatus_eIgnore;
2851 nsPIDOMWindow *window = mDocument->GetWindow();
2852 if (window) {
2853 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
2854 mInResize = PR_TRUE;
2855 nsEventDispatcher::Dispatch(window, mPresContext, &event, nsnull, &status);
2856 mInResize = PR_FALSE;
2860 void
2861 PresShell::SetIgnoreFrameDestruction(PRBool aIgnore)
2863 mIgnoreFrameDestruction = aIgnore;
2866 void
2867 PresShell::NotifyDestroyingFrame(nsIFrame* aFrame)
2869 NS_TIME_FUNCTION_MIN(1.0);
2871 mPresContext->ForgetUpdatePluginGeometryFrame(aFrame);
2873 if (!mIgnoreFrameDestruction) {
2874 mPresContext->StopImagesFor(aFrame);
2876 mFrameConstructor->NotifyDestroyingFrame(aFrame);
2878 for (PRInt32 idx = mDirtyRoots.Length(); idx; ) {
2879 --idx;
2880 if (mDirtyRoots[idx] == aFrame) {
2881 mDirtyRoots.RemoveElementAt(idx);
2885 // Notify the frame manager
2886 FrameManager()->NotifyDestroyingFrame(aFrame);
2888 // Remove frame properties
2889 mPresContext->NotifyDestroyingFrame(aFrame);
2891 if (aFrame == mCurrentEventFrame) {
2892 mCurrentEventContent = aFrame->GetContent();
2893 mCurrentEventFrame = nsnull;
2896 #ifdef NS_DEBUG
2897 if (aFrame == mDrawEventTargetFrame) {
2898 mDrawEventTargetFrame = nsnull;
2900 #endif
2902 for (unsigned int i=0; i < mCurrentEventFrameStack.Length(); i++) {
2903 if (aFrame == mCurrentEventFrameStack.ElementAt(i)) {
2904 //One of our stack frames was deleted. Get its content so that when we
2905 //pop it we can still get its new frame from its content
2906 nsIContent *currentEventContent = aFrame->GetContent();
2907 mCurrentEventContentStack.ReplaceObjectAt(currentEventContent, i);
2908 mCurrentEventFrameStack[i] = nsnull;
2912 mFramesToDirty.RemoveEntry(aFrame);
2916 already_AddRefed<nsCaret> PresShell::GetCaret() const
2918 nsCaret* caret = mCaret;
2919 NS_IF_ADDREF(caret);
2920 return caret;
2923 void PresShell::MaybeInvalidateCaretPosition()
2925 if (mCaret) {
2926 mCaret->InvalidateOutsideCaret();
2930 void PresShell::SetCaret(nsCaret *aNewCaret)
2932 mCaret = aNewCaret;
2935 void PresShell::RestoreCaret()
2937 mCaret = mOriginalCaret;
2940 NS_IMETHODIMP PresShell::SetCaretEnabled(PRBool aInEnable)
2942 PRBool oldEnabled = mCaretEnabled;
2944 mCaretEnabled = aInEnable;
2946 if (mCaret && (mCaretEnabled != oldEnabled))
2948 /* Don't change the caret's selection here! This was an evil side-effect of SetCaretEnabled()
2949 nsCOMPtr<nsIDOMSelection> domSel;
2950 if (NS_SUCCEEDED(GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel))) && domSel)
2951 mCaret->SetCaretDOMSelection(domSel);
2953 mCaret->SetCaretVisible(mCaretEnabled);
2956 return NS_OK;
2959 NS_IMETHODIMP PresShell::SetCaretReadOnly(PRBool aReadOnly)
2961 if (mCaret)
2962 mCaret->SetCaretReadOnly(aReadOnly);
2963 return NS_OK;
2966 NS_IMETHODIMP PresShell::GetCaretEnabled(PRBool *aOutEnabled)
2968 NS_ENSURE_ARG_POINTER(aOutEnabled);
2969 *aOutEnabled = mCaretEnabled;
2970 return NS_OK;
2973 NS_IMETHODIMP PresShell::SetCaretVisibilityDuringSelection(PRBool aVisibility)
2975 if (mCaret)
2976 mCaret->SetVisibilityDuringSelection(aVisibility);
2977 return NS_OK;
2980 NS_IMETHODIMP PresShell::GetCaretVisible(PRBool *aOutIsVisible)
2982 *aOutIsVisible = PR_FALSE;
2983 if (mCaret) {
2984 nsresult rv = mCaret->GetCaretVisible(aOutIsVisible);
2985 NS_ENSURE_SUCCESS(rv,rv);
2987 return NS_OK;
2990 NS_IMETHODIMP PresShell::SetSelectionFlags(PRInt16 aInEnable)
2992 mSelectionFlags = aInEnable;
2993 return NS_OK;
2996 NS_IMETHODIMP PresShell::GetSelectionFlags(PRInt16 *aOutEnable)
2998 if (!aOutEnable)
2999 return NS_ERROR_INVALID_ARG;
3000 *aOutEnable = mSelectionFlags;
3001 return NS_OK;
3004 //implementation of nsISelectionController
3006 NS_IMETHODIMP
3007 PresShell::CharacterMove(PRBool aForward, PRBool aExtend)
3009 return mSelection->CharacterMove(aForward, aExtend);
3012 NS_IMETHODIMP
3013 PresShell::CharacterExtendForDelete()
3015 return mSelection->CharacterExtendForDelete();
3018 NS_IMETHODIMP
3019 PresShell::CharacterExtendForBackspace()
3021 return mSelection->CharacterExtendForBackspace();
3024 NS_IMETHODIMP
3025 PresShell::WordMove(PRBool aForward, PRBool aExtend)
3027 return mSelection->WordMove(aForward, aExtend);
3030 NS_IMETHODIMP
3031 PresShell::WordExtendForDelete(PRBool aForward)
3033 return mSelection->WordExtendForDelete(aForward);
3036 NS_IMETHODIMP
3037 PresShell::LineMove(PRBool aForward, PRBool aExtend)
3039 nsresult result = mSelection->LineMove(aForward, aExtend);
3040 // if we can't go down/up any more we must then move caret completely to
3041 // end/beginning respectively.
3042 if (NS_FAILED(result))
3043 result = CompleteMove(aForward,aExtend);
3044 return result;
3047 NS_IMETHODIMP
3048 PresShell::IntraLineMove(PRBool aForward, PRBool aExtend)
3050 return mSelection->IntraLineMove(aForward, aExtend);
3055 NS_IMETHODIMP
3056 PresShell::PageMove(PRBool aForward, PRBool aExtend)
3058 nsIScrollableFrame *scrollableFrame =
3059 GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
3060 if (!scrollableFrame)
3061 return NS_OK;
3063 mSelection->CommonPageMove(aForward, aExtend, scrollableFrame);
3064 // After ScrollSelectionIntoView(), the pending notifications might be
3065 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
3066 return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
3071 NS_IMETHODIMP
3072 PresShell::ScrollPage(PRBool aForward)
3074 nsIScrollableFrame* scrollFrame =
3075 GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
3076 if (scrollFrame) {
3077 scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
3078 nsIScrollableFrame::PAGES,
3079 nsIScrollableFrame::SMOOTH);
3081 return NS_OK;
3084 NS_IMETHODIMP
3085 PresShell::ScrollLine(PRBool aForward)
3087 nsIScrollableFrame* scrollFrame =
3088 GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
3089 if (scrollFrame) {
3090 PRInt32 lineCount = 1;
3091 #ifdef MOZ_WIDGET_COCOA
3092 // Emulate the Mac IE behavior of scrolling a minimum of 2 lines
3093 // rather than 1. This vastly improves scrolling speed.
3094 lineCount = 2;
3095 #endif
3096 scrollFrame->ScrollBy(nsIntPoint(0, aForward ? lineCount : -lineCount),
3097 nsIScrollableFrame::LINES,
3098 nsIScrollableFrame::SMOOTH);
3100 //NEW FOR LINES
3101 // force the update to happen now, otherwise multiple scrolls can
3102 // occur before the update is processed. (bug #7354)
3104 // I'd use Composite here, but it doesn't always work.
3105 // vm->Composite();
3106 nsIViewManager* viewManager = GetViewManager();
3107 if (viewManager) {
3108 viewManager->ForceUpdate();
3111 return NS_OK;
3114 NS_IMETHODIMP
3115 PresShell::ScrollHorizontal(PRBool aLeft)
3117 nsIScrollableFrame* scrollFrame =
3118 GetFrameToScrollAsScrollable(nsIPresShell::eHorizontal);
3119 if (scrollFrame) {
3120 scrollFrame->ScrollBy(nsIntPoint(aLeft ? -1 : 1, 0),
3121 nsIScrollableFrame::LINES,
3122 nsIScrollableFrame::SMOOTH);
3123 //NEW FOR LINES
3124 // force the update to happen now, otherwise multiple scrolls can
3125 // occur before the update is processed. (bug #7354)
3127 // I'd use Composite here, but it doesn't always work.
3128 // vm->Composite();
3129 nsIViewManager* viewManager = GetViewManager();
3130 if (viewManager) {
3131 viewManager->ForceUpdate();
3134 return NS_OK;
3137 NS_IMETHODIMP
3138 PresShell::CompleteScroll(PRBool aForward)
3140 nsIScrollableFrame* scrollFrame =
3141 GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
3142 if (scrollFrame) {
3143 scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
3144 nsIScrollableFrame::WHOLE,
3145 nsIScrollableFrame::INSTANT);
3147 return NS_OK;
3150 NS_IMETHODIMP
3151 PresShell::CompleteMove(PRBool aForward, PRBool aExtend)
3153 // Beware! This may flush notifications via synchronous
3154 // ScrollSelectionIntoView.
3155 nsIContent* limiter = mSelection->GetAncestorLimiter();
3156 nsIFrame* frame = limiter ? limiter->GetPrimaryFrame()
3157 : FrameConstructor()->GetRootElementFrame();
3158 if (!frame)
3159 return NS_ERROR_FAILURE;
3160 nsPeekOffsetStruct pos = frame->GetExtremeCaretPosition(!aForward);
3161 mSelection->HandleClick(pos.mResultContent, pos.mContentOffset,
3162 pos.mContentOffset, aExtend, PR_FALSE, aForward);
3163 if (limiter) {
3164 // HandleClick resets ancestorLimiter, so set it again.
3165 mSelection->SetAncestorLimiter(limiter);
3168 // After ScrollSelectionIntoView(), the pending notifications might be
3169 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
3170 return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
3171 nsISelectionController::SELECTION_FOCUS_REGION,
3172 PR_TRUE);
3175 NS_IMETHODIMP
3176 PresShell::SelectAll()
3178 return mSelection->SelectAll();
3181 NS_IMETHODIMP
3182 PresShell::CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, PRBool *_retval)
3184 if (!node || startOffset>EndOffset || !_retval || startOffset<0 || EndOffset<0)
3185 return NS_ERROR_INVALID_ARG;
3186 *_retval = PR_FALSE; //initialize return parameter
3187 nsCOMPtr<nsIContent> content(do_QueryInterface(node));
3188 if (!content)
3189 return NS_ERROR_FAILURE;
3190 nsIFrame *frame = content->GetPrimaryFrame();
3191 if (!frame) //no frame to look at so it must not be visible
3192 return NS_OK;
3193 //start process now to go through all frames to find startOffset. then check chars after that to see
3194 //if anything until EndOffset is visible.
3195 PRBool finished = PR_FALSE;
3196 frame->CheckVisibility(mPresContext,startOffset,EndOffset,PR_TRUE,&finished, _retval);
3197 return NS_OK;//dont worry about other return val
3200 //end implementations nsISelectionController
3203 void
3204 PresShell::StyleChangeReflow()
3206 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
3207 // At the moment at least, we don't have a root frame before the initial
3208 // reflow; it's safe to just ignore the request in that case
3209 if (!rootFrame)
3210 return;
3212 FrameNeedsReflow(rootFrame, eStyleChange, NS_FRAME_IS_DIRTY);
3215 nsIFrame*
3216 nsIPresShell::GetRootFrameExternal() const
3218 return FrameManager()->GetRootFrame();
3221 nsIFrame*
3222 nsIPresShell::GetRootScrollFrame() const
3224 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
3225 // Ensure root frame is a viewport frame
3226 if (!rootFrame || nsGkAtoms::viewportFrame != rootFrame->GetType())
3227 return nsnull;
3228 nsIFrame* theFrame = rootFrame->GetFirstChild(nsnull);
3229 if (!theFrame || nsGkAtoms::scrollFrame != theFrame->GetType())
3230 return nsnull;
3231 return theFrame;
3234 nsIScrollableFrame*
3235 nsIPresShell::GetRootScrollFrameAsScrollable() const
3237 nsIFrame* frame = GetRootScrollFrame();
3238 if (!frame)
3239 return nsnull;
3240 nsIScrollableFrame* scrollableFrame = do_QueryFrame(frame);
3241 NS_ASSERTION(scrollableFrame,
3242 "All scroll frames must implement nsIScrollableFrame");
3243 return scrollableFrame;
3246 nsIScrollableFrame*
3247 nsIPresShell::GetRootScrollFrameAsScrollableExternal() const
3249 return GetRootScrollFrameAsScrollable();
3252 nsIPageSequenceFrame*
3253 PresShell::GetPageSequenceFrame() const
3255 nsIFrame* frame = mFrameConstructor->GetPageSequenceFrame();
3256 return do_QueryFrame(frame);
3259 nsIFrame*
3260 PresShell::GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt)
3262 return nsLayoutUtils::GetFrameForPoint(aFrame, aPt);
3265 void
3266 PresShell::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
3268 #ifdef DEBUG
3269 mUpdateCount++;
3270 #endif
3271 mFrameConstructor->BeginUpdate();
3273 if (aUpdateType & UPDATE_STYLE)
3274 mStyleSet->BeginUpdate();
3277 void
3278 PresShell::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
3280 #ifdef DEBUG
3281 NS_PRECONDITION(0 != mUpdateCount, "too many EndUpdate's");
3282 --mUpdateCount;
3283 #endif
3285 if (aUpdateType & UPDATE_STYLE) {
3286 mStyleSet->EndUpdate();
3287 if (mStylesHaveChanged)
3288 ReconstructStyleData();
3291 mFrameConstructor->EndUpdate();
3294 void
3295 PresShell::RestoreRootScrollPosition()
3297 // Restore frame state for the root scroll frame
3298 nsCOMPtr<nsILayoutHistoryState> historyState =
3299 mDocument->GetLayoutHistoryState();
3300 // Make sure we don't reenter reflow via the sync paint that happens while
3301 // we're scrolling to our restored position. Entering reflow for the
3302 // scrollable frame will cause it to reenter ScrollToRestoredPosition(), and
3303 // it'll get all confused.
3304 nsAutoScriptBlocker scriptBlocker;
3305 ++mChangeNestCount;
3307 if (historyState) {
3308 nsIFrame* scrollFrame = GetRootScrollFrame();
3309 if (scrollFrame) {
3310 nsIScrollableFrame* scrollableFrame = do_QueryFrame(scrollFrame);
3311 if (scrollableFrame) {
3312 FrameManager()->RestoreFrameStateFor(scrollFrame, historyState,
3313 nsIStatefulFrame::eDocumentScrollState);
3314 scrollableFrame->ScrollToRestoredPosition();
3319 --mChangeNestCount;
3322 void
3323 PresShell::BeginLoad(nsIDocument *aDocument)
3325 mDocumentLoading = PR_TRUE;
3328 void
3329 PresShell::EndLoad(nsIDocument *aDocument)
3331 NS_PRECONDITION(aDocument == mDocument, "Wrong document");
3333 RestoreRootScrollPosition();
3335 mDocumentLoading = PR_FALSE;
3338 #ifdef DEBUG
3339 void
3340 PresShell::VerifyHasDirtyRootAncestor(nsIFrame* aFrame)
3342 // XXXbz due to bug 372769, can't actually assert anything here...
3343 return;
3345 // XXXbz shouldn't need this part; remove it once FrameNeedsReflow
3346 // handles the root frame correctly.
3347 if (!aFrame->GetParent()) {
3348 return;
3351 // Make sure that there is a reflow root ancestor of |aFrame| that's
3352 // in mDirtyRoots already.
3353 while (aFrame && (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)) {
3354 if (((aFrame->GetStateBits() & NS_FRAME_REFLOW_ROOT) ||
3355 !aFrame->GetParent()) &&
3356 mDirtyRoots.Contains(aFrame)) {
3357 return;
3360 aFrame = aFrame->GetParent();
3362 NS_NOTREACHED("Frame has dirty bits set but isn't scheduled to be "
3363 "reflowed?");
3365 #endif
3367 void
3368 PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
3369 nsFrameState aBitToAdd)
3371 #ifdef NS_FUNCTION_TIMER
3372 NS_TIME_FUNCTION_DECLARE_DOCURL;
3373 nsCAutoString frameType__("N/A");
3374 nsIAtom *atomType__ = aFrame ? aFrame->GetType() : nsnull;
3375 if (atomType__) atomType__->ToUTF8String(frameType__);
3376 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (document: %s, frame type: %s)", MOZ_FUNCTION_NAME,
3377 __LINE__, docURL__.get(), frameType__.get());
3378 #endif
3380 NS_PRECONDITION(aBitToAdd == NS_FRAME_IS_DIRTY ||
3381 aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN,
3382 "Unexpected bits being added");
3383 NS_PRECONDITION(aIntrinsicDirty != eStyleChange ||
3384 aBitToAdd == NS_FRAME_IS_DIRTY,
3385 "bits don't correspond to style change reason");
3387 NS_ASSERTION(!mIsReflowing, "can't mark frame dirty during reflow");
3389 // If we've not yet done the initial reflow, then don't bother
3390 // enqueuing a reflow command yet.
3391 if (! mDidInitialReflow)
3392 return;
3394 // If we're already destroying, don't bother with this either.
3395 if (mIsDestroying)
3396 return;
3398 #ifdef DEBUG
3399 //printf("gShellCounter: %d\n", gShellCounter++);
3400 if (mInVerifyReflow)
3401 return;
3403 if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
3404 printf("\nPresShell@%p: frame %p needs reflow\n", (void*)this, (void*)aFrame);
3405 if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) {
3406 printf("Current content model:\n");
3407 Element *rootElement = mDocument->GetRootElement();
3408 if (rootElement) {
3409 rootElement->List(stdout, 0);
3413 #endif
3415 nsAutoTArray<nsIFrame*, 4> subtrees;
3416 subtrees.AppendElement(aFrame);
3418 do {
3419 nsIFrame *subtreeRoot = subtrees.ElementAt(subtrees.Length() - 1);
3420 subtrees.RemoveElementAt(subtrees.Length() - 1);
3422 // Grab |wasDirty| now so we can go ahead and update the bits on
3423 // subtreeRoot.
3424 PRBool wasDirty = NS_SUBTREE_DIRTY(subtreeRoot);
3425 subtreeRoot->AddStateBits(aBitToAdd);
3427 // Now if subtreeRoot is a reflow root we can cut off this reflow at it if
3428 // the bit being added is NS_FRAME_HAS_DIRTY_CHILDREN.
3429 PRBool targetFrameDirty = (aBitToAdd == NS_FRAME_IS_DIRTY);
3431 #define FRAME_IS_REFLOW_ROOT(_f) \
3432 ((_f->GetStateBits() & NS_FRAME_REFLOW_ROOT) && \
3433 (_f != subtreeRoot || !targetFrameDirty))
3436 // Mark the intrinsic widths as dirty on the frame, all of its ancestors,
3437 // and all of its descendants, if needed:
3439 if (aIntrinsicDirty != eResize) {
3440 // Mark argument and all ancestors dirty. (Unless we hit a reflow
3441 // root that should contain the reflow. That root could be
3442 // subtreeRoot itself if it's not dirty, or it could be some
3443 // ancestor of subtreeRoot.)
3444 for (nsIFrame *a = subtreeRoot;
3445 a && !FRAME_IS_REFLOW_ROOT(a);
3446 a = a->GetParent())
3447 a->MarkIntrinsicWidthsDirty();
3450 if (aIntrinsicDirty == eStyleChange) {
3451 // Mark all descendants dirty (using an nsTArray stack rather than
3452 // recursion).
3453 nsAutoTArray<nsIFrame*, 32> stack;
3454 stack.AppendElement(subtreeRoot);
3456 do {
3457 nsIFrame *f = stack.ElementAt(stack.Length() - 1);
3458 stack.RemoveElementAt(stack.Length() - 1);
3460 if (f->GetType() == nsGkAtoms::placeholderFrame) {
3461 nsIFrame *oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
3462 if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
3463 // We have another distinct subtree we need to mark.
3464 subtrees.AppendElement(oof);
3468 PRInt32 childListIndex = 0;
3469 nsIAtom *childListName;
3470 do {
3471 childListName = f->GetAdditionalChildListName(childListIndex++);
3472 for (nsIFrame *kid = f->GetFirstChild(childListName); kid;
3473 kid = kid->GetNextSibling()) {
3474 kid->MarkIntrinsicWidthsDirty();
3475 stack.AppendElement(kid);
3477 } while (childListName);
3478 } while (stack.Length() != 0);
3481 // Set NS_FRAME_HAS_DIRTY_CHILDREN bits (via nsIFrame::ChildIsDirty)
3482 // up the tree until we reach either a frame that's already dirty or
3483 // a reflow root.
3484 nsIFrame *f = subtreeRoot;
3485 for (;;) {
3486 if (FRAME_IS_REFLOW_ROOT(f) || !f->GetParent()) {
3487 // we've hit a reflow root or the root frame
3488 if (!wasDirty) {
3489 mDirtyRoots.AppendElement(f);
3491 #ifdef DEBUG
3492 else {
3493 VerifyHasDirtyRootAncestor(f);
3495 #endif
3497 break;
3500 nsIFrame *child = f;
3501 f = f->GetParent();
3502 wasDirty = NS_SUBTREE_DIRTY(f);
3503 f->ChildIsDirty(child);
3504 NS_ASSERTION(f->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN,
3505 "ChildIsDirty didn't do its job");
3506 if (wasDirty) {
3507 // This frame was already marked dirty.
3508 #ifdef DEBUG
3509 VerifyHasDirtyRootAncestor(f);
3510 #endif
3511 break;
3514 } while (subtrees.Length() != 0);
3516 MaybeScheduleReflow();
3519 void
3520 PresShell::FrameNeedsToContinueReflow(nsIFrame *aFrame)
3522 NS_ASSERTION(mIsReflowing, "Must be in reflow when marking path dirty.");
3523 NS_PRECONDITION(mCurrentReflowRoot, "Must have a current reflow root here");
3524 NS_ASSERTION(aFrame == mCurrentReflowRoot ||
3525 nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot, aFrame),
3526 "Frame passed in is not the descendant of mCurrentReflowRoot");
3527 NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW,
3528 "Frame passed in not in reflow?");
3530 mFramesToDirty.PutEntry(aFrame);
3533 nsIScrollableFrame*
3534 nsIPresShell::GetFrameToScrollAsScrollable(
3535 nsIPresShell::ScrollDirection aDirection)
3537 nsIScrollableFrame* scrollFrame = nsnull;
3539 nsCOMPtr<nsIContent> focusedContent;
3540 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3541 if (fm && mDocument) {
3542 nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(mDocument->GetWindow());
3544 nsCOMPtr<nsIDOMElement> focusedElement;
3545 fm->GetFocusedElementForWindow(window, PR_FALSE, nsnull, getter_AddRefs(focusedElement));
3546 focusedContent = do_QueryInterface(focusedElement);
3548 if (!focusedContent && mSelection) {
3549 nsISelection* domSelection = mSelection->
3550 GetSelection(nsISelectionController::SELECTION_NORMAL);
3551 if (domSelection) {
3552 nsCOMPtr<nsIDOMNode> focusedNode;
3553 domSelection->GetFocusNode(getter_AddRefs(focusedNode));
3554 focusedContent = do_QueryInterface(focusedNode);
3557 if (focusedContent) {
3558 nsIFrame* startFrame = focusedContent->GetPrimaryFrame();
3559 if (startFrame) {
3560 scrollFrame = startFrame->GetScrollTargetFrame();
3561 if (scrollFrame) {
3562 startFrame = scrollFrame->GetScrolledFrame();
3564 if (aDirection == nsIPresShell::eEither) {
3565 scrollFrame =
3566 nsLayoutUtils::GetNearestScrollableFrame(startFrame);
3567 } else {
3568 scrollFrame =
3569 nsLayoutUtils::GetNearestScrollableFrameForDirection(startFrame,
3570 aDirection == eVertical ? nsLayoutUtils::eVertical :
3571 nsLayoutUtils::eHorizontal);
3575 if (!scrollFrame) {
3576 scrollFrame = GetRootScrollFrameAsScrollable();
3578 return scrollFrame;
3581 void
3582 PresShell::CancelAllPendingReflows()
3584 mDirtyRoots.Clear();
3586 if (mReflowScheduled) {
3587 GetPresContext()->RefreshDriver()->RemoveLayoutFlushObserver(this);
3588 mReflowScheduled = PR_FALSE;
3591 ASSERT_REFLOW_SCHEDULED_STATE();
3594 #ifdef ACCESSIBILITY
3595 void nsIPresShell::InvalidateAccessibleSubtree(nsIContent *aContent)
3597 if (gIsAccessibilityActive) {
3598 nsCOMPtr<nsIAccessibilityService> accService =
3599 do_GetService("@mozilla.org/accessibilityService;1");
3600 if (accService) {
3601 accService->InvalidateSubtreeFor(this, aContent,
3602 nsIAccessibilityService::FRAME_SIGNIFICANT_CHANGE);
3606 #endif
3608 nsresult
3609 PresShell::RecreateFramesFor(nsIContent* aContent)
3611 NS_TIME_FUNCTION_MIN(1.0);
3613 NS_ENSURE_TRUE(mPresContext, NS_ERROR_FAILURE);
3614 if (!mDidInitialReflow) {
3615 // Nothing to do here. In fact, if we proceed and aContent is the
3616 // root we will crash.
3617 return NS_OK;
3620 // Don't call RecreateFramesForContent since that is not exported and we want
3621 // to keep the number of entrypoints down.
3623 NS_ASSERTION(mViewManager, "Should have view manager");
3624 nsIViewManager::UpdateViewBatch batch(mViewManager);
3626 // Have to make sure that the content notifications are flushed before we
3627 // start messing with the frame model; otherwise we can get content doubling.
3628 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
3630 nsAutoScriptBlocker scriptBlocker;
3632 nsStyleChangeList changeList;
3633 changeList.AppendChange(nsnull, aContent, nsChangeHint_ReconstructFrame);
3635 // Mark ourselves as not safe to flush while we're doing frame construction.
3636 ++mChangeNestCount;
3637 nsresult rv = mFrameConstructor->ProcessRestyledFrames(changeList);
3638 --mChangeNestCount;
3640 batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
3641 #ifdef ACCESSIBILITY
3642 InvalidateAccessibleSubtree(aContent);
3643 #endif
3644 return rv;
3647 void
3648 nsIPresShell::PostRecreateFramesFor(Element* aElement)
3650 FrameConstructor()->PostRestyleEvent(aElement, nsRestyleHint(0),
3651 nsChangeHint_ReconstructFrame);
3654 void
3655 nsIPresShell::RestyleForAnimation(Element* aElement, nsRestyleHint aHint)
3657 FrameConstructor()->PostAnimationRestyleEvent(aElement, aHint,
3658 NS_STYLE_HINT_NONE);
3661 void
3662 PresShell::ClearFrameRefs(nsIFrame* aFrame)
3664 mPresContext->EventStateManager()->ClearFrameRefs(aFrame);
3666 nsWeakFrame* weakFrame = mWeakFrames;
3667 while (weakFrame) {
3668 nsWeakFrame* prev = weakFrame->GetPreviousWeakFrame();
3669 if (weakFrame->GetFrame() == aFrame) {
3670 // This removes weakFrame from mWeakFrames.
3671 weakFrame->Clear(this);
3673 weakFrame = prev;
3677 already_AddRefed<nsIRenderingContext>
3678 PresShell::GetReferenceRenderingContext()
3680 NS_TIME_FUNCTION_MIN(1.0);
3682 nsIDeviceContext* devCtx = mPresContext->DeviceContext();
3683 nsRefPtr<nsIRenderingContext> rc;
3684 if (mPresContext->IsScreen()) {
3685 devCtx->CreateRenderingContextInstance(*getter_AddRefs(rc));
3686 if (rc) {
3687 rc->Init(devCtx, gfxPlatform::GetPlatform()->ScreenReferenceSurface());
3689 } else {
3690 devCtx->CreateRenderingContext(*getter_AddRefs(rc));
3692 return rc.forget();
3695 nsresult
3696 PresShell::GoToAnchor(const nsAString& aAnchorName, PRBool aScroll)
3698 if (!mDocument) {
3699 return NS_ERROR_FAILURE;
3702 // Hold a reference to the ESM in case event dispatch tears us down.
3703 nsCOMPtr<nsIEventStateManager> esm = mPresContext->EventStateManager();
3705 if (aAnchorName.IsEmpty()) {
3706 NS_ASSERTION(!aScroll, "can't scroll to empty anchor name");
3707 esm->SetContentState(nsnull, NS_EVENT_STATE_URLTARGET);
3708 return NS_OK;
3711 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
3712 nsresult rv = NS_OK;
3713 nsCOMPtr<nsIContent> content;
3715 // Search for an element with a matching "id" attribute
3716 if (mDocument) {
3717 content = mDocument->GetElementById(aAnchorName);
3720 // Search for an anchor element with a matching "name" attribute
3721 if (!content && htmlDoc) {
3722 nsCOMPtr<nsIDOMNodeList> list;
3723 // Find a matching list of named nodes
3724 rv = htmlDoc->GetElementsByName(aAnchorName, getter_AddRefs(list));
3725 if (NS_SUCCEEDED(rv) && list) {
3726 PRUint32 i;
3727 // Loop through the named nodes looking for the first anchor
3728 for (i = 0; PR_TRUE; i++) {
3729 nsCOMPtr<nsIDOMNode> node;
3730 rv = list->Item(i, getter_AddRefs(node));
3731 if (!node) { // End of list
3732 break;
3734 // Ensure it's an anchor element
3735 content = do_QueryInterface(node);
3736 if (content) {
3737 if (content->Tag() == nsGkAtoms::a && content->IsHTML()) {
3738 break;
3740 content = nsnull;
3746 // Search for anchor in the HTML namespace with a matching name
3747 if (!content && !htmlDoc)
3749 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
3750 nsCOMPtr<nsIDOMNodeList> list;
3751 NS_NAMED_LITERAL_STRING(nameSpace, "http://www.w3.org/1999/xhtml");
3752 // Get the list of anchor elements
3753 rv = doc->GetElementsByTagNameNS(nameSpace, NS_LITERAL_STRING("a"), getter_AddRefs(list));
3754 if (NS_SUCCEEDED(rv) && list) {
3755 PRUint32 i;
3756 // Loop through the named nodes looking for the first anchor
3757 for (i = 0; PR_TRUE; i++) {
3758 nsCOMPtr<nsIDOMNode> node;
3759 rv = list->Item(i, getter_AddRefs(node));
3760 if (!node) { // End of list
3761 break;
3763 // Compare the name attribute
3764 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(node);
3765 nsAutoString value;
3766 if (element && NS_SUCCEEDED(element->GetAttribute(NS_LITERAL_STRING("name"), value))) {
3767 if (value.Equals(aAnchorName)) {
3768 content = do_QueryInterface(element);
3769 break;
3776 nsCOMPtr<nsIDOMRange> jumpToRange;
3777 nsCOMPtr<nsIXPointerResult> xpointerResult;
3778 if (!content) {
3779 nsCOMPtr<nsIDOMXMLDocument> xmldoc = do_QueryInterface(mDocument);
3780 if (xmldoc) {
3781 // Try XPointer
3782 xmldoc->EvaluateXPointer(aAnchorName, getter_AddRefs(xpointerResult));
3783 if (xpointerResult) {
3784 xpointerResult->Item(0, getter_AddRefs(jumpToRange));
3785 if (!jumpToRange) {
3786 // We know it was an XPointer, so there is no point in
3787 // trying any other pointer types, let's just return
3788 // an error.
3789 return NS_ERROR_FAILURE;
3793 // Finally try FIXptr
3794 if (!jumpToRange) {
3795 xmldoc->EvaluateFIXptr(aAnchorName,getter_AddRefs(jumpToRange));
3798 if (jumpToRange) {
3799 nsCOMPtr<nsIDOMNode> node;
3800 jumpToRange->GetStartContainer(getter_AddRefs(node));
3801 if (node) {
3802 PRUint16 nodeType;
3803 node->GetNodeType(&nodeType);
3804 PRInt32 offset = -1;
3805 jumpToRange->GetStartOffset(&offset);
3806 switch (nodeType) {
3807 case nsIDOMNode::ATTRIBUTE_NODE:
3809 // XXX Assuming jumping to the ownerElement is the sanest action.
3810 nsCOMPtr<nsIAttribute> attr = do_QueryInterface(node);
3811 content = attr->GetContent();
3812 break;
3814 case nsIDOMNode::DOCUMENT_NODE:
3816 if (offset >= 0) {
3817 nsCOMPtr<nsIDocument> document = do_QueryInterface(node);
3818 content = document->GetChildAt(offset);
3820 break;
3822 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
3823 case nsIDOMNode::ELEMENT_NODE:
3824 case nsIDOMNode::ENTITY_REFERENCE_NODE:
3826 if (offset >= 0) {
3827 nsCOMPtr<nsIContent> parent = do_QueryInterface(node);
3828 content = parent->GetChildAt(offset);
3830 break;
3832 case nsIDOMNode::CDATA_SECTION_NODE:
3833 case nsIDOMNode::COMMENT_NODE:
3834 case nsIDOMNode::TEXT_NODE:
3835 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
3837 // XXX This should scroll to a specific position in the text.
3838 content = do_QueryInterface(node);
3839 break;
3847 esm->SetContentState(content, NS_EVENT_STATE_URLTARGET);
3849 #ifdef ACCESSIBILITY
3850 nsIContent *anchorTarget = content;
3851 #endif
3853 if (content) {
3854 if (aScroll) {
3855 rv = ScrollContentIntoView(content, NS_PRESSHELL_SCROLL_TOP,
3856 NS_PRESSHELL_SCROLL_ANYWHERE);
3857 NS_ENSURE_SUCCESS(rv, rv);
3859 nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3860 if (rootScroll) {
3861 mLastAnchorScrolledTo = content;
3862 mLastAnchorScrollPositionY = rootScroll->GetScrollPosition().y;
3866 // Should we select the target? This action is controlled by a
3867 // preference: the default is to not select.
3868 PRBool selectAnchor = nsContentUtils::GetBoolPref("layout.selectanchor");
3870 // Even if select anchor pref is false, we must still move the
3871 // caret there. That way tabbing will start from the new
3872 // location
3873 if (!jumpToRange) {
3874 jumpToRange = do_CreateInstance(kRangeCID);
3875 if (jumpToRange) {
3876 while (content && content->GetChildCount() > 0) {
3877 content = content->GetChildAt(0);
3879 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(content));
3880 NS_ASSERTION(node, "No nsIDOMNode for descendant of anchor");
3881 jumpToRange->SelectNodeContents(node);
3884 if (jumpToRange) {
3885 // Select the anchor
3886 nsISelection* sel = mSelection->
3887 GetSelection(nsISelectionController::SELECTION_NORMAL);
3888 if (sel) {
3889 sel->RemoveAllRanges();
3890 sel->AddRange(jumpToRange);
3891 if (!selectAnchor) {
3892 // Use a caret (collapsed selection) at the start of the anchor
3893 sel->CollapseToStart();
3897 if (selectAnchor && xpointerResult) {
3898 // Select the rest (if any) of the ranges in XPointerResult
3899 PRUint32 count, i;
3900 xpointerResult->GetLength(&count);
3901 for (i = 1; i < count; i++) { // jumpToRange is i = 0
3902 nsCOMPtr<nsIDOMRange> range;
3903 xpointerResult->Item(i, getter_AddRefs(range));
3904 sel->AddRange(range);
3907 // Selection is at anchor.
3908 // Now focus the document itself if focus is on an element within it.
3909 nsPIDOMWindow *win = mDocument->GetWindow();
3911 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3912 if (fm && win) {
3913 nsCOMPtr<nsIDOMWindow> focusedWindow;
3914 fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
3915 if (SameCOMIdentity(win, focusedWindow))
3916 fm->ClearFocus(focusedWindow);
3919 } else {
3920 rv = NS_ERROR_FAILURE; //changed to NS_OK in quirks mode if ScrollTo is called
3922 // Scroll to the top/left if the anchor can not be
3923 // found and it is labelled top (quirks mode only). @see bug 80784
3924 if ((NS_LossyConvertUTF16toASCII(aAnchorName).LowerCaseEqualsLiteral("top")) &&
3925 (mPresContext->CompatibilityMode() == eCompatibility_NavQuirks)) {
3926 rv = NS_OK;
3927 nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable();
3928 // Check |aScroll| after setting |rv| so we set |rv| to the same
3929 // thing whether or not |aScroll| is true.
3930 if (aScroll && sf) {
3931 // Scroll to the top of the page
3932 sf->ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT);
3937 #ifdef ACCESSIBILITY
3938 if (anchorTarget && gIsAccessibilityActive) {
3939 nsCOMPtr<nsIAccessibilityService> accService =
3940 do_GetService("@mozilla.org/accessibilityService;1");
3941 if (accService)
3942 accService->NotifyOfAnchorJumpTo(anchorTarget);
3944 #endif
3946 return rv;
3949 nsresult
3950 PresShell::ScrollToAnchor()
3952 if (!mLastAnchorScrolledTo)
3953 return NS_OK;
3955 NS_ASSERTION(mDidInitialReflow, "should have done initial reflow by now");
3957 nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3958 if (!rootScroll ||
3959 mLastAnchorScrollPositionY != rootScroll->GetScrollPosition().y)
3960 return NS_OK;
3962 nsresult rv = ScrollContentIntoView(mLastAnchorScrolledTo, NS_PRESSHELL_SCROLL_TOP,
3963 NS_PRESSHELL_SCROLL_ANYWHERE);
3964 mLastAnchorScrolledTo = nsnull;
3965 return rv;
3969 * Helper (per-continuation) for ScrollContentIntoView.
3971 * @param aContainerFrame [in] the frame which aRect is relative to
3972 * @param aFrame [in] Frame whose bounds should be unioned
3973 * @param aUseWholeLineHeightForInlines [in] if true, then for inline frames
3974 * we should include the top of the line in the added rectangle
3975 * @param aRect [inout] rect into which its bounds should be unioned
3976 * @param aHaveRect [inout] whether aRect contains data yet
3978 static void
3979 AccumulateFrameBounds(nsIFrame* aContainerFrame,
3980 nsIFrame* aFrame,
3981 PRBool aUseWholeLineHeightForInlines,
3982 nsRect& aRect,
3983 PRBool& aHaveRect)
3985 nsRect frameBounds = aFrame->GetRect() +
3986 aFrame->GetParent()->GetOffsetTo(aContainerFrame);
3988 // If this is an inline frame and either the bounds height is 0 (quirks
3989 // layout model) or aUseWholeLineHeightForInlines is set, we need to
3990 // change the top of the bounds to include the whole line.
3991 if (frameBounds.height == 0 || aUseWholeLineHeightForInlines) {
3992 nsIAtom* frameType = NULL;
3993 nsIFrame *prevFrame = aFrame;
3994 nsIFrame *f = aFrame;
3996 while (f &&
3997 (frameType = f->GetType()) == nsGkAtoms::inlineFrame) {
3998 prevFrame = f;
3999 f = prevFrame->GetParent();
4002 if (f != aFrame &&
4003 f &&
4004 frameType == nsGkAtoms::blockFrame) {
4005 // find the line containing aFrame and increase the top of |offset|.
4006 nsAutoLineIterator lines = f->GetLineIterator();
4007 if (lines) {
4008 PRInt32 index = lines->FindLineContaining(prevFrame);
4009 if (index >= 0) {
4010 nsIFrame *trash1;
4011 PRInt32 trash2;
4012 nsRect lineBounds;
4013 PRUint32 trash3;
4015 if (NS_SUCCEEDED(lines->GetLine(index, &trash1, &trash2,
4016 lineBounds, &trash3))) {
4017 lineBounds += f->GetOffsetTo(aContainerFrame);
4018 if (lineBounds.y < frameBounds.y) {
4019 frameBounds.height = frameBounds.YMost() - lineBounds.y;
4020 frameBounds.y = lineBounds.y;
4028 if (aHaveRect) {
4029 // We can't use nsRect::UnionRect since it drops empty rects on
4030 // the floor, and we need to include them. (Thus we need
4031 // aHaveRect to know when to drop the initial value on the floor.)
4032 aRect.UnionRectIncludeEmpty(aRect, frameBounds);
4033 } else {
4034 aHaveRect = PR_TRUE;
4035 aRect = frameBounds;
4040 * This function takes a scrollable frame, a rect in the coordinate system
4041 * of the scrolled frame, and a desired percentage-based scroll
4042 * position and attempts to scroll the rect to that position in the
4043 * scrollport.
4045 * This needs to work even if aRect has a width or height of zero.
4047 static void ScrollToShowRect(nsIScrollableFrame* aScrollFrame,
4048 const nsRect& aRect,
4049 PRIntn aVPercent,
4050 PRIntn aHPercent,
4051 PRUint32 aFlags)
4053 nsPoint scrollPt = aScrollFrame->GetScrollPosition();
4054 nsRect visibleRect(scrollPt, aScrollFrame->GetScrollPortRect().Size());
4055 nsSize lineSize = aScrollFrame->GetLineScrollAmount();
4056 nsPresContext::ScrollbarStyles ss = aScrollFrame->GetScrollbarStyles();
4058 if ((aFlags & nsIPresShell::SCROLL_OVERFLOW_HIDDEN) ||
4059 ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN) {
4060 // See how the rect should be positioned vertically
4061 if (NS_PRESSHELL_SCROLL_ANYWHERE == aVPercent ||
4062 (NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE == aVPercent &&
4063 aRect.height < lineSize.height)) {
4064 // The caller doesn't care where the frame is positioned vertically,
4065 // so long as it's fully visible
4066 if (aRect.y < visibleRect.y) {
4067 // Scroll up so the frame's top edge is visible
4068 scrollPt.y = aRect.y;
4069 } else if (aRect.YMost() > visibleRect.YMost()) {
4070 // Scroll down so the frame's bottom edge is visible. Make sure the
4071 // frame's top edge is still visible
4072 scrollPt.y += aRect.YMost() - visibleRect.YMost();
4073 if (scrollPt.y > aRect.y) {
4074 scrollPt.y = aRect.y;
4077 } else if (NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE == aVPercent) {
4078 // Scroll only if no part of the frame is visible in this view
4079 if (aRect.YMost() - lineSize.height < visibleRect.y) {
4080 // Scroll up so the frame's top edge is visible
4081 scrollPt.y = aRect.y;
4082 } else if (aRect.y + lineSize.height > visibleRect.YMost()) {
4083 // Scroll down so the frame's bottom edge is visible. Make sure the
4084 // frame's top edge is still visible
4085 scrollPt.y += aRect.YMost() - visibleRect.YMost();
4086 if (scrollPt.y > aRect.y) {
4087 scrollPt.y = aRect.y;
4090 } else {
4091 // Align the frame edge according to the specified percentage
4092 nscoord frameAlignY =
4093 NSToCoordRound(aRect.y + aRect.height * (aVPercent / 100.0f));
4094 scrollPt.y =
4095 NSToCoordRound(frameAlignY - visibleRect.height * (aVPercent / 100.0f));
4099 if ((aFlags & nsIPresShell::SCROLL_OVERFLOW_HIDDEN) ||
4100 ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) {
4101 // See how the frame should be positioned horizontally
4102 if (NS_PRESSHELL_SCROLL_ANYWHERE == aHPercent ||
4103 (NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE == aHPercent &&
4104 aRect.width < lineSize.width)) {
4105 // The caller doesn't care where the frame is positioned horizontally,
4106 // so long as it's fully visible
4107 if (aRect.x < visibleRect.x) {
4108 // Scroll left so the frame's left edge is visible
4109 scrollPt.x = aRect.x;
4110 } else if (aRect.XMost() > visibleRect.XMost()) {
4111 // Scroll right so the frame's right edge is visible. Make sure the
4112 // frame's left edge is still visible
4113 scrollPt.x += aRect.XMost() - visibleRect.XMost();
4114 if (scrollPt.x > aRect.x) {
4115 scrollPt.x = aRect.x;
4118 } else if (NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE == aHPercent) {
4119 // Scroll only if no part of the frame is visible in this view
4120 if (aRect.XMost() - lineSize.width < visibleRect.x) {
4121 // Scroll left so the frame's left edge is visible
4122 scrollPt.x = aRect.x;
4123 } else if (aRect.x + lineSize.width > visibleRect.XMost()) {
4124 // Scroll right so the frame's right edge is visible. Make sure the
4125 // frame's left edge is still visible
4126 scrollPt.x += aRect.XMost() - visibleRect.XMost();
4127 if (scrollPt.x > aRect.x) {
4128 scrollPt.x = aRect.x;
4131 } else {
4132 // Align the frame edge according to the specified percentage
4133 nscoord frameAlignX =
4134 NSToCoordRound(aRect.x + (aRect.width) * (aHPercent / 100.0f));
4135 scrollPt.x =
4136 NSToCoordRound(frameAlignX - visibleRect.width * (aHPercent / 100.0f));
4140 aScrollFrame->ScrollTo(scrollPt, nsIScrollableFrame::INSTANT);
4143 nsresult
4144 PresShell::ScrollContentIntoView(nsIContent* aContent,
4145 PRIntn aVPercent,
4146 PRIntn aHPercent)
4148 nsCOMPtr<nsIContent> content = aContent; // Keep content alive while flushing.
4149 NS_ENSURE_TRUE(content, NS_ERROR_NULL_POINTER);
4150 nsCOMPtr<nsIDocument> currentDoc = content->GetCurrentDoc();
4151 NS_ENSURE_STATE(currentDoc);
4153 NS_ASSERTION(mDidInitialReflow, "should have done initial reflow by now");
4155 mContentToScrollTo = aContent;
4156 mContentScrollVPosition = aVPercent;
4157 mContentScrollHPosition = aHPercent;
4159 // Flush layout and attempt to scroll in the process.
4160 currentDoc->FlushPendingNotifications(Flush_InterruptibleLayout);
4162 // If mContentToScrollTo is non-null, that means we interrupted the reflow
4163 // (or suppressed it altogether because we're suppressing interruptible
4164 // flushes right now) and won't necessarily get the position correct, but do
4165 // a best-effort scroll here. The other option would be to do this inside
4166 // FlushPendingNotifications, but I'm not sure the repeated scrolling that
4167 // could trigger if reflows keep getting interrupted would be more desirable
4168 // than a single best-effort scroll followed by one final scroll on the first
4169 // completed reflow.
4170 if (mContentToScrollTo) {
4171 DoScrollContentIntoView(content, aVPercent, aHPercent);
4173 return NS_OK;
4176 void
4177 PresShell::DoScrollContentIntoView(nsIContent* aContent,
4178 PRIntn aVPercent,
4179 PRIntn aHPercent)
4181 NS_ASSERTION(mDidInitialReflow, "should have done initial reflow by now");
4183 nsIFrame* frame = aContent->GetPrimaryFrame();
4184 if (!frame) {
4185 mContentToScrollTo = nsnull;
4186 return;
4189 if (frame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
4190 // The reflow flush before this scroll got interrupted, and this frame's
4191 // coords and size are all zero, and it has no content showing anyway.
4192 // Don't bother scrolling to it. We'll try again when we finish up layout.
4193 return;
4196 nsIFrame* container =
4197 nsLayoutUtils::GetClosestFrameOfType(frame, nsGkAtoms::scrollFrame);
4198 if (!container) {
4199 // nothing can be scrolled
4200 return;
4203 // This is a two-step process.
4204 // Step 1: Find the bounds of the rect we want to scroll into view. For
4205 // example, for an inline frame we may want to scroll in the whole
4206 // line, or we may want to scroll multiple lines into view.
4207 // Step 2: Walk container frame and its ancestors and scroll them
4208 // appropriately.
4209 // frameBounds is relative to container. We're assuming
4210 // that scrollframes don't split so every continuation of frame will
4211 // be a descendant of container. (Things would still mostly work
4212 // even if that assumption was false.)
4213 nsRect frameBounds;
4214 PRBool haveRect = PR_FALSE;
4215 PRBool useWholeLineHeightForInlines = aVPercent != NS_PRESSHELL_SCROLL_ANYWHERE;
4216 do {
4217 AccumulateFrameBounds(container, frame, useWholeLineHeightForInlines,
4218 frameBounds, haveRect);
4219 } while ((frame = frame->GetNextContinuation()));
4221 ScrollFrameRectIntoView(container, frameBounds, aVPercent, aHPercent,
4222 SCROLL_OVERFLOW_HIDDEN);
4225 PRBool
4226 PresShell::ScrollFrameRectIntoView(nsIFrame* aFrame,
4227 const nsRect& aRect,
4228 PRIntn aVPercent,
4229 PRIntn aHPercent,
4230 PRUint32 aFlags)
4232 PRBool didScroll = PR_FALSE;
4233 // This function needs to work even if rect has a width or height of 0.
4234 nsRect rect = aRect;
4235 nsIFrame* container = aFrame;
4236 // Walk up the frame hierarchy scrolling the rect into view and
4237 // keeping rect relative to container
4238 do {
4239 nsIScrollableFrame* sf = do_QueryFrame(container);
4240 if (sf) {
4241 nsPoint oldPosition = sf->GetScrollPosition();
4242 ScrollToShowRect(sf, rect - sf->GetScrolledFrame()->GetPosition(),
4243 aVPercent, aHPercent, aFlags);
4244 nsPoint newPosition = sf->GetScrollPosition();
4245 // If the scroll position increased, that means our content moved up,
4246 // so our rect's offset should decrease
4247 rect += oldPosition - newPosition;
4249 if (oldPosition != newPosition) {
4250 didScroll = PR_TRUE;
4253 // only scroll one container when this flag is set
4254 if (aFlags & SCROLL_FIRST_ANCESTOR_ONLY) {
4255 break;
4258 nsRect scrollPort = sf->GetScrollPortRect();
4259 if (rect.XMost() < scrollPort.x ||
4260 rect.x > scrollPort.XMost() ||
4261 rect.YMost() < scrollPort.y ||
4262 rect.y > scrollPort.YMost()) {
4263 // We tried to show the rectangle, but none of it is visible,
4264 // not even an edge.
4265 // Stop trying to scroll ancestors into view.
4266 break;
4269 // Restrict rect to the area that is actually visible through
4270 // the scrollport. We don't want to try to scroll some clipped-out
4271 // part of 'rect' into view in some ancestor.
4272 rect.IntersectRect(rect, sf->GetScrollPortRect());
4274 rect += container->GetPosition();
4275 nsIFrame* parent = container->GetParent();
4276 if (!parent) {
4277 nsPoint extraOffset(0,0);
4278 parent = nsLayoutUtils::GetCrossDocParentFrame(container, &extraOffset);
4279 if (parent) {
4280 PRInt32 APD = container->PresContext()->AppUnitsPerDevPixel();
4281 PRInt32 parentAPD = parent->PresContext()->AppUnitsPerDevPixel();
4282 rect = rect.ConvertAppUnitsRoundOut(APD, parentAPD);
4283 rect += extraOffset;
4286 container = parent;
4287 } while (container);
4289 return didScroll;
4292 nsRectVisibility
4293 PresShell::GetRectVisibility(nsIFrame* aFrame,
4294 const nsRect &aRect,
4295 nscoord aMinTwips) const
4297 NS_ASSERTION(aFrame->PresContext() == GetPresContext(),
4298 "prescontext mismatch?");
4299 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
4300 NS_ASSERTION(rootFrame,
4301 "How can someone have a frame for this presshell when there's no root?");
4302 nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable();
4303 nsRect scrollPortRect;
4304 if (sf) {
4305 scrollPortRect = sf->GetScrollPortRect();
4306 nsIFrame* f = do_QueryFrame(sf);
4307 scrollPortRect += f->GetOffsetTo(rootFrame);
4308 } else {
4309 scrollPortRect = nsRect(nsPoint(0,0), rootFrame->GetSize());
4312 nsRect r = aRect + aFrame->GetOffsetTo(rootFrame);
4313 // If aRect is entirely visible then we don't need to ensure that
4314 // at least aMinTwips of it is visible
4315 if (scrollPortRect.Contains(r))
4316 return nsRectVisibility_kVisible;
4318 nsRect insetRect = scrollPortRect;
4319 insetRect.Deflate(aMinTwips, aMinTwips);
4320 if (r.YMost() <= insetRect.y)
4321 return nsRectVisibility_kAboveViewport;
4322 if (r.y >= insetRect.YMost())
4323 return nsRectVisibility_kBelowViewport;
4324 if (r.XMost() <= insetRect.x)
4325 return nsRectVisibility_kLeftOfViewport;
4326 if (r.x >= insetRect.XMost())
4327 return nsRectVisibility_kRightOfViewport;
4329 return nsRectVisibility_kVisible;
4332 // GetLinkLocation: copy link location to clipboard
4333 nsresult PresShell::GetLinkLocation(nsIDOMNode* aNode, nsAString& aLocationString) const
4335 #ifdef DEBUG_dr
4336 printf("dr :: PresShell::GetLinkLocation\n");
4337 #endif
4339 NS_ENSURE_ARG_POINTER(aNode);
4340 nsresult rv;
4341 nsAutoString anchorText;
4342 static char strippedChars[] = {'\t','\r','\n'};
4344 // are we an anchor?
4345 nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(aNode));
4346 nsCOMPtr<nsIDOMHTMLAreaElement> area;
4347 nsCOMPtr<nsIDOMHTMLLinkElement> link;
4348 nsAutoString xlinkType;
4349 if (anchor) {
4350 rv = anchor->GetHref(anchorText);
4351 NS_ENSURE_SUCCESS(rv, rv);
4352 } else {
4353 // area?
4354 area = do_QueryInterface(aNode);
4355 if (area) {
4356 rv = area->GetHref(anchorText);
4357 NS_ENSURE_SUCCESS(rv, rv);
4358 } else {
4359 // link?
4360 link = do_QueryInterface(aNode);
4361 if (link) {
4362 rv = link->GetHref(anchorText);
4363 NS_ENSURE_SUCCESS(rv, rv);
4364 } else {
4365 // Xlink?
4366 nsCOMPtr<nsIDOMElement> element(do_QueryInterface(aNode));
4367 if (element) {
4368 NS_NAMED_LITERAL_STRING(xlinkNS,"http://www.w3.org/1999/xlink");
4369 element->GetAttributeNS(xlinkNS,NS_LITERAL_STRING("type"),xlinkType);
4370 if (xlinkType.EqualsLiteral("simple")) {
4371 element->GetAttributeNS(xlinkNS,NS_LITERAL_STRING("href"),anchorText);
4372 if (!anchorText.IsEmpty()) {
4373 // Resolve the full URI using baseURI property
4375 nsAutoString base;
4376 nsCOMPtr<nsIDOM3Node> node(do_QueryInterface(aNode,&rv));
4377 NS_ENSURE_SUCCESS(rv, rv);
4378 node->GetBaseURI(base);
4380 nsCOMPtr<nsIIOService>
4381 ios(do_GetService("@mozilla.org/network/io-service;1", &rv));
4382 NS_ENSURE_SUCCESS(rv, rv);
4384 nsCOMPtr<nsIURI> baseURI;
4385 rv = ios->NewURI(NS_ConvertUTF16toUTF8(base),nsnull,nsnull,getter_AddRefs(baseURI));
4386 NS_ENSURE_SUCCESS(rv, rv);
4388 nsCAutoString spec;
4389 rv = baseURI->Resolve(NS_ConvertUTF16toUTF8(anchorText),spec);
4390 NS_ENSURE_SUCCESS(rv, rv);
4392 CopyUTF8toUTF16(spec, anchorText);
4400 if (anchor || area || link || xlinkType.EqualsLiteral("simple")) {
4401 //Remove all the '\t', '\r' and '\n' from 'anchorText'
4402 anchorText.StripChars(strippedChars);
4404 aLocationString = anchorText;
4406 return NS_OK;
4409 // if no link, fail.
4410 return NS_ERROR_FAILURE;
4413 NS_IMETHODIMP_(void)
4414 PresShell::DispatchSynthMouseMove(nsGUIEvent *aEvent,
4415 PRBool aFlushOnHoverChange)
4417 PRUint32 hoverGenerationBefore = mFrameConstructor->GetHoverGeneration();
4418 nsEventStatus status;
4419 nsIView* targetView = nsIView::GetViewFor(aEvent->widget);
4420 targetView->GetViewManager()->DispatchEvent(aEvent, targetView, &status);
4421 if (aFlushOnHoverChange &&
4422 hoverGenerationBefore != mFrameConstructor->GetHoverGeneration()) {
4423 // Flush so that the resulting reflow happens now so that our caller
4424 // can suppress any synthesized mouse moves caused by that reflow.
4425 FlushPendingNotifications(Flush_Layout);
4429 NS_IMETHODIMP_(void)
4430 PresShell::ClearMouseCapture(nsIView* aView)
4432 if (gCaptureInfo.mContent) {
4433 if (aView) {
4434 // if a view was specified, ensure that the captured content is within
4435 // this view.
4436 nsIFrame* frame = gCaptureInfo.mContent->GetPrimaryFrame();
4437 if (frame) {
4438 nsIView* view = frame->GetClosestView();
4439 // if there is no view, capturing won't be handled any more, so
4440 // just release the capture.
4441 if (view) {
4442 do {
4443 if (view == aView) {
4444 NS_RELEASE(gCaptureInfo.mContent);
4445 // the view containing the captured content likely disappeared so
4446 // disable capture for now.
4447 gCaptureInfo.mAllowed = PR_FALSE;
4448 break;
4451 view = view->GetParent();
4452 } while (view);
4453 // return if the view wasn't found
4454 return;
4459 NS_RELEASE(gCaptureInfo.mContent);
4462 // disable mouse capture until the next mousedown as a dialog has opened
4463 // or a drag has started. Otherwise, someone could start capture during
4464 // the modal dialog or drag.
4465 gCaptureInfo.mAllowed = PR_FALSE;
4468 nsresult
4469 PresShell::CaptureHistoryState(nsILayoutHistoryState** aState, PRBool aLeavingPage)
4471 NS_TIME_FUNCTION_MIN(1.0);
4473 nsresult rv = NS_OK;
4475 NS_PRECONDITION(nsnull != aState, "null state pointer");
4477 // We actually have to mess with the docshell here, since we want to
4478 // store the state back in it.
4479 // XXXbz this isn't really right, since this is being called in the
4480 // content viewer's Hide() method... by that point the docshell's
4481 // state could be wrong. We should sort out a better ownership
4482 // model for the layout history state.
4483 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
4484 if (!container)
4485 return NS_ERROR_FAILURE;
4487 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
4488 if (!docShell)
4489 return NS_ERROR_FAILURE;
4491 nsCOMPtr<nsILayoutHistoryState> historyState;
4492 docShell->GetLayoutHistoryState(getter_AddRefs(historyState));
4493 if (!historyState) {
4494 // Create the document state object
4495 rv = NS_NewLayoutHistoryState(getter_AddRefs(historyState));
4497 if (NS_FAILED(rv)) {
4498 *aState = nsnull;
4499 return rv;
4502 docShell->SetLayoutHistoryState(historyState);
4505 *aState = historyState;
4506 NS_IF_ADDREF(*aState);
4508 // Capture frame state for the entire frame hierarchy
4509 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
4510 if (!rootFrame) return NS_OK;
4511 // Capture frame state for the root scroll frame
4512 // Don't capture state when first creating doc element hierarchy
4513 // As the scroll position is 0 and this will cause us to lose
4514 // our previously saved place!
4515 if (aLeavingPage) {
4516 nsIFrame* scrollFrame = GetRootScrollFrame();
4517 if (scrollFrame) {
4518 FrameManager()->CaptureFrameStateFor(scrollFrame, historyState,
4519 nsIStatefulFrame::eDocumentScrollState);
4523 FrameManager()->CaptureFrameState(rootFrame, historyState);
4525 return NS_OK;
4528 void
4529 PresShell::UnsuppressAndInvalidate()
4531 if (!mPresContext->EnsureVisible() || mHaveShutDown) {
4532 // No point; we're about to be torn down anyway.
4533 return;
4536 mPaintingSuppressed = PR_FALSE;
4537 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
4538 if (rootFrame) {
4539 // let's assume that outline on a root frame is not supported
4540 nsRect rect(nsPoint(0, 0), rootFrame->GetSize());
4541 rootFrame->Invalidate(rect);
4543 if (mCaretEnabled && mCaret) {
4544 mCaret->CheckCaretDrawingState();
4547 nsRootPresContext* rootPC = mPresContext->GetRootPresContext();
4548 if (rootPC) {
4549 rootPC->RequestUpdatePluginGeometry(rootFrame);
4553 // now that painting is unsuppressed, focus may be set on the document
4554 nsPIDOMWindow *win = mDocument->GetWindow();
4555 if (win)
4556 win->SetReadyForFocus();
4558 if (!mHaveShutDown && mViewManager)
4559 mViewManager->SynthesizeMouseMove(PR_FALSE);
4562 void
4563 PresShell::UnsuppressPainting()
4565 if (mPaintSuppressionTimer) {
4566 mPaintSuppressionTimer->Cancel();
4567 mPaintSuppressionTimer = nsnull;
4570 if (mIsDocumentGone || !mPaintingSuppressed)
4571 return;
4573 // If we have reflows pending, just wait until we process
4574 // the reflows and get all the frames where we want them
4575 // before actually unlocking the painting. Otherwise
4576 // go ahead and unlock now.
4577 if (mDirtyRoots.Length() > 0)
4578 mShouldUnsuppressPainting = PR_TRUE;
4579 else
4580 UnsuppressAndInvalidate();
4583 // Post a request to handle an arbitrary callback after reflow has finished.
4584 nsresult
4585 PresShell::PostReflowCallback(nsIReflowCallback* aCallback)
4587 void* result = AllocateMisc(sizeof(nsCallbackEventRequest));
4588 if (NS_UNLIKELY(!result)) {
4589 return NS_ERROR_OUT_OF_MEMORY;
4591 nsCallbackEventRequest* request = (nsCallbackEventRequest*)result;
4593 request->callback = aCallback;
4594 request->next = nsnull;
4596 if (mLastCallbackEventRequest) {
4597 mLastCallbackEventRequest = mLastCallbackEventRequest->next = request;
4598 } else {
4599 mFirstCallbackEventRequest = request;
4600 mLastCallbackEventRequest = request;
4603 return NS_OK;
4606 void
4607 PresShell::CancelReflowCallback(nsIReflowCallback* aCallback)
4609 nsCallbackEventRequest* before = nsnull;
4610 nsCallbackEventRequest* node = mFirstCallbackEventRequest;
4611 while(node)
4613 nsIReflowCallback* callback = node->callback;
4615 if (callback == aCallback)
4617 nsCallbackEventRequest* toFree = node;
4618 if (node == mFirstCallbackEventRequest) {
4619 node = node->next;
4620 mFirstCallbackEventRequest = node;
4621 NS_ASSERTION(before == nsnull, "impossible");
4622 } else {
4623 node = node->next;
4624 before->next = node;
4627 if (toFree == mLastCallbackEventRequest) {
4628 mLastCallbackEventRequest = before;
4631 FreeMisc(sizeof(nsCallbackEventRequest), toFree);
4632 } else {
4633 before = node;
4634 node = node->next;
4639 void
4640 PresShell::CancelPostedReflowCallbacks()
4642 while (mFirstCallbackEventRequest) {
4643 nsCallbackEventRequest* node = mFirstCallbackEventRequest;
4644 mFirstCallbackEventRequest = node->next;
4645 if (!mFirstCallbackEventRequest) {
4646 mLastCallbackEventRequest = nsnull;
4648 nsIReflowCallback* callback = node->callback;
4649 FreeMisc(sizeof(nsCallbackEventRequest), node);
4650 if (callback) {
4651 callback->ReflowCallbackCanceled();
4656 void
4657 PresShell::HandlePostedReflowCallbacks(PRBool aInterruptible)
4659 PRBool shouldFlush = PR_FALSE;
4661 while (mFirstCallbackEventRequest) {
4662 nsCallbackEventRequest* node = mFirstCallbackEventRequest;
4663 mFirstCallbackEventRequest = node->next;
4664 if (!mFirstCallbackEventRequest) {
4665 mLastCallbackEventRequest = nsnull;
4667 nsIReflowCallback* callback = node->callback;
4668 FreeMisc(sizeof(nsCallbackEventRequest), node);
4669 if (callback) {
4670 if (callback->ReflowFinished()) {
4671 shouldFlush = PR_TRUE;
4676 mozFlushType flushType =
4677 aInterruptible ? Flush_InterruptibleLayout : Flush_Layout;
4678 if (shouldFlush)
4679 FlushPendingNotifications(flushType);
4682 PRBool
4683 PresShell::IsSafeToFlush() const
4685 // Not safe if we are reflowing or in the middle of frame construction
4686 PRBool isSafeToFlush = !mIsReflowing &&
4687 !mChangeNestCount;
4689 if (isSafeToFlush) {
4690 // Not safe if we are painting
4691 nsIViewManager* viewManager = GetViewManager();
4692 if (viewManager) {
4693 PRBool isPainting = PR_FALSE;
4694 viewManager->IsPainting(isPainting);
4695 if (isPainting) {
4696 isSafeToFlush = PR_FALSE;
4701 return isSafeToFlush;
4705 void
4706 PresShell::FlushPendingNotifications(mozFlushType aType)
4708 #ifdef NS_FUNCTION_TIMER
4709 NS_TIME_FUNCTION_DECLARE_DOCURL;
4710 static const char *flushTypeNames[] = {
4711 "Flush_Content",
4712 "Flush_ContentAndNotify",
4713 "Flush_Styles",
4714 "Flush_InterruptibleLayout",
4715 "Flush_Layout",
4716 "Flush_Display"
4718 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (document: %s, type: %s)", MOZ_FUNCTION_NAME,
4719 __LINE__, docURL__.get(), flushTypeNames[aType - 1]);
4720 #endif
4722 NS_ASSERTION(aType >= Flush_Frames, "Why did we get called?");
4724 PRBool isSafeToFlush = IsSafeToFlush();
4725 isSafeToFlush = isSafeToFlush && nsContentUtils::IsSafeToRunScript();
4727 NS_ASSERTION(!isSafeToFlush || mViewManager, "Must have view manager");
4728 // Make sure the view manager stays alive while batching view updates.
4729 nsCOMPtr<nsIViewManager> viewManagerDeathGrip = mViewManager;
4730 if (isSafeToFlush && mViewManager) {
4731 // Processing pending notifications can kill us, and some callers only
4732 // hold weak refs when calling FlushPendingNotifications(). :(
4733 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
4735 if (mResizeEvent.IsPending()) {
4736 FireResizeEvent();
4737 if (mIsDestroying) {
4738 return;
4742 // Style reresolves not in conjunction with reflows can't cause
4743 // painting or geometry changes, so don't bother with view update
4744 // batching if we only have style reresolve
4745 nsIViewManager::UpdateViewBatch batch(mViewManager);
4747 // We need to make sure external resource documents are flushed too (for
4748 // example, svg filters that reference a filter in an external document
4749 // need the frames in the external document to be constructed for the
4750 // filter to work). We only need external resources to be flushed when the
4751 // main document is flushing >= Flush_Frames, so we flush external
4752 // resources here instead of nsDocument::FlushPendingNotifications.
4753 mDocument->FlushExternalResources(aType);
4755 // Force flushing of any pending content notifications that might have
4756 // queued up while our event was pending. That will ensure that we don't
4757 // construct frames for content right now that's still waiting to be
4758 // notified on,
4759 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
4761 // Process pending restyles, since any flush of the presshell wants
4762 // up-to-date style data.
4763 if (!mIsDestroying) {
4764 mViewManager->FlushDelayedResize(PR_FALSE);
4765 mPresContext->FlushPendingMediaFeatureValuesChanged();
4767 // Flush any pending update of the user font set, since that could
4768 // cause style changes (for updating ex/ch units, and to cause a
4769 // reflow).
4770 mPresContext->FlushUserFontSet();
4772 #ifdef MOZ_SMIL
4773 // Flush any requested SMIL samples.
4774 if (mDocument->HasAnimationController()) {
4775 mDocument->GetAnimationController()->FlushResampleRequests();
4777 #endif // MOZ_SMIL
4779 nsAutoScriptBlocker scriptBlocker;
4780 mFrameConstructor->CreateNeededFrames();
4781 mFrameConstructor->ProcessPendingRestyles();
4784 // Process whatever XBL constructors those restyles queued up. This
4785 // ensures that onload doesn't fire too early and that we won't do extra
4786 // reflows after those constructors run.
4787 if (!mIsDestroying) {
4788 mDocument->BindingManager()->ProcessAttachedQueue();
4791 // Now those constructors might have posted restyle events. At the same
4792 // time, we still need up-to-date style data. In particular, reflow
4793 // depends on style being completely up to date. If it's not, then style
4794 // context reparenting, which can happen during reflow, might suddenly pick
4795 // up the new rules and we'll end up with frames whose style doesn't match
4796 // the frame type.
4797 if (!mIsDestroying) {
4798 nsAutoScriptBlocker scriptBlocker;
4799 mFrameConstructor->CreateNeededFrames();
4800 mFrameConstructor->ProcessPendingRestyles();
4804 // There might be more pending constructors now, but we're not going to
4805 // worry about them. They can't be triggered during reflow, so we should
4806 // be good.
4808 if (aType >= (mSuppressInterruptibleReflows ? Flush_Layout : Flush_InterruptibleLayout) &&
4809 !mIsDestroying) {
4810 mFrameConstructor->RecalcQuotesAndCounters();
4811 mViewManager->FlushDelayedResize(PR_TRUE);
4812 if (ProcessReflowCommands(aType < Flush_Layout) && mContentToScrollTo) {
4813 // We didn't get interrupted. Go ahead and scroll to our content
4814 DoScrollContentIntoView(mContentToScrollTo, mContentScrollVPosition,
4815 mContentScrollHPosition);
4816 mContentToScrollTo = nsnull;
4820 if (aType >= Flush_Layout) {
4821 // Flush plugin geometry. Don't flush plugin geometry for
4822 // interruptible layouts, since WillPaint does an interruptible
4823 // layout.
4824 nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
4825 if (rootPresContext) {
4826 rootPresContext->UpdatePluginGeometry();
4830 PRUint32 updateFlags = NS_VMREFRESH_NO_SYNC;
4831 if (aType >= Flush_Display) {
4832 // Flushing paints, so perform the invalidates and drawing
4833 // immediately
4834 updateFlags = NS_VMREFRESH_IMMEDIATE;
4836 batch.EndUpdateViewBatch(updateFlags);
4840 void
4841 PresShell::CharacterDataChanged(nsIDocument *aDocument,
4842 nsIContent* aContent,
4843 CharacterDataChangeInfo* aInfo)
4845 NS_PRECONDITION(!mIsDocumentGone, "Unexpected CharacterDataChanged");
4846 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4848 nsAutoCauseReflowNotifier crNotifier(this);
4850 if (mCaret) {
4851 // Invalidate the caret's current location before we call into the frame
4852 // constructor. It is important to do this now, and not wait until the
4853 // resulting reflow, because this call causes continuation frames of the
4854 // text frame the caret is in to forget what part of the content they
4855 // refer to, making it hard for them to return the correct continuation
4856 // frame to the caret.
4857 mCaret->InvalidateOutsideCaret();
4860 // Call this here so it only happens for real content mutations and
4861 // not cases when the frame constructor calls its own methods to force
4862 // frame reconstruction.
4863 nsIContent *container = aContent->GetParent();
4864 PRUint32 selectorFlags =
4865 container ? (container->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
4866 if (selectorFlags != 0 && !aContent->IsRootOfAnonymousSubtree()) {
4867 Element* element = container->AsElement();
4868 if (aInfo->mAppend && !aContent->GetNextSibling())
4869 mFrameConstructor->RestyleForAppend(element, aContent);
4870 else
4871 mFrameConstructor->RestyleForInsertOrChange(element, aContent);
4874 mFrameConstructor->CharacterDataChanged(aContent, aInfo);
4875 VERIFY_STYLE_TREE;
4878 void
4879 PresShell::ContentStatesChanged(nsIDocument* aDocument,
4880 nsIContent* aContent1,
4881 nsIContent* aContent2,
4882 PRInt32 aStateMask)
4884 NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentStatesChanged");
4885 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4887 if (mDidInitialReflow) {
4888 nsAutoCauseReflowNotifier crNotifier(this);
4889 mFrameConstructor->ContentStatesChanged(aContent1, aContent2, aStateMask);
4890 VERIFY_STYLE_TREE;
4894 void
4895 PresShell::DocumentStatesChanged(nsIDocument* aDocument,
4896 PRInt32 aStateMask)
4898 NS_PRECONDITION(!mIsDocumentGone, "Unexpected DocumentStatesChanged");
4899 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4901 if (mDidInitialReflow &&
4902 mStyleSet->HasDocumentStateDependentStyle(mPresContext,
4903 mDocument->GetRootElement(),
4904 aStateMask)) {
4905 mFrameConstructor->PostRestyleEvent(mDocument->GetRootElement(),
4906 eRestyle_Subtree, NS_STYLE_HINT_NONE);
4907 VERIFY_STYLE_TREE;
4910 if (aStateMask & NS_DOCUMENT_STATE_WINDOW_INACTIVE) {
4911 nsIFrame* root = FrameManager()->GetRootFrame();
4912 if (root) {
4913 // It's a display root. So, invalidate the layer contents of
4914 // everything we can find. We need to do this because the contents
4915 // of controls etc can depend on whether the window is active,
4916 // and when a window becomes (in)active it just gets repainted
4917 // and we don't specifically invalidate each affected control.
4918 nsIWidget* widget = root->GetNearestWidget();
4919 if (widget) {
4920 LayerManager* layerManager = widget->GetLayerManager();
4921 if (layerManager) {
4922 FrameLayerBuilder::InvalidateAllThebesLayerContents(layerManager);
4929 void
4930 PresShell::AttributeWillChange(nsIDocument* aDocument,
4931 Element* aElement,
4932 PRInt32 aNameSpaceID,
4933 nsIAtom* aAttribute,
4934 PRInt32 aModType)
4936 NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeWillChange");
4937 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4939 // XXXwaterson it might be more elegant to wait until after the
4940 // initial reflow to begin observing the document. That would
4941 // squelch any other inappropriate notifications as well.
4942 if (mDidInitialReflow) {
4943 nsAutoCauseReflowNotifier crNotifier(this);
4944 mFrameConstructor->AttributeWillChange(aElement, aNameSpaceID,
4945 aAttribute, aModType);
4946 VERIFY_STYLE_TREE;
4950 void
4951 PresShell::AttributeChanged(nsIDocument* aDocument,
4952 Element* aElement,
4953 PRInt32 aNameSpaceID,
4954 nsIAtom* aAttribute,
4955 PRInt32 aModType)
4957 NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeChanged");
4958 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4960 // XXXwaterson it might be more elegant to wait until after the
4961 // initial reflow to begin observing the document. That would
4962 // squelch any other inappropriate notifications as well.
4963 if (mDidInitialReflow) {
4964 nsAutoCauseReflowNotifier crNotifier(this);
4965 mFrameConstructor->AttributeChanged(aElement, aNameSpaceID,
4966 aAttribute, aModType);
4967 VERIFY_STYLE_TREE;
4971 void
4972 PresShell::ContentAppended(nsIDocument *aDocument,
4973 nsIContent* aContainer,
4974 nsIContent* aFirstNewContent,
4975 PRInt32 aNewIndexInContainer)
4977 NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentAppended");
4978 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4979 NS_PRECONDITION(aContainer, "must have container");
4981 if (!mDidInitialReflow) {
4982 return;
4985 nsAutoCauseReflowNotifier crNotifier(this);
4987 // Call this here so it only happens for real content mutations and
4988 // not cases when the frame constructor calls its own methods to force
4989 // frame reconstruction.
4990 mFrameConstructor->RestyleForAppend(aContainer->AsElement(), aFirstNewContent);
4992 mFrameConstructor->ContentAppended(aContainer, aFirstNewContent, PR_TRUE);
4993 VERIFY_STYLE_TREE;
4996 void
4997 PresShell::ContentInserted(nsIDocument* aDocument,
4998 nsIContent* aContainer,
4999 nsIContent* aChild,
5000 PRInt32 aIndexInContainer)
5002 NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentInserted");
5003 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
5005 if (!mDidInitialReflow) {
5006 return;
5009 nsAutoCauseReflowNotifier crNotifier(this);
5011 // Call this here so it only happens for real content mutations and
5012 // not cases when the frame constructor calls its own methods to force
5013 // frame reconstruction.
5014 if (aContainer)
5015 mFrameConstructor->RestyleForInsertOrChange(aContainer->AsElement(), aChild);
5017 mFrameConstructor->ContentInserted(aContainer, aChild, nsnull, PR_TRUE);
5018 VERIFY_STYLE_TREE;
5021 void
5022 PresShell::ContentRemoved(nsIDocument *aDocument,
5023 nsIContent* aContainer,
5024 nsIContent* aChild,
5025 PRInt32 aIndexInContainer,
5026 nsIContent* aPreviousSibling)
5028 NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentRemoved");
5029 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
5031 // Make sure that the caret doesn't leave a turd where the child used to be.
5032 if (mCaret) {
5033 mCaret->InvalidateOutsideCaret();
5036 // Notify the ESM that the content has been removed, so that
5037 // it can clean up any state related to the content.
5038 mPresContext->EventStateManager()->ContentRemoved(aDocument, aChild);
5040 nsAutoCauseReflowNotifier crNotifier(this);
5042 // Call this here so it only happens for real content mutations and
5043 // not cases when the frame constructor calls its own methods to force
5044 // frame reconstruction.
5045 nsIContent* oldNextSibling;
5046 if (aContainer) {
5047 oldNextSibling = aContainer->GetChildAt(aIndexInContainer);
5048 } else {
5049 oldNextSibling = nsnull;
5052 if (aContainer)
5053 mFrameConstructor->RestyleForRemove(aContainer->AsElement(), aChild,
5054 oldNextSibling);
5056 PRBool didReconstruct;
5057 mFrameConstructor->ContentRemoved(aContainer, aChild, oldNextSibling,
5058 nsCSSFrameConstructor::REMOVE_CONTENT,
5059 &didReconstruct);
5061 VERIFY_STYLE_TREE;
5064 nsresult
5065 PresShell::ReconstructFrames(void)
5067 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
5069 // Have to make sure that the content notifications are flushed before we
5070 // start messing with the frame model; otherwise we can get content doubling.
5071 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
5073 nsAutoCauseReflowNotifier crNotifier(this);
5074 mFrameConstructor->BeginUpdate();
5075 nsresult rv = mFrameConstructor->ReconstructDocElementHierarchy();
5076 VERIFY_STYLE_TREE;
5077 mFrameConstructor->EndUpdate();
5079 return rv;
5082 void
5083 nsIPresShell::ReconstructStyleDataInternal()
5085 mStylesHaveChanged = PR_FALSE;
5087 if (mIsDestroying) {
5088 // We don't want to mess with restyles at this point
5089 return;
5092 if (mPresContext) {
5093 mPresContext->RebuildUserFontSet();
5096 Element* root = mDocument->GetRootElement();
5097 if (!mDidInitialReflow) {
5098 // Nothing to do here, since we have no frames yet
5099 return;
5102 if (!root) {
5103 // No content to restyle
5104 return;
5107 mFrameConstructor->PostRestyleEvent(root, eRestyle_Subtree, NS_STYLE_HINT_NONE);
5109 #ifdef ACCESSIBILITY
5110 InvalidateAccessibleSubtree(nsnull);
5111 #endif
5114 void
5115 nsIPresShell::ReconstructStyleDataExternal()
5117 ReconstructStyleDataInternal();
5120 void
5121 PresShell::StyleSheetAdded(nsIDocument *aDocument,
5122 nsIStyleSheet* aStyleSheet,
5123 PRBool aDocumentSheet)
5125 // We only care when enabled sheets are added
5126 NS_PRECONDITION(aStyleSheet, "Must have a style sheet!");
5128 if (aStyleSheet->IsApplicable() && aStyleSheet->HasRules()) {
5129 mStylesHaveChanged = PR_TRUE;
5133 void
5134 PresShell::StyleSheetRemoved(nsIDocument *aDocument,
5135 nsIStyleSheet* aStyleSheet,
5136 PRBool aDocumentSheet)
5138 // We only care when enabled sheets are removed
5139 NS_PRECONDITION(aStyleSheet, "Must have a style sheet!");
5141 if (aStyleSheet->IsApplicable() && aStyleSheet->HasRules()) {
5142 mStylesHaveChanged = PR_TRUE;
5146 void
5147 PresShell::StyleSheetApplicableStateChanged(nsIDocument *aDocument,
5148 nsIStyleSheet* aStyleSheet,
5149 PRBool aApplicable)
5151 if (aStyleSheet->HasRules()) {
5152 mStylesHaveChanged = PR_TRUE;
5156 void
5157 PresShell::StyleRuleChanged(nsIDocument *aDocument,
5158 nsIStyleSheet* aStyleSheet,
5159 nsIStyleRule* aOldStyleRule,
5160 nsIStyleRule* aNewStyleRule)
5162 mStylesHaveChanged = PR_TRUE;
5165 void
5166 PresShell::StyleRuleAdded(nsIDocument *aDocument,
5167 nsIStyleSheet* aStyleSheet,
5168 nsIStyleRule* aStyleRule)
5170 mStylesHaveChanged = PR_TRUE;
5173 void
5174 PresShell::StyleRuleRemoved(nsIDocument *aDocument,
5175 nsIStyleSheet* aStyleSheet,
5176 nsIStyleRule* aStyleRule)
5178 mStylesHaveChanged = PR_TRUE;
5181 nsIFrame*
5182 PresShell::GetRealPrimaryFrameFor(nsIContent* aContent) const
5184 if (aContent->GetDocument() != GetDocument()) {
5185 return nsnull;
5187 nsIFrame *primaryFrame = aContent->GetPrimaryFrame();
5188 if (!primaryFrame)
5189 return nsnull;
5190 return nsPlaceholderFrame::GetRealFrameFor(primaryFrame);
5193 nsIFrame*
5194 PresShell::GetPlaceholderFrameFor(nsIFrame* aFrame) const
5196 return FrameManager()->GetPlaceholderFrameFor(aFrame);
5199 nsresult
5200 PresShell::RenderDocument(const nsRect& aRect, PRUint32 aFlags,
5201 nscolor aBackgroundColor,
5202 gfxContext* aThebesContext)
5204 NS_TIME_FUNCTION_WITH_DOCURL;
5206 NS_ENSURE_TRUE(!(aFlags & RENDER_IS_UNTRUSTED), NS_ERROR_NOT_IMPLEMENTED);
5208 // Set up the rectangle as the path in aThebesContext
5209 gfxRect r(0, 0,
5210 nsPresContext::AppUnitsToFloatCSSPixels(aRect.width),
5211 nsPresContext::AppUnitsToFloatCSSPixels(aRect.height));
5212 aThebesContext->NewPath();
5213 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
5214 aThebesContext->Rectangle(r, PR_TRUE);
5215 #else
5216 aThebesContext->Rectangle(r);
5217 #endif
5219 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
5220 if (!rootFrame) {
5221 // Nothing to paint, just fill the rect
5222 aThebesContext->SetColor(gfxRGBA(aBackgroundColor));
5223 aThebesContext->Fill();
5224 return NS_OK;
5227 gfxContextAutoSaveRestore save(aThebesContext);
5229 gfxContext::GraphicsOperator oldOperator = aThebesContext->CurrentOperator();
5230 if (oldOperator == gfxContext::OPERATOR_OVER) {
5231 // Clip to the destination rectangle before we push the group,
5232 // to limit the size of the temporary surface
5233 aThebesContext->Clip();
5236 // we want the window to be composited as a single image using
5237 // whatever operator was set; set OPERATOR_OVER here, which is
5238 // either already the case, or overrides the operator in a group.
5239 // the original operator will be present when we PopGroup.
5240 // we can avoid using a temporary surface if we're using OPERATOR_OVER
5241 // and our background color has no alpha (so we'll be compositing on top
5242 // of a fully opaque solid color region)
5243 PRBool needsGroup = NS_GET_A(aBackgroundColor) < 0xff ||
5244 oldOperator != gfxContext::OPERATOR_OVER;
5246 if (needsGroup) {
5247 aThebesContext->PushGroup(NS_GET_A(aBackgroundColor) == 0xff ?
5248 gfxASurface::CONTENT_COLOR :
5249 gfxASurface::CONTENT_COLOR_ALPHA);
5250 aThebesContext->Save();
5252 if (oldOperator != gfxContext::OPERATOR_OVER) {
5253 // Clip now while we paint to the temporary surface. For
5254 // non-source-bounded operators (e.g., SOURCE), we need to do clip
5255 // here after we've pushed the group, so that eventually popping
5256 // the group and painting it will be able to clear the entire
5257 // destination surface.
5258 aThebesContext->Clip();
5259 aThebesContext->SetOperator(gfxContext::OPERATOR_OVER);
5263 aThebesContext->Translate(gfxPoint(-nsPresContext::AppUnitsToFloatCSSPixels(aRect.x),
5264 -nsPresContext::AppUnitsToFloatCSSPixels(aRect.y)));
5266 nsIDeviceContext* devCtx = mPresContext->DeviceContext();
5267 gfxFloat scale = gfxFloat(devCtx->AppUnitsPerDevPixel())/nsPresContext::AppUnitsPerCSSPixel();
5268 aThebesContext->Scale(scale, scale);
5270 // Since canvas APIs use floats to set up their matrices, we may have
5271 // some slight inaccuracy here. Adjust matrix components that are
5272 // integers up to the accuracy of floats to be those integers.
5273 aThebesContext->NudgeCurrentMatrixToIntegers();
5275 nsCOMPtr<nsIRenderingContext> rc;
5276 devCtx->CreateRenderingContextInstance(*getter_AddRefs(rc));
5277 rc->Init(devCtx, aThebesContext);
5279 PRUint32 flags = nsLayoutUtils::PAINT_IGNORE_SUPPRESSION;
5280 if (!(aFlags & RENDER_ASYNC_DECODE_IMAGES)) {
5281 flags |= nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES;
5283 if (aFlags & RENDER_USE_WIDGET_LAYERS) {
5284 // We only support using widget layers on display root's with widgets.
5285 nsIView* view = rootFrame->GetView();
5286 if (view && view->GetWidget() &&
5287 nsLayoutUtils::GetDisplayRootFrame(rootFrame) == rootFrame) {
5288 flags |= nsLayoutUtils::PAINT_WIDGET_LAYERS;
5291 if (!(aFlags & RENDER_CARET)) {
5292 flags |= nsLayoutUtils::PAINT_HIDE_CARET;
5294 if (aFlags & RENDER_IGNORE_VIEWPORT_SCROLLING) {
5295 flags |= nsLayoutUtils::PAINT_IGNORE_VIEWPORT_SCROLLING;
5297 nsLayoutUtils::PaintFrame(rc, rootFrame, nsRegion(aRect),
5298 aBackgroundColor, flags);
5300 // if we had to use a group, paint it to the destination now
5301 if (needsGroup) {
5302 aThebesContext->Restore();
5303 aThebesContext->PopGroupToSource();
5304 aThebesContext->Paint();
5307 return NS_OK;
5311 * Clip the display list aList to a range. Returns the clipped
5312 * rectangle surrounding the range.
5314 nsRect
5315 PresShell::ClipListToRange(nsDisplayListBuilder *aBuilder,
5316 nsDisplayList* aList,
5317 nsIRange* aRange)
5319 NS_TIME_FUNCTION_WITH_DOCURL;
5321 // iterate though the display items and add up the bounding boxes of each.
5322 // This will allow the total area of the frames within the range to be
5323 // determined. To do this, remove an item from the bottom of the list, check
5324 // whether it should be part of the range, and if so, append it to the top
5325 // of the temporary list tmpList. If the item is a text frame at the end of
5326 // the selection range, wrap it in an nsDisplayClip to clip the display to
5327 // the portion of the text frame that is part of the selection. Then, append
5328 // the wrapper to the top of the list. Otherwise, just delete the item and
5329 // don't append it.
5330 nsRect surfaceRect;
5331 nsDisplayList tmpList;
5333 nsDisplayItem* i;
5334 while ((i = aList->RemoveBottom())) {
5335 // itemToInsert indiciates the item that should be inserted into the
5336 // temporary list. If null, no item should be inserted.
5337 nsDisplayItem* itemToInsert = nsnull;
5338 nsIFrame* frame = i->GetUnderlyingFrame();
5339 if (frame) {
5340 nsIContent* content = frame->GetContent();
5341 if (content) {
5342 PRBool atStart = (content == aRange->GetStartParent());
5343 PRBool atEnd = (content == aRange->GetEndParent());
5344 if ((atStart || atEnd) && frame->GetType() == nsGkAtoms::textFrame) {
5345 PRInt32 frameStartOffset, frameEndOffset;
5346 frame->GetOffsets(frameStartOffset, frameEndOffset);
5348 PRInt32 hilightStart =
5349 atStart ? NS_MAX(aRange->StartOffset(), frameStartOffset) : frameStartOffset;
5350 PRInt32 hilightEnd =
5351 atEnd ? NS_MIN(aRange->EndOffset(), frameEndOffset) : frameEndOffset;
5352 if (hilightStart < hilightEnd) {
5353 // determine the location of the start and end edges of the range.
5354 nsPoint startPoint, endPoint;
5355 frame->GetPointFromOffset(hilightStart, &startPoint);
5356 frame->GetPointFromOffset(hilightEnd, &endPoint);
5358 // the clip rectangle is determined by taking the the start and
5359 // end points of the range, offset from the reference frame.
5360 // Because of rtl, the end point may be to the left of the
5361 // start point, so x is set to the lowest value
5362 nsRect textRect(aBuilder->ToReferenceFrame(frame), frame->GetSize());
5363 nscoord x = NS_MIN(startPoint.x, endPoint.x);
5364 textRect.x += x;
5365 textRect.width = NS_MAX(startPoint.x, endPoint.x) - x;
5366 surfaceRect.UnionRect(surfaceRect, textRect);
5368 // wrap the item in an nsDisplayClip so that it can be clipped to
5369 // the selection. If the allocation fails, fall through and delete
5370 // the item below.
5371 itemToInsert = new (aBuilder)
5372 nsDisplayClip(aBuilder, frame, frame, i, textRect);
5375 // Don't try to descend into subdocuments.
5376 // If this ever changes we'd need to add handling for subdocuments with
5377 // different zoom levels.
5378 else if (content->GetCurrentDoc() ==
5379 aRange->GetStartParent()->GetCurrentDoc()) {
5380 // if the node is within the range, append it to the temporary list
5381 PRBool before, after;
5382 nsresult rv =
5383 nsRange::CompareNodeToRange(content, aRange, &before, &after);
5384 if (NS_SUCCEEDED(rv) && !before && !after) {
5385 itemToInsert = i;
5386 surfaceRect.UnionRect(surfaceRect, i->GetBounds(aBuilder));
5392 // insert the item into the list if necessary. If the item has a child
5393 // list, insert that as well
5394 nsDisplayList* sublist = i->GetList();
5395 if (itemToInsert || sublist) {
5396 tmpList.AppendToTop(itemToInsert ? itemToInsert : i);
5397 // if the item is a list, iterate over it as well
5398 if (sublist)
5399 surfaceRect.UnionRect(surfaceRect,
5400 ClipListToRange(aBuilder, sublist, aRange));
5402 else {
5403 // otherwise, just delete the item and don't readd it to the list
5404 i->~nsDisplayItem();
5408 // now add all the items back onto the original list again
5409 aList->AppendToTop(&tmpList);
5411 return surfaceRect;
5414 #ifdef DEBUG
5415 #include <stdio.h>
5417 static PRBool gDumpRangePaintList = PR_FALSE;
5418 #endif
5420 RangePaintInfo*
5421 PresShell::CreateRangePaintInfo(nsIDOMRange* aRange,
5422 nsRect& aSurfaceRect,
5423 PRBool aForPrimarySelection)
5425 NS_TIME_FUNCTION_WITH_DOCURL;
5427 RangePaintInfo* info = nsnull;
5429 nsCOMPtr<nsIRange> range = do_QueryInterface(aRange);
5430 if (!range)
5431 return nsnull;
5433 nsIFrame* ancestorFrame;
5434 nsIFrame* rootFrame = GetRootFrame();
5436 // If the start or end of the range is the document, just use the root
5437 // frame, otherwise get the common ancestor of the two endpoints of the
5438 // range.
5439 nsINode* startParent = range->GetStartParent();
5440 nsINode* endParent = range->GetEndParent();
5441 nsIDocument* doc = startParent->GetCurrentDoc();
5442 if (startParent == doc || endParent == doc) {
5443 ancestorFrame = rootFrame;
5445 else {
5446 nsINode* ancestor = nsContentUtils::GetCommonAncestor(startParent, endParent);
5447 NS_ASSERTION(!ancestor || ancestor->IsNodeOfType(nsINode::eCONTENT),
5448 "common ancestor is not content");
5449 if (!ancestor || !ancestor->IsNodeOfType(nsINode::eCONTENT))
5450 return nsnull;
5452 nsIContent* ancestorContent = static_cast<nsIContent*>(ancestor);
5453 ancestorFrame = ancestorContent->GetPrimaryFrame();
5455 // use the nearest ancestor frame that includes all continuations as the
5456 // root for building the display list
5457 while (ancestorFrame &&
5458 nsLayoutUtils::GetNextContinuationOrSpecialSibling(ancestorFrame))
5459 ancestorFrame = ancestorFrame->GetParent();
5462 if (!ancestorFrame)
5463 return nsnull;
5465 info = new RangePaintInfo(range, ancestorFrame);
5466 if (!info)
5467 return nsnull;
5469 nsRect ancestorRect = ancestorFrame->GetOverflowRect();
5471 // get a display list containing the range
5472 if (aForPrimarySelection) {
5473 info->mBuilder.SetSelectedFramesOnly();
5475 info->mBuilder.EnterPresShell(ancestorFrame, ancestorRect);
5476 ancestorFrame->BuildDisplayListForStackingContext(&info->mBuilder,
5477 ancestorRect, &info->mList);
5478 info->mBuilder.LeavePresShell(ancestorFrame, ancestorRect);
5480 #ifdef DEBUG
5481 if (gDumpRangePaintList) {
5482 fprintf(stderr, "CreateRangePaintInfo --- before ClipListToRange:\n");
5483 nsFrame::PrintDisplayList(&(info->mBuilder), info->mList);
5485 #endif
5487 nsRect rangeRect = ClipListToRange(&info->mBuilder, &info->mList, range);
5489 #ifdef DEBUG
5490 if (gDumpRangePaintList) {
5491 fprintf(stderr, "CreateRangePaintInfo --- after ClipListToRange:\n");
5492 nsFrame::PrintDisplayList(&(info->mBuilder), info->mList);
5494 #endif
5496 // determine the offset of the reference frame for the display list
5497 // to the root frame. This will allow the coordinates used when painting
5498 // to all be offset from the same point
5499 info->mRootOffset = ancestorFrame->GetOffsetTo(rootFrame);
5500 rangeRect.MoveBy(info->mRootOffset);
5501 aSurfaceRect.UnionRect(aSurfaceRect, rangeRect);
5503 return info;
5506 already_AddRefed<gfxASurface>
5507 PresShell::PaintRangePaintInfo(nsTArray<nsAutoPtr<RangePaintInfo> >* aItems,
5508 nsISelection* aSelection,
5509 nsIntRegion* aRegion,
5510 nsRect aArea,
5511 nsIntPoint& aPoint,
5512 nsIntRect* aScreenRect)
5514 NS_TIME_FUNCTION_WITH_DOCURL;
5516 nsPresContext* pc = GetPresContext();
5517 if (!pc || aArea.width == 0 || aArea.height == 0)
5518 return nsnull;
5520 nsIDeviceContext* deviceContext = pc->DeviceContext();
5522 // use the rectangle to create the surface
5523 nsIntRect pixelArea = aArea.ToOutsidePixels(pc->AppUnitsPerDevPixel());
5525 // if the area of the image is larger than the maximum area, scale it down
5526 float scale = 0.0;
5527 nsIntRect rootScreenRect =
5528 GetRootFrame()->GetScreenRectInAppUnits().ToNearestPixels(
5529 pc->AppUnitsPerDevPixel());
5531 // if the image is larger in one or both directions than half the size of
5532 // the available screen area, scale the image down to that size.
5533 nsRect maxSize;
5534 deviceContext->GetClientRect(maxSize);
5535 nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1);
5536 nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1);
5537 PRBool resize = (pixelArea.width > maxWidth || pixelArea.height > maxHeight);
5538 if (resize) {
5539 scale = 1.0;
5540 // divide the maximum size by the image size in both directions. Whichever
5541 // direction produces the smallest result determines how much should be
5542 // scaled.
5543 if (pixelArea.width > maxWidth)
5544 scale = NS_MIN(scale, float(maxWidth) / pixelArea.width);
5545 if (pixelArea.height > maxHeight)
5546 scale = NS_MIN(scale, float(maxHeight) / pixelArea.height);
5548 pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale);
5549 pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale);
5551 // adjust the screen position based on the rescaled size
5552 nscoord left = rootScreenRect.x + pixelArea.x;
5553 nscoord top = rootScreenRect.y + pixelArea.y;
5554 aScreenRect->x = NSToIntFloor(aPoint.x - float(aPoint.x - left) * scale);
5555 aScreenRect->y = NSToIntFloor(aPoint.y - float(aPoint.y - top) * scale);
5557 else {
5558 // move aScreenRect to the position of the surface in screen coordinates
5559 aScreenRect->MoveTo(rootScreenRect.x + pixelArea.x, rootScreenRect.y + pixelArea.y);
5561 aScreenRect->width = pixelArea.width;
5562 aScreenRect->height = pixelArea.height;
5564 gfxImageSurface* surface =
5565 new gfxImageSurface(gfxIntSize(pixelArea.width, pixelArea.height),
5566 gfxImageSurface::ImageFormatARGB32);
5567 if (!surface || surface->CairoStatus()) {
5568 delete surface;
5569 return nsnull;
5572 // clear the image
5573 gfxContext context(surface);
5574 context.SetOperator(gfxContext::OPERATOR_CLEAR);
5575 context.Rectangle(gfxRect(0, 0, pixelArea.width, pixelArea.height));
5576 context.Fill();
5578 nsCOMPtr<nsIRenderingContext> rc;
5579 deviceContext->CreateRenderingContextInstance(*getter_AddRefs(rc));
5580 rc->Init(deviceContext, surface);
5582 if (aRegion) {
5583 // Convert aRegion from CSS pixels to dev pixels
5584 nsIntRegion region =
5585 aRegion->ToAppUnits(nsPresContext::AppUnitsPerCSSPixel())
5586 .ToOutsidePixels(pc->AppUnitsPerDevPixel());
5587 rc->SetClipRegion(region, nsClipCombine_kReplace);
5590 if (resize)
5591 rc->Scale(scale, scale);
5593 // translate so that points are relative to the surface area
5594 rc->Translate(-aArea.x, -aArea.y);
5596 // temporarily hide the selection so that text is drawn normally. If a
5597 // selection is being rendered, use that, otherwise use the presshell's
5598 // selection.
5599 nsCOMPtr<nsFrameSelection> frameSelection;
5600 if (aSelection) {
5601 nsCOMPtr<nsISelectionPrivate> selpriv = do_QueryInterface(aSelection);
5602 selpriv->GetFrameSelection(getter_AddRefs(frameSelection));
5604 else {
5605 frameSelection = FrameSelection();
5607 PRInt16 oldDisplaySelection = frameSelection->GetDisplaySelection();
5608 frameSelection->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
5610 // next, paint each range in the selection
5611 PRInt32 count = aItems->Length();
5612 for (PRInt32 i = 0; i < count; i++) {
5613 RangePaintInfo* rangeInfo = (*aItems)[i];
5614 // the display lists paint relative to the offset from the reference
5615 // frame, so translate the rendering context
5616 nsIRenderingContext::AutoPushTranslation
5617 translate(rc, rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y);
5619 aArea.MoveBy(-rangeInfo->mRootOffset.x, -rangeInfo->mRootOffset.y);
5620 nsRegion visible(aArea);
5621 rangeInfo->mList.ComputeVisibility(&rangeInfo->mBuilder, &visible);
5622 rangeInfo->mList.PaintRoot(&rangeInfo->mBuilder, rc, nsDisplayList::PAINT_DEFAULT);
5623 aArea.MoveBy(rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y);
5626 // restore the old selection display state
5627 frameSelection->SetDisplaySelection(oldDisplaySelection);
5629 NS_ADDREF(surface);
5630 return surface;
5633 already_AddRefed<gfxASurface>
5634 PresShell::RenderNode(nsIDOMNode* aNode,
5635 nsIntRegion* aRegion,
5636 nsIntPoint& aPoint,
5637 nsIntRect* aScreenRect)
5639 // area will hold the size of the surface needed to draw the node, measured
5640 // from the root frame.
5641 nsRect area;
5642 nsTArray<nsAutoPtr<RangePaintInfo> > rangeItems;
5644 // nothing to draw if the node isn't in a document
5645 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
5646 if (!node->IsInDoc())
5647 return nsnull;
5649 nsCOMPtr<nsIDOMRange> range;
5650 NS_NewRange(getter_AddRefs(range));
5651 if (NS_FAILED(range->SelectNode(aNode)))
5652 return nsnull;
5654 RangePaintInfo* info = CreateRangePaintInfo(range, area, PR_FALSE);
5655 if (info && !rangeItems.AppendElement(info)) {
5656 delete info;
5657 return nsnull;
5660 if (aRegion) {
5661 // combine the area with the supplied region
5662 nsIntRect rrectPixels = aRegion->GetBounds();
5664 nsRect rrect = rrectPixels.ToAppUnits(nsPresContext::AppUnitsPerCSSPixel());
5665 area.IntersectRect(area, rrect);
5667 nsPresContext* pc = GetPresContext();
5668 if (!pc)
5669 return nsnull;
5671 // move the region so that it is offset from the topleft corner of the surface
5672 aRegion->MoveBy(-pc->AppUnitsToDevPixels(area.x),
5673 -pc->AppUnitsToDevPixels(area.y));
5676 return PaintRangePaintInfo(&rangeItems, nsnull, aRegion, area, aPoint,
5677 aScreenRect);
5680 already_AddRefed<gfxASurface>
5681 PresShell::RenderSelection(nsISelection* aSelection,
5682 nsIntPoint& aPoint,
5683 nsIntRect* aScreenRect)
5685 // area will hold the size of the surface needed to draw the selection,
5686 // measured from the root frame.
5687 nsRect area;
5688 nsTArray<nsAutoPtr<RangePaintInfo> > rangeItems;
5690 // iterate over each range and collect them into the rangeItems array.
5691 // This is done so that the size of selection can be determined so as
5692 // to allocate a surface area
5693 PRInt32 numRanges;
5694 aSelection->GetRangeCount(&numRanges);
5695 NS_ASSERTION(numRanges > 0, "RenderSelection called with no selection");
5697 for (PRInt32 r = 0; r < numRanges; r++)
5699 nsCOMPtr<nsIDOMRange> range;
5700 aSelection->GetRangeAt(r, getter_AddRefs(range));
5702 RangePaintInfo* info = CreateRangePaintInfo(range, area, PR_TRUE);
5703 if (info && !rangeItems.AppendElement(info)) {
5704 delete info;
5705 return nsnull;
5709 return PaintRangePaintInfo(&rangeItems, aSelection, nsnull, area, aPoint,
5710 aScreenRect);
5713 nsresult
5714 PresShell::AddPrintPreviewBackgroundItem(nsDisplayListBuilder& aBuilder,
5715 nsDisplayList& aList,
5716 nsIFrame* aFrame,
5717 const nsRect& aBounds)
5719 return aList.AppendNewToBottom(new (&aBuilder)
5720 nsDisplaySolidColor(&aBuilder, aFrame, aBounds, NS_RGB(115, 115, 115)));
5723 static PRBool
5724 AddCanvasBackgroundColor(const nsDisplayList& aList, nsIFrame* aCanvasFrame,
5725 nscolor aColor)
5727 for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
5728 if (i->GetUnderlyingFrame() == aCanvasFrame &&
5729 i->GetType() == nsDisplayItem::TYPE_CANVAS_BACKGROUND) {
5730 nsDisplayCanvasBackground* bg = static_cast<nsDisplayCanvasBackground*>(i);
5731 bg->SetExtraBackgroundColor(aColor);
5732 return PR_TRUE;
5734 nsDisplayList* sublist = i->GetList();
5735 if (sublist && AddCanvasBackgroundColor(*sublist, aCanvasFrame, aColor))
5736 return PR_TRUE;
5738 return PR_FALSE;
5741 nsresult PresShell::AddCanvasBackgroundColorItem(nsDisplayListBuilder& aBuilder,
5742 nsDisplayList& aList,
5743 nsIFrame* aFrame,
5744 const nsRect& aBounds,
5745 nscolor aBackstopColor,
5746 PRBool aForceDraw)
5748 // We don't want to add an item for the canvas background color if the frame
5749 // (sub)tree we are painting doesn't include any canvas frames. There isn't
5750 // an easy way to check this directly, but if we check if the root of the
5751 // (sub)tree we are painting is a canvas frame that should cover us in all
5752 // cases (it will usually be a viewport frame when we have a canvas frame in
5753 // the (sub)tree).
5754 if (!aForceDraw && !nsCSSRendering::IsCanvasFrame(aFrame))
5755 return NS_OK;
5757 nscolor bgcolor = NS_ComposeColors(aBackstopColor, mCanvasBackgroundColor);
5759 // To make layers work better, we want to avoid having a big non-scrolled
5760 // color background behind a scrolled transparent background. Instead,
5761 // we'll try to move the color background into the scrolled content
5762 // by making nsDisplayCanvasBackground paint it.
5763 if (!aFrame->GetParent()) {
5764 nsIScrollableFrame* sf =
5765 aFrame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable();
5766 if (sf) {
5767 nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
5768 if (canvasFrame && canvasFrame->IsVisibleForPainting(&aBuilder)) {
5769 if (AddCanvasBackgroundColor(aList, canvasFrame, bgcolor))
5770 return NS_OK;
5775 return aList.AppendNewToBottom(
5776 new (&aBuilder) nsDisplaySolidColor(&aBuilder, aFrame, aBounds, bgcolor));
5779 void PresShell::UpdateCanvasBackground()
5781 // If we have a frame tree and it has style information that
5782 // specifies the background color of the canvas, update our local
5783 // cache of that color.
5784 nsIFrame* rootFrame = FrameConstructor()->GetRootElementStyleFrame();
5785 if (rootFrame) {
5786 nsStyleContext* bgStyle =
5787 nsCSSRendering::FindRootFrameBackground(rootFrame);
5788 // XXX We should really be passing the canvasframe, not the root element
5789 // style frame but we don't have access to the canvasframe here. It isn't
5790 // a problem because only a few frames can return something other than true
5791 // and none of them would be a canvas frame or root element style frame.
5792 mCanvasBackgroundColor =
5793 nsCSSRendering::DetermineBackgroundColor(GetPresContext(), bgStyle,
5794 rootFrame);
5797 // If the root element of the document (ie html) has style 'display: none'
5798 // then the document's background color does not get drawn; cache the
5799 // color we actually draw.
5800 if (!FrameConstructor()->GetRootElementFrame()) {
5801 mCanvasBackgroundColor = mPresContext->DefaultBackgroundColor();
5805 nscolor PresShell::ComputeBackstopColor(nsIView* aDisplayRoot)
5807 nsIWidget* widget = aDisplayRoot->GetWidget();
5808 if (widget && widget->GetTransparencyMode() != eTransparencyOpaque) {
5809 // Within a transparent widget, so the backstop color must be
5810 // totally transparent.
5811 return NS_RGBA(0,0,0,0);
5813 // Within an opaque widget (or no widget at all), so the backstop
5814 // color must be totally opaque. The user's default background
5815 // as reported by the prescontext is guaranteed to be opaque.
5816 return GetPresContext()->DefaultBackgroundColor();
5819 struct PaintParams {
5820 nsIFrame* mFrame;
5821 nsPoint mOffsetToWidget;
5822 const nsRegion* mDirtyRegion;
5823 nscolor mBackgroundColor;
5826 LayerManager* PresShell::GetLayerManager()
5828 NS_ASSERTION(mViewManager, "Should have view manager");
5830 nsIView* rootView;
5831 if (NS_SUCCEEDED(mViewManager->GetRootView(rootView)) && rootView) {
5832 if (nsIWidget* widget = rootView->GetWidget()) {
5833 return widget->GetLayerManager();
5836 return nsnull;
5839 static void DrawThebesLayer(ThebesLayer* aLayer,
5840 gfxContext* aContext,
5841 const nsIntRegion& aRegionToDraw,
5842 const nsIntRegion& aRegionToInvalidate,
5843 void* aCallbackData)
5845 PaintParams* params = static_cast<PaintParams*>(aCallbackData);
5846 nsIFrame* frame = params->mFrame;
5847 if (frame) {
5848 // We're drawing into a child window.
5849 nsIDeviceContext* devCtx = frame->PresContext()->DeviceContext();
5850 nsCOMPtr<nsIRenderingContext> rc;
5851 nsresult rv = devCtx->CreateRenderingContextInstance(*getter_AddRefs(rc));
5852 if (NS_SUCCEEDED(rv)) {
5853 rc->Init(devCtx, aContext);
5854 nsIRenderingContext::AutoPushTranslation
5855 push(rc, params->mOffsetToWidget.x, params->mOffsetToWidget.y);
5856 nsLayoutUtils::PaintFrame(rc, frame, *params->mDirtyRegion,
5857 params->mBackgroundColor,
5858 nsLayoutUtils::PAINT_WIDGET_LAYERS);
5860 } else {
5861 aContext->NewPath();
5862 aContext->SetColor(gfxRGBA(params->mBackgroundColor));
5863 nsIntRect dirtyRect = aRegionToDraw.GetBounds();
5864 aContext->Rectangle(
5865 gfxRect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
5866 aContext->Fill();
5870 NS_IMETHODIMP
5871 PresShell::Paint(nsIView* aDisplayRoot,
5872 nsIView* aViewToPaint,
5873 nsIWidget* aWidgetToPaint,
5874 const nsRegion& aDirtyRegion,
5875 const nsIntRegion& aIntDirtyRegion,
5876 PRBool aPaintDefaultBackground,
5877 PRBool aWillSendDidPaint)
5879 #ifdef NS_FUNCTION_TIMER
5880 NS_TIME_FUNCTION_DECLARE_DOCURL;
5881 const nsRect& bounds__ = aDirtyRegion.GetBounds();
5882 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (document: %s, dirty rect: (<%f, %f>, <%f, %f>)",
5883 MOZ_FUNCTION_NAME, __LINE__, docURL__.get(),
5884 NSCoordToFloat(bounds__.x),
5885 NSCoordToFloat(bounds__.y),
5886 NSCoordToFloat(bounds__.XMost()),
5887 NSCoordToFloat(bounds__.YMost()));
5888 #endif
5890 nsPresContext* presContext = GetPresContext();
5891 AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
5893 NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
5894 NS_ASSERTION(aDisplayRoot, "null view");
5895 NS_ASSERTION(aViewToPaint, "null view");
5896 NS_ASSERTION(aWidgetToPaint, "Can't paint without a widget");
5898 nscolor bgcolor = ComputeBackstopColor(aDisplayRoot);
5900 nsIFrame* frame = aPaintDefaultBackground
5901 ? nsnull : static_cast<nsIFrame*>(aDisplayRoot->GetClientData());
5903 if (frame && aViewToPaint == aDisplayRoot) {
5904 // Defer invalidates that are triggered during painting, and discard
5905 // invalidates of areas that are already being repainted.
5906 // The layer system can trigger invalidates during painting
5907 // (see FrameLayerBuilder).
5908 frame->BeginDeferringInvalidatesForDisplayRoot(aDirtyRegion);
5910 // We can paint directly into the widget using its layer manager.
5911 // When we get rid of child widgets, this will be the only path we
5912 // need. (aPaintDefaultBackground will never be needed since the
5913 // chrome can always paint a default background.)
5914 nsLayoutUtils::PaintFrame(nsnull, frame, aDirtyRegion, bgcolor,
5915 nsLayoutUtils::PAINT_WIDGET_LAYERS);
5917 frame->EndDeferringInvalidatesForDisplayRoot();
5918 return NS_OK;
5921 if (frame) {
5922 // Defer invalidates that are triggered during painting, and discard
5923 // invalidates of areas that are already being repainted.
5924 frame->BeginDeferringInvalidatesForDisplayRoot(aDirtyRegion);
5927 LayerManager* layerManager = aWidgetToPaint->GetLayerManager();
5928 NS_ASSERTION(layerManager, "Must be in paint event");
5930 layerManager->BeginTransaction();
5931 nsRefPtr<ThebesLayer> root = layerManager->CreateThebesLayer();
5932 if (root) {
5933 root->SetVisibleRegion(aIntDirtyRegion);
5934 layerManager->SetRoot(root);
5936 if (!frame) {
5937 bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
5939 PaintParams params =
5940 { frame,
5941 aDisplayRoot->GetOffsetToWidget(aWidgetToPaint),
5942 &aDirtyRegion,
5943 bgcolor };
5944 layerManager->EndTransaction(DrawThebesLayer, &params);
5946 if (frame) {
5947 frame->EndDeferringInvalidatesForDisplayRoot();
5949 return NS_OK;
5952 // static
5953 void
5954 nsIPresShell::SetCapturingContent(nsIContent* aContent, PRUint8 aFlags)
5956 NS_IF_RELEASE(gCaptureInfo.mContent);
5958 // only set capturing content if allowed or the CAPTURE_IGNOREALLOWED flag
5959 // is used
5960 if ((aFlags & CAPTURE_IGNOREALLOWED) || gCaptureInfo.mAllowed) {
5961 if (aContent) {
5962 NS_ADDREF(gCaptureInfo.mContent = aContent);
5964 gCaptureInfo.mRetargetToElement = (aFlags & CAPTURE_RETARGETTOELEMENT) != 0;
5965 gCaptureInfo.mPreventDrag = (aFlags & CAPTURE_PREVENTDRAG) != 0;
5969 nsIFrame*
5970 PresShell::GetCurrentEventFrame()
5972 if (NS_UNLIKELY(mIsDestroying)) {
5973 return nsnull;
5976 if (!mCurrentEventFrame && mCurrentEventContent) {
5977 // Make sure the content still has a document reference. If not,
5978 // then we assume it is no longer in the content tree and the
5979 // frame shouldn't get an event, nor should we even assume its
5980 // safe to try and find the frame.
5981 if (mCurrentEventContent->GetDocument()) {
5982 mCurrentEventFrame = mCurrentEventContent->GetPrimaryFrame();
5986 return mCurrentEventFrame;
5989 nsIFrame*
5990 PresShell::GetEventTargetFrame()
5992 return GetCurrentEventFrame();
5995 already_AddRefed<nsIContent>
5996 PresShell::GetEventTargetContent(nsEvent* aEvent)
5998 nsIContent* content = nsnull;
6000 if (mCurrentEventContent) {
6001 content = mCurrentEventContent;
6002 NS_IF_ADDREF(content);
6003 } else {
6004 nsIFrame* currentEventFrame = GetCurrentEventFrame();
6005 if (currentEventFrame) {
6006 currentEventFrame->GetContentForEvent(mPresContext, aEvent, &content);
6007 } else {
6008 content = nsnull;
6011 return content;
6014 void
6015 PresShell::PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent)
6017 if (mCurrentEventFrame || mCurrentEventContent) {
6018 mCurrentEventFrameStack.InsertElementAt(0, mCurrentEventFrame);
6019 mCurrentEventContentStack.InsertObjectAt(mCurrentEventContent, 0);
6021 mCurrentEventFrame = aFrame;
6022 mCurrentEventContent = aContent;
6025 void
6026 PresShell::PopCurrentEventInfo()
6028 mCurrentEventFrame = nsnull;
6029 mCurrentEventContent = nsnull;
6031 if (0 != mCurrentEventFrameStack.Length()) {
6032 mCurrentEventFrame = mCurrentEventFrameStack.ElementAt(0);
6033 mCurrentEventFrameStack.RemoveElementAt(0);
6034 mCurrentEventContent = mCurrentEventContentStack.ObjectAt(0);
6035 mCurrentEventContentStack.RemoveObjectAt(0);
6039 PRBool PresShell::InZombieDocument(nsIContent *aContent)
6041 // If a content node points to a null document, or the document is not
6042 // attached to a window, then it is possibly in a zombie document,
6043 // about to be replaced by a newly loading document.
6044 // Such documents cannot handle DOM events.
6045 // It might actually be in a node not attached to any document,
6046 // in which case there is not parent presshell to retarget it to.
6047 nsIDocument *doc = aContent->GetDocument();
6048 return !doc || !doc->GetWindow();
6051 already_AddRefed<nsPIDOMWindow>
6052 PresShell::GetRootWindow()
6054 nsCOMPtr<nsPIDOMWindow> window =
6055 do_QueryInterface(mDocument->GetWindow());
6056 if (window) {
6057 nsCOMPtr<nsPIDOMWindow> rootWindow = window->GetPrivateRoot();
6058 NS_ASSERTION(rootWindow, "nsPIDOMWindow::GetPrivateRoot() returns NULL");
6059 return rootWindow.forget();
6062 // If we don't have DOM window, we're zombie, we should find the root window
6063 // with our parent shell.
6064 nsCOMPtr<nsIPresShell> parent = GetParentPresShell();
6065 NS_ENSURE_TRUE(parent, nsnull);
6066 return parent->GetRootWindow();
6069 already_AddRefed<nsIPresShell>
6070 PresShell::GetParentPresShell()
6072 NS_ENSURE_TRUE(mPresContext, nsnull);
6073 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
6074 if (!container) {
6075 container = do_QueryReferent(mForwardingContainer);
6078 // Now, find the parent pres shell and send the event there
6079 nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(container);
6080 // Might have gone away, or never been around to start with
6081 NS_ENSURE_TRUE(treeItem, nsnull);
6083 nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
6084 treeItem->GetParent(getter_AddRefs(parentTreeItem));
6085 nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentTreeItem);
6086 NS_ENSURE_TRUE(parentDocShell && treeItem != parentTreeItem, nsnull);
6088 nsIPresShell* parentPresShell = nsnull;
6089 parentDocShell->GetPresShell(&parentPresShell);
6090 return parentPresShell;
6093 nsresult
6094 PresShell::RetargetEventToParent(nsGUIEvent* aEvent,
6095 nsEventStatus* aEventStatus)
6097 // Send this events straight up to the parent pres shell.
6098 // We do this for keystroke events in zombie documents or if either a frame
6099 // or a root content is not present.
6100 // That way at least the UI key bindings can work.
6102 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
6103 nsCOMPtr<nsIPresShell> parentPresShell = GetParentPresShell();
6104 NS_ENSURE_TRUE(parentPresShell, NS_ERROR_FAILURE);
6105 nsCOMPtr<nsIViewObserver> parentViewObserver =
6106 do_QueryInterface(parentPresShell);
6107 if (!parentViewObserver) {
6108 return NS_ERROR_FAILURE;
6111 // Fake the event as though it'ss from the parent pres shell's root view.
6112 nsIView *parentRootView;
6113 parentPresShell->GetViewManager()->GetRootView(parentRootView);
6115 sDontRetargetEvents = PR_TRUE;
6116 nsresult rv = parentViewObserver->HandleEvent(parentRootView, aEvent, aEventStatus);
6117 sDontRetargetEvents = PR_FALSE;
6118 return rv;
6121 void
6122 PresShell::DisableNonTestMouseEvents(PRBool aDisable)
6124 sDisableNonTestMouseEvents = aDisable;
6127 already_AddRefed<nsPIDOMWindow>
6128 PresShell::GetFocusedDOMWindowInOurWindow()
6130 nsCOMPtr<nsPIDOMWindow> rootWindow = GetRootWindow();
6131 NS_ENSURE_TRUE(rootWindow, nsnull);
6132 nsPIDOMWindow* focusedWindow;
6133 nsFocusManager::GetFocusedDescendant(rootWindow, PR_TRUE, &focusedWindow);
6134 return focusedWindow;
6137 NS_IMETHODIMP
6138 PresShell::HandleEvent(nsIView *aView,
6139 nsGUIEvent* aEvent,
6140 nsEventStatus* aEventStatus)
6142 NS_ASSERTION(aView, "null view");
6144 if (mIsDestroying || !nsContentUtils::IsSafeToRunScript() ||
6145 (sDisableNonTestMouseEvents && NS_IS_MOUSE_EVENT(aEvent) &&
6146 !(aEvent->flags & NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT))) {
6147 return NS_OK;
6150 NS_TIME_FUNCTION_MIN(1.0);
6152 #ifdef ACCESSIBILITY
6153 if (aEvent->eventStructType == NS_ACCESSIBLE_EVENT) {
6154 // Accessibility events come through OS requests and not from scripts,
6155 // so it is safe to handle here
6156 return HandleEventInternal(aEvent, aView, aEventStatus);
6158 #endif
6160 nsIContent* capturingContent =
6161 NS_IS_MOUSE_EVENT(aEvent) ? GetCapturingContent() : nsnull;
6163 nsCOMPtr<nsIDocument> retargetEventDoc;
6164 if (!sDontRetargetEvents) {
6165 // key and IME related events should not cross top level window boundary.
6166 // Basically, such input events should be fired only on focused widget.
6167 // However, some IMEs might need to clean up composition after focused
6168 // window is deactivated. And also some tests on MozMill want to test key
6169 // handling on deactivated window because MozMill window can be activated
6170 // during tests. So, there is no merit the events should be redirected to
6171 // active window. So, the events should be handled on the last focused
6172 // content in the last focused DOM window in same top level window.
6173 // Note, if no DOM window has been focused yet, we can discard the events.
6174 if (NS_IsEventTargetedAtFocusedWindow(aEvent)) {
6175 nsCOMPtr<nsPIDOMWindow> window = GetFocusedDOMWindowInOurWindow();
6176 // No DOM window in same top level window has not been focused yet,
6177 // discard the events.
6178 if (!window) {
6179 return NS_OK;
6182 retargetEventDoc = do_QueryInterface(window->GetExtantDocument());
6183 if (!retargetEventDoc)
6184 return NS_OK;
6185 } else if (capturingContent) {
6186 // if the mouse is being captured then retarget the mouse event at the
6187 // document that is being captured.
6188 retargetEventDoc = capturingContent->GetCurrentDoc();
6191 if (retargetEventDoc) {
6192 nsIPresShell* presShell = retargetEventDoc->GetShell();
6193 if (!presShell)
6194 return NS_OK;
6196 if (presShell != this) {
6197 nsCOMPtr<nsIViewObserver> viewObserver = do_QueryInterface(presShell);
6198 if (!viewObserver)
6199 return NS_ERROR_FAILURE;
6201 nsIView *view;
6202 presShell->GetViewManager()->GetRootView(view);
6203 sDontRetargetEvents = PR_TRUE;
6204 nsresult rv = viewObserver->HandleEvent(view, aEvent, aEventStatus);
6205 sDontRetargetEvents = PR_FALSE;
6206 return rv;
6211 // Check for a theme change up front, since the frame type is irrelevant
6212 if (aEvent->message == NS_THEMECHANGED && mPresContext) {
6213 mPresContext->ThemeChanged();
6214 return NS_OK;
6217 if (aEvent->message == NS_UISTATECHANGED && mDocument) {
6218 nsPIDOMWindow* win = mDocument->GetWindow();
6219 if (win) {
6220 nsUIStateChangeEvent* event = (nsUIStateChangeEvent*)aEvent;
6221 win->SetKeyboardIndicators(event->showAccelerators, event->showFocusRings);
6223 return NS_OK;
6226 // Check for a system color change up front, since the frame type is
6227 // irrelevant
6228 if ((aEvent->message == NS_SYSCOLORCHANGED) && mPresContext) {
6229 nsIViewManager* vm = GetViewManager();
6230 if (vm) {
6231 // Only dispatch system color change when the message originates from
6232 // from the root views widget. This is necessary to prevent us from
6233 // dispatching the SysColorChanged notification for each child window
6234 // which may be redundant.
6235 nsIView *view;
6236 vm->GetRootView(view);
6237 if (view == aView) {
6238 *aEventStatus = nsEventStatus_eConsumeDoDefault;
6239 mPresContext->SysColorChanged();
6240 return NS_OK;
6243 return NS_OK;
6246 if (aEvent->eventStructType == NS_KEY_EVENT &&
6247 mDocument && mDocument->EventHandlingSuppressed()) {
6248 if (aEvent->message == NS_KEY_DOWN) {
6249 mNoDelayedKeyEvents = PR_TRUE;
6250 } else if (!mNoDelayedKeyEvents) {
6251 nsDelayedEvent* event =
6252 new nsDelayedKeyEvent(static_cast<nsKeyEvent*>(aEvent));
6253 if (event && !mDelayedEvents.AppendElement(event)) {
6254 delete event;
6257 return NS_OK;
6260 nsIFrame* frame = static_cast<nsIFrame*>(aView->GetClientData());
6261 PRBool dispatchUsingCoordinates = NS_IsEventUsingCoordinates(aEvent);
6263 // if this event has no frame, we need to retarget it at a parent
6264 // view that has a frame.
6265 if (!frame &&
6266 (dispatchUsingCoordinates || NS_IS_KEY_EVENT(aEvent) ||
6267 NS_IS_IME_RELATED_EVENT(aEvent) || NS_IS_NON_RETARGETED_PLUGIN_EVENT(aEvent) ||
6268 aEvent->message == NS_PLUGIN_ACTIVATE)) {
6269 nsIView* targetView = aView;
6270 while (targetView && !targetView->GetClientData()) {
6271 targetView = targetView->GetParent();
6274 if (targetView) {
6275 aView = targetView;
6276 frame = static_cast<nsIFrame*>(aView->GetClientData());
6280 if (dispatchUsingCoordinates) {
6281 NS_WARN_IF_FALSE(frame, "Nothing to handle this event!");
6282 if (!frame)
6283 return NS_OK;
6285 nsPresContext* framePresContext = frame->PresContext();
6286 nsPresContext* rootPresContext = framePresContext->GetRootPresContext();
6287 NS_ASSERTION(rootPresContext == mPresContext->GetRootPresContext(),
6288 "How did we end up outside the connected prescontext/viewmanager hierarchy?");
6289 // If we aren't starting our event dispatch from the root frame of the root prescontext,
6290 // then someone must be capturing the mouse. In that case we don't want to search the popup
6291 // list.
6292 if (framePresContext == rootPresContext &&
6293 frame == FrameManager()->GetRootFrame()) {
6294 nsIFrame* popupFrame =
6295 nsLayoutUtils::GetPopupFrameForEventCoordinates(rootPresContext, aEvent);
6296 // If the popupFrame is an ancestor of the 'frame', the frame should
6297 // handle the event, otherwise, the popup should handle it.
6298 if (popupFrame &&
6299 !nsContentUtils::ContentIsCrossDocDescendantOf(
6300 framePresContext->GetPresShell()->GetDocument(),
6301 popupFrame->GetContent())) {
6302 frame = popupFrame;
6306 PRBool captureRetarget = PR_FALSE;
6307 if (capturingContent) {
6308 // If a capture is active, determine if the docshell is visible. If not,
6309 // clear the capture and target the mouse event normally instead. This
6310 // would occur if the mouse button is held down while a tab change occurs.
6311 // If the docshell is visible, look for a scrolling container.
6312 PRBool vis;
6313 nsCOMPtr<nsISupports> supports = mPresContext->GetContainer();
6314 nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(supports));
6315 if (baseWin && NS_SUCCEEDED(baseWin->GetVisibility(&vis)) && vis) {
6316 captureRetarget = gCaptureInfo.mRetargetToElement;
6317 if (!captureRetarget) {
6318 // A check was already done above to ensure that capturingContent is
6319 // in this presshell.
6320 NS_ASSERTION(capturingContent->GetCurrentDoc() == GetDocument(),
6321 "Unexpected document");
6322 nsIFrame* captureFrame = capturingContent->GetPrimaryFrame();
6323 if (captureFrame) {
6324 if (capturingContent->Tag() == nsGkAtoms::select &&
6325 capturingContent->IsHTML()) {
6326 // a dropdown <select> has a child in its selectPopupList and we should
6327 // capture on that instead.
6328 nsIFrame* childFrame = captureFrame->GetChildList(nsGkAtoms::selectPopupList).FirstChild();
6329 if (childFrame) {
6330 captureFrame = childFrame;
6334 // scrollable frames should use the scrolling container as
6335 // the root instead of the document
6336 nsIScrollableFrame* scrollFrame = do_QueryFrame(captureFrame);
6337 if (scrollFrame) {
6338 frame = scrollFrame->GetScrolledFrame();
6343 else {
6344 ClearMouseCapture(nsnull);
6345 capturingContent = nsnull;
6349 // Get the frame at the event point. However, don't do this if we're
6350 // capturing and retargeting the event because the captured frame will
6351 // be used instead below.
6352 if (!captureRetarget) {
6353 nsPoint eventPoint
6354 = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
6356 PRBool ignoreRootScrollFrame = PR_FALSE;
6357 if (aEvent->eventStructType == NS_MOUSE_EVENT) {
6358 ignoreRootScrollFrame = static_cast<nsMouseEvent*>(aEvent)->ignoreRootScrollFrame;
6360 nsIFrame* target = nsLayoutUtils::GetFrameForPoint(frame, eventPoint,
6361 PR_FALSE, ignoreRootScrollFrame);
6362 if (target) {
6363 frame = target;
6368 // if a node is capturing the mouse, check if the event needs to be
6369 // retargeted at the capturing content instead. This will be the case when
6370 // capture retargeting is being used, no frame was found or the frame's
6371 // content is not a descendant of the capturing content.
6372 if (capturingContent &&
6373 (gCaptureInfo.mRetargetToElement || !frame->GetContent() ||
6374 !nsContentUtils::ContentIsCrossDocDescendantOf(frame->GetContent(),
6375 capturingContent))) {
6376 // A check was already done above to ensure that capturingContent is
6377 // in this presshell.
6378 NS_ASSERTION(capturingContent->GetCurrentDoc() == GetDocument(),
6379 "Unexpected document");
6380 nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
6381 if (capturingFrame) {
6382 frame = capturingFrame;
6383 aView = frame->GetClosestView();
6387 // Suppress mouse event if it's being targeted at an element inside
6388 // a document which needs events suppressed
6389 if (aEvent->eventStructType == NS_MOUSE_EVENT &&
6390 frame->PresContext()->Document()->EventHandlingSuppressed()) {
6391 if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
6392 mNoDelayedMouseEvents = PR_TRUE;
6393 } else if (!mNoDelayedMouseEvents && aEvent->message == NS_MOUSE_BUTTON_UP) {
6394 nsDelayedEvent* event =
6395 new nsDelayedMouseEvent(static_cast<nsMouseEvent*>(aEvent));
6396 if (!mDelayedEvents.AppendElement(event)) {
6397 delete event;
6401 return NS_OK;
6404 PresShell* shell =
6405 static_cast<PresShell*>(frame->PresContext()->PresShell());
6406 if (shell != this) {
6407 // Handle the event in the correct shell.
6408 // Prevent deletion until we're done with event handling (bug 336582).
6409 nsCOMPtr<nsIPresShell> kungFuDeathGrip(shell);
6410 nsIView* subshellRootView;
6411 shell->GetViewManager()->GetRootView(subshellRootView);
6412 // We pass the subshell's root view as the view to start from. This is
6413 // the only correct alternative; if the event was captured then it
6414 // must have been captured by us or some ancestor shell and we
6415 // now ask the subshell to dispatch it normally.
6416 return shell->HandlePositionedEvent(subshellRootView, frame,
6417 aEvent, aEventStatus);
6420 return HandlePositionedEvent(aView, frame, aEvent, aEventStatus);
6423 nsresult rv = NS_OK;
6425 if (frame) {
6426 PushCurrentEventInfo(nsnull, nsnull);
6428 // key and IME related events go to the focused frame in this DOM window.
6429 if (NS_IsEventTargetedAtFocusedContent(aEvent)) {
6430 NS_ASSERTION(mDocument, "mDocument is null");
6431 nsCOMPtr<nsPIDOMWindow> window =
6432 do_QueryInterface(mDocument->GetWindow());
6433 nsCOMPtr<nsPIDOMWindow> focusedWindow;
6434 mCurrentEventContent =
6435 nsFocusManager::GetFocusedDescendant(window, PR_FALSE,
6436 getter_AddRefs(focusedWindow));
6438 // otherwise, if there is no focused content or the focused content has
6439 // no frame, just use the root content. This ensures that key events
6440 // still get sent to the window properly if nothing is focused or if a
6441 // frame goes away while it is focused.
6442 if (!mCurrentEventContent || !GetCurrentEventFrame())
6443 mCurrentEventContent = mDocument->GetRootElement();
6445 if (aEvent->message == NS_KEY_DOWN) {
6446 NS_IF_RELEASE(gKeyDownTarget);
6447 NS_IF_ADDREF(gKeyDownTarget = mCurrentEventContent);
6449 else if ((aEvent->message == NS_KEY_PRESS || aEvent->message == NS_KEY_UP) &&
6450 gKeyDownTarget) {
6451 // If a different element is now focused for the keypress/keyup event
6452 // than what was focused during the keydown event, check if the new
6453 // focused element is not in a chrome document any more, and if so,
6454 // retarget the event back at the keydown target. This prevents a
6455 // content area from grabbing the focus from chrome in-between key
6456 // events.
6457 if (mCurrentEventContent &&
6458 nsContentUtils::IsChromeDoc(gKeyDownTarget->GetCurrentDoc()) &&
6459 !nsContentUtils::IsChromeDoc(mCurrentEventContent->GetCurrentDoc())) {
6460 mCurrentEventContent = gKeyDownTarget;
6463 if (aEvent->message == NS_KEY_UP) {
6464 NS_RELEASE(gKeyDownTarget);
6468 mCurrentEventFrame = nsnull;
6470 if (!mCurrentEventContent || !GetCurrentEventFrame() ||
6471 InZombieDocument(mCurrentEventContent)) {
6472 rv = RetargetEventToParent(aEvent, aEventStatus);
6473 PopCurrentEventInfo();
6474 return rv;
6476 } else {
6477 mCurrentEventFrame = frame;
6479 if (GetCurrentEventFrame()) {
6480 rv = HandleEventInternal(aEvent, aView, aEventStatus);
6483 #ifdef NS_DEBUG
6484 ShowEventTargetDebug();
6485 #endif
6486 PopCurrentEventInfo();
6487 } else {
6488 // Activation events need to be dispatched even if no frame was found, since
6489 // we don't want the focus to be out of sync.
6491 if (!NS_EVENT_NEEDS_FRAME(aEvent)) {
6492 mCurrentEventFrame = nsnull;
6493 return HandleEventInternal(aEvent, aView, aEventStatus);
6495 else if (NS_IS_KEY_EVENT(aEvent)) {
6496 // Keypress events in new blank tabs should not be completely thrown away.
6497 // Retarget them -- the parent chrome shell might make use of them.
6498 return RetargetEventToParent(aEvent, aEventStatus);
6502 return rv;
6505 #ifdef NS_DEBUG
6506 void
6507 PresShell::ShowEventTargetDebug()
6509 if (nsFrame::GetShowEventTargetFrameBorder() &&
6510 GetCurrentEventFrame()) {
6511 if (mDrawEventTargetFrame) {
6512 mDrawEventTargetFrame->Invalidate(
6513 nsRect(nsPoint(0, 0), mDrawEventTargetFrame->GetSize()));
6516 mDrawEventTargetFrame = mCurrentEventFrame;
6517 mDrawEventTargetFrame->Invalidate(
6518 nsRect(nsPoint(0, 0), mDrawEventTargetFrame->GetSize()));
6521 #endif
6523 nsresult
6524 PresShell::HandlePositionedEvent(nsIView* aView,
6525 nsIFrame* aTargetFrame,
6526 nsGUIEvent* aEvent,
6527 nsEventStatus* aEventStatus)
6529 nsresult rv = NS_OK;
6531 PushCurrentEventInfo(nsnull, nsnull);
6533 mCurrentEventFrame = aTargetFrame;
6535 if (mCurrentEventFrame) {
6536 nsCOMPtr<nsIContent> targetElement;
6537 mCurrentEventFrame->GetContentForEvent(mPresContext, aEvent,
6538 getter_AddRefs(targetElement));
6540 // If there is no content for this frame, target it anyway. Some
6541 // frames can be targeted but do not have content, particularly
6542 // windows with scrolling off.
6543 if (targetElement) {
6544 // Bug 103055, bug 185889: mouse events apply to *elements*, not all
6545 // nodes. Thus we get the nearest element parent here.
6546 // XXX we leave the frame the same even if we find an element
6547 // parent, so that the text frame will receive the event (selection
6548 // and friends are the ones who care about that anyway)
6550 // We use weak pointers because during this tight loop, the node
6551 // will *not* go away. And this happens on every mousemove.
6552 while (targetElement && !targetElement->IsElement()) {
6553 targetElement = targetElement->GetParent();
6556 // If we found an element, target it. Otherwise, target *nothing*.
6557 if (!targetElement) {
6558 mCurrentEventContent = nsnull;
6559 mCurrentEventFrame = nsnull;
6560 } else if (targetElement != mCurrentEventContent) {
6561 mCurrentEventContent = targetElement;
6566 if (GetCurrentEventFrame()) {
6567 rv = HandleEventInternal(aEvent, aView, aEventStatus);
6570 #ifdef NS_DEBUG
6571 ShowEventTargetDebug();
6572 #endif
6573 PopCurrentEventInfo();
6574 return rv;
6577 nsresult
6578 PresShell::HandleEventWithTarget(nsEvent* aEvent, nsIFrame* aFrame,
6579 nsIContent* aContent, nsEventStatus* aStatus)
6581 PushCurrentEventInfo(aFrame, aContent);
6582 nsresult rv = HandleEventInternal(aEvent, nsnull, aStatus);
6583 PopCurrentEventInfo();
6584 return rv;
6587 static inline PRBool
6588 IsSynthesizedMouseEvent(nsEvent* aEvent)
6590 return aEvent->eventStructType == NS_MOUSE_EVENT &&
6591 static_cast<nsMouseEvent*>(aEvent)->reason != nsMouseEvent::eReal;
6594 static PRBool CanHandleContextMenuEvent(nsMouseEvent* aMouseEvent,
6595 nsIFrame* aFrame)
6597 #if defined(XP_MACOSX) && defined(MOZ_XUL)
6598 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
6599 if (pm) {
6600 nsIFrame* popupFrame = pm->GetTopPopup(ePopupTypeMenu);
6601 if (popupFrame) {
6602 // context menus should not be opened while another menu is open on Mac,
6603 // so return false so that the event is not fired.
6604 if (aMouseEvent->context == nsMouseEvent::eContextMenuKey) {
6605 return PR_FALSE;
6606 } else if (aMouseEvent->widget) {
6607 nsWindowType windowType;
6608 aMouseEvent->widget->GetWindowType(windowType);
6609 if (windowType == eWindowType_popup) {
6610 for (nsIFrame* current = aFrame; current;
6611 current = nsLayoutUtils::GetCrossDocParentFrame(current)) {
6612 if (current->GetType() == nsGkAtoms::menuPopupFrame) {
6613 return PR_FALSE;
6620 #endif
6621 return PR_TRUE;
6624 nsresult
6625 PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView,
6626 nsEventStatus* aStatus)
6628 NS_TIME_FUNCTION_MIN(1.0);
6630 #ifdef ACCESSIBILITY
6631 if (aEvent->eventStructType == NS_ACCESSIBLE_EVENT)
6633 nsAccessibleEvent *accEvent = static_cast<nsAccessibleEvent*>(aEvent);
6634 accEvent->mAccessible = nsnull;
6636 nsCOMPtr<nsIAccessibilityService> accService =
6637 do_GetService("@mozilla.org/accessibilityService;1");
6638 if (accService) {
6639 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
6640 if (!container) {
6641 // This presshell is not active. This often happens when a
6642 // preshell is being held onto for fastback.
6643 return NS_OK;
6646 accEvent->mAccessible = accService->GetAccessibleInShell(mDocument, this);
6648 // Ensure this is set in case a11y was activated before any
6649 // nsPresShells existed to observe "a11y-init-or-shutdown" topic
6650 gIsAccessibilityActive = PR_TRUE;
6651 return NS_OK;
6654 #endif
6656 nsCOMPtr<nsIEventStateManager> manager = mPresContext->EventStateManager();
6657 nsresult rv = NS_OK;
6659 if (!NS_EVENT_NEEDS_FRAME(aEvent) || GetCurrentEventFrame()) {
6660 PRBool isHandlingUserInput = PR_FALSE;
6662 if (NS_IS_TRUSTED_EVENT(aEvent)) {
6663 switch (aEvent->message) {
6664 case NS_MOUSE_BUTTON_DOWN:
6665 case NS_MOUSE_BUTTON_UP:
6666 case NS_KEY_PRESS:
6667 case NS_KEY_DOWN:
6668 case NS_KEY_UP:
6669 isHandlingUserInput = PR_TRUE;
6670 break;
6671 case NS_DRAGDROP_DROP:
6672 nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
6673 if (session) {
6674 PRBool onlyChromeDrop = PR_FALSE;
6675 session->GetOnlyChromeDrop(&onlyChromeDrop);
6676 if (onlyChromeDrop) {
6677 aEvent->flags |= NS_EVENT_FLAG_ONLY_CHROME_DISPATCH;
6680 break;
6684 if (aEvent->message == NS_CONTEXTMENU) {
6685 nsMouseEvent* me = static_cast<nsMouseEvent*>(aEvent);
6686 if (!CanHandleContextMenuEvent(me, GetCurrentEventFrame())) {
6687 return NS_OK;
6689 if (me->context == nsMouseEvent::eContextMenuKey &&
6690 !AdjustContextMenuKeyEvent(me)) {
6691 return NS_OK;
6695 nsAutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput,
6696 aEvent->message == NS_MOUSE_BUTTON_DOWN);
6698 nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent));
6700 // FIXME. If the event was reused, we need to clear the old target,
6701 // bug 329430
6702 aEvent->target = nsnull;
6704 nsWeakView weakView(aView);
6705 // 1. Give event to event manager for pre event state changes and
6706 // generation of synthetic events.
6707 rv = manager->PreHandleEvent(mPresContext, aEvent, mCurrentEventFrame,
6708 aStatus, aView);
6710 // 2. Give event to the DOM for third party and JS use.
6711 if (GetCurrentEventFrame() && NS_SUCCEEDED(rv)) {
6712 PRBool wasHandlingKeyBoardEvent =
6713 nsContentUtils::IsHandlingKeyBoardEvent();
6714 if (aEvent->eventStructType == NS_KEY_EVENT) {
6715 nsContentUtils::SetIsHandlingKeyBoardEvent(PR_TRUE);
6717 // We want synthesized mouse moves to cause mouseover and mouseout
6718 // DOM events (PreHandleEvent above), but not mousemove DOM events.
6719 // Synthesized button up events also do not cause DOM events
6720 // because they do not have a reliable refPoint.
6721 if (!IsSynthesizedMouseEvent(aEvent)) {
6722 nsPresShellEventCB eventCB(this);
6723 if (mCurrentEventContent) {
6724 nsEventDispatcher::Dispatch(mCurrentEventContent, mPresContext,
6725 aEvent, nsnull, aStatus, &eventCB);
6727 else {
6728 nsCOMPtr<nsIContent> targetContent;
6729 rv = mCurrentEventFrame->GetContentForEvent(mPresContext, aEvent,
6730 getter_AddRefs(targetContent));
6731 if (NS_SUCCEEDED(rv) && targetContent) {
6732 nsEventDispatcher::Dispatch(targetContent, mPresContext, aEvent,
6733 nsnull, aStatus, &eventCB);
6734 } else if (mDocument) {
6735 nsEventDispatcher::Dispatch(mDocument, mPresContext, aEvent,
6736 nsnull, aStatus, nsnull);
6741 nsContentUtils::SetIsHandlingKeyBoardEvent(wasHandlingKeyBoardEvent);
6743 // 3. Give event to event manager for post event state changes and
6744 // generation of synthetic events.
6745 if (!mIsDestroying && NS_SUCCEEDED(rv)) {
6746 rv = manager->PostHandleEvent(mPresContext, aEvent,
6747 GetCurrentEventFrame(), aStatus,
6748 weakView.GetView());
6752 if (aEvent->message == NS_MOUSE_BUTTON_UP) {
6753 // reset the capturing content now that the mouse button is up
6754 SetCapturingContent(nsnull, 0);
6757 return rv;
6760 // Dispatch event to content only (NOT full processing)
6761 // See also HandleEventWithTarget which does full event processing.
6762 nsresult
6763 PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent, nsEvent* aEvent,
6764 nsEventStatus* aStatus)
6766 nsresult rv = NS_OK;
6768 PushCurrentEventInfo(nsnull, aTargetContent);
6770 // Bug 41013: Check if the event should be dispatched to content.
6771 // It's possible that we are in the middle of destroying the window
6772 // and the js context is out of date. This check detects the case
6773 // that caused a crash in bug 41013, but there may be a better way
6774 // to handle this situation!
6775 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
6776 if (container) {
6778 // Dispatch event to content
6779 rv = nsEventDispatcher::Dispatch(aTargetContent, mPresContext, aEvent, nsnull,
6780 aStatus);
6783 PopCurrentEventInfo();
6784 return rv;
6787 // See the method above.
6788 nsresult
6789 PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent,
6790 nsIDOMEvent* aEvent,
6791 nsEventStatus* aStatus)
6793 nsresult rv = NS_OK;
6795 PushCurrentEventInfo(nsnull, aTargetContent);
6796 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
6797 if (container) {
6798 rv = nsEventDispatcher::DispatchDOMEvent(aTargetContent, nsnull, aEvent,
6799 mPresContext, aStatus);
6802 PopCurrentEventInfo();
6803 return rv;
6806 PRBool
6807 PresShell::AdjustContextMenuKeyEvent(nsMouseEvent* aEvent)
6809 #ifdef MOZ_XUL
6810 // if a menu is open, open the context menu relative to the active item on the menu.
6811 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
6812 if (pm) {
6813 nsIFrame* popupFrame = pm->GetTopPopup(ePopupTypeMenu);
6814 if (popupFrame) {
6815 nsIFrame* itemFrame =
6816 (static_cast<nsMenuPopupFrame *>(popupFrame))->GetCurrentMenuItem();
6817 if (!itemFrame)
6818 itemFrame = popupFrame;
6820 nsCOMPtr<nsIWidget> widget = popupFrame->GetNearestWidget();
6821 aEvent->widget = widget;
6822 nsIntPoint widgetPoint = widget->WidgetToScreenOffset();
6823 aEvent->refPoint = itemFrame->GetScreenRect().BottomLeft() - widgetPoint;
6825 mCurrentEventContent = itemFrame->GetContent();
6826 mCurrentEventFrame = itemFrame;
6828 return PR_TRUE;
6831 #endif
6833 // If we're here because of the key-equiv for showing context menus, we
6834 // have to twiddle with the NS event to make sure the context menu comes
6835 // up in the upper left of the relevant content area before we create
6836 // the DOM event. Since we never call InitMouseEvent() on the event,
6837 // the client X/Y will be 0,0. We can make use of that if the widget is null.
6838 // Use the root view manager's widget since it's most likely to have one,
6839 // and the coordinates returned by GetCurrentItemAndPositionForElement
6840 // are relative to the widget of the root of the root view manager.
6841 nsRootPresContext* rootPC = mPresContext->GetRootPresContext();
6842 aEvent->refPoint.x = 0;
6843 aEvent->refPoint.y = 0;
6844 if (rootPC) {
6845 rootPC->PresShell()->GetViewManager()->
6846 GetRootWidget(getter_AddRefs(aEvent->widget));
6848 if (aEvent->widget) {
6849 // default the refpoint to the topleft of our document
6850 nsPoint offset(0, 0);
6851 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
6852 if (rootFrame) {
6853 nsIView* view = rootFrame->GetClosestView(&offset);
6854 offset += view->GetOffsetToWidget(aEvent->widget);
6855 aEvent->refPoint =
6856 offset.ToNearestPixels(mPresContext->AppUnitsPerDevPixel());
6859 } else {
6860 aEvent->widget = nsnull;
6863 // see if we should use the caret position for the popup
6864 nsIntPoint caretPoint;
6865 // Beware! This may flush notifications via synchronous
6866 // ScrollSelectionIntoView.
6867 if (PrepareToUseCaretPosition(aEvent->widget, caretPoint)) {
6868 // caret position is good
6869 aEvent->refPoint = caretPoint;
6870 return PR_TRUE;
6873 // If we're here because of the key-equiv for showing context menus, we
6874 // have to reset the event target to the currently focused element. Get it
6875 // from the focus controller.
6876 nsCOMPtr<nsIDOMElement> currentFocus;
6877 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
6878 if (fm)
6879 fm->GetFocusedElement(getter_AddRefs(currentFocus));
6881 // Reset event coordinates relative to focused frame in view
6882 if (currentFocus) {
6883 nsCOMPtr<nsIContent> currentPointElement;
6884 GetCurrentItemAndPositionForElement(currentFocus,
6885 getter_AddRefs(currentPointElement),
6886 aEvent->refPoint,
6887 aEvent->widget);
6888 if (currentPointElement) {
6889 mCurrentEventContent = currentPointElement;
6890 mCurrentEventFrame = nsnull;
6891 GetCurrentEventFrame();
6895 return PR_TRUE;
6898 // PresShell::PrepareToUseCaretPosition
6900 // This checks to see if we should use the caret position for popup context
6901 // menus. Returns true if the caret position should be used, and the
6902 // coordinates of that position is returned in aTargetPt. This function
6903 // will also scroll the window as needed to make the caret visible.
6905 // The event widget should be the widget that generated the event, and
6906 // whose coordinate system the resulting event's refPoint should be
6907 // relative to. The returned point is in device pixels realtive to the
6908 // widget passed in.
6909 PRBool
6910 PresShell::PrepareToUseCaretPosition(nsIWidget* aEventWidget, nsIntPoint& aTargetPt)
6912 nsresult rv;
6914 // check caret visibility
6915 nsRefPtr<nsCaret> caret = GetCaret();
6916 NS_ENSURE_TRUE(caret, PR_FALSE);
6918 PRBool caretVisible = PR_FALSE;
6919 rv = caret->GetCaretVisible(&caretVisible);
6920 if (NS_FAILED(rv) || ! caretVisible)
6921 return PR_FALSE;
6923 // caret selection, this is a temporary weak reference, so no refcounting is
6924 // needed
6925 nsISelection* domSelection = caret->GetCaretDOMSelection();
6926 NS_ENSURE_TRUE(domSelection, PR_FALSE);
6928 // since the match could be an anonymous textnode inside a
6929 // <textarea> or text <input>, we need to get the outer frame
6930 // note: frames are not refcounted
6931 nsIFrame* frame = nsnull; // may be NULL
6932 nsCOMPtr<nsIDOMNode> node;
6933 rv = domSelection->GetFocusNode(getter_AddRefs(node));
6934 NS_ENSURE_SUCCESS(rv, PR_FALSE);
6935 NS_ENSURE_TRUE(node, PR_FALSE);
6936 nsCOMPtr<nsIContent> content(do_QueryInterface(node));
6937 if (content) {
6938 nsIContent* nonNative = content->FindFirstNonNativeAnonymous();
6939 content = nonNative;
6942 if (content) {
6943 // It seems like ScrollSelectionIntoView should be enough, but it's
6944 // not. The problem is that scrolling the selection into view when it is
6945 // below the current viewport will align the top line of the frame exactly
6946 // with the bottom of the window. This is fine, BUT, the popup event causes
6947 // the control to be re-focused which does this exact call to
6948 // ScrollContentIntoView, which has a one-pixel disagreement of whether the
6949 // frame is actually in view. The result is that the frame is aligned with
6950 // the top of the window, but the menu is still at the bottom.
6952 // Doing this call first forces the frame to be in view, eliminating the
6953 // problem. The only difference in the result is that if your cursor is in
6954 // an edit box below the current view, you'll get the edit box aligned with
6955 // the top of the window. This is arguably better behavior anyway.
6956 rv = ScrollContentIntoView(content, NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
6957 NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
6958 NS_ENSURE_SUCCESS(rv, PR_FALSE);
6959 frame = content->GetPrimaryFrame();
6960 NS_WARN_IF_FALSE(frame, "No frame for focused content?");
6963 // Actually scroll the selection (ie caret) into view. Note that this must
6964 // be synchronous since we will be checking the caret position on the screen.
6966 // Be easy about errors, and just don't scroll in those cases. Better to have
6967 // the correct menu at a weird place than the wrong menu.
6968 // After ScrollSelectionIntoView(), the pending notifications might be
6969 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
6970 nsCOMPtr<nsISelectionController> selCon;
6971 if (frame)
6972 frame->GetSelectionController(GetPresContext(), getter_AddRefs(selCon));
6973 else
6974 selCon = static_cast<nsISelectionController *>(this);
6975 if (selCon) {
6976 rv = selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
6977 nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
6978 NS_ENSURE_SUCCESS(rv, PR_FALSE);
6981 nsPresContext* presContext = GetPresContext();
6983 // get caret position relative to the closest view
6984 nsRect caretCoords;
6985 nsIFrame* caretFrame = caret->GetGeometry(domSelection, &caretCoords);
6986 if (!caretFrame)
6987 return PR_FALSE;
6988 nsPoint viewOffset;
6989 nsIView* view = caretFrame->GetClosestView(&viewOffset);
6990 if (!view)
6991 return PR_FALSE;
6992 // and then get the caret coords relative to the event widget
6993 if (aEventWidget) {
6994 viewOffset += view->GetOffsetToWidget(aEventWidget);
6996 caretCoords.MoveBy(viewOffset);
6998 // caret coordinates are in app units, convert to pixels
6999 aTargetPt.x =
7000 presContext->AppUnitsToDevPixels(caretCoords.x + caretCoords.width);
7001 aTargetPt.y =
7002 presContext->AppUnitsToDevPixels(caretCoords.y + caretCoords.height);
7004 // make sure rounding doesn't return a pixel which is outside the caret
7005 // (e.g. one line lower)
7006 aTargetPt.y -= 1;
7008 return PR_TRUE;
7011 void
7012 PresShell::GetCurrentItemAndPositionForElement(nsIDOMElement *aCurrentEl,
7013 nsIContent** aTargetToUse,
7014 nsIntPoint& aTargetPt,
7015 nsIWidget *aRootWidget)
7017 nsCOMPtr<nsIContent> focusedContent(do_QueryInterface(aCurrentEl));
7018 ScrollContentIntoView(focusedContent, NS_PRESSHELL_SCROLL_ANYWHERE,
7019 NS_PRESSHELL_SCROLL_ANYWHERE);
7021 nsPresContext* presContext = GetPresContext();
7023 PRBool istree = PR_FALSE, checkLineHeight = PR_TRUE;
7024 nscoord extraTreeY = 0;
7026 #ifdef MOZ_XUL
7027 // Set the position to just underneath the current item for multi-select
7028 // lists or just underneath the selected item for single-select lists. If
7029 // the element is not a list, or there is no selection, leave the position
7030 // as is.
7031 nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
7032 nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
7033 do_QueryInterface(aCurrentEl);
7034 if (multiSelect) {
7035 checkLineHeight = PR_FALSE;
7037 PRInt32 currentIndex;
7038 multiSelect->GetCurrentIndex(&currentIndex);
7039 if (currentIndex >= 0) {
7040 nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aCurrentEl));
7041 if (xulElement) {
7042 nsCOMPtr<nsIBoxObject> box;
7043 xulElement->GetBoxObject(getter_AddRefs(box));
7044 nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
7045 // Tree view special case (tree items have no frames)
7046 // Get the focused row and add its coordinates, which are already in pixels
7047 // XXX Boris, should we create a new interface so that this doesn't
7048 // need to know about trees? Something like nsINodelessChildCreator which
7049 // could provide the current focus coordinates?
7050 if (treeBox) {
7051 treeBox->EnsureRowIsVisible(currentIndex);
7052 PRInt32 firstVisibleRow, rowHeight;
7053 treeBox->GetFirstVisibleRow(&firstVisibleRow);
7054 treeBox->GetRowHeight(&rowHeight);
7056 extraTreeY += presContext->CSSPixelsToAppUnits(
7057 (currentIndex - firstVisibleRow + 1) * rowHeight);
7058 istree = PR_TRUE;
7060 nsCOMPtr<nsITreeColumns> cols;
7061 treeBox->GetColumns(getter_AddRefs(cols));
7062 if (cols) {
7063 nsCOMPtr<nsITreeColumn> col;
7064 cols->GetFirstColumn(getter_AddRefs(col));
7065 if (col) {
7066 nsCOMPtr<nsIDOMElement> colElement;
7067 col->GetElement(getter_AddRefs(colElement));
7068 nsCOMPtr<nsIContent> colContent(do_QueryInterface(colElement));
7069 if (colContent) {
7070 nsIFrame* frame = colContent->GetPrimaryFrame();
7071 if (frame) {
7072 extraTreeY += frame->GetSize().height;
7078 else {
7079 multiSelect->GetCurrentItem(getter_AddRefs(item));
7084 else {
7085 // don't check menulists as the selected item will be inside a popup.
7086 nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aCurrentEl);
7087 if (!menulist) {
7088 nsCOMPtr<nsIDOMXULSelectControlElement> select =
7089 do_QueryInterface(aCurrentEl);
7090 if (select) {
7091 checkLineHeight = PR_FALSE;
7092 select->GetSelectedItem(getter_AddRefs(item));
7097 if (item)
7098 focusedContent = do_QueryInterface(item);
7099 #endif
7101 nsIFrame *frame = focusedContent->GetPrimaryFrame();
7102 if (frame) {
7103 NS_ASSERTION(frame->PresContext() == GetPresContext(),
7104 "handling event for focused content that is not in our document?");
7106 nsPoint frameOrigin(0, 0);
7108 // Get the frame's origin within its view
7109 nsIView *view = frame->GetClosestView(&frameOrigin);
7110 NS_ASSERTION(view, "No view for frame");
7112 // View's origin relative the widget
7113 if (aRootWidget) {
7114 frameOrigin += view->GetOffsetToWidget(aRootWidget);
7117 // Start context menu down and to the right from top left of frame
7118 // use the lineheight. This is a good distance to move the context
7119 // menu away from the top left corner of the frame. If we always
7120 // used the frame height, the context menu could end up far away,
7121 // for example when we're focused on linked images.
7122 // On the other hand, we want to use the frame height if it's less
7123 // than the current line height, so that the context menu appears
7124 // associated with the correct frame.
7125 nscoord extra = 0;
7126 if (!istree) {
7127 extra = frame->GetSize().height;
7128 if (checkLineHeight) {
7129 nsIScrollableFrame *scrollFrame =
7130 nsLayoutUtils::GetNearestScrollableFrame(frame);
7131 if (scrollFrame) {
7132 nsSize scrollAmount = scrollFrame->GetLineScrollAmount();
7133 nsIFrame* f = do_QueryFrame(scrollFrame);
7134 PRInt32 APD = presContext->AppUnitsPerDevPixel();
7135 PRInt32 scrollAPD = f->PresContext()->AppUnitsPerDevPixel();
7136 scrollAmount = scrollAmount.ConvertAppUnits(scrollAPD, APD);
7137 if (extra > scrollAmount.height) {
7138 extra = scrollAmount.height;
7144 aTargetPt.x = presContext->AppUnitsToDevPixels(frameOrigin.x);
7145 aTargetPt.y = presContext->AppUnitsToDevPixels(
7146 frameOrigin.y + extra + extraTreeY);
7149 NS_IF_ADDREF(*aTargetToUse = focusedContent);
7152 NS_IMETHODIMP
7153 PresShell::ResizeReflow(nsIView *aView, nscoord aWidth, nscoord aHeight)
7155 return ResizeReflow(aWidth, aHeight);
7158 NS_IMETHODIMP_(PRBool)
7159 PresShell::IsVisible()
7161 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
7162 nsCOMPtr<nsIBaseWindow> bw = do_QueryInterface(container);
7163 if (!bw)
7164 return PR_FALSE;
7165 PRBool res = PR_TRUE;
7166 bw->GetVisibility(&res);
7167 return res;
7170 NS_IMETHODIMP_(PRBool)
7171 PresShell::ShouldIgnoreInvalidation()
7173 return mPaintingSuppressed;
7176 NS_IMETHODIMP_(void)
7177 PresShell::WillPaint(PRBool aWillSendDidPaint)
7179 // Don't bother doing anything if some viewmanager in our tree is
7180 // painting while we still have painting suppressed.
7181 if (mPaintingSuppressed) {
7182 return;
7185 if (!aWillSendDidPaint) {
7186 nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
7187 if (!rootPresContext) {
7188 return;
7190 if (rootPresContext == mPresContext) {
7191 rootPresContext->UpdatePluginGeometry();
7195 // Process reflows, if we have them, to reduce flicker due to invalidates and
7196 // reflow being interspersed. Note that we _do_ allow this to be
7197 // interruptible; if we can't do all the reflows it's better to flicker a bit
7198 // than to freeze up.
7199 FlushPendingNotifications(Flush_InterruptibleLayout);
7202 NS_IMETHODIMP_(void)
7203 PresShell::DidPaint()
7205 nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
7206 if (!rootPresContext) {
7207 return;
7209 if (rootPresContext == mPresContext) {
7210 rootPresContext->UpdatePluginGeometry();
7214 nsresult
7215 PresShell::GetAgentStyleSheets(nsCOMArray<nsIStyleSheet>& aSheets)
7217 aSheets.Clear();
7218 PRInt32 sheetCount = mStyleSet->SheetCount(nsStyleSet::eAgentSheet);
7220 for (PRInt32 i = 0; i < sheetCount; ++i) {
7221 nsIStyleSheet *sheet = mStyleSet->StyleSheetAt(nsStyleSet::eAgentSheet, i);
7222 if (!aSheets.AppendObject(sheet))
7223 return NS_ERROR_OUT_OF_MEMORY;
7226 return NS_OK;
7229 nsresult
7230 PresShell::SetAgentStyleSheets(const nsCOMArray<nsIStyleSheet>& aSheets)
7232 return mStyleSet->ReplaceSheets(nsStyleSet::eAgentSheet, aSheets);
7235 nsresult
7236 PresShell::AddOverrideStyleSheet(nsIStyleSheet *aSheet)
7238 return mStyleSet->PrependStyleSheet(nsStyleSet::eOverrideSheet, aSheet);
7241 nsresult
7242 PresShell::RemoveOverrideStyleSheet(nsIStyleSheet *aSheet)
7244 return mStyleSet->RemoveStyleSheet(nsStyleSet::eOverrideSheet, aSheet);
7247 static void
7248 FreezeElement(nsIContent *aContent, void * /* unused */)
7250 nsIFrame *frame = aContent->GetPrimaryFrame();
7251 nsIObjectFrame *objectFrame = do_QueryFrame(frame);
7252 if (objectFrame) {
7253 objectFrame->StopPlugin();
7257 static PRBool
7258 FreezeSubDocument(nsIDocument *aDocument, void *aData)
7260 nsIPresShell *shell = aDocument->GetShell();
7261 if (shell)
7262 shell->Freeze();
7264 return PR_TRUE;
7267 void
7268 PresShell::Freeze()
7270 MaybeReleaseCapturingContent();
7272 mDocument->EnumerateFreezableElements(FreezeElement, nsnull);
7274 if (mCaret)
7275 mCaret->SetCaretVisible(PR_FALSE);
7277 mPaintingSuppressed = PR_TRUE;
7279 if (mDocument)
7280 mDocument->EnumerateSubDocuments(FreezeSubDocument, nsnull);
7282 nsPresContext* presContext = GetPresContext();
7283 if (presContext &&
7284 presContext->RefreshDriver()->PresContext() == presContext) {
7285 presContext->RefreshDriver()->Freeze();
7288 mFrozen = PR_TRUE;
7289 UpdateImageLockingState();
7292 void
7293 PresShell::FireOrClearDelayedEvents(PRBool aFireEvents)
7295 mNoDelayedMouseEvents = PR_FALSE;
7296 mNoDelayedKeyEvents = PR_FALSE;
7297 if (!aFireEvents) {
7298 mDelayedEvents.Clear();
7299 return;
7302 if (mDocument) {
7303 nsCOMPtr<nsIDocument> doc = mDocument;
7304 while (!mIsDestroying && mDelayedEvents.Length() &&
7305 !doc->EventHandlingSuppressed()) {
7306 nsAutoPtr<nsDelayedEvent> ev(mDelayedEvents[0].forget());
7307 mDelayedEvents.RemoveElementAt(0);
7308 ev->Dispatch(this);
7310 if (!doc->EventHandlingSuppressed()) {
7311 mDelayedEvents.Clear();
7316 static void
7317 ThawElement(nsIContent *aContent, void *aShell)
7319 nsCOMPtr<nsIObjectLoadingContent> objlc(do_QueryInterface(aContent));
7320 if (objlc) {
7321 nsCOMPtr<nsIPluginInstance> inst;
7322 objlc->EnsureInstantiation(getter_AddRefs(inst));
7326 static PRBool
7327 ThawSubDocument(nsIDocument *aDocument, void *aData)
7329 nsIPresShell *shell = aDocument->GetShell();
7330 if (shell)
7331 shell->Thaw();
7333 return PR_TRUE;
7336 void
7337 PresShell::Thaw()
7339 nsPresContext* presContext = GetPresContext();
7340 if (presContext &&
7341 presContext->RefreshDriver()->PresContext() == presContext) {
7342 presContext->RefreshDriver()->Thaw();
7345 mDocument->EnumerateFreezableElements(ThawElement, this);
7347 if (mDocument)
7348 mDocument->EnumerateSubDocuments(ThawSubDocument, nsnull);
7350 UnsuppressPainting();
7352 // Get the activeness of our presshell, as this might have changed
7353 // while we were in the bfcache
7354 QueryIsActive();
7356 // We're now unfrozen
7357 mFrozen = PR_FALSE;
7358 UpdateImageLockingState();
7361 //--------------------------------------------------------
7362 // Start of protected and private methods on the PresShell
7363 //--------------------------------------------------------
7365 void
7366 PresShell::MaybeScheduleReflow()
7368 ASSERT_REFLOW_SCHEDULED_STATE();
7369 if (mReflowScheduled || mIsDestroying || mIsReflowing ||
7370 mDirtyRoots.Length() == 0)
7371 return;
7373 if (!mPresContext->HasPendingInterrupt() || !ScheduleReflowOffTimer()) {
7374 ScheduleReflow();
7377 ASSERT_REFLOW_SCHEDULED_STATE();
7380 void
7381 PresShell::ScheduleReflow()
7383 NS_PRECONDITION(!mReflowScheduled, "Why are we trying to schedule a reflow?");
7384 ASSERT_REFLOW_SCHEDULED_STATE();
7386 if (GetPresContext()->RefreshDriver()->AddLayoutFlushObserver(this)) {
7387 mReflowScheduled = PR_TRUE;
7390 ASSERT_REFLOW_SCHEDULED_STATE();
7393 nsresult
7394 PresShell::DidCauseReflow()
7396 NS_ASSERTION(mChangeNestCount != 0, "Unexpected call to DidCauseReflow()");
7397 --mChangeNestCount;
7398 nsContentUtils::RemoveScriptBlocker();
7400 return NS_OK;
7403 void
7404 PresShell::WillDoReflow()
7406 // We just reflowed, tell the caret that its frame might have moved.
7407 // XXXbz that comment makes no sense
7408 if (mCaret) {
7409 mCaret->InvalidateOutsideCaret();
7410 mCaret->UpdateCaretPosition();
7413 mPresContext->FlushUserFontSet();
7415 mFrameConstructor->BeginUpdate();
7418 void
7419 PresShell::DidDoReflow(PRBool aInterruptible)
7421 mFrameConstructor->EndUpdate();
7423 HandlePostedReflowCallbacks(aInterruptible);
7424 // Null-check mViewManager in case this happens during Destroy. See
7425 // bugs 244435 and 238546.
7426 if (!mPaintingSuppressed && mViewManager)
7427 mViewManager->SynthesizeMouseMove(PR_FALSE);
7428 if (mCaret) {
7429 // Update the caret's position now to account for any changes created by
7430 // the reflow.
7431 mCaret->InvalidateOutsideCaret();
7432 mCaret->UpdateCaretPosition();
7436 static PLDHashOperator
7437 MarkFramesDirtyToRoot(nsPtrHashKey<nsIFrame>* p, void* closure)
7439 nsIFrame* target = static_cast<nsIFrame*>(closure);
7440 for (nsIFrame* f = p->GetKey(); f && !NS_SUBTREE_DIRTY(f);
7441 f = f->GetParent()) {
7442 f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
7444 if (f == target) {
7445 break;
7449 return PL_DHASH_NEXT;
7452 void
7453 PresShell::sReflowContinueCallback(nsITimer* aTimer, void* aPresShell)
7455 nsRefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
7457 NS_PRECONDITION(aTimer == self->mReflowContinueTimer, "Unexpected timer");
7458 self->mReflowContinueTimer = nsnull;
7459 self->ScheduleReflow();
7462 PRBool
7463 PresShell::ScheduleReflowOffTimer()
7465 NS_PRECONDITION(!mReflowScheduled, "Shouldn't get here");
7466 ASSERT_REFLOW_SCHEDULED_STATE();
7468 if (!mReflowContinueTimer) {
7469 mReflowContinueTimer = do_CreateInstance("@mozilla.org/timer;1");
7470 if (!mReflowContinueTimer ||
7471 NS_FAILED(mReflowContinueTimer->
7472 InitWithFuncCallback(sReflowContinueCallback, this, 30,
7473 nsITimer::TYPE_ONE_SHOT))) {
7474 return PR_FALSE;
7477 return PR_TRUE;
7480 PRBool
7481 PresShell::DoReflow(nsIFrame* target, PRBool aInterruptible)
7483 NS_TIME_FUNCTION_WITH_DOCURL;
7485 if (mReflowContinueTimer) {
7486 mReflowContinueTimer->Cancel();
7487 mReflowContinueTimer = nsnull;
7490 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
7492 nsCOMPtr<nsIRenderingContext> rcx = GetReferenceRenderingContext();
7493 if (!rcx) {
7494 NS_NOTREACHED("CreateRenderingContext failure");
7495 return PR_FALSE;
7498 #ifdef DEBUG
7499 mCurrentReflowRoot = target;
7500 #endif
7502 target->WillReflow(mPresContext);
7504 // If the target frame is the root of the frame hierarchy, then
7505 // use all the available space. If it's simply a `reflow root',
7506 // then use the target frame's size as the available space.
7507 nsSize size;
7508 if (target == rootFrame) {
7509 size = mPresContext->GetVisibleArea().Size();
7511 // target->GetRect() has the old size of the frame,
7512 // mPresContext->GetVisibleArea() has the new size.
7513 target->InvalidateRectDifference(mPresContext->GetVisibleArea(),
7514 target->GetRect());
7515 } else {
7516 size = target->GetSize();
7519 NS_ASSERTION(!target->GetNextInFlow() && !target->GetPrevInFlow(),
7520 "reflow roots should never split");
7522 // Don't pass size directly to the reflow state, since a
7523 // constrained height implies page/column breaking.
7524 nsSize reflowSize(size.width, NS_UNCONSTRAINEDSIZE);
7525 nsHTMLReflowState reflowState(mPresContext, target, rcx, reflowSize);
7527 // fix the computed height
7528 NS_ASSERTION(reflowState.mComputedMargin == nsMargin(0, 0, 0, 0),
7529 "reflow state should not set margin for reflow roots");
7530 if (size.height != NS_UNCONSTRAINEDSIZE) {
7531 nscoord computedHeight =
7532 size.height - reflowState.mComputedBorderPadding.TopBottom();
7533 computedHeight = NS_MAX(computedHeight, 0);
7534 reflowState.SetComputedHeight(computedHeight);
7536 NS_ASSERTION(reflowState.ComputedWidth() ==
7537 size.width -
7538 reflowState.mComputedBorderPadding.LeftRight(),
7539 "reflow state computed incorrect width");
7541 mPresContext->ReflowStarted(aInterruptible);
7542 mIsReflowing = PR_TRUE;
7544 nsReflowStatus status;
7545 nsHTMLReflowMetrics desiredSize;
7546 target->Reflow(mPresContext, desiredSize, reflowState, status);
7548 // If an incremental reflow is initiated at a frame other than the
7549 // root frame, then its desired size had better not change! If it's
7550 // initiated at the root, then the size better not change unless its
7551 // height was unconstrained to start with.
7552 NS_ASSERTION((target == rootFrame && size.height == NS_UNCONSTRAINEDSIZE) ||
7553 (desiredSize.width == size.width &&
7554 desiredSize.height == size.height),
7555 "non-root frame's desired size changed during an "
7556 "incremental reflow");
7557 NS_ASSERTION(desiredSize.mOverflowArea ==
7558 nsRect(nsPoint(0, 0),
7559 nsSize(desiredSize.width, desiredSize.height)),
7560 "reflow roots must not have visible overflow");
7561 NS_ASSERTION(status == NS_FRAME_COMPLETE,
7562 "reflow roots should never split");
7564 target->SetSize(nsSize(desiredSize.width, desiredSize.height));
7566 nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, target,
7567 target->GetView(),
7568 &desiredSize.mOverflowArea);
7569 nsContainerFrame::SyncWindowProperties(mPresContext, target,
7570 target->GetView());
7572 target->DidReflow(mPresContext, nsnull, NS_FRAME_REFLOW_FINISHED);
7573 if (target == rootFrame && size.height == NS_UNCONSTRAINEDSIZE) {
7574 mPresContext->SetVisibleArea(nsRect(0, 0, desiredSize.width,
7575 desiredSize.height));
7578 #ifdef DEBUG
7579 mCurrentReflowRoot = nsnull;
7580 #endif
7582 NS_ASSERTION(mPresContext->HasPendingInterrupt() ||
7583 mFramesToDirty.Count() == 0,
7584 "Why do we need to dirty anything if not interrupted?");
7586 mIsReflowing = PR_FALSE;
7587 PRBool interrupted = mPresContext->HasPendingInterrupt();
7588 if (interrupted) {
7589 // Make sure target gets reflowed again.
7590 mFramesToDirty.EnumerateEntries(&MarkFramesDirtyToRoot, target);
7591 NS_ASSERTION(NS_SUBTREE_DIRTY(target), "Why is the target not dirty?");
7592 mDirtyRoots.AppendElement(target);
7594 // Clear mFramesToDirty after we've done the NS_SUBTREE_DIRTY(target)
7595 // assertion so that if it fails it's easier to see what's going on.
7596 #ifdef NOISY_INTERRUPTIBLE_REFLOW
7597 printf("mFramesToDirty.Count() == %u\n", mFramesToDirty.Count());
7598 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
7599 mFramesToDirty.Clear();
7601 // Any FlushPendingNotifications with interruptible reflows
7602 // should be suppressed now. We don't want to do extra reflow work
7603 // before our reflow event happens.
7604 mSuppressInterruptibleReflows = PR_TRUE;
7605 MaybeScheduleReflow();
7608 nsRootPresContext* rootPC = mPresContext->GetRootPresContext();
7609 if (rootPC) {
7610 rootPC->RequestUpdatePluginGeometry(target);
7613 return !interrupted;
7616 #ifdef DEBUG
7617 void
7618 PresShell::DoVerifyReflow()
7620 if (GetVerifyReflowEnable()) {
7621 // First synchronously render what we have so far so that we can
7622 // see it.
7623 nsIView* rootView;
7624 mViewManager->GetRootView(rootView);
7625 mViewManager->UpdateView(rootView, NS_VMREFRESH_IMMEDIATE);
7627 FlushPendingNotifications(Flush_Layout);
7628 mInVerifyReflow = PR_TRUE;
7629 PRBool ok = VerifyIncrementalReflow();
7630 mInVerifyReflow = PR_FALSE;
7631 if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) {
7632 printf("ProcessReflowCommands: finished (%s)\n",
7633 ok ? "ok" : "failed");
7636 if (0 != mDirtyRoots.Length()) {
7637 printf("XXX yikes! reflow commands queued during verify-reflow\n");
7641 #endif
7643 PRBool
7644 PresShell::ProcessReflowCommands(PRBool aInterruptible)
7646 NS_TIME_FUNCTION_WITH_DOCURL;
7648 PRBool interrupted = PR_FALSE;
7649 if (0 != mDirtyRoots.Length()) {
7651 #ifdef DEBUG
7652 if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
7653 printf("ProcessReflowCommands: begin incremental reflow\n");
7655 #endif
7657 // If reflow is interruptible, then make a note of our deadline.
7658 const PRIntervalTime deadline = aInterruptible
7659 ? PR_IntervalNow() + PR_MicrosecondsToInterval(gMaxRCProcessingTime)
7660 : (PRIntervalTime)0;
7662 // Scope for the reflow entry point
7664 nsAutoScriptBlocker scriptBlocker;
7665 WillDoReflow();
7666 AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
7668 do {
7669 // Send an incremental reflow notification to the target frame.
7670 PRInt32 idx = mDirtyRoots.Length() - 1;
7671 nsIFrame *target = mDirtyRoots[idx];
7672 mDirtyRoots.RemoveElementAt(idx);
7674 if (!NS_SUBTREE_DIRTY(target)) {
7675 // It's not dirty anymore, which probably means the notification
7676 // was posted in the middle of a reflow (perhaps with a reflow
7677 // root in the middle). Don't do anything.
7678 continue;
7681 interrupted = !DoReflow(target, aInterruptible);
7683 // Keep going until we're out of reflow commands, or we've run
7684 // past our deadline, or we're interrupted.
7685 } while (!interrupted && mDirtyRoots.Length() &&
7686 (!aInterruptible || PR_IntervalNow() < deadline));
7688 interrupted = mDirtyRoots.Length() != 0;
7691 // Exiting the scriptblocker might have killed us
7692 if (!mIsDestroying) {
7693 DidDoReflow(aInterruptible);
7696 // DidDoReflow might have killed us
7697 if (!mIsDestroying) {
7698 #ifdef DEBUG
7699 if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
7700 printf("\nPresShell::ProcessReflowCommands() finished: this=%p\n",
7701 (void*)this);
7703 DoVerifyReflow();
7704 #endif
7706 // If any new reflow commands were enqueued during the reflow, schedule
7707 // another reflow event to process them. Note that we want to do this
7708 // after DidDoReflow(), since that method can change whether there are
7709 // dirty roots around by flushing, and there's no point in posting a
7710 // reflow event just to have the flush revoke it.
7711 if (mDirtyRoots.Length())
7712 MaybeScheduleReflow();
7716 if (!mIsDestroying && mShouldUnsuppressPainting &&
7717 mDirtyRoots.Length() == 0) {
7718 // We only unlock if we're out of reflows. It's pointless
7719 // to unlock if reflows are still pending, since reflows
7720 // are just going to thrash the frames around some more. By
7721 // waiting we avoid an overeager "jitter" effect.
7722 mShouldUnsuppressPainting = PR_FALSE;
7723 UnsuppressAndInvalidate();
7726 return !interrupted;
7729 #ifdef MOZ_XUL
7731 * It's better to add stuff to the |DidSetStyleContext| method of the
7732 * relevant frames than adding it here. These methods should (ideally,
7733 * anyway) go away.
7736 // Return value says whether to walk children.
7737 typedef PRBool (* frameWalkerFn)(nsIFrame *aFrame, void *aClosure);
7739 static PRBool
7740 ReResolveMenusAndTrees(nsIFrame *aFrame, void *aClosure)
7742 // Trees have a special style cache that needs to be flushed when
7743 // the theme changes.
7744 nsTreeBodyFrame *treeBody = do_QueryFrame(aFrame);
7745 if (treeBody)
7746 treeBody->ClearStyleAndImageCaches();
7748 // We deliberately don't re-resolve style on a menu's popup
7749 // sub-content, since doing so slows menus to a crawl. That means we
7750 // have to special-case them on a skin switch, and ensure that the
7751 // popup frames just get destroyed completely.
7752 if (aFrame && aFrame->GetType() == nsGkAtoms::menuFrame)
7753 (static_cast<nsMenuFrame *>(aFrame))->CloseMenu(PR_TRUE);
7754 return PR_TRUE;
7757 static PRBool
7758 ReframeImageBoxes(nsIFrame *aFrame, void *aClosure)
7760 nsStyleChangeList *list = static_cast<nsStyleChangeList*>(aClosure);
7761 if (aFrame->GetType() == nsGkAtoms::imageBoxFrame) {
7762 list->AppendChange(aFrame, aFrame->GetContent(),
7763 NS_STYLE_HINT_FRAMECHANGE);
7764 return PR_FALSE; // don't walk descendants
7766 return PR_TRUE; // walk descendants
7769 static void
7770 WalkFramesThroughPlaceholders(nsPresContext *aPresContext, nsIFrame *aFrame,
7771 frameWalkerFn aFunc, void *aClosure)
7773 PRBool walkChildren = (*aFunc)(aFrame, aClosure);
7774 if (!walkChildren)
7775 return;
7777 PRInt32 listIndex = 0;
7778 nsIAtom* childList = nsnull;
7780 do {
7781 nsIFrame *child = aFrame->GetFirstChild(childList);
7782 while (child) {
7783 if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
7784 // only do frames that are in flow, and recur through the
7785 // out-of-flows of placeholders.
7786 WalkFramesThroughPlaceholders(aPresContext,
7787 nsPlaceholderFrame::GetRealFrameFor(child),
7788 aFunc, aClosure);
7790 child = child->GetNextSibling();
7793 childList = aFrame->GetAdditionalChildListName(listIndex++);
7794 } while (childList);
7796 #endif
7798 NS_IMETHODIMP
7799 PresShell::Observe(nsISupports* aSubject,
7800 const char* aTopic,
7801 const PRUnichar* aData)
7803 #ifdef MOZ_XUL
7804 if (!nsCRT::strcmp(aTopic, "chrome-flush-skin-caches")) {
7805 nsIFrame *rootFrame = FrameManager()->GetRootFrame();
7806 // Need to null-check because "chrome-flush-skin-caches" can happen
7807 // at interesting times during startup.
7808 if (rootFrame) {
7809 NS_ASSERTION(mViewManager, "View manager must exist");
7810 nsIViewManager::UpdateViewBatch batch(mViewManager);
7812 nsWeakFrame weakRoot(rootFrame);
7813 // Have to make sure that the content notifications are flushed before we
7814 // start messing with the frame model; otherwise we can get content doubling.
7815 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
7817 if (weakRoot.IsAlive()) {
7818 WalkFramesThroughPlaceholders(mPresContext, rootFrame,
7819 &ReResolveMenusAndTrees, nsnull);
7821 // Because "chrome:" URL equality is messy, reframe image box
7822 // frames (hack!).
7823 nsStyleChangeList changeList;
7824 WalkFramesThroughPlaceholders(mPresContext, rootFrame,
7825 ReframeImageBoxes, &changeList);
7826 // Mark ourselves as not safe to flush while we're doing frame
7827 // construction.
7829 nsAutoScriptBlocker scriptBlocker;
7830 ++mChangeNestCount;
7831 mFrameConstructor->ProcessRestyledFrames(changeList);
7832 --mChangeNestCount;
7835 batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
7836 #ifdef ACCESSIBILITY
7837 InvalidateAccessibleSubtree(nsnull);
7838 #endif
7840 return NS_OK;
7842 #endif
7844 if (!nsCRT::strcmp(aTopic, "agent-sheet-added") && mStyleSet) {
7845 AddAgentSheet(aSubject);
7846 return NS_OK;
7849 if (!nsCRT::strcmp(aTopic, "user-sheet-added") && mStyleSet) {
7850 AddUserSheet(aSubject);
7851 return NS_OK;
7854 if (!nsCRT::strcmp(aTopic, "agent-sheet-removed") && mStyleSet) {
7855 RemoveSheet(nsStyleSet::eAgentSheet, aSubject);
7856 return NS_OK;
7859 if (!nsCRT::strcmp(aTopic, "user-sheet-removed") && mStyleSet) {
7860 RemoveSheet(nsStyleSet::eUserSheet, aSubject);
7861 return NS_OK;
7864 #ifdef ACCESSIBILITY
7865 if (!nsCRT::strcmp(aTopic, "a11y-init-or-shutdown")) {
7866 gIsAccessibilityActive = aData && *aData == '1';
7868 #endif
7869 NS_WARNING("unrecognized topic in PresShell::Observe");
7870 return NS_ERROR_FAILURE;
7873 PRBool
7874 nsIPresShell::AddRefreshObserverInternal(nsARefreshObserver* aObserver,
7875 mozFlushType aFlushType)
7877 return GetPresContext()->RefreshDriver()->
7878 AddRefreshObserver(aObserver, aFlushType);
7881 /* virtual */ PRBool
7882 nsIPresShell::AddRefreshObserverExternal(nsARefreshObserver* aObserver,
7883 mozFlushType aFlushType)
7885 return AddRefreshObserverInternal(aObserver, aFlushType);
7888 PRBool
7889 nsIPresShell::RemoveRefreshObserverInternal(nsARefreshObserver* aObserver,
7890 mozFlushType aFlushType)
7892 return GetPresContext()->RefreshDriver()->
7893 RemoveRefreshObserver(aObserver, aFlushType);
7896 /* virtual */ PRBool
7897 nsIPresShell::RemoveRefreshObserverExternal(nsARefreshObserver* aObserver,
7898 mozFlushType aFlushType)
7900 return RemoveRefreshObserverInternal(aObserver, aFlushType);
7903 //------------------------------------------------------
7904 // End of protected and private methods on the PresShell
7905 //------------------------------------------------------
7907 // Start of DEBUG only code
7909 #ifdef NS_DEBUG
7910 #include "nsViewsCID.h"
7911 #include "nsWidgetsCID.h"
7912 #include "nsIDeviceContext.h"
7913 #include "nsIURL.h"
7914 #include "nsILinkHandler.h"
7916 static NS_DEFINE_CID(kViewManagerCID, NS_VIEW_MANAGER_CID);
7918 static void
7919 LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg)
7921 nsAutoString n1, n2;
7922 if (k1) {
7923 k1->GetFrameName(n1);
7924 } else {
7925 n1.Assign(NS_LITERAL_STRING("(null)"));
7928 if (k2) {
7929 k2->GetFrameName(n2);
7930 } else {
7931 n2.Assign(NS_LITERAL_STRING("(null)"));
7934 printf("verifyreflow: %s %p != %s %p %s\n",
7935 NS_LossyConvertUTF16toASCII(n1).get(), (void*)k1,
7936 NS_LossyConvertUTF16toASCII(n2).get(), (void*)k2, aMsg);
7939 static void
7940 LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
7941 const nsRect& r1, const nsRect& r2)
7943 printf("VerifyReflow Error:\n");
7944 nsAutoString name;
7946 if (k1) {
7947 k1->GetFrameName(name);
7948 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1);
7950 printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height);
7952 if (k2) {
7953 k2->GetFrameName(name);
7954 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2);
7956 printf("{%d, %d, %d, %d}\n %s\n",
7957 r2.x, r2.y, r2.width, r2.height, aMsg);
7960 static void
7961 LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
7962 const nsIntRect& r1, const nsIntRect& r2)
7964 printf("VerifyReflow Error:\n");
7965 nsAutoString name;
7967 if (k1) {
7968 k1->GetFrameName(name);
7969 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1);
7971 printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height);
7973 if (k2) {
7974 k2->GetFrameName(name);
7975 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2);
7977 printf("{%d, %d, %d, %d}\n %s\n",
7978 r2.x, r2.y, r2.width, r2.height, aMsg);
7981 static PRBool
7982 CompareTrees(nsPresContext* aFirstPresContext, nsIFrame* aFirstFrame,
7983 nsPresContext* aSecondPresContext, nsIFrame* aSecondFrame)
7985 if (!aFirstPresContext || !aFirstFrame || !aSecondPresContext || !aSecondFrame)
7986 return PR_TRUE;
7987 // XXX Evil hack to reduce false positives; I can't seem to figure
7988 // out how to flush scrollbar changes correctly
7989 //if (aFirstFrame->GetType() == nsGkAtoms::scrollbarFrame)
7990 // return PR_TRUE;
7991 PRBool ok = PR_TRUE;
7992 nsIAtom* listName = nsnull;
7993 PRInt32 listIndex = 0;
7994 do {
7995 const nsFrameList& kids1 = aFirstFrame->GetChildList(listName);
7996 const nsFrameList& kids2 = aSecondFrame->GetChildList(listName);
7997 PRInt32 l1 = kids1.GetLength();
7998 PRInt32 l2 = kids2.GetLength();;
7999 if (l1 != l2) {
8000 ok = PR_FALSE;
8001 LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(),
8002 "child counts don't match: ");
8003 printf("%d != %d\n", l1, l2);
8004 if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
8005 break;
8009 nsIntRect r1, r2;
8010 nsIView* v1, *v2;
8011 for (nsFrameList::Enumerator e1(kids1), e2(kids2);
8013 e1.Next(), e2.Next()) {
8014 nsIFrame* k1 = e1.get();
8015 nsIFrame* k2 = e2.get();
8016 if (((nsnull == k1) && (nsnull != k2)) ||
8017 ((nsnull != k1) && (nsnull == k2))) {
8018 ok = PR_FALSE;
8019 LogVerifyMessage(k1, k2, "child lists are different\n");
8020 break;
8022 else if (nsnull != k1) {
8023 // Verify that the frames are the same size
8024 if (k1->GetRect() != k2->GetRect()) {
8025 ok = PR_FALSE;
8026 LogVerifyMessage(k1, k2, "(frame rects)", k1->GetRect(), k2->GetRect());
8029 // Make sure either both have views or neither have views; if they
8030 // do have views, make sure the views are the same size. If the
8031 // views have widgets, make sure they both do or neither does. If
8032 // they do, make sure the widgets are the same size.
8033 v1 = k1->GetView();
8034 v2 = k2->GetView();
8035 if (((nsnull == v1) && (nsnull != v2)) ||
8036 ((nsnull != v1) && (nsnull == v2))) {
8037 ok = PR_FALSE;
8038 LogVerifyMessage(k1, k2, "child views are not matched\n");
8040 else if (nsnull != v1) {
8041 if (v1->GetBounds() != v2->GetBounds()) {
8042 LogVerifyMessage(k1, k2, "(view rects)", v1->GetBounds(), v2->GetBounds());
8045 nsIWidget* w1 = v1->GetWidget();
8046 nsIWidget* w2 = v2->GetWidget();
8047 if (((nsnull == w1) && (nsnull != w2)) ||
8048 ((nsnull != w1) && (nsnull == w2))) {
8049 ok = PR_FALSE;
8050 LogVerifyMessage(k1, k2, "child widgets are not matched\n");
8052 else if (nsnull != w1) {
8053 w1->GetBounds(r1);
8054 w2->GetBounds(r2);
8055 if (r1 != r2) {
8056 LogVerifyMessage(k1, k2, "(widget rects)", r1, r2);
8060 if (!ok && (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags))) {
8061 break;
8064 // XXX Should perhaps compare their float managers.
8066 // Compare the sub-trees too
8067 if (!CompareTrees(aFirstPresContext, k1, aSecondPresContext, k2)) {
8068 ok = PR_FALSE;
8069 if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
8070 break;
8074 else {
8075 break;
8078 if (!ok && (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags))) {
8079 break;
8082 nsIAtom* listName1 = aFirstFrame->GetAdditionalChildListName(listIndex);
8083 nsIAtom* listName2 = aSecondFrame->GetAdditionalChildListName(listIndex);
8084 listIndex++;
8085 if (listName1 != listName2) {
8086 if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
8087 ok = PR_FALSE;
8089 LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(),
8090 "child list names are not matched: ");
8091 nsAutoString tmp;
8092 if (nsnull != listName1) {
8093 listName1->ToString(tmp);
8094 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), stdout);
8096 else
8097 fputs("(null)", stdout);
8098 printf(" != ");
8099 if (nsnull != listName2) {
8100 listName2->ToString(tmp);
8101 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), stdout);
8103 else
8104 fputs("(null)", stdout);
8105 printf("\n");
8106 break;
8108 listName = listName1;
8109 } while (ok && (listName != nsnull));
8111 return ok;
8113 #endif
8115 #if 0
8116 static nsIFrame*
8117 FindTopFrame(nsIFrame* aRoot)
8119 if (aRoot) {
8120 nsIContent* content = aRoot->GetContent();
8121 if (content) {
8122 nsIAtom* tag;
8123 content->GetTag(tag);
8124 if (nsnull != tag) {
8125 NS_RELEASE(tag);
8126 return aRoot;
8130 // Try one of the children
8131 nsIFrame* kid = aRoot->GetFirstChild(nsnull);
8132 while (nsnull != kid) {
8133 nsIFrame* result = FindTopFrame(kid);
8134 if (nsnull != result) {
8135 return result;
8137 kid = kid->GetNextSibling();
8140 return nsnull;
8142 #endif
8145 #ifdef DEBUG
8147 nsresult
8148 PresShell::CloneStyleSet(nsStyleSet* aSet, nsStyleSet** aResult)
8150 nsStyleSet *clone = new nsStyleSet();
8151 if (!clone) {
8152 return NS_ERROR_OUT_OF_MEMORY;
8155 PRInt32 i, n = aSet->SheetCount(nsStyleSet::eOverrideSheet);
8156 for (i = 0; i < n; i++) {
8157 nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eOverrideSheet, i);
8158 if (ss)
8159 clone->AppendStyleSheet(nsStyleSet::eOverrideSheet, ss);
8162 // The document expects to insert document stylesheets itself
8163 #if 0
8164 n = aSet->SheetCount(nsStyleSet::eDocSheet);
8165 for (i = 0; i < n; i++) {
8166 nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eDocSheet, i);
8167 if (ss)
8168 clone->AddDocStyleSheet(ss, mDocument);
8170 #endif
8172 n = aSet->SheetCount(nsStyleSet::eUserSheet);
8173 for (i = 0; i < n; i++) {
8174 nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eUserSheet, i);
8175 if (ss)
8176 clone->AppendStyleSheet(nsStyleSet::eUserSheet, ss);
8179 n = aSet->SheetCount(nsStyleSet::eAgentSheet);
8180 for (i = 0; i < n; i++) {
8181 nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eAgentSheet, i);
8182 if (ss)
8183 clone->AppendStyleSheet(nsStyleSet::eAgentSheet, ss);
8185 *aResult = clone;
8186 return NS_OK;
8189 #ifdef DEBUG_Eli
8190 static nsresult
8191 DumpToPNG(nsIPresShell* shell, nsAString& name) {
8192 PRInt32 width=1000, height=1000;
8193 nsRect r(0, 0, shell->GetPresContext()->DevPixelsToAppUnits(width),
8194 shell->GetPresContext()->DevPixelsToAppUnits(height));
8196 nsRefPtr<gfxImageSurface> imgSurface =
8197 new gfxImageSurface(gfxIntSize(width, height),
8198 gfxImageSurface::ImageFormatARGB32);
8199 NS_ENSURE_TRUE(imgSurface, NS_ERROR_OUT_OF_MEMORY);
8201 nsRefPtr<gfxContext> imgContext = new gfxContext(imgSurface);
8203 nsRefPtr<gfxASurface> surface =
8204 gfxPlatform::GetPlatform()->
8205 CreateOffscreenSurface(gfxIntSize(width, height),
8206 gfxASurface::ImageFormatARGB32);
8207 NS_ENSURE_TRUE(surface, NS_ERROR_OUT_OF_MEMORY);
8209 nsRefPtr<gfxContext> context = new gfxContext(surface);
8210 NS_ENSURE_TRUE(context, NS_ERROR_OUT_OF_MEMORY);
8212 shell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context);
8214 imgContext->DrawSurface(surface, gfxSize(width, height));
8216 nsCOMPtr<imgIEncoder> encoder = do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png");
8217 NS_ENSURE_TRUE(encoder, NS_ERROR_FAILURE);
8218 encoder->InitFromData(imgSurface->Data(), imgSurface->Stride() * height,
8219 width, height, imgSurface->Stride(),
8220 imgIEncoder::INPUT_FORMAT_HOSTARGB, EmptyString());
8222 // XXX not sure if this is the right way to write to a file
8223 nsCOMPtr<nsILocalFile> file = do_CreateInstance("@mozilla.org/file/local;1");
8224 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
8225 rv = file->InitWithPath(name);
8226 NS_ENSURE_SUCCESS(rv, rv);
8228 PRUint32 length;
8229 encoder->Available(&length);
8231 nsCOMPtr<nsIOutputStream> outputStream;
8232 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
8233 NS_ENSURE_SUCCESS(rv, rv);
8235 nsCOMPtr<nsIOutputStream> bufferedOutputStream;
8236 rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
8237 outputStream, length);
8239 PRUint32 numWritten;
8240 rv = bufferedOutputStream->WriteFrom(encoder, length, &numWritten);
8241 NS_ENSURE_SUCCESS(rv, rv);
8243 return NS_OK;
8245 #endif
8247 // After an incremental reflow, we verify the correctness by doing a
8248 // full reflow into a fresh frame tree.
8249 PRBool
8250 PresShell::VerifyIncrementalReflow()
8252 if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
8253 printf("Building Verification Tree...\n");
8256 // Create a presentation context to view the new frame tree
8257 nsRefPtr<nsPresContext> cx =
8258 new nsRootPresContext(mDocument, mPresContext->IsPaginated() ?
8259 nsPresContext::eContext_PrintPreview :
8260 nsPresContext::eContext_Galley);
8261 NS_ENSURE_TRUE(cx, PR_FALSE);
8263 nsIDeviceContext *dc = mPresContext->DeviceContext();
8264 nsresult rv = cx->Init(dc);
8265 NS_ENSURE_SUCCESS(rv, PR_FALSE);
8267 // Get our scrolling preference
8268 nsIView* rootView;
8269 mViewManager->GetRootView(rootView);
8270 NS_ENSURE_TRUE(rootView->HasWidget(), PR_FALSE);
8271 nsIWidget* parentWidget = rootView->GetWidget();
8273 // Create a new view manager.
8274 nsCOMPtr<nsIViewManager> vm = do_CreateInstance(kViewManagerCID);
8275 NS_ENSURE_TRUE(vm, PR_FALSE);
8276 rv = vm->Init(dc);
8277 NS_ENSURE_SUCCESS(rv, PR_FALSE);
8279 // Create a child window of the parent that is our "root view/window"
8280 // Create a view
8281 nsRect tbounds = mPresContext->GetVisibleArea();
8282 nsIView* view = vm->CreateView(tbounds, nsnull);
8283 NS_ENSURE_TRUE(view, PR_FALSE);
8285 //now create the widget for the view
8286 rv = view->CreateWidgetForParent(parentWidget, nsnull, PR_TRUE);
8287 NS_ENSURE_SUCCESS(rv, PR_FALSE);
8289 // Setup hierarchical relationship in view manager
8290 vm->SetRootView(view);
8292 // Make the new presentation context the same size as our
8293 // presentation context.
8294 nsRect r = mPresContext->GetVisibleArea();
8295 cx->SetVisibleArea(r);
8297 // Create a new presentation shell to view the document. Use the
8298 // exact same style information that this document has.
8299 nsAutoPtr<nsStyleSet> newSet;
8300 rv = CloneStyleSet(mStyleSet, getter_Transfers(newSet));
8301 NS_ENSURE_SUCCESS(rv, PR_FALSE);
8302 nsCOMPtr<nsIPresShell> sh;
8303 rv = mDocument->CreateShell(cx, vm, newSet, getter_AddRefs(sh));
8304 NS_ENSURE_SUCCESS(rv, PR_FALSE);
8305 newSet.forget();
8306 // Note that after we create the shell, we must make sure to destroy it
8307 sh->SetVerifyReflowEnable(PR_FALSE); // turn off verify reflow while we're reflowing the test frame tree
8308 vm->SetViewObserver((nsIViewObserver *)((PresShell*)sh.get()));
8310 nsAutoCauseReflowNotifier crNotifier(this);
8311 sh->InitialReflow(r.width, r.height);
8313 mDocument->BindingManager()->ProcessAttachedQueue();
8314 sh->FlushPendingNotifications(Flush_Layout);
8315 sh->SetVerifyReflowEnable(PR_TRUE); // turn on verify reflow again now that we're done reflowing the test frame tree
8316 // Force the non-primary presshell to unsuppress; it doesn't want to normally
8317 // because it thinks it's hidden
8318 ((PresShell*)sh.get())->mPaintingSuppressed = PR_FALSE;
8319 if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
8320 printf("Verification Tree built, comparing...\n");
8323 // Now that the document has been reflowed, use its frame tree to
8324 // compare against our frame tree.
8325 nsIFrame* root1 = FrameManager()->GetRootFrame();
8326 nsIFrame* root2 = sh->FrameManager()->GetRootFrame();
8327 PRBool ok = CompareTrees(mPresContext, root1, cx, root2);
8328 if (!ok && (VERIFY_REFLOW_NOISY & gVerifyReflowFlags)) {
8329 printf("Verify reflow failed, primary tree:\n");
8330 root1->List(stdout, 0);
8331 printf("Verification tree:\n");
8332 root2->List(stdout, 0);
8335 #ifdef DEBUG_Eli
8336 // Sample code for dumping page to png
8337 // XXX Needs to be made more flexible
8338 if (!ok) {
8339 nsString stra;
8340 static int num = 0;
8341 stra.AppendLiteral("C:\\mozilla\\mozilla\\debug\\filea");
8342 stra.AppendInt(num);
8343 stra.AppendLiteral(".png");
8344 DumpToPNG(sh, stra);
8345 nsString strb;
8346 strb.AppendLiteral("C:\\mozilla\\mozilla\\debug\\fileb");
8347 strb.AppendInt(num);
8348 strb.AppendLiteral(".png");
8349 DumpToPNG(this, strb);
8350 ++num;
8352 #endif
8354 sh->EndObservingDocument();
8355 sh->Destroy();
8356 if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
8357 printf("Finished Verifying Reflow...\n");
8360 return ok;
8363 // Layout debugging hooks
8364 void
8365 PresShell::ListStyleContexts(nsIFrame *aRootFrame, FILE *out, PRInt32 aIndent)
8367 nsStyleContext *sc = aRootFrame->GetStyleContext();
8368 if (sc)
8369 sc->List(out, aIndent);
8372 void
8373 PresShell::ListStyleSheets(FILE *out, PRInt32 aIndent)
8375 PRInt32 sheetCount = mStyleSet->SheetCount(nsStyleSet::eDocSheet);
8376 for (PRInt32 i = 0; i < sheetCount; ++i) {
8377 mStyleSet->StyleSheetAt(nsStyleSet::eDocSheet, i)->List(out, aIndent);
8378 fputs("\n", out);
8382 void
8383 PresShell::VerifyStyleTree()
8385 VERIFY_STYLE_TREE;
8387 #endif
8389 //=============================================================
8390 //=============================================================
8391 //-- Debug Reflow Counts
8392 //=============================================================
8393 //=============================================================
8394 #ifdef MOZ_REFLOW_PERF
8395 //-------------------------------------------------------------
8396 void
8397 PresShell::DumpReflows()
8399 if (mReflowCountMgr) {
8400 nsCAutoString uriStr;
8401 if (mDocument) {
8402 nsIURI *uri = mDocument->GetDocumentURI();
8403 if (uri) {
8404 uri->GetPath(uriStr);
8407 mReflowCountMgr->DisplayTotals(uriStr.get());
8408 mReflowCountMgr->DisplayHTMLTotals(uriStr.get());
8409 mReflowCountMgr->DisplayDiffsInTotals("Differences");
8413 //-------------------------------------------------------------
8414 void
8415 PresShell::CountReflows(const char * aName, nsIFrame * aFrame)
8417 if (mReflowCountMgr) {
8418 mReflowCountMgr->Add(aName, aFrame);
8422 //-------------------------------------------------------------
8423 void
8424 PresShell::PaintCount(const char * aName,
8425 nsIRenderingContext* aRenderingContext,
8426 nsPresContext* aPresContext,
8427 nsIFrame * aFrame,
8428 PRUint32 aColor)
8430 if (mReflowCountMgr) {
8431 mReflowCountMgr->PaintCount(aName, aRenderingContext, aPresContext, aFrame, aColor);
8435 //-------------------------------------------------------------
8436 void
8437 PresShell::SetPaintFrameCount(PRBool aPaintFrameCounts)
8439 if (mReflowCountMgr) {
8440 mReflowCountMgr->SetPaintFrameCounts(aPaintFrameCounts);
8444 PRBool
8445 PresShell::IsPaintingFrameCounts()
8447 if (mReflowCountMgr)
8448 return mReflowCountMgr->IsPaintingFrameCounts();
8449 return PR_FALSE;
8452 //------------------------------------------------------------------
8453 //-- Reflow Counter Classes Impls
8454 //------------------------------------------------------------------
8456 //------------------------------------------------------------------
8457 ReflowCounter::ReflowCounter(ReflowCountMgr * aMgr) :
8458 mMgr(aMgr)
8460 ClearTotals();
8461 SetTotalsCache();
8464 //------------------------------------------------------------------
8465 ReflowCounter::~ReflowCounter()
8470 //------------------------------------------------------------------
8471 void ReflowCounter::ClearTotals()
8473 mTotal = 0;
8476 //------------------------------------------------------------------
8477 void ReflowCounter::SetTotalsCache()
8479 mCacheTotal = mTotal;
8482 //------------------------------------------------------------------
8483 void ReflowCounter::CalcDiffInTotals()
8485 mCacheTotal = mTotal - mCacheTotal;
8488 //------------------------------------------------------------------
8489 void ReflowCounter::DisplayTotals(const char * aStr)
8491 DisplayTotals(mTotal, aStr?aStr:"Totals");
8494 //------------------------------------------------------------------
8495 void ReflowCounter::DisplayDiffTotals(const char * aStr)
8497 DisplayTotals(mCacheTotal, aStr?aStr:"Diff Totals");
8500 //------------------------------------------------------------------
8501 void ReflowCounter::DisplayHTMLTotals(const char * aStr)
8503 DisplayHTMLTotals(mTotal, aStr?aStr:"Totals");
8506 //------------------------------------------------------------------
8507 void ReflowCounter::DisplayTotals(PRUint32 aTotal, const char * aTitle)
8509 // figure total
8510 if (aTotal == 0) {
8511 return;
8513 ReflowCounter * gTots = (ReflowCounter *)mMgr->LookUp(kGrandTotalsStr);
8515 printf("%25s\t", aTitle);
8516 printf("%d\t", aTotal);
8517 if (gTots != this && aTotal > 0) {
8518 gTots->Add(aTotal);
8522 //------------------------------------------------------------------
8523 void ReflowCounter::DisplayHTMLTotals(PRUint32 aTotal, const char * aTitle)
8525 if (aTotal == 0) {
8526 return;
8529 ReflowCounter * gTots = (ReflowCounter *)mMgr->LookUp(kGrandTotalsStr);
8530 FILE * fd = mMgr->GetOutFile();
8531 if (!fd) {
8532 return;
8535 fprintf(fd, "<tr><td><center>%s</center></td>", aTitle);
8536 fprintf(fd, "<td><center>%d</center></td></tr>\n", aTotal);
8538 if (gTots != this && aTotal > 0) {
8539 gTots->Add(aTotal);
8543 //------------------------------------------------------------------
8544 //-- ReflowCountMgr
8545 //------------------------------------------------------------------
8546 ReflowCountMgr::ReflowCountMgr()
8548 mCounts = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
8549 PL_CompareValues, nsnull, nsnull);
8550 mIndiFrameCounts = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
8551 PL_CompareValues, nsnull, nsnull);
8552 mCycledOnce = PR_FALSE;
8553 mDumpFrameCounts = PR_FALSE;
8554 mDumpFrameByFrameCounts = PR_FALSE;
8555 mPaintFrameByFrameCounts = PR_FALSE;
8558 //------------------------------------------------------------------
8559 ReflowCountMgr::~ReflowCountMgr()
8561 CleanUp();
8564 //------------------------------------------------------------------
8565 ReflowCounter * ReflowCountMgr::LookUp(const char * aName)
8567 if (nsnull != mCounts) {
8568 ReflowCounter * counter = (ReflowCounter *)PL_HashTableLookup(mCounts, aName);
8569 return counter;
8571 return nsnull;
8575 //------------------------------------------------------------------
8576 void ReflowCountMgr::Add(const char * aName, nsIFrame * aFrame)
8578 NS_ASSERTION(aName != nsnull, "Name shouldn't be null!");
8580 if (mDumpFrameCounts && nsnull != mCounts) {
8581 ReflowCounter * counter = (ReflowCounter *)PL_HashTableLookup(mCounts, aName);
8582 if (counter == nsnull) {
8583 counter = new ReflowCounter(this);
8584 NS_ASSERTION(counter != nsnull, "null ptr");
8585 char * name = NS_strdup(aName);
8586 NS_ASSERTION(name != nsnull, "null ptr");
8587 PL_HashTableAdd(mCounts, name, counter);
8589 counter->Add();
8592 if ((mDumpFrameByFrameCounts || mPaintFrameByFrameCounts) &&
8593 nsnull != mIndiFrameCounts &&
8594 aFrame != nsnull) {
8595 char * key = new char[16];
8596 sprintf(key, "%p", (void*)aFrame);
8597 IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(mIndiFrameCounts, key);
8598 if (counter == nsnull) {
8599 counter = new IndiReflowCounter(this);
8600 NS_ASSERTION(counter != nsnull, "null ptr");
8601 counter->mFrame = aFrame;
8602 counter->mName.AssignASCII(aName);
8603 PL_HashTableAdd(mIndiFrameCounts, key, counter);
8605 // this eliminates extra counts from super classes
8606 if (counter != nsnull && counter->mName.EqualsASCII(aName)) {
8607 counter->mCount++;
8608 counter->mCounter.Add(1);
8613 //------------------------------------------------------------------
8614 void ReflowCountMgr::PaintCount(const char * aName,
8615 nsIRenderingContext* aRenderingContext,
8616 nsPresContext* aPresContext,
8617 nsIFrame* aFrame,
8618 PRUint32 aColor)
8620 if (mPaintFrameByFrameCounts &&
8621 nsnull != mIndiFrameCounts &&
8622 aFrame != nsnull) {
8623 char * key = new char[16];
8624 sprintf(key, "%p", (void*)aFrame);
8625 IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(mIndiFrameCounts, key);
8626 if (counter != nsnull && counter->mName.EqualsASCII(aName)) {
8627 aRenderingContext->PushState();
8628 nsFont font("Times", NS_FONT_STYLE_NORMAL, NS_FONT_VARIANT_NORMAL,
8629 NS_FONT_WEIGHT_NORMAL, NS_FONT_STRETCH_NORMAL, 0,
8630 nsPresContext::CSSPixelsToAppUnits(11));
8632 nsCOMPtr<nsIFontMetrics> fm = aPresContext->GetMetricsFor(font);
8633 aRenderingContext->SetFont(fm);
8634 char buf[16];
8635 sprintf(buf, "%d", counter->mCount);
8636 nscoord x = 0, y;
8637 nscoord width, height;
8638 aRenderingContext->SetTextRunRTL(PR_FALSE);
8639 aRenderingContext->GetWidth((char*)buf, width);
8640 fm->GetHeight(height);
8641 fm->GetMaxAscent(y);
8643 PRUint32 color;
8644 PRUint32 color2;
8645 if (aColor != 0) {
8646 color = aColor;
8647 color2 = NS_RGB(0,0,0);
8648 } else {
8649 PRUint8 rc = 0, gc = 0, bc = 0;
8650 if (counter->mCount < 5) {
8651 rc = 255;
8652 gc = 255;
8653 } else if ( counter->mCount < 11) {
8654 gc = 255;
8655 } else {
8656 rc = 255;
8658 color = NS_RGB(rc,gc,bc);
8659 color2 = NS_RGB(rc/2,gc/2,bc/2);
8662 nsRect rect(0,0, width+15, height+15);
8663 aRenderingContext->SetColor(NS_RGB(0,0,0));
8664 aRenderingContext->FillRect(rect);
8665 aRenderingContext->SetColor(color2);
8666 aRenderingContext->DrawString(buf, strlen(buf), x+15,y+15);
8667 aRenderingContext->SetColor(color);
8668 aRenderingContext->DrawString(buf, strlen(buf), x,y);
8670 aRenderingContext->PopState();
8675 //------------------------------------------------------------------
8676 PRIntn ReflowCountMgr::RemoveItems(PLHashEntry *he, PRIntn i, void *arg)
8678 char *str = (char *)he->key;
8679 ReflowCounter * counter = (ReflowCounter *)he->value;
8680 delete counter;
8681 NS_Free(str);
8683 return HT_ENUMERATE_REMOVE;
8686 //------------------------------------------------------------------
8687 PRIntn ReflowCountMgr::RemoveIndiItems(PLHashEntry *he, PRIntn i, void *arg)
8689 char *str = (char *)he->key;
8690 IndiReflowCounter * counter = (IndiReflowCounter *)he->value;
8691 delete counter;
8692 NS_Free(str);
8694 return HT_ENUMERATE_REMOVE;
8697 //------------------------------------------------------------------
8698 void ReflowCountMgr::CleanUp()
8700 if (nsnull != mCounts) {
8701 PL_HashTableEnumerateEntries(mCounts, RemoveItems, nsnull);
8702 PL_HashTableDestroy(mCounts);
8703 mCounts = nsnull;
8706 if (nsnull != mIndiFrameCounts) {
8707 PL_HashTableEnumerateEntries(mIndiFrameCounts, RemoveIndiItems, nsnull);
8708 PL_HashTableDestroy(mIndiFrameCounts);
8709 mIndiFrameCounts = nsnull;
8713 //------------------------------------------------------------------
8714 PRIntn ReflowCountMgr::DoSingleTotal(PLHashEntry *he, PRIntn i, void *arg)
8716 char *str = (char *)he->key;
8717 ReflowCounter * counter = (ReflowCounter *)he->value;
8719 counter->DisplayTotals(str);
8721 return HT_ENUMERATE_NEXT;
8724 //------------------------------------------------------------------
8725 void ReflowCountMgr::DoGrandTotals()
8727 if (nsnull != mCounts) {
8728 ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
8729 if (gTots == nsnull) {
8730 gTots = new ReflowCounter(this);
8731 PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
8732 } else {
8733 gTots->ClearTotals();
8736 printf("\t\t\t\tTotal\n");
8737 for (PRUint32 i=0;i<78;i++) {
8738 printf("-");
8740 printf("\n");
8741 PL_HashTableEnumerateEntries(mCounts, DoSingleTotal, this);
8745 static void RecurseIndiTotals(nsPresContext* aPresContext,
8746 PLHashTable * aHT,
8747 nsIFrame * aParentFrame,
8748 PRInt32 aLevel)
8750 if (aParentFrame == nsnull) {
8751 return;
8754 char key[16];
8755 sprintf(key, "%p", (void*)aParentFrame);
8756 IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(aHT, key);
8757 if (counter) {
8758 counter->mHasBeenOutput = PR_TRUE;
8759 char * name = ToNewCString(counter->mName);
8760 for (PRInt32 i=0;i<aLevel;i++) printf(" ");
8761 printf("%s - %p [%d][", name, (void*)aParentFrame, counter->mCount);
8762 printf("%d", counter->mCounter.GetTotal());
8763 printf("]\n");
8764 nsMemory::Free(name);
8767 nsIFrame* child = aParentFrame->GetFirstChild(nsnull);
8768 while (child) {
8769 RecurseIndiTotals(aPresContext, aHT, child, aLevel+1);
8770 child = child->GetNextSibling();
8775 //------------------------------------------------------------------
8776 PRIntn ReflowCountMgr::DoSingleIndi(PLHashEntry *he, PRIntn i, void *arg)
8778 IndiReflowCounter * counter = (IndiReflowCounter *)he->value;
8779 if (counter && !counter->mHasBeenOutput) {
8780 char * name = ToNewCString(counter->mName);
8781 printf("%s - %p [%d][", name, (void*)counter->mFrame, counter->mCount);
8782 printf("%d", counter->mCounter.GetTotal());
8783 printf("]\n");
8784 nsMemory::Free(name);
8786 return HT_ENUMERATE_NEXT;
8789 //------------------------------------------------------------------
8790 void ReflowCountMgr::DoIndiTotalsTree()
8792 if (nsnull != mCounts) {
8793 printf("\n------------------------------------------------\n");
8794 printf("-- Individual Frame Counts\n");
8795 printf("------------------------------------------------\n");
8797 if (mPresShell) {
8798 nsIFrame * rootFrame = mPresShell->FrameManager()->GetRootFrame();
8799 RecurseIndiTotals(mPresContext, mIndiFrameCounts, rootFrame, 0);
8800 printf("------------------------------------------------\n");
8801 printf("-- Individual Counts of Frames not in Root Tree\n");
8802 printf("------------------------------------------------\n");
8803 PL_HashTableEnumerateEntries(mIndiFrameCounts, DoSingleIndi, this);
8808 //------------------------------------------------------------------
8809 PRIntn ReflowCountMgr::DoSingleHTMLTotal(PLHashEntry *he, PRIntn i, void *arg)
8811 char *str = (char *)he->key;
8812 ReflowCounter * counter = (ReflowCounter *)he->value;
8814 counter->DisplayHTMLTotals(str);
8816 return HT_ENUMERATE_NEXT;
8819 //------------------------------------------------------------------
8820 void ReflowCountMgr::DoGrandHTMLTotals()
8822 if (nsnull != mCounts) {
8823 ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
8824 if (gTots == nsnull) {
8825 gTots = new ReflowCounter(this);
8826 PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
8827 } else {
8828 gTots->ClearTotals();
8831 static const char * title[] = {"Class", "Reflows"};
8832 fprintf(mFD, "<tr>");
8833 for (PRUint32 i=0; i < NS_ARRAY_LENGTH(title); i++) {
8834 fprintf(mFD, "<td><center><b>%s<b></center></td>", title[i]);
8836 fprintf(mFD, "</tr>\n");
8837 PL_HashTableEnumerateEntries(mCounts, DoSingleHTMLTotal, this);
8841 //------------------------------------
8842 void ReflowCountMgr::DisplayTotals(const char * aStr)
8844 #ifdef DEBUG_rods
8845 printf("%s\n", aStr?aStr:"No name");
8846 #endif
8847 if (mDumpFrameCounts) {
8848 DoGrandTotals();
8850 if (mDumpFrameByFrameCounts) {
8851 DoIndiTotalsTree();
8855 //------------------------------------
8856 void ReflowCountMgr::DisplayHTMLTotals(const char * aStr)
8858 #ifdef WIN32x // XXX NOT XP!
8859 char name[1024];
8861 char * sptr = strrchr(aStr, '/');
8862 if (sptr) {
8863 sptr++;
8864 strcpy(name, sptr);
8865 char * eptr = strrchr(name, '.');
8866 if (eptr) {
8867 *eptr = 0;
8869 strcat(name, "_stats.html");
8871 mFD = fopen(name, "w");
8872 if (mFD) {
8873 fprintf(mFD, "<html><head><title>Reflow Stats</title></head><body>\n");
8874 const char * title = aStr?aStr:"No name";
8875 fprintf(mFD, "<center><b>%s</b><br><table border=1 style=\"background-color:#e0e0e0\">", title);
8876 DoGrandHTMLTotals();
8877 fprintf(mFD, "</center></table>\n");
8878 fprintf(mFD, "</body></html>\n");
8879 fclose(mFD);
8880 mFD = nsnull;
8882 #endif // not XP!
8885 //------------------------------------------------------------------
8886 PRIntn ReflowCountMgr::DoClearTotals(PLHashEntry *he, PRIntn i, void *arg)
8888 ReflowCounter * counter = (ReflowCounter *)he->value;
8889 counter->ClearTotals();
8891 return HT_ENUMERATE_NEXT;
8894 //------------------------------------------------------------------
8895 void ReflowCountMgr::ClearTotals()
8897 PL_HashTableEnumerateEntries(mCounts, DoClearTotals, this);
8900 //------------------------------------------------------------------
8901 void ReflowCountMgr::ClearGrandTotals()
8903 if (nsnull != mCounts) {
8904 ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
8905 if (gTots == nsnull) {
8906 gTots = new ReflowCounter(this);
8907 PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
8908 } else {
8909 gTots->ClearTotals();
8910 gTots->SetTotalsCache();
8915 //------------------------------------------------------------------
8916 PRIntn ReflowCountMgr::DoDisplayDiffTotals(PLHashEntry *he, PRIntn i, void *arg)
8918 PRBool cycledOnce = (arg != 0);
8920 char *str = (char *)he->key;
8921 ReflowCounter * counter = (ReflowCounter *)he->value;
8923 if (cycledOnce) {
8924 counter->CalcDiffInTotals();
8925 counter->DisplayDiffTotals(str);
8927 counter->SetTotalsCache();
8929 return HT_ENUMERATE_NEXT;
8932 //------------------------------------------------------------------
8933 void ReflowCountMgr::DisplayDiffsInTotals(const char * aStr)
8935 if (mCycledOnce) {
8936 printf("Differences\n");
8937 for (PRInt32 i=0;i<78;i++) {
8938 printf("-");
8940 printf("\n");
8941 ClearGrandTotals();
8943 PL_HashTableEnumerateEntries(mCounts, DoDisplayDiffTotals, (void *)mCycledOnce);
8945 mCycledOnce = PR_TRUE;
8948 #endif // MOZ_REFLOW_PERF
8950 // make a color string like #RRGGBB
8951 void ColorToString(nscolor aColor, nsAutoString &aString)
8953 char buf[8];
8955 PR_snprintf(buf, sizeof(buf), "#%02x%02x%02x",
8956 NS_GET_R(aColor), NS_GET_G(aColor), NS_GET_B(aColor));
8957 CopyASCIItoUTF16(buf, aString);
8960 nsIFrame* nsIPresShell::GetAbsoluteContainingBlock(nsIFrame *aFrame)
8962 return FrameConstructor()->GetAbsoluteContainingBlock(aFrame);
8965 void nsIPresShell::InitializeStatics()
8967 NS_ASSERTION(sLiveShells == nsnull, "InitializeStatics called multiple times!");
8968 sLiveShells = new nsTHashtable<PresShellPtrKey>();
8969 sLiveShells->Init();
8972 void nsIPresShell::ReleaseStatics()
8974 NS_ASSERTION(sLiveShells, "ReleaseStatics called without Initialize!");
8975 delete sLiveShells;
8976 sLiveShells = nsnull;
8979 // Asks our docshell whether we're active.
8980 void PresShell::QueryIsActive()
8982 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
8983 nsCOMPtr<nsIDocShell> docshell(do_QueryInterface(container));
8984 if (docshell) {
8985 PRBool isActive;
8986 nsresult rv = docshell->GetIsActive(&isActive);
8987 if (NS_SUCCEEDED(rv))
8988 SetIsActive(isActive);
8992 nsresult
8993 PresShell::SetIsActive(PRBool aIsActive)
8995 mIsActive = aIsActive;
8996 return UpdateImageLockingState();
9000 * Determines the current image locking state. Called when one of the
9001 * dependent factors changes.
9003 nsresult
9004 PresShell::UpdateImageLockingState()
9006 // We're locked if we're both thawed and active.
9007 return mDocument->SetImageLockingState(!mFrozen && mIsActive);