Tracer build fixes. (b=588021, r=dvander)
[mozilla-central.git] / layout / base / nsPresShell.cpp
blob5249ac358f780b9d3689d12d834efae9e06e4da9
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>
29 * Mihai Șucan <mihai.sucan@gmail.com>
31 * Alternatively, the contents of this file may be used under the terms of
32 * either of the GNU General Public License Version 2 or later (the "GPL"),
33 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
34 * in which case the provisions of the GPL or the LGPL are applicable instead
35 * of those above. If you wish to allow use of your version of this file only
36 * under the terms of either the GPL or the LGPL, and not to allow others to
37 * use your version of this file under the terms of the MPL, indicate your
38 * decision by deleting the provisions above and replace them with the notice
39 * and other provisions required by the GPL or the LGPL. If you do not delete
40 * the provisions above, a recipient may use your version of this file under
41 * the terms of any one of the MPL, the GPL or the LGPL.
43 * ***** END LICENSE BLOCK *****
45 * This Original Code has been modified by IBM Corporation.
46 * Modifications made by IBM described herein are
47 * Copyright (c) International Business Machines
48 * Corporation, 2000
50 * Modifications to Mozilla code or documentation
51 * identified per MPL Section 3.3
53 * Date Modified by Description of modification
54 * 05/03/2000 IBM Corp. Observer events for reflow states
57 /* a presentation of a document, part 2 */
59 #include "nsIPresShell.h"
60 #include "nsPresContext.h"
61 #include "nsIContent.h"
62 #include "mozilla/dom/Element.h"
63 #include "nsIDocument.h"
64 #include "nsIDOMXULDocument.h"
65 #include "nsStubDocumentObserver.h"
66 #include "nsStyleSet.h"
67 #include "nsCSSStyleSheet.h" // XXX for UA sheet loading hack, can this go away please?
68 #include "nsIDOMCSSStyleSheet.h" // for Pref-related rule management (bugs 22963,20760,31816)
69 #include "nsINameSpaceManager.h" // for Pref-related rule management (bugs 22963,20760,31816)
70 #include "nsIServiceManager.h"
71 #include "nsFrame.h"
72 #include "nsIViewManager.h"
73 #include "nsCRT.h"
74 #include "nsCRTGlue.h"
75 #include "prlog.h"
76 #include "prmem.h"
77 #include "prprf.h"
78 #include "prinrval.h"
79 #include "nsTArray.h"
80 #include "nsCOMArray.h"
81 #include "nsHashtable.h"
82 #include "nsIViewObserver.h"
83 #include "nsContainerFrame.h"
84 #include "nsIDeviceContext.h"
85 #include "nsEventStateManager.h"
86 #include "nsDOMEvent.h"
87 #include "nsGUIEvent.h"
88 #include "nsHTMLParts.h"
89 #include "nsContentUtils.h"
90 #include "nsISelection.h"
91 #include "nsISelectionController.h"
92 #include "nsISelectionPrivate.h"
93 #include "nsLayoutCID.h"
94 #include "nsGkAtoms.h"
95 #include "nsIDOMRange.h"
96 #include "nsIDOMDocument.h"
97 #include "nsIDOMNode.h"
98 #include "nsIDOM3Node.h"
99 #include "nsIDOMNodeList.h"
100 #include "nsIDOMElement.h"
101 #include "nsRange.h"
102 #include "nsCSSPseudoElements.h"
103 #include "nsCOMPtr.h"
104 #include "nsAutoPtr.h"
105 #include "nsReadableUtils.h"
106 #include "nsUnicharUtils.h"
107 #include "nsWeakReference.h"
108 #include "nsIPageSequenceFrame.h"
109 #include "nsCaret.h"
110 #include "nsIDOMHTMLDocument.h"
111 #include "nsIXPointer.h"
112 #include "nsIDOMXMLDocument.h"
113 #include "nsIParser.h"
114 #include "nsParserCIID.h"
115 #include "nsFrameSelection.h"
116 #include "nsIDOMNSHTMLTextAreaElement.h"
117 #include "nsViewsCID.h"
118 #include "nsPresArena.h"
119 #include "nsFrameManager.h"
120 #include "nsXPCOM.h"
121 #include "nsISupportsPrimitives.h"
122 #include "nsILayoutHistoryState.h"
123 #include "nsILineIterator.h" // for ScrollContentIntoView
124 #include "nsWeakPtr.h"
125 #include "pldhash.h"
126 #include "nsIObserverService.h"
127 #include "nsIObserver.h"
128 #include "nsIDocShell.h" // for reflow observation
129 #include "nsIBaseWindow.h"
130 #include "nsLayoutErrors.h"
131 #include "nsLayoutUtils.h"
132 #include "nsCSSRendering.h"
133 // for |#ifdef DEBUG| code
134 #include "prenv.h"
135 #include "nsIAttribute.h"
136 #include "nsIGlobalHistory2.h"
137 #include "nsDisplayList.h"
138 #include "nsIRegion.h"
139 #include "nsRegion.h"
141 #ifdef MOZ_REFLOW_PERF
142 #include "nsIRenderingContext.h"
143 #include "nsIFontMetrics.h"
144 #endif
146 #include "nsIReflowCallback.h"
148 #include "nsPIDOMWindow.h"
149 #include "nsFocusManager.h"
150 #include "nsIPluginInstance.h"
151 #include "nsIObjectFrame.h"
152 #include "nsIObjectLoadingContent.h"
153 #include "nsNetUtil.h"
154 #include "nsEventDispatcher.h"
155 #include "nsThreadUtils.h"
156 #include "nsStyleSheetService.h"
157 #include "gfxImageSurface.h"
158 #include "gfxContext.h"
159 #ifdef MOZ_MEDIA
160 #include "nsHTMLMediaElement.h"
161 #endif
162 #ifdef MOZ_SMIL
163 #include "nsSMILAnimationController.h"
164 #endif
166 #include "nsRefreshDriver.h"
168 // Drag & Drop, Clipboard
169 #include "nsWidgetsCID.h"
170 #include "nsIClipboard.h"
171 #include "nsIClipboardHelper.h"
172 #include "nsIDocShellTreeItem.h"
173 #include "nsIURI.h"
174 #include "nsIScrollableFrame.h"
175 #include "prtime.h"
176 #include "prlong.h"
177 #include "nsIDragService.h"
178 #include "nsCopySupport.h"
179 #include "nsIDOMHTMLAnchorElement.h"
180 #include "nsIDOMHTMLAreaElement.h"
181 #include "nsIDOMHTMLLinkElement.h"
182 #include "nsITimer.h"
183 #ifdef ACCESSIBILITY
184 #include "nsIAccessibilityService.h"
185 #include "nsAccessible.h"
186 #endif
188 // For style data reconstruction
189 #include "nsStyleChangeList.h"
190 #include "nsCSSFrameConstructor.h"
191 #ifdef MOZ_XUL
192 #include "nsMenuFrame.h"
193 #include "nsTreeBodyFrame.h"
194 #include "nsIBoxObject.h"
195 #include "nsITreeBoxObject.h"
196 #include "nsMenuPopupFrame.h"
197 #include "nsITreeColumns.h"
198 #include "nsIDOMXULMultSelectCntrlEl.h"
199 #include "nsIDOMXULSelectCntrlItemEl.h"
200 #include "nsIDOMXULMenuListElement.h"
202 #endif
203 #include "nsPlaceholderFrame.h"
204 #include "nsCanvasFrame.h"
206 // Content viewer interfaces
207 #include "nsIContentViewer.h"
208 #include "imgIEncoder.h"
209 #include "gfxPlatform.h"
211 #include "mozilla/FunctionTimer.h"
213 #include "Layers.h"
215 #ifdef NS_FUNCTION_TIMER
216 #define NS_TIME_FUNCTION_DECLARE_DOCURL \
217 nsCAutoString docURL__("N/A"); \
218 nsIURI *uri__ = mDocument->GetDocumentURI(); \
219 if (uri__) uri__->GetSpec(docURL__);
220 #define NS_TIME_FUNCTION_WITH_DOCURL \
221 NS_TIME_FUNCTION_DECLARE_DOCURL \
222 NS_TIME_FUNCTION_MIN_FMT(1.0, \
223 "%s (line %d) (document: %s)", MOZ_FUNCTION_NAME, \
224 __LINE__, docURL__.get())
225 #else
226 #define NS_TIME_FUNCTION_WITH_DOCURL do{} while(0)
227 #endif
229 #include "nsContentCID.h"
230 static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
232 /* for NS_MEMORY_REPORTER_IMPLEMENT */
233 #include "nsIMemoryReporter.h"
235 using namespace mozilla;
236 using namespace mozilla::dom;
237 using namespace mozilla::layers;
239 PRBool nsIPresShell::gIsAccessibilityActive = PR_FALSE;
240 CapturingContentInfo nsIPresShell::gCaptureInfo =
241 { PR_FALSE /* mAllowed */, PR_FALSE /* mRetargetToElement */,
242 PR_FALSE /* mPreventDrag */, nsnull /* mContent */ };
243 nsIContent* nsIPresShell::gKeyDownTarget;
245 static PRUint32
246 ChangeFlag(PRUint32 aFlags, PRBool aOnOff, PRUint32 aFlag)
248 PRUint32 flags;
249 if (aOnOff) {
250 flags = (aFlags | aFlag);
251 } else {
252 flags = (aFlag & ~aFlag);
254 return flags;
257 // convert a color value to a string, in the CSS format #RRGGBB
258 // * - initially created for bugs 31816, 20760, 22963
259 static void ColorToString(nscolor aColor, nsAutoString &aString);
261 // Class ID's
262 static NS_DEFINE_CID(kFrameSelectionCID, NS_FRAMESELECTION_CID);
264 // RangePaintInfo is used to paint ranges to offscreen buffers
265 struct RangePaintInfo {
266 nsCOMPtr<nsIRange> mRange;
267 nsDisplayListBuilder mBuilder;
268 nsDisplayList mList;
270 // offset of builder's reference frame to the root frame
271 nsPoint mRootOffset;
273 RangePaintInfo(nsIRange* aRange, nsIFrame* aFrame)
274 : mRange(aRange), mBuilder(aFrame, nsDisplayListBuilder::PAINTING, PR_FALSE)
276 MOZ_COUNT_CTOR(RangePaintInfo);
279 ~RangePaintInfo()
281 mList.DeleteAll();
282 MOZ_COUNT_DTOR(RangePaintInfo);
286 #undef NOISY
288 // ----------------------------------------------------------------------
290 #ifdef NS_DEBUG
291 // Set the environment variable GECKO_VERIFY_REFLOW_FLAGS to one or
292 // more of the following flags (comma separated) for handy debug
293 // output.
294 static PRUint32 gVerifyReflowFlags;
296 struct VerifyReflowFlags {
297 const char* name;
298 PRUint32 bit;
301 static const VerifyReflowFlags gFlags[] = {
302 { "verify", VERIFY_REFLOW_ON },
303 { "reflow", VERIFY_REFLOW_NOISY },
304 { "all", VERIFY_REFLOW_ALL },
305 { "list-commands", VERIFY_REFLOW_DUMP_COMMANDS },
306 { "noisy-commands", VERIFY_REFLOW_NOISY_RC },
307 { "really-noisy-commands", VERIFY_REFLOW_REALLY_NOISY_RC },
308 { "resize", VERIFY_REFLOW_DURING_RESIZE_REFLOW },
311 #define NUM_VERIFY_REFLOW_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
313 static void
314 ShowVerifyReflowFlags()
316 printf("Here are the available GECKO_VERIFY_REFLOW_FLAGS:\n");
317 const VerifyReflowFlags* flag = gFlags;
318 const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
319 while (flag < limit) {
320 printf(" %s\n", flag->name);
321 ++flag;
323 printf("Note: GECKO_VERIFY_REFLOW_FLAGS is a comma separated list of flag\n");
324 printf("names (no whitespace)\n");
326 #endif
328 //========================================================================
329 //========================================================================
330 //========================================================================
331 #ifdef MOZ_REFLOW_PERF
332 class ReflowCountMgr;
334 static const char kGrandTotalsStr[] = "Grand Totals";
336 // Counting Class
337 class ReflowCounter {
338 public:
339 ReflowCounter(ReflowCountMgr * aMgr = nsnull);
340 ~ReflowCounter();
342 void ClearTotals();
343 void DisplayTotals(const char * aStr);
344 void DisplayDiffTotals(const char * aStr);
345 void DisplayHTMLTotals(const char * aStr);
347 void Add() { mTotal++; }
348 void Add(PRUint32 aTotal) { mTotal += aTotal; }
350 void CalcDiffInTotals();
351 void SetTotalsCache();
353 void SetMgr(ReflowCountMgr * aMgr) { mMgr = aMgr; }
355 PRUint32 GetTotal() { return mTotal; }
357 protected:
358 void DisplayTotals(PRUint32 aTotal, const char * aTitle);
359 void DisplayHTMLTotals(PRUint32 aTotal, const char * aTitle);
361 PRUint32 mTotal;
362 PRUint32 mCacheTotal;
364 ReflowCountMgr * mMgr; // weak reference (don't delete)
367 // Counting Class
368 class IndiReflowCounter {
369 public:
370 IndiReflowCounter(ReflowCountMgr * aMgr = nsnull)
371 : mFrame(nsnull),
372 mCount(0),
373 mMgr(aMgr),
374 mCounter(aMgr),
375 mHasBeenOutput(PR_FALSE)
377 virtual ~IndiReflowCounter() {}
379 nsAutoString mName;
380 nsIFrame * mFrame; // weak reference (don't delete)
381 PRInt32 mCount;
383 ReflowCountMgr * mMgr; // weak reference (don't delete)
385 ReflowCounter mCounter;
386 PRBool mHasBeenOutput;
390 //--------------------
391 // Manager Class
392 //--------------------
393 class ReflowCountMgr {
394 public:
395 ReflowCountMgr();
396 virtual ~ReflowCountMgr();
398 void ClearTotals();
399 void ClearGrandTotals();
400 void DisplayTotals(const char * aStr);
401 void DisplayHTMLTotals(const char * aStr);
402 void DisplayDiffsInTotals(const char * aStr);
404 void Add(const char * aName, nsIFrame * aFrame);
405 ReflowCounter * LookUp(const char * aName);
407 void PaintCount(const char * aName, nsIRenderingContext* aRenderingContext, nsPresContext* aPresContext, nsIFrame * aFrame, PRUint32 aColor);
409 FILE * GetOutFile() { return mFD; }
411 PLHashTable * GetIndiFrameHT() { return mIndiFrameCounts; }
413 void SetPresContext(nsPresContext * aPresContext) { mPresContext = aPresContext; } // weak reference
414 void SetPresShell(nsIPresShell* aPresShell) { mPresShell= aPresShell; } // weak reference
416 void SetDumpFrameCounts(PRBool aVal) { mDumpFrameCounts = aVal; }
417 void SetDumpFrameByFrameCounts(PRBool aVal) { mDumpFrameByFrameCounts = aVal; }
418 void SetPaintFrameCounts(PRBool aVal) { mPaintFrameByFrameCounts = aVal; }
420 PRBool IsPaintingFrameCounts() { return mPaintFrameByFrameCounts; }
422 protected:
423 void DisplayTotals(PRUint32 aTotal, PRUint32 * aDupArray, char * aTitle);
424 void DisplayHTMLTotals(PRUint32 aTotal, PRUint32 * aDupArray, char * aTitle);
426 static PRIntn RemoveItems(PLHashEntry *he, PRIntn i, void *arg);
427 static PRIntn RemoveIndiItems(PLHashEntry *he, PRIntn i, void *arg);
428 void CleanUp();
430 // stdout Output Methods
431 static PRIntn DoSingleTotal(PLHashEntry *he, PRIntn i, void *arg);
432 static PRIntn DoSingleIndi(PLHashEntry *he, PRIntn i, void *arg);
434 void DoGrandTotals();
435 void DoIndiTotalsTree();
437 // HTML Output Methods
438 static PRIntn DoSingleHTMLTotal(PLHashEntry *he, PRIntn i, void *arg);
439 void DoGrandHTMLTotals();
441 // Zero Out the Totals
442 static PRIntn DoClearTotals(PLHashEntry *he, PRIntn i, void *arg);
444 // Displays the Diff Totals
445 static PRIntn DoDisplayDiffTotals(PLHashEntry *he, PRIntn i, void *arg);
447 PLHashTable * mCounts;
448 PLHashTable * mIndiFrameCounts;
449 FILE * mFD;
451 PRBool mDumpFrameCounts;
452 PRBool mDumpFrameByFrameCounts;
453 PRBool mPaintFrameByFrameCounts;
455 PRBool mCycledOnce;
457 // Root Frame for Individual Tracking
458 nsPresContext * mPresContext;
459 nsIPresShell* mPresShell;
461 // ReflowCountMgr gReflowCountMgr;
463 #endif
464 //========================================================================
466 // comment out to hide caret
467 #define SHOW_CARET
469 // The upper bound on the amount of time to spend reflowing, in
470 // microseconds. When this bound is exceeded and reflow commands are
471 // still queued up, a reflow event is posted. The idea is for reflow
472 // to not hog the processor beyond the time specifed in
473 // gMaxRCProcessingTime. This data member is initialized from the
474 // layout.reflow.timeslice pref.
475 #define NS_MAX_REFLOW_TIME 1000000
476 static PRInt32 gMaxRCProcessingTime = -1;
478 #define MARK_INCREMENT 50
479 #define BLOCK_INCREMENT 4044 /* a bit under 4096, for malloc overhead */
481 /**A block of memory that the stack will
482 * chop up and hand out
484 struct StackBlock {
486 // a block of memory. Note that this must be first so that it will
487 // be aligned.
488 char mBlock[BLOCK_INCREMENT];
490 // another block of memory that would only be created
491 // if our stack overflowed. Yes we have the ability
492 // to grow on a stack overflow
493 StackBlock* mNext;
495 StackBlock() : mNext(nsnull) { }
496 ~StackBlock() { }
499 /* we hold an array of marks. A push pushes a mark on the stack
500 * a pop pops it off.
502 struct StackMark {
503 // the block of memory we are currently handing out chunks of
504 StackBlock* mBlock;
506 // our current position in the memory
507 size_t mPos;
511 /* A stack arena allows a stack based interface to a block of memory.
512 * It should be used when you need to allocate some temporary memory that
513 * you will immediately return.
515 class StackArena {
516 public:
517 StackArena();
518 ~StackArena();
520 nsresult Init() { return mBlocks ? NS_OK : NS_ERROR_OUT_OF_MEMORY; }
522 // Memory management functions
523 void* Allocate(size_t aSize);
524 void Push();
525 void Pop();
527 PRUint32 Size() {
528 PRUint32 result = 0;
529 StackBlock *block = mBlocks;
530 while (block) {
531 result += sizeof(StackBlock);
532 block = block->mNext;
534 return result;
537 private:
538 // our current position in memory
539 size_t mPos;
541 // a list of memory block. Usually there is only one
542 // but if we overrun our stack size we can get more memory.
543 StackBlock* mBlocks;
545 // the current block of memory we are passing our chucks of
546 StackBlock* mCurBlock;
548 // our stack of mark where push has been called
549 StackMark* mMarks;
551 // the current top of the mark list
552 PRUint32 mStackTop;
554 // the size of the mark array
555 PRUint32 mMarkLength;
560 StackArena::StackArena()
562 mMarkLength = 0;
563 mMarks = nsnull;
565 // allocate our stack memory
566 mBlocks = new StackBlock();
567 mCurBlock = mBlocks;
569 mStackTop = 0;
570 mPos = 0;
573 StackArena::~StackArena()
575 // free up our data
576 delete[] mMarks;
577 while(mBlocks)
579 StackBlock* toDelete = mBlocks;
580 mBlocks = mBlocks->mNext;
581 delete toDelete;
585 void
586 StackArena::Push()
588 // Resize the mark array if we overrun it. Failure to allocate the
589 // mark array is not fatal; we just won't free to that mark. This
590 // allows callers not to worry about error checking.
591 if (mStackTop >= mMarkLength)
593 PRUint32 newLength = mStackTop + MARK_INCREMENT;
594 StackMark* newMarks = new StackMark[newLength];
595 if (newMarks) {
596 if (mMarkLength)
597 memcpy(newMarks, mMarks, sizeof(StackMark)*mMarkLength);
598 // Fill in any marks that we couldn't allocate during a prior call
599 // to Push().
600 for (; mMarkLength < mStackTop; ++mMarkLength) {
601 NS_NOTREACHED("should only hit this on out-of-memory");
602 newMarks[mMarkLength].mBlock = mCurBlock;
603 newMarks[mMarkLength].mPos = mPos;
605 delete [] mMarks;
606 mMarks = newMarks;
607 mMarkLength = newLength;
611 // set a mark at the top (if we can)
612 NS_ASSERTION(mStackTop < mMarkLength, "out of memory");
613 if (mStackTop < mMarkLength) {
614 mMarks[mStackTop].mBlock = mCurBlock;
615 mMarks[mStackTop].mPos = mPos;
618 mStackTop++;
621 void*
622 StackArena::Allocate(size_t aSize)
624 NS_ASSERTION(mStackTop > 0, "Allocate called without Push");
626 // make sure we are aligned. Beard said 8 was safer then 4.
627 // Round size to multiple of 8
628 aSize = PR_ROUNDUP(aSize, 8);
630 // if the size makes the stack overflow. Grab another block for the stack
631 if (mPos + aSize >= BLOCK_INCREMENT)
633 NS_ASSERTION(aSize <= BLOCK_INCREMENT,"Requested memory is greater that our block size!!");
634 if (mCurBlock->mNext == nsnull)
635 mCurBlock->mNext = new StackBlock();
637 mCurBlock = mCurBlock->mNext;
638 mPos = 0;
641 // return the chunk they need.
642 void *result = mCurBlock->mBlock + mPos;
643 mPos += aSize;
645 return result;
648 void
649 StackArena::Pop()
651 // pop off the mark
652 NS_ASSERTION(mStackTop > 0, "unmatched pop");
653 mStackTop--;
655 if (mStackTop >= mMarkLength) {
656 // We couldn't allocate the marks array at the time of the push, so
657 // we don't know where we're freeing to.
658 NS_NOTREACHED("out of memory");
659 if (mStackTop == 0) {
660 // But we do know if we've completely pushed the stack.
661 mCurBlock = mBlocks;
662 mPos = 0;
664 return;
667 #ifdef DEBUG
668 // Mark the "freed" memory with 0xdd to help with debugging of memory
669 // allocation problems.
671 StackBlock *block = mMarks[mStackTop].mBlock, *block_end = mCurBlock;
672 size_t pos = mMarks[mStackTop].mPos;
673 for (; block != block_end; block = block->mNext, pos = 0) {
674 memset(block->mBlock + pos, 0xdd, sizeof(block->mBlock) - pos);
676 memset(block->mBlock + pos, 0xdd, mPos - pos);
678 #endif
680 mCurBlock = mMarks[mStackTop].mBlock;
681 mPos = mMarks[mStackTop].mPos;
684 struct nsCallbackEventRequest
686 nsIReflowCallback* callback;
687 nsCallbackEventRequest* next;
690 // ----------------------------------------------------------------------------
691 #define ASSERT_REFLOW_SCHEDULED_STATE() \
692 NS_ASSERTION(mReflowScheduled == \
693 GetPresContext()->RefreshDriver()-> \
694 IsLayoutFlushObserver(this), "Unexpected state")
696 class nsPresShellEventCB;
697 class nsAutoCauseReflowNotifier;
699 class PresShell : public nsIPresShell, public nsIViewObserver,
700 public nsStubDocumentObserver,
701 public nsISelectionController, public nsIObserver,
702 public nsSupportsWeakReference
704 public:
705 PresShell();
707 NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
709 // nsISupports
710 NS_DECL_ISUPPORTS
712 // nsIPresShell
713 virtual NS_HIDDEN_(nsresult) Init(nsIDocument* aDocument,
714 nsPresContext* aPresContext,
715 nsIViewManager* aViewManager,
716 nsStyleSet* aStyleSet,
717 nsCompatibility aCompatMode);
718 virtual NS_HIDDEN_(void) Destroy();
720 virtual NS_HIDDEN_(void*) AllocateFrame(nsQueryFrame::FrameIID aCode,
721 size_t aSize);
722 virtual NS_HIDDEN_(void) FreeFrame(nsQueryFrame::FrameIID aCode,
723 void* aChunk);
725 virtual NS_HIDDEN_(void*) AllocateMisc(size_t aSize);
726 virtual NS_HIDDEN_(void) FreeMisc(size_t aSize, void* aChunk);
728 // Dynamic stack memory allocation
729 virtual NS_HIDDEN_(void) PushStackMemory();
730 virtual NS_HIDDEN_(void) PopStackMemory();
731 virtual NS_HIDDEN_(void*) AllocateStackMemory(size_t aSize);
733 virtual NS_HIDDEN_(nsresult) SetPreferenceStyleRules(PRBool aForceReflow);
735 NS_IMETHOD GetSelection(SelectionType aType, nsISelection** aSelection);
736 virtual nsISelection* GetCurrentSelection(SelectionType aType);
738 NS_IMETHOD SetDisplaySelection(PRInt16 aToggle);
739 NS_IMETHOD GetDisplaySelection(PRInt16 *aToggle);
740 NS_IMETHOD ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion,
741 PRInt16 aFlags);
742 NS_IMETHOD RepaintSelection(SelectionType aType);
744 virtual NS_HIDDEN_(void) BeginObservingDocument();
745 virtual NS_HIDDEN_(void) EndObservingDocument();
746 virtual NS_HIDDEN_(nsresult) InitialReflow(nscoord aWidth, nscoord aHeight);
747 virtual NS_HIDDEN_(nsresult) ResizeReflow(nscoord aWidth, nscoord aHeight);
748 virtual NS_HIDDEN_(nsresult) ResizeReflowOverride(nscoord aWidth, nscoord aHeight);
749 virtual NS_HIDDEN_(void) StyleChangeReflow();
750 virtual NS_HIDDEN_(nsIPageSequenceFrame*) GetPageSequenceFrame() const;
751 virtual NS_HIDDEN_(nsIFrame*) GetRealPrimaryFrameFor(nsIContent* aContent) const;
753 virtual NS_HIDDEN_(nsIFrame*) GetPlaceholderFrameFor(nsIFrame* aFrame) const;
754 virtual NS_HIDDEN_(void) FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
755 nsFrameState aBitToAdd);
756 virtual NS_HIDDEN_(void) FrameNeedsToContinueReflow(nsIFrame *aFrame);
757 virtual NS_HIDDEN_(void) CancelAllPendingReflows();
758 virtual NS_HIDDEN_(PRBool) IsSafeToFlush() const;
759 virtual NS_HIDDEN_(void) FlushPendingNotifications(mozFlushType aType);
762 * Recreates the frames for a node
764 virtual NS_HIDDEN_(nsresult) RecreateFramesFor(nsIContent* aContent);
767 * Post a callback that should be handled after reflow has finished.
769 virtual NS_HIDDEN_(nsresult) PostReflowCallback(nsIReflowCallback* aCallback);
770 virtual NS_HIDDEN_(void) CancelReflowCallback(nsIReflowCallback* aCallback);
772 virtual NS_HIDDEN_(void) ClearFrameRefs(nsIFrame* aFrame);
773 virtual NS_HIDDEN_(already_AddRefed<nsIRenderingContext>) GetReferenceRenderingContext();
774 virtual NS_HIDDEN_(nsresult) GoToAnchor(const nsAString& aAnchorName, PRBool aScroll);
775 virtual NS_HIDDEN_(nsresult) ScrollToAnchor();
777 virtual NS_HIDDEN_(nsresult) ScrollContentIntoView(nsIContent* aContent,
778 PRIntn aVPercent,
779 PRIntn aHPercent,
780 PRUint32 aFlags);
781 virtual PRBool ScrollFrameRectIntoView(nsIFrame* aFrame,
782 const nsRect& aRect,
783 PRIntn aVPercent,
784 PRIntn aHPercent,
785 PRUint32 aFlags);
786 virtual nsRectVisibility GetRectVisibility(nsIFrame *aFrame,
787 const nsRect &aRect,
788 nscoord aMinTwips) const;
790 virtual NS_HIDDEN_(void) SetIgnoreFrameDestruction(PRBool aIgnore);
791 virtual NS_HIDDEN_(void) NotifyDestroyingFrame(nsIFrame* aFrame);
793 virtual NS_HIDDEN_(nsresult) GetLinkLocation(nsIDOMNode* aNode, nsAString& aLocationString) const;
795 virtual NS_HIDDEN_(nsresult) CaptureHistoryState(nsILayoutHistoryState** aLayoutHistoryState, PRBool aLeavingPage);
797 virtual NS_HIDDEN_(void) UnsuppressPainting();
799 virtual nsresult GetAgentStyleSheets(nsCOMArray<nsIStyleSheet>& aSheets);
800 virtual nsresult SetAgentStyleSheets(const nsCOMArray<nsIStyleSheet>& aSheets);
802 virtual nsresult AddOverrideStyleSheet(nsIStyleSheet *aSheet);
803 virtual nsresult RemoveOverrideStyleSheet(nsIStyleSheet *aSheet);
805 virtual NS_HIDDEN_(nsresult) HandleEventWithTarget(nsEvent* aEvent, nsIFrame* aFrame,
806 nsIContent* aContent,
807 nsEventStatus* aStatus);
808 virtual NS_HIDDEN_(nsIFrame*) GetEventTargetFrame();
809 virtual NS_HIDDEN_(already_AddRefed<nsIContent>) GetEventTargetContent(nsEvent* aEvent);
812 virtual nsresult ReconstructFrames(void);
813 virtual void Freeze();
814 virtual void Thaw();
815 virtual void FireOrClearDelayedEvents(PRBool aFireEvents);
817 virtual nsIFrame* GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt);
819 virtual NS_HIDDEN_(nsresult) RenderDocument(const nsRect& aRect, PRUint32 aFlags,
820 nscolor aBackgroundColor,
821 gfxContext* aThebesContext);
823 virtual already_AddRefed<gfxASurface> RenderNode(nsIDOMNode* aNode,
824 nsIntRegion* aRegion,
825 nsIntPoint& aPoint,
826 nsIntRect* aScreenRect);
828 virtual already_AddRefed<gfxASurface> RenderSelection(nsISelection* aSelection,
829 nsIntPoint& aPoint,
830 nsIntRect* aScreenRect);
832 virtual already_AddRefed<nsPIDOMWindow> GetRootWindow();
834 virtual LayerManager* GetLayerManager();
836 virtual void SetIgnoreViewportScrolling(PRBool aIgnore);
838 virtual void SetDisplayPort(const nsRect& aDisplayPort);
840 virtual nsresult SetResolution(float aXResolution, float aYResolution);
842 virtual void SynthesizeMouseMove(PRBool aFromScroll);
844 //nsIViewObserver interface
846 NS_IMETHOD Paint(nsIView* aDisplayRoot,
847 nsIView* aViewToPaint,
848 nsIWidget* aWidget,
849 const nsRegion& aDirtyRegion,
850 const nsIntRegion& aIntDirtyRegion,
851 PRBool aPaintDefaultBackground,
852 PRBool aWillSendDidPaint);
853 NS_IMETHOD HandleEvent(nsIView* aView,
854 nsGUIEvent* aEvent,
855 nsEventStatus* aEventStatus);
856 virtual NS_HIDDEN_(nsresult) HandleDOMEventWithTarget(nsIContent* aTargetContent,
857 nsEvent* aEvent,
858 nsEventStatus* aStatus);
859 virtual NS_HIDDEN_(nsresult) HandleDOMEventWithTarget(nsIContent* aTargetContent,
860 nsIDOMEvent* aEvent,
861 nsEventStatus* aStatus);
862 NS_IMETHOD ResizeReflow(nsIView *aView, nscoord aWidth, nscoord aHeight);
863 NS_IMETHOD_(PRBool) ShouldIgnoreInvalidation();
864 NS_IMETHOD_(void) WillPaint(PRBool aWillSendDidPaint);
865 NS_IMETHOD_(void) DidPaint();
866 NS_IMETHOD_(void) DispatchSynthMouseMove(nsGUIEvent *aEvent,
867 PRBool aFlushOnHoverChange);
868 NS_IMETHOD_(void) ClearMouseCapture(nsIView* aView);
870 // caret handling
871 virtual NS_HIDDEN_(already_AddRefed<nsCaret>) GetCaret() const;
872 virtual NS_HIDDEN_(void) MaybeInvalidateCaretPosition();
873 NS_IMETHOD SetCaretEnabled(PRBool aInEnable);
874 NS_IMETHOD SetCaretReadOnly(PRBool aReadOnly);
875 NS_IMETHOD GetCaretEnabled(PRBool *aOutEnabled);
876 NS_IMETHOD SetCaretVisibilityDuringSelection(PRBool aVisibility);
877 NS_IMETHOD GetCaretVisible(PRBool *_retval);
878 virtual void SetCaret(nsCaret *aNewCaret);
879 virtual void RestoreCaret();
881 NS_IMETHOD SetSelectionFlags(PRInt16 aInEnable);
882 NS_IMETHOD GetSelectionFlags(PRInt16 *aOutEnable);
884 // nsISelectionController
886 NS_IMETHOD CharacterMove(PRBool aForward, PRBool aExtend);
887 NS_IMETHOD CharacterExtendForDelete();
888 NS_IMETHOD CharacterExtendForBackspace();
889 NS_IMETHOD WordMove(PRBool aForward, PRBool aExtend);
890 NS_IMETHOD WordExtendForDelete(PRBool aForward);
891 NS_IMETHOD LineMove(PRBool aForward, PRBool aExtend);
892 NS_IMETHOD IntraLineMove(PRBool aForward, PRBool aExtend);
893 NS_IMETHOD PageMove(PRBool aForward, PRBool aExtend);
894 NS_IMETHOD ScrollPage(PRBool aForward);
895 NS_IMETHOD ScrollLine(PRBool aForward);
896 NS_IMETHOD ScrollHorizontal(PRBool aLeft);
897 NS_IMETHOD CompleteScroll(PRBool aForward);
898 NS_IMETHOD CompleteMove(PRBool aForward, PRBool aExtend);
899 NS_IMETHOD SelectAll();
900 NS_IMETHOD CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, PRBool *_retval);
902 // nsIDocumentObserver
903 NS_DECL_NSIDOCUMENTOBSERVER_BEGINUPDATE
904 NS_DECL_NSIDOCUMENTOBSERVER_ENDUPDATE
905 NS_DECL_NSIDOCUMENTOBSERVER_BEGINLOAD
906 NS_DECL_NSIDOCUMENTOBSERVER_ENDLOAD
907 NS_DECL_NSIDOCUMENTOBSERVER_CONTENTSTATESCHANGED
908 NS_DECL_NSIDOCUMENTOBSERVER_DOCUMENTSTATESCHANGED
909 NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETADDED
910 NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETREMOVED
911 NS_DECL_NSIDOCUMENTOBSERVER_STYLESHEETAPPLICABLESTATECHANGED
912 NS_DECL_NSIDOCUMENTOBSERVER_STYLERULECHANGED
913 NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEADDED
914 NS_DECL_NSIDOCUMENTOBSERVER_STYLERULEREMOVED
916 // nsIMutationObserver
917 NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
918 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTEWILLCHANGE
919 NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
920 NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
921 NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
922 NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
924 NS_DECL_NSIOBSERVER
926 #ifdef MOZ_REFLOW_PERF
927 virtual NS_HIDDEN_(void) DumpReflows();
928 virtual NS_HIDDEN_(void) CountReflows(const char * aName, nsIFrame * aFrame);
929 virtual NS_HIDDEN_(void) PaintCount(const char * aName,
930 nsIRenderingContext* aRenderingContext,
931 nsPresContext* aPresContext,
932 nsIFrame * aFrame,
933 PRUint32 aColor);
934 virtual NS_HIDDEN_(void) SetPaintFrameCount(PRBool aOn);
935 virtual PRBool IsPaintingFrameCounts();
936 #endif
938 #ifdef DEBUG
939 virtual void ListStyleContexts(nsIFrame *aRootFrame, FILE *out,
940 PRInt32 aIndent = 0);
942 virtual void ListStyleSheets(FILE *out, PRInt32 aIndent = 0);
943 virtual void VerifyStyleTree();
944 #endif
946 #ifdef PR_LOGGING
947 static PRLogModuleInfo* gLog;
948 #endif
950 virtual NS_HIDDEN_(void) DisableNonTestMouseEvents(PRBool aDisable);
952 virtual void UpdateCanvasBackground();
954 virtual nsresult AddCanvasBackgroundColorItem(nsDisplayListBuilder& aBuilder,
955 nsDisplayList& aList,
956 nsIFrame* aFrame,
957 const nsRect& aBounds,
958 nscolor aBackstopColor,
959 PRBool aForceDraw);
961 virtual nsresult AddPrintPreviewBackgroundItem(nsDisplayListBuilder& aBuilder,
962 nsDisplayList& aList,
963 nsIFrame* aFrame,
964 const nsRect& aBounds);
966 virtual nscolor ComputeBackstopColor(nsIView* aDisplayRoot);
968 virtual NS_HIDDEN_(nsresult) SetIsActive(PRBool aIsActive);
970 protected:
971 virtual ~PresShell();
973 void HandlePostedReflowCallbacks(PRBool aInterruptible);
974 void CancelPostedReflowCallbacks();
976 void UnsuppressAndInvalidate();
978 void WillCauseReflow() {
979 nsContentUtils::AddScriptBlocker();
980 ++mChangeNestCount;
982 nsresult DidCauseReflow();
983 friend class nsAutoCauseReflowNotifier;
985 void WillDoReflow();
986 void DidDoReflow(PRBool aInterruptible);
987 // ProcessReflowCommands returns whether we processed all our dirty roots
988 // without interruptions.
989 PRBool ProcessReflowCommands(PRBool aInterruptible);
990 // MaybeScheduleReflow checks if posting a reflow is needed, then checks if
991 // the last reflow was interrupted. In the interrupted case ScheduleReflow is
992 // called off a timer, otherwise it is called directly.
993 void MaybeScheduleReflow();
994 // Actually schedules a reflow. This should only be called by
995 // MaybeScheduleReflow and the reflow timer ScheduleReflowOffTimer
996 // sets up.
997 void ScheduleReflow();
999 // Reflow regardless of whether the override bit has been set.
1000 nsresult ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight);
1002 // DoReflow returns whether the reflow finished without interruption
1003 PRBool DoReflow(nsIFrame* aFrame, PRBool aInterruptible);
1004 #ifdef DEBUG
1005 void DoVerifyReflow();
1006 void VerifyHasDirtyRootAncestor(nsIFrame* aFrame);
1007 #endif
1009 // Helper for ScrollContentIntoView
1010 void DoScrollContentIntoView(nsIContent* aContent,
1011 PRIntn aVPercent,
1012 PRIntn aHPercent,
1013 PRUint32 aFlags);
1015 friend struct AutoRenderingStateSaveRestore;
1016 friend struct RenderingState;
1018 struct RenderingState {
1019 RenderingState(PresShell* aPresShell)
1020 : mRenderFlags(aPresShell->mRenderFlags)
1021 , mDisplayPort(aPresShell->mDisplayPort)
1022 , mXResolution(aPresShell->mXResolution)
1023 , mYResolution(aPresShell->mYResolution)
1025 PRUint32 mRenderFlags;
1026 nsRect mDisplayPort;
1027 float mXResolution;
1028 float mYResolution;
1031 struct AutoSaveRestoreRenderingState {
1032 AutoSaveRestoreRenderingState(PresShell* aPresShell)
1033 : mPresShell(aPresShell)
1034 , mOldState(aPresShell)
1037 ~AutoSaveRestoreRenderingState()
1039 mPresShell->mRenderFlags = mOldState.mRenderFlags;
1040 mPresShell->mDisplayPort = mOldState.mDisplayPort;
1041 mPresShell->mXResolution = mOldState.mXResolution;
1042 mPresShell->mYResolution = mOldState.mYResolution;
1045 PresShell* mPresShell;
1046 RenderingState mOldState;
1049 void SetRenderingState(const RenderingState& aState);
1051 friend class nsPresShellEventCB;
1053 PRBool mCaretEnabled;
1054 #ifdef NS_DEBUG
1055 nsresult CloneStyleSet(nsStyleSet* aSet, nsStyleSet** aResult);
1056 PRBool VerifyIncrementalReflow();
1057 PRBool mInVerifyReflow;
1058 void ShowEventTargetDebug();
1059 #endif
1062 * methods that manage rules that are used to implement the associated preferences
1063 * - initially created for bugs 31816, 20760, 22963
1065 nsresult ClearPreferenceStyleRules(void);
1066 nsresult CreatePreferenceStyleSheet(void);
1067 nsresult SetPrefLinkRules(void);
1068 nsresult SetPrefFocusRules(void);
1069 nsresult SetPrefNoScriptRule();
1070 nsresult SetPrefNoFramesRule(void);
1072 // methods for painting a range to an offscreen buffer
1074 // given a display list, clip the items within the list to
1075 // the range
1076 nsRect ClipListToRange(nsDisplayListBuilder *aBuilder,
1077 nsDisplayList* aList,
1078 nsIRange* aRange);
1080 // create a RangePaintInfo for the range aRange containing the
1081 // display list needed to paint the range to a surface
1082 RangePaintInfo* CreateRangePaintInfo(nsIDOMRange* aRange,
1083 nsRect& aSurfaceRect,
1084 PRBool aForPrimarySelection);
1087 * Paint the items to a new surface and return it.
1089 * aSelection - selection being painted, if any
1090 * aRegion - clip region, if any
1091 * aArea - area that the surface occupies, relative to the root frame
1092 * aPoint - reference point, typically the mouse position
1093 * aScreenRect - [out] set to the area of the screen the painted area should
1094 * be displayed at
1096 already_AddRefed<gfxASurface>
1097 PaintRangePaintInfo(nsTArray<nsAutoPtr<RangePaintInfo> >* aItems,
1098 nsISelection* aSelection,
1099 nsIntRegion* aRegion,
1100 nsRect aArea,
1101 nsIntPoint& aPoint,
1102 nsIntRect* aScreenRect);
1105 * Methods to handle changes to user and UA sheet lists that we get
1106 * notified about.
1108 void AddUserSheet(nsISupports* aSheet);
1109 void AddAgentSheet(nsISupports* aSheet);
1110 void RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet);
1112 // Hide a view if it is a popup
1113 void HideViewIfPopup(nsIView* aView);
1115 // Utility method to restore the root scrollframe state
1116 void RestoreRootScrollPosition();
1118 void MaybeReleaseCapturingContent()
1120 nsCOMPtr<nsFrameSelection> frameSelection = FrameSelection();
1121 if (frameSelection) {
1122 frameSelection->SetMouseDownState(PR_FALSE);
1124 if (gCaptureInfo.mContent &&
1125 gCaptureInfo.mContent->GetOwnerDoc() == mDocument) {
1126 SetCapturingContent(nsnull, 0);
1130 nsRefPtr<nsCSSStyleSheet> mPrefStyleSheet; // mStyleSet owns it but we
1131 // maintain a ref, may be null
1132 #ifdef DEBUG
1133 PRUint32 mUpdateCount;
1134 #endif
1135 // reflow roots that need to be reflowed, as both a queue and a hashtable
1136 nsTArray<nsIFrame*> mDirtyRoots;
1138 PRPackedBool mDocumentLoading;
1140 PRPackedBool mIgnoreFrameDestruction;
1141 PRPackedBool mHaveShutDown;
1143 PRPackedBool mViewportOverridden;
1145 PRPackedBool mLastRootReflowHadUnconstrainedHeight;
1147 // This is used to protect ourselves from triggering reflow while in the
1148 // middle of frame construction and the like... it really shouldn't be
1149 // needed, one hopes, but it is for now.
1150 PRUint32 mChangeNestCount;
1152 nsIFrame* mCurrentEventFrame;
1153 nsCOMPtr<nsIContent> mCurrentEventContent;
1154 nsTArray<nsIFrame*> mCurrentEventFrameStack;
1155 nsCOMArray<nsIContent> mCurrentEventContentStack;
1157 nsCOMPtr<nsIContent> mLastAnchorScrolledTo;
1158 nscoord mLastAnchorScrollPositionY;
1159 nsRefPtr<nsCaret> mCaret;
1160 nsRefPtr<nsCaret> mOriginalCaret;
1161 nsPresArena mFrameArena;
1162 StackArena mStackArena;
1163 nsCOMPtr<nsIDragService> mDragService;
1165 #ifdef DEBUG
1166 // The reflow root under which we're currently reflowing. Null when
1167 // not in reflow.
1168 nsIFrame* mCurrentReflowRoot;
1169 #endif
1171 // Set of frames that we should mark with NS_FRAME_HAS_DIRTY_CHILDREN after
1172 // we finish reflowing mCurrentReflowRoot.
1173 nsTHashtable< nsPtrHashKey<nsIFrame> > mFramesToDirty;
1175 // Information needed to properly handle scrolling content into view if the
1176 // pre-scroll reflow flush can be interrupted. mContentToScrollTo is
1177 // non-null between the initial scroll attempt and the first time we finish
1178 // processing all our dirty roots. mContentScrollVPosition and
1179 // mContentScrollHPosition are only used when it's non-null.
1180 nsCOMPtr<nsIContent> mContentToScrollTo;
1181 PRIntn mContentScrollVPosition;
1182 PRIntn mContentScrollHPosition;
1184 class nsDelayedEvent
1186 public:
1187 virtual ~nsDelayedEvent() {};
1188 virtual void Dispatch(PresShell* aShell) {}
1191 class nsDelayedInputEvent : public nsDelayedEvent
1193 public:
1194 virtual void Dispatch(PresShell* aShell)
1196 if (mEvent && mEvent->widget) {
1197 nsCOMPtr<nsIWidget> w = mEvent->widget;
1198 nsEventStatus status;
1199 w->DispatchEvent(mEvent, status);
1203 protected:
1204 void Init(nsInputEvent* aEvent)
1206 mEvent->time = aEvent->time;
1207 mEvent->refPoint = aEvent->refPoint;
1208 mEvent->isShift = aEvent->isShift;
1209 mEvent->isControl = aEvent->isControl;
1210 mEvent->isAlt = aEvent->isAlt;
1211 mEvent->isMeta = aEvent->isMeta;
1214 nsDelayedInputEvent()
1215 : nsDelayedEvent(), mEvent(nsnull) {}
1217 nsInputEvent* mEvent;
1220 class nsDelayedMouseEvent : public nsDelayedInputEvent
1222 public:
1223 nsDelayedMouseEvent(nsMouseEvent* aEvent) : nsDelayedInputEvent()
1225 mEvent = new nsMouseEvent(NS_IS_TRUSTED_EVENT(aEvent),
1226 aEvent->message,
1227 aEvent->widget,
1228 aEvent->reason,
1229 aEvent->context);
1230 if (mEvent) {
1231 Init(aEvent);
1232 static_cast<nsMouseEvent*>(mEvent)->clickCount = aEvent->clickCount;
1236 virtual ~nsDelayedMouseEvent()
1238 delete static_cast<nsMouseEvent*>(mEvent);
1242 class nsDelayedKeyEvent : public nsDelayedInputEvent
1244 public:
1245 nsDelayedKeyEvent(nsKeyEvent* aEvent) : nsDelayedInputEvent()
1247 mEvent = new nsKeyEvent(NS_IS_TRUSTED_EVENT(aEvent),
1248 aEvent->message,
1249 aEvent->widget);
1250 if (mEvent) {
1251 Init(aEvent);
1252 static_cast<nsKeyEvent*>(mEvent)->keyCode = aEvent->keyCode;
1253 static_cast<nsKeyEvent*>(mEvent)->charCode = aEvent->charCode;
1254 static_cast<nsKeyEvent*>(mEvent)->alternativeCharCodes =
1255 aEvent->alternativeCharCodes;
1256 static_cast<nsKeyEvent*>(mEvent)->isChar = aEvent->isChar;
1260 virtual ~nsDelayedKeyEvent()
1262 delete static_cast<nsKeyEvent*>(mEvent);
1266 PRPackedBool mNoDelayedMouseEvents;
1267 PRPackedBool mNoDelayedKeyEvents;
1268 nsTArray<nsAutoPtr<nsDelayedEvent> > mDelayedEvents;
1270 nsCallbackEventRequest* mFirstCallbackEventRequest;
1271 nsCallbackEventRequest* mLastCallbackEventRequest;
1273 PRPackedBool mIsDocumentGone; // We've been disconnected from the document.
1274 // We will refuse to paint the document until either
1275 // (a) our timer fires or (b) all frames are constructed.
1276 PRPackedBool mShouldUnsuppressPainting; // Indicates that it is safe to unlock painting once all pending
1277 // reflows have been processed.
1278 nsCOMPtr<nsITimer> mPaintSuppressionTimer; // This timer controls painting suppression. Until it fires
1279 // or all frames are constructed, we won't paint anything but
1280 // our <body> background and scrollbars.
1281 #define PAINTLOCK_EVENT_DELAY 250 // 250ms. This is actually
1282 // pref-controlled, but we use this
1283 // value if we fail to get the pref
1284 // for any reason.
1286 static void sPaintSuppressionCallback(nsITimer* aTimer, void* aPresShell); // A callback for the timer.
1288 // At least on Win32 and Mac after interupting a reflow we need to post
1289 // the resume reflow event off a timer to avoid event starvation because
1290 // posted messages are processed before other messages when the modal
1291 // moving/sizing loop is running, see bug 491700 for details.
1292 nsCOMPtr<nsITimer> mReflowContinueTimer;
1293 static void sReflowContinueCallback(nsITimer* aTimer, void* aPresShell);
1294 PRBool ScheduleReflowOffTimer();
1296 #ifdef MOZ_REFLOW_PERF
1297 ReflowCountMgr * mReflowCountMgr;
1298 #endif
1300 static PRBool sDisableNonTestMouseEvents;
1302 // false if a check should be done for key/ime events that should be
1303 // retargeted to the currently focused presshell
1304 static PRBool sDontRetargetEvents;
1306 private:
1308 PRBool InZombieDocument(nsIContent *aContent);
1309 already_AddRefed<nsIPresShell> GetParentPresShell();
1310 nsresult RetargetEventToParent(nsGUIEvent* aEvent,
1311 nsEventStatus* aEventStatus);
1313 //helper funcs for event handling
1314 protected:
1315 //protected because nsPresShellEventCB needs this.
1316 nsIFrame* GetCurrentEventFrame();
1317 private:
1318 void PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent);
1319 void PopCurrentEventInfo();
1320 nsresult HandleEventInternal(nsEvent* aEvent, nsIView* aView,
1321 nsEventStatus *aStatus);
1322 nsresult HandlePositionedEvent(nsIView* aView,
1323 nsIFrame* aTargetFrame,
1324 nsGUIEvent* aEvent,
1325 nsEventStatus* aEventStatus);
1326 // This returns the focused DOM window under our top level window.
1327 // I.e., when we are deactive, this returns the *last* focused DOM window.
1328 already_AddRefed<nsPIDOMWindow> GetFocusedDOMWindowInOurWindow();
1331 * This and the next two helper methods are used to target and position the
1332 * context menu when the keyboard shortcut is used to open it.
1334 * If another menu is open, the context menu is opened relative to the
1335 * active menuitem within the menu, or the menu itself if no item is active.
1336 * Otherwise, if the caret is visible, the menu is opened near the caret.
1337 * Otherwise, if a selectable list such as a listbox is focused, the
1338 * current item within the menu is opened relative to this item.
1339 * Otherwise, the context menu is opened at the topleft corner of the
1340 * view.
1342 * Returns true if the context menu event should fire and false if it should
1343 * not.
1345 PRBool AdjustContextMenuKeyEvent(nsMouseEvent* aEvent);
1348 PRBool PrepareToUseCaretPosition(nsIWidget* aEventWidget, nsIntPoint& aTargetPt);
1350 // Get the selected item and coordinates in device pixels relative to root
1351 // document's root view for element, first ensuring the element is onscreen
1352 void GetCurrentItemAndPositionForElement(nsIDOMElement *aCurrentEl,
1353 nsIContent **aTargetToUse,
1354 nsIntPoint& aTargetPt,
1355 nsIWidget *aRootWidget);
1357 void FireResizeEvent();
1358 void FireBeforeResizeEvent();
1359 static void AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell);
1360 nsRevocableEventPtr<nsRunnableMethod<PresShell> > mResizeEvent;
1361 nsCOMPtr<nsITimer> mAsyncResizeEventTimer;
1362 PRPackedBool mAsyncResizeTimerIsActive;
1363 PRPackedBool mInResize;
1365 private:
1366 #ifdef DEBUG
1367 // Ensure that every allocation from the PresArena is eventually freed.
1368 PRUint32 mPresArenaAllocCount;
1369 #endif
1371 public:
1373 PRUint32 EstimateMemoryUsed() {
1374 PRUint32 result = 0;
1376 result += sizeof(PresShell);
1377 result += mStackArena.Size();
1378 result += mFrameArena.Size();
1380 return result;
1383 static PLDHashOperator LiveShellSizeEnumerator(PresShellPtrKey *aEntry,
1384 void *userArg)
1386 PresShell *aShell = static_cast<PresShell*>(aEntry->GetKey());
1387 PRUint32 *val = (PRUint32*)userArg;
1388 *val += aShell->EstimateMemoryUsed();
1389 *val += aShell->mPresContext->EstimateMemoryUsed();
1390 return PL_DHASH_NEXT;
1393 static PLDHashOperator LiveShellBidiSizeEnumerator(PresShellPtrKey *aEntry,
1394 void *userArg)
1396 PresShell *aShell = static_cast<PresShell*>(aEntry->GetKey());
1397 PRUint32 *val = (PRUint32*)userArg;
1398 *val += aShell->mPresContext->GetBidiMemoryUsed();
1399 return PL_DHASH_NEXT;
1402 static PRUint32
1403 EstimateShellsMemory(nsTHashtable<PresShellPtrKey>::Enumerator aEnumerator)
1405 PRUint32 result = 0;
1406 sLiveShells->EnumerateEntries(aEnumerator, &result);
1407 return result;
1411 static PRInt64 SizeOfLayoutMemoryReporter(void *) {
1412 return EstimateShellsMemory(LiveShellSizeEnumerator);
1415 static PRInt64 SizeOfBidiMemoryReporter(void *) {
1416 return EstimateShellsMemory(LiveShellBidiSizeEnumerator);
1419 protected:
1420 void QueryIsActive();
1421 nsresult UpdateImageLockingState();
1424 class nsAutoCauseReflowNotifier
1426 public:
1427 nsAutoCauseReflowNotifier(PresShell* aShell)
1428 : mShell(aShell)
1430 mShell->WillCauseReflow();
1432 ~nsAutoCauseReflowNotifier()
1434 // This check should not be needed. Currently the only place that seem
1435 // to need it is the code that deals with bug 337586.
1436 if (!mShell->mHaveShutDown) {
1437 mShell->DidCauseReflow();
1439 else {
1440 nsContentUtils::RemoveScriptBlocker();
1444 PresShell* mShell;
1447 class NS_STACK_CLASS nsPresShellEventCB : public nsDispatchingCallback
1449 public:
1450 nsPresShellEventCB(PresShell* aPresShell) : mPresShell(aPresShell) {}
1452 virtual void HandleEvent(nsEventChainPostVisitor& aVisitor)
1454 if (aVisitor.mPresContext && aVisitor.mEvent->eventStructType != NS_EVENT) {
1455 nsIFrame* frame = mPresShell->GetCurrentEventFrame();
1456 if (frame) {
1457 frame->HandleEvent(aVisitor.mPresContext,
1458 (nsGUIEvent*) aVisitor.mEvent,
1459 &aVisitor.mEventStatus);
1464 nsRefPtr<PresShell> mPresShell;
1467 PRBool PresShell::sDisableNonTestMouseEvents = PR_FALSE;
1468 PRBool PresShell::sDontRetargetEvents = PR_FALSE;
1470 #ifdef PR_LOGGING
1471 PRLogModuleInfo* PresShell::gLog;
1472 #endif
1474 #ifdef NS_DEBUG
1475 static void
1476 VerifyStyleTree(nsPresContext* aPresContext, nsFrameManager* aFrameManager)
1478 if (nsFrame::GetVerifyStyleTreeEnable()) {
1479 nsIFrame* rootFrame = aFrameManager->GetRootFrame();
1480 aFrameManager->DebugVerifyStyleTree(rootFrame);
1483 #define VERIFY_STYLE_TREE ::VerifyStyleTree(mPresContext, FrameManager())
1484 #else
1485 #define VERIFY_STYLE_TREE
1486 #endif
1488 static PRBool gVerifyReflowEnabled;
1490 PRBool
1491 nsIPresShell::GetVerifyReflowEnable()
1493 #ifdef NS_DEBUG
1494 static PRBool firstTime = PR_TRUE;
1495 if (firstTime) {
1496 firstTime = PR_FALSE;
1497 char* flags = PR_GetEnv("GECKO_VERIFY_REFLOW_FLAGS");
1498 if (flags) {
1499 PRBool error = PR_FALSE;
1501 for (;;) {
1502 char* comma = PL_strchr(flags, ',');
1503 if (comma)
1504 *comma = '\0';
1506 PRBool found = PR_FALSE;
1507 const VerifyReflowFlags* flag = gFlags;
1508 const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
1509 while (flag < limit) {
1510 if (PL_strcasecmp(flag->name, flags) == 0) {
1511 gVerifyReflowFlags |= flag->bit;
1512 found = PR_TRUE;
1513 break;
1515 ++flag;
1518 if (! found)
1519 error = PR_TRUE;
1521 if (! comma)
1522 break;
1524 *comma = ',';
1525 flags = comma + 1;
1528 if (error)
1529 ShowVerifyReflowFlags();
1532 if (VERIFY_REFLOW_ON & gVerifyReflowFlags) {
1533 gVerifyReflowEnabled = PR_TRUE;
1535 printf("Note: verifyreflow is enabled");
1536 if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
1537 printf(" (noisy)");
1539 if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) {
1540 printf(" (all)");
1542 if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
1543 printf(" (show reflow commands)");
1545 if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
1546 printf(" (noisy reflow commands)");
1547 if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) {
1548 printf(" (REALLY noisy reflow commands)");
1551 printf("\n");
1554 #endif
1555 return gVerifyReflowEnabled;
1558 void
1559 nsIPresShell::SetVerifyReflowEnable(PRBool aEnabled)
1561 gVerifyReflowEnabled = aEnabled;
1564 /* virtual */ void
1565 nsIPresShell::AddWeakFrameExternal(nsWeakFrame* aWeakFrame)
1567 AddWeakFrameInternal(aWeakFrame);
1570 void
1571 nsIPresShell::AddWeakFrameInternal(nsWeakFrame* aWeakFrame)
1573 if (aWeakFrame->GetFrame()) {
1574 aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
1576 aWeakFrame->SetPreviousWeakFrame(mWeakFrames);
1577 mWeakFrames = aWeakFrame;
1580 /* virtual */ void
1581 nsIPresShell::RemoveWeakFrameExternal(nsWeakFrame* aWeakFrame)
1583 RemoveWeakFrameInternal(aWeakFrame);
1586 void
1587 nsIPresShell::RemoveWeakFrameInternal(nsWeakFrame* aWeakFrame)
1589 if (mWeakFrames == aWeakFrame) {
1590 mWeakFrames = aWeakFrame->GetPreviousWeakFrame();
1591 return;
1593 nsWeakFrame* nextWeak = mWeakFrames;
1594 while (nextWeak && nextWeak->GetPreviousWeakFrame() != aWeakFrame) {
1595 nextWeak = nextWeak->GetPreviousWeakFrame();
1597 if (nextWeak) {
1598 nextWeak->SetPreviousWeakFrame(aWeakFrame->GetPreviousWeakFrame());
1602 already_AddRefed<nsFrameSelection>
1603 nsIPresShell::FrameSelection()
1605 NS_IF_ADDREF(mSelection);
1606 return mSelection;
1609 //----------------------------------------------------------------------
1611 nsresult
1612 NS_NewPresShell(nsIPresShell** aInstancePtrResult)
1614 NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
1616 if (!aInstancePtrResult)
1617 return NS_ERROR_NULL_POINTER;
1619 *aInstancePtrResult = new PresShell();
1620 if (!*aInstancePtrResult)
1621 return NS_ERROR_OUT_OF_MEMORY;
1623 NS_ADDREF(*aInstancePtrResult);
1624 return NS_OK;
1627 nsTHashtable<PresShell::PresShellPtrKey> *nsIPresShell::sLiveShells = 0;
1629 NS_MEMORY_REPORTER_IMPLEMENT(LayoutPresShell,
1630 "layout/all",
1631 "Memory in use by layout PresShell, PresContext, and other related areas.",
1632 PresShell::SizeOfLayoutMemoryReporter,
1633 nsnull)
1635 NS_MEMORY_REPORTER_IMPLEMENT(LayoutBidi,
1636 "layout/bidi",
1637 "Memory in use by layout Bidi processor.",
1638 PresShell::SizeOfBidiMemoryReporter,
1639 nsnull)
1641 PresShell::PresShell()
1643 mSelection = nsnull;
1644 #ifdef MOZ_REFLOW_PERF
1645 mReflowCountMgr = new ReflowCountMgr();
1646 mReflowCountMgr->SetPresContext(mPresContext);
1647 mReflowCountMgr->SetPresShell(this);
1648 #endif
1649 #ifdef PR_LOGGING
1650 if (! gLog)
1651 gLog = PR_NewLogModule("PresShell");
1652 #endif
1653 mSelectionFlags = nsISelectionDisplay::DISPLAY_TEXT | nsISelectionDisplay::DISPLAY_IMAGES;
1654 mIsThemeSupportDisabled = PR_FALSE;
1655 mIsActive = PR_TRUE;
1656 mFrozen = PR_FALSE;
1657 #ifdef DEBUG
1658 mPresArenaAllocCount = 0;
1659 #endif
1660 mRenderFlags = 0;
1661 mXResolution = 1.0;
1662 mYResolution = 1.0;
1663 mViewportOverridden = PR_FALSE;
1665 static bool registeredReporter = false;
1666 if (!registeredReporter) {
1667 NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(LayoutPresShell));
1668 NS_RegisterMemoryReporter(new NS_MEMORY_REPORTER_NAME(LayoutBidi));
1669 registeredReporter = true;
1672 new (this) nsFrameManager();
1674 sLiveShells->PutEntry(this);
1677 NS_IMPL_ISUPPORTS8(PresShell, nsIPresShell, nsIDocumentObserver,
1678 nsIViewObserver, nsISelectionController,
1679 nsISelectionDisplay, nsIObserver, nsISupportsWeakReference,
1680 nsIMutationObserver)
1682 PresShell::~PresShell()
1684 sLiveShells->RemoveEntry(this);
1686 if (!mHaveShutDown) {
1687 NS_NOTREACHED("Someone did not call nsIPresShell::destroy");
1688 Destroy();
1691 NS_ASSERTION(mCurrentEventContentStack.Count() == 0,
1692 "Huh, event content left on the stack in pres shell dtor!");
1693 NS_ASSERTION(mFirstCallbackEventRequest == nsnull &&
1694 mLastCallbackEventRequest == nsnull,
1695 "post-reflow queues not empty. This means we're leaking");
1697 #ifdef DEBUG
1698 NS_ASSERTION(mPresArenaAllocCount == 0,
1699 "Some pres arena objects were not freed");
1700 #endif
1702 delete mStyleSet;
1703 delete mFrameConstructor;
1705 mCurrentEventContent = nsnull;
1707 NS_IF_RELEASE(mPresContext);
1708 NS_IF_RELEASE(mDocument);
1709 NS_IF_RELEASE(mSelection);
1713 * Initialize the presentation shell. Create view manager and style
1714 * manager.
1716 nsresult
1717 PresShell::Init(nsIDocument* aDocument,
1718 nsPresContext* aPresContext,
1719 nsIViewManager* aViewManager,
1720 nsStyleSet* aStyleSet,
1721 nsCompatibility aCompatMode)
1723 NS_TIME_FUNCTION_MIN(1.0);
1725 NS_PRECONDITION(nsnull != aDocument, "null ptr");
1726 NS_PRECONDITION(nsnull != aPresContext, "null ptr");
1727 NS_PRECONDITION(nsnull != aViewManager, "null ptr");
1728 nsresult result;
1730 if ((nsnull == aDocument) || (nsnull == aPresContext) ||
1731 (nsnull == aViewManager)) {
1732 return NS_ERROR_NULL_POINTER;
1734 if (mDocument) {
1735 NS_WARNING("PresShell double init'ed");
1736 return NS_ERROR_ALREADY_INITIALIZED;
1738 result = mStackArena.Init();
1739 NS_ENSURE_SUCCESS(result, result);
1741 if (!mFramesToDirty.Init()) {
1742 return NS_ERROR_OUT_OF_MEMORY;
1745 mDocument = aDocument;
1746 NS_ADDREF(mDocument);
1747 mViewManager = aViewManager;
1749 // Create our frame constructor.
1750 mFrameConstructor = new nsCSSFrameConstructor(mDocument, this);
1751 NS_ENSURE_TRUE(mFrameConstructor, NS_ERROR_OUT_OF_MEMORY);
1753 // The document viewer owns both view manager and pres shell.
1754 mViewManager->SetViewObserver(this);
1756 // Bind the context to the presentation shell.
1757 mPresContext = aPresContext;
1758 NS_ADDREF(mPresContext);
1759 aPresContext->SetShell(this);
1761 // Now we can initialize the style set.
1762 result = aStyleSet->Init(aPresContext);
1763 NS_ENSURE_SUCCESS(result, result);
1765 // From this point on, any time we return an error we need to make
1766 // sure to null out mStyleSet first, since an error return from this
1767 // method will cause the caller to delete the style set, so we don't
1768 // want to delete it in our destructor.
1769 mStyleSet = aStyleSet;
1771 // Notify our prescontext that it now has a compatibility mode. Note that
1772 // this MUST happen after we set up our style set but before we create any
1773 // frames.
1774 mPresContext->CompatibilityModeChanged();
1776 // setup the preference style rules (no forced reflow), and do it
1777 // before creating any frames.
1778 SetPreferenceStyleRules(PR_FALSE);
1780 result = CallCreateInstance(kFrameSelectionCID, &mSelection);
1781 if (NS_FAILED(result)) {
1782 mStyleSet = nsnull;
1783 return result;
1786 // Create and initialize the frame manager
1787 result = FrameManager()->Init(this, mStyleSet);
1788 if (NS_FAILED(result)) {
1789 NS_WARNING("Frame manager initialization failed");
1790 mStyleSet = nsnull;
1791 return result;
1794 mSelection->Init(this, nsnull);
1796 // Important: this has to happen after the selection has been set up
1797 #ifdef SHOW_CARET
1798 // make the caret
1799 nsresult err = NS_NewCaret(getter_AddRefs(mCaret));
1800 if (NS_SUCCEEDED(err))
1802 mCaret->Init(this);
1803 mOriginalCaret = mCaret;
1806 //SetCaretEnabled(PR_TRUE); // make it show in browser windows
1807 #endif
1808 //set up selection to be displayed in document
1809 // Don't enable selection for print media
1810 nsPresContext::nsPresContextType type = aPresContext->Type();
1811 if (type != nsPresContext::eContext_PrintPreview &&
1812 type != nsPresContext::eContext_Print)
1813 SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
1815 if (gMaxRCProcessingTime == -1) {
1816 gMaxRCProcessingTime =
1817 nsContentUtils::GetIntPref("layout.reflow.timeslice",
1818 NS_MAX_REFLOW_TIME);
1822 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1823 if (os) {
1824 os->AddObserver(this, "agent-sheet-added", PR_FALSE);
1825 os->AddObserver(this, "user-sheet-added", PR_FALSE);
1826 os->AddObserver(this, "agent-sheet-removed", PR_FALSE);
1827 os->AddObserver(this, "user-sheet-removed", PR_FALSE);
1828 #ifdef MOZ_XUL
1829 os->AddObserver(this, "chrome-flush-skin-caches", PR_FALSE);
1830 #endif
1831 #ifdef ACCESSIBILITY
1832 os->AddObserver(this, "a11y-init-or-shutdown", PR_FALSE);
1833 #endif
1837 // cache the drag service so we can check it during reflows
1838 mDragService = do_GetService("@mozilla.org/widget/dragservice;1");
1840 #ifdef MOZ_REFLOW_PERF
1841 if (mReflowCountMgr) {
1842 PRBool paintFrameCounts =
1843 nsContentUtils::GetBoolPref("layout.reflow.showframecounts");
1845 PRBool dumpFrameCounts =
1846 nsContentUtils::GetBoolPref("layout.reflow.dumpframecounts");
1848 PRBool dumpFrameByFrameCounts =
1849 nsContentUtils::GetBoolPref("layout.reflow.dumpframebyframecounts");
1851 mReflowCountMgr->SetDumpFrameCounts(dumpFrameCounts);
1852 mReflowCountMgr->SetDumpFrameByFrameCounts(dumpFrameByFrameCounts);
1853 mReflowCountMgr->SetPaintFrameCounts(paintFrameCounts);
1855 #endif
1857 #ifdef MOZ_SMIL
1858 if (mDocument->HasAnimationController()) {
1859 nsSMILAnimationController* animCtrl = mDocument->GetAnimationController();
1860 animCtrl->NotifyRefreshDriverCreated(GetPresContext()->RefreshDriver());
1862 #endif // MOZ_SMIL
1864 // Get our activeness from the docShell.
1865 QueryIsActive();
1867 return NS_OK;
1870 void
1871 PresShell::Destroy()
1873 NS_TIME_FUNCTION_MIN(1.0);
1875 NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
1876 "destroy called on presshell while scripts not blocked");
1878 #ifdef MOZ_REFLOW_PERF
1879 DumpReflows();
1880 if (mReflowCountMgr) {
1881 delete mReflowCountMgr;
1882 mReflowCountMgr = nsnull;
1884 #endif
1886 if (mHaveShutDown)
1887 return;
1889 #ifdef ACCESSIBILITY
1890 if (gIsAccessibilityActive) {
1891 nsCOMPtr<nsIAccessibilityService> accService =
1892 do_GetService("@mozilla.org/accessibilityService;1");
1893 if (accService) {
1894 accService->PresShellDestroyed(this);
1897 #endif // ACCESSIBILITY
1899 MaybeReleaseCapturingContent();
1901 if (gKeyDownTarget && gKeyDownTarget->GetOwnerDoc() == mDocument) {
1902 NS_RELEASE(gKeyDownTarget);
1905 mContentToScrollTo = nsnull;
1907 if (mPresContext) {
1908 // We need to notify the destroying the nsPresContext to ESM for
1909 // suppressing to use from ESM.
1910 mPresContext->EventStateManager()->NotifyDestroyPresContext(mPresContext);
1914 nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
1915 if (os) {
1916 os->RemoveObserver(this, "agent-sheet-added");
1917 os->RemoveObserver(this, "user-sheet-added");
1918 os->RemoveObserver(this, "agent-sheet-removed");
1919 os->RemoveObserver(this, "user-sheet-removed");
1920 #ifdef MOZ_XUL
1921 os->RemoveObserver(this, "chrome-flush-skin-caches");
1922 #endif
1923 #ifdef ACCESSIBILITY
1924 os->RemoveObserver(this, "a11y-init-or-shutdown");
1925 #endif
1929 // If our paint suppression timer is still active, kill it.
1930 if (mPaintSuppressionTimer) {
1931 mPaintSuppressionTimer->Cancel();
1932 mPaintSuppressionTimer = nsnull;
1935 // Same for our reflow continuation timer
1936 if (mReflowContinueTimer) {
1937 mReflowContinueTimer->Cancel();
1938 mReflowContinueTimer = nsnull;
1941 if (mCaret) {
1942 mCaret->Terminate();
1943 mCaret = nsnull;
1946 if (mSelection) {
1947 mSelection->DisconnectFromPresShell();
1950 // release our pref style sheet, if we have one still
1951 ClearPreferenceStyleRules();
1953 mIsDestroying = PR_TRUE;
1955 // We can't release all the event content in
1956 // mCurrentEventContentStack here since there might be code on the
1957 // stack that will release the event content too. Double release
1958 // bad!
1960 // The frames will be torn down, so remove them from the current
1961 // event frame stack (since they'd be dangling references if we'd
1962 // leave them in) and null out the mCurrentEventFrame pointer as
1963 // well.
1965 mCurrentEventFrame = nsnull;
1967 PRInt32 i, count = mCurrentEventFrameStack.Length();
1968 for (i = 0; i < count; i++) {
1969 mCurrentEventFrameStack[i] = nsnull;
1972 mFramesToDirty.Clear();
1974 if (mViewManager) {
1975 // Clear the view manager's weak pointer back to |this| in case it
1976 // was leaked.
1977 mViewManager->SetViewObserver(nsnull);
1978 mViewManager = nsnull;
1981 mStyleSet->BeginShutdown(mPresContext);
1982 nsRefreshDriver* rd = GetPresContext()->RefreshDriver();
1984 // This shell must be removed from the document before the frame
1985 // hierarchy is torn down to avoid finding deleted frames through
1986 // this presshell while the frames are being torn down
1987 if (mDocument) {
1988 NS_ASSERTION(mDocument->GetShell() == this, "Wrong shell?");
1989 mDocument->DeleteShell();
1991 #ifdef MOZ_SMIL
1992 if (mDocument->HasAnimationController()) {
1993 mDocument->GetAnimationController()->NotifyRefreshDriverDestroying(rd);
1995 #endif // MOZ_SMIL
1998 // Revoke any pending events. We need to do this and cancel pending reflows
1999 // before we destroy the frame manager, since apparently frame destruction
2000 // sometimes spins the event queue when plug-ins are involved(!).
2001 rd->RemoveLayoutFlushObserver(this);
2002 mResizeEvent.Revoke();
2003 if (mAsyncResizeTimerIsActive) {
2004 mAsyncResizeEventTimer->Cancel();
2005 mAsyncResizeTimerIsActive = PR_FALSE;
2008 CancelAllPendingReflows();
2009 CancelPostedReflowCallbacks();
2011 // Destroy the frame manager. This will destroy the frame hierarchy
2012 mFrameConstructor->WillDestroyFrameTree();
2013 FrameManager()->Destroy();
2015 // Destroy all frame properties (whose destruction was suppressed
2016 // while destroying the frame tree, but which might contain more
2017 // frames within the properties.
2018 if (mPresContext) {
2019 // Clear out the prescontext's property table -- since our frame tree is
2020 // now dead, we shouldn't be looking up any more properties in that table.
2021 // We want to do this before we call SetShell() on the prescontext, so
2022 // property destructors can usefully call GetPresShell() on the
2023 // prescontext.
2024 mPresContext->PropertyTable()->DeleteAll();
2028 NS_WARN_IF_FALSE(!mWeakFrames, "Weak frames alive after destroying FrameManager");
2029 while (mWeakFrames) {
2030 mWeakFrames->Clear(this);
2033 // Let the style set do its cleanup.
2034 mStyleSet->Shutdown(mPresContext);
2036 if (mPresContext) {
2037 // We hold a reference to the pres context, and it holds a weak link back
2038 // to us. To avoid the pres context having a dangling reference, set its
2039 // pres shell to NULL
2040 mPresContext->SetShell(nsnull);
2042 // Clear the link handler (weak reference) as well
2043 mPresContext->SetLinkHandler(nsnull);
2046 mHaveShutDown = PR_TRUE;
2049 // Dynamic stack memory allocation
2050 /* virtual */ void
2051 PresShell::PushStackMemory()
2053 mStackArena.Push();
2056 /* virtual */ void
2057 PresShell::PopStackMemory()
2059 mStackArena.Pop();
2062 /* virtual */ void*
2063 PresShell::AllocateStackMemory(size_t aSize)
2065 return mStackArena.Allocate(aSize);
2068 void
2069 PresShell::FreeFrame(nsQueryFrame::FrameIID aCode, void* aPtr)
2071 #ifdef DEBUG
2072 mPresArenaAllocCount--;
2073 #endif
2074 if (PRESARENA_MUST_FREE_DURING_DESTROY || !mIsDestroying)
2075 mFrameArena.FreeByCode(aCode, aPtr);
2078 void*
2079 PresShell::AllocateFrame(nsQueryFrame::FrameIID aCode, size_t aSize)
2081 #ifdef DEBUG
2082 mPresArenaAllocCount++;
2083 #endif
2084 void* result = mFrameArena.AllocateByCode(aCode, aSize);
2086 if (result) {
2087 memset(result, 0, aSize);
2089 return result;
2092 void
2093 PresShell::FreeMisc(size_t aSize, void* aPtr)
2095 #ifdef DEBUG
2096 mPresArenaAllocCount--;
2097 #endif
2098 if (PRESARENA_MUST_FREE_DURING_DESTROY || !mIsDestroying)
2099 mFrameArena.FreeBySize(aSize, aPtr);
2102 void*
2103 PresShell::AllocateMisc(size_t aSize)
2105 #ifdef DEBUG
2106 mPresArenaAllocCount++;
2107 #endif
2108 return mFrameArena.AllocateBySize(aSize);
2111 void
2112 nsIPresShell::SetAuthorStyleDisabled(PRBool aStyleDisabled)
2114 if (aStyleDisabled != mStyleSet->GetAuthorStyleDisabled()) {
2115 mStyleSet->SetAuthorStyleDisabled(aStyleDisabled);
2116 ReconstructStyleData();
2120 PRBool
2121 nsIPresShell::GetAuthorStyleDisabled() const
2123 return mStyleSet->GetAuthorStyleDisabled();
2126 nsresult
2127 PresShell::SetPreferenceStyleRules(PRBool aForceReflow)
2129 NS_TIME_FUNCTION_MIN(1.0);
2131 if (!mDocument) {
2132 return NS_ERROR_NULL_POINTER;
2135 nsPIDOMWindow *window = mDocument->GetWindow();
2137 // If the document doesn't have a window there's no need to notify
2138 // its presshell about changes to preferences since the document is
2139 // in a state where it doesn't matter any more (see
2140 // DocumentViewerImpl::Close()).
2142 if (!window) {
2143 return NS_ERROR_NULL_POINTER;
2146 NS_PRECONDITION(mPresContext, "presContext cannot be null");
2147 if (mPresContext) {
2148 // first, make sure this is not a chrome shell
2149 if (nsContentUtils::IsInChromeDocshell(mDocument)) {
2150 return NS_OK;
2153 #ifdef DEBUG_attinasi
2154 printf("Setting Preference Style Rules:\n");
2155 #endif
2156 // if here, we need to create rules for the prefs
2157 // - this includes the background-color, the text-color,
2158 // the link color, the visited link color and the link-underlining
2160 // first clear any exising rules
2161 nsresult result = ClearPreferenceStyleRules();
2163 // now the link rules (must come after the color rules, or links will not be correct color!)
2164 // XXX - when there is both an override and agent pref stylesheet this won't matter,
2165 // as the color rules will be overrides and the links rules will be agent
2166 if (NS_SUCCEEDED(result)) {
2167 result = SetPrefLinkRules();
2169 if (NS_SUCCEEDED(result)) {
2170 result = SetPrefFocusRules();
2172 if (NS_SUCCEEDED(result)) {
2173 result = SetPrefNoScriptRule();
2175 if (NS_SUCCEEDED(result)) {
2176 result = SetPrefNoFramesRule();
2178 #ifdef DEBUG_attinasi
2179 printf( "Preference Style Rules set: error=%ld\n", (long)result);
2180 #endif
2182 // Note that this method never needs to force any calculation; the caller
2183 // will recalculate style if needed
2185 return result;
2188 return NS_ERROR_NULL_POINTER;
2191 nsresult PresShell::ClearPreferenceStyleRules(void)
2193 nsresult result = NS_OK;
2194 if (mPrefStyleSheet) {
2195 NS_ASSERTION(mStyleSet, "null styleset entirely unexpected!");
2196 if (mStyleSet) {
2197 // remove the sheet from the styleset:
2198 // - note that we have to check for success by comparing the count before and after...
2199 #ifdef NS_DEBUG
2200 PRInt32 numBefore = mStyleSet->SheetCount(nsStyleSet::eUserSheet);
2201 NS_ASSERTION(numBefore > 0, "no user stylesheets in styleset, but we have one!");
2202 #endif
2203 mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet);
2205 #ifdef DEBUG_attinasi
2206 NS_ASSERTION((numBefore - 1) == mStyleSet->GetNumberOfUserStyleSheets(),
2207 "Pref stylesheet was not removed");
2208 printf("PrefStyleSheet removed\n");
2209 #endif
2210 // clear the sheet pointer: it is strictly historical now
2211 mPrefStyleSheet = nsnull;
2214 return result;
2217 nsresult PresShell::CreatePreferenceStyleSheet(void)
2219 NS_TIME_FUNCTION_MIN(1.0);
2221 NS_ASSERTION(!mPrefStyleSheet, "prefStyleSheet already exists");
2222 nsresult result = NS_NewCSSStyleSheet(getter_AddRefs(mPrefStyleSheet));
2223 if (NS_SUCCEEDED(result)) {
2224 NS_ASSERTION(mPrefStyleSheet, "null but no error");
2225 nsCOMPtr<nsIURI> uri;
2226 result = NS_NewURI(getter_AddRefs(uri), "about:PreferenceStyleSheet", nsnull);
2227 if (NS_SUCCEEDED(result)) {
2228 NS_ASSERTION(uri, "null but no error");
2229 mPrefStyleSheet->SetURIs(uri, uri, uri);
2230 mPrefStyleSheet->SetComplete();
2231 PRUint32 index;
2232 result =
2233 mPrefStyleSheet->InsertRuleInternal(NS_LITERAL_STRING("@namespace url(http://www.w3.org/1999/xhtml);"),
2234 0, &index);
2235 if (NS_SUCCEEDED(result)) {
2236 mStyleSet->AppendStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet);
2241 #ifdef DEBUG_attinasi
2242 printf("CreatePrefStyleSheet completed: error=%ld\n",(long)result);
2243 #endif
2245 if (NS_FAILED(result)) {
2246 mPrefStyleSheet = nsnull;
2249 return result;
2252 // XXX We want these after the @namespace rule. Does order matter
2253 // for these rules, or can we call nsICSSStyleRule::StyleRuleCount()
2254 // and just "append"?
2255 static PRUint32 sInsertPrefSheetRulesAt = 1;
2257 nsresult
2258 PresShell::SetPrefNoScriptRule()
2260 NS_TIME_FUNCTION_MIN(1.0);
2262 nsresult rv = NS_OK;
2264 // also handle the case where print is done from print preview
2265 // see bug #342439 for more details
2266 nsIDocument* doc = mDocument;
2267 if (mPresContext->Type() == nsPresContext::eContext_PrintPreview ||
2268 mPresContext->Type() == nsPresContext::eContext_Print) {
2269 while (doc->GetOriginalDocument()) {
2270 doc = doc->GetOriginalDocument();
2274 PRBool scriptEnabled = doc->IsScriptEnabled();
2275 if (scriptEnabled) {
2276 if (!mPrefStyleSheet) {
2277 rv = CreatePreferenceStyleSheet();
2278 NS_ENSURE_SUCCESS(rv, rv);
2281 PRUint32 index = 0;
2282 mPrefStyleSheet->
2283 InsertRuleInternal(NS_LITERAL_STRING("noscript{display:none!important}"),
2284 sInsertPrefSheetRulesAt, &index);
2287 return rv;
2290 nsresult PresShell::SetPrefNoFramesRule(void)
2292 NS_TIME_FUNCTION_MIN(1.0);
2294 NS_ASSERTION(mPresContext,"null prescontext not allowed");
2295 if (!mPresContext) {
2296 return NS_ERROR_FAILURE;
2299 nsresult rv = NS_OK;
2301 if (!mPrefStyleSheet) {
2302 rv = CreatePreferenceStyleSheet();
2303 NS_ENSURE_SUCCESS(rv, rv);
2306 NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
2308 PRBool allowSubframes = PR_TRUE;
2309 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
2310 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
2311 if (docShell) {
2312 docShell->GetAllowSubframes(&allowSubframes);
2314 if (!allowSubframes) {
2315 PRUint32 index = 0;
2316 rv = mPrefStyleSheet->
2317 InsertRuleInternal(NS_LITERAL_STRING("noframes{display:block}"),
2318 sInsertPrefSheetRulesAt, &index);
2319 NS_ENSURE_SUCCESS(rv, rv);
2320 rv = mPrefStyleSheet->
2321 InsertRuleInternal(NS_LITERAL_STRING("frame, frameset, iframe {display:none!important}"),
2322 sInsertPrefSheetRulesAt, &index);
2324 return rv;
2327 nsresult PresShell::SetPrefLinkRules(void)
2329 NS_TIME_FUNCTION_MIN(1.0);
2331 NS_ASSERTION(mPresContext,"null prescontext not allowed");
2332 if (!mPresContext) {
2333 return NS_ERROR_FAILURE;
2336 nsresult rv = NS_OK;
2338 if (!mPrefStyleSheet) {
2339 rv = CreatePreferenceStyleSheet();
2340 NS_ENSURE_SUCCESS(rv, rv);
2343 NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
2345 // support default link colors:
2346 // this means the link colors need to be overridable,
2347 // which they are if we put them in the agent stylesheet,
2348 // though if using an override sheet this will cause authors grief still
2349 // In the agent stylesheet, they are !important when we are ignoring document colors
2351 nscolor linkColor(mPresContext->DefaultLinkColor());
2352 nscolor activeColor(mPresContext->DefaultActiveLinkColor());
2353 nscolor visitedColor(mPresContext->DefaultVisitedLinkColor());
2355 NS_NAMED_LITERAL_STRING(ruleClose, "}");
2356 PRUint32 index = 0;
2357 nsAutoString strColor;
2359 // insert a rule to color links: '*|*:link {color: #RRGGBB [!important];}'
2360 ColorToString(linkColor, strColor);
2361 rv = mPrefStyleSheet->
2362 InsertRuleInternal(NS_LITERAL_STRING("*|*:link{color:") +
2363 strColor + ruleClose,
2364 sInsertPrefSheetRulesAt, &index);
2365 NS_ENSURE_SUCCESS(rv, rv);
2367 // - visited links: '*|*:visited {color: #RRGGBB [!important];}'
2368 ColorToString(visitedColor, strColor);
2369 rv = mPrefStyleSheet->
2370 InsertRuleInternal(NS_LITERAL_STRING("*|*:visited{color:") +
2371 strColor + ruleClose,
2372 sInsertPrefSheetRulesAt, &index);
2373 NS_ENSURE_SUCCESS(rv, rv);
2375 // - active links: '*|*:-moz-any-link:active {color: #RRGGBB [!important];}'
2376 ColorToString(activeColor, strColor);
2377 rv = mPrefStyleSheet->
2378 InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link:active{color:") +
2379 strColor + ruleClose,
2380 sInsertPrefSheetRulesAt, &index);
2381 NS_ENSURE_SUCCESS(rv, rv);
2383 PRBool underlineLinks =
2384 mPresContext->GetCachedBoolPref(kPresContext_UnderlineLinks);
2386 if (underlineLinks) {
2387 // create a rule to make underlining happen
2388 // '*|*:-moz-any-link {text-decoration:[underline|none];}'
2389 // no need for important, we want these to be overridable
2390 // NOTE: these must go in the agent stylesheet or they cannot be
2391 // overridden by authors
2392 rv = mPrefStyleSheet->
2393 InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link{text-decoration:underline}"),
2394 sInsertPrefSheetRulesAt, &index);
2395 } else {
2396 rv = mPrefStyleSheet->
2397 InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link{text-decoration:none}"),
2398 sInsertPrefSheetRulesAt, &index);
2401 return rv;
2404 nsresult PresShell::SetPrefFocusRules(void)
2406 NS_TIME_FUNCTION_MIN(1.0);
2408 NS_ASSERTION(mPresContext,"null prescontext not allowed");
2409 nsresult result = NS_OK;
2411 if (!mPresContext)
2412 result = NS_ERROR_FAILURE;
2414 if (NS_SUCCEEDED(result) && !mPrefStyleSheet)
2415 result = CreatePreferenceStyleSheet();
2417 if (NS_SUCCEEDED(result)) {
2418 NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
2420 if (mPresContext->GetUseFocusColors()) {
2421 nscolor focusBackground(mPresContext->FocusBackgroundColor());
2422 nscolor focusText(mPresContext->FocusTextColor());
2424 // insert a rule to make focus the preferred color
2425 PRUint32 index = 0;
2426 nsAutoString strRule, strColor;
2428 ///////////////////////////////////////////////////////////////
2429 // - focus: '*:focus
2430 ColorToString(focusText,strColor);
2431 strRule.AppendLiteral("*:focus,*:focus>font {color: ");
2432 strRule.Append(strColor);
2433 strRule.AppendLiteral(" !important; background-color: ");
2434 ColorToString(focusBackground,strColor);
2435 strRule.Append(strColor);
2436 strRule.AppendLiteral(" !important; } ");
2437 // insert the rules
2438 result = mPrefStyleSheet->
2439 InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
2441 PRUint8 focusRingWidth = mPresContext->FocusRingWidth();
2442 PRBool focusRingOnAnything = mPresContext->GetFocusRingOnAnything();
2443 PRUint8 focusRingStyle = mPresContext->GetFocusRingStyle();
2445 if ((NS_SUCCEEDED(result) && focusRingWidth != 1 && focusRingWidth <= 4 ) || focusRingOnAnything) {
2446 PRUint32 index = 0;
2447 nsAutoString strRule;
2448 if (!focusRingOnAnything)
2449 strRule.AppendLiteral("*|*:link:focus, *|*:visited"); // If we only want focus rings on the normal things like links
2450 strRule.AppendLiteral(":focus {outline: "); // For example 3px dotted WindowText (maximum 4)
2451 strRule.AppendInt(focusRingWidth);
2452 if (focusRingStyle == 0) // solid
2453 strRule.AppendLiteral("px solid -moz-mac-focusring !important; -moz-outline-radius: 3px; outline-offset: 1px; } ");
2454 else // dotted
2455 strRule.AppendLiteral("px dotted WindowText !important; } ");
2456 // insert the rules
2457 result = mPrefStyleSheet->
2458 InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
2459 NS_ENSURE_SUCCESS(result, result);
2460 if (focusRingWidth != 1) {
2461 // If the focus ring width is different from the default, fix buttons with rings
2462 strRule.AssignLiteral("button::-moz-focus-inner, input[type=\"reset\"]::-moz-focus-inner,");
2463 strRule.AppendLiteral("input[type=\"button\"]::-moz-focus-inner, ");
2464 strRule.AppendLiteral("input[type=\"submit\"]::-moz-focus-inner { padding: 1px 2px 1px 2px; border: ");
2465 strRule.AppendInt(focusRingWidth);
2466 if (focusRingStyle == 0) // solid
2467 strRule.AppendLiteral("px solid transparent !important; } ");
2468 else
2469 strRule.AppendLiteral("px dotted transparent !important; } ");
2470 result = mPrefStyleSheet->
2471 InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
2472 NS_ENSURE_SUCCESS(result, result);
2474 strRule.AssignLiteral("button:focus::-moz-focus-inner, input[type=\"reset\"]:focus::-moz-focus-inner,");
2475 strRule.AppendLiteral("input[type=\"button\"]:focus::-moz-focus-inner, input[type=\"submit\"]:focus::-moz-focus-inner {");
2476 strRule.AppendLiteral("border-color: ButtonText !important; }");
2477 result = mPrefStyleSheet->
2478 InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
2482 return result;
2485 void
2486 PresShell::AddUserSheet(nsISupports* aSheet)
2488 // Make sure this does what DocumentViewerImpl::CreateStyleSet does wrt
2489 // ordering. We want this new sheet to come after all the existing stylesheet
2490 // service sheets, but before other user sheets; see nsIStyleSheetService.idl
2491 // for the ordering. Just remove and readd all the nsStyleSheetService
2492 // sheets.
2493 nsCOMPtr<nsIStyleSheetService> dummy =
2494 do_GetService(NS_STYLESHEETSERVICE_CONTRACTID);
2496 mStyleSet->BeginUpdate();
2498 nsStyleSheetService *sheetService = nsStyleSheetService::gInstance;
2499 nsCOMArray<nsIStyleSheet> & userSheets = *sheetService->UserStyleSheets();
2500 PRInt32 i;
2501 // Iterate forwards when removing so the searches for RemoveStyleSheet are as
2502 // short as possible.
2503 for (i = 0; i < userSheets.Count(); ++i) {
2504 mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, userSheets[i]);
2507 // Now iterate backwards, so that the order of userSheets will be the same as
2508 // the order of sheets from it in the style set.
2509 for (i = userSheets.Count() - 1; i >= 0; --i) {
2510 mStyleSet->PrependStyleSheet(nsStyleSet::eUserSheet, userSheets[i]);
2513 mStyleSet->EndUpdate();
2515 ReconstructStyleData();
2518 void
2519 PresShell::AddAgentSheet(nsISupports* aSheet)
2521 // Make sure this does what DocumentViewerImpl::CreateStyleSet does
2522 // wrt ordering.
2523 nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet);
2524 if (!sheet) {
2525 return;
2528 mStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet);
2529 ReconstructStyleData();
2532 void
2533 PresShell::RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet)
2535 nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet);
2536 if (!sheet) {
2537 return;
2540 mStyleSet->RemoveStyleSheet(aType, sheet);
2541 ReconstructStyleData();
2544 NS_IMETHODIMP
2545 PresShell::SetDisplaySelection(PRInt16 aToggle)
2547 mSelection->SetDisplaySelection(aToggle);
2548 return NS_OK;
2551 NS_IMETHODIMP
2552 PresShell::GetDisplaySelection(PRInt16 *aToggle)
2554 *aToggle = mSelection->GetDisplaySelection();
2555 return NS_OK;
2558 NS_IMETHODIMP
2559 PresShell::GetSelection(SelectionType aType, nsISelection **aSelection)
2561 if (!aSelection || !mSelection)
2562 return NS_ERROR_NULL_POINTER;
2564 *aSelection = mSelection->GetSelection(aType);
2566 if (!(*aSelection))
2567 return NS_ERROR_INVALID_ARG;
2569 NS_ADDREF(*aSelection);
2571 return NS_OK;
2574 nsISelection*
2575 PresShell::GetCurrentSelection(SelectionType aType)
2577 if (!mSelection)
2578 return nsnull;
2580 return mSelection->GetSelection(aType);
2583 NS_IMETHODIMP
2584 PresShell::ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion,
2585 PRInt16 aFlags)
2587 if (!mSelection)
2588 return NS_ERROR_NULL_POINTER;
2590 return mSelection->ScrollSelectionIntoView(aType, aRegion, aFlags);
2593 NS_IMETHODIMP
2594 PresShell::RepaintSelection(SelectionType aType)
2596 if (!mSelection)
2597 return NS_ERROR_NULL_POINTER;
2599 return mSelection->RepaintSelection(aType);
2602 // Make shell be a document observer
2603 void
2604 PresShell::BeginObservingDocument()
2606 if (mDocument && !mIsDestroying) {
2607 mDocument->AddObserver(this);
2608 if (mIsDocumentGone) {
2609 NS_WARNING("Adding a presshell that was disconnected from the document "
2610 "as a document observer? Sounds wrong...");
2611 mIsDocumentGone = PR_FALSE;
2616 // Make shell stop being a document observer
2617 void
2618 PresShell::EndObservingDocument()
2620 // XXXbz do we need to tell the frame constructor that the document
2621 // is gone, perhaps? Except for printing it's NOT gone, sometimes.
2622 mIsDocumentGone = PR_TRUE;
2623 if (mDocument) {
2624 mDocument->RemoveObserver(this);
2628 #ifdef DEBUG_kipp
2629 char* nsPresShell_ReflowStackPointerTop;
2630 #endif
2632 nsresult
2633 PresShell::InitialReflow(nscoord aWidth, nscoord aHeight)
2635 if (mIsDestroying) {
2636 return NS_OK;
2639 if (!mDocument) {
2640 // Nothing to do
2641 return NS_OK;
2644 NS_TIME_FUNCTION_WITH_DOCURL;
2646 NS_ASSERTION(!mDidInitialReflow, "Why are we being called?");
2648 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
2649 mDidInitialReflow = PR_TRUE;
2651 #ifdef NS_DEBUG
2652 if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
2653 if (mDocument) {
2654 nsIURI *uri = mDocument->GetDocumentURI();
2655 if (uri) {
2656 nsCAutoString url;
2657 uri->GetSpec(url);
2658 printf("*** PresShell::InitialReflow (this=%p, url='%s')\n", (void*)this, url.get());
2662 #endif
2664 if (mCaret)
2665 mCaret->EraseCaret();
2667 // XXX Do a full invalidate at the beginning so that invalidates along
2668 // the way don't have region accumulation issues?
2670 mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
2672 // Get the root frame from the frame manager
2673 // XXXbz it would be nice to move this somewhere else... like frame manager
2674 // Init(), say. But we need to make sure our views are all set up by the
2675 // time we do this!
2676 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
2677 NS_ASSERTION(!rootFrame, "How did that happen, exactly?");
2678 if (!rootFrame) {
2679 nsAutoScriptBlocker scriptBlocker;
2680 mFrameConstructor->BeginUpdate();
2681 mFrameConstructor->ConstructRootFrame(&rootFrame);
2682 FrameManager()->SetRootFrame(rootFrame);
2683 mFrameConstructor->EndUpdate();
2686 NS_ENSURE_STATE(!mHaveShutDown);
2688 if (!rootFrame) {
2689 return NS_ERROR_OUT_OF_MEMORY;
2692 Element *root = mDocument->GetRootElement();
2694 if (root) {
2696 nsAutoCauseReflowNotifier reflowNotifier(this);
2697 mFrameConstructor->BeginUpdate();
2699 // Have the style sheet processor construct frame for the root
2700 // content object down
2701 mFrameConstructor->ContentInserted(nsnull, root, nsnull, PR_FALSE);
2702 VERIFY_STYLE_TREE;
2704 // Something in mFrameConstructor->ContentInserted may have caused
2705 // Destroy() to get called, bug 337586.
2706 NS_ENSURE_STATE(!mHaveShutDown);
2708 mFrameConstructor->EndUpdate();
2711 // nsAutoScriptBlocker going out of scope may have killed us too
2712 NS_ENSURE_STATE(!mHaveShutDown);
2714 // Run the XBL binding constructors for any new frames we've constructed
2715 mDocument->BindingManager()->ProcessAttachedQueue();
2717 NS_TIME_FUNCTION_MARK("XBL binding constructors fired");
2719 // Constructors may have killed us too
2720 NS_ENSURE_STATE(!mHaveShutDown);
2722 // Now flush out pending restyles before we actually reflow, in
2723 // case XBL constructors changed styles somewhere.
2725 nsAutoScriptBlocker scriptBlocker;
2726 mFrameConstructor->CreateNeededFrames();
2727 mFrameConstructor->ProcessPendingRestyles();
2730 // And that might have run _more_ XBL constructors
2731 NS_ENSURE_STATE(!mHaveShutDown);
2734 NS_ASSERTION(rootFrame, "How did that happen?");
2736 // Note: Because the frame just got created, it has the NS_FRAME_IS_DIRTY
2737 // bit set. Unset it so that FrameNeedsReflow() will work right.
2738 NS_ASSERTION(!mDirtyRoots.Contains(rootFrame),
2739 "Why is the root in mDirtyRoots already?");
2741 rootFrame->RemoveStateBits(NS_FRAME_IS_DIRTY |
2742 NS_FRAME_HAS_DIRTY_CHILDREN);
2743 FrameNeedsReflow(rootFrame, eResize, NS_FRAME_IS_DIRTY);
2745 NS_ASSERTION(mDirtyRoots.Contains(rootFrame),
2746 "Should be in mDirtyRoots now");
2747 NS_ASSERTION(mReflowScheduled, "Why no reflow scheduled?");
2749 // Restore our root scroll position now if we're getting here after EndLoad
2750 // got called, since this is our one chance to do it. Note that we need not
2751 // have reflowed for this to work; when the scrollframe is finally reflowed
2752 // it'll puick up the position we store in it here.
2753 if (!mDocumentLoading) {
2754 RestoreRootScrollPosition();
2757 // For printing, we just immediately unsuppress.
2758 if (!mPresContext->IsPaginated()) {
2759 // Kick off a one-shot timer based off our pref value. When this timer
2760 // fires, if painting is still locked down, then we will go ahead and
2761 // trigger a full invalidate and allow painting to proceed normally.
2762 mPaintingSuppressed = PR_TRUE;
2763 mPaintSuppressionTimer = do_CreateInstance("@mozilla.org/timer;1");
2764 if (!mPaintSuppressionTimer)
2765 // Uh-oh. We must be out of memory. No point in keeping painting locked down.
2766 mPaintingSuppressed = PR_FALSE;
2767 else {
2768 // Initialize the timer.
2770 // Default to PAINTLOCK_EVENT_DELAY if we can't get the pref value.
2771 PRInt32 delay =
2772 nsContentUtils::GetIntPref("nglayout.initialpaint.delay",
2773 PAINTLOCK_EVENT_DELAY);
2775 mPaintSuppressionTimer->InitWithFuncCallback(sPaintSuppressionCallback,
2776 this, delay,
2777 nsITimer::TYPE_ONE_SHOT);
2781 return NS_OK; //XXX this needs to be real. MMP
2784 void
2785 PresShell::sPaintSuppressionCallback(nsITimer *aTimer, void* aPresShell)
2787 nsRefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
2788 if (self)
2789 self->UnsuppressPainting();
2792 void
2793 PresShell::AsyncResizeEventCallback(nsITimer* aTimer, void* aPresShell)
2795 static_cast<PresShell*>(aPresShell)->FireResizeEvent();
2798 nsresult
2799 PresShell::ResizeReflowOverride(nscoord aWidth, nscoord aHeight)
2801 mViewportOverridden = PR_TRUE;
2802 return ResizeReflowIgnoreOverride(aWidth, aHeight);
2805 nsresult
2806 PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight)
2808 if (mViewportOverridden) {
2809 // The viewport has been overridden, and this reflow request
2810 // didn't ask to ignore the override. Pretend it didn't happen.
2811 return NS_OK;
2813 return ResizeReflowIgnoreOverride(aWidth, aHeight);
2816 nsresult
2817 PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight)
2819 NS_PRECONDITION(!mIsReflowing, "Shouldn't be in reflow here!");
2820 NS_PRECONDITION(aWidth != NS_UNCONSTRAINEDSIZE,
2821 "shouldn't use unconstrained widths anymore");
2823 // If we don't have a root frame yet, that means we haven't had our initial
2824 // reflow... If that's the case, and aWidth or aHeight is unconstrained,
2825 // ignore them altogether.
2826 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
2828 if (!rootFrame && aHeight == NS_UNCONSTRAINEDSIZE) {
2829 // We can't do the work needed for SizeToContent without a root
2830 // frame, and we want to return before setting the visible area.
2831 return NS_ERROR_NOT_AVAILABLE;
2834 if (!mIsDestroying && !mResizeEvent.IsPending() &&
2835 !mAsyncResizeTimerIsActive) {
2836 FireBeforeResizeEvent();
2839 mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
2841 // There isn't anything useful we can do if the initial reflow hasn't happened
2842 if (!rootFrame)
2843 return NS_OK;
2845 NS_ASSERTION(mViewManager, "Must have view manager");
2846 nsCOMPtr<nsIViewManager> viewManagerDeathGrip = mViewManager;
2847 // Take this ref after viewManager so it'll make sure to go away first
2848 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
2849 if (!GetPresContext()->SupressingResizeReflow())
2851 nsIViewManager::UpdateViewBatch batch(mViewManager);
2853 // Have to make sure that the content notifications are flushed before we
2854 // start messing with the frame model; otherwise we can get content doubling.
2855 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
2857 // Make sure style is up to date
2859 nsAutoScriptBlocker scriptBlocker;
2860 mFrameConstructor->CreateNeededFrames();
2861 mFrameConstructor->ProcessPendingRestyles();
2864 if (!mIsDestroying) {
2865 // XXX Do a full invalidate at the beginning so that invalidates along
2866 // the way don't have region accumulation issues?
2869 nsAutoCauseReflowNotifier crNotifier(this);
2870 WillDoReflow();
2872 // Kick off a top-down reflow
2873 AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
2875 mDirtyRoots.RemoveElement(rootFrame);
2876 DoReflow(rootFrame, PR_TRUE);
2879 DidDoReflow(PR_TRUE);
2882 batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
2885 if (aHeight == NS_UNCONSTRAINEDSIZE) {
2886 mPresContext->SetVisibleArea(
2887 nsRect(0, 0, aWidth, rootFrame->GetRect().height));
2890 if (!mIsDestroying && !mResizeEvent.IsPending() &&
2891 !mAsyncResizeTimerIsActive) {
2892 if (mInResize) {
2893 if (!mAsyncResizeEventTimer) {
2894 mAsyncResizeEventTimer = do_CreateInstance("@mozilla.org/timer;1");
2896 if (mAsyncResizeEventTimer) {
2897 mAsyncResizeTimerIsActive = PR_TRUE;
2898 mAsyncResizeEventTimer->InitWithFuncCallback(AsyncResizeEventCallback,
2899 this, 15,
2900 nsITimer::TYPE_ONE_SHOT);
2902 } else {
2903 nsRefPtr<nsRunnableMethod<PresShell> > resizeEvent =
2904 NS_NewRunnableMethod(this, &PresShell::FireResizeEvent);
2905 if (NS_SUCCEEDED(NS_DispatchToCurrentThread(resizeEvent))) {
2906 mResizeEvent = resizeEvent;
2911 return NS_OK; //XXX this needs to be real. MMP
2914 void
2915 PresShell::FireBeforeResizeEvent()
2917 if (mIsDocumentGone)
2918 return;
2920 // Send beforeresize event from here.
2921 nsEvent event(PR_TRUE, NS_BEFORERESIZE_EVENT);
2923 nsPIDOMWindow *window = mDocument->GetWindow();
2924 if (window) {
2925 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
2926 nsEventDispatcher::Dispatch(window, mPresContext, &event);
2930 void
2931 PresShell::FireResizeEvent()
2933 if (mAsyncResizeTimerIsActive) {
2934 mAsyncResizeTimerIsActive = PR_FALSE;
2935 mAsyncResizeEventTimer->Cancel();
2937 mResizeEvent.Revoke();
2939 if (mIsDocumentGone)
2940 return;
2942 //Send resize event from here.
2943 nsEvent event(PR_TRUE, NS_RESIZE_EVENT);
2944 nsEventStatus status = nsEventStatus_eIgnore;
2946 nsPIDOMWindow *window = mDocument->GetWindow();
2947 if (window) {
2948 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
2949 mInResize = PR_TRUE;
2950 nsEventDispatcher::Dispatch(window, mPresContext, &event, nsnull, &status);
2951 mInResize = PR_FALSE;
2955 void
2956 PresShell::SetIgnoreFrameDestruction(PRBool aIgnore)
2958 mIgnoreFrameDestruction = aIgnore;
2961 void
2962 PresShell::NotifyDestroyingFrame(nsIFrame* aFrame)
2964 NS_TIME_FUNCTION_MIN(1.0);
2966 mPresContext->ForgetUpdatePluginGeometryFrame(aFrame);
2968 if (!mIgnoreFrameDestruction) {
2969 mPresContext->StopImagesFor(aFrame);
2971 mFrameConstructor->NotifyDestroyingFrame(aFrame);
2973 for (PRInt32 idx = mDirtyRoots.Length(); idx; ) {
2974 --idx;
2975 if (mDirtyRoots[idx] == aFrame) {
2976 mDirtyRoots.RemoveElementAt(idx);
2980 // Notify the frame manager
2981 FrameManager()->NotifyDestroyingFrame(aFrame);
2983 // Remove frame properties
2984 mPresContext->NotifyDestroyingFrame(aFrame);
2986 if (aFrame == mCurrentEventFrame) {
2987 mCurrentEventContent = aFrame->GetContent();
2988 mCurrentEventFrame = nsnull;
2991 #ifdef NS_DEBUG
2992 if (aFrame == mDrawEventTargetFrame) {
2993 mDrawEventTargetFrame = nsnull;
2995 #endif
2997 for (unsigned int i=0; i < mCurrentEventFrameStack.Length(); i++) {
2998 if (aFrame == mCurrentEventFrameStack.ElementAt(i)) {
2999 //One of our stack frames was deleted. Get its content so that when we
3000 //pop it we can still get its new frame from its content
3001 nsIContent *currentEventContent = aFrame->GetContent();
3002 mCurrentEventContentStack.ReplaceObjectAt(currentEventContent, i);
3003 mCurrentEventFrameStack[i] = nsnull;
3007 mFramesToDirty.RemoveEntry(aFrame);
3011 already_AddRefed<nsCaret> PresShell::GetCaret() const
3013 nsCaret* caret = mCaret;
3014 NS_IF_ADDREF(caret);
3015 return caret;
3018 void PresShell::MaybeInvalidateCaretPosition()
3020 if (mCaret) {
3021 mCaret->InvalidateOutsideCaret();
3025 void PresShell::SetCaret(nsCaret *aNewCaret)
3027 mCaret = aNewCaret;
3030 void PresShell::RestoreCaret()
3032 mCaret = mOriginalCaret;
3035 NS_IMETHODIMP PresShell::SetCaretEnabled(PRBool aInEnable)
3037 PRBool oldEnabled = mCaretEnabled;
3039 mCaretEnabled = aInEnable;
3041 if (mCaret && (mCaretEnabled != oldEnabled))
3043 /* Don't change the caret's selection here! This was an evil side-effect of SetCaretEnabled()
3044 nsCOMPtr<nsIDOMSelection> domSel;
3045 if (NS_SUCCEEDED(GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel))) && domSel)
3046 mCaret->SetCaretDOMSelection(domSel);
3048 mCaret->SetCaretVisible(mCaretEnabled);
3051 return NS_OK;
3054 NS_IMETHODIMP PresShell::SetCaretReadOnly(PRBool aReadOnly)
3056 if (mCaret)
3057 mCaret->SetCaretReadOnly(aReadOnly);
3058 return NS_OK;
3061 NS_IMETHODIMP PresShell::GetCaretEnabled(PRBool *aOutEnabled)
3063 NS_ENSURE_ARG_POINTER(aOutEnabled);
3064 *aOutEnabled = mCaretEnabled;
3065 return NS_OK;
3068 NS_IMETHODIMP PresShell::SetCaretVisibilityDuringSelection(PRBool aVisibility)
3070 if (mCaret)
3071 mCaret->SetVisibilityDuringSelection(aVisibility);
3072 return NS_OK;
3075 NS_IMETHODIMP PresShell::GetCaretVisible(PRBool *aOutIsVisible)
3077 *aOutIsVisible = PR_FALSE;
3078 if (mCaret) {
3079 nsresult rv = mCaret->GetCaretVisible(aOutIsVisible);
3080 NS_ENSURE_SUCCESS(rv,rv);
3082 return NS_OK;
3085 NS_IMETHODIMP PresShell::SetSelectionFlags(PRInt16 aInEnable)
3087 mSelectionFlags = aInEnable;
3088 return NS_OK;
3091 NS_IMETHODIMP PresShell::GetSelectionFlags(PRInt16 *aOutEnable)
3093 if (!aOutEnable)
3094 return NS_ERROR_INVALID_ARG;
3095 *aOutEnable = mSelectionFlags;
3096 return NS_OK;
3099 //implementation of nsISelectionController
3101 NS_IMETHODIMP
3102 PresShell::CharacterMove(PRBool aForward, PRBool aExtend)
3104 return mSelection->CharacterMove(aForward, aExtend);
3107 NS_IMETHODIMP
3108 PresShell::CharacterExtendForDelete()
3110 return mSelection->CharacterExtendForDelete();
3113 NS_IMETHODIMP
3114 PresShell::CharacterExtendForBackspace()
3116 return mSelection->CharacterExtendForBackspace();
3119 NS_IMETHODIMP
3120 PresShell::WordMove(PRBool aForward, PRBool aExtend)
3122 return mSelection->WordMove(aForward, aExtend);
3125 NS_IMETHODIMP
3126 PresShell::WordExtendForDelete(PRBool aForward)
3128 return mSelection->WordExtendForDelete(aForward);
3131 NS_IMETHODIMP
3132 PresShell::LineMove(PRBool aForward, PRBool aExtend)
3134 nsresult result = mSelection->LineMove(aForward, aExtend);
3135 // if we can't go down/up any more we must then move caret completely to
3136 // end/beginning respectively.
3137 if (NS_FAILED(result))
3138 result = CompleteMove(aForward,aExtend);
3139 return result;
3142 NS_IMETHODIMP
3143 PresShell::IntraLineMove(PRBool aForward, PRBool aExtend)
3145 return mSelection->IntraLineMove(aForward, aExtend);
3150 NS_IMETHODIMP
3151 PresShell::PageMove(PRBool aForward, PRBool aExtend)
3153 nsIScrollableFrame *scrollableFrame =
3154 GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
3155 if (!scrollableFrame)
3156 return NS_OK;
3158 mSelection->CommonPageMove(aForward, aExtend, scrollableFrame);
3159 // After ScrollSelectionIntoView(), the pending notifications might be
3160 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
3161 return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
3162 nsISelectionController::SELECTION_FOCUS_REGION,
3163 nsISelectionController::SCROLL_SYNCHRONOUS);
3168 NS_IMETHODIMP
3169 PresShell::ScrollPage(PRBool aForward)
3171 nsIScrollableFrame* scrollFrame =
3172 GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
3173 if (scrollFrame) {
3174 scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
3175 nsIScrollableFrame::PAGES,
3176 nsIScrollableFrame::SMOOTH);
3178 return NS_OK;
3181 NS_IMETHODIMP
3182 PresShell::ScrollLine(PRBool aForward)
3184 nsIScrollableFrame* scrollFrame =
3185 GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
3186 if (scrollFrame) {
3187 PRInt32 lineCount = 1;
3188 #ifdef MOZ_WIDGET_COCOA
3189 // Emulate the Mac IE behavior of scrolling a minimum of 2 lines
3190 // rather than 1. This vastly improves scrolling speed.
3191 lineCount = 2;
3192 #endif
3193 scrollFrame->ScrollBy(nsIntPoint(0, aForward ? lineCount : -lineCount),
3194 nsIScrollableFrame::LINES,
3195 nsIScrollableFrame::SMOOTH);
3197 //NEW FOR LINES
3198 // force the update to happen now, otherwise multiple scrolls can
3199 // occur before the update is processed. (bug #7354)
3201 // I'd use Composite here, but it doesn't always work.
3202 // vm->Composite();
3203 nsIViewManager* viewManager = GetViewManager();
3204 if (viewManager) {
3205 viewManager->ForceUpdate();
3208 return NS_OK;
3211 NS_IMETHODIMP
3212 PresShell::ScrollHorizontal(PRBool aLeft)
3214 nsIScrollableFrame* scrollFrame =
3215 GetFrameToScrollAsScrollable(nsIPresShell::eHorizontal);
3216 if (scrollFrame) {
3217 scrollFrame->ScrollBy(nsIntPoint(aLeft ? -1 : 1, 0),
3218 nsIScrollableFrame::LINES,
3219 nsIScrollableFrame::SMOOTH);
3220 //NEW FOR LINES
3221 // force the update to happen now, otherwise multiple scrolls can
3222 // occur before the update is processed. (bug #7354)
3224 // I'd use Composite here, but it doesn't always work.
3225 // vm->Composite();
3226 nsIViewManager* viewManager = GetViewManager();
3227 if (viewManager) {
3228 viewManager->ForceUpdate();
3231 return NS_OK;
3234 NS_IMETHODIMP
3235 PresShell::CompleteScroll(PRBool aForward)
3237 nsIScrollableFrame* scrollFrame =
3238 GetFrameToScrollAsScrollable(nsIPresShell::eVertical);
3239 if (scrollFrame) {
3240 scrollFrame->ScrollBy(nsIntPoint(0, aForward ? 1 : -1),
3241 nsIScrollableFrame::WHOLE,
3242 nsIScrollableFrame::INSTANT);
3244 return NS_OK;
3247 NS_IMETHODIMP
3248 PresShell::CompleteMove(PRBool aForward, PRBool aExtend)
3250 // Beware! This may flush notifications via synchronous
3251 // ScrollSelectionIntoView.
3252 nsIContent* limiter = mSelection->GetAncestorLimiter();
3253 nsIFrame* frame = limiter ? limiter->GetPrimaryFrame()
3254 : FrameConstructor()->GetRootElementFrame();
3255 if (!frame)
3256 return NS_ERROR_FAILURE;
3257 nsPeekOffsetStruct pos = frame->GetExtremeCaretPosition(!aForward);
3258 mSelection->HandleClick(pos.mResultContent, pos.mContentOffset,
3259 pos.mContentOffset, aExtend, PR_FALSE, aForward);
3260 if (limiter) {
3261 // HandleClick resets ancestorLimiter, so set it again.
3262 mSelection->SetAncestorLimiter(limiter);
3265 // After ScrollSelectionIntoView(), the pending notifications might be
3266 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
3267 return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
3268 nsISelectionController::SELECTION_FOCUS_REGION,
3269 nsISelectionController::SCROLL_SYNCHRONOUS);
3272 NS_IMETHODIMP
3273 PresShell::SelectAll()
3275 return mSelection->SelectAll();
3278 NS_IMETHODIMP
3279 PresShell::CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, PRBool *_retval)
3281 if (!node || startOffset>EndOffset || !_retval || startOffset<0 || EndOffset<0)
3282 return NS_ERROR_INVALID_ARG;
3283 *_retval = PR_FALSE; //initialize return parameter
3284 nsCOMPtr<nsIContent> content(do_QueryInterface(node));
3285 if (!content)
3286 return NS_ERROR_FAILURE;
3287 nsIFrame *frame = content->GetPrimaryFrame();
3288 if (!frame) //no frame to look at so it must not be visible
3289 return NS_OK;
3290 //start process now to go through all frames to find startOffset. then check chars after that to see
3291 //if anything until EndOffset is visible.
3292 PRBool finished = PR_FALSE;
3293 frame->CheckVisibility(mPresContext,startOffset,EndOffset,PR_TRUE,&finished, _retval);
3294 return NS_OK;//dont worry about other return val
3297 //end implementations nsISelectionController
3300 void
3301 PresShell::StyleChangeReflow()
3303 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
3304 // At the moment at least, we don't have a root frame before the initial
3305 // reflow; it's safe to just ignore the request in that case
3306 if (!rootFrame)
3307 return;
3309 FrameNeedsReflow(rootFrame, eStyleChange, NS_FRAME_IS_DIRTY);
3312 nsIFrame*
3313 nsIPresShell::GetRootFrameExternal() const
3315 return FrameManager()->GetRootFrame();
3318 nsIFrame*
3319 nsIPresShell::GetRootScrollFrame() const
3321 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
3322 // Ensure root frame is a viewport frame
3323 if (!rootFrame || nsGkAtoms::viewportFrame != rootFrame->GetType())
3324 return nsnull;
3325 nsIFrame* theFrame = rootFrame->GetFirstChild(nsnull);
3326 if (!theFrame || nsGkAtoms::scrollFrame != theFrame->GetType())
3327 return nsnull;
3328 return theFrame;
3331 nsIScrollableFrame*
3332 nsIPresShell::GetRootScrollFrameAsScrollable() const
3334 nsIFrame* frame = GetRootScrollFrame();
3335 if (!frame)
3336 return nsnull;
3337 nsIScrollableFrame* scrollableFrame = do_QueryFrame(frame);
3338 NS_ASSERTION(scrollableFrame,
3339 "All scroll frames must implement nsIScrollableFrame");
3340 return scrollableFrame;
3343 nsIScrollableFrame*
3344 nsIPresShell::GetRootScrollFrameAsScrollableExternal() const
3346 return GetRootScrollFrameAsScrollable();
3349 nsIPageSequenceFrame*
3350 PresShell::GetPageSequenceFrame() const
3352 nsIFrame* frame = mFrameConstructor->GetPageSequenceFrame();
3353 return do_QueryFrame(frame);
3356 nsIFrame*
3357 PresShell::GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt)
3359 return nsLayoutUtils::GetFrameForPoint(aFrame, aPt);
3362 void
3363 PresShell::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
3365 #ifdef DEBUG
3366 mUpdateCount++;
3367 #endif
3368 mFrameConstructor->BeginUpdate();
3370 if (aUpdateType & UPDATE_STYLE)
3371 mStyleSet->BeginUpdate();
3374 void
3375 PresShell::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
3377 #ifdef DEBUG
3378 NS_PRECONDITION(0 != mUpdateCount, "too many EndUpdate's");
3379 --mUpdateCount;
3380 #endif
3382 if (aUpdateType & UPDATE_STYLE) {
3383 mStyleSet->EndUpdate();
3384 if (mStylesHaveChanged)
3385 ReconstructStyleData();
3388 mFrameConstructor->EndUpdate();
3391 void
3392 PresShell::RestoreRootScrollPosition()
3394 // Restore frame state for the root scroll frame
3395 nsCOMPtr<nsILayoutHistoryState> historyState =
3396 mDocument->GetLayoutHistoryState();
3397 // Make sure we don't reenter reflow via the sync paint that happens while
3398 // we're scrolling to our restored position. Entering reflow for the
3399 // scrollable frame will cause it to reenter ScrollToRestoredPosition(), and
3400 // it'll get all confused.
3401 nsAutoScriptBlocker scriptBlocker;
3402 ++mChangeNestCount;
3404 if (historyState) {
3405 nsIFrame* scrollFrame = GetRootScrollFrame();
3406 if (scrollFrame) {
3407 nsIScrollableFrame* scrollableFrame = do_QueryFrame(scrollFrame);
3408 if (scrollableFrame) {
3409 FrameManager()->RestoreFrameStateFor(scrollFrame, historyState,
3410 nsIStatefulFrame::eDocumentScrollState);
3411 scrollableFrame->ScrollToRestoredPosition();
3416 --mChangeNestCount;
3419 void
3420 PresShell::BeginLoad(nsIDocument *aDocument)
3422 mDocumentLoading = PR_TRUE;
3425 void
3426 PresShell::EndLoad(nsIDocument *aDocument)
3428 NS_PRECONDITION(aDocument == mDocument, "Wrong document");
3430 RestoreRootScrollPosition();
3432 mDocumentLoading = PR_FALSE;
3435 #ifdef DEBUG
3436 void
3437 PresShell::VerifyHasDirtyRootAncestor(nsIFrame* aFrame)
3439 // XXXbz due to bug 372769, can't actually assert anything here...
3440 return;
3442 // XXXbz shouldn't need this part; remove it once FrameNeedsReflow
3443 // handles the root frame correctly.
3444 if (!aFrame->GetParent()) {
3445 return;
3448 // Make sure that there is a reflow root ancestor of |aFrame| that's
3449 // in mDirtyRoots already.
3450 while (aFrame && (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)) {
3451 if (((aFrame->GetStateBits() & NS_FRAME_REFLOW_ROOT) ||
3452 !aFrame->GetParent()) &&
3453 mDirtyRoots.Contains(aFrame)) {
3454 return;
3457 aFrame = aFrame->GetParent();
3459 NS_NOTREACHED("Frame has dirty bits set but isn't scheduled to be "
3460 "reflowed?");
3462 #endif
3464 void
3465 PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
3466 nsFrameState aBitToAdd)
3468 #ifdef NS_FUNCTION_TIMER
3469 NS_TIME_FUNCTION_DECLARE_DOCURL;
3470 nsCAutoString frameType__("N/A");
3471 nsIAtom *atomType__ = aFrame ? aFrame->GetType() : nsnull;
3472 if (atomType__) atomType__->ToUTF8String(frameType__);
3473 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (document: %s, frame type: %s)", MOZ_FUNCTION_NAME,
3474 __LINE__, docURL__.get(), frameType__.get());
3475 #endif
3477 NS_PRECONDITION(aBitToAdd == NS_FRAME_IS_DIRTY ||
3478 aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN,
3479 "Unexpected bits being added");
3480 NS_PRECONDITION(aIntrinsicDirty != eStyleChange ||
3481 aBitToAdd == NS_FRAME_IS_DIRTY,
3482 "bits don't correspond to style change reason");
3484 NS_ASSERTION(!mIsReflowing, "can't mark frame dirty during reflow");
3486 // If we've not yet done the initial reflow, then don't bother
3487 // enqueuing a reflow command yet.
3488 if (! mDidInitialReflow)
3489 return;
3491 // If we're already destroying, don't bother with this either.
3492 if (mIsDestroying)
3493 return;
3495 #ifdef DEBUG
3496 //printf("gShellCounter: %d\n", gShellCounter++);
3497 if (mInVerifyReflow)
3498 return;
3500 if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
3501 printf("\nPresShell@%p: frame %p needs reflow\n", (void*)this, (void*)aFrame);
3502 if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) {
3503 printf("Current content model:\n");
3504 Element *rootElement = mDocument->GetRootElement();
3505 if (rootElement) {
3506 rootElement->List(stdout, 0);
3510 #endif
3512 nsAutoTArray<nsIFrame*, 4> subtrees;
3513 subtrees.AppendElement(aFrame);
3515 do {
3516 nsIFrame *subtreeRoot = subtrees.ElementAt(subtrees.Length() - 1);
3517 subtrees.RemoveElementAt(subtrees.Length() - 1);
3519 // Grab |wasDirty| now so we can go ahead and update the bits on
3520 // subtreeRoot.
3521 PRBool wasDirty = NS_SUBTREE_DIRTY(subtreeRoot);
3522 subtreeRoot->AddStateBits(aBitToAdd);
3524 // Now if subtreeRoot is a reflow root we can cut off this reflow at it if
3525 // the bit being added is NS_FRAME_HAS_DIRTY_CHILDREN.
3526 PRBool targetFrameDirty = (aBitToAdd == NS_FRAME_IS_DIRTY);
3528 #define FRAME_IS_REFLOW_ROOT(_f) \
3529 ((_f->GetStateBits() & NS_FRAME_REFLOW_ROOT) && \
3530 (_f != subtreeRoot || !targetFrameDirty))
3533 // Mark the intrinsic widths as dirty on the frame, all of its ancestors,
3534 // and all of its descendants, if needed:
3536 if (aIntrinsicDirty != eResize) {
3537 // Mark argument and all ancestors dirty. (Unless we hit a reflow
3538 // root that should contain the reflow. That root could be
3539 // subtreeRoot itself if it's not dirty, or it could be some
3540 // ancestor of subtreeRoot.)
3541 for (nsIFrame *a = subtreeRoot;
3542 a && !FRAME_IS_REFLOW_ROOT(a);
3543 a = a->GetParent())
3544 a->MarkIntrinsicWidthsDirty();
3547 if (aIntrinsicDirty == eStyleChange) {
3548 // Mark all descendants dirty (using an nsTArray stack rather than
3549 // recursion).
3550 nsAutoTArray<nsIFrame*, 32> stack;
3551 stack.AppendElement(subtreeRoot);
3553 do {
3554 nsIFrame *f = stack.ElementAt(stack.Length() - 1);
3555 stack.RemoveElementAt(stack.Length() - 1);
3557 if (f->GetType() == nsGkAtoms::placeholderFrame) {
3558 nsIFrame *oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
3559 if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
3560 // We have another distinct subtree we need to mark.
3561 subtrees.AppendElement(oof);
3565 PRInt32 childListIndex = 0;
3566 nsIAtom *childListName;
3567 do {
3568 childListName = f->GetAdditionalChildListName(childListIndex++);
3569 for (nsIFrame *kid = f->GetFirstChild(childListName); kid;
3570 kid = kid->GetNextSibling()) {
3571 kid->MarkIntrinsicWidthsDirty();
3572 stack.AppendElement(kid);
3574 } while (childListName);
3575 } while (stack.Length() != 0);
3578 // Set NS_FRAME_HAS_DIRTY_CHILDREN bits (via nsIFrame::ChildIsDirty)
3579 // up the tree until we reach either a frame that's already dirty or
3580 // a reflow root.
3581 nsIFrame *f = subtreeRoot;
3582 for (;;) {
3583 if (FRAME_IS_REFLOW_ROOT(f) || !f->GetParent()) {
3584 // we've hit a reflow root or the root frame
3585 if (!wasDirty) {
3586 mDirtyRoots.AppendElement(f);
3588 #ifdef DEBUG
3589 else {
3590 VerifyHasDirtyRootAncestor(f);
3592 #endif
3594 break;
3597 nsIFrame *child = f;
3598 f = f->GetParent();
3599 wasDirty = NS_SUBTREE_DIRTY(f);
3600 f->ChildIsDirty(child);
3601 NS_ASSERTION(f->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN,
3602 "ChildIsDirty didn't do its job");
3603 if (wasDirty) {
3604 // This frame was already marked dirty.
3605 #ifdef DEBUG
3606 VerifyHasDirtyRootAncestor(f);
3607 #endif
3608 break;
3611 } while (subtrees.Length() != 0);
3613 MaybeScheduleReflow();
3616 void
3617 PresShell::FrameNeedsToContinueReflow(nsIFrame *aFrame)
3619 NS_ASSERTION(mIsReflowing, "Must be in reflow when marking path dirty.");
3620 NS_PRECONDITION(mCurrentReflowRoot, "Must have a current reflow root here");
3621 NS_ASSERTION(aFrame == mCurrentReflowRoot ||
3622 nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot, aFrame),
3623 "Frame passed in is not the descendant of mCurrentReflowRoot");
3624 NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW,
3625 "Frame passed in not in reflow?");
3627 mFramesToDirty.PutEntry(aFrame);
3630 nsIScrollableFrame*
3631 nsIPresShell::GetFrameToScrollAsScrollable(
3632 nsIPresShell::ScrollDirection aDirection)
3634 nsIScrollableFrame* scrollFrame = nsnull;
3636 nsCOMPtr<nsIContent> focusedContent;
3637 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3638 if (fm && mDocument) {
3639 nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(mDocument->GetWindow());
3641 nsCOMPtr<nsIDOMElement> focusedElement;
3642 fm->GetFocusedElementForWindow(window, PR_FALSE, nsnull, getter_AddRefs(focusedElement));
3643 focusedContent = do_QueryInterface(focusedElement);
3645 if (!focusedContent && mSelection) {
3646 nsISelection* domSelection = mSelection->
3647 GetSelection(nsISelectionController::SELECTION_NORMAL);
3648 if (domSelection) {
3649 nsCOMPtr<nsIDOMNode> focusedNode;
3650 domSelection->GetFocusNode(getter_AddRefs(focusedNode));
3651 focusedContent = do_QueryInterface(focusedNode);
3654 if (focusedContent) {
3655 nsIFrame* startFrame = focusedContent->GetPrimaryFrame();
3656 if (startFrame) {
3657 scrollFrame = startFrame->GetScrollTargetFrame();
3658 if (scrollFrame) {
3659 startFrame = scrollFrame->GetScrolledFrame();
3661 if (aDirection == nsIPresShell::eEither) {
3662 scrollFrame =
3663 nsLayoutUtils::GetNearestScrollableFrame(startFrame);
3664 } else {
3665 scrollFrame =
3666 nsLayoutUtils::GetNearestScrollableFrameForDirection(startFrame,
3667 aDirection == eVertical ? nsLayoutUtils::eVertical :
3668 nsLayoutUtils::eHorizontal);
3672 if (!scrollFrame) {
3673 scrollFrame = GetRootScrollFrameAsScrollable();
3675 return scrollFrame;
3678 void
3679 PresShell::CancelAllPendingReflows()
3681 mDirtyRoots.Clear();
3683 if (mReflowScheduled) {
3684 GetPresContext()->RefreshDriver()->RemoveLayoutFlushObserver(this);
3685 mReflowScheduled = PR_FALSE;
3688 ASSERT_REFLOW_SCHEDULED_STATE();
3691 nsresult
3692 PresShell::RecreateFramesFor(nsIContent* aContent)
3694 NS_TIME_FUNCTION_MIN(1.0);
3696 NS_ENSURE_TRUE(mPresContext, NS_ERROR_FAILURE);
3697 if (!mDidInitialReflow) {
3698 // Nothing to do here. In fact, if we proceed and aContent is the
3699 // root we will crash.
3700 return NS_OK;
3703 // Don't call RecreateFramesForContent since that is not exported and we want
3704 // to keep the number of entrypoints down.
3706 NS_ASSERTION(mViewManager, "Should have view manager");
3707 nsIViewManager::UpdateViewBatch batch(mViewManager);
3709 // Have to make sure that the content notifications are flushed before we
3710 // start messing with the frame model; otherwise we can get content doubling.
3711 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
3713 nsAutoScriptBlocker scriptBlocker;
3715 nsStyleChangeList changeList;
3716 changeList.AppendChange(nsnull, aContent, nsChangeHint_ReconstructFrame);
3718 // Mark ourselves as not safe to flush while we're doing frame construction.
3719 ++mChangeNestCount;
3720 nsresult rv = mFrameConstructor->ProcessRestyledFrames(changeList);
3721 --mChangeNestCount;
3723 batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
3724 return rv;
3727 void
3728 nsIPresShell::PostRecreateFramesFor(Element* aElement)
3730 FrameConstructor()->PostRestyleEvent(aElement, nsRestyleHint(0),
3731 nsChangeHint_ReconstructFrame);
3734 void
3735 nsIPresShell::RestyleForAnimation(Element* aElement, nsRestyleHint aHint)
3737 FrameConstructor()->PostAnimationRestyleEvent(aElement, aHint,
3738 NS_STYLE_HINT_NONE);
3741 void
3742 PresShell::ClearFrameRefs(nsIFrame* aFrame)
3744 mPresContext->EventStateManager()->ClearFrameRefs(aFrame);
3746 nsWeakFrame* weakFrame = mWeakFrames;
3747 while (weakFrame) {
3748 nsWeakFrame* prev = weakFrame->GetPreviousWeakFrame();
3749 if (weakFrame->GetFrame() == aFrame) {
3750 // This removes weakFrame from mWeakFrames.
3751 weakFrame->Clear(this);
3753 weakFrame = prev;
3757 already_AddRefed<nsIRenderingContext>
3758 PresShell::GetReferenceRenderingContext()
3760 NS_TIME_FUNCTION_MIN(1.0);
3762 nsIDeviceContext* devCtx = mPresContext->DeviceContext();
3763 nsRefPtr<nsIRenderingContext> rc;
3764 if (mPresContext->IsScreen()) {
3765 devCtx->CreateRenderingContextInstance(*getter_AddRefs(rc));
3766 if (rc) {
3767 rc->Init(devCtx, gfxPlatform::GetPlatform()->ScreenReferenceSurface());
3769 } else {
3770 devCtx->CreateRenderingContext(*getter_AddRefs(rc));
3772 return rc.forget();
3775 nsresult
3776 PresShell::GoToAnchor(const nsAString& aAnchorName, PRBool aScroll)
3778 if (!mDocument) {
3779 return NS_ERROR_FAILURE;
3782 // Hold a reference to the ESM in case event dispatch tears us down.
3783 nsCOMPtr<nsIEventStateManager> esm = mPresContext->EventStateManager();
3785 if (aAnchorName.IsEmpty()) {
3786 NS_ASSERTION(!aScroll, "can't scroll to empty anchor name");
3787 esm->SetContentState(nsnull, NS_EVENT_STATE_URLTARGET);
3788 return NS_OK;
3791 nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
3792 nsresult rv = NS_OK;
3793 nsCOMPtr<nsIContent> content;
3795 // Search for an element with a matching "id" attribute
3796 if (mDocument) {
3797 content = mDocument->GetElementById(aAnchorName);
3800 // Search for an anchor element with a matching "name" attribute
3801 if (!content && htmlDoc) {
3802 nsCOMPtr<nsIDOMNodeList> list;
3803 // Find a matching list of named nodes
3804 rv = htmlDoc->GetElementsByName(aAnchorName, getter_AddRefs(list));
3805 if (NS_SUCCEEDED(rv) && list) {
3806 PRUint32 i;
3807 // Loop through the named nodes looking for the first anchor
3808 for (i = 0; PR_TRUE; i++) {
3809 nsCOMPtr<nsIDOMNode> node;
3810 rv = list->Item(i, getter_AddRefs(node));
3811 if (!node) { // End of list
3812 break;
3814 // Ensure it's an anchor element
3815 content = do_QueryInterface(node);
3816 if (content) {
3817 if (content->Tag() == nsGkAtoms::a && content->IsHTML()) {
3818 break;
3820 content = nsnull;
3826 // Search for anchor in the HTML namespace with a matching name
3827 if (!content && !htmlDoc)
3829 nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
3830 nsCOMPtr<nsIDOMNodeList> list;
3831 NS_NAMED_LITERAL_STRING(nameSpace, "http://www.w3.org/1999/xhtml");
3832 // Get the list of anchor elements
3833 rv = doc->GetElementsByTagNameNS(nameSpace, NS_LITERAL_STRING("a"), getter_AddRefs(list));
3834 if (NS_SUCCEEDED(rv) && list) {
3835 PRUint32 i;
3836 // Loop through the named nodes looking for the first anchor
3837 for (i = 0; PR_TRUE; i++) {
3838 nsCOMPtr<nsIDOMNode> node;
3839 rv = list->Item(i, getter_AddRefs(node));
3840 if (!node) { // End of list
3841 break;
3843 // Compare the name attribute
3844 nsCOMPtr<nsIDOMElement> element = do_QueryInterface(node);
3845 nsAutoString value;
3846 if (element && NS_SUCCEEDED(element->GetAttribute(NS_LITERAL_STRING("name"), value))) {
3847 if (value.Equals(aAnchorName)) {
3848 content = do_QueryInterface(element);
3849 break;
3856 nsCOMPtr<nsIDOMRange> jumpToRange;
3857 nsCOMPtr<nsIXPointerResult> xpointerResult;
3858 if (!content) {
3859 nsCOMPtr<nsIDOMXMLDocument> xmldoc = do_QueryInterface(mDocument);
3860 if (xmldoc) {
3861 // Try XPointer
3862 xmldoc->EvaluateXPointer(aAnchorName, getter_AddRefs(xpointerResult));
3863 if (xpointerResult) {
3864 xpointerResult->Item(0, getter_AddRefs(jumpToRange));
3865 if (!jumpToRange) {
3866 // We know it was an XPointer, so there is no point in
3867 // trying any other pointer types, let's just return
3868 // an error.
3869 return NS_ERROR_FAILURE;
3873 // Finally try FIXptr
3874 if (!jumpToRange) {
3875 xmldoc->EvaluateFIXptr(aAnchorName,getter_AddRefs(jumpToRange));
3878 if (jumpToRange) {
3879 nsCOMPtr<nsIDOMNode> node;
3880 jumpToRange->GetStartContainer(getter_AddRefs(node));
3881 if (node) {
3882 PRUint16 nodeType;
3883 node->GetNodeType(&nodeType);
3884 PRInt32 offset = -1;
3885 jumpToRange->GetStartOffset(&offset);
3886 switch (nodeType) {
3887 case nsIDOMNode::ATTRIBUTE_NODE:
3889 // XXX Assuming jumping to the ownerElement is the sanest action.
3890 nsCOMPtr<nsIAttribute> attr = do_QueryInterface(node);
3891 content = attr->GetContent();
3892 break;
3894 case nsIDOMNode::DOCUMENT_NODE:
3896 if (offset >= 0) {
3897 nsCOMPtr<nsIDocument> document = do_QueryInterface(node);
3898 content = document->GetChildAt(offset);
3900 break;
3902 case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
3903 case nsIDOMNode::ELEMENT_NODE:
3904 case nsIDOMNode::ENTITY_REFERENCE_NODE:
3906 if (offset >= 0) {
3907 nsCOMPtr<nsIContent> parent = do_QueryInterface(node);
3908 content = parent->GetChildAt(offset);
3910 break;
3912 case nsIDOMNode::CDATA_SECTION_NODE:
3913 case nsIDOMNode::COMMENT_NODE:
3914 case nsIDOMNode::TEXT_NODE:
3915 case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
3917 // XXX This should scroll to a specific position in the text.
3918 content = do_QueryInterface(node);
3919 break;
3927 esm->SetContentState(content, NS_EVENT_STATE_URLTARGET);
3929 #ifdef ACCESSIBILITY
3930 nsIContent *anchorTarget = content;
3931 #endif
3933 if (content) {
3934 if (aScroll) {
3935 rv = ScrollContentIntoView(content, NS_PRESSHELL_SCROLL_TOP,
3936 NS_PRESSHELL_SCROLL_ANYWHERE,
3937 SCROLL_OVERFLOW_HIDDEN);
3938 NS_ENSURE_SUCCESS(rv, rv);
3940 nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
3941 if (rootScroll) {
3942 mLastAnchorScrolledTo = content;
3943 mLastAnchorScrollPositionY = rootScroll->GetScrollPosition().y;
3947 // Should we select the target? This action is controlled by a
3948 // preference: the default is to not select.
3949 PRBool selectAnchor = nsContentUtils::GetBoolPref("layout.selectanchor");
3951 // Even if select anchor pref is false, we must still move the
3952 // caret there. That way tabbing will start from the new
3953 // location
3954 if (!jumpToRange) {
3955 jumpToRange = do_CreateInstance(kRangeCID);
3956 if (jumpToRange) {
3957 while (content && content->GetChildCount() > 0) {
3958 content = content->GetChildAt(0);
3960 nsCOMPtr<nsIDOMNode> node(do_QueryInterface(content));
3961 NS_ASSERTION(node, "No nsIDOMNode for descendant of anchor");
3962 jumpToRange->SelectNodeContents(node);
3965 if (jumpToRange) {
3966 // Select the anchor
3967 nsISelection* sel = mSelection->
3968 GetSelection(nsISelectionController::SELECTION_NORMAL);
3969 if (sel) {
3970 sel->RemoveAllRanges();
3971 sel->AddRange(jumpToRange);
3972 if (!selectAnchor) {
3973 // Use a caret (collapsed selection) at the start of the anchor
3974 sel->CollapseToStart();
3978 if (selectAnchor && xpointerResult) {
3979 // Select the rest (if any) of the ranges in XPointerResult
3980 PRUint32 count, i;
3981 xpointerResult->GetLength(&count);
3982 for (i = 1; i < count; i++) { // jumpToRange is i = 0
3983 nsCOMPtr<nsIDOMRange> range;
3984 xpointerResult->Item(i, getter_AddRefs(range));
3985 sel->AddRange(range);
3988 // Selection is at anchor.
3989 // Now focus the document itself if focus is on an element within it.
3990 nsPIDOMWindow *win = mDocument->GetWindow();
3992 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
3993 if (fm && win) {
3994 nsCOMPtr<nsIDOMWindow> focusedWindow;
3995 fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
3996 if (SameCOMIdentity(win, focusedWindow))
3997 fm->ClearFocus(focusedWindow);
4000 } else {
4001 rv = NS_ERROR_FAILURE; //changed to NS_OK in quirks mode if ScrollTo is called
4003 // Scroll to the top/left if the anchor can not be
4004 // found and it is labelled top (quirks mode only). @see bug 80784
4005 if ((NS_LossyConvertUTF16toASCII(aAnchorName).LowerCaseEqualsLiteral("top")) &&
4006 (mPresContext->CompatibilityMode() == eCompatibility_NavQuirks)) {
4007 rv = NS_OK;
4008 nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable();
4009 // Check |aScroll| after setting |rv| so we set |rv| to the same
4010 // thing whether or not |aScroll| is true.
4011 if (aScroll && sf) {
4012 // Scroll to the top of the page
4013 sf->ScrollTo(nsPoint(0, 0), nsIScrollableFrame::INSTANT);
4018 #ifdef ACCESSIBILITY
4019 if (anchorTarget && gIsAccessibilityActive) {
4020 nsCOMPtr<nsIAccessibilityService> accService =
4021 do_GetService("@mozilla.org/accessibilityService;1");
4022 if (accService)
4023 accService->NotifyOfAnchorJumpTo(anchorTarget);
4025 #endif
4027 return rv;
4030 nsresult
4031 PresShell::ScrollToAnchor()
4033 if (!mLastAnchorScrolledTo)
4034 return NS_OK;
4036 NS_ASSERTION(mDidInitialReflow, "should have done initial reflow by now");
4038 nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
4039 if (!rootScroll ||
4040 mLastAnchorScrollPositionY != rootScroll->GetScrollPosition().y)
4041 return NS_OK;
4043 nsresult rv = ScrollContentIntoView(mLastAnchorScrolledTo, NS_PRESSHELL_SCROLL_TOP,
4044 NS_PRESSHELL_SCROLL_ANYWHERE,
4045 SCROLL_OVERFLOW_HIDDEN);
4046 mLastAnchorScrolledTo = nsnull;
4047 return rv;
4051 * Helper (per-continuation) for ScrollContentIntoView.
4053 * @param aContainerFrame [in] the frame which aRect is relative to
4054 * @param aFrame [in] Frame whose bounds should be unioned
4055 * @param aUseWholeLineHeightForInlines [in] if true, then for inline frames
4056 * we should include the top of the line in the added rectangle
4057 * @param aRect [inout] rect into which its bounds should be unioned
4058 * @param aHaveRect [inout] whether aRect contains data yet
4060 static void
4061 AccumulateFrameBounds(nsIFrame* aContainerFrame,
4062 nsIFrame* aFrame,
4063 PRBool aUseWholeLineHeightForInlines,
4064 nsRect& aRect,
4065 PRBool& aHaveRect)
4067 nsRect frameBounds = aFrame->GetRect() +
4068 aFrame->GetParent()->GetOffsetTo(aContainerFrame);
4070 // If this is an inline frame and either the bounds height is 0 (quirks
4071 // layout model) or aUseWholeLineHeightForInlines is set, we need to
4072 // change the top of the bounds to include the whole line.
4073 if (frameBounds.height == 0 || aUseWholeLineHeightForInlines) {
4074 nsIAtom* frameType = NULL;
4075 nsIFrame *prevFrame = aFrame;
4076 nsIFrame *f = aFrame;
4078 while (f &&
4079 (frameType = f->GetType()) == nsGkAtoms::inlineFrame) {
4080 prevFrame = f;
4081 f = prevFrame->GetParent();
4084 if (f != aFrame &&
4085 f &&
4086 frameType == nsGkAtoms::blockFrame) {
4087 // find the line containing aFrame and increase the top of |offset|.
4088 nsAutoLineIterator lines = f->GetLineIterator();
4089 if (lines) {
4090 PRInt32 index = lines->FindLineContaining(prevFrame);
4091 if (index >= 0) {
4092 nsIFrame *trash1;
4093 PRInt32 trash2;
4094 nsRect lineBounds;
4095 PRUint32 trash3;
4097 if (NS_SUCCEEDED(lines->GetLine(index, &trash1, &trash2,
4098 lineBounds, &trash3))) {
4099 lineBounds += f->GetOffsetTo(aContainerFrame);
4100 if (lineBounds.y < frameBounds.y) {
4101 frameBounds.height = frameBounds.YMost() - lineBounds.y;
4102 frameBounds.y = lineBounds.y;
4110 if (aHaveRect) {
4111 // We can't use nsRect::UnionRect since it drops empty rects on
4112 // the floor, and we need to include them. (Thus we need
4113 // aHaveRect to know when to drop the initial value on the floor.)
4114 aRect.UnionRectIncludeEmpty(aRect, frameBounds);
4115 } else {
4116 aHaveRect = PR_TRUE;
4117 aRect = frameBounds;
4122 * This function takes a scrollable frame, a rect in the coordinate system
4123 * of the scrolled frame, and a desired percentage-based scroll
4124 * position and attempts to scroll the rect to that position in the
4125 * scrollport.
4127 * This needs to work even if aRect has a width or height of zero.
4129 static void ScrollToShowRect(nsIScrollableFrame* aScrollFrame,
4130 const nsRect& aRect,
4131 PRIntn aVPercent,
4132 PRIntn aHPercent,
4133 PRUint32 aFlags)
4135 nsPoint scrollPt = aScrollFrame->GetScrollPosition();
4136 nsRect visibleRect(scrollPt, aScrollFrame->GetScrollPortRect().Size());
4137 nsSize lineSize = aScrollFrame->GetLineScrollAmount();
4138 nsPresContext::ScrollbarStyles ss = aScrollFrame->GetScrollbarStyles();
4140 if ((aFlags & nsIPresShell::SCROLL_OVERFLOW_HIDDEN) ||
4141 ss.mVertical != NS_STYLE_OVERFLOW_HIDDEN) {
4142 // See how the rect should be positioned vertically
4143 if (NS_PRESSHELL_SCROLL_ANYWHERE == aVPercent ||
4144 (NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE == aVPercent &&
4145 aRect.height < lineSize.height)) {
4146 // The caller doesn't care where the frame is positioned vertically,
4147 // so long as it's fully visible
4148 if (aRect.y < visibleRect.y) {
4149 // Scroll up so the frame's top edge is visible
4150 scrollPt.y = aRect.y;
4151 } else if (aRect.YMost() > visibleRect.YMost()) {
4152 // Scroll down so the frame's bottom edge is visible. Make sure the
4153 // frame's top edge is still visible
4154 scrollPt.y += aRect.YMost() - visibleRect.YMost();
4155 if (scrollPt.y > aRect.y) {
4156 scrollPt.y = aRect.y;
4159 } else if (NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE == aVPercent) {
4160 // Scroll only if no part of the frame is visible in this view
4161 if (aRect.YMost() - lineSize.height < visibleRect.y) {
4162 // Scroll up so the frame's top edge is visible
4163 scrollPt.y = aRect.y;
4164 } else if (aRect.y + lineSize.height > visibleRect.YMost()) {
4165 // Scroll down so the frame's bottom edge is visible. Make sure the
4166 // frame's top edge is still visible
4167 scrollPt.y += aRect.YMost() - visibleRect.YMost();
4168 if (scrollPt.y > aRect.y) {
4169 scrollPt.y = aRect.y;
4172 } else {
4173 // Align the frame edge according to the specified percentage
4174 nscoord frameAlignY =
4175 NSToCoordRound(aRect.y + aRect.height * (aVPercent / 100.0f));
4176 scrollPt.y =
4177 NSToCoordRound(frameAlignY - visibleRect.height * (aVPercent / 100.0f));
4181 if ((aFlags & nsIPresShell::SCROLL_OVERFLOW_HIDDEN) ||
4182 ss.mHorizontal != NS_STYLE_OVERFLOW_HIDDEN) {
4183 // See how the frame should be positioned horizontally
4184 if (NS_PRESSHELL_SCROLL_ANYWHERE == aHPercent ||
4185 (NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE == aHPercent &&
4186 aRect.width < lineSize.width)) {
4187 // The caller doesn't care where the frame is positioned horizontally,
4188 // so long as it's fully visible
4189 if (aRect.x < visibleRect.x) {
4190 // Scroll left so the frame's left edge is visible
4191 scrollPt.x = aRect.x;
4192 } else if (aRect.XMost() > visibleRect.XMost()) {
4193 // Scroll right so the frame's right edge is visible. Make sure the
4194 // frame's left edge is still visible
4195 scrollPt.x += aRect.XMost() - visibleRect.XMost();
4196 if (scrollPt.x > aRect.x) {
4197 scrollPt.x = aRect.x;
4200 } else if (NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE == aHPercent) {
4201 // Scroll only if no part of the frame is visible in this view
4202 if (aRect.XMost() - lineSize.width < visibleRect.x) {
4203 // Scroll left so the frame's left edge is visible
4204 scrollPt.x = aRect.x;
4205 } else if (aRect.x + lineSize.width > visibleRect.XMost()) {
4206 // Scroll right so the frame's right edge is visible. Make sure the
4207 // frame's left edge is still visible
4208 scrollPt.x += aRect.XMost() - visibleRect.XMost();
4209 if (scrollPt.x > aRect.x) {
4210 scrollPt.x = aRect.x;
4213 } else {
4214 // Align the frame edge according to the specified percentage
4215 nscoord frameAlignX =
4216 NSToCoordRound(aRect.x + (aRect.width) * (aHPercent / 100.0f));
4217 scrollPt.x =
4218 NSToCoordRound(frameAlignX - visibleRect.width * (aHPercent / 100.0f));
4222 aScrollFrame->ScrollTo(scrollPt, nsIScrollableFrame::INSTANT);
4225 nsresult
4226 PresShell::ScrollContentIntoView(nsIContent* aContent,
4227 PRIntn aVPercent,
4228 PRIntn aHPercent,
4229 PRUint32 aFlags)
4231 nsCOMPtr<nsIContent> content = aContent; // Keep content alive while flushing.
4232 NS_ENSURE_TRUE(content, NS_ERROR_NULL_POINTER);
4233 nsCOMPtr<nsIDocument> currentDoc = content->GetCurrentDoc();
4234 NS_ENSURE_STATE(currentDoc);
4236 NS_ASSERTION(mDidInitialReflow, "should have done initial reflow by now");
4238 mContentToScrollTo = aContent;
4239 mContentScrollVPosition = aVPercent;
4240 mContentScrollHPosition = aHPercent;
4242 // Flush layout and attempt to scroll in the process.
4243 currentDoc->FlushPendingNotifications(Flush_InterruptibleLayout);
4245 // If mContentToScrollTo is non-null, that means we interrupted the reflow
4246 // (or suppressed it altogether because we're suppressing interruptible
4247 // flushes right now) and won't necessarily get the position correct, but do
4248 // a best-effort scroll here. The other option would be to do this inside
4249 // FlushPendingNotifications, but I'm not sure the repeated scrolling that
4250 // could trigger if reflows keep getting interrupted would be more desirable
4251 // than a single best-effort scroll followed by one final scroll on the first
4252 // completed reflow.
4253 if (mContentToScrollTo) {
4254 DoScrollContentIntoView(content, aVPercent, aHPercent, aFlags);
4256 return NS_OK;
4259 void
4260 PresShell::DoScrollContentIntoView(nsIContent* aContent,
4261 PRIntn aVPercent,
4262 PRIntn aHPercent,
4263 PRUint32 aFlags)
4265 NS_ASSERTION(mDidInitialReflow, "should have done initial reflow by now");
4267 nsIFrame* frame = aContent->GetPrimaryFrame();
4268 if (!frame) {
4269 mContentToScrollTo = nsnull;
4270 return;
4273 if (frame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
4274 // The reflow flush before this scroll got interrupted, and this frame's
4275 // coords and size are all zero, and it has no content showing anyway.
4276 // Don't bother scrolling to it. We'll try again when we finish up layout.
4277 return;
4280 nsIFrame* container =
4281 nsLayoutUtils::GetClosestFrameOfType(frame, nsGkAtoms::scrollFrame);
4282 if (!container) {
4283 // nothing can be scrolled
4284 return;
4287 // This is a two-step process.
4288 // Step 1: Find the bounds of the rect we want to scroll into view. For
4289 // example, for an inline frame we may want to scroll in the whole
4290 // line, or we may want to scroll multiple lines into view.
4291 // Step 2: Walk container frame and its ancestors and scroll them
4292 // appropriately.
4293 // frameBounds is relative to container. We're assuming
4294 // that scrollframes don't split so every continuation of frame will
4295 // be a descendant of container. (Things would still mostly work
4296 // even if that assumption was false.)
4297 nsRect frameBounds;
4298 PRBool haveRect = PR_FALSE;
4299 PRBool useWholeLineHeightForInlines = aVPercent != NS_PRESSHELL_SCROLL_ANYWHERE;
4300 do {
4301 AccumulateFrameBounds(container, frame, useWholeLineHeightForInlines,
4302 frameBounds, haveRect);
4303 } while ((frame = frame->GetNextContinuation()));
4305 ScrollFrameRectIntoView(container, frameBounds, aVPercent, aHPercent,
4306 aFlags);
4309 PRBool
4310 PresShell::ScrollFrameRectIntoView(nsIFrame* aFrame,
4311 const nsRect& aRect,
4312 PRIntn aVPercent,
4313 PRIntn aHPercent,
4314 PRUint32 aFlags)
4316 PRBool didScroll = PR_FALSE;
4317 // This function needs to work even if rect has a width or height of 0.
4318 nsRect rect = aRect;
4319 nsIFrame* container = aFrame;
4320 // Walk up the frame hierarchy scrolling the rect into view and
4321 // keeping rect relative to container
4322 do {
4323 nsIScrollableFrame* sf = do_QueryFrame(container);
4324 if (sf) {
4325 nsPoint oldPosition = sf->GetScrollPosition();
4326 ScrollToShowRect(sf, rect - sf->GetScrolledFrame()->GetPosition(),
4327 aVPercent, aHPercent, aFlags);
4328 nsPoint newPosition = sf->GetScrollPosition();
4329 // If the scroll position increased, that means our content moved up,
4330 // so our rect's offset should decrease
4331 rect += oldPosition - newPosition;
4333 if (oldPosition != newPosition) {
4334 didScroll = PR_TRUE;
4337 // only scroll one container when this flag is set
4338 if (aFlags & nsIPresShell::SCROLL_FIRST_ANCESTOR_ONLY) {
4339 break;
4342 nsRect scrollPort = sf->GetScrollPortRect();
4343 if (rect.XMost() < scrollPort.x ||
4344 rect.x > scrollPort.XMost() ||
4345 rect.YMost() < scrollPort.y ||
4346 rect.y > scrollPort.YMost()) {
4347 // We tried to show the rectangle, but none of it is visible,
4348 // not even an edge.
4349 // Stop trying to scroll ancestors into view.
4350 break;
4353 // Restrict rect to the area that is actually visible through
4354 // the scrollport. We don't want to try to scroll some clipped-out
4355 // part of 'rect' into view in some ancestor.
4356 rect.IntersectRect(rect, sf->GetScrollPortRect());
4358 rect += container->GetPosition();
4359 nsIFrame* parent = container->GetParent();
4360 if (!parent) {
4361 nsPoint extraOffset(0,0);
4362 parent = nsLayoutUtils::GetCrossDocParentFrame(container, &extraOffset);
4363 if (parent) {
4364 PRInt32 APD = container->PresContext()->AppUnitsPerDevPixel();
4365 PRInt32 parentAPD = parent->PresContext()->AppUnitsPerDevPixel();
4366 rect = rect.ConvertAppUnitsRoundOut(APD, parentAPD);
4367 rect += extraOffset;
4370 container = parent;
4371 } while (container);
4373 return didScroll;
4376 nsRectVisibility
4377 PresShell::GetRectVisibility(nsIFrame* aFrame,
4378 const nsRect &aRect,
4379 nscoord aMinTwips) const
4381 NS_ASSERTION(aFrame->PresContext() == GetPresContext(),
4382 "prescontext mismatch?");
4383 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
4384 NS_ASSERTION(rootFrame,
4385 "How can someone have a frame for this presshell when there's no root?");
4386 nsIScrollableFrame* sf = GetRootScrollFrameAsScrollable();
4387 nsRect scrollPortRect;
4388 if (sf) {
4389 scrollPortRect = sf->GetScrollPortRect();
4390 nsIFrame* f = do_QueryFrame(sf);
4391 scrollPortRect += f->GetOffsetTo(rootFrame);
4392 } else {
4393 scrollPortRect = nsRect(nsPoint(0,0), rootFrame->GetSize());
4396 nsRect r = aRect + aFrame->GetOffsetTo(rootFrame);
4397 // If aRect is entirely visible then we don't need to ensure that
4398 // at least aMinTwips of it is visible
4399 if (scrollPortRect.Contains(r))
4400 return nsRectVisibility_kVisible;
4402 nsRect insetRect = scrollPortRect;
4403 insetRect.Deflate(aMinTwips, aMinTwips);
4404 if (r.YMost() <= insetRect.y)
4405 return nsRectVisibility_kAboveViewport;
4406 if (r.y >= insetRect.YMost())
4407 return nsRectVisibility_kBelowViewport;
4408 if (r.XMost() <= insetRect.x)
4409 return nsRectVisibility_kLeftOfViewport;
4410 if (r.x >= insetRect.XMost())
4411 return nsRectVisibility_kRightOfViewport;
4413 return nsRectVisibility_kVisible;
4416 // GetLinkLocation: copy link location to clipboard
4417 nsresult PresShell::GetLinkLocation(nsIDOMNode* aNode, nsAString& aLocationString) const
4419 #ifdef DEBUG_dr
4420 printf("dr :: PresShell::GetLinkLocation\n");
4421 #endif
4423 NS_ENSURE_ARG_POINTER(aNode);
4424 nsresult rv;
4425 nsAutoString anchorText;
4426 static char strippedChars[] = {'\t','\r','\n'};
4428 // are we an anchor?
4429 nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(aNode));
4430 nsCOMPtr<nsIDOMHTMLAreaElement> area;
4431 nsCOMPtr<nsIDOMHTMLLinkElement> link;
4432 nsAutoString xlinkType;
4433 if (anchor) {
4434 rv = anchor->GetHref(anchorText);
4435 NS_ENSURE_SUCCESS(rv, rv);
4436 } else {
4437 // area?
4438 area = do_QueryInterface(aNode);
4439 if (area) {
4440 rv = area->GetHref(anchorText);
4441 NS_ENSURE_SUCCESS(rv, rv);
4442 } else {
4443 // link?
4444 link = do_QueryInterface(aNode);
4445 if (link) {
4446 rv = link->GetHref(anchorText);
4447 NS_ENSURE_SUCCESS(rv, rv);
4448 } else {
4449 // Xlink?
4450 nsCOMPtr<nsIDOMElement> element(do_QueryInterface(aNode));
4451 if (element) {
4452 NS_NAMED_LITERAL_STRING(xlinkNS,"http://www.w3.org/1999/xlink");
4453 element->GetAttributeNS(xlinkNS,NS_LITERAL_STRING("type"),xlinkType);
4454 if (xlinkType.EqualsLiteral("simple")) {
4455 element->GetAttributeNS(xlinkNS,NS_LITERAL_STRING("href"),anchorText);
4456 if (!anchorText.IsEmpty()) {
4457 // Resolve the full URI using baseURI property
4459 nsAutoString base;
4460 nsCOMPtr<nsIDOM3Node> node(do_QueryInterface(aNode,&rv));
4461 NS_ENSURE_SUCCESS(rv, rv);
4462 node->GetBaseURI(base);
4464 nsCOMPtr<nsIIOService>
4465 ios(do_GetService("@mozilla.org/network/io-service;1", &rv));
4466 NS_ENSURE_SUCCESS(rv, rv);
4468 nsCOMPtr<nsIURI> baseURI;
4469 rv = ios->NewURI(NS_ConvertUTF16toUTF8(base),nsnull,nsnull,getter_AddRefs(baseURI));
4470 NS_ENSURE_SUCCESS(rv, rv);
4472 nsCAutoString spec;
4473 rv = baseURI->Resolve(NS_ConvertUTF16toUTF8(anchorText),spec);
4474 NS_ENSURE_SUCCESS(rv, rv);
4476 CopyUTF8toUTF16(spec, anchorText);
4484 if (anchor || area || link || xlinkType.EqualsLiteral("simple")) {
4485 //Remove all the '\t', '\r' and '\n' from 'anchorText'
4486 anchorText.StripChars(strippedChars);
4488 aLocationString = anchorText;
4490 return NS_OK;
4493 // if no link, fail.
4494 return NS_ERROR_FAILURE;
4497 NS_IMETHODIMP_(void)
4498 PresShell::DispatchSynthMouseMove(nsGUIEvent *aEvent,
4499 PRBool aFlushOnHoverChange)
4501 PRUint32 hoverGenerationBefore = mFrameConstructor->GetHoverGeneration();
4502 nsEventStatus status;
4503 nsIView* targetView = nsIView::GetViewFor(aEvent->widget);
4504 targetView->GetViewManager()->DispatchEvent(aEvent, targetView, &status);
4505 if (aFlushOnHoverChange &&
4506 hoverGenerationBefore != mFrameConstructor->GetHoverGeneration()) {
4507 // Flush so that the resulting reflow happens now so that our caller
4508 // can suppress any synthesized mouse moves caused by that reflow.
4509 FlushPendingNotifications(Flush_Layout);
4513 NS_IMETHODIMP_(void)
4514 PresShell::ClearMouseCapture(nsIView* aView)
4516 if (gCaptureInfo.mContent) {
4517 if (aView) {
4518 // if a view was specified, ensure that the captured content is within
4519 // this view.
4520 nsIFrame* frame = gCaptureInfo.mContent->GetPrimaryFrame();
4521 if (frame) {
4522 nsIView* view = frame->GetClosestView();
4523 // if there is no view, capturing won't be handled any more, so
4524 // just release the capture.
4525 if (view) {
4526 do {
4527 if (view == aView) {
4528 NS_RELEASE(gCaptureInfo.mContent);
4529 // the view containing the captured content likely disappeared so
4530 // disable capture for now.
4531 gCaptureInfo.mAllowed = PR_FALSE;
4532 break;
4535 view = view->GetParent();
4536 } while (view);
4537 // return if the view wasn't found
4538 return;
4543 NS_RELEASE(gCaptureInfo.mContent);
4546 // disable mouse capture until the next mousedown as a dialog has opened
4547 // or a drag has started. Otherwise, someone could start capture during
4548 // the modal dialog or drag.
4549 gCaptureInfo.mAllowed = PR_FALSE;
4552 nsresult
4553 PresShell::CaptureHistoryState(nsILayoutHistoryState** aState, PRBool aLeavingPage)
4555 NS_TIME_FUNCTION_MIN(1.0);
4557 nsresult rv = NS_OK;
4559 NS_PRECONDITION(nsnull != aState, "null state pointer");
4561 // We actually have to mess with the docshell here, since we want to
4562 // store the state back in it.
4563 // XXXbz this isn't really right, since this is being called in the
4564 // content viewer's Hide() method... by that point the docshell's
4565 // state could be wrong. We should sort out a better ownership
4566 // model for the layout history state.
4567 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
4568 if (!container)
4569 return NS_ERROR_FAILURE;
4571 nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
4572 if (!docShell)
4573 return NS_ERROR_FAILURE;
4575 nsCOMPtr<nsILayoutHistoryState> historyState;
4576 docShell->GetLayoutHistoryState(getter_AddRefs(historyState));
4577 if (!historyState) {
4578 // Create the document state object
4579 rv = NS_NewLayoutHistoryState(getter_AddRefs(historyState));
4581 if (NS_FAILED(rv)) {
4582 *aState = nsnull;
4583 return rv;
4586 docShell->SetLayoutHistoryState(historyState);
4589 *aState = historyState;
4590 NS_IF_ADDREF(*aState);
4592 // Capture frame state for the entire frame hierarchy
4593 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
4594 if (!rootFrame) return NS_OK;
4595 // Capture frame state for the root scroll frame
4596 // Don't capture state when first creating doc element hierarchy
4597 // As the scroll position is 0 and this will cause us to lose
4598 // our previously saved place!
4599 if (aLeavingPage) {
4600 nsIFrame* scrollFrame = GetRootScrollFrame();
4601 if (scrollFrame) {
4602 FrameManager()->CaptureFrameStateFor(scrollFrame, historyState,
4603 nsIStatefulFrame::eDocumentScrollState);
4607 FrameManager()->CaptureFrameState(rootFrame, historyState);
4609 return NS_OK;
4612 void
4613 PresShell::UnsuppressAndInvalidate()
4615 // Note: We ignore the EnsureVisible check for resource documents, because
4616 // they won't have a docshell, so they'll always fail EnsureVisible.
4617 if ((!mDocument->IsResourceDoc() && !mPresContext->EnsureVisible()) ||
4618 mHaveShutDown) {
4619 // No point; we're about to be torn down anyway.
4620 return;
4623 mPaintingSuppressed = PR_FALSE;
4624 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
4625 if (rootFrame) {
4626 // let's assume that outline on a root frame is not supported
4627 nsRect rect(nsPoint(0, 0), rootFrame->GetSize());
4628 rootFrame->Invalidate(rect);
4630 if (mCaretEnabled && mCaret) {
4631 mCaret->CheckCaretDrawingState();
4634 nsRootPresContext* rootPC = mPresContext->GetRootPresContext();
4635 if (rootPC) {
4636 rootPC->RequestUpdatePluginGeometry(rootFrame);
4640 // now that painting is unsuppressed, focus may be set on the document
4641 nsPIDOMWindow *win = mDocument->GetWindow();
4642 if (win)
4643 win->SetReadyForFocus();
4645 if (!mHaveShutDown)
4646 SynthesizeMouseMove(PR_FALSE);
4649 void
4650 PresShell::UnsuppressPainting()
4652 if (mPaintSuppressionTimer) {
4653 mPaintSuppressionTimer->Cancel();
4654 mPaintSuppressionTimer = nsnull;
4657 if (mIsDocumentGone || !mPaintingSuppressed)
4658 return;
4660 // If we have reflows pending, just wait until we process
4661 // the reflows and get all the frames where we want them
4662 // before actually unlocking the painting. Otherwise
4663 // go ahead and unlock now.
4664 if (mDirtyRoots.Length() > 0)
4665 mShouldUnsuppressPainting = PR_TRUE;
4666 else
4667 UnsuppressAndInvalidate();
4670 // Post a request to handle an arbitrary callback after reflow has finished.
4671 nsresult
4672 PresShell::PostReflowCallback(nsIReflowCallback* aCallback)
4674 void* result = AllocateMisc(sizeof(nsCallbackEventRequest));
4675 if (NS_UNLIKELY(!result)) {
4676 return NS_ERROR_OUT_OF_MEMORY;
4678 nsCallbackEventRequest* request = (nsCallbackEventRequest*)result;
4680 request->callback = aCallback;
4681 request->next = nsnull;
4683 if (mLastCallbackEventRequest) {
4684 mLastCallbackEventRequest = mLastCallbackEventRequest->next = request;
4685 } else {
4686 mFirstCallbackEventRequest = request;
4687 mLastCallbackEventRequest = request;
4690 return NS_OK;
4693 void
4694 PresShell::CancelReflowCallback(nsIReflowCallback* aCallback)
4696 nsCallbackEventRequest* before = nsnull;
4697 nsCallbackEventRequest* node = mFirstCallbackEventRequest;
4698 while(node)
4700 nsIReflowCallback* callback = node->callback;
4702 if (callback == aCallback)
4704 nsCallbackEventRequest* toFree = node;
4705 if (node == mFirstCallbackEventRequest) {
4706 node = node->next;
4707 mFirstCallbackEventRequest = node;
4708 NS_ASSERTION(before == nsnull, "impossible");
4709 } else {
4710 node = node->next;
4711 before->next = node;
4714 if (toFree == mLastCallbackEventRequest) {
4715 mLastCallbackEventRequest = before;
4718 FreeMisc(sizeof(nsCallbackEventRequest), toFree);
4719 } else {
4720 before = node;
4721 node = node->next;
4726 void
4727 PresShell::CancelPostedReflowCallbacks()
4729 while (mFirstCallbackEventRequest) {
4730 nsCallbackEventRequest* node = mFirstCallbackEventRequest;
4731 mFirstCallbackEventRequest = node->next;
4732 if (!mFirstCallbackEventRequest) {
4733 mLastCallbackEventRequest = nsnull;
4735 nsIReflowCallback* callback = node->callback;
4736 FreeMisc(sizeof(nsCallbackEventRequest), node);
4737 if (callback) {
4738 callback->ReflowCallbackCanceled();
4743 void
4744 PresShell::HandlePostedReflowCallbacks(PRBool aInterruptible)
4746 PRBool shouldFlush = PR_FALSE;
4748 while (mFirstCallbackEventRequest) {
4749 nsCallbackEventRequest* node = mFirstCallbackEventRequest;
4750 mFirstCallbackEventRequest = node->next;
4751 if (!mFirstCallbackEventRequest) {
4752 mLastCallbackEventRequest = nsnull;
4754 nsIReflowCallback* callback = node->callback;
4755 FreeMisc(sizeof(nsCallbackEventRequest), node);
4756 if (callback) {
4757 if (callback->ReflowFinished()) {
4758 shouldFlush = PR_TRUE;
4763 mozFlushType flushType =
4764 aInterruptible ? Flush_InterruptibleLayout : Flush_Layout;
4765 if (shouldFlush)
4766 FlushPendingNotifications(flushType);
4769 PRBool
4770 PresShell::IsSafeToFlush() const
4772 // Not safe if we are reflowing or in the middle of frame construction
4773 PRBool isSafeToFlush = !mIsReflowing &&
4774 !mChangeNestCount;
4776 if (isSafeToFlush) {
4777 // Not safe if we are painting
4778 nsIViewManager* viewManager = GetViewManager();
4779 if (viewManager) {
4780 PRBool isPainting = PR_FALSE;
4781 viewManager->IsPainting(isPainting);
4782 if (isPainting) {
4783 isSafeToFlush = PR_FALSE;
4788 return isSafeToFlush;
4792 void
4793 PresShell::FlushPendingNotifications(mozFlushType aType)
4795 #ifdef NS_FUNCTION_TIMER
4796 NS_TIME_FUNCTION_DECLARE_DOCURL;
4797 static const char *flushTypeNames[] = {
4798 "Flush_Content",
4799 "Flush_ContentAndNotify",
4800 "Flush_Styles",
4801 "Flush_InterruptibleLayout",
4802 "Flush_Layout",
4803 "Flush_Display"
4805 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (document: %s, type: %s)", MOZ_FUNCTION_NAME,
4806 __LINE__, docURL__.get(), flushTypeNames[aType - 1]);
4807 #endif
4809 NS_ASSERTION(aType >= Flush_Frames, "Why did we get called?");
4811 PRBool isSafeToFlush = IsSafeToFlush();
4813 // If layout could possibly trigger scripts, then it's only safe to flush if
4814 // it's safe to run script.
4815 if (mDocument->GetScriptGlobalObject()) {
4816 isSafeToFlush = isSafeToFlush && nsContentUtils::IsSafeToRunScript();
4819 NS_ASSERTION(!isSafeToFlush || mViewManager, "Must have view manager");
4820 // Make sure the view manager stays alive while batching view updates.
4821 nsCOMPtr<nsIViewManager> viewManagerDeathGrip = mViewManager;
4822 if (isSafeToFlush && mViewManager) {
4823 // Processing pending notifications can kill us, and some callers only
4824 // hold weak refs when calling FlushPendingNotifications(). :(
4825 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
4827 if (mResizeEvent.IsPending()) {
4828 FireResizeEvent();
4829 if (mIsDestroying) {
4830 return;
4834 // Style reresolves not in conjunction with reflows can't cause
4835 // painting or geometry changes, so don't bother with view update
4836 // batching if we only have style reresolve
4837 nsIViewManager::UpdateViewBatch batch(mViewManager);
4839 // We need to make sure external resource documents are flushed too (for
4840 // example, svg filters that reference a filter in an external document
4841 // need the frames in the external document to be constructed for the
4842 // filter to work). We only need external resources to be flushed when the
4843 // main document is flushing >= Flush_Frames, so we flush external
4844 // resources here instead of nsDocument::FlushPendingNotifications.
4845 mDocument->FlushExternalResources(aType);
4847 // Force flushing of any pending content notifications that might have
4848 // queued up while our event was pending. That will ensure that we don't
4849 // construct frames for content right now that's still waiting to be
4850 // notified on,
4851 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
4853 // Process pending restyles, since any flush of the presshell wants
4854 // up-to-date style data.
4855 if (!mIsDestroying) {
4856 mViewManager->FlushDelayedResize(PR_FALSE);
4857 mPresContext->FlushPendingMediaFeatureValuesChanged();
4859 // Flush any pending update of the user font set, since that could
4860 // cause style changes (for updating ex/ch units, and to cause a
4861 // reflow).
4862 mPresContext->FlushUserFontSet();
4864 #ifdef MOZ_SMIL
4865 // Flush any requested SMIL samples.
4866 if (mDocument->HasAnimationController()) {
4867 mDocument->GetAnimationController()->FlushResampleRequests();
4869 #endif // MOZ_SMIL
4871 nsAutoScriptBlocker scriptBlocker;
4872 mFrameConstructor->CreateNeededFrames();
4873 mFrameConstructor->ProcessPendingRestyles();
4876 // Process whatever XBL constructors those restyles queued up. This
4877 // ensures that onload doesn't fire too early and that we won't do extra
4878 // reflows after those constructors run.
4879 if (!mIsDestroying) {
4880 mDocument->BindingManager()->ProcessAttachedQueue();
4883 // Now those constructors might have posted restyle events. At the same
4884 // time, we still need up-to-date style data. In particular, reflow
4885 // depends on style being completely up to date. If it's not, then style
4886 // context reparenting, which can happen during reflow, might suddenly pick
4887 // up the new rules and we'll end up with frames whose style doesn't match
4888 // the frame type.
4889 if (!mIsDestroying) {
4890 nsAutoScriptBlocker scriptBlocker;
4891 mFrameConstructor->CreateNeededFrames();
4892 mFrameConstructor->ProcessPendingRestyles();
4896 // There might be more pending constructors now, but we're not going to
4897 // worry about them. They can't be triggered during reflow, so we should
4898 // be good.
4900 if (aType >= (mSuppressInterruptibleReflows ? Flush_Layout : Flush_InterruptibleLayout) &&
4901 !mIsDestroying) {
4902 mFrameConstructor->RecalcQuotesAndCounters();
4903 mViewManager->FlushDelayedResize(PR_TRUE);
4904 if (ProcessReflowCommands(aType < Flush_Layout) && mContentToScrollTo) {
4905 // We didn't get interrupted. Go ahead and scroll to our content
4906 DoScrollContentIntoView(mContentToScrollTo, mContentScrollVPosition,
4907 mContentScrollHPosition,
4908 SCROLL_OVERFLOW_HIDDEN);
4909 mContentToScrollTo = nsnull;
4913 if (aType >= Flush_Layout) {
4914 // Flush plugin geometry. Don't flush plugin geometry for
4915 // interruptible layouts, since WillPaint does an interruptible
4916 // layout.
4917 nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
4918 if (rootPresContext) {
4919 rootPresContext->UpdatePluginGeometry();
4923 PRUint32 updateFlags = NS_VMREFRESH_NO_SYNC;
4924 if (aType >= Flush_Display) {
4925 // Flushing paints, so perform the invalidates and drawing
4926 // immediately
4927 updateFlags = NS_VMREFRESH_IMMEDIATE;
4929 batch.EndUpdateViewBatch(updateFlags);
4933 void
4934 PresShell::CharacterDataChanged(nsIDocument *aDocument,
4935 nsIContent* aContent,
4936 CharacterDataChangeInfo* aInfo)
4938 NS_PRECONDITION(!mIsDocumentGone, "Unexpected CharacterDataChanged");
4939 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4941 nsAutoCauseReflowNotifier crNotifier(this);
4943 if (mCaret) {
4944 // Invalidate the caret's current location before we call into the frame
4945 // constructor. It is important to do this now, and not wait until the
4946 // resulting reflow, because this call causes continuation frames of the
4947 // text frame the caret is in to forget what part of the content they
4948 // refer to, making it hard for them to return the correct continuation
4949 // frame to the caret.
4950 mCaret->InvalidateOutsideCaret();
4953 // Call this here so it only happens for real content mutations and
4954 // not cases when the frame constructor calls its own methods to force
4955 // frame reconstruction.
4956 nsIContent *container = aContent->GetParent();
4957 PRUint32 selectorFlags =
4958 container ? (container->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
4959 if (selectorFlags != 0 && !aContent->IsRootOfAnonymousSubtree()) {
4960 Element* element = container->AsElement();
4961 if (aInfo->mAppend && !aContent->GetNextSibling())
4962 mFrameConstructor->RestyleForAppend(element, aContent);
4963 else
4964 mFrameConstructor->RestyleForInsertOrChange(element, aContent);
4967 mFrameConstructor->CharacterDataChanged(aContent, aInfo);
4968 VERIFY_STYLE_TREE;
4971 void
4972 PresShell::ContentStatesChanged(nsIDocument* aDocument,
4973 nsIContent* aContent1,
4974 nsIContent* aContent2,
4975 nsEventStates aStateMask)
4977 NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentStatesChanged");
4978 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4980 if (mDidInitialReflow) {
4981 nsAutoCauseReflowNotifier crNotifier(this);
4982 mFrameConstructor->ContentStatesChanged(aContent1, aContent2, aStateMask);
4983 VERIFY_STYLE_TREE;
4987 void
4988 PresShell::DocumentStatesChanged(nsIDocument* aDocument,
4989 nsEventStates aStateMask)
4991 NS_PRECONDITION(!mIsDocumentGone, "Unexpected DocumentStatesChanged");
4992 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
4994 if (mDidInitialReflow &&
4995 mStyleSet->HasDocumentStateDependentStyle(mPresContext,
4996 mDocument->GetRootElement(),
4997 aStateMask)) {
4998 mFrameConstructor->PostRestyleEvent(mDocument->GetRootElement(),
4999 eRestyle_Subtree, NS_STYLE_HINT_NONE);
5000 VERIFY_STYLE_TREE;
5003 if (aStateMask.HasState(NS_DOCUMENT_STATE_WINDOW_INACTIVE)) {
5004 nsIFrame* root = FrameManager()->GetRootFrame();
5005 if (root) {
5006 // It's a display root. So, invalidate the layer contents of
5007 // everything we can find. We need to do this because the contents
5008 // of controls etc can depend on whether the window is active,
5009 // and when a window becomes (in)active it just gets repainted
5010 // and we don't specifically invalidate each affected control.
5011 nsIWidget* widget = root->GetNearestWidget();
5012 if (widget) {
5013 LayerManager* layerManager = widget->GetLayerManager();
5014 if (layerManager) {
5015 FrameLayerBuilder::InvalidateAllThebesLayerContents(layerManager);
5022 void
5023 PresShell::AttributeWillChange(nsIDocument* aDocument,
5024 Element* aElement,
5025 PRInt32 aNameSpaceID,
5026 nsIAtom* aAttribute,
5027 PRInt32 aModType)
5029 NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeWillChange");
5030 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
5032 // XXXwaterson it might be more elegant to wait until after the
5033 // initial reflow to begin observing the document. That would
5034 // squelch any other inappropriate notifications as well.
5035 if (mDidInitialReflow) {
5036 nsAutoCauseReflowNotifier crNotifier(this);
5037 mFrameConstructor->AttributeWillChange(aElement, aNameSpaceID,
5038 aAttribute, aModType);
5039 VERIFY_STYLE_TREE;
5043 void
5044 PresShell::AttributeChanged(nsIDocument* aDocument,
5045 Element* aElement,
5046 PRInt32 aNameSpaceID,
5047 nsIAtom* aAttribute,
5048 PRInt32 aModType)
5050 NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeChanged");
5051 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
5053 // XXXwaterson it might be more elegant to wait until after the
5054 // initial reflow to begin observing the document. That would
5055 // squelch any other inappropriate notifications as well.
5056 if (mDidInitialReflow) {
5057 nsAutoCauseReflowNotifier crNotifier(this);
5058 mFrameConstructor->AttributeChanged(aElement, aNameSpaceID,
5059 aAttribute, aModType);
5060 VERIFY_STYLE_TREE;
5064 void
5065 PresShell::ContentAppended(nsIDocument *aDocument,
5066 nsIContent* aContainer,
5067 nsIContent* aFirstNewContent,
5068 PRInt32 aNewIndexInContainer)
5070 NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentAppended");
5071 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
5072 NS_PRECONDITION(aContainer, "must have container");
5074 if (!mDidInitialReflow) {
5075 return;
5078 nsAutoCauseReflowNotifier crNotifier(this);
5080 // Call this here so it only happens for real content mutations and
5081 // not cases when the frame constructor calls its own methods to force
5082 // frame reconstruction.
5083 mFrameConstructor->RestyleForAppend(aContainer->AsElement(), aFirstNewContent);
5085 mFrameConstructor->ContentAppended(aContainer, aFirstNewContent, PR_TRUE);
5086 VERIFY_STYLE_TREE;
5089 void
5090 PresShell::ContentInserted(nsIDocument* aDocument,
5091 nsIContent* aContainer,
5092 nsIContent* aChild,
5093 PRInt32 aIndexInContainer)
5095 NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentInserted");
5096 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
5098 if (!mDidInitialReflow) {
5099 return;
5102 nsAutoCauseReflowNotifier crNotifier(this);
5104 // Call this here so it only happens for real content mutations and
5105 // not cases when the frame constructor calls its own methods to force
5106 // frame reconstruction.
5107 if (aContainer)
5108 mFrameConstructor->RestyleForInsertOrChange(aContainer->AsElement(), aChild);
5110 mFrameConstructor->ContentInserted(aContainer, aChild, nsnull, PR_TRUE);
5111 VERIFY_STYLE_TREE;
5114 void
5115 PresShell::ContentRemoved(nsIDocument *aDocument,
5116 nsIContent* aContainer,
5117 nsIContent* aChild,
5118 PRInt32 aIndexInContainer,
5119 nsIContent* aPreviousSibling)
5121 NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentRemoved");
5122 NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
5124 // Make sure that the caret doesn't leave a turd where the child used to be.
5125 if (mCaret) {
5126 mCaret->InvalidateOutsideCaret();
5129 // Notify the ESM that the content has been removed, so that
5130 // it can clean up any state related to the content.
5131 mPresContext->EventStateManager()->ContentRemoved(aDocument, aChild);
5133 nsAutoCauseReflowNotifier crNotifier(this);
5135 // Call this here so it only happens for real content mutations and
5136 // not cases when the frame constructor calls its own methods to force
5137 // frame reconstruction.
5138 nsIContent* oldNextSibling;
5139 if (aContainer) {
5140 oldNextSibling = aContainer->GetChildAt(aIndexInContainer);
5141 } else {
5142 oldNextSibling = nsnull;
5145 if (aContainer)
5146 mFrameConstructor->RestyleForRemove(aContainer->AsElement(), aChild,
5147 oldNextSibling);
5149 PRBool didReconstruct;
5150 mFrameConstructor->ContentRemoved(aContainer, aChild, oldNextSibling,
5151 nsCSSFrameConstructor::REMOVE_CONTENT,
5152 &didReconstruct);
5154 VERIFY_STYLE_TREE;
5157 nsresult
5158 PresShell::ReconstructFrames(void)
5160 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
5162 // Have to make sure that the content notifications are flushed before we
5163 // start messing with the frame model; otherwise we can get content doubling.
5164 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
5166 nsAutoCauseReflowNotifier crNotifier(this);
5167 mFrameConstructor->BeginUpdate();
5168 nsresult rv = mFrameConstructor->ReconstructDocElementHierarchy();
5169 VERIFY_STYLE_TREE;
5170 mFrameConstructor->EndUpdate();
5172 return rv;
5175 void
5176 nsIPresShell::ReconstructStyleDataInternal()
5178 mStylesHaveChanged = PR_FALSE;
5180 if (mIsDestroying) {
5181 // We don't want to mess with restyles at this point
5182 return;
5185 if (mPresContext) {
5186 mPresContext->RebuildUserFontSet();
5189 Element* root = mDocument->GetRootElement();
5190 if (!mDidInitialReflow) {
5191 // Nothing to do here, since we have no frames yet
5192 return;
5195 if (!root) {
5196 // No content to restyle
5197 return;
5200 mFrameConstructor->PostRestyleEvent(root, eRestyle_Subtree, NS_STYLE_HINT_NONE);
5203 void
5204 nsIPresShell::ReconstructStyleDataExternal()
5206 ReconstructStyleDataInternal();
5209 void
5210 PresShell::StyleSheetAdded(nsIDocument *aDocument,
5211 nsIStyleSheet* aStyleSheet,
5212 PRBool aDocumentSheet)
5214 // We only care when enabled sheets are added
5215 NS_PRECONDITION(aStyleSheet, "Must have a style sheet!");
5217 if (aStyleSheet->IsApplicable() && aStyleSheet->HasRules()) {
5218 mStylesHaveChanged = PR_TRUE;
5222 void
5223 PresShell::StyleSheetRemoved(nsIDocument *aDocument,
5224 nsIStyleSheet* aStyleSheet,
5225 PRBool aDocumentSheet)
5227 // We only care when enabled sheets are removed
5228 NS_PRECONDITION(aStyleSheet, "Must have a style sheet!");
5230 if (aStyleSheet->IsApplicable() && aStyleSheet->HasRules()) {
5231 mStylesHaveChanged = PR_TRUE;
5235 void
5236 PresShell::StyleSheetApplicableStateChanged(nsIDocument *aDocument,
5237 nsIStyleSheet* aStyleSheet,
5238 PRBool aApplicable)
5240 if (aStyleSheet->HasRules()) {
5241 mStylesHaveChanged = PR_TRUE;
5245 void
5246 PresShell::StyleRuleChanged(nsIDocument *aDocument,
5247 nsIStyleSheet* aStyleSheet,
5248 nsIStyleRule* aOldStyleRule,
5249 nsIStyleRule* aNewStyleRule)
5251 mStylesHaveChanged = PR_TRUE;
5254 void
5255 PresShell::StyleRuleAdded(nsIDocument *aDocument,
5256 nsIStyleSheet* aStyleSheet,
5257 nsIStyleRule* aStyleRule)
5259 mStylesHaveChanged = PR_TRUE;
5262 void
5263 PresShell::StyleRuleRemoved(nsIDocument *aDocument,
5264 nsIStyleSheet* aStyleSheet,
5265 nsIStyleRule* aStyleRule)
5267 mStylesHaveChanged = PR_TRUE;
5270 nsIFrame*
5271 PresShell::GetRealPrimaryFrameFor(nsIContent* aContent) const
5273 if (aContent->GetDocument() != GetDocument()) {
5274 return nsnull;
5276 nsIFrame *primaryFrame = aContent->GetPrimaryFrame();
5277 if (!primaryFrame)
5278 return nsnull;
5279 return nsPlaceholderFrame::GetRealFrameFor(primaryFrame);
5282 nsIFrame*
5283 PresShell::GetPlaceholderFrameFor(nsIFrame* aFrame) const
5285 return FrameManager()->GetPlaceholderFrameFor(aFrame);
5288 nsresult
5289 PresShell::RenderDocument(const nsRect& aRect, PRUint32 aFlags,
5290 nscolor aBackgroundColor,
5291 gfxContext* aThebesContext)
5293 NS_TIME_FUNCTION_WITH_DOCURL;
5295 NS_ENSURE_TRUE(!(aFlags & RENDER_IS_UNTRUSTED), NS_ERROR_NOT_IMPLEMENTED);
5297 // Set up the rectangle as the path in aThebesContext
5298 gfxRect r(0, 0,
5299 nsPresContext::AppUnitsToFloatCSSPixels(aRect.width),
5300 nsPresContext::AppUnitsToFloatCSSPixels(aRect.height));
5301 aThebesContext->NewPath();
5302 #ifdef MOZ_GFX_OPTIMIZE_MOBILE
5303 aThebesContext->Rectangle(r, PR_TRUE);
5304 #else
5305 aThebesContext->Rectangle(r);
5306 #endif
5308 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
5309 if (!rootFrame) {
5310 // Nothing to paint, just fill the rect
5311 aThebesContext->SetColor(gfxRGBA(aBackgroundColor));
5312 aThebesContext->Fill();
5313 return NS_OK;
5316 gfxContextAutoSaveRestore save(aThebesContext);
5318 gfxContext::GraphicsOperator oldOperator = aThebesContext->CurrentOperator();
5319 if (oldOperator == gfxContext::OPERATOR_OVER) {
5320 // Clip to the destination rectangle before we push the group,
5321 // to limit the size of the temporary surface
5322 aThebesContext->Clip();
5325 // we want the window to be composited as a single image using
5326 // whatever operator was set; set OPERATOR_OVER here, which is
5327 // either already the case, or overrides the operator in a group.
5328 // the original operator will be present when we PopGroup.
5329 // we can avoid using a temporary surface if we're using OPERATOR_OVER
5330 // and our background color has no alpha (so we'll be compositing on top
5331 // of a fully opaque solid color region)
5332 PRBool needsGroup = NS_GET_A(aBackgroundColor) < 0xff ||
5333 oldOperator != gfxContext::OPERATOR_OVER;
5335 if (needsGroup) {
5336 aThebesContext->PushGroup(NS_GET_A(aBackgroundColor) == 0xff ?
5337 gfxASurface::CONTENT_COLOR :
5338 gfxASurface::CONTENT_COLOR_ALPHA);
5339 aThebesContext->Save();
5341 if (oldOperator != gfxContext::OPERATOR_OVER) {
5342 // Clip now while we paint to the temporary surface. For
5343 // non-source-bounded operators (e.g., SOURCE), we need to do clip
5344 // here after we've pushed the group, so that eventually popping
5345 // the group and painting it will be able to clear the entire
5346 // destination surface.
5347 aThebesContext->Clip();
5348 aThebesContext->SetOperator(gfxContext::OPERATOR_OVER);
5352 aThebesContext->Translate(gfxPoint(-nsPresContext::AppUnitsToFloatCSSPixels(aRect.x),
5353 -nsPresContext::AppUnitsToFloatCSSPixels(aRect.y)));
5355 nsIDeviceContext* devCtx = mPresContext->DeviceContext();
5356 gfxFloat scale = gfxFloat(devCtx->AppUnitsPerDevPixel())/nsPresContext::AppUnitsPerCSSPixel();
5357 aThebesContext->Scale(scale, scale);
5359 // Since canvas APIs use floats to set up their matrices, we may have
5360 // some slight inaccuracy here. Adjust matrix components that are
5361 // integers up to the accuracy of floats to be those integers.
5362 aThebesContext->NudgeCurrentMatrixToIntegers();
5364 AutoSaveRestoreRenderingState _(this);
5366 nsCOMPtr<nsIRenderingContext> rc;
5367 devCtx->CreateRenderingContextInstance(*getter_AddRefs(rc));
5368 rc->Init(devCtx, aThebesContext);
5370 PRBool wouldFlushRetainedLayers = PR_FALSE;
5371 PRUint32 flags = nsLayoutUtils::PAINT_IGNORE_SUPPRESSION;
5372 if (!(aFlags & RENDER_ASYNC_DECODE_IMAGES)) {
5373 flags |= nsLayoutUtils::PAINT_SYNC_DECODE_IMAGES;
5375 if (aFlags & RENDER_USE_WIDGET_LAYERS) {
5376 // We only support using widget layers on display root's with widgets.
5377 nsIView* view = rootFrame->GetView();
5378 if (view && view->GetWidget() &&
5379 nsLayoutUtils::GetDisplayRootFrame(rootFrame) == rootFrame) {
5380 flags |= nsLayoutUtils::PAINT_WIDGET_LAYERS;
5383 if (!(aFlags & RENDER_CARET)) {
5384 wouldFlushRetainedLayers = PR_TRUE;
5385 flags |= nsLayoutUtils::PAINT_HIDE_CARET;
5387 if (aFlags & RENDER_IGNORE_VIEWPORT_SCROLLING) {
5388 wouldFlushRetainedLayers = !IgnoringViewportScrolling();
5389 mRenderFlags = ChangeFlag(mRenderFlags, PR_TRUE, STATE_IGNORING_VIEWPORT_SCROLLING);
5391 if (aFlags & RENDER_DOCUMENT_RELATIVE) {
5392 // XXX be smarter about this ... drawWindow might want a rect
5393 // that's "pretty close" to what our retained layer tree covers.
5394 // In that case, it wouldn't disturb normal rendering too much,
5395 // and we should allow it.
5396 wouldFlushRetainedLayers = PR_TRUE;
5397 flags |= nsLayoutUtils::PAINT_DOCUMENT_RELATIVE;
5400 // Don't let drawWindow blow away our retained layer tree
5401 if ((flags & nsLayoutUtils::PAINT_WIDGET_LAYERS) && wouldFlushRetainedLayers) {
5402 flags &= ~nsLayoutUtils::PAINT_WIDGET_LAYERS;
5405 nsLayoutUtils::PaintFrame(rc, rootFrame, nsRegion(aRect),
5406 aBackgroundColor, flags);
5408 // if we had to use a group, paint it to the destination now
5409 if (needsGroup) {
5410 aThebesContext->Restore();
5411 aThebesContext->PopGroupToSource();
5412 aThebesContext->Paint();
5415 return NS_OK;
5419 * Clip the display list aList to a range. Returns the clipped
5420 * rectangle surrounding the range.
5422 nsRect
5423 PresShell::ClipListToRange(nsDisplayListBuilder *aBuilder,
5424 nsDisplayList* aList,
5425 nsIRange* aRange)
5427 NS_TIME_FUNCTION_WITH_DOCURL;
5429 // iterate though the display items and add up the bounding boxes of each.
5430 // This will allow the total area of the frames within the range to be
5431 // determined. To do this, remove an item from the bottom of the list, check
5432 // whether it should be part of the range, and if so, append it to the top
5433 // of the temporary list tmpList. If the item is a text frame at the end of
5434 // the selection range, wrap it in an nsDisplayClip to clip the display to
5435 // the portion of the text frame that is part of the selection. Then, append
5436 // the wrapper to the top of the list. Otherwise, just delete the item and
5437 // don't append it.
5438 nsRect surfaceRect;
5439 nsDisplayList tmpList;
5441 nsDisplayItem* i;
5442 while ((i = aList->RemoveBottom())) {
5443 // itemToInsert indiciates the item that should be inserted into the
5444 // temporary list. If null, no item should be inserted.
5445 nsDisplayItem* itemToInsert = nsnull;
5446 nsIFrame* frame = i->GetUnderlyingFrame();
5447 if (frame) {
5448 nsIContent* content = frame->GetContent();
5449 if (content) {
5450 PRBool atStart = (content == aRange->GetStartParent());
5451 PRBool atEnd = (content == aRange->GetEndParent());
5452 if ((atStart || atEnd) && frame->GetType() == nsGkAtoms::textFrame) {
5453 PRInt32 frameStartOffset, frameEndOffset;
5454 frame->GetOffsets(frameStartOffset, frameEndOffset);
5456 PRInt32 hilightStart =
5457 atStart ? NS_MAX(aRange->StartOffset(), frameStartOffset) : frameStartOffset;
5458 PRInt32 hilightEnd =
5459 atEnd ? NS_MIN(aRange->EndOffset(), frameEndOffset) : frameEndOffset;
5460 if (hilightStart < hilightEnd) {
5461 // determine the location of the start and end edges of the range.
5462 nsPoint startPoint, endPoint;
5463 frame->GetPointFromOffset(hilightStart, &startPoint);
5464 frame->GetPointFromOffset(hilightEnd, &endPoint);
5466 // the clip rectangle is determined by taking the the start and
5467 // end points of the range, offset from the reference frame.
5468 // Because of rtl, the end point may be to the left of the
5469 // start point, so x is set to the lowest value
5470 nsRect textRect(aBuilder->ToReferenceFrame(frame), frame->GetSize());
5471 nscoord x = NS_MIN(startPoint.x, endPoint.x);
5472 textRect.x += x;
5473 textRect.width = NS_MAX(startPoint.x, endPoint.x) - x;
5474 surfaceRect.UnionRect(surfaceRect, textRect);
5476 // wrap the item in an nsDisplayClip so that it can be clipped to
5477 // the selection. If the allocation fails, fall through and delete
5478 // the item below.
5479 itemToInsert = new (aBuilder)
5480 nsDisplayClip(aBuilder, frame, i, textRect);
5483 // Don't try to descend into subdocuments.
5484 // If this ever changes we'd need to add handling for subdocuments with
5485 // different zoom levels.
5486 else if (content->GetCurrentDoc() ==
5487 aRange->GetStartParent()->GetCurrentDoc()) {
5488 // if the node is within the range, append it to the temporary list
5489 PRBool before, after;
5490 nsresult rv =
5491 nsRange::CompareNodeToRange(content, aRange, &before, &after);
5492 if (NS_SUCCEEDED(rv) && !before && !after) {
5493 itemToInsert = i;
5494 surfaceRect.UnionRect(surfaceRect, i->GetBounds(aBuilder));
5500 // insert the item into the list if necessary. If the item has a child
5501 // list, insert that as well
5502 nsDisplayList* sublist = i->GetList();
5503 if (itemToInsert || sublist) {
5504 tmpList.AppendToTop(itemToInsert ? itemToInsert : i);
5505 // if the item is a list, iterate over it as well
5506 if (sublist)
5507 surfaceRect.UnionRect(surfaceRect,
5508 ClipListToRange(aBuilder, sublist, aRange));
5510 else {
5511 // otherwise, just delete the item and don't readd it to the list
5512 i->~nsDisplayItem();
5516 // now add all the items back onto the original list again
5517 aList->AppendToTop(&tmpList);
5519 return surfaceRect;
5522 #ifdef DEBUG
5523 #include <stdio.h>
5525 static PRBool gDumpRangePaintList = PR_FALSE;
5526 #endif
5528 RangePaintInfo*
5529 PresShell::CreateRangePaintInfo(nsIDOMRange* aRange,
5530 nsRect& aSurfaceRect,
5531 PRBool aForPrimarySelection)
5533 NS_TIME_FUNCTION_WITH_DOCURL;
5535 RangePaintInfo* info = nsnull;
5537 nsCOMPtr<nsIRange> range = do_QueryInterface(aRange);
5538 if (!range)
5539 return nsnull;
5541 nsIFrame* ancestorFrame;
5542 nsIFrame* rootFrame = GetRootFrame();
5544 // If the start or end of the range is the document, just use the root
5545 // frame, otherwise get the common ancestor of the two endpoints of the
5546 // range.
5547 nsINode* startParent = range->GetStartParent();
5548 nsINode* endParent = range->GetEndParent();
5549 nsIDocument* doc = startParent->GetCurrentDoc();
5550 if (startParent == doc || endParent == doc) {
5551 ancestorFrame = rootFrame;
5553 else {
5554 nsINode* ancestor = nsContentUtils::GetCommonAncestor(startParent, endParent);
5555 NS_ASSERTION(!ancestor || ancestor->IsNodeOfType(nsINode::eCONTENT),
5556 "common ancestor is not content");
5557 if (!ancestor || !ancestor->IsNodeOfType(nsINode::eCONTENT))
5558 return nsnull;
5560 nsIContent* ancestorContent = static_cast<nsIContent*>(ancestor);
5561 ancestorFrame = ancestorContent->GetPrimaryFrame();
5563 // use the nearest ancestor frame that includes all continuations as the
5564 // root for building the display list
5565 while (ancestorFrame &&
5566 nsLayoutUtils::GetNextContinuationOrSpecialSibling(ancestorFrame))
5567 ancestorFrame = ancestorFrame->GetParent();
5570 if (!ancestorFrame)
5571 return nsnull;
5573 info = new RangePaintInfo(range, ancestorFrame);
5574 if (!info)
5575 return nsnull;
5577 nsRect ancestorRect = ancestorFrame->GetVisualOverflowRect();
5579 // get a display list containing the range
5580 if (aForPrimarySelection) {
5581 info->mBuilder.SetSelectedFramesOnly();
5583 info->mBuilder.EnterPresShell(ancestorFrame, ancestorRect);
5584 ancestorFrame->BuildDisplayListForStackingContext(&info->mBuilder,
5585 ancestorRect, &info->mList);
5586 info->mBuilder.LeavePresShell(ancestorFrame, ancestorRect);
5588 #ifdef DEBUG
5589 if (gDumpRangePaintList) {
5590 fprintf(stderr, "CreateRangePaintInfo --- before ClipListToRange:\n");
5591 nsFrame::PrintDisplayList(&(info->mBuilder), info->mList);
5593 #endif
5595 nsRect rangeRect = ClipListToRange(&info->mBuilder, &info->mList, range);
5597 #ifdef DEBUG
5598 if (gDumpRangePaintList) {
5599 fprintf(stderr, "CreateRangePaintInfo --- after ClipListToRange:\n");
5600 nsFrame::PrintDisplayList(&(info->mBuilder), info->mList);
5602 #endif
5604 // determine the offset of the reference frame for the display list
5605 // to the root frame. This will allow the coordinates used when painting
5606 // to all be offset from the same point
5607 info->mRootOffset = ancestorFrame->GetOffsetTo(rootFrame);
5608 rangeRect.MoveBy(info->mRootOffset);
5609 aSurfaceRect.UnionRect(aSurfaceRect, rangeRect);
5611 return info;
5614 already_AddRefed<gfxASurface>
5615 PresShell::PaintRangePaintInfo(nsTArray<nsAutoPtr<RangePaintInfo> >* aItems,
5616 nsISelection* aSelection,
5617 nsIntRegion* aRegion,
5618 nsRect aArea,
5619 nsIntPoint& aPoint,
5620 nsIntRect* aScreenRect)
5622 NS_TIME_FUNCTION_WITH_DOCURL;
5624 nsPresContext* pc = GetPresContext();
5625 if (!pc || aArea.width == 0 || aArea.height == 0)
5626 return nsnull;
5628 nsIDeviceContext* deviceContext = pc->DeviceContext();
5630 // use the rectangle to create the surface
5631 nsIntRect pixelArea = aArea.ToOutsidePixels(pc->AppUnitsPerDevPixel());
5633 // if the area of the image is larger than the maximum area, scale it down
5634 float scale = 0.0;
5635 nsIntRect rootScreenRect =
5636 GetRootFrame()->GetScreenRectInAppUnits().ToNearestPixels(
5637 pc->AppUnitsPerDevPixel());
5639 // if the image is larger in one or both directions than half the size of
5640 // the available screen area, scale the image down to that size.
5641 nsRect maxSize;
5642 deviceContext->GetClientRect(maxSize);
5643 nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1);
5644 nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1);
5645 PRBool resize = (pixelArea.width > maxWidth || pixelArea.height > maxHeight);
5646 if (resize) {
5647 scale = 1.0;
5648 // divide the maximum size by the image size in both directions. Whichever
5649 // direction produces the smallest result determines how much should be
5650 // scaled.
5651 if (pixelArea.width > maxWidth)
5652 scale = NS_MIN(scale, float(maxWidth) / pixelArea.width);
5653 if (pixelArea.height > maxHeight)
5654 scale = NS_MIN(scale, float(maxHeight) / pixelArea.height);
5656 pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale);
5657 pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale);
5659 // adjust the screen position based on the rescaled size
5660 nscoord left = rootScreenRect.x + pixelArea.x;
5661 nscoord top = rootScreenRect.y + pixelArea.y;
5662 aScreenRect->x = NSToIntFloor(aPoint.x - float(aPoint.x - left) * scale);
5663 aScreenRect->y = NSToIntFloor(aPoint.y - float(aPoint.y - top) * scale);
5665 else {
5666 // move aScreenRect to the position of the surface in screen coordinates
5667 aScreenRect->MoveTo(rootScreenRect.x + pixelArea.x, rootScreenRect.y + pixelArea.y);
5669 aScreenRect->width = pixelArea.width;
5670 aScreenRect->height = pixelArea.height;
5672 gfxImageSurface* surface =
5673 new gfxImageSurface(gfxIntSize(pixelArea.width, pixelArea.height),
5674 gfxImageSurface::ImageFormatARGB32);
5675 if (!surface || surface->CairoStatus()) {
5676 delete surface;
5677 return nsnull;
5680 // clear the image
5681 gfxContext context(surface);
5682 context.SetOperator(gfxContext::OPERATOR_CLEAR);
5683 context.Rectangle(gfxRect(0, 0, pixelArea.width, pixelArea.height));
5684 context.Fill();
5686 nsCOMPtr<nsIRenderingContext> rc;
5687 deviceContext->CreateRenderingContextInstance(*getter_AddRefs(rc));
5688 rc->Init(deviceContext, surface);
5690 if (aRegion) {
5691 // Convert aRegion from CSS pixels to dev pixels
5692 nsIntRegion region =
5693 aRegion->ToAppUnits(nsPresContext::AppUnitsPerCSSPixel())
5694 .ToOutsidePixels(pc->AppUnitsPerDevPixel());
5695 rc->SetClipRegion(region, nsClipCombine_kReplace);
5698 if (resize)
5699 rc->Scale(scale, scale);
5701 // translate so that points are relative to the surface area
5702 rc->Translate(-aArea.x, -aArea.y);
5704 // temporarily hide the selection so that text is drawn normally. If a
5705 // selection is being rendered, use that, otherwise use the presshell's
5706 // selection.
5707 nsCOMPtr<nsFrameSelection> frameSelection;
5708 if (aSelection) {
5709 nsCOMPtr<nsISelectionPrivate> selpriv = do_QueryInterface(aSelection);
5710 selpriv->GetFrameSelection(getter_AddRefs(frameSelection));
5712 else {
5713 frameSelection = FrameSelection();
5715 PRInt16 oldDisplaySelection = frameSelection->GetDisplaySelection();
5716 frameSelection->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
5718 // next, paint each range in the selection
5719 PRInt32 count = aItems->Length();
5720 for (PRInt32 i = 0; i < count; i++) {
5721 RangePaintInfo* rangeInfo = (*aItems)[i];
5722 // the display lists paint relative to the offset from the reference
5723 // frame, so translate the rendering context
5724 nsIRenderingContext::AutoPushTranslation
5725 translate(rc, rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y);
5727 aArea.MoveBy(-rangeInfo->mRootOffset.x, -rangeInfo->mRootOffset.y);
5728 nsRegion visible(aArea);
5729 rangeInfo->mList.ComputeVisibilityForRoot(&rangeInfo->mBuilder, &visible);
5730 rangeInfo->mList.PaintRoot(&rangeInfo->mBuilder, rc, nsDisplayList::PAINT_DEFAULT);
5731 aArea.MoveBy(rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y);
5734 // restore the old selection display state
5735 frameSelection->SetDisplaySelection(oldDisplaySelection);
5737 NS_ADDREF(surface);
5738 return surface;
5741 already_AddRefed<gfxASurface>
5742 PresShell::RenderNode(nsIDOMNode* aNode,
5743 nsIntRegion* aRegion,
5744 nsIntPoint& aPoint,
5745 nsIntRect* aScreenRect)
5747 // area will hold the size of the surface needed to draw the node, measured
5748 // from the root frame.
5749 nsRect area;
5750 nsTArray<nsAutoPtr<RangePaintInfo> > rangeItems;
5752 // nothing to draw if the node isn't in a document
5753 nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
5754 if (!node->IsInDoc())
5755 return nsnull;
5757 nsCOMPtr<nsIDOMRange> range;
5758 NS_NewRange(getter_AddRefs(range));
5759 if (NS_FAILED(range->SelectNode(aNode)))
5760 return nsnull;
5762 RangePaintInfo* info = CreateRangePaintInfo(range, area, PR_FALSE);
5763 if (info && !rangeItems.AppendElement(info)) {
5764 delete info;
5765 return nsnull;
5768 if (aRegion) {
5769 // combine the area with the supplied region
5770 nsIntRect rrectPixels = aRegion->GetBounds();
5772 nsRect rrect = rrectPixels.ToAppUnits(nsPresContext::AppUnitsPerCSSPixel());
5773 area.IntersectRect(area, rrect);
5775 nsPresContext* pc = GetPresContext();
5776 if (!pc)
5777 return nsnull;
5779 // move the region so that it is offset from the topleft corner of the surface
5780 aRegion->MoveBy(-pc->AppUnitsToDevPixels(area.x),
5781 -pc->AppUnitsToDevPixels(area.y));
5784 return PaintRangePaintInfo(&rangeItems, nsnull, aRegion, area, aPoint,
5785 aScreenRect);
5788 already_AddRefed<gfxASurface>
5789 PresShell::RenderSelection(nsISelection* aSelection,
5790 nsIntPoint& aPoint,
5791 nsIntRect* aScreenRect)
5793 // area will hold the size of the surface needed to draw the selection,
5794 // measured from the root frame.
5795 nsRect area;
5796 nsTArray<nsAutoPtr<RangePaintInfo> > rangeItems;
5798 // iterate over each range and collect them into the rangeItems array.
5799 // This is done so that the size of selection can be determined so as
5800 // to allocate a surface area
5801 PRInt32 numRanges;
5802 aSelection->GetRangeCount(&numRanges);
5803 NS_ASSERTION(numRanges > 0, "RenderSelection called with no selection");
5805 for (PRInt32 r = 0; r < numRanges; r++)
5807 nsCOMPtr<nsIDOMRange> range;
5808 aSelection->GetRangeAt(r, getter_AddRefs(range));
5810 RangePaintInfo* info = CreateRangePaintInfo(range, area, PR_TRUE);
5811 if (info && !rangeItems.AppendElement(info)) {
5812 delete info;
5813 return nsnull;
5817 return PaintRangePaintInfo(&rangeItems, aSelection, nsnull, area, aPoint,
5818 aScreenRect);
5821 nsresult
5822 PresShell::AddPrintPreviewBackgroundItem(nsDisplayListBuilder& aBuilder,
5823 nsDisplayList& aList,
5824 nsIFrame* aFrame,
5825 const nsRect& aBounds)
5827 return aList.AppendNewToBottom(new (&aBuilder)
5828 nsDisplaySolidColor(&aBuilder, aFrame, aBounds, NS_RGB(115, 115, 115)));
5831 static PRBool
5832 AddCanvasBackgroundColor(const nsDisplayList& aList, nsIFrame* aCanvasFrame,
5833 nscolor aColor)
5835 for (nsDisplayItem* i = aList.GetBottom(); i; i = i->GetAbove()) {
5836 if (i->GetUnderlyingFrame() == aCanvasFrame &&
5837 i->GetType() == nsDisplayItem::TYPE_CANVAS_BACKGROUND) {
5838 nsDisplayCanvasBackground* bg = static_cast<nsDisplayCanvasBackground*>(i);
5839 bg->SetExtraBackgroundColor(aColor);
5840 return PR_TRUE;
5842 nsDisplayList* sublist = i->GetList();
5843 if (sublist && AddCanvasBackgroundColor(*sublist, aCanvasFrame, aColor))
5844 return PR_TRUE;
5846 return PR_FALSE;
5849 nsresult PresShell::AddCanvasBackgroundColorItem(nsDisplayListBuilder& aBuilder,
5850 nsDisplayList& aList,
5851 nsIFrame* aFrame,
5852 const nsRect& aBounds,
5853 nscolor aBackstopColor,
5854 PRBool aForceDraw)
5856 // We don't want to add an item for the canvas background color if the frame
5857 // (sub)tree we are painting doesn't include any canvas frames. There isn't
5858 // an easy way to check this directly, but if we check if the root of the
5859 // (sub)tree we are painting is a canvas frame that should cover us in all
5860 // cases (it will usually be a viewport frame when we have a canvas frame in
5861 // the (sub)tree).
5862 if (!aForceDraw && !nsCSSRendering::IsCanvasFrame(aFrame))
5863 return NS_OK;
5865 nscolor bgcolor = NS_ComposeColors(aBackstopColor, mCanvasBackgroundColor);
5867 // To make layers work better, we want to avoid having a big non-scrolled
5868 // color background behind a scrolled transparent background. Instead,
5869 // we'll try to move the color background into the scrolled content
5870 // by making nsDisplayCanvasBackground paint it.
5871 if (!aFrame->GetParent()) {
5872 nsIScrollableFrame* sf =
5873 aFrame->PresContext()->PresShell()->GetRootScrollFrameAsScrollable();
5874 if (sf) {
5875 nsCanvasFrame* canvasFrame = do_QueryFrame(sf->GetScrolledFrame());
5876 if (canvasFrame && canvasFrame->IsVisibleForPainting(&aBuilder)) {
5877 if (AddCanvasBackgroundColor(aList, canvasFrame, bgcolor))
5878 return NS_OK;
5883 return aList.AppendNewToBottom(
5884 new (&aBuilder) nsDisplaySolidColor(&aBuilder, aFrame, aBounds, bgcolor));
5887 static PRBool IsTransparentContainerElement(nsPresContext* aPresContext)
5889 nsCOMPtr<nsISupports> container = aPresContext->GetContainerInternal();
5890 nsCOMPtr<nsIDocShellTreeItem> docShellItem = do_QueryInterface(container);
5891 nsCOMPtr<nsPIDOMWindow> pwin(do_GetInterface(docShellItem));
5892 if (!pwin)
5893 return PR_FALSE;
5894 nsCOMPtr<nsIContent> containerElement =
5895 do_QueryInterface(pwin->GetFrameElementInternal());
5896 return containerElement &&
5897 containerElement->HasAttr(kNameSpaceID_None, nsGkAtoms::transparent);
5900 void PresShell::UpdateCanvasBackground()
5902 // If we have a frame tree and it has style information that
5903 // specifies the background color of the canvas, update our local
5904 // cache of that color.
5905 nsIFrame* rootStyleFrame = FrameConstructor()->GetRootElementStyleFrame();
5906 if (rootStyleFrame) {
5907 nsStyleContext* bgStyle =
5908 nsCSSRendering::FindRootFrameBackground(rootStyleFrame);
5909 // XXX We should really be passing the canvasframe, not the root element
5910 // style frame but we don't have access to the canvasframe here. It isn't
5911 // a problem because only a few frames can return something other than true
5912 // and none of them would be a canvas frame or root element style frame.
5913 mCanvasBackgroundColor =
5914 nsCSSRendering::DetermineBackgroundColor(mPresContext, bgStyle,
5915 rootStyleFrame);
5916 if (GetPresContext()->IsRootContentDocument() &&
5917 !IsTransparentContainerElement(mPresContext)) {
5918 mCanvasBackgroundColor =
5919 NS_ComposeColors(mPresContext->DefaultBackgroundColor(), mCanvasBackgroundColor);
5923 // If the root element of the document (ie html) has style 'display: none'
5924 // then the document's background color does not get drawn; cache the
5925 // color we actually draw.
5926 if (!FrameConstructor()->GetRootElementFrame()) {
5927 mCanvasBackgroundColor = mPresContext->DefaultBackgroundColor();
5931 nscolor PresShell::ComputeBackstopColor(nsIView* aDisplayRoot)
5933 nsIWidget* widget = aDisplayRoot->GetWidget();
5934 if (widget && widget->GetTransparencyMode() != eTransparencyOpaque) {
5935 // Within a transparent widget, so the backstop color must be
5936 // totally transparent.
5937 return NS_RGBA(0,0,0,0);
5939 // Within an opaque widget (or no widget at all), so the backstop
5940 // color must be totally opaque. The user's default background
5941 // as reported by the prescontext is guaranteed to be opaque.
5942 return GetPresContext()->DefaultBackgroundColor();
5945 struct PaintParams {
5946 nsIFrame* mFrame;
5947 nsPoint mOffsetToWidget;
5948 const nsRegion* mDirtyRegion;
5949 nscolor mBackgroundColor;
5952 LayerManager* PresShell::GetLayerManager()
5954 NS_ASSERTION(mViewManager, "Should have view manager");
5956 nsIView* rootView;
5957 if (NS_SUCCEEDED(mViewManager->GetRootView(rootView)) && rootView) {
5958 if (nsIWidget* widget = rootView->GetWidget()) {
5959 return widget->GetLayerManager();
5962 return nsnull;
5965 void PresShell::SetIgnoreViewportScrolling(PRBool aIgnore)
5967 if (IgnoringViewportScrolling() == aIgnore) {
5968 return;
5970 RenderingState state(this);
5971 state.mRenderFlags = ChangeFlag(state.mRenderFlags, aIgnore,
5972 STATE_IGNORING_VIEWPORT_SCROLLING);
5973 SetRenderingState(state);
5976 void PresShell::SetDisplayPort(const nsRect& aDisplayPort)
5978 if (UsingDisplayPort() && mDisplayPort == aDisplayPort) {
5979 return;
5981 RenderingState state(this);
5982 state.mRenderFlags = ChangeFlag(mRenderFlags, PR_TRUE,
5983 STATE_USING_DISPLAYPORT);
5984 state.mDisplayPort = aDisplayPort;
5985 SetRenderingState(state);
5988 nsresult PresShell::SetResolution(float aXResolution, float aYResolution)
5990 if (!(aXResolution > 0.0 && aXResolution > 0.0)) {
5991 return NS_ERROR_ILLEGAL_VALUE;
5993 if (aXResolution == mXResolution && aYResolution == mYResolution) {
5994 return NS_OK;
5996 RenderingState state(this);
5997 state.mXResolution = aXResolution;
5998 state.mYResolution = aYResolution;
5999 SetRenderingState(state);
6000 return NS_OK;
6003 void PresShell::SetRenderingState(const RenderingState& aState)
6005 if (mRenderFlags != aState.mRenderFlags) {
6006 // Rendering state changed in a way that forces us to flush any
6007 // retained layers we might already have.
6008 LayerManager* manager = GetLayerManager();
6009 if (manager) {
6010 FrameLayerBuilder::InvalidateAllLayers(manager);
6014 mRenderFlags = aState.mRenderFlags;
6015 if (UsingDisplayPort()) {
6016 mDisplayPort = aState.mDisplayPort;
6017 } else {
6018 mDisplayPort = nsRect();
6020 mXResolution = aState.mXResolution;
6021 mYResolution = aState.mYResolution;
6023 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
6024 if (rootFrame) {
6025 rootFrame->InvalidateFrameSubtree();
6029 void PresShell::SynthesizeMouseMove(PRBool aFromScroll)
6031 if (mViewManager && !mPaintingSuppressed && mIsActive) {
6032 mViewManager->SynthesizeMouseMove(aFromScroll);
6036 static void DrawThebesLayer(ThebesLayer* aLayer,
6037 gfxContext* aContext,
6038 const nsIntRegion& aRegionToDraw,
6039 const nsIntRegion& aRegionToInvalidate,
6040 void* aCallbackData)
6042 PaintParams* params = static_cast<PaintParams*>(aCallbackData);
6043 nsIFrame* frame = params->mFrame;
6044 if (frame) {
6045 // We're drawing into a child window.
6046 nsIDeviceContext* devCtx = frame->PresContext()->DeviceContext();
6047 nsCOMPtr<nsIRenderingContext> rc;
6048 nsresult rv = devCtx->CreateRenderingContextInstance(*getter_AddRefs(rc));
6049 if (NS_SUCCEEDED(rv)) {
6050 rc->Init(devCtx, aContext);
6051 nsIRenderingContext::AutoPushTranslation
6052 push(rc, params->mOffsetToWidget.x, params->mOffsetToWidget.y);
6053 nsLayoutUtils::PaintFrame(rc, frame, *params->mDirtyRegion,
6054 params->mBackgroundColor,
6055 nsLayoutUtils::PAINT_WIDGET_LAYERS);
6057 } else {
6058 aContext->NewPath();
6059 aContext->SetColor(gfxRGBA(params->mBackgroundColor));
6060 nsIntRect dirtyRect = aRegionToDraw.GetBounds();
6061 aContext->Rectangle(
6062 gfxRect(dirtyRect.x, dirtyRect.y, dirtyRect.width, dirtyRect.height));
6063 aContext->Fill();
6067 NS_IMETHODIMP
6068 PresShell::Paint(nsIView* aDisplayRoot,
6069 nsIView* aViewToPaint,
6070 nsIWidget* aWidgetToPaint,
6071 const nsRegion& aDirtyRegion,
6072 const nsIntRegion& aIntDirtyRegion,
6073 PRBool aPaintDefaultBackground,
6074 PRBool aWillSendDidPaint)
6076 #ifdef NS_FUNCTION_TIMER
6077 NS_TIME_FUNCTION_DECLARE_DOCURL;
6078 const nsRect& bounds__ = aDirtyRegion.GetBounds();
6079 NS_TIME_FUNCTION_MIN_FMT(1.0, "%s (line %d) (document: %s, dirty rect: (<%f, %f>, <%f, %f>)",
6080 MOZ_FUNCTION_NAME, __LINE__, docURL__.get(),
6081 NSCoordToFloat(bounds__.x),
6082 NSCoordToFloat(bounds__.y),
6083 NSCoordToFloat(bounds__.XMost()),
6084 NSCoordToFloat(bounds__.YMost()));
6085 #endif
6087 nsPresContext* presContext = GetPresContext();
6088 AUTO_LAYOUT_PHASE_ENTRY_POINT(presContext, Paint);
6090 NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
6091 NS_ASSERTION(aDisplayRoot, "null view");
6092 NS_ASSERTION(aViewToPaint, "null view");
6093 NS_ASSERTION(aWidgetToPaint, "Can't paint without a widget");
6095 nscolor bgcolor = ComputeBackstopColor(aDisplayRoot);
6097 nsIFrame* frame = aPaintDefaultBackground
6098 ? nsnull : static_cast<nsIFrame*>(aDisplayRoot->GetClientData());
6100 if (frame && aViewToPaint == aDisplayRoot) {
6101 // Defer invalidates that are triggered during painting, and discard
6102 // invalidates of areas that are already being repainted.
6103 // The layer system can trigger invalidates during painting
6104 // (see FrameLayerBuilder).
6105 frame->BeginDeferringInvalidatesForDisplayRoot(aDirtyRegion);
6107 // We can paint directly into the widget using its layer manager.
6108 // When we get rid of child widgets, this will be the only path we
6109 // need. (aPaintDefaultBackground will never be needed since the
6110 // chrome can always paint a default background.)
6111 nsLayoutUtils::PaintFrame(nsnull, frame, aDirtyRegion, bgcolor,
6112 nsLayoutUtils::PAINT_WIDGET_LAYERS);
6114 frame->EndDeferringInvalidatesForDisplayRoot();
6115 return NS_OK;
6118 if (frame) {
6119 // Defer invalidates that are triggered during painting, and discard
6120 // invalidates of areas that are already being repainted.
6121 frame->BeginDeferringInvalidatesForDisplayRoot(aDirtyRegion);
6124 LayerManager* layerManager = aWidgetToPaint->GetLayerManager();
6125 NS_ASSERTION(layerManager, "Must be in paint event");
6127 layerManager->BeginTransaction();
6128 nsRefPtr<ThebesLayer> root = layerManager->CreateThebesLayer();
6129 if (root) {
6130 root->SetVisibleRegion(aIntDirtyRegion);
6131 layerManager->SetRoot(root);
6133 if (!frame) {
6134 bgcolor = NS_ComposeColors(bgcolor, mCanvasBackgroundColor);
6136 PaintParams params =
6137 { frame,
6138 aDisplayRoot->GetOffsetToWidget(aWidgetToPaint),
6139 &aDirtyRegion,
6140 bgcolor };
6141 layerManager->EndTransaction(DrawThebesLayer, &params);
6143 if (frame) {
6144 frame->EndDeferringInvalidatesForDisplayRoot();
6146 return NS_OK;
6149 // static
6150 void
6151 nsIPresShell::SetCapturingContent(nsIContent* aContent, PRUint8 aFlags)
6153 NS_IF_RELEASE(gCaptureInfo.mContent);
6155 // only set capturing content if allowed or the CAPTURE_IGNOREALLOWED flag
6156 // is used
6157 if ((aFlags & CAPTURE_IGNOREALLOWED) || gCaptureInfo.mAllowed) {
6158 if (aContent) {
6159 NS_ADDREF(gCaptureInfo.mContent = aContent);
6161 gCaptureInfo.mRetargetToElement = (aFlags & CAPTURE_RETARGETTOELEMENT) != 0;
6162 gCaptureInfo.mPreventDrag = (aFlags & CAPTURE_PREVENTDRAG) != 0;
6166 nsIFrame*
6167 PresShell::GetCurrentEventFrame()
6169 if (NS_UNLIKELY(mIsDestroying)) {
6170 return nsnull;
6173 if (!mCurrentEventFrame && mCurrentEventContent) {
6174 // Make sure the content still has a document reference. If not,
6175 // then we assume it is no longer in the content tree and the
6176 // frame shouldn't get an event, nor should we even assume its
6177 // safe to try and find the frame.
6178 if (mCurrentEventContent->GetDocument()) {
6179 mCurrentEventFrame = mCurrentEventContent->GetPrimaryFrame();
6183 return mCurrentEventFrame;
6186 nsIFrame*
6187 PresShell::GetEventTargetFrame()
6189 return GetCurrentEventFrame();
6192 already_AddRefed<nsIContent>
6193 PresShell::GetEventTargetContent(nsEvent* aEvent)
6195 nsIContent* content = nsnull;
6197 if (mCurrentEventContent) {
6198 content = mCurrentEventContent;
6199 NS_IF_ADDREF(content);
6200 } else {
6201 nsIFrame* currentEventFrame = GetCurrentEventFrame();
6202 if (currentEventFrame) {
6203 currentEventFrame->GetContentForEvent(mPresContext, aEvent, &content);
6204 } else {
6205 content = nsnull;
6208 return content;
6211 void
6212 PresShell::PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent)
6214 if (mCurrentEventFrame || mCurrentEventContent) {
6215 mCurrentEventFrameStack.InsertElementAt(0, mCurrentEventFrame);
6216 mCurrentEventContentStack.InsertObjectAt(mCurrentEventContent, 0);
6218 mCurrentEventFrame = aFrame;
6219 mCurrentEventContent = aContent;
6222 void
6223 PresShell::PopCurrentEventInfo()
6225 mCurrentEventFrame = nsnull;
6226 mCurrentEventContent = nsnull;
6228 if (0 != mCurrentEventFrameStack.Length()) {
6229 mCurrentEventFrame = mCurrentEventFrameStack.ElementAt(0);
6230 mCurrentEventFrameStack.RemoveElementAt(0);
6231 mCurrentEventContent = mCurrentEventContentStack.ObjectAt(0);
6232 mCurrentEventContentStack.RemoveObjectAt(0);
6236 PRBool PresShell::InZombieDocument(nsIContent *aContent)
6238 // If a content node points to a null document, or the document is not
6239 // attached to a window, then it is possibly in a zombie document,
6240 // about to be replaced by a newly loading document.
6241 // Such documents cannot handle DOM events.
6242 // It might actually be in a node not attached to any document,
6243 // in which case there is not parent presshell to retarget it to.
6244 nsIDocument *doc = aContent->GetDocument();
6245 return !doc || !doc->GetWindow();
6248 already_AddRefed<nsPIDOMWindow>
6249 PresShell::GetRootWindow()
6251 nsCOMPtr<nsPIDOMWindow> window =
6252 do_QueryInterface(mDocument->GetWindow());
6253 if (window) {
6254 nsCOMPtr<nsPIDOMWindow> rootWindow = window->GetPrivateRoot();
6255 NS_ASSERTION(rootWindow, "nsPIDOMWindow::GetPrivateRoot() returns NULL");
6256 return rootWindow.forget();
6259 // If we don't have DOM window, we're zombie, we should find the root window
6260 // with our parent shell.
6261 nsCOMPtr<nsIPresShell> parent = GetParentPresShell();
6262 NS_ENSURE_TRUE(parent, nsnull);
6263 return parent->GetRootWindow();
6266 already_AddRefed<nsIPresShell>
6267 PresShell::GetParentPresShell()
6269 NS_ENSURE_TRUE(mPresContext, nsnull);
6270 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
6271 if (!container) {
6272 container = do_QueryReferent(mForwardingContainer);
6275 // Now, find the parent pres shell and send the event there
6276 nsCOMPtr<nsIDocShellTreeItem> treeItem = do_QueryInterface(container);
6277 // Might have gone away, or never been around to start with
6278 NS_ENSURE_TRUE(treeItem, nsnull);
6280 nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
6281 treeItem->GetParent(getter_AddRefs(parentTreeItem));
6282 nsCOMPtr<nsIDocShell> parentDocShell = do_QueryInterface(parentTreeItem);
6283 NS_ENSURE_TRUE(parentDocShell && treeItem != parentTreeItem, nsnull);
6285 nsIPresShell* parentPresShell = nsnull;
6286 parentDocShell->GetPresShell(&parentPresShell);
6287 return parentPresShell;
6290 nsresult
6291 PresShell::RetargetEventToParent(nsGUIEvent* aEvent,
6292 nsEventStatus* aEventStatus)
6294 // Send this events straight up to the parent pres shell.
6295 // We do this for keystroke events in zombie documents or if either a frame
6296 // or a root content is not present.
6297 // That way at least the UI key bindings can work.
6299 nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
6300 nsCOMPtr<nsIPresShell> parentPresShell = GetParentPresShell();
6301 NS_ENSURE_TRUE(parentPresShell, NS_ERROR_FAILURE);
6302 nsCOMPtr<nsIViewObserver> parentViewObserver =
6303 do_QueryInterface(parentPresShell);
6304 if (!parentViewObserver) {
6305 return NS_ERROR_FAILURE;
6308 // Fake the event as though it'ss from the parent pres shell's root view.
6309 nsIView *parentRootView;
6310 parentPresShell->GetViewManager()->GetRootView(parentRootView);
6312 sDontRetargetEvents = PR_TRUE;
6313 nsresult rv = parentViewObserver->HandleEvent(parentRootView, aEvent, aEventStatus);
6314 sDontRetargetEvents = PR_FALSE;
6315 return rv;
6318 void
6319 PresShell::DisableNonTestMouseEvents(PRBool aDisable)
6321 sDisableNonTestMouseEvents = aDisable;
6324 already_AddRefed<nsPIDOMWindow>
6325 PresShell::GetFocusedDOMWindowInOurWindow()
6327 nsCOMPtr<nsPIDOMWindow> rootWindow = GetRootWindow();
6328 NS_ENSURE_TRUE(rootWindow, nsnull);
6329 nsPIDOMWindow* focusedWindow;
6330 nsFocusManager::GetFocusedDescendant(rootWindow, PR_TRUE, &focusedWindow);
6331 return focusedWindow;
6334 NS_IMETHODIMP
6335 PresShell::HandleEvent(nsIView *aView,
6336 nsGUIEvent* aEvent,
6337 nsEventStatus* aEventStatus)
6339 NS_ASSERTION(aView, "null view");
6341 if (mIsDestroying ||
6342 (sDisableNonTestMouseEvents && NS_IS_MOUSE_EVENT(aEvent) &&
6343 !(aEvent->flags & NS_EVENT_FLAG_SYNTHETIC_TEST_EVENT))) {
6344 return NS_OK;
6347 #ifdef ACCESSIBILITY
6348 if (aEvent->eventStructType == NS_ACCESSIBLE_EVENT) {
6349 NS_TIME_FUNCTION_MIN(1.0);
6351 // Accessibility events come through OS requests and not from scripts,
6352 // so it is safe to handle here
6353 return HandleEventInternal(aEvent, aView, aEventStatus);
6355 #endif
6357 if (!nsContentUtils::IsSafeToRunScript())
6358 return NS_OK;
6360 NS_TIME_FUNCTION_MIN(1.0);
6362 nsIContent* capturingContent =
6363 NS_IS_MOUSE_EVENT(aEvent) ? GetCapturingContent() : nsnull;
6365 nsCOMPtr<nsIDocument> retargetEventDoc;
6366 if (!sDontRetargetEvents) {
6367 // key and IME related events should not cross top level window boundary.
6368 // Basically, such input events should be fired only on focused widget.
6369 // However, some IMEs might need to clean up composition after focused
6370 // window is deactivated. And also some tests on MozMill want to test key
6371 // handling on deactivated window because MozMill window can be activated
6372 // during tests. So, there is no merit the events should be redirected to
6373 // active window. So, the events should be handled on the last focused
6374 // content in the last focused DOM window in same top level window.
6375 // Note, if no DOM window has been focused yet, we can discard the events.
6376 if (NS_IsEventTargetedAtFocusedWindow(aEvent)) {
6377 nsCOMPtr<nsPIDOMWindow> window = GetFocusedDOMWindowInOurWindow();
6378 // No DOM window in same top level window has not been focused yet,
6379 // discard the events.
6380 if (!window) {
6381 return NS_OK;
6384 retargetEventDoc = do_QueryInterface(window->GetExtantDocument());
6385 if (!retargetEventDoc)
6386 return NS_OK;
6387 } else if (capturingContent) {
6388 // if the mouse is being captured then retarget the mouse event at the
6389 // document that is being captured.
6390 retargetEventDoc = capturingContent->GetCurrentDoc();
6393 if (retargetEventDoc) {
6394 nsIPresShell* presShell = retargetEventDoc->GetShell();
6395 if (!presShell)
6396 return NS_OK;
6398 if (presShell != this) {
6399 nsCOMPtr<nsIViewObserver> viewObserver = do_QueryInterface(presShell);
6400 if (!viewObserver)
6401 return NS_ERROR_FAILURE;
6403 nsIView *view;
6404 presShell->GetViewManager()->GetRootView(view);
6405 sDontRetargetEvents = PR_TRUE;
6406 nsresult rv = viewObserver->HandleEvent(view, aEvent, aEventStatus);
6407 sDontRetargetEvents = PR_FALSE;
6408 return rv;
6413 // Check for a theme change up front, since the frame type is irrelevant
6414 if (aEvent->message == NS_THEMECHANGED && mPresContext) {
6415 mPresContext->ThemeChanged();
6416 return NS_OK;
6419 if (aEvent->message == NS_UISTATECHANGED && mDocument) {
6420 nsPIDOMWindow* win = mDocument->GetWindow();
6421 if (win) {
6422 nsUIStateChangeEvent* event = (nsUIStateChangeEvent*)aEvent;
6423 win->SetKeyboardIndicators(event->showAccelerators, event->showFocusRings);
6425 return NS_OK;
6428 // Check for a system color change up front, since the frame type is
6429 // irrelevant
6430 if ((aEvent->message == NS_SYSCOLORCHANGED) && mPresContext) {
6431 nsIViewManager* vm = GetViewManager();
6432 if (vm) {
6433 // Only dispatch system color change when the message originates from
6434 // from the root views widget. This is necessary to prevent us from
6435 // dispatching the SysColorChanged notification for each child window
6436 // which may be redundant.
6437 nsIView *view;
6438 vm->GetRootView(view);
6439 if (view == aView) {
6440 *aEventStatus = nsEventStatus_eConsumeDoDefault;
6441 mPresContext->SysColorChanged();
6442 return NS_OK;
6445 return NS_OK;
6448 if (aEvent->eventStructType == NS_KEY_EVENT &&
6449 mDocument && mDocument->EventHandlingSuppressed()) {
6450 if (aEvent->message == NS_KEY_DOWN) {
6451 mNoDelayedKeyEvents = PR_TRUE;
6452 } else if (!mNoDelayedKeyEvents) {
6453 nsDelayedEvent* event =
6454 new nsDelayedKeyEvent(static_cast<nsKeyEvent*>(aEvent));
6455 if (event && !mDelayedEvents.AppendElement(event)) {
6456 delete event;
6459 return NS_OK;
6462 nsIFrame* frame = static_cast<nsIFrame*>(aView->GetClientData());
6463 PRBool dispatchUsingCoordinates = NS_IsEventUsingCoordinates(aEvent);
6465 // if this event has no frame, we need to retarget it at a parent
6466 // view that has a frame.
6467 if (!frame &&
6468 (dispatchUsingCoordinates || NS_IS_KEY_EVENT(aEvent) ||
6469 NS_IS_IME_RELATED_EVENT(aEvent) || NS_IS_NON_RETARGETED_PLUGIN_EVENT(aEvent) ||
6470 aEvent->message == NS_PLUGIN_ACTIVATE || aEvent->message == NS_PLUGIN_FOCUS)) {
6471 nsIView* targetView = aView;
6472 while (targetView && !targetView->GetClientData()) {
6473 targetView = targetView->GetParent();
6476 if (targetView) {
6477 aView = targetView;
6478 frame = static_cast<nsIFrame*>(aView->GetClientData());
6482 if (dispatchUsingCoordinates) {
6483 NS_WARN_IF_FALSE(frame, "Nothing to handle this event!");
6484 if (!frame)
6485 return NS_OK;
6487 nsPresContext* framePresContext = frame->PresContext();
6488 nsPresContext* rootPresContext = framePresContext->GetRootPresContext();
6489 NS_ASSERTION(rootPresContext == mPresContext->GetRootPresContext(),
6490 "How did we end up outside the connected prescontext/viewmanager hierarchy?");
6491 // If we aren't starting our event dispatch from the root frame of the root prescontext,
6492 // then someone must be capturing the mouse. In that case we don't want to search the popup
6493 // list.
6494 if (framePresContext == rootPresContext &&
6495 frame == FrameManager()->GetRootFrame()) {
6496 nsIFrame* popupFrame =
6497 nsLayoutUtils::GetPopupFrameForEventCoordinates(rootPresContext, aEvent);
6498 // If the popupFrame is an ancestor of the 'frame', the frame should
6499 // handle the event, otherwise, the popup should handle it.
6500 if (popupFrame &&
6501 !nsContentUtils::ContentIsCrossDocDescendantOf(
6502 framePresContext->GetPresShell()->GetDocument(),
6503 popupFrame->GetContent())) {
6504 frame = popupFrame;
6508 PRBool captureRetarget = PR_FALSE;
6509 if (capturingContent) {
6510 // If a capture is active, determine if the docshell is visible. If not,
6511 // clear the capture and target the mouse event normally instead. This
6512 // would occur if the mouse button is held down while a tab change occurs.
6513 // If the docshell is visible, look for a scrolling container.
6514 PRBool vis;
6515 nsCOMPtr<nsISupports> supports = mPresContext->GetContainer();
6516 nsCOMPtr<nsIBaseWindow> baseWin(do_QueryInterface(supports));
6517 if (baseWin && NS_SUCCEEDED(baseWin->GetVisibility(&vis)) && vis) {
6518 captureRetarget = gCaptureInfo.mRetargetToElement;
6519 if (!captureRetarget) {
6520 // A check was already done above to ensure that capturingContent is
6521 // in this presshell.
6522 NS_ASSERTION(capturingContent->GetCurrentDoc() == GetDocument(),
6523 "Unexpected document");
6524 nsIFrame* captureFrame = capturingContent->GetPrimaryFrame();
6525 if (captureFrame) {
6526 if (capturingContent->Tag() == nsGkAtoms::select &&
6527 capturingContent->IsHTML()) {
6528 // a dropdown <select> has a child in its selectPopupList and we should
6529 // capture on that instead.
6530 nsIFrame* childFrame = captureFrame->GetChildList(nsGkAtoms::selectPopupList).FirstChild();
6531 if (childFrame) {
6532 captureFrame = childFrame;
6536 // scrollable frames should use the scrolling container as
6537 // the root instead of the document
6538 nsIScrollableFrame* scrollFrame = do_QueryFrame(captureFrame);
6539 if (scrollFrame) {
6540 frame = scrollFrame->GetScrolledFrame();
6545 else {
6546 ClearMouseCapture(nsnull);
6547 capturingContent = nsnull;
6551 // Get the frame at the event point. However, don't do this if we're
6552 // capturing and retargeting the event because the captured frame will
6553 // be used instead below.
6554 if (!captureRetarget) {
6555 nsPoint eventPoint
6556 = nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
6558 PRBool ignoreRootScrollFrame = PR_FALSE;
6559 if (aEvent->eventStructType == NS_MOUSE_EVENT) {
6560 ignoreRootScrollFrame = static_cast<nsMouseEvent*>(aEvent)->ignoreRootScrollFrame;
6562 nsIFrame* target = nsLayoutUtils::GetFrameForPoint(frame, eventPoint,
6563 PR_FALSE, ignoreRootScrollFrame);
6564 if (target) {
6565 frame = target;
6570 // if a node is capturing the mouse, check if the event needs to be
6571 // retargeted at the capturing content instead. This will be the case when
6572 // capture retargeting is being used, no frame was found or the frame's
6573 // content is not a descendant of the capturing content.
6574 if (capturingContent &&
6575 (gCaptureInfo.mRetargetToElement || !frame->GetContent() ||
6576 !nsContentUtils::ContentIsCrossDocDescendantOf(frame->GetContent(),
6577 capturingContent))) {
6578 // A check was already done above to ensure that capturingContent is
6579 // in this presshell.
6580 NS_ASSERTION(capturingContent->GetCurrentDoc() == GetDocument(),
6581 "Unexpected document");
6582 nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
6583 if (capturingFrame) {
6584 frame = capturingFrame;
6585 aView = frame->GetClosestView();
6589 // Suppress mouse event if it's being targeted at an element inside
6590 // a document which needs events suppressed
6591 if (aEvent->eventStructType == NS_MOUSE_EVENT &&
6592 frame->PresContext()->Document()->EventHandlingSuppressed()) {
6593 if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
6594 mNoDelayedMouseEvents = PR_TRUE;
6595 } else if (!mNoDelayedMouseEvents && aEvent->message == NS_MOUSE_BUTTON_UP) {
6596 nsDelayedEvent* event =
6597 new nsDelayedMouseEvent(static_cast<nsMouseEvent*>(aEvent));
6598 if (!mDelayedEvents.AppendElement(event)) {
6599 delete event;
6603 return NS_OK;
6606 PresShell* shell =
6607 static_cast<PresShell*>(frame->PresContext()->PresShell());
6609 // Check if we have an active EventStateManager which isn't the
6610 // EventStateManager of the current PresContext.
6611 // If that is the case, and mouse is over some ancestor document,
6612 // forward event handling to the active document.
6613 // This way content can get mouse events even when
6614 // mouse is over the chrome or outside the window.
6616 // Note, currently for backwards compatibility we don't forward mouse events
6617 // to the active document when mouse is over some subdocument.
6618 nsIEventStateManager* activeESM =
6619 nsEventStateManager::GetActiveEventStateManager();
6620 if (activeESM && NS_IS_MOUSE_EVENT(aEvent) &&
6621 activeESM != shell->GetPresContext()->EventStateManager() &&
6622 static_cast<nsEventStateManager*>(activeESM)->GetPresContext()) {
6623 nsIPresShell* activeShell =
6624 static_cast<nsEventStateManager*>(activeESM)->GetPresContext()->GetPresShell();
6625 if (activeShell &&
6626 nsContentUtils::ContentIsCrossDocDescendantOf(activeShell->GetDocument(),
6627 shell->GetDocument())) {
6628 shell = static_cast<PresShell*>(activeShell);
6629 nsIView* activeShellRootView;
6630 shell->GetViewManager()->GetRootView(activeShellRootView);
6631 frame = static_cast<nsIFrame*>(activeShellRootView->GetClientData());
6635 if (shell != this) {
6636 // Handle the event in the correct shell.
6637 // Prevent deletion until we're done with event handling (bug 336582).
6638 nsCOMPtr<nsIPresShell> kungFuDeathGrip(shell);
6639 nsIView* subshellRootView;
6640 shell->GetViewManager()->GetRootView(subshellRootView);
6641 // We pass the subshell's root view as the view to start from. This is
6642 // the only correct alternative; if the event was captured then it
6643 // must have been captured by us or some ancestor shell and we
6644 // now ask the subshell to dispatch it normally.
6645 return shell->HandlePositionedEvent(subshellRootView, frame,
6646 aEvent, aEventStatus);
6649 return HandlePositionedEvent(aView, frame, aEvent, aEventStatus);
6652 nsresult rv = NS_OK;
6654 if (frame) {
6655 PushCurrentEventInfo(nsnull, nsnull);
6657 // key and IME related events go to the focused frame in this DOM window.
6658 if (NS_IsEventTargetedAtFocusedContent(aEvent)) {
6659 NS_ASSERTION(mDocument, "mDocument is null");
6660 nsCOMPtr<nsPIDOMWindow> window =
6661 do_QueryInterface(mDocument->GetWindow());
6662 nsCOMPtr<nsPIDOMWindow> focusedWindow;
6663 mCurrentEventContent =
6664 nsFocusManager::GetFocusedDescendant(window, PR_FALSE,
6665 getter_AddRefs(focusedWindow));
6667 // otherwise, if there is no focused content or the focused content has
6668 // no frame, just use the root content. This ensures that key events
6669 // still get sent to the window properly if nothing is focused or if a
6670 // frame goes away while it is focused.
6671 if (!mCurrentEventContent || !GetCurrentEventFrame())
6672 mCurrentEventContent = mDocument->GetRootElement();
6674 if (aEvent->message == NS_KEY_DOWN) {
6675 NS_IF_RELEASE(gKeyDownTarget);
6676 NS_IF_ADDREF(gKeyDownTarget = mCurrentEventContent);
6678 else if ((aEvent->message == NS_KEY_PRESS || aEvent->message == NS_KEY_UP) &&
6679 gKeyDownTarget) {
6680 // If a different element is now focused for the keypress/keyup event
6681 // than what was focused during the keydown event, check if the new
6682 // focused element is not in a chrome document any more, and if so,
6683 // retarget the event back at the keydown target. This prevents a
6684 // content area from grabbing the focus from chrome in-between key
6685 // events.
6686 if (mCurrentEventContent &&
6687 nsContentUtils::IsChromeDoc(gKeyDownTarget->GetCurrentDoc()) &&
6688 !nsContentUtils::IsChromeDoc(mCurrentEventContent->GetCurrentDoc())) {
6689 mCurrentEventContent = gKeyDownTarget;
6692 if (aEvent->message == NS_KEY_UP) {
6693 NS_RELEASE(gKeyDownTarget);
6697 mCurrentEventFrame = nsnull;
6699 if (!mCurrentEventContent || !GetCurrentEventFrame() ||
6700 InZombieDocument(mCurrentEventContent)) {
6701 rv = RetargetEventToParent(aEvent, aEventStatus);
6702 PopCurrentEventInfo();
6703 return rv;
6705 } else {
6706 mCurrentEventFrame = frame;
6708 if (GetCurrentEventFrame()) {
6709 rv = HandleEventInternal(aEvent, aView, aEventStatus);
6712 #ifdef NS_DEBUG
6713 ShowEventTargetDebug();
6714 #endif
6715 PopCurrentEventInfo();
6716 } else {
6717 // Activation events need to be dispatched even if no frame was found, since
6718 // we don't want the focus to be out of sync.
6720 if (!NS_EVENT_NEEDS_FRAME(aEvent)) {
6721 mCurrentEventFrame = nsnull;
6722 return HandleEventInternal(aEvent, aView, aEventStatus);
6724 else if (NS_IS_KEY_EVENT(aEvent)) {
6725 // Keypress events in new blank tabs should not be completely thrown away.
6726 // Retarget them -- the parent chrome shell might make use of them.
6727 return RetargetEventToParent(aEvent, aEventStatus);
6731 return rv;
6734 #ifdef NS_DEBUG
6735 void
6736 PresShell::ShowEventTargetDebug()
6738 if (nsFrame::GetShowEventTargetFrameBorder() &&
6739 GetCurrentEventFrame()) {
6740 if (mDrawEventTargetFrame) {
6741 mDrawEventTargetFrame->Invalidate(
6742 nsRect(nsPoint(0, 0), mDrawEventTargetFrame->GetSize()));
6745 mDrawEventTargetFrame = mCurrentEventFrame;
6746 mDrawEventTargetFrame->Invalidate(
6747 nsRect(nsPoint(0, 0), mDrawEventTargetFrame->GetSize()));
6750 #endif
6752 nsresult
6753 PresShell::HandlePositionedEvent(nsIView* aView,
6754 nsIFrame* aTargetFrame,
6755 nsGUIEvent* aEvent,
6756 nsEventStatus* aEventStatus)
6758 nsresult rv = NS_OK;
6760 PushCurrentEventInfo(nsnull, nsnull);
6762 mCurrentEventFrame = aTargetFrame;
6764 if (mCurrentEventFrame) {
6765 nsCOMPtr<nsIContent> targetElement;
6766 mCurrentEventFrame->GetContentForEvent(mPresContext, aEvent,
6767 getter_AddRefs(targetElement));
6769 // If there is no content for this frame, target it anyway. Some
6770 // frames can be targeted but do not have content, particularly
6771 // windows with scrolling off.
6772 if (targetElement) {
6773 // Bug 103055, bug 185889: mouse events apply to *elements*, not all
6774 // nodes. Thus we get the nearest element parent here.
6775 // XXX we leave the frame the same even if we find an element
6776 // parent, so that the text frame will receive the event (selection
6777 // and friends are the ones who care about that anyway)
6779 // We use weak pointers because during this tight loop, the node
6780 // will *not* go away. And this happens on every mousemove.
6781 while (targetElement && !targetElement->IsElement()) {
6782 targetElement = targetElement->GetParent();
6785 // If we found an element, target it. Otherwise, target *nothing*.
6786 if (!targetElement) {
6787 mCurrentEventContent = nsnull;
6788 mCurrentEventFrame = nsnull;
6789 } else if (targetElement != mCurrentEventContent) {
6790 mCurrentEventContent = targetElement;
6795 if (GetCurrentEventFrame()) {
6796 rv = HandleEventInternal(aEvent, aView, aEventStatus);
6799 #ifdef NS_DEBUG
6800 ShowEventTargetDebug();
6801 #endif
6802 PopCurrentEventInfo();
6803 return rv;
6806 nsresult
6807 PresShell::HandleEventWithTarget(nsEvent* aEvent, nsIFrame* aFrame,
6808 nsIContent* aContent, nsEventStatus* aStatus)
6810 PushCurrentEventInfo(aFrame, aContent);
6811 nsresult rv = HandleEventInternal(aEvent, nsnull, aStatus);
6812 PopCurrentEventInfo();
6813 return rv;
6816 static inline PRBool
6817 IsSynthesizedMouseEvent(nsEvent* aEvent)
6819 return aEvent->eventStructType == NS_MOUSE_EVENT &&
6820 static_cast<nsMouseEvent*>(aEvent)->reason != nsMouseEvent::eReal;
6823 static PRBool CanHandleContextMenuEvent(nsMouseEvent* aMouseEvent,
6824 nsIFrame* aFrame)
6826 #if defined(XP_MACOSX) && defined(MOZ_XUL)
6827 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
6828 if (pm) {
6829 nsIFrame* popupFrame = pm->GetTopPopup(ePopupTypeMenu);
6830 if (popupFrame) {
6831 // context menus should not be opened while another menu is open on Mac,
6832 // so return false so that the event is not fired.
6833 if (aMouseEvent->context == nsMouseEvent::eContextMenuKey) {
6834 return PR_FALSE;
6835 } else if (aMouseEvent->widget) {
6836 nsWindowType windowType;
6837 aMouseEvent->widget->GetWindowType(windowType);
6838 if (windowType == eWindowType_popup) {
6839 for (nsIFrame* current = aFrame; current;
6840 current = nsLayoutUtils::GetCrossDocParentFrame(current)) {
6841 if (current->GetType() == nsGkAtoms::menuPopupFrame) {
6842 return PR_FALSE;
6849 #endif
6850 return PR_TRUE;
6853 nsresult
6854 PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView,
6855 nsEventStatus* aStatus)
6857 NS_TIME_FUNCTION_MIN(1.0);
6859 #ifdef ACCESSIBILITY
6860 if (aEvent->eventStructType == NS_ACCESSIBLE_EVENT)
6862 nsAccessibleEvent *accEvent = static_cast<nsAccessibleEvent*>(aEvent);
6863 accEvent->mAccessible = nsnull;
6865 nsCOMPtr<nsIAccessibilityService> accService =
6866 do_GetService("@mozilla.org/accessibilityService;1");
6867 if (accService) {
6868 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
6869 if (!container) {
6870 // This presshell is not active. This often happens when a
6871 // preshell is being held onto for fastback.
6872 return NS_OK;
6875 // Accessible creation might be not safe so we make sure it's not created
6876 // at unsafe times.
6877 accEvent->mAccessible =
6878 accService->GetRootDocumentAccessible(this, nsContentUtils::IsSafeToRunScript());
6880 // Ensure this is set in case a11y was activated before any
6881 // nsPresShells existed to observe "a11y-init-or-shutdown" topic
6882 gIsAccessibilityActive = PR_TRUE;
6883 return NS_OK;
6886 #endif
6888 nsCOMPtr<nsIEventStateManager> manager = mPresContext->EventStateManager();
6889 nsresult rv = NS_OK;
6891 if (!NS_EVENT_NEEDS_FRAME(aEvent) || GetCurrentEventFrame()) {
6892 PRBool isHandlingUserInput = PR_FALSE;
6894 if (NS_IS_TRUSTED_EVENT(aEvent)) {
6895 switch (aEvent->message) {
6896 case NS_MOUSE_BUTTON_DOWN:
6897 case NS_MOUSE_BUTTON_UP:
6898 case NS_KEY_PRESS:
6899 case NS_KEY_DOWN:
6900 case NS_KEY_UP:
6901 isHandlingUserInput = PR_TRUE;
6902 break;
6903 case NS_DRAGDROP_DROP:
6904 nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
6905 if (session) {
6906 PRBool onlyChromeDrop = PR_FALSE;
6907 session->GetOnlyChromeDrop(&onlyChromeDrop);
6908 if (onlyChromeDrop) {
6909 aEvent->flags |= NS_EVENT_FLAG_ONLY_CHROME_DISPATCH;
6912 break;
6916 if (aEvent->message == NS_CONTEXTMENU) {
6917 nsMouseEvent* me = static_cast<nsMouseEvent*>(aEvent);
6918 if (!CanHandleContextMenuEvent(me, GetCurrentEventFrame())) {
6919 return NS_OK;
6921 if (me->context == nsMouseEvent::eContextMenuKey &&
6922 !AdjustContextMenuKeyEvent(me)) {
6923 return NS_OK;
6927 nsAutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput,
6928 aEvent, mDocument);
6930 if (NS_IS_TRUSTED_EVENT(aEvent) && aEvent->message == NS_MOUSE_MOVE) {
6931 nsIPresShell::AllowMouseCapture(
6932 nsEventStateManager::GetActiveEventStateManager() == manager);
6935 nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent));
6937 // FIXME. If the event was reused, we need to clear the old target,
6938 // bug 329430
6939 aEvent->target = nsnull;
6941 nsWeakView weakView(aView);
6942 // 1. Give event to event manager for pre event state changes and
6943 // generation of synthetic events.
6944 rv = manager->PreHandleEvent(mPresContext, aEvent, mCurrentEventFrame,
6945 aStatus, aView);
6947 // 2. Give event to the DOM for third party and JS use.
6948 if (GetCurrentEventFrame() && NS_SUCCEEDED(rv)) {
6949 PRBool wasHandlingKeyBoardEvent =
6950 nsContentUtils::IsHandlingKeyBoardEvent();
6951 if (aEvent->eventStructType == NS_KEY_EVENT) {
6952 nsContentUtils::SetIsHandlingKeyBoardEvent(PR_TRUE);
6954 // We want synthesized mouse moves to cause mouseover and mouseout
6955 // DOM events (PreHandleEvent above), but not mousemove DOM events.
6956 // Synthesized button up events also do not cause DOM events
6957 // because they do not have a reliable refPoint.
6958 if (!IsSynthesizedMouseEvent(aEvent)) {
6959 nsPresShellEventCB eventCB(this);
6960 if (mCurrentEventContent) {
6961 nsEventDispatcher::Dispatch(mCurrentEventContent, mPresContext,
6962 aEvent, nsnull, aStatus, &eventCB);
6964 else {
6965 nsCOMPtr<nsIContent> targetContent;
6966 rv = mCurrentEventFrame->GetContentForEvent(mPresContext, aEvent,
6967 getter_AddRefs(targetContent));
6968 if (NS_SUCCEEDED(rv) && targetContent) {
6969 nsEventDispatcher::Dispatch(targetContent, mPresContext, aEvent,
6970 nsnull, aStatus, &eventCB);
6971 } else if (mDocument) {
6972 nsEventDispatcher::Dispatch(mDocument, mPresContext, aEvent,
6973 nsnull, aStatus, nsnull);
6978 nsContentUtils::SetIsHandlingKeyBoardEvent(wasHandlingKeyBoardEvent);
6980 // 3. Give event to event manager for post event state changes and
6981 // generation of synthetic events.
6982 if (!mIsDestroying && NS_SUCCEEDED(rv)) {
6983 rv = manager->PostHandleEvent(mPresContext, aEvent,
6984 GetCurrentEventFrame(), aStatus,
6985 weakView.GetView());
6989 if (aEvent->message == NS_MOUSE_BUTTON_UP) {
6990 // reset the capturing content now that the mouse button is up
6991 SetCapturingContent(nsnull, 0);
6992 } else if (aEvent->message == NS_MOUSE_MOVE) {
6993 nsIPresShell::AllowMouseCapture(PR_FALSE);
6996 return rv;
6999 // Dispatch event to content only (NOT full processing)
7000 // See also HandleEventWithTarget which does full event processing.
7001 nsresult
7002 PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent, nsEvent* aEvent,
7003 nsEventStatus* aStatus)
7005 nsresult rv = NS_OK;
7007 PushCurrentEventInfo(nsnull, aTargetContent);
7009 // Bug 41013: Check if the event should be dispatched to content.
7010 // It's possible that we are in the middle of destroying the window
7011 // and the js context is out of date. This check detects the case
7012 // that caused a crash in bug 41013, but there may be a better way
7013 // to handle this situation!
7014 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
7015 if (container) {
7017 // Dispatch event to content
7018 rv = nsEventDispatcher::Dispatch(aTargetContent, mPresContext, aEvent, nsnull,
7019 aStatus);
7022 PopCurrentEventInfo();
7023 return rv;
7026 // See the method above.
7027 nsresult
7028 PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent,
7029 nsIDOMEvent* aEvent,
7030 nsEventStatus* aStatus)
7032 nsresult rv = NS_OK;
7034 PushCurrentEventInfo(nsnull, aTargetContent);
7035 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
7036 if (container) {
7037 rv = nsEventDispatcher::DispatchDOMEvent(aTargetContent, nsnull, aEvent,
7038 mPresContext, aStatus);
7041 PopCurrentEventInfo();
7042 return rv;
7045 PRBool
7046 PresShell::AdjustContextMenuKeyEvent(nsMouseEvent* aEvent)
7048 #ifdef MOZ_XUL
7049 // if a menu is open, open the context menu relative to the active item on the menu.
7050 nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
7051 if (pm) {
7052 nsIFrame* popupFrame = pm->GetTopPopup(ePopupTypeMenu);
7053 if (popupFrame) {
7054 nsIFrame* itemFrame =
7055 (static_cast<nsMenuPopupFrame *>(popupFrame))->GetCurrentMenuItem();
7056 if (!itemFrame)
7057 itemFrame = popupFrame;
7059 nsCOMPtr<nsIWidget> widget = popupFrame->GetNearestWidget();
7060 aEvent->widget = widget;
7061 nsIntPoint widgetPoint = widget->WidgetToScreenOffset();
7062 aEvent->refPoint = itemFrame->GetScreenRect().BottomLeft() - widgetPoint;
7064 mCurrentEventContent = itemFrame->GetContent();
7065 mCurrentEventFrame = itemFrame;
7067 return PR_TRUE;
7070 #endif
7072 // If we're here because of the key-equiv for showing context menus, we
7073 // have to twiddle with the NS event to make sure the context menu comes
7074 // up in the upper left of the relevant content area before we create
7075 // the DOM event. Since we never call InitMouseEvent() on the event,
7076 // the client X/Y will be 0,0. We can make use of that if the widget is null.
7077 // Use the root view manager's widget since it's most likely to have one,
7078 // and the coordinates returned by GetCurrentItemAndPositionForElement
7079 // are relative to the widget of the root of the root view manager.
7080 nsRootPresContext* rootPC = mPresContext->GetRootPresContext();
7081 aEvent->refPoint.x = 0;
7082 aEvent->refPoint.y = 0;
7083 if (rootPC) {
7084 rootPC->PresShell()->GetViewManager()->
7085 GetRootWidget(getter_AddRefs(aEvent->widget));
7087 if (aEvent->widget) {
7088 // default the refpoint to the topleft of our document
7089 nsPoint offset(0, 0);
7090 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
7091 if (rootFrame) {
7092 nsIView* view = rootFrame->GetClosestView(&offset);
7093 offset += view->GetOffsetToWidget(aEvent->widget);
7094 aEvent->refPoint =
7095 offset.ToNearestPixels(mPresContext->AppUnitsPerDevPixel());
7098 } else {
7099 aEvent->widget = nsnull;
7102 // see if we should use the caret position for the popup
7103 nsIntPoint caretPoint;
7104 // Beware! This may flush notifications via synchronous
7105 // ScrollSelectionIntoView.
7106 if (PrepareToUseCaretPosition(aEvent->widget, caretPoint)) {
7107 // caret position is good
7108 aEvent->refPoint = caretPoint;
7109 return PR_TRUE;
7112 // If we're here because of the key-equiv for showing context menus, we
7113 // have to reset the event target to the currently focused element. Get it
7114 // from the focus controller.
7115 nsCOMPtr<nsIDOMElement> currentFocus;
7116 nsIFocusManager* fm = nsFocusManager::GetFocusManager();
7117 if (fm)
7118 fm->GetFocusedElement(getter_AddRefs(currentFocus));
7120 // Reset event coordinates relative to focused frame in view
7121 if (currentFocus) {
7122 nsCOMPtr<nsIContent> currentPointElement;
7123 GetCurrentItemAndPositionForElement(currentFocus,
7124 getter_AddRefs(currentPointElement),
7125 aEvent->refPoint,
7126 aEvent->widget);
7127 if (currentPointElement) {
7128 mCurrentEventContent = currentPointElement;
7129 mCurrentEventFrame = nsnull;
7130 GetCurrentEventFrame();
7134 return PR_TRUE;
7137 // PresShell::PrepareToUseCaretPosition
7139 // This checks to see if we should use the caret position for popup context
7140 // menus. Returns true if the caret position should be used, and the
7141 // coordinates of that position is returned in aTargetPt. This function
7142 // will also scroll the window as needed to make the caret visible.
7144 // The event widget should be the widget that generated the event, and
7145 // whose coordinate system the resulting event's refPoint should be
7146 // relative to. The returned point is in device pixels realtive to the
7147 // widget passed in.
7148 PRBool
7149 PresShell::PrepareToUseCaretPosition(nsIWidget* aEventWidget, nsIntPoint& aTargetPt)
7151 nsresult rv;
7153 // check caret visibility
7154 nsRefPtr<nsCaret> caret = GetCaret();
7155 NS_ENSURE_TRUE(caret, PR_FALSE);
7157 PRBool caretVisible = PR_FALSE;
7158 rv = caret->GetCaretVisible(&caretVisible);
7159 if (NS_FAILED(rv) || ! caretVisible)
7160 return PR_FALSE;
7162 // caret selection, this is a temporary weak reference, so no refcounting is
7163 // needed
7164 nsISelection* domSelection = caret->GetCaretDOMSelection();
7165 NS_ENSURE_TRUE(domSelection, PR_FALSE);
7167 // since the match could be an anonymous textnode inside a
7168 // <textarea> or text <input>, we need to get the outer frame
7169 // note: frames are not refcounted
7170 nsIFrame* frame = nsnull; // may be NULL
7171 nsCOMPtr<nsIDOMNode> node;
7172 rv = domSelection->GetFocusNode(getter_AddRefs(node));
7173 NS_ENSURE_SUCCESS(rv, PR_FALSE);
7174 NS_ENSURE_TRUE(node, PR_FALSE);
7175 nsCOMPtr<nsIContent> content(do_QueryInterface(node));
7176 if (content) {
7177 nsIContent* nonNative = content->FindFirstNonNativeAnonymous();
7178 content = nonNative;
7181 if (content) {
7182 // It seems like ScrollSelectionIntoView should be enough, but it's
7183 // not. The problem is that scrolling the selection into view when it is
7184 // below the current viewport will align the top line of the frame exactly
7185 // with the bottom of the window. This is fine, BUT, the popup event causes
7186 // the control to be re-focused which does this exact call to
7187 // ScrollContentIntoView, which has a one-pixel disagreement of whether the
7188 // frame is actually in view. The result is that the frame is aligned with
7189 // the top of the window, but the menu is still at the bottom.
7191 // Doing this call first forces the frame to be in view, eliminating the
7192 // problem. The only difference in the result is that if your cursor is in
7193 // an edit box below the current view, you'll get the edit box aligned with
7194 // the top of the window. This is arguably better behavior anyway.
7195 rv = ScrollContentIntoView(content, NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
7196 NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
7197 SCROLL_OVERFLOW_HIDDEN);
7198 NS_ENSURE_SUCCESS(rv, PR_FALSE);
7199 frame = content->GetPrimaryFrame();
7200 NS_WARN_IF_FALSE(frame, "No frame for focused content?");
7203 // Actually scroll the selection (ie caret) into view. Note that this must
7204 // be synchronous since we will be checking the caret position on the screen.
7206 // Be easy about errors, and just don't scroll in those cases. Better to have
7207 // the correct menu at a weird place than the wrong menu.
7208 // After ScrollSelectionIntoView(), the pending notifications might be
7209 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
7210 nsCOMPtr<nsISelectionController> selCon;
7211 if (frame)
7212 frame->GetSelectionController(GetPresContext(), getter_AddRefs(selCon));
7213 else
7214 selCon = static_cast<nsISelectionController *>(this);
7215 if (selCon) {
7216 rv = selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
7217 nsISelectionController::SELECTION_FOCUS_REGION,
7218 nsISelectionController::SCROLL_SYNCHRONOUS);
7219 NS_ENSURE_SUCCESS(rv, PR_FALSE);
7222 nsPresContext* presContext = GetPresContext();
7224 // get caret position relative to the closest view
7225 nsRect caretCoords;
7226 nsIFrame* caretFrame = caret->GetGeometry(domSelection, &caretCoords);
7227 if (!caretFrame)
7228 return PR_FALSE;
7229 nsPoint viewOffset;
7230 nsIView* view = caretFrame->GetClosestView(&viewOffset);
7231 if (!view)
7232 return PR_FALSE;
7233 // and then get the caret coords relative to the event widget
7234 if (aEventWidget) {
7235 viewOffset += view->GetOffsetToWidget(aEventWidget);
7237 caretCoords.MoveBy(viewOffset);
7239 // caret coordinates are in app units, convert to pixels
7240 aTargetPt.x =
7241 presContext->AppUnitsToDevPixels(caretCoords.x + caretCoords.width);
7242 aTargetPt.y =
7243 presContext->AppUnitsToDevPixels(caretCoords.y + caretCoords.height);
7245 // make sure rounding doesn't return a pixel which is outside the caret
7246 // (e.g. one line lower)
7247 aTargetPt.y -= 1;
7249 return PR_TRUE;
7252 void
7253 PresShell::GetCurrentItemAndPositionForElement(nsIDOMElement *aCurrentEl,
7254 nsIContent** aTargetToUse,
7255 nsIntPoint& aTargetPt,
7256 nsIWidget *aRootWidget)
7258 nsCOMPtr<nsIContent> focusedContent(do_QueryInterface(aCurrentEl));
7259 ScrollContentIntoView(focusedContent, NS_PRESSHELL_SCROLL_ANYWHERE,
7260 NS_PRESSHELL_SCROLL_ANYWHERE,
7261 SCROLL_OVERFLOW_HIDDEN);
7263 nsPresContext* presContext = GetPresContext();
7265 PRBool istree = PR_FALSE, checkLineHeight = PR_TRUE;
7266 nscoord extraTreeY = 0;
7268 #ifdef MOZ_XUL
7269 // Set the position to just underneath the current item for multi-select
7270 // lists or just underneath the selected item for single-select lists. If
7271 // the element is not a list, or there is no selection, leave the position
7272 // as is.
7273 nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
7274 nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
7275 do_QueryInterface(aCurrentEl);
7276 if (multiSelect) {
7277 checkLineHeight = PR_FALSE;
7279 PRInt32 currentIndex;
7280 multiSelect->GetCurrentIndex(&currentIndex);
7281 if (currentIndex >= 0) {
7282 nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aCurrentEl));
7283 if (xulElement) {
7284 nsCOMPtr<nsIBoxObject> box;
7285 xulElement->GetBoxObject(getter_AddRefs(box));
7286 nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
7287 // Tree view special case (tree items have no frames)
7288 // Get the focused row and add its coordinates, which are already in pixels
7289 // XXX Boris, should we create a new interface so that this doesn't
7290 // need to know about trees? Something like nsINodelessChildCreator which
7291 // could provide the current focus coordinates?
7292 if (treeBox) {
7293 treeBox->EnsureRowIsVisible(currentIndex);
7294 PRInt32 firstVisibleRow, rowHeight;
7295 treeBox->GetFirstVisibleRow(&firstVisibleRow);
7296 treeBox->GetRowHeight(&rowHeight);
7298 extraTreeY += presContext->CSSPixelsToAppUnits(
7299 (currentIndex - firstVisibleRow + 1) * rowHeight);
7300 istree = PR_TRUE;
7302 nsCOMPtr<nsITreeColumns> cols;
7303 treeBox->GetColumns(getter_AddRefs(cols));
7304 if (cols) {
7305 nsCOMPtr<nsITreeColumn> col;
7306 cols->GetFirstColumn(getter_AddRefs(col));
7307 if (col) {
7308 nsCOMPtr<nsIDOMElement> colElement;
7309 col->GetElement(getter_AddRefs(colElement));
7310 nsCOMPtr<nsIContent> colContent(do_QueryInterface(colElement));
7311 if (colContent) {
7312 nsIFrame* frame = colContent->GetPrimaryFrame();
7313 if (frame) {
7314 extraTreeY += frame->GetSize().height;
7320 else {
7321 multiSelect->GetCurrentItem(getter_AddRefs(item));
7326 else {
7327 // don't check menulists as the selected item will be inside a popup.
7328 nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aCurrentEl);
7329 if (!menulist) {
7330 nsCOMPtr<nsIDOMXULSelectControlElement> select =
7331 do_QueryInterface(aCurrentEl);
7332 if (select) {
7333 checkLineHeight = PR_FALSE;
7334 select->GetSelectedItem(getter_AddRefs(item));
7339 if (item)
7340 focusedContent = do_QueryInterface(item);
7341 #endif
7343 nsIFrame *frame = focusedContent->GetPrimaryFrame();
7344 if (frame) {
7345 NS_ASSERTION(frame->PresContext() == GetPresContext(),
7346 "handling event for focused content that is not in our document?");
7348 nsPoint frameOrigin(0, 0);
7350 // Get the frame's origin within its view
7351 nsIView *view = frame->GetClosestView(&frameOrigin);
7352 NS_ASSERTION(view, "No view for frame");
7354 // View's origin relative the widget
7355 if (aRootWidget) {
7356 frameOrigin += view->GetOffsetToWidget(aRootWidget);
7359 // Start context menu down and to the right from top left of frame
7360 // use the lineheight. This is a good distance to move the context
7361 // menu away from the top left corner of the frame. If we always
7362 // used the frame height, the context menu could end up far away,
7363 // for example when we're focused on linked images.
7364 // On the other hand, we want to use the frame height if it's less
7365 // than the current line height, so that the context menu appears
7366 // associated with the correct frame.
7367 nscoord extra = 0;
7368 if (!istree) {
7369 extra = frame->GetSize().height;
7370 if (checkLineHeight) {
7371 nsIScrollableFrame *scrollFrame =
7372 nsLayoutUtils::GetNearestScrollableFrame(frame);
7373 if (scrollFrame) {
7374 nsSize scrollAmount = scrollFrame->GetLineScrollAmount();
7375 nsIFrame* f = do_QueryFrame(scrollFrame);
7376 PRInt32 APD = presContext->AppUnitsPerDevPixel();
7377 PRInt32 scrollAPD = f->PresContext()->AppUnitsPerDevPixel();
7378 scrollAmount = scrollAmount.ConvertAppUnits(scrollAPD, APD);
7379 if (extra > scrollAmount.height) {
7380 extra = scrollAmount.height;
7386 aTargetPt.x = presContext->AppUnitsToDevPixels(frameOrigin.x);
7387 aTargetPt.y = presContext->AppUnitsToDevPixels(
7388 frameOrigin.y + extra + extraTreeY);
7391 NS_IF_ADDREF(*aTargetToUse = focusedContent);
7394 NS_IMETHODIMP
7395 PresShell::ResizeReflow(nsIView *aView, nscoord aWidth, nscoord aHeight)
7397 return ResizeReflow(aWidth, aHeight);
7400 NS_IMETHODIMP_(PRBool)
7401 PresShell::ShouldIgnoreInvalidation()
7403 return mPaintingSuppressed || !mIsActive;
7406 NS_IMETHODIMP_(void)
7407 PresShell::WillPaint(PRBool aWillSendDidPaint)
7409 // Don't bother doing anything if some viewmanager in our tree is painting
7410 // while we still have painting suppressed or we are not active.
7411 if (mPaintingSuppressed || !mIsActive) {
7412 return;
7415 if (!aWillSendDidPaint) {
7416 nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
7417 if (!rootPresContext) {
7418 return;
7420 if (rootPresContext == mPresContext) {
7421 rootPresContext->UpdatePluginGeometry();
7425 // Process reflows, if we have them, to reduce flicker due to invalidates and
7426 // reflow being interspersed. Note that we _do_ allow this to be
7427 // interruptible; if we can't do all the reflows it's better to flicker a bit
7428 // than to freeze up.
7429 FlushPendingNotifications(Flush_InterruptibleLayout);
7432 NS_IMETHODIMP_(void)
7433 PresShell::DidPaint()
7435 nsRootPresContext* rootPresContext = mPresContext->GetRootPresContext();
7436 if (!rootPresContext) {
7437 return;
7439 if (rootPresContext == mPresContext) {
7440 rootPresContext->UpdatePluginGeometry();
7444 nsresult
7445 PresShell::GetAgentStyleSheets(nsCOMArray<nsIStyleSheet>& aSheets)
7447 aSheets.Clear();
7448 PRInt32 sheetCount = mStyleSet->SheetCount(nsStyleSet::eAgentSheet);
7450 for (PRInt32 i = 0; i < sheetCount; ++i) {
7451 nsIStyleSheet *sheet = mStyleSet->StyleSheetAt(nsStyleSet::eAgentSheet, i);
7452 if (!aSheets.AppendObject(sheet))
7453 return NS_ERROR_OUT_OF_MEMORY;
7456 return NS_OK;
7459 nsresult
7460 PresShell::SetAgentStyleSheets(const nsCOMArray<nsIStyleSheet>& aSheets)
7462 return mStyleSet->ReplaceSheets(nsStyleSet::eAgentSheet, aSheets);
7465 nsresult
7466 PresShell::AddOverrideStyleSheet(nsIStyleSheet *aSheet)
7468 return mStyleSet->PrependStyleSheet(nsStyleSet::eOverrideSheet, aSheet);
7471 nsresult
7472 PresShell::RemoveOverrideStyleSheet(nsIStyleSheet *aSheet)
7474 return mStyleSet->RemoveStyleSheet(nsStyleSet::eOverrideSheet, aSheet);
7477 static void
7478 FreezeElement(nsIContent *aContent, void * /* unused */)
7480 nsIFrame *frame = aContent->GetPrimaryFrame();
7481 nsIObjectFrame *objectFrame = do_QueryFrame(frame);
7482 if (objectFrame) {
7483 objectFrame->StopPlugin();
7487 static PRBool
7488 FreezeSubDocument(nsIDocument *aDocument, void *aData)
7490 nsIPresShell *shell = aDocument->GetShell();
7491 if (shell)
7492 shell->Freeze();
7494 return PR_TRUE;
7497 void
7498 PresShell::Freeze()
7500 MaybeReleaseCapturingContent();
7502 mDocument->EnumerateFreezableElements(FreezeElement, nsnull);
7504 if (mCaret)
7505 mCaret->SetCaretVisible(PR_FALSE);
7507 mPaintingSuppressed = PR_TRUE;
7509 if (mDocument)
7510 mDocument->EnumerateSubDocuments(FreezeSubDocument, nsnull);
7512 nsPresContext* presContext = GetPresContext();
7513 if (presContext &&
7514 presContext->RefreshDriver()->PresContext() == presContext) {
7515 presContext->RefreshDriver()->Freeze();
7518 mFrozen = PR_TRUE;
7519 UpdateImageLockingState();
7522 void
7523 PresShell::FireOrClearDelayedEvents(PRBool aFireEvents)
7525 mNoDelayedMouseEvents = PR_FALSE;
7526 mNoDelayedKeyEvents = PR_FALSE;
7527 if (!aFireEvents) {
7528 mDelayedEvents.Clear();
7529 return;
7532 if (mDocument) {
7533 nsCOMPtr<nsIDocument> doc = mDocument;
7534 while (!mIsDestroying && mDelayedEvents.Length() &&
7535 !doc->EventHandlingSuppressed()) {
7536 nsAutoPtr<nsDelayedEvent> ev(mDelayedEvents[0].forget());
7537 mDelayedEvents.RemoveElementAt(0);
7538 ev->Dispatch(this);
7540 if (!doc->EventHandlingSuppressed()) {
7541 mDelayedEvents.Clear();
7546 static void
7547 ThawElement(nsIContent *aContent, void *aShell)
7549 nsCOMPtr<nsIObjectLoadingContent> objlc(do_QueryInterface(aContent));
7550 if (objlc) {
7551 nsCOMPtr<nsIPluginInstance> inst;
7552 objlc->EnsureInstantiation(getter_AddRefs(inst));
7556 static PRBool
7557 ThawSubDocument(nsIDocument *aDocument, void *aData)
7559 nsIPresShell *shell = aDocument->GetShell();
7560 if (shell)
7561 shell->Thaw();
7563 return PR_TRUE;
7566 void
7567 PresShell::Thaw()
7569 nsPresContext* presContext = GetPresContext();
7570 if (presContext &&
7571 presContext->RefreshDriver()->PresContext() == presContext) {
7572 presContext->RefreshDriver()->Thaw();
7575 mDocument->EnumerateFreezableElements(ThawElement, this);
7577 if (mDocument)
7578 mDocument->EnumerateSubDocuments(ThawSubDocument, nsnull);
7580 // Get the activeness of our presshell, as this might have changed
7581 // while we were in the bfcache
7582 QueryIsActive();
7584 // We're now unfrozen
7585 mFrozen = PR_FALSE;
7586 UpdateImageLockingState();
7588 UnsuppressPainting();
7591 //--------------------------------------------------------
7592 // Start of protected and private methods on the PresShell
7593 //--------------------------------------------------------
7595 void
7596 PresShell::MaybeScheduleReflow()
7598 ASSERT_REFLOW_SCHEDULED_STATE();
7599 if (mReflowScheduled || mIsDestroying || mIsReflowing ||
7600 mDirtyRoots.Length() == 0)
7601 return;
7603 if (!mPresContext->HasPendingInterrupt() || !ScheduleReflowOffTimer()) {
7604 ScheduleReflow();
7607 ASSERT_REFLOW_SCHEDULED_STATE();
7610 void
7611 PresShell::ScheduleReflow()
7613 NS_PRECONDITION(!mReflowScheduled, "Why are we trying to schedule a reflow?");
7614 ASSERT_REFLOW_SCHEDULED_STATE();
7616 if (GetPresContext()->RefreshDriver()->AddLayoutFlushObserver(this)) {
7617 mReflowScheduled = PR_TRUE;
7620 ASSERT_REFLOW_SCHEDULED_STATE();
7623 nsresult
7624 PresShell::DidCauseReflow()
7626 NS_ASSERTION(mChangeNestCount != 0, "Unexpected call to DidCauseReflow()");
7627 --mChangeNestCount;
7628 nsContentUtils::RemoveScriptBlocker();
7630 return NS_OK;
7633 void
7634 PresShell::WillDoReflow()
7636 // We just reflowed, tell the caret that its frame might have moved.
7637 // XXXbz that comment makes no sense
7638 if (mCaret) {
7639 mCaret->InvalidateOutsideCaret();
7642 mPresContext->FlushUserFontSet();
7644 mFrameConstructor->BeginUpdate();
7647 void
7648 PresShell::DidDoReflow(PRBool aInterruptible)
7650 mFrameConstructor->EndUpdate();
7652 HandlePostedReflowCallbacks(aInterruptible);
7653 SynthesizeMouseMove(PR_FALSE);
7654 if (mCaret) {
7655 // Update the caret's position now to account for any changes created by
7656 // the reflow.
7657 mCaret->InvalidateOutsideCaret();
7658 mCaret->UpdateCaretPosition();
7662 static PLDHashOperator
7663 MarkFramesDirtyToRoot(nsPtrHashKey<nsIFrame>* p, void* closure)
7665 nsIFrame* target = static_cast<nsIFrame*>(closure);
7666 for (nsIFrame* f = p->GetKey(); f && !NS_SUBTREE_DIRTY(f);
7667 f = f->GetParent()) {
7668 f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
7670 if (f == target) {
7671 break;
7675 return PL_DHASH_NEXT;
7678 void
7679 PresShell::sReflowContinueCallback(nsITimer* aTimer, void* aPresShell)
7681 nsRefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
7683 NS_PRECONDITION(aTimer == self->mReflowContinueTimer, "Unexpected timer");
7684 self->mReflowContinueTimer = nsnull;
7685 self->ScheduleReflow();
7688 PRBool
7689 PresShell::ScheduleReflowOffTimer()
7691 NS_PRECONDITION(!mReflowScheduled, "Shouldn't get here");
7692 ASSERT_REFLOW_SCHEDULED_STATE();
7694 if (!mReflowContinueTimer) {
7695 mReflowContinueTimer = do_CreateInstance("@mozilla.org/timer;1");
7696 if (!mReflowContinueTimer ||
7697 NS_FAILED(mReflowContinueTimer->
7698 InitWithFuncCallback(sReflowContinueCallback, this, 30,
7699 nsITimer::TYPE_ONE_SHOT))) {
7700 return PR_FALSE;
7703 return PR_TRUE;
7706 PRBool
7707 PresShell::DoReflow(nsIFrame* target, PRBool aInterruptible)
7709 NS_TIME_FUNCTION_WITH_DOCURL;
7711 if (mReflowContinueTimer) {
7712 mReflowContinueTimer->Cancel();
7713 mReflowContinueTimer = nsnull;
7716 nsIFrame* rootFrame = FrameManager()->GetRootFrame();
7718 nsCOMPtr<nsIRenderingContext> rcx = GetReferenceRenderingContext();
7719 if (!rcx) {
7720 NS_NOTREACHED("CreateRenderingContext failure");
7721 return PR_FALSE;
7724 #ifdef DEBUG
7725 mCurrentReflowRoot = target;
7726 #endif
7728 target->WillReflow(mPresContext);
7730 // If the target frame is the root of the frame hierarchy, then
7731 // use all the available space. If it's simply a `reflow root',
7732 // then use the target frame's size as the available space.
7733 nsSize size;
7734 if (target == rootFrame) {
7735 size = mPresContext->GetVisibleArea().Size();
7737 // target->GetRect() has the old size of the frame,
7738 // mPresContext->GetVisibleArea() has the new size.
7739 target->InvalidateRectDifference(mPresContext->GetVisibleArea(),
7740 target->GetRect());
7741 } else {
7742 size = target->GetSize();
7745 NS_ASSERTION(!target->GetNextInFlow() && !target->GetPrevInFlow(),
7746 "reflow roots should never split");
7748 // Don't pass size directly to the reflow state, since a
7749 // constrained height implies page/column breaking.
7750 nsSize reflowSize(size.width, NS_UNCONSTRAINEDSIZE);
7751 nsHTMLReflowState reflowState(mPresContext, target, rcx, reflowSize);
7753 if (rootFrame == target) {
7754 // When the root frame is being reflowed with unconstrained height
7755 // (which happens when we're called from
7756 // DocumentViewerImpl::SizeToContent), we're effectively doing a
7757 // vertical resize, since it changes the meaning of percentage
7758 // heights even if no heights actually changed. The same applies
7759 // when we reflow again after that computation. This is an unusual
7760 // case, and isn't caught by nsHTMLReflowState::InitResizeFlags.
7761 PRBool hasUnconstrainedHeight = size.height == NS_UNCONSTRAINEDSIZE;
7763 if (hasUnconstrainedHeight || mLastRootReflowHadUnconstrainedHeight) {
7764 reflowState.mFlags.mVResize = PR_TRUE;
7767 mLastRootReflowHadUnconstrainedHeight = hasUnconstrainedHeight;
7770 // fix the computed height
7771 NS_ASSERTION(reflowState.mComputedMargin == nsMargin(0, 0, 0, 0),
7772 "reflow state should not set margin for reflow roots");
7773 if (size.height != NS_UNCONSTRAINEDSIZE) {
7774 nscoord computedHeight =
7775 size.height - reflowState.mComputedBorderPadding.TopBottom();
7776 computedHeight = NS_MAX(computedHeight, 0);
7777 reflowState.SetComputedHeight(computedHeight);
7779 NS_ASSERTION(reflowState.ComputedWidth() ==
7780 size.width -
7781 reflowState.mComputedBorderPadding.LeftRight(),
7782 "reflow state computed incorrect width");
7784 mPresContext->ReflowStarted(aInterruptible);
7785 mIsReflowing = PR_TRUE;
7787 nsReflowStatus status;
7788 nsHTMLReflowMetrics desiredSize;
7789 target->Reflow(mPresContext, desiredSize, reflowState, status);
7791 // If an incremental reflow is initiated at a frame other than the
7792 // root frame, then its desired size had better not change! If it's
7793 // initiated at the root, then the size better not change unless its
7794 // height was unconstrained to start with.
7795 NS_ASSERTION((target == rootFrame && size.height == NS_UNCONSTRAINEDSIZE) ||
7796 (desiredSize.width == size.width &&
7797 desiredSize.height == size.height),
7798 "non-root frame's desired size changed during an "
7799 "incremental reflow");
7800 NS_ASSERTION(desiredSize.VisualOverflow() ==
7801 nsRect(nsPoint(0, 0),
7802 nsSize(desiredSize.width, desiredSize.height)),
7803 "reflow roots must not have visible overflow");
7804 NS_ASSERTION(desiredSize.ScrollableOverflow() ==
7805 nsRect(nsPoint(0, 0),
7806 nsSize(desiredSize.width, desiredSize.height)),
7807 "reflow roots must not have scrollable overflow");
7808 NS_ASSERTION(status == NS_FRAME_COMPLETE,
7809 "reflow roots should never split");
7811 target->SetSize(nsSize(desiredSize.width, desiredSize.height));
7813 nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, target,
7814 target->GetView(),
7815 desiredSize.VisualOverflow());
7816 nsContainerFrame::SyncWindowProperties(mPresContext, target,
7817 target->GetView());
7819 target->DidReflow(mPresContext, nsnull, NS_FRAME_REFLOW_FINISHED);
7820 if (target == rootFrame && size.height == NS_UNCONSTRAINEDSIZE) {
7821 mPresContext->SetVisibleArea(nsRect(0, 0, desiredSize.width,
7822 desiredSize.height));
7825 #ifdef DEBUG
7826 mCurrentReflowRoot = nsnull;
7827 #endif
7829 NS_ASSERTION(mPresContext->HasPendingInterrupt() ||
7830 mFramesToDirty.Count() == 0,
7831 "Why do we need to dirty anything if not interrupted?");
7833 mIsReflowing = PR_FALSE;
7834 PRBool interrupted = mPresContext->HasPendingInterrupt();
7835 if (interrupted) {
7836 // Make sure target gets reflowed again.
7837 mFramesToDirty.EnumerateEntries(&MarkFramesDirtyToRoot, target);
7838 NS_ASSERTION(NS_SUBTREE_DIRTY(target), "Why is the target not dirty?");
7839 mDirtyRoots.AppendElement(target);
7841 // Clear mFramesToDirty after we've done the NS_SUBTREE_DIRTY(target)
7842 // assertion so that if it fails it's easier to see what's going on.
7843 #ifdef NOISY_INTERRUPTIBLE_REFLOW
7844 printf("mFramesToDirty.Count() == %u\n", mFramesToDirty.Count());
7845 #endif /* NOISY_INTERRUPTIBLE_REFLOW */
7846 mFramesToDirty.Clear();
7848 // Any FlushPendingNotifications with interruptible reflows
7849 // should be suppressed now. We don't want to do extra reflow work
7850 // before our reflow event happens.
7851 mSuppressInterruptibleReflows = PR_TRUE;
7852 MaybeScheduleReflow();
7855 nsRootPresContext* rootPC = mPresContext->GetRootPresContext();
7856 if (rootPC) {
7857 rootPC->RequestUpdatePluginGeometry(target);
7860 return !interrupted;
7863 #ifdef DEBUG
7864 void
7865 PresShell::DoVerifyReflow()
7867 if (GetVerifyReflowEnable()) {
7868 // First synchronously render what we have so far so that we can
7869 // see it.
7870 nsIView* rootView;
7871 mViewManager->GetRootView(rootView);
7872 mViewManager->UpdateView(rootView, NS_VMREFRESH_IMMEDIATE);
7874 FlushPendingNotifications(Flush_Layout);
7875 mInVerifyReflow = PR_TRUE;
7876 PRBool ok = VerifyIncrementalReflow();
7877 mInVerifyReflow = PR_FALSE;
7878 if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) {
7879 printf("ProcessReflowCommands: finished (%s)\n",
7880 ok ? "ok" : "failed");
7883 if (0 != mDirtyRoots.Length()) {
7884 printf("XXX yikes! reflow commands queued during verify-reflow\n");
7888 #endif
7890 PRBool
7891 PresShell::ProcessReflowCommands(PRBool aInterruptible)
7893 NS_TIME_FUNCTION_WITH_DOCURL;
7895 PRBool interrupted = PR_FALSE;
7896 if (0 != mDirtyRoots.Length()) {
7898 #ifdef DEBUG
7899 if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
7900 printf("ProcessReflowCommands: begin incremental reflow\n");
7902 #endif
7904 // If reflow is interruptible, then make a note of our deadline.
7905 const PRIntervalTime deadline = aInterruptible
7906 ? PR_IntervalNow() + PR_MicrosecondsToInterval(gMaxRCProcessingTime)
7907 : (PRIntervalTime)0;
7909 // Scope for the reflow entry point
7911 nsAutoScriptBlocker scriptBlocker;
7912 WillDoReflow();
7913 AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
7915 do {
7916 // Send an incremental reflow notification to the target frame.
7917 PRInt32 idx = mDirtyRoots.Length() - 1;
7918 nsIFrame *target = mDirtyRoots[idx];
7919 mDirtyRoots.RemoveElementAt(idx);
7921 if (!NS_SUBTREE_DIRTY(target)) {
7922 // It's not dirty anymore, which probably means the notification
7923 // was posted in the middle of a reflow (perhaps with a reflow
7924 // root in the middle). Don't do anything.
7925 continue;
7928 interrupted = !DoReflow(target, aInterruptible);
7930 // Keep going until we're out of reflow commands, or we've run
7931 // past our deadline, or we're interrupted.
7932 } while (!interrupted && mDirtyRoots.Length() &&
7933 (!aInterruptible || PR_IntervalNow() < deadline));
7935 interrupted = mDirtyRoots.Length() != 0;
7938 // Exiting the scriptblocker might have killed us
7939 if (!mIsDestroying) {
7940 DidDoReflow(aInterruptible);
7943 // DidDoReflow might have killed us
7944 if (!mIsDestroying) {
7945 #ifdef DEBUG
7946 if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
7947 printf("\nPresShell::ProcessReflowCommands() finished: this=%p\n",
7948 (void*)this);
7950 DoVerifyReflow();
7951 #endif
7953 // If any new reflow commands were enqueued during the reflow, schedule
7954 // another reflow event to process them. Note that we want to do this
7955 // after DidDoReflow(), since that method can change whether there are
7956 // dirty roots around by flushing, and there's no point in posting a
7957 // reflow event just to have the flush revoke it.
7958 if (mDirtyRoots.Length())
7959 MaybeScheduleReflow();
7963 if (!mIsDestroying && mShouldUnsuppressPainting &&
7964 mDirtyRoots.Length() == 0) {
7965 // We only unlock if we're out of reflows. It's pointless
7966 // to unlock if reflows are still pending, since reflows
7967 // are just going to thrash the frames around some more. By
7968 // waiting we avoid an overeager "jitter" effect.
7969 mShouldUnsuppressPainting = PR_FALSE;
7970 UnsuppressAndInvalidate();
7973 return !interrupted;
7976 #ifdef MOZ_XUL
7978 * It's better to add stuff to the |DidSetStyleContext| method of the
7979 * relevant frames than adding it here. These methods should (ideally,
7980 * anyway) go away.
7983 // Return value says whether to walk children.
7984 typedef PRBool (* frameWalkerFn)(nsIFrame *aFrame, void *aClosure);
7986 static PRBool
7987 ReResolveMenusAndTrees(nsIFrame *aFrame, void *aClosure)
7989 // Trees have a special style cache that needs to be flushed when
7990 // the theme changes.
7991 nsTreeBodyFrame *treeBody = do_QueryFrame(aFrame);
7992 if (treeBody)
7993 treeBody->ClearStyleAndImageCaches();
7995 // We deliberately don't re-resolve style on a menu's popup
7996 // sub-content, since doing so slows menus to a crawl. That means we
7997 // have to special-case them on a skin switch, and ensure that the
7998 // popup frames just get destroyed completely.
7999 if (aFrame && aFrame->GetType() == nsGkAtoms::menuFrame)
8000 (static_cast<nsMenuFrame *>(aFrame))->CloseMenu(PR_TRUE);
8001 return PR_TRUE;
8004 static PRBool
8005 ReframeImageBoxes(nsIFrame *aFrame, void *aClosure)
8007 nsStyleChangeList *list = static_cast<nsStyleChangeList*>(aClosure);
8008 if (aFrame->GetType() == nsGkAtoms::imageBoxFrame) {
8009 list->AppendChange(aFrame, aFrame->GetContent(),
8010 NS_STYLE_HINT_FRAMECHANGE);
8011 return PR_FALSE; // don't walk descendants
8013 return PR_TRUE; // walk descendants
8016 static void
8017 WalkFramesThroughPlaceholders(nsPresContext *aPresContext, nsIFrame *aFrame,
8018 frameWalkerFn aFunc, void *aClosure)
8020 PRBool walkChildren = (*aFunc)(aFrame, aClosure);
8021 if (!walkChildren)
8022 return;
8024 PRInt32 listIndex = 0;
8025 nsIAtom* childList = nsnull;
8027 do {
8028 nsIFrame *child = aFrame->GetFirstChild(childList);
8029 while (child) {
8030 if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
8031 // only do frames that are in flow, and recur through the
8032 // out-of-flows of placeholders.
8033 WalkFramesThroughPlaceholders(aPresContext,
8034 nsPlaceholderFrame::GetRealFrameFor(child),
8035 aFunc, aClosure);
8037 child = child->GetNextSibling();
8040 childList = aFrame->GetAdditionalChildListName(listIndex++);
8041 } while (childList);
8043 #endif
8045 NS_IMETHODIMP
8046 PresShell::Observe(nsISupports* aSubject,
8047 const char* aTopic,
8048 const PRUnichar* aData)
8050 #ifdef MOZ_XUL
8051 if (!nsCRT::strcmp(aTopic, "chrome-flush-skin-caches")) {
8052 nsIFrame *rootFrame = FrameManager()->GetRootFrame();
8053 // Need to null-check because "chrome-flush-skin-caches" can happen
8054 // at interesting times during startup.
8055 if (rootFrame) {
8056 NS_ASSERTION(mViewManager, "View manager must exist");
8057 nsIViewManager::UpdateViewBatch batch(mViewManager);
8059 nsWeakFrame weakRoot(rootFrame);
8060 // Have to make sure that the content notifications are flushed before we
8061 // start messing with the frame model; otherwise we can get content doubling.
8062 mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
8064 if (weakRoot.IsAlive()) {
8065 WalkFramesThroughPlaceholders(mPresContext, rootFrame,
8066 &ReResolveMenusAndTrees, nsnull);
8068 // Because "chrome:" URL equality is messy, reframe image box
8069 // frames (hack!).
8070 nsStyleChangeList changeList;
8071 WalkFramesThroughPlaceholders(mPresContext, rootFrame,
8072 ReframeImageBoxes, &changeList);
8073 // Mark ourselves as not safe to flush while we're doing frame
8074 // construction.
8076 nsAutoScriptBlocker scriptBlocker;
8077 ++mChangeNestCount;
8078 mFrameConstructor->ProcessRestyledFrames(changeList);
8079 --mChangeNestCount;
8082 batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
8084 return NS_OK;
8086 #endif
8088 if (!nsCRT::strcmp(aTopic, "agent-sheet-added") && mStyleSet) {
8089 AddAgentSheet(aSubject);
8090 return NS_OK;
8093 if (!nsCRT::strcmp(aTopic, "user-sheet-added") && mStyleSet) {
8094 AddUserSheet(aSubject);
8095 return NS_OK;
8098 if (!nsCRT::strcmp(aTopic, "agent-sheet-removed") && mStyleSet) {
8099 RemoveSheet(nsStyleSet::eAgentSheet, aSubject);
8100 return NS_OK;
8103 if (!nsCRT::strcmp(aTopic, "user-sheet-removed") && mStyleSet) {
8104 RemoveSheet(nsStyleSet::eUserSheet, aSubject);
8105 return NS_OK;
8108 #ifdef ACCESSIBILITY
8109 if (!nsCRT::strcmp(aTopic, "a11y-init-or-shutdown")) {
8110 gIsAccessibilityActive = aData && *aData == '1';
8112 #endif
8113 NS_WARNING("unrecognized topic in PresShell::Observe");
8114 return NS_ERROR_FAILURE;
8117 PRBool
8118 nsIPresShell::AddRefreshObserverInternal(nsARefreshObserver* aObserver,
8119 mozFlushType aFlushType)
8121 return GetPresContext()->RefreshDriver()->
8122 AddRefreshObserver(aObserver, aFlushType);
8125 /* virtual */ PRBool
8126 nsIPresShell::AddRefreshObserverExternal(nsARefreshObserver* aObserver,
8127 mozFlushType aFlushType)
8129 return AddRefreshObserverInternal(aObserver, aFlushType);
8132 PRBool
8133 nsIPresShell::RemoveRefreshObserverInternal(nsARefreshObserver* aObserver,
8134 mozFlushType aFlushType)
8136 return GetPresContext()->RefreshDriver()->
8137 RemoveRefreshObserver(aObserver, aFlushType);
8140 /* virtual */ PRBool
8141 nsIPresShell::RemoveRefreshObserverExternal(nsARefreshObserver* aObserver,
8142 mozFlushType aFlushType)
8144 return RemoveRefreshObserverInternal(aObserver, aFlushType);
8147 //------------------------------------------------------
8148 // End of protected and private methods on the PresShell
8149 //------------------------------------------------------
8151 // Start of DEBUG only code
8153 #ifdef NS_DEBUG
8154 #include "nsViewsCID.h"
8155 #include "nsWidgetsCID.h"
8156 #include "nsIDeviceContext.h"
8157 #include "nsIURL.h"
8158 #include "nsILinkHandler.h"
8160 static NS_DEFINE_CID(kViewManagerCID, NS_VIEW_MANAGER_CID);
8162 static void
8163 LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg)
8165 nsAutoString n1, n2;
8166 if (k1) {
8167 k1->GetFrameName(n1);
8168 } else {
8169 n1.Assign(NS_LITERAL_STRING("(null)"));
8172 if (k2) {
8173 k2->GetFrameName(n2);
8174 } else {
8175 n2.Assign(NS_LITERAL_STRING("(null)"));
8178 printf("verifyreflow: %s %p != %s %p %s\n",
8179 NS_LossyConvertUTF16toASCII(n1).get(), (void*)k1,
8180 NS_LossyConvertUTF16toASCII(n2).get(), (void*)k2, aMsg);
8183 static void
8184 LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
8185 const nsRect& r1, const nsRect& r2)
8187 printf("VerifyReflow Error:\n");
8188 nsAutoString name;
8190 if (k1) {
8191 k1->GetFrameName(name);
8192 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1);
8194 printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height);
8196 if (k2) {
8197 k2->GetFrameName(name);
8198 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2);
8200 printf("{%d, %d, %d, %d}\n %s\n",
8201 r2.x, r2.y, r2.width, r2.height, aMsg);
8204 static void
8205 LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
8206 const nsIntRect& r1, const nsIntRect& r2)
8208 printf("VerifyReflow Error:\n");
8209 nsAutoString name;
8211 if (k1) {
8212 k1->GetFrameName(name);
8213 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k1);
8215 printf("{%d, %d, %d, %d} != \n", r1.x, r1.y, r1.width, r1.height);
8217 if (k2) {
8218 k2->GetFrameName(name);
8219 printf(" %s %p ", NS_LossyConvertUTF16toASCII(name).get(), (void*)k2);
8221 printf("{%d, %d, %d, %d}\n %s\n",
8222 r2.x, r2.y, r2.width, r2.height, aMsg);
8225 static PRBool
8226 CompareTrees(nsPresContext* aFirstPresContext, nsIFrame* aFirstFrame,
8227 nsPresContext* aSecondPresContext, nsIFrame* aSecondFrame)
8229 if (!aFirstPresContext || !aFirstFrame || !aSecondPresContext || !aSecondFrame)
8230 return PR_TRUE;
8231 // XXX Evil hack to reduce false positives; I can't seem to figure
8232 // out how to flush scrollbar changes correctly
8233 //if (aFirstFrame->GetType() == nsGkAtoms::scrollbarFrame)
8234 // return PR_TRUE;
8235 PRBool ok = PR_TRUE;
8236 nsIAtom* listName = nsnull;
8237 PRInt32 listIndex = 0;
8238 do {
8239 const nsFrameList& kids1 = aFirstFrame->GetChildList(listName);
8240 const nsFrameList& kids2 = aSecondFrame->GetChildList(listName);
8241 PRInt32 l1 = kids1.GetLength();
8242 PRInt32 l2 = kids2.GetLength();;
8243 if (l1 != l2) {
8244 ok = PR_FALSE;
8245 LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(),
8246 "child counts don't match: ");
8247 printf("%d != %d\n", l1, l2);
8248 if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
8249 break;
8253 nsIntRect r1, r2;
8254 nsIView* v1, *v2;
8255 for (nsFrameList::Enumerator e1(kids1), e2(kids2);
8257 e1.Next(), e2.Next()) {
8258 nsIFrame* k1 = e1.get();
8259 nsIFrame* k2 = e2.get();
8260 if (((nsnull == k1) && (nsnull != k2)) ||
8261 ((nsnull != k1) && (nsnull == k2))) {
8262 ok = PR_FALSE;
8263 LogVerifyMessage(k1, k2, "child lists are different\n");
8264 break;
8266 else if (nsnull != k1) {
8267 // Verify that the frames are the same size
8268 if (k1->GetRect() != k2->GetRect()) {
8269 ok = PR_FALSE;
8270 LogVerifyMessage(k1, k2, "(frame rects)", k1->GetRect(), k2->GetRect());
8273 // Make sure either both have views or neither have views; if they
8274 // do have views, make sure the views are the same size. If the
8275 // views have widgets, make sure they both do or neither does. If
8276 // they do, make sure the widgets are the same size.
8277 v1 = k1->GetView();
8278 v2 = k2->GetView();
8279 if (((nsnull == v1) && (nsnull != v2)) ||
8280 ((nsnull != v1) && (nsnull == v2))) {
8281 ok = PR_FALSE;
8282 LogVerifyMessage(k1, k2, "child views are not matched\n");
8284 else if (nsnull != v1) {
8285 if (v1->GetBounds() != v2->GetBounds()) {
8286 LogVerifyMessage(k1, k2, "(view rects)", v1->GetBounds(), v2->GetBounds());
8289 nsIWidget* w1 = v1->GetWidget();
8290 nsIWidget* w2 = v2->GetWidget();
8291 if (((nsnull == w1) && (nsnull != w2)) ||
8292 ((nsnull != w1) && (nsnull == w2))) {
8293 ok = PR_FALSE;
8294 LogVerifyMessage(k1, k2, "child widgets are not matched\n");
8296 else if (nsnull != w1) {
8297 w1->GetBounds(r1);
8298 w2->GetBounds(r2);
8299 if (r1 != r2) {
8300 LogVerifyMessage(k1, k2, "(widget rects)", r1, r2);
8304 if (!ok && (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags))) {
8305 break;
8308 // XXX Should perhaps compare their float managers.
8310 // Compare the sub-trees too
8311 if (!CompareTrees(aFirstPresContext, k1, aSecondPresContext, k2)) {
8312 ok = PR_FALSE;
8313 if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
8314 break;
8318 else {
8319 break;
8322 if (!ok && (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags))) {
8323 break;
8326 nsIAtom* listName1 = aFirstFrame->GetAdditionalChildListName(listIndex);
8327 nsIAtom* listName2 = aSecondFrame->GetAdditionalChildListName(listIndex);
8328 listIndex++;
8329 if (listName1 != listName2) {
8330 if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
8331 ok = PR_FALSE;
8333 LogVerifyMessage(kids1.FirstChild(), kids2.FirstChild(),
8334 "child list names are not matched: ");
8335 nsAutoString tmp;
8336 if (nsnull != listName1) {
8337 listName1->ToString(tmp);
8338 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), stdout);
8340 else
8341 fputs("(null)", stdout);
8342 printf(" != ");
8343 if (nsnull != listName2) {
8344 listName2->ToString(tmp);
8345 fputs(NS_LossyConvertUTF16toASCII(tmp).get(), stdout);
8347 else
8348 fputs("(null)", stdout);
8349 printf("\n");
8350 break;
8352 listName = listName1;
8353 } while (ok && (listName != nsnull));
8355 return ok;
8357 #endif
8359 #if 0
8360 static nsIFrame*
8361 FindTopFrame(nsIFrame* aRoot)
8363 if (aRoot) {
8364 nsIContent* content = aRoot->GetContent();
8365 if (content) {
8366 nsIAtom* tag;
8367 content->GetTag(tag);
8368 if (nsnull != tag) {
8369 NS_RELEASE(tag);
8370 return aRoot;
8374 // Try one of the children
8375 nsIFrame* kid = aRoot->GetFirstChild(nsnull);
8376 while (nsnull != kid) {
8377 nsIFrame* result = FindTopFrame(kid);
8378 if (nsnull != result) {
8379 return result;
8381 kid = kid->GetNextSibling();
8384 return nsnull;
8386 #endif
8389 #ifdef DEBUG
8391 nsresult
8392 PresShell::CloneStyleSet(nsStyleSet* aSet, nsStyleSet** aResult)
8394 nsStyleSet *clone = new nsStyleSet();
8395 if (!clone) {
8396 return NS_ERROR_OUT_OF_MEMORY;
8399 PRInt32 i, n = aSet->SheetCount(nsStyleSet::eOverrideSheet);
8400 for (i = 0; i < n; i++) {
8401 nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eOverrideSheet, i);
8402 if (ss)
8403 clone->AppendStyleSheet(nsStyleSet::eOverrideSheet, ss);
8406 // The document expects to insert document stylesheets itself
8407 #if 0
8408 n = aSet->SheetCount(nsStyleSet::eDocSheet);
8409 for (i = 0; i < n; i++) {
8410 nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eDocSheet, i);
8411 if (ss)
8412 clone->AddDocStyleSheet(ss, mDocument);
8414 #endif
8416 n = aSet->SheetCount(nsStyleSet::eUserSheet);
8417 for (i = 0; i < n; i++) {
8418 nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eUserSheet, i);
8419 if (ss)
8420 clone->AppendStyleSheet(nsStyleSet::eUserSheet, ss);
8423 n = aSet->SheetCount(nsStyleSet::eAgentSheet);
8424 for (i = 0; i < n; i++) {
8425 nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eAgentSheet, i);
8426 if (ss)
8427 clone->AppendStyleSheet(nsStyleSet::eAgentSheet, ss);
8429 *aResult = clone;
8430 return NS_OK;
8433 #ifdef DEBUG_Eli
8434 static nsresult
8435 DumpToPNG(nsIPresShell* shell, nsAString& name) {
8436 PRInt32 width=1000, height=1000;
8437 nsRect r(0, 0, shell->GetPresContext()->DevPixelsToAppUnits(width),
8438 shell->GetPresContext()->DevPixelsToAppUnits(height));
8440 nsRefPtr<gfxImageSurface> imgSurface =
8441 new gfxImageSurface(gfxIntSize(width, height),
8442 gfxImageSurface::ImageFormatARGB32);
8443 NS_ENSURE_TRUE(imgSurface, NS_ERROR_OUT_OF_MEMORY);
8445 nsRefPtr<gfxContext> imgContext = new gfxContext(imgSurface);
8447 nsRefPtr<gfxASurface> surface =
8448 gfxPlatform::GetPlatform()->
8449 CreateOffscreenSurface(gfxIntSize(width, height),
8450 gfxASurface::ContentFromFormat(gfxASurface::ImageFormatARGB32));
8451 NS_ENSURE_TRUE(surface, NS_ERROR_OUT_OF_MEMORY);
8453 nsRefPtr<gfxContext> context = new gfxContext(surface);
8454 NS_ENSURE_TRUE(context, NS_ERROR_OUT_OF_MEMORY);
8456 shell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context);
8458 imgContext->DrawSurface(surface, gfxSize(width, height));
8460 nsCOMPtr<imgIEncoder> encoder = do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png");
8461 NS_ENSURE_TRUE(encoder, NS_ERROR_FAILURE);
8462 encoder->InitFromData(imgSurface->Data(), imgSurface->Stride() * height,
8463 width, height, imgSurface->Stride(),
8464 imgIEncoder::INPUT_FORMAT_HOSTARGB, EmptyString());
8466 // XXX not sure if this is the right way to write to a file
8467 nsCOMPtr<nsILocalFile> file = do_CreateInstance("@mozilla.org/file/local;1");
8468 NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
8469 rv = file->InitWithPath(name);
8470 NS_ENSURE_SUCCESS(rv, rv);
8472 PRUint32 length;
8473 encoder->Available(&length);
8475 nsCOMPtr<nsIOutputStream> outputStream;
8476 rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
8477 NS_ENSURE_SUCCESS(rv, rv);
8479 nsCOMPtr<nsIOutputStream> bufferedOutputStream;
8480 rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
8481 outputStream, length);
8483 PRUint32 numWritten;
8484 rv = bufferedOutputStream->WriteFrom(encoder, length, &numWritten);
8485 NS_ENSURE_SUCCESS(rv, rv);
8487 return NS_OK;
8489 #endif
8491 // After an incremental reflow, we verify the correctness by doing a
8492 // full reflow into a fresh frame tree.
8493 PRBool
8494 PresShell::VerifyIncrementalReflow()
8496 if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
8497 printf("Building Verification Tree...\n");
8500 // Create a presentation context to view the new frame tree
8501 nsRefPtr<nsPresContext> cx =
8502 new nsRootPresContext(mDocument, mPresContext->IsPaginated() ?
8503 nsPresContext::eContext_PrintPreview :
8504 nsPresContext::eContext_Galley);
8505 NS_ENSURE_TRUE(cx, PR_FALSE);
8507 nsIDeviceContext *dc = mPresContext->DeviceContext();
8508 nsresult rv = cx->Init(dc);
8509 NS_ENSURE_SUCCESS(rv, PR_FALSE);
8511 // Get our scrolling preference
8512 nsIView* rootView;
8513 mViewManager->GetRootView(rootView);
8514 NS_ENSURE_TRUE(rootView->HasWidget(), PR_FALSE);
8515 nsIWidget* parentWidget = rootView->GetWidget();
8517 // Create a new view manager.
8518 nsCOMPtr<nsIViewManager> vm = do_CreateInstance(kViewManagerCID);
8519 NS_ENSURE_TRUE(vm, PR_FALSE);
8520 rv = vm->Init(dc);
8521 NS_ENSURE_SUCCESS(rv, PR_FALSE);
8523 // Create a child window of the parent that is our "root view/window"
8524 // Create a view
8525 nsRect tbounds = mPresContext->GetVisibleArea();
8526 nsIView* view = vm->CreateView(tbounds, nsnull);
8527 NS_ENSURE_TRUE(view, PR_FALSE);
8529 //now create the widget for the view
8530 rv = view->CreateWidgetForParent(parentWidget, nsnull, PR_TRUE);
8531 NS_ENSURE_SUCCESS(rv, PR_FALSE);
8533 // Setup hierarchical relationship in view manager
8534 vm->SetRootView(view);
8536 // Make the new presentation context the same size as our
8537 // presentation context.
8538 nsRect r = mPresContext->GetVisibleArea();
8539 cx->SetVisibleArea(r);
8541 // Create a new presentation shell to view the document. Use the
8542 // exact same style information that this document has.
8543 nsAutoPtr<nsStyleSet> newSet;
8544 rv = CloneStyleSet(mStyleSet, getter_Transfers(newSet));
8545 NS_ENSURE_SUCCESS(rv, PR_FALSE);
8546 nsCOMPtr<nsIPresShell> sh;
8547 rv = mDocument->CreateShell(cx, vm, newSet, getter_AddRefs(sh));
8548 NS_ENSURE_SUCCESS(rv, PR_FALSE);
8549 newSet.forget();
8550 // Note that after we create the shell, we must make sure to destroy it
8551 sh->SetVerifyReflowEnable(PR_FALSE); // turn off verify reflow while we're reflowing the test frame tree
8552 vm->SetViewObserver((nsIViewObserver *)((PresShell*)sh.get()));
8554 nsAutoCauseReflowNotifier crNotifier(this);
8555 sh->InitialReflow(r.width, r.height);
8557 mDocument->BindingManager()->ProcessAttachedQueue();
8558 sh->FlushPendingNotifications(Flush_Layout);
8559 sh->SetVerifyReflowEnable(PR_TRUE); // turn on verify reflow again now that we're done reflowing the test frame tree
8560 // Force the non-primary presshell to unsuppress; it doesn't want to normally
8561 // because it thinks it's hidden
8562 ((PresShell*)sh.get())->mPaintingSuppressed = PR_FALSE;
8563 if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
8564 printf("Verification Tree built, comparing...\n");
8567 // Now that the document has been reflowed, use its frame tree to
8568 // compare against our frame tree.
8569 nsIFrame* root1 = FrameManager()->GetRootFrame();
8570 nsIFrame* root2 = sh->FrameManager()->GetRootFrame();
8571 PRBool ok = CompareTrees(mPresContext, root1, cx, root2);
8572 if (!ok && (VERIFY_REFLOW_NOISY & gVerifyReflowFlags)) {
8573 printf("Verify reflow failed, primary tree:\n");
8574 root1->List(stdout, 0);
8575 printf("Verification tree:\n");
8576 root2->List(stdout, 0);
8579 #ifdef DEBUG_Eli
8580 // Sample code for dumping page to png
8581 // XXX Needs to be made more flexible
8582 if (!ok) {
8583 nsString stra;
8584 static int num = 0;
8585 stra.AppendLiteral("C:\\mozilla\\mozilla\\debug\\filea");
8586 stra.AppendInt(num);
8587 stra.AppendLiteral(".png");
8588 DumpToPNG(sh, stra);
8589 nsString strb;
8590 strb.AppendLiteral("C:\\mozilla\\mozilla\\debug\\fileb");
8591 strb.AppendInt(num);
8592 strb.AppendLiteral(".png");
8593 DumpToPNG(this, strb);
8594 ++num;
8596 #endif
8598 sh->EndObservingDocument();
8599 sh->Destroy();
8600 if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
8601 printf("Finished Verifying Reflow...\n");
8604 return ok;
8607 // Layout debugging hooks
8608 void
8609 PresShell::ListStyleContexts(nsIFrame *aRootFrame, FILE *out, PRInt32 aIndent)
8611 nsStyleContext *sc = aRootFrame->GetStyleContext();
8612 if (sc)
8613 sc->List(out, aIndent);
8616 void
8617 PresShell::ListStyleSheets(FILE *out, PRInt32 aIndent)
8619 PRInt32 sheetCount = mStyleSet->SheetCount(nsStyleSet::eDocSheet);
8620 for (PRInt32 i = 0; i < sheetCount; ++i) {
8621 mStyleSet->StyleSheetAt(nsStyleSet::eDocSheet, i)->List(out, aIndent);
8622 fputs("\n", out);
8626 void
8627 PresShell::VerifyStyleTree()
8629 VERIFY_STYLE_TREE;
8631 #endif
8633 //=============================================================
8634 //=============================================================
8635 //-- Debug Reflow Counts
8636 //=============================================================
8637 //=============================================================
8638 #ifdef MOZ_REFLOW_PERF
8639 //-------------------------------------------------------------
8640 void
8641 PresShell::DumpReflows()
8643 if (mReflowCountMgr) {
8644 nsCAutoString uriStr;
8645 if (mDocument) {
8646 nsIURI *uri = mDocument->GetDocumentURI();
8647 if (uri) {
8648 uri->GetPath(uriStr);
8651 mReflowCountMgr->DisplayTotals(uriStr.get());
8652 mReflowCountMgr->DisplayHTMLTotals(uriStr.get());
8653 mReflowCountMgr->DisplayDiffsInTotals("Differences");
8657 //-------------------------------------------------------------
8658 void
8659 PresShell::CountReflows(const char * aName, nsIFrame * aFrame)
8661 if (mReflowCountMgr) {
8662 mReflowCountMgr->Add(aName, aFrame);
8666 //-------------------------------------------------------------
8667 void
8668 PresShell::PaintCount(const char * aName,
8669 nsIRenderingContext* aRenderingContext,
8670 nsPresContext* aPresContext,
8671 nsIFrame * aFrame,
8672 PRUint32 aColor)
8674 if (mReflowCountMgr) {
8675 mReflowCountMgr->PaintCount(aName, aRenderingContext, aPresContext, aFrame, aColor);
8679 //-------------------------------------------------------------
8680 void
8681 PresShell::SetPaintFrameCount(PRBool aPaintFrameCounts)
8683 if (mReflowCountMgr) {
8684 mReflowCountMgr->SetPaintFrameCounts(aPaintFrameCounts);
8688 PRBool
8689 PresShell::IsPaintingFrameCounts()
8691 if (mReflowCountMgr)
8692 return mReflowCountMgr->IsPaintingFrameCounts();
8693 return PR_FALSE;
8696 //------------------------------------------------------------------
8697 //-- Reflow Counter Classes Impls
8698 //------------------------------------------------------------------
8700 //------------------------------------------------------------------
8701 ReflowCounter::ReflowCounter(ReflowCountMgr * aMgr) :
8702 mMgr(aMgr)
8704 ClearTotals();
8705 SetTotalsCache();
8708 //------------------------------------------------------------------
8709 ReflowCounter::~ReflowCounter()
8714 //------------------------------------------------------------------
8715 void ReflowCounter::ClearTotals()
8717 mTotal = 0;
8720 //------------------------------------------------------------------
8721 void ReflowCounter::SetTotalsCache()
8723 mCacheTotal = mTotal;
8726 //------------------------------------------------------------------
8727 void ReflowCounter::CalcDiffInTotals()
8729 mCacheTotal = mTotal - mCacheTotal;
8732 //------------------------------------------------------------------
8733 void ReflowCounter::DisplayTotals(const char * aStr)
8735 DisplayTotals(mTotal, aStr?aStr:"Totals");
8738 //------------------------------------------------------------------
8739 void ReflowCounter::DisplayDiffTotals(const char * aStr)
8741 DisplayTotals(mCacheTotal, aStr?aStr:"Diff Totals");
8744 //------------------------------------------------------------------
8745 void ReflowCounter::DisplayHTMLTotals(const char * aStr)
8747 DisplayHTMLTotals(mTotal, aStr?aStr:"Totals");
8750 //------------------------------------------------------------------
8751 void ReflowCounter::DisplayTotals(PRUint32 aTotal, const char * aTitle)
8753 // figure total
8754 if (aTotal == 0) {
8755 return;
8757 ReflowCounter * gTots = (ReflowCounter *)mMgr->LookUp(kGrandTotalsStr);
8759 printf("%25s\t", aTitle);
8760 printf("%d\t", aTotal);
8761 if (gTots != this && aTotal > 0) {
8762 gTots->Add(aTotal);
8766 //------------------------------------------------------------------
8767 void ReflowCounter::DisplayHTMLTotals(PRUint32 aTotal, const char * aTitle)
8769 if (aTotal == 0) {
8770 return;
8773 ReflowCounter * gTots = (ReflowCounter *)mMgr->LookUp(kGrandTotalsStr);
8774 FILE * fd = mMgr->GetOutFile();
8775 if (!fd) {
8776 return;
8779 fprintf(fd, "<tr><td><center>%s</center></td>", aTitle);
8780 fprintf(fd, "<td><center>%d</center></td></tr>\n", aTotal);
8782 if (gTots != this && aTotal > 0) {
8783 gTots->Add(aTotal);
8787 //------------------------------------------------------------------
8788 //-- ReflowCountMgr
8789 //------------------------------------------------------------------
8790 ReflowCountMgr::ReflowCountMgr()
8792 mCounts = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
8793 PL_CompareValues, nsnull, nsnull);
8794 mIndiFrameCounts = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
8795 PL_CompareValues, nsnull, nsnull);
8796 mCycledOnce = PR_FALSE;
8797 mDumpFrameCounts = PR_FALSE;
8798 mDumpFrameByFrameCounts = PR_FALSE;
8799 mPaintFrameByFrameCounts = PR_FALSE;
8802 //------------------------------------------------------------------
8803 ReflowCountMgr::~ReflowCountMgr()
8805 CleanUp();
8808 //------------------------------------------------------------------
8809 ReflowCounter * ReflowCountMgr::LookUp(const char * aName)
8811 if (nsnull != mCounts) {
8812 ReflowCounter * counter = (ReflowCounter *)PL_HashTableLookup(mCounts, aName);
8813 return counter;
8815 return nsnull;
8819 //------------------------------------------------------------------
8820 void ReflowCountMgr::Add(const char * aName, nsIFrame * aFrame)
8822 NS_ASSERTION(aName != nsnull, "Name shouldn't be null!");
8824 if (mDumpFrameCounts && nsnull != mCounts) {
8825 ReflowCounter * counter = (ReflowCounter *)PL_HashTableLookup(mCounts, aName);
8826 if (counter == nsnull) {
8827 counter = new ReflowCounter(this);
8828 NS_ASSERTION(counter != nsnull, "null ptr");
8829 char * name = NS_strdup(aName);
8830 NS_ASSERTION(name != nsnull, "null ptr");
8831 PL_HashTableAdd(mCounts, name, counter);
8833 counter->Add();
8836 if ((mDumpFrameByFrameCounts || mPaintFrameByFrameCounts) &&
8837 nsnull != mIndiFrameCounts &&
8838 aFrame != nsnull) {
8839 char * key = new char[16];
8840 sprintf(key, "%p", (void*)aFrame);
8841 IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(mIndiFrameCounts, key);
8842 if (counter == nsnull) {
8843 counter = new IndiReflowCounter(this);
8844 NS_ASSERTION(counter != nsnull, "null ptr");
8845 counter->mFrame = aFrame;
8846 counter->mName.AssignASCII(aName);
8847 PL_HashTableAdd(mIndiFrameCounts, key, counter);
8849 // this eliminates extra counts from super classes
8850 if (counter != nsnull && counter->mName.EqualsASCII(aName)) {
8851 counter->mCount++;
8852 counter->mCounter.Add(1);
8857 //------------------------------------------------------------------
8858 void ReflowCountMgr::PaintCount(const char * aName,
8859 nsIRenderingContext* aRenderingContext,
8860 nsPresContext* aPresContext,
8861 nsIFrame* aFrame,
8862 PRUint32 aColor)
8864 if (mPaintFrameByFrameCounts &&
8865 nsnull != mIndiFrameCounts &&
8866 aFrame != nsnull) {
8867 char * key = new char[16];
8868 sprintf(key, "%p", (void*)aFrame);
8869 IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(mIndiFrameCounts, key);
8870 if (counter != nsnull && counter->mName.EqualsASCII(aName)) {
8871 aRenderingContext->PushState();
8872 nsFont font("Times", NS_FONT_STYLE_NORMAL, NS_FONT_VARIANT_NORMAL,
8873 NS_FONT_WEIGHT_NORMAL, NS_FONT_STRETCH_NORMAL, 0,
8874 nsPresContext::CSSPixelsToAppUnits(11));
8876 nsCOMPtr<nsIFontMetrics> fm = aPresContext->GetMetricsFor(font);
8877 aRenderingContext->SetFont(fm);
8878 char buf[16];
8879 sprintf(buf, "%d", counter->mCount);
8880 nscoord x = 0, y;
8881 nscoord width, height;
8882 aRenderingContext->SetTextRunRTL(PR_FALSE);
8883 aRenderingContext->GetWidth((char*)buf, width);
8884 fm->GetHeight(height);
8885 fm->GetMaxAscent(y);
8887 PRUint32 color;
8888 PRUint32 color2;
8889 if (aColor != 0) {
8890 color = aColor;
8891 color2 = NS_RGB(0,0,0);
8892 } else {
8893 PRUint8 rc = 0, gc = 0, bc = 0;
8894 if (counter->mCount < 5) {
8895 rc = 255;
8896 gc = 255;
8897 } else if ( counter->mCount < 11) {
8898 gc = 255;
8899 } else {
8900 rc = 255;
8902 color = NS_RGB(rc,gc,bc);
8903 color2 = NS_RGB(rc/2,gc/2,bc/2);
8906 nsRect rect(0,0, width+15, height+15);
8907 aRenderingContext->SetColor(NS_RGB(0,0,0));
8908 aRenderingContext->FillRect(rect);
8909 aRenderingContext->SetColor(color2);
8910 aRenderingContext->DrawString(buf, strlen(buf), x+15,y+15);
8911 aRenderingContext->SetColor(color);
8912 aRenderingContext->DrawString(buf, strlen(buf), x,y);
8914 aRenderingContext->PopState();
8919 //------------------------------------------------------------------
8920 PRIntn ReflowCountMgr::RemoveItems(PLHashEntry *he, PRIntn i, void *arg)
8922 char *str = (char *)he->key;
8923 ReflowCounter * counter = (ReflowCounter *)he->value;
8924 delete counter;
8925 NS_Free(str);
8927 return HT_ENUMERATE_REMOVE;
8930 //------------------------------------------------------------------
8931 PRIntn ReflowCountMgr::RemoveIndiItems(PLHashEntry *he, PRIntn i, void *arg)
8933 char *str = (char *)he->key;
8934 IndiReflowCounter * counter = (IndiReflowCounter *)he->value;
8935 delete counter;
8936 NS_Free(str);
8938 return HT_ENUMERATE_REMOVE;
8941 //------------------------------------------------------------------
8942 void ReflowCountMgr::CleanUp()
8944 if (nsnull != mCounts) {
8945 PL_HashTableEnumerateEntries(mCounts, RemoveItems, nsnull);
8946 PL_HashTableDestroy(mCounts);
8947 mCounts = nsnull;
8950 if (nsnull != mIndiFrameCounts) {
8951 PL_HashTableEnumerateEntries(mIndiFrameCounts, RemoveIndiItems, nsnull);
8952 PL_HashTableDestroy(mIndiFrameCounts);
8953 mIndiFrameCounts = nsnull;
8957 //------------------------------------------------------------------
8958 PRIntn ReflowCountMgr::DoSingleTotal(PLHashEntry *he, PRIntn i, void *arg)
8960 char *str = (char *)he->key;
8961 ReflowCounter * counter = (ReflowCounter *)he->value;
8963 counter->DisplayTotals(str);
8965 return HT_ENUMERATE_NEXT;
8968 //------------------------------------------------------------------
8969 void ReflowCountMgr::DoGrandTotals()
8971 if (nsnull != mCounts) {
8972 ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
8973 if (gTots == nsnull) {
8974 gTots = new ReflowCounter(this);
8975 PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
8976 } else {
8977 gTots->ClearTotals();
8980 printf("\t\t\t\tTotal\n");
8981 for (PRUint32 i=0;i<78;i++) {
8982 printf("-");
8984 printf("\n");
8985 PL_HashTableEnumerateEntries(mCounts, DoSingleTotal, this);
8989 static void RecurseIndiTotals(nsPresContext* aPresContext,
8990 PLHashTable * aHT,
8991 nsIFrame * aParentFrame,
8992 PRInt32 aLevel)
8994 if (aParentFrame == nsnull) {
8995 return;
8998 char key[16];
8999 sprintf(key, "%p", (void*)aParentFrame);
9000 IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(aHT, key);
9001 if (counter) {
9002 counter->mHasBeenOutput = PR_TRUE;
9003 char * name = ToNewCString(counter->mName);
9004 for (PRInt32 i=0;i<aLevel;i++) printf(" ");
9005 printf("%s - %p [%d][", name, (void*)aParentFrame, counter->mCount);
9006 printf("%d", counter->mCounter.GetTotal());
9007 printf("]\n");
9008 nsMemory::Free(name);
9011 nsIFrame* child = aParentFrame->GetFirstChild(nsnull);
9012 while (child) {
9013 RecurseIndiTotals(aPresContext, aHT, child, aLevel+1);
9014 child = child->GetNextSibling();
9019 //------------------------------------------------------------------
9020 PRIntn ReflowCountMgr::DoSingleIndi(PLHashEntry *he, PRIntn i, void *arg)
9022 IndiReflowCounter * counter = (IndiReflowCounter *)he->value;
9023 if (counter && !counter->mHasBeenOutput) {
9024 char * name = ToNewCString(counter->mName);
9025 printf("%s - %p [%d][", name, (void*)counter->mFrame, counter->mCount);
9026 printf("%d", counter->mCounter.GetTotal());
9027 printf("]\n");
9028 nsMemory::Free(name);
9030 return HT_ENUMERATE_NEXT;
9033 //------------------------------------------------------------------
9034 void ReflowCountMgr::DoIndiTotalsTree()
9036 if (nsnull != mCounts) {
9037 printf("\n------------------------------------------------\n");
9038 printf("-- Individual Frame Counts\n");
9039 printf("------------------------------------------------\n");
9041 if (mPresShell) {
9042 nsIFrame * rootFrame = mPresShell->FrameManager()->GetRootFrame();
9043 RecurseIndiTotals(mPresContext, mIndiFrameCounts, rootFrame, 0);
9044 printf("------------------------------------------------\n");
9045 printf("-- Individual Counts of Frames not in Root Tree\n");
9046 printf("------------------------------------------------\n");
9047 PL_HashTableEnumerateEntries(mIndiFrameCounts, DoSingleIndi, this);
9052 //------------------------------------------------------------------
9053 PRIntn ReflowCountMgr::DoSingleHTMLTotal(PLHashEntry *he, PRIntn i, void *arg)
9055 char *str = (char *)he->key;
9056 ReflowCounter * counter = (ReflowCounter *)he->value;
9058 counter->DisplayHTMLTotals(str);
9060 return HT_ENUMERATE_NEXT;
9063 //------------------------------------------------------------------
9064 void ReflowCountMgr::DoGrandHTMLTotals()
9066 if (nsnull != mCounts) {
9067 ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
9068 if (gTots == nsnull) {
9069 gTots = new ReflowCounter(this);
9070 PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
9071 } else {
9072 gTots->ClearTotals();
9075 static const char * title[] = {"Class", "Reflows"};
9076 fprintf(mFD, "<tr>");
9077 for (PRUint32 i=0; i < NS_ARRAY_LENGTH(title); i++) {
9078 fprintf(mFD, "<td><center><b>%s<b></center></td>", title[i]);
9080 fprintf(mFD, "</tr>\n");
9081 PL_HashTableEnumerateEntries(mCounts, DoSingleHTMLTotal, this);
9085 //------------------------------------
9086 void ReflowCountMgr::DisplayTotals(const char * aStr)
9088 #ifdef DEBUG_rods
9089 printf("%s\n", aStr?aStr:"No name");
9090 #endif
9091 if (mDumpFrameCounts) {
9092 DoGrandTotals();
9094 if (mDumpFrameByFrameCounts) {
9095 DoIndiTotalsTree();
9099 //------------------------------------
9100 void ReflowCountMgr::DisplayHTMLTotals(const char * aStr)
9102 #ifdef WIN32x // XXX NOT XP!
9103 char name[1024];
9105 char * sptr = strrchr(aStr, '/');
9106 if (sptr) {
9107 sptr++;
9108 strcpy(name, sptr);
9109 char * eptr = strrchr(name, '.');
9110 if (eptr) {
9111 *eptr = 0;
9113 strcat(name, "_stats.html");
9115 mFD = fopen(name, "w");
9116 if (mFD) {
9117 fprintf(mFD, "<html><head><title>Reflow Stats</title></head><body>\n");
9118 const char * title = aStr?aStr:"No name";
9119 fprintf(mFD, "<center><b>%s</b><br><table border=1 style=\"background-color:#e0e0e0\">", title);
9120 DoGrandHTMLTotals();
9121 fprintf(mFD, "</center></table>\n");
9122 fprintf(mFD, "</body></html>\n");
9123 fclose(mFD);
9124 mFD = nsnull;
9126 #endif // not XP!
9129 //------------------------------------------------------------------
9130 PRIntn ReflowCountMgr::DoClearTotals(PLHashEntry *he, PRIntn i, void *arg)
9132 ReflowCounter * counter = (ReflowCounter *)he->value;
9133 counter->ClearTotals();
9135 return HT_ENUMERATE_NEXT;
9138 //------------------------------------------------------------------
9139 void ReflowCountMgr::ClearTotals()
9141 PL_HashTableEnumerateEntries(mCounts, DoClearTotals, this);
9144 //------------------------------------------------------------------
9145 void ReflowCountMgr::ClearGrandTotals()
9147 if (nsnull != mCounts) {
9148 ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
9149 if (gTots == nsnull) {
9150 gTots = new ReflowCounter(this);
9151 PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
9152 } else {
9153 gTots->ClearTotals();
9154 gTots->SetTotalsCache();
9159 //------------------------------------------------------------------
9160 PRIntn ReflowCountMgr::DoDisplayDiffTotals(PLHashEntry *he, PRIntn i, void *arg)
9162 PRBool cycledOnce = (arg != 0);
9164 char *str = (char *)he->key;
9165 ReflowCounter * counter = (ReflowCounter *)he->value;
9167 if (cycledOnce) {
9168 counter->CalcDiffInTotals();
9169 counter->DisplayDiffTotals(str);
9171 counter->SetTotalsCache();
9173 return HT_ENUMERATE_NEXT;
9176 //------------------------------------------------------------------
9177 void ReflowCountMgr::DisplayDiffsInTotals(const char * aStr)
9179 if (mCycledOnce) {
9180 printf("Differences\n");
9181 for (PRInt32 i=0;i<78;i++) {
9182 printf("-");
9184 printf("\n");
9185 ClearGrandTotals();
9187 PL_HashTableEnumerateEntries(mCounts, DoDisplayDiffTotals, (void *)mCycledOnce);
9189 mCycledOnce = PR_TRUE;
9192 #endif // MOZ_REFLOW_PERF
9194 // make a color string like #RRGGBB
9195 void ColorToString(nscolor aColor, nsAutoString &aString)
9197 char buf[8];
9199 PR_snprintf(buf, sizeof(buf), "#%02x%02x%02x",
9200 NS_GET_R(aColor), NS_GET_G(aColor), NS_GET_B(aColor));
9201 CopyASCIItoUTF16(buf, aString);
9204 nsIFrame* nsIPresShell::GetAbsoluteContainingBlock(nsIFrame *aFrame)
9206 return FrameConstructor()->GetAbsoluteContainingBlock(aFrame);
9209 void nsIPresShell::InitializeStatics()
9211 NS_ASSERTION(sLiveShells == nsnull, "InitializeStatics called multiple times!");
9212 sLiveShells = new nsTHashtable<PresShellPtrKey>();
9213 sLiveShells->Init();
9216 void nsIPresShell::ReleaseStatics()
9218 NS_ASSERTION(sLiveShells, "ReleaseStatics called without Initialize!");
9219 delete sLiveShells;
9220 sLiveShells = nsnull;
9223 // Asks our docshell whether we're active.
9224 void PresShell::QueryIsActive()
9226 nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
9227 if (mDocument) {
9228 nsIDocument* displayDoc = mDocument->GetDisplayDocument();
9229 if (displayDoc) {
9230 // Ok, we're an external resource document -- we need to use our display
9231 // document's docshell to determine "IsActive" status, since we lack
9232 // a container.
9233 NS_ABORT_IF_FALSE(!container,
9234 "external resource doc shouldn't have "
9235 "its own container");
9237 nsIPresShell* displayPresShell = displayDoc->GetShell();
9238 if (displayPresShell) {
9239 container = displayPresShell->GetPresContext()->GetContainer();
9244 nsCOMPtr<nsIDocShell> docshell(do_QueryInterface(container));
9245 if (docshell) {
9246 PRBool isActive;
9247 nsresult rv = docshell->GetIsActive(&isActive);
9248 if (NS_SUCCEEDED(rv))
9249 SetIsActive(isActive);
9253 // Helper for propagating mIsActive changes to external resources
9254 static PRBool
9255 SetExternalResourceIsActive(nsIDocument* aDocument, void* aClosure)
9257 nsIPresShell* shell = aDocument->GetShell();
9258 if (shell) {
9259 shell->SetIsActive(*static_cast<PRBool*>(aClosure));
9261 return PR_TRUE;
9264 nsresult
9265 PresShell::SetIsActive(PRBool aIsActive)
9267 mIsActive = aIsActive;
9268 nsPresContext* presContext = GetPresContext();
9269 if (presContext &&
9270 presContext->RefreshDriver()->PresContext() == presContext) {
9271 presContext->RefreshDriver()->SetThrottled(!mIsActive);
9274 // Propagate state-change to my resource documents' PresShells
9275 if (mDocument) {
9276 mDocument->EnumerateExternalResources(SetExternalResourceIsActive,
9277 &aIsActive);
9279 return UpdateImageLockingState();
9283 * Determines the current image locking state. Called when one of the
9284 * dependent factors changes.
9286 nsresult
9287 PresShell::UpdateImageLockingState()
9289 // We're locked if we're both thawed and active.
9290 return mDocument->SetImageLockingState(!mFrozen && mIsActive);