2007-03-22 10:30:00 -07:00
|
|
|
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-
|
|
|
|
* vim: set ts=2 sw=2 et tw=78:
|
|
|
|
* ***** BEGIN LICENSE BLOCK *****
|
|
|
|
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
|
|
|
|
*
|
|
|
|
* The contents of this file are subject to the Mozilla Public License Version
|
|
|
|
* 1.1 (the "License"); you may not use this file except in compliance with
|
|
|
|
* the License. You may obtain a copy of the License at
|
|
|
|
* http://www.mozilla.org/MPL/
|
|
|
|
*
|
|
|
|
* Software distributed under the License is distributed on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
|
|
|
|
* for the specific language governing rights and limitations under the
|
|
|
|
* License.
|
|
|
|
*
|
|
|
|
* The Original Code is mozilla.org code.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is
|
|
|
|
* Netscape Communications Corporation.
|
|
|
|
* Portions created by the Initial Developer are Copyright (C) 1998
|
|
|
|
* the Initial Developer. All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s):
|
|
|
|
* Steve Clark <buster@netscape.com>
|
2007-08-22 04:13:46 -07:00
|
|
|
* HÃ¥kan Waara <hwaara@chello.se>
|
2007-03-22 10:30:00 -07:00
|
|
|
* Dan Rosen <dr@netscape.com>
|
|
|
|
* Daniel Glazman <glazman@netscape.com>
|
|
|
|
* Mats Palmgren <mats.palmgren@bredband.net>
|
|
|
|
*
|
|
|
|
* Alternatively, the contents of this file may be used under the terms of
|
|
|
|
* either of the GNU General Public License Version 2 or later (the "GPL"),
|
|
|
|
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
|
|
|
|
* in which case the provisions of the GPL or the LGPL are applicable instead
|
|
|
|
* of those above. If you wish to allow use of your version of this file only
|
|
|
|
* under the terms of either the GPL or the LGPL, and not to allow others to
|
|
|
|
* use your version of this file under the terms of the MPL, indicate your
|
|
|
|
* decision by deleting the provisions above and replace them with the notice
|
|
|
|
* and other provisions required by the GPL or the LGPL. If you do not delete
|
|
|
|
* the provisions above, a recipient may use your version of this file under
|
|
|
|
* the terms of any one of the MPL, the GPL or the LGPL.
|
|
|
|
*
|
|
|
|
* ***** END LICENSE BLOCK *****
|
|
|
|
*
|
|
|
|
* This Original Code has been modified by IBM Corporation.
|
|
|
|
* Modifications made by IBM described herein are
|
|
|
|
* Copyright (c) International Business Machines
|
|
|
|
* Corporation, 2000
|
|
|
|
*
|
|
|
|
* Modifications to Mozilla code or documentation
|
|
|
|
* identified per MPL Section 3.3
|
|
|
|
*
|
|
|
|
* Date Modified by Description of modification
|
|
|
|
* 05/03/2000 IBM Corp. Observer events for reflow states
|
|
|
|
*/
|
|
|
|
|
|
|
|
/* a presentation of a document, part 2 */
|
|
|
|
|
|
|
|
#define PL_ARENA_CONST_ALIGN_MASK 3
|
|
|
|
|
|
|
|
#include "nsIPresShell.h"
|
|
|
|
#include "nsPresContext.h"
|
|
|
|
#include "nsIContent.h"
|
|
|
|
#include "nsIDocument.h"
|
|
|
|
#include "nsIDOMXULDocument.h"
|
|
|
|
#include "nsStubDocumentObserver.h"
|
|
|
|
#include "nsStyleSet.h"
|
|
|
|
#include "nsICSSStyleSheet.h" // XXX for UA sheet loading hack, can this go away please?
|
|
|
|
#include "nsIDOMCSSStyleSheet.h" // for Pref-related rule management (bugs 22963,20760,31816)
|
|
|
|
#include "nsINameSpaceManager.h" // for Pref-related rule management (bugs 22963,20760,31816)
|
|
|
|
#include "nsIServiceManager.h"
|
|
|
|
#include "nsFrame.h"
|
|
|
|
#include "nsIViewManager.h"
|
|
|
|
#include "nsCRT.h"
|
|
|
|
#include "nsCRTGlue.h"
|
|
|
|
#include "prlog.h"
|
|
|
|
#include "prmem.h"
|
|
|
|
#include "prprf.h"
|
|
|
|
#include "prinrval.h"
|
2009-02-03 06:42:18 -08:00
|
|
|
#include "nsTArray.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsCOMArray.h"
|
|
|
|
#include "nsHashtable.h"
|
|
|
|
#include "nsIViewObserver.h"
|
|
|
|
#include "nsContainerFrame.h"
|
|
|
|
#include "nsIDeviceContext.h"
|
|
|
|
#include "nsEventStateManager.h"
|
|
|
|
#include "nsDOMEvent.h"
|
|
|
|
#include "nsGUIEvent.h"
|
|
|
|
#include "nsHTMLParts.h"
|
|
|
|
#include "nsContentUtils.h"
|
|
|
|
#include "nsISelection.h"
|
|
|
|
#include "nsISelectionController.h"
|
|
|
|
#include "nsISelectionPrivate.h"
|
|
|
|
#include "nsLayoutCID.h"
|
|
|
|
#include "nsGkAtoms.h"
|
|
|
|
#include "nsIDOMRange.h"
|
|
|
|
#include "nsIDOMDocument.h"
|
|
|
|
#include "nsIDOMNode.h"
|
|
|
|
#include "nsIDOM3Node.h"
|
|
|
|
#include "nsIDOMNodeList.h"
|
|
|
|
#include "nsIDOMElement.h"
|
|
|
|
#include "nsRange.h"
|
|
|
|
#include "nsCSSPseudoElements.h"
|
|
|
|
#include "nsCOMPtr.h"
|
|
|
|
#include "nsAutoPtr.h"
|
|
|
|
#include "nsReadableUtils.h"
|
|
|
|
#include "nsUnicharUtils.h"
|
|
|
|
#include "nsWeakReference.h"
|
|
|
|
#include "nsIPageSequenceFrame.h"
|
2008-07-16 03:52:01 -07:00
|
|
|
#include "nsCaret.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsIDOMHTMLDocument.h"
|
|
|
|
#include "nsIXPointer.h"
|
|
|
|
#include "nsIDOMXMLDocument.h"
|
|
|
|
#include "nsIScrollableView.h"
|
|
|
|
#include "nsIParser.h"
|
|
|
|
#include "nsParserCIID.h"
|
|
|
|
#include "nsFrameSelection.h"
|
|
|
|
#include "nsIDOMNSHTMLInputElement.h" //optimization for ::DoXXX commands
|
|
|
|
#include "nsIDOMNSHTMLTextAreaElement.h"
|
|
|
|
#include "nsViewsCID.h"
|
|
|
|
#include "nsFrameManager.h"
|
|
|
|
#include "nsXPCOM.h"
|
|
|
|
#include "nsISupportsPrimitives.h"
|
|
|
|
#include "nsILayoutHistoryState.h"
|
|
|
|
#include "nsILineIterator.h" // for ScrollContentIntoView
|
|
|
|
#include "nsTimer.h"
|
|
|
|
#include "nsWeakPtr.h"
|
|
|
|
#include "plarena.h"
|
|
|
|
#include "pldhash.h"
|
|
|
|
#include "nsIObserverService.h"
|
|
|
|
#include "nsIObserver.h"
|
|
|
|
#include "nsIDocShell.h" // for reflow observation
|
|
|
|
#include "nsIBaseWindow.h"
|
|
|
|
#include "nsLayoutErrors.h"
|
|
|
|
#include "nsLayoutUtils.h"
|
|
|
|
#include "nsCSSRendering.h"
|
|
|
|
#ifdef NS_DEBUG
|
|
|
|
#include "nsIFrameDebug.h"
|
|
|
|
#endif
|
|
|
|
// for |#ifdef DEBUG| code
|
|
|
|
#include "prenv.h"
|
|
|
|
#include "nsIAttribute.h"
|
|
|
|
#include "nsIGlobalHistory2.h"
|
|
|
|
#include "nsDisplayList.h"
|
|
|
|
#include "nsIRegion.h"
|
|
|
|
#include "nsRegion.h"
|
|
|
|
|
2009-05-03 18:35:33 -07:00
|
|
|
#ifdef MOZ_REFLOW_PERF
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsIRenderingContext.h"
|
|
|
|
#include "nsIFontMetrics.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#include "nsIReflowCallback.h"
|
|
|
|
|
|
|
|
#include "nsPIDOMWindow.h"
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
#include "nsFocusManager.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
#include "nsIPluginInstance.h"
|
|
|
|
#include "nsIObjectFrame.h"
|
|
|
|
#include "nsIObjectLoadingContent.h"
|
|
|
|
#include "nsNetUtil.h"
|
|
|
|
#include "nsEventDispatcher.h"
|
|
|
|
#include "nsThreadUtils.h"
|
|
|
|
#include "nsStyleSheetService.h"
|
|
|
|
#include "gfxImageSurface.h"
|
|
|
|
#include "gfxContext.h"
|
2008-07-09 01:56:02 -07:00
|
|
|
#ifdef MOZ_MEDIA
|
2008-10-28 11:48:39 -07:00
|
|
|
#include "nsHTMLMediaElement.h"
|
2008-07-09 01:56:02 -07:00
|
|
|
#endif
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Drag & Drop, Clipboard
|
|
|
|
#include "nsWidgetsCID.h"
|
|
|
|
#include "nsIClipboard.h"
|
|
|
|
#include "nsIClipboardHelper.h"
|
|
|
|
#include "nsIDocShellTreeItem.h"
|
|
|
|
#include "nsIURI.h"
|
|
|
|
#include "nsIScrollableFrame.h"
|
|
|
|
#include "prtime.h"
|
|
|
|
#include "prlong.h"
|
|
|
|
#include "nsIDragService.h"
|
|
|
|
#include "nsCopySupport.h"
|
|
|
|
#include "nsIDOMHTMLAnchorElement.h"
|
|
|
|
#include "nsIDOMHTMLAreaElement.h"
|
|
|
|
#include "nsIDOMHTMLLinkElement.h"
|
|
|
|
#include "nsITimer.h"
|
|
|
|
#ifdef ACCESSIBILITY
|
|
|
|
#include "nsIAccessibilityService.h"
|
|
|
|
#include "nsIAccessible.h"
|
|
|
|
#include "nsIAccessibleEvent.h"
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// For style data reconstruction
|
|
|
|
#include "nsStyleChangeList.h"
|
|
|
|
#include "nsCSSFrameConstructor.h"
|
|
|
|
#ifdef MOZ_XUL
|
2007-07-04 08:49:38 -07:00
|
|
|
#include "nsMenuFrame.h"
|
2008-10-27 21:47:19 -07:00
|
|
|
#include "nsTreeBodyFrame.h"
|
2009-01-17 12:50:00 -08:00
|
|
|
#include "nsIBoxObject.h"
|
|
|
|
#include "nsITreeBoxObject.h"
|
|
|
|
#include "nsMenuPopupFrame.h"
|
|
|
|
#include "nsITreeColumns.h"
|
|
|
|
#include "nsIDOMXULMultSelectCntrlEl.h"
|
|
|
|
#include "nsIDOMXULSelectCntrlItemEl.h"
|
|
|
|
#include "nsIDOMXULMenuListElement.h"
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
|
|
|
#include "nsPlaceholderFrame.h"
|
|
|
|
|
|
|
|
// Content viewer interfaces
|
|
|
|
#include "nsIContentViewer.h"
|
2007-05-22 17:11:05 -07:00
|
|
|
#include "imgIEncoder.h"
|
|
|
|
#include "gfxPlatform.h"
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#include "nsContentCID.h"
|
|
|
|
static NS_DEFINE_CID(kCSSStyleSheetCID, NS_CSS_STYLESHEET_CID);
|
|
|
|
static NS_DEFINE_IID(kRangeCID, NS_RANGE_CID);
|
|
|
|
|
2007-05-03 19:19:18 -07:00
|
|
|
PRBool nsIPresShell::gIsAccessibilityActive = PR_FALSE;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// convert a color value to a string, in the CSS format #RRGGBB
|
|
|
|
// * - initially created for bugs 31816, 20760, 22963
|
|
|
|
static void ColorToString(nscolor aColor, nsAutoString &aString);
|
|
|
|
|
|
|
|
// Class ID's
|
|
|
|
static NS_DEFINE_CID(kFrameSelectionCID, NS_FRAMESELECTION_CID);
|
|
|
|
|
|
|
|
// RangePaintInfo is used to paint ranges to offscreen buffers
|
|
|
|
struct RangePaintInfo {
|
|
|
|
nsCOMPtr<nsIRange> mRange;
|
|
|
|
nsDisplayListBuilder mBuilder;
|
|
|
|
nsDisplayList mList;
|
|
|
|
|
|
|
|
// offset of builder's reference frame to the root frame
|
|
|
|
nsPoint mRootOffset;
|
|
|
|
|
|
|
|
RangePaintInfo(nsIRange* aRange, nsIFrame* aFrame)
|
|
|
|
: mRange(aRange), mBuilder(aFrame, PR_FALSE, PR_FALSE)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
~RangePaintInfo()
|
|
|
|
{
|
|
|
|
mList.DeleteAll();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
#undef NOISY
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
|
|
|
|
#ifdef NS_DEBUG
|
|
|
|
// Set the environment variable GECKO_VERIFY_REFLOW_FLAGS to one or
|
|
|
|
// more of the following flags (comma separated) for handy debug
|
|
|
|
// output.
|
|
|
|
static PRUint32 gVerifyReflowFlags;
|
|
|
|
|
|
|
|
struct VerifyReflowFlags {
|
|
|
|
const char* name;
|
|
|
|
PRUint32 bit;
|
|
|
|
};
|
|
|
|
|
|
|
|
static const VerifyReflowFlags gFlags[] = {
|
|
|
|
{ "verify", VERIFY_REFLOW_ON },
|
|
|
|
{ "reflow", VERIFY_REFLOW_NOISY },
|
|
|
|
{ "all", VERIFY_REFLOW_ALL },
|
|
|
|
{ "list-commands", VERIFY_REFLOW_DUMP_COMMANDS },
|
|
|
|
{ "noisy-commands", VERIFY_REFLOW_NOISY_RC },
|
|
|
|
{ "really-noisy-commands", VERIFY_REFLOW_REALLY_NOISY_RC },
|
|
|
|
{ "resize", VERIFY_REFLOW_DURING_RESIZE_REFLOW },
|
|
|
|
};
|
|
|
|
|
|
|
|
#define NUM_VERIFY_REFLOW_FLAGS (sizeof(gFlags) / sizeof(gFlags[0]))
|
|
|
|
|
|
|
|
static void
|
|
|
|
ShowVerifyReflowFlags()
|
|
|
|
{
|
|
|
|
printf("Here are the available GECKO_VERIFY_REFLOW_FLAGS:\n");
|
|
|
|
const VerifyReflowFlags* flag = gFlags;
|
|
|
|
const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
|
|
|
|
while (flag < limit) {
|
|
|
|
printf(" %s\n", flag->name);
|
|
|
|
++flag;
|
|
|
|
}
|
|
|
|
printf("Note: GECKO_VERIFY_REFLOW_FLAGS is a comma separated list of flag\n");
|
|
|
|
printf("names (no whitespace)\n");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//========================================================================
|
|
|
|
//========================================================================
|
|
|
|
//========================================================================
|
|
|
|
#ifdef MOZ_REFLOW_PERF
|
|
|
|
class ReflowCountMgr;
|
|
|
|
|
|
|
|
static const char kGrandTotalsStr[] = "Grand Totals";
|
|
|
|
|
|
|
|
// Counting Class
|
|
|
|
class ReflowCounter {
|
|
|
|
public:
|
|
|
|
ReflowCounter(ReflowCountMgr * aMgr = nsnull);
|
|
|
|
~ReflowCounter();
|
|
|
|
|
|
|
|
void ClearTotals();
|
|
|
|
void DisplayTotals(const char * aStr);
|
|
|
|
void DisplayDiffTotals(const char * aStr);
|
|
|
|
void DisplayHTMLTotals(const char * aStr);
|
|
|
|
|
|
|
|
void Add() { mTotal++; }
|
|
|
|
void Add(PRUint32 aTotal) { mTotal += aTotal; }
|
|
|
|
|
|
|
|
void CalcDiffInTotals();
|
|
|
|
void SetTotalsCache();
|
|
|
|
|
|
|
|
void SetMgr(ReflowCountMgr * aMgr) { mMgr = aMgr; }
|
|
|
|
|
|
|
|
PRUint32 GetTotal() { return mTotal; }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void DisplayTotals(PRUint32 aTotal, const char * aTitle);
|
|
|
|
void DisplayHTMLTotals(PRUint32 aTotal, const char * aTitle);
|
|
|
|
|
|
|
|
PRUint32 mTotal;
|
|
|
|
PRUint32 mCacheTotal;
|
|
|
|
|
|
|
|
ReflowCountMgr * mMgr; // weak reference (don't delete)
|
|
|
|
};
|
|
|
|
|
|
|
|
// Counting Class
|
|
|
|
class IndiReflowCounter {
|
|
|
|
public:
|
|
|
|
IndiReflowCounter(ReflowCountMgr * aMgr = nsnull)
|
|
|
|
: mFrame(nsnull),
|
|
|
|
mCount(0),
|
|
|
|
mMgr(aMgr),
|
|
|
|
mCounter(aMgr),
|
|
|
|
mHasBeenOutput(PR_FALSE)
|
|
|
|
{}
|
|
|
|
virtual ~IndiReflowCounter() {}
|
|
|
|
|
|
|
|
nsAutoString mName;
|
|
|
|
nsIFrame * mFrame; // weak reference (don't delete)
|
|
|
|
PRInt32 mCount;
|
|
|
|
|
|
|
|
ReflowCountMgr * mMgr; // weak reference (don't delete)
|
|
|
|
|
|
|
|
ReflowCounter mCounter;
|
|
|
|
PRBool mHasBeenOutput;
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
//--------------------
|
|
|
|
// Manager Class
|
|
|
|
//--------------------
|
|
|
|
class ReflowCountMgr {
|
|
|
|
public:
|
|
|
|
ReflowCountMgr();
|
|
|
|
virtual ~ReflowCountMgr();
|
|
|
|
|
|
|
|
void ClearTotals();
|
|
|
|
void ClearGrandTotals();
|
|
|
|
void DisplayTotals(const char * aStr);
|
|
|
|
void DisplayHTMLTotals(const char * aStr);
|
|
|
|
void DisplayDiffsInTotals(const char * aStr);
|
|
|
|
|
|
|
|
void Add(const char * aName, nsIFrame * aFrame);
|
|
|
|
ReflowCounter * LookUp(const char * aName);
|
|
|
|
|
|
|
|
void PaintCount(const char * aName, nsIRenderingContext* aRenderingContext, nsPresContext* aPresContext, nsIFrame * aFrame, PRUint32 aColor);
|
|
|
|
|
|
|
|
FILE * GetOutFile() { return mFD; }
|
|
|
|
|
|
|
|
PLHashTable * GetIndiFrameHT() { return mIndiFrameCounts; }
|
|
|
|
|
|
|
|
void SetPresContext(nsPresContext * aPresContext) { mPresContext = aPresContext; } // weak reference
|
|
|
|
void SetPresShell(nsIPresShell* aPresShell) { mPresShell= aPresShell; } // weak reference
|
|
|
|
|
|
|
|
void SetDumpFrameCounts(PRBool aVal) { mDumpFrameCounts = aVal; }
|
|
|
|
void SetDumpFrameByFrameCounts(PRBool aVal) { mDumpFrameByFrameCounts = aVal; }
|
|
|
|
void SetPaintFrameCounts(PRBool aVal) { mPaintFrameByFrameCounts = aVal; }
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void DisplayTotals(PRUint32 aTotal, PRUint32 * aDupArray, char * aTitle);
|
|
|
|
void DisplayHTMLTotals(PRUint32 aTotal, PRUint32 * aDupArray, char * aTitle);
|
|
|
|
|
2008-10-10 08:04:34 -07:00
|
|
|
static PRIntn RemoveItems(PLHashEntry *he, PRIntn i, void *arg);
|
|
|
|
static PRIntn RemoveIndiItems(PLHashEntry *he, PRIntn i, void *arg);
|
2007-03-22 10:30:00 -07:00
|
|
|
void CleanUp();
|
|
|
|
|
|
|
|
// stdout Output Methods
|
2008-10-10 08:04:34 -07:00
|
|
|
static PRIntn DoSingleTotal(PLHashEntry *he, PRIntn i, void *arg);
|
|
|
|
static PRIntn DoSingleIndi(PLHashEntry *he, PRIntn i, void *arg);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
void DoGrandTotals();
|
|
|
|
void DoIndiTotalsTree();
|
|
|
|
|
|
|
|
// HTML Output Methods
|
2008-10-10 08:04:34 -07:00
|
|
|
static PRIntn DoSingleHTMLTotal(PLHashEntry *he, PRIntn i, void *arg);
|
2007-03-22 10:30:00 -07:00
|
|
|
void DoGrandHTMLTotals();
|
|
|
|
|
|
|
|
// Zero Out the Totals
|
2008-10-10 08:04:34 -07:00
|
|
|
static PRIntn DoClearTotals(PLHashEntry *he, PRIntn i, void *arg);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Displays the Diff Totals
|
2008-10-10 08:04:34 -07:00
|
|
|
static PRIntn DoDisplayDiffTotals(PLHashEntry *he, PRIntn i, void *arg);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
PLHashTable * mCounts;
|
|
|
|
PLHashTable * mIndiFrameCounts;
|
|
|
|
FILE * mFD;
|
|
|
|
|
|
|
|
PRBool mDumpFrameCounts;
|
|
|
|
PRBool mDumpFrameByFrameCounts;
|
|
|
|
PRBool mPaintFrameByFrameCounts;
|
|
|
|
|
|
|
|
PRBool mCycledOnce;
|
|
|
|
|
|
|
|
// Root Frame for Individual Tracking
|
|
|
|
nsPresContext * mPresContext;
|
|
|
|
nsIPresShell* mPresShell;
|
|
|
|
|
|
|
|
// ReflowCountMgr gReflowCountMgr;
|
|
|
|
};
|
|
|
|
#endif
|
|
|
|
//========================================================================
|
|
|
|
|
|
|
|
// comment out to hide caret
|
|
|
|
#define SHOW_CARET
|
|
|
|
|
|
|
|
// The upper bound on the amount of time to spend reflowing, in
|
|
|
|
// microseconds. When this bound is exceeded and reflow commands are
|
|
|
|
// still queued up, a reflow event is posted. The idea is for reflow
|
|
|
|
// to not hog the processor beyond the time specifed in
|
|
|
|
// gMaxRCProcessingTime. This data member is initialized from the
|
|
|
|
// layout.reflow.timeslice pref.
|
|
|
|
#define NS_MAX_REFLOW_TIME 1000000
|
|
|
|
static PRInt32 gMaxRCProcessingTime = -1;
|
|
|
|
|
|
|
|
// Largest chunk size we recycle
|
|
|
|
static const size_t gMaxRecycledSize = 400;
|
|
|
|
|
|
|
|
#define MARK_INCREMENT 50
|
|
|
|
#define BLOCK_INCREMENT 4044 /* a bit under 4096, for malloc overhead */
|
|
|
|
|
|
|
|
/**A block of memory that the stack will
|
|
|
|
* chop up and hand out
|
|
|
|
*/
|
|
|
|
struct StackBlock {
|
|
|
|
|
|
|
|
// a block of memory. Note that this must be first so that it will
|
|
|
|
// be aligned.
|
|
|
|
char mBlock[BLOCK_INCREMENT];
|
|
|
|
|
|
|
|
// another block of memory that would only be created
|
|
|
|
// if our stack overflowed. Yes we have the ability
|
|
|
|
// to grow on a stack overflow
|
|
|
|
StackBlock* mNext;
|
|
|
|
|
|
|
|
StackBlock() : mNext(nsnull) { }
|
|
|
|
~StackBlock() { }
|
|
|
|
};
|
|
|
|
|
|
|
|
/* we hold an array of marks. A push pushes a mark on the stack
|
|
|
|
* a pop pops it off.
|
|
|
|
*/
|
|
|
|
struct StackMark {
|
|
|
|
// the block of memory we are currently handing out chunks of
|
|
|
|
StackBlock* mBlock;
|
|
|
|
|
|
|
|
// our current position in the memory
|
|
|
|
size_t mPos;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
/* A stack arena allows a stack based interface to a block of memory.
|
|
|
|
* It should be used when you need to allocate some temporary memory that
|
|
|
|
* you will immediately return.
|
|
|
|
*/
|
|
|
|
class StackArena {
|
|
|
|
public:
|
|
|
|
StackArena();
|
|
|
|
~StackArena();
|
|
|
|
|
|
|
|
nsresult Init() { return mBlocks ? NS_OK : NS_ERROR_OUT_OF_MEMORY; }
|
|
|
|
|
|
|
|
// Memory management functions
|
|
|
|
void* Allocate(size_t aSize);
|
|
|
|
void Push();
|
|
|
|
void Pop();
|
|
|
|
|
|
|
|
private:
|
|
|
|
// our current position in memory
|
|
|
|
size_t mPos;
|
|
|
|
|
|
|
|
// a list of memory block. Usually there is only one
|
|
|
|
// but if we overrun our stack size we can get more memory.
|
|
|
|
StackBlock* mBlocks;
|
|
|
|
|
|
|
|
// the current block of memory we are passing our chucks of
|
|
|
|
StackBlock* mCurBlock;
|
|
|
|
|
|
|
|
// our stack of mark where push has been called
|
|
|
|
StackMark* mMarks;
|
|
|
|
|
|
|
|
// the current top of the mark list
|
|
|
|
PRUint32 mStackTop;
|
|
|
|
|
|
|
|
// the size of the mark array
|
|
|
|
PRUint32 mMarkLength;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
StackArena::StackArena()
|
|
|
|
{
|
|
|
|
mMarkLength = 0;
|
|
|
|
mMarks = nsnull;
|
|
|
|
|
|
|
|
// allocate our stack memory
|
|
|
|
mBlocks = new StackBlock();
|
|
|
|
mCurBlock = mBlocks;
|
|
|
|
|
|
|
|
mStackTop = 0;
|
|
|
|
mPos = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
StackArena::~StackArena()
|
|
|
|
{
|
|
|
|
// free up our data
|
|
|
|
delete[] mMarks;
|
|
|
|
while(mBlocks)
|
|
|
|
{
|
|
|
|
StackBlock* toDelete = mBlocks;
|
|
|
|
mBlocks = mBlocks->mNext;
|
|
|
|
delete toDelete;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StackArena::Push()
|
|
|
|
{
|
|
|
|
// Resize the mark array if we overrun it. Failure to allocate the
|
|
|
|
// mark array is not fatal; we just won't free to that mark. This
|
|
|
|
// allows callers not to worry about error checking.
|
|
|
|
if (mStackTop >= mMarkLength)
|
|
|
|
{
|
|
|
|
PRUint32 newLength = mStackTop + MARK_INCREMENT;
|
|
|
|
StackMark* newMarks = new StackMark[newLength];
|
|
|
|
if (newMarks) {
|
|
|
|
if (mMarkLength)
|
|
|
|
memcpy(newMarks, mMarks, sizeof(StackMark)*mMarkLength);
|
|
|
|
// Fill in any marks that we couldn't allocate during a prior call
|
|
|
|
// to Push().
|
|
|
|
for (; mMarkLength < mStackTop; ++mMarkLength) {
|
|
|
|
NS_NOTREACHED("should only hit this on out-of-memory");
|
|
|
|
newMarks[mMarkLength].mBlock = mCurBlock;
|
|
|
|
newMarks[mMarkLength].mPos = mPos;
|
|
|
|
}
|
|
|
|
delete [] mMarks;
|
|
|
|
mMarks = newMarks;
|
|
|
|
mMarkLength = newLength;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// set a mark at the top (if we can)
|
|
|
|
NS_ASSERTION(mStackTop < mMarkLength, "out of memory");
|
|
|
|
if (mStackTop < mMarkLength) {
|
|
|
|
mMarks[mStackTop].mBlock = mCurBlock;
|
|
|
|
mMarks[mStackTop].mPos = mPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
mStackTop++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void*
|
|
|
|
StackArena::Allocate(size_t aSize)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(mStackTop > 0, "Allocate called without Push");
|
|
|
|
|
|
|
|
// make sure we are aligned. Beard said 8 was safer then 4.
|
|
|
|
// Round size to multiple of 8
|
|
|
|
aSize = PR_ROUNDUP(aSize, 8);
|
|
|
|
|
|
|
|
// if the size makes the stack overflow. Grab another block for the stack
|
|
|
|
if (mPos + aSize >= BLOCK_INCREMENT)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aSize <= BLOCK_INCREMENT,"Requested memory is greater that our block size!!");
|
|
|
|
if (mCurBlock->mNext == nsnull)
|
|
|
|
mCurBlock->mNext = new StackBlock();
|
|
|
|
|
|
|
|
mCurBlock = mCurBlock->mNext;
|
|
|
|
mPos = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
// return the chunk they need.
|
|
|
|
void *result = mCurBlock->mBlock + mPos;
|
|
|
|
mPos += aSize;
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
StackArena::Pop()
|
|
|
|
{
|
|
|
|
// pop off the mark
|
|
|
|
NS_ASSERTION(mStackTop > 0, "unmatched pop");
|
|
|
|
mStackTop--;
|
|
|
|
|
|
|
|
if (mStackTop >= mMarkLength) {
|
|
|
|
// We couldn't allocate the marks array at the time of the push, so
|
|
|
|
// we don't know where we're freeing to.
|
|
|
|
NS_NOTREACHED("out of memory");
|
|
|
|
if (mStackTop == 0) {
|
|
|
|
// But we do know if we've completely pushed the stack.
|
|
|
|
mCurBlock = mBlocks;
|
|
|
|
mPos = 0;
|
|
|
|
}
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
// Mark the "freed" memory with 0xdd to help with debugging of memory
|
|
|
|
// allocation problems.
|
|
|
|
{
|
|
|
|
StackBlock *block = mMarks[mStackTop].mBlock, *block_end = mCurBlock;
|
|
|
|
size_t pos = mMarks[mStackTop].mPos;
|
|
|
|
for (; block != block_end; block = block->mNext, pos = 0) {
|
|
|
|
memset(block->mBlock + pos, 0xdd, sizeof(block->mBlock) - pos);
|
|
|
|
}
|
|
|
|
memset(block->mBlock + pos, 0xdd, mPos - pos);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
mCurBlock = mMarks[mStackTop].mBlock;
|
|
|
|
mPos = mMarks[mStackTop].mPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Uncomment this to disable the frame arena.
|
|
|
|
//#define DEBUG_TRACEMALLOC_FRAMEARENA 1
|
|
|
|
|
|
|
|
// Memory is allocated 4-byte aligned. We have recyclers for chunks up to
|
|
|
|
// 200 bytes
|
|
|
|
class FrameArena {
|
|
|
|
public:
|
|
|
|
FrameArena(PRUint32 aArenaSize = 4096);
|
|
|
|
~FrameArena();
|
|
|
|
|
|
|
|
// Memory management functions
|
|
|
|
NS_HIDDEN_(void*) AllocateFrame(size_t aSize);
|
|
|
|
NS_HIDDEN_(void) FreeFrame(size_t aSize, void* aPtr);
|
|
|
|
|
|
|
|
private:
|
|
|
|
#ifdef DEBUG
|
|
|
|
// Number of frames in the pool
|
|
|
|
PRUint32 mFrameCount;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if !defined(DEBUG_TRACEMALLOC_FRAMEARENA)
|
|
|
|
// Underlying arena pool
|
|
|
|
PLArenaPool mPool;
|
|
|
|
|
|
|
|
// The recycler array is sparse with the indices being multiples of 4,
|
|
|
|
// i.e., 0, 4, 8, 12, 16, 20, ...
|
|
|
|
void* mRecyclers[gMaxRecycledSize >> 2];
|
|
|
|
#endif
|
|
|
|
};
|
|
|
|
|
|
|
|
FrameArena::FrameArena(PRUint32 aArenaSize)
|
|
|
|
#ifdef DEBUG
|
|
|
|
: mFrameCount(0)
|
|
|
|
#endif
|
|
|
|
{
|
|
|
|
#if !defined(DEBUG_TRACEMALLOC_FRAMEARENA)
|
|
|
|
// Initialize the arena pool
|
|
|
|
PL_INIT_ARENA_POOL(&mPool, "FrameArena", aArenaSize);
|
|
|
|
|
|
|
|
// Zero out the recyclers array
|
|
|
|
memset(mRecyclers, 0, sizeof(mRecyclers));
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
FrameArena::~FrameArena()
|
|
|
|
{
|
2007-07-03 15:49:00 -07:00
|
|
|
NS_ASSERTION(mFrameCount == 0,
|
|
|
|
"Some objects allocated with AllocateFrame were not freed");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#if !defined(DEBUG_TRACEMALLOC_FRAMEARENA)
|
|
|
|
// Free the arena in the pool and finish using it
|
|
|
|
PL_FinishArenaPool(&mPool);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void*
|
|
|
|
FrameArena::AllocateFrame(size_t aSize)
|
|
|
|
{
|
|
|
|
void* result = nsnull;
|
|
|
|
|
|
|
|
#if defined(DEBUG_TRACEMALLOC_FRAMEARENA)
|
|
|
|
|
|
|
|
result = PR_Malloc(aSize);
|
|
|
|
|
|
|
|
#else
|
|
|
|
|
|
|
|
// Ensure we have correct alignment for pointers. Important for Tru64
|
|
|
|
aSize = PR_ROUNDUP(aSize, sizeof(void*));
|
|
|
|
|
|
|
|
// Check recyclers first
|
|
|
|
if (aSize < gMaxRecycledSize) {
|
|
|
|
const int index = aSize >> 2;
|
|
|
|
|
|
|
|
result = mRecyclers[index];
|
|
|
|
if (result) {
|
|
|
|
// Need to move to the next object
|
|
|
|
void* next = *((void**)result);
|
|
|
|
mRecyclers[index] = next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!result) {
|
|
|
|
// Allocate a new chunk from the arena
|
|
|
|
PL_ARENA_ALLOCATE(result, &mPool, aSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (result != nsnull)
|
|
|
|
++mFrameCount;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
FrameArena::FreeFrame(size_t aSize, void* aPtr)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
--mFrameCount;
|
|
|
|
|
|
|
|
// Mark the memory with 0xdd in DEBUG builds so that there will be
|
|
|
|
// problems if someone tries to access memory that they've freed.
|
|
|
|
memset(aPtr, 0xdd, aSize);
|
|
|
|
#endif
|
|
|
|
#if defined(DEBUG_TRACEMALLOC_FRAMEARENA)
|
|
|
|
PR_Free(aPtr);
|
|
|
|
#else
|
|
|
|
// Ensure we have correct alignment for pointers. Important for Tru64
|
|
|
|
aSize = PR_ROUNDUP(aSize, sizeof(void*));
|
|
|
|
|
|
|
|
// See if it's a size that we recycle
|
|
|
|
if (aSize < gMaxRecycledSize) {
|
|
|
|
const int index = aSize >> 2;
|
|
|
|
void* currentTop = mRecyclers[index];
|
|
|
|
mRecyclers[index] = aPtr;
|
|
|
|
*((void**)aPtr) = currentTop;
|
|
|
|
}
|
|
|
|
#ifdef DEBUG_dbaron
|
|
|
|
else {
|
|
|
|
fprintf(stderr,
|
|
|
|
"WARNING: FrameArena::FreeFrame leaking chunk of %d bytes.\n",
|
|
|
|
aSize);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
struct nsCallbackEventRequest
|
|
|
|
{
|
|
|
|
nsIReflowCallback* callback;
|
|
|
|
nsCallbackEventRequest* next;
|
|
|
|
};
|
|
|
|
|
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
class nsPresShellEventCB;
|
|
|
|
|
|
|
|
class PresShell : public nsIPresShell, public nsIViewObserver,
|
|
|
|
public nsStubDocumentObserver,
|
|
|
|
public nsISelectionController, public nsIObserver,
|
|
|
|
public nsSupportsWeakReference
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
PresShell();
|
|
|
|
|
|
|
|
NS_DECL_AND_IMPL_ZEROING_OPERATOR_NEW
|
|
|
|
|
|
|
|
// nsISupports
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
|
|
|
|
// nsIPresShell
|
|
|
|
NS_IMETHOD Init(nsIDocument* aDocument,
|
|
|
|
nsPresContext* aPresContext,
|
|
|
|
nsIViewManager* aViewManager,
|
|
|
|
nsStyleSet* aStyleSet,
|
|
|
|
nsCompatibility aCompatMode);
|
|
|
|
NS_IMETHOD Destroy();
|
|
|
|
|
|
|
|
virtual NS_HIDDEN_(void*) AllocateFrame(size_t aSize);
|
|
|
|
virtual NS_HIDDEN_(void) FreeFrame(size_t aSize, void* aFreeChunk);
|
|
|
|
|
|
|
|
// Dynamic stack memory allocation
|
|
|
|
virtual NS_HIDDEN_(void) PushStackMemory();
|
|
|
|
virtual NS_HIDDEN_(void) PopStackMemory();
|
|
|
|
virtual NS_HIDDEN_(void*) AllocateStackMemory(size_t aSize);
|
|
|
|
|
|
|
|
NS_IMETHOD SetPreferenceStyleRules(PRBool aForceReflow);
|
|
|
|
|
|
|
|
NS_IMETHOD GetSelection(SelectionType aType, nsISelection** aSelection);
|
|
|
|
virtual nsISelection* GetCurrentSelection(SelectionType aType);
|
|
|
|
|
|
|
|
NS_IMETHOD SetDisplaySelection(PRInt16 aToggle);
|
|
|
|
NS_IMETHOD GetDisplaySelection(PRInt16 *aToggle);
|
|
|
|
NS_IMETHOD ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion, PRBool aIsSynchronous);
|
|
|
|
NS_IMETHOD RepaintSelection(SelectionType aType);
|
|
|
|
|
|
|
|
NS_IMETHOD BeginObservingDocument();
|
|
|
|
NS_IMETHOD EndObservingDocument();
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
NS_IMETHOD GetDidInitialReflow(PRBool *aDidInitialReflow);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHOD InitialReflow(nscoord aWidth, nscoord aHeight);
|
|
|
|
NS_IMETHOD ResizeReflow(nscoord aWidth, nscoord aHeight);
|
|
|
|
NS_IMETHOD StyleChangeReflow();
|
|
|
|
NS_IMETHOD GetPageSequenceFrame(nsIPageSequenceFrame** aResult) const;
|
|
|
|
virtual NS_HIDDEN_(nsIFrame*) GetPrimaryFrameFor(nsIContent* aContent) const;
|
2008-01-29 20:17:57 -08:00
|
|
|
virtual NS_HIDDEN_(nsIFrame*) GetRealPrimaryFrameFor(nsIContent* aContent) const;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
NS_IMETHOD GetPlaceholderFrameFor(nsIFrame* aFrame,
|
|
|
|
nsIFrame** aPlaceholderFrame) const;
|
2007-05-06 12:16:51 -07:00
|
|
|
NS_IMETHOD FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
|
2007-05-06 15:50:21 -07:00
|
|
|
nsFrameState aBitToAdd);
|
2009-04-21 16:53:52 -07:00
|
|
|
NS_IMETHOD_(void) FrameNeedsToContinueReflow(nsIFrame *aFrame);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHOD CancelAllPendingReflows();
|
|
|
|
NS_IMETHOD IsSafeToFlush(PRBool& aIsSafeToFlush);
|
|
|
|
NS_IMETHOD FlushPendingNotifications(mozFlushType aType);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Recreates the frames for a node
|
|
|
|
*/
|
|
|
|
NS_IMETHOD RecreateFramesFor(nsIContent* aContent);
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Post a callback that should be handled after reflow has finished.
|
|
|
|
*/
|
|
|
|
NS_IMETHOD PostReflowCallback(nsIReflowCallback* aCallback);
|
|
|
|
NS_IMETHOD CancelReflowCallback(nsIReflowCallback* aCallback);
|
|
|
|
|
|
|
|
NS_IMETHOD ClearFrameRefs(nsIFrame* aFrame);
|
|
|
|
NS_IMETHOD CreateRenderingContext(nsIFrame *aFrame,
|
|
|
|
nsIRenderingContext** aContext);
|
|
|
|
NS_IMETHOD GoToAnchor(const nsAString& aAnchorName, PRBool aScroll);
|
2008-02-27 02:01:17 -08:00
|
|
|
NS_IMETHOD ScrollToAnchor();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
NS_IMETHOD ScrollContentIntoView(nsIContent* aContent,
|
|
|
|
PRIntn aVPercent,
|
2009-04-21 16:53:52 -07:00
|
|
|
PRIntn aHPercent);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
NS_IMETHOD SetIgnoreFrameDestruction(PRBool aIgnore);
|
|
|
|
NS_IMETHOD NotifyDestroyingFrame(nsIFrame* aFrame);
|
|
|
|
|
|
|
|
NS_IMETHOD DoCopy();
|
|
|
|
NS_IMETHOD GetSelectionForCopy(nsISelection** outSelection);
|
|
|
|
|
|
|
|
NS_IMETHOD GetLinkLocation(nsIDOMNode* aNode, nsAString& aLocationString);
|
|
|
|
NS_IMETHOD DoGetContents(const nsACString& aMimeType, PRUint32 aFlags, PRBool aSelectionOnly, nsAString& outValue);
|
|
|
|
|
|
|
|
NS_IMETHOD CaptureHistoryState(nsILayoutHistoryState** aLayoutHistoryState, PRBool aLeavingPage);
|
|
|
|
|
|
|
|
NS_IMETHOD IsPaintingSuppressed(PRBool* aResult);
|
|
|
|
NS_IMETHOD UnsuppressPainting();
|
|
|
|
|
|
|
|
NS_IMETHOD DisableThemeSupport();
|
|
|
|
virtual PRBool IsThemeSupportEnabled();
|
|
|
|
|
|
|
|
virtual nsresult GetAgentStyleSheets(nsCOMArray<nsIStyleSheet>& aSheets);
|
|
|
|
virtual nsresult SetAgentStyleSheets(const nsCOMArray<nsIStyleSheet>& aSheets);
|
|
|
|
|
|
|
|
virtual nsresult AddOverrideStyleSheet(nsIStyleSheet *aSheet);
|
|
|
|
virtual nsresult RemoveOverrideStyleSheet(nsIStyleSheet *aSheet);
|
|
|
|
|
|
|
|
NS_IMETHOD HandleEventWithTarget(nsEvent* aEvent, nsIFrame* aFrame,
|
|
|
|
nsIContent* aContent,
|
|
|
|
nsEventStatus* aStatus);
|
|
|
|
NS_IMETHOD GetEventTargetFrame(nsIFrame** aFrame);
|
|
|
|
NS_IMETHOD GetEventTargetContent(nsEvent* aEvent, nsIContent** aContent);
|
|
|
|
|
|
|
|
NS_IMETHOD IsReflowLocked(PRBool* aIsLocked);
|
|
|
|
|
|
|
|
virtual nsresult ReconstructFrames(void);
|
|
|
|
virtual void Freeze();
|
|
|
|
virtual void Thaw();
|
2009-03-03 12:11:14 -08:00
|
|
|
virtual void FireOrClearDelayedEvents(PRBool aFireEvents);
|
2007-09-05 01:00:40 -07:00
|
|
|
|
|
|
|
virtual nsIFrame* GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt);
|
|
|
|
|
2008-12-12 02:50:31 -08:00
|
|
|
NS_IMETHOD RenderDocument(const nsRect& aRect, PRUint32 aFlags,
|
2007-05-22 17:11:05 -07:00
|
|
|
nscolor aBackgroundColor,
|
|
|
|
gfxContext* aThebesContext);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
virtual already_AddRefed<gfxASurface> RenderNode(nsIDOMNode* aNode,
|
|
|
|
nsIRegion* aRegion,
|
2009-01-14 19:27:09 -08:00
|
|
|
nsIntPoint& aPoint,
|
|
|
|
nsIntRect* aScreenRect);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
virtual already_AddRefed<gfxASurface> RenderSelection(nsISelection* aSelection,
|
2009-01-14 19:27:09 -08:00
|
|
|
nsIntPoint& aPoint,
|
|
|
|
nsIntRect* aScreenRect);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
//nsIViewObserver interface
|
|
|
|
|
|
|
|
NS_IMETHOD Paint(nsIView *aView,
|
|
|
|
nsIRenderingContext* aRenderingContext,
|
|
|
|
const nsRegion& aDirtyRegion);
|
2009-02-22 17:10:23 -08:00
|
|
|
NS_IMETHOD PaintDefaultBackground(nsIView *aView,
|
|
|
|
nsIRenderingContext* aRenderingContext,
|
|
|
|
const nsRect& aDirtyRect);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHOD ComputeRepaintRegionForCopy(nsIView* aRootView,
|
|
|
|
nsIView* aMovingView,
|
|
|
|
nsPoint aDelta,
|
|
|
|
const nsRect& aCopyRect,
|
|
|
|
nsRegion* aRepaintRegion);
|
|
|
|
NS_IMETHOD HandleEvent(nsIView* aView,
|
|
|
|
nsGUIEvent* aEvent,
|
|
|
|
nsEventStatus* aEventStatus);
|
|
|
|
NS_IMETHOD HandleDOMEventWithTarget(nsIContent* aTargetContent,
|
|
|
|
nsEvent* aEvent,
|
|
|
|
nsEventStatus* aStatus);
|
|
|
|
NS_IMETHOD ResizeReflow(nsIView *aView, nscoord aWidth, nscoord aHeight);
|
|
|
|
NS_IMETHOD_(PRBool) IsVisible();
|
|
|
|
NS_IMETHOD_(void) WillPaint();
|
2008-09-13 02:42:11 -07:00
|
|
|
NS_IMETHOD_(void) InvalidateFrameForView(nsIView *view);
|
2008-12-07 13:58:07 -08:00
|
|
|
NS_IMETHOD_(void) DispatchSynthMouseMove(nsGUIEvent *aEvent,
|
|
|
|
PRBool aFlushOnHoverChange);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// caret handling
|
2008-07-16 03:52:01 -07:00
|
|
|
NS_IMETHOD GetCaret(nsCaret **aOutCaret);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHOD_(void) MaybeInvalidateCaretPosition();
|
|
|
|
NS_IMETHOD SetCaretEnabled(PRBool aInEnable);
|
|
|
|
NS_IMETHOD SetCaretReadOnly(PRBool aReadOnly);
|
|
|
|
NS_IMETHOD GetCaretEnabled(PRBool *aOutEnabled);
|
|
|
|
NS_IMETHOD SetCaretVisibilityDuringSelection(PRBool aVisibility);
|
2008-04-08 11:52:48 -07:00
|
|
|
NS_IMETHOD GetCaretVisible(PRBool *_retval);
|
2008-07-16 03:52:01 -07:00
|
|
|
virtual void SetCaret(nsCaret *aNewCaret);
|
2007-09-22 14:06:58 -07:00
|
|
|
virtual void RestoreCaret();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
NS_IMETHOD SetSelectionFlags(PRInt16 aInEnable);
|
|
|
|
NS_IMETHOD GetSelectionFlags(PRInt16 *aOutEnable);
|
|
|
|
|
|
|
|
// nsISelectionController
|
|
|
|
|
|
|
|
NS_IMETHOD CharacterMove(PRBool aForward, PRBool aExtend);
|
2008-10-16 00:44:32 -07:00
|
|
|
NS_IMETHOD CharacterExtendForDelete();
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHOD WordMove(PRBool aForward, PRBool aExtend);
|
|
|
|
NS_IMETHOD WordExtendForDelete(PRBool aForward);
|
|
|
|
NS_IMETHOD LineMove(PRBool aForward, PRBool aExtend);
|
|
|
|
NS_IMETHOD IntraLineMove(PRBool aForward, PRBool aExtend);
|
|
|
|
NS_IMETHOD PageMove(PRBool aForward, PRBool aExtend);
|
|
|
|
NS_IMETHOD ScrollPage(PRBool aForward);
|
|
|
|
NS_IMETHOD ScrollLine(PRBool aForward);
|
|
|
|
NS_IMETHOD ScrollHorizontal(PRBool aLeft);
|
|
|
|
NS_IMETHOD CompleteScroll(PRBool aForward);
|
|
|
|
NS_IMETHOD CompleteMove(PRBool aForward, PRBool aExtend);
|
|
|
|
NS_IMETHOD SelectAll();
|
|
|
|
NS_IMETHOD CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, PRBool *_retval);
|
|
|
|
|
|
|
|
// nsIDocumentObserver
|
|
|
|
virtual void BeginUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType);
|
|
|
|
virtual void EndUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType);
|
|
|
|
virtual void BeginLoad(nsIDocument* aDocument);
|
|
|
|
virtual void EndLoad(nsIDocument* aDocument);
|
|
|
|
virtual void ContentStatesChanged(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContent1,
|
|
|
|
nsIContent* aContent2,
|
|
|
|
PRInt32 aStateMask);
|
|
|
|
virtual void StyleSheetAdded(nsIDocument* aDocument,
|
|
|
|
nsIStyleSheet* aStyleSheet,
|
|
|
|
PRBool aDocumentSheet);
|
|
|
|
virtual void StyleSheetRemoved(nsIDocument* aDocument,
|
|
|
|
nsIStyleSheet* aStyleSheet,
|
|
|
|
PRBool aDocumentSheet);
|
|
|
|
virtual void StyleSheetApplicableStateChanged(nsIDocument* aDocument,
|
|
|
|
nsIStyleSheet* aStyleSheet,
|
|
|
|
PRBool aApplicable);
|
|
|
|
virtual void StyleRuleChanged(nsIDocument* aDocument,
|
|
|
|
nsIStyleSheet* aStyleSheet,
|
|
|
|
nsIStyleRule* aOldStyleRule,
|
|
|
|
nsIStyleRule* aNewStyleRule);
|
|
|
|
virtual void StyleRuleAdded(nsIDocument* aDocument,
|
|
|
|
nsIStyleSheet* aStyleSheet,
|
|
|
|
nsIStyleRule* aStyleRule);
|
|
|
|
virtual void StyleRuleRemoved(nsIDocument* aDocument,
|
|
|
|
nsIStyleSheet* aStyleSheet,
|
|
|
|
nsIStyleRule* aStyleRule);
|
|
|
|
|
2007-07-12 13:05:45 -07:00
|
|
|
// nsIMutationObserver
|
|
|
|
NS_DECL_NSIMUTATIONOBSERVER_CHARACTERDATACHANGED
|
|
|
|
NS_DECL_NSIMUTATIONOBSERVER_ATTRIBUTECHANGED
|
|
|
|
NS_DECL_NSIMUTATIONOBSERVER_CONTENTAPPENDED
|
|
|
|
NS_DECL_NSIMUTATIONOBSERVER_CONTENTINSERTED
|
|
|
|
NS_DECL_NSIMUTATIONOBSERVER_CONTENTREMOVED
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_DECL_NSIOBSERVER
|
|
|
|
|
|
|
|
#ifdef MOZ_REFLOW_PERF
|
|
|
|
NS_IMETHOD DumpReflows();
|
|
|
|
NS_IMETHOD CountReflows(const char * aName, nsIFrame * aFrame);
|
|
|
|
NS_IMETHOD PaintCount(const char * aName, nsIRenderingContext* aRenderingContext, nsPresContext* aPresContext, nsIFrame * aFrame, PRUint32 aColor);
|
|
|
|
|
|
|
|
NS_IMETHOD SetPaintFrameCount(PRBool aOn);
|
|
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
virtual void ListStyleContexts(nsIFrame *aRootFrame, FILE *out,
|
|
|
|
PRInt32 aIndent = 0);
|
|
|
|
|
|
|
|
virtual void ListStyleSheets(FILE *out, PRInt32 aIndent = 0);
|
|
|
|
virtual void VerifyStyleTree();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef PR_LOGGING
|
|
|
|
static PRLogModuleInfo* gLog;
|
|
|
|
#endif
|
|
|
|
|
2009-02-12 02:44:38 -08:00
|
|
|
NS_IMETHOD DisableNonTestMouseEvents(PRBool aDisable);
|
|
|
|
|
2009-04-25 01:19:23 -07:00
|
|
|
virtual void UpdateCanvasBackground();
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
protected:
|
|
|
|
virtual ~PresShell();
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
void HandlePostedReflowCallbacks(PRBool aInterruptible);
|
2007-11-30 23:22:44 -08:00
|
|
|
void CancelPostedReflowCallbacks();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
void UnsuppressAndInvalidate();
|
|
|
|
|
|
|
|
void WillDoReflow();
|
2009-04-21 16:53:52 -07:00
|
|
|
void DidDoReflow(PRBool aInterruptible);
|
|
|
|
// ProcessReflowCommands returns whether we processed all our dirty roots
|
|
|
|
// without interruptions.
|
|
|
|
PRBool ProcessReflowCommands(PRBool aInterruptible);
|
2007-03-22 10:30:00 -07:00
|
|
|
void ClearReflowEventStatus();
|
2009-06-12 18:28:41 -07:00
|
|
|
// PostReflowEvent checks if posting a reflow is needed, then checks if the
|
|
|
|
// last reflow was interupted. In the interupted case DoPostReflowEvent is
|
|
|
|
// called off a timer, otherwise it is called directly.
|
2007-03-22 10:30:00 -07:00
|
|
|
void PostReflowEvent();
|
2009-06-12 18:28:41 -07:00
|
|
|
// Actually posts a reflow event to the current thread.
|
|
|
|
void DoPostReflowEvent();
|
2009-04-21 16:53:52 -07:00
|
|
|
|
|
|
|
// DoReflow returns whether the reflow finished without interruption
|
|
|
|
PRBool DoReflow(nsIFrame* aFrame, PRBool aInterruptible);
|
2007-04-25 08:49:55 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
void DoVerifyReflow();
|
2007-04-29 23:43:03 -07:00
|
|
|
void VerifyHasDirtyRootAncestor(nsIFrame* aFrame);
|
2007-04-25 08:49:55 -07:00
|
|
|
#endif
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
// Helper for ScrollContentIntoView
|
2009-05-09 20:57:53 -07:00
|
|
|
void DoScrollContentIntoView(nsIContent* aContent,
|
|
|
|
PRIntn aVPercent,
|
|
|
|
PRIntn aHPercent);
|
2009-04-21 16:53:52 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
friend class nsPresShellEventCB;
|
|
|
|
|
|
|
|
class ReflowEvent;
|
|
|
|
friend class ReflowEvent;
|
|
|
|
|
|
|
|
class ReflowEvent : public nsRunnable {
|
|
|
|
public:
|
|
|
|
NS_DECL_NSIRUNNABLE
|
|
|
|
ReflowEvent(PresShell *aPresShell) : mPresShell(aPresShell) {
|
|
|
|
NS_ASSERTION(aPresShell, "Null parameters!");
|
|
|
|
}
|
|
|
|
void Revoke() { mPresShell = nsnull; }
|
|
|
|
private:
|
|
|
|
PresShell *mPresShell;
|
|
|
|
};
|
|
|
|
|
|
|
|
// Utility to find which view to scroll.
|
|
|
|
nsIScrollableView* GetViewToScroll(nsLayoutUtils::Direction aDirection);
|
|
|
|
|
|
|
|
PRBool mCaretEnabled;
|
|
|
|
#ifdef NS_DEBUG
|
|
|
|
nsresult CloneStyleSet(nsStyleSet* aSet, nsStyleSet** aResult);
|
|
|
|
PRBool VerifyIncrementalReflow();
|
|
|
|
PRBool mInVerifyReflow;
|
|
|
|
void ShowEventTargetDebug();
|
|
|
|
#endif
|
|
|
|
|
|
|
|
/**
|
|
|
|
* methods that manage rules that are used to implement the associated preferences
|
|
|
|
* - initially created for bugs 31816, 20760, 22963
|
|
|
|
*/
|
|
|
|
nsresult ClearPreferenceStyleRules(void);
|
|
|
|
nsresult CreatePreferenceStyleSheet(void);
|
|
|
|
nsresult SetPrefLinkRules(void);
|
|
|
|
nsresult SetPrefFocusRules(void);
|
|
|
|
nsresult SetPrefNoScriptRule();
|
|
|
|
nsresult SetPrefNoFramesRule(void);
|
|
|
|
|
|
|
|
// methods for painting a range to an offscreen buffer
|
|
|
|
|
|
|
|
// given a display list, clip the items within the list to
|
|
|
|
// the range
|
|
|
|
nsRect ClipListToRange(nsDisplayListBuilder *aBuilder,
|
|
|
|
nsDisplayList* aList,
|
2007-08-16 20:43:00 -07:00
|
|
|
nsIRange* aRange);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// create a RangePaintInfo for the range aRange containing the
|
|
|
|
// display list needed to paint the range to a surface
|
|
|
|
RangePaintInfo* CreateRangePaintInfo(nsIDOMRange* aRange,
|
|
|
|
nsRect& aSurfaceRect);
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Paint the items to a new surface and return it.
|
|
|
|
*
|
|
|
|
* aSelection - selection being painted, if any
|
|
|
|
* aRegion - clip region, if any
|
|
|
|
* aArea - area that the surface occupies, relative to the root frame
|
|
|
|
* aPoint - reference point, typically the mouse position
|
|
|
|
* aScreenRect - [out] set to the area of the screen the painted area should
|
|
|
|
* be displayed at
|
|
|
|
*/
|
|
|
|
already_AddRefed<gfxASurface>
|
|
|
|
PaintRangePaintInfo(nsTArray<nsAutoPtr<RangePaintInfo> >* aItems,
|
|
|
|
nsISelection* aSelection,
|
|
|
|
nsIRegion* aRegion,
|
|
|
|
nsRect aArea,
|
2009-01-14 19:27:09 -08:00
|
|
|
nsIntPoint& aPoint,
|
|
|
|
nsIntRect* aScreenRect);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Methods to handle changes to user and UA sheet lists that we get
|
|
|
|
* notified about.
|
|
|
|
*/
|
|
|
|
void AddUserSheet(nsISupports* aSheet);
|
|
|
|
void AddAgentSheet(nsISupports* aSheet);
|
|
|
|
void RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet);
|
|
|
|
|
2007-04-20 11:20:03 -07:00
|
|
|
// Hide a view if it is a popup
|
|
|
|
void HideViewIfPopup(nsIView* aView);
|
|
|
|
|
2007-04-25 19:33:02 -07:00
|
|
|
// Utility method to restore the root scrollframe state
|
|
|
|
void RestoreRootScrollPosition();
|
|
|
|
|
2008-01-17 21:23:44 -08:00
|
|
|
nsCOMPtr<nsICSSStyleSheet> mPrefStyleSheet; // mStyleSet owns it but we
|
|
|
|
// maintain a ref, may be null
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
PRUint32 mUpdateCount;
|
|
|
|
#endif
|
|
|
|
// reflow roots that need to be reflowed, as both a queue and a hashtable
|
2009-02-03 06:42:18 -08:00
|
|
|
nsTArray<nsIFrame*> mDirtyRoots;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
PRPackedBool mDocumentLoading;
|
|
|
|
PRPackedBool mIsReflowing;
|
|
|
|
|
|
|
|
PRPackedBool mIgnoreFrameDestruction;
|
|
|
|
PRPackedBool mHaveShutDown;
|
|
|
|
|
|
|
|
nsIFrame* mCurrentEventFrame;
|
|
|
|
nsCOMPtr<nsIContent> mCurrentEventContent;
|
2009-02-03 06:42:18 -08:00
|
|
|
nsTArray<nsIFrame*> mCurrentEventFrameStack;
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMArray<nsIContent> mCurrentEventContentStack;
|
|
|
|
|
2008-02-27 02:01:17 -08:00
|
|
|
nsCOMPtr<nsIContent> mLastAnchorScrolledTo;
|
|
|
|
nscoord mLastAnchorScrollPositionY;
|
2008-07-16 03:52:01 -07:00
|
|
|
nsRefPtr<nsCaret> mCaret;
|
|
|
|
nsRefPtr<nsCaret> mOriginalCaret;
|
2007-03-22 10:30:00 -07:00
|
|
|
PRInt16 mSelectionFlags;
|
|
|
|
FrameArena mFrameArena;
|
|
|
|
StackArena mStackArena;
|
|
|
|
nsCOMPtr<nsIDragService> mDragService;
|
|
|
|
|
|
|
|
nsRevocableEventPtr<ReflowEvent> mReflowEvent;
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
// The reflow root under which we're currently reflowing. Null when
|
|
|
|
// not in reflow.
|
|
|
|
nsIFrame* mCurrentReflowRoot;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// Set of frames that we should mark with NS_FRAME_HAS_DIRTY_CHILDREN after
|
|
|
|
// we finish reflowing mCurrentReflowRoot.
|
|
|
|
nsTHashtable< nsPtrHashKey<nsIFrame> > mFramesToDirty;
|
|
|
|
|
|
|
|
// Information needed to properly handle scrolling content into view if the
|
|
|
|
// pre-scroll reflow flush can be interrupted. mContentToScrollTo is
|
|
|
|
// non-null between the initial scroll attempt and the first time we finish
|
|
|
|
// processing all our dirty roots. mContentScrollVPosition and
|
|
|
|
// mContentScrollHPosition are only used when it's non-null.
|
|
|
|
nsCOMPtr<nsIContent> mContentToScrollTo;
|
|
|
|
PRIntn mContentScrollVPosition;
|
|
|
|
PRIntn mContentScrollHPosition;
|
|
|
|
|
2009-05-18 02:17:45 -07:00
|
|
|
class nsDelayedEvent
|
2009-03-03 12:20:11 -08:00
|
|
|
{
|
2009-05-18 02:17:45 -07:00
|
|
|
public:
|
|
|
|
virtual ~nsDelayedEvent() {};
|
|
|
|
virtual void Dispatch(PresShell* aShell) {}
|
|
|
|
// This is needed only by nsDelayedFocusBlur.
|
|
|
|
virtual PRBool Equals(nsPIDOMEventTarget* aTarget, PRUint32 aEventType)
|
|
|
|
{
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class nsDelayedInputEvent : public nsDelayedEvent
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
virtual void Dispatch(PresShell* aShell)
|
|
|
|
{
|
|
|
|
if (mEvent && mEvent->widget) {
|
|
|
|
nsCOMPtr<nsIWidget> w = mEvent->widget;
|
|
|
|
nsEventStatus status;
|
|
|
|
w->DispatchEvent(mEvent, status);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
void Init(nsInputEvent* aEvent)
|
|
|
|
{
|
|
|
|
mEvent->time = aEvent->time;
|
|
|
|
mEvent->refPoint = aEvent->refPoint;
|
|
|
|
mEvent->isShift = aEvent->isShift;
|
|
|
|
mEvent->isControl = aEvent->isControl;
|
|
|
|
mEvent->isAlt = aEvent->isAlt;
|
|
|
|
mEvent->isMeta = aEvent->isMeta;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsDelayedInputEvent()
|
|
|
|
: nsDelayedEvent(), mEvent(nsnull) {}
|
|
|
|
|
|
|
|
nsInputEvent* mEvent;
|
|
|
|
};
|
|
|
|
|
|
|
|
class nsDelayedMouseEvent : public nsDelayedInputEvent
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
nsDelayedMouseEvent(nsMouseEvent* aEvent) : nsDelayedInputEvent()
|
|
|
|
{
|
|
|
|
mEvent = new nsMouseEvent(NS_IS_TRUSTED_EVENT(aEvent),
|
|
|
|
aEvent->message,
|
|
|
|
aEvent->widget,
|
|
|
|
aEvent->reason,
|
|
|
|
aEvent->context);
|
|
|
|
if (mEvent) {
|
|
|
|
Init(aEvent);
|
|
|
|
static_cast<nsMouseEvent*>(mEvent)->clickCount = aEvent->clickCount;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~nsDelayedMouseEvent()
|
|
|
|
{
|
|
|
|
delete static_cast<nsMouseEvent*>(mEvent);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
class nsDelayedKeyEvent : public nsDelayedInputEvent
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
nsDelayedKeyEvent(nsKeyEvent* aEvent) : nsDelayedInputEvent()
|
|
|
|
{
|
|
|
|
mEvent = new nsKeyEvent(NS_IS_TRUSTED_EVENT(aEvent),
|
|
|
|
aEvent->message,
|
|
|
|
aEvent->widget);
|
|
|
|
if (mEvent) {
|
|
|
|
Init(aEvent);
|
|
|
|
static_cast<nsKeyEvent*>(mEvent)->keyCode = aEvent->keyCode;
|
|
|
|
static_cast<nsKeyEvent*>(mEvent)->charCode = aEvent->charCode;
|
|
|
|
static_cast<nsKeyEvent*>(mEvent)->alternativeCharCodes =
|
|
|
|
aEvent->alternativeCharCodes;
|
|
|
|
static_cast<nsKeyEvent*>(mEvent)->isChar = aEvent->isChar;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
virtual ~nsDelayedKeyEvent()
|
|
|
|
{
|
|
|
|
delete static_cast<nsKeyEvent*>(mEvent);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
PRPackedBool mNoDelayedMouseEvents;
|
|
|
|
PRPackedBool mNoDelayedKeyEvents;
|
|
|
|
nsTArray<nsAutoPtr<nsDelayedEvent> > mDelayedEvents;
|
2009-03-03 12:11:14 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCallbackEventRequest* mFirstCallbackEventRequest;
|
|
|
|
nsCallbackEventRequest* mLastCallbackEventRequest;
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
PRPackedBool mSuppressInterruptibleReflows;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
PRPackedBool mIsThemeSupportDisabled; // Whether or not form controls should use nsITheme in this shell.
|
|
|
|
|
|
|
|
PRPackedBool mIsDocumentGone; // We've been disconnected from the document.
|
|
|
|
PRPackedBool mPaintingSuppressed; // For all documents we initially lock down painting.
|
|
|
|
// We will refuse to paint the document until either
|
|
|
|
// (a) our timer fires or (b) all frames are constructed.
|
|
|
|
PRPackedBool mShouldUnsuppressPainting; // Indicates that it is safe to unlock painting once all pending
|
|
|
|
// reflows have been processed.
|
|
|
|
nsCOMPtr<nsITimer> mPaintSuppressionTimer; // This timer controls painting suppression. Until it fires
|
|
|
|
// or all frames are constructed, we won't paint anything but
|
|
|
|
// our <body> background and scrollbars.
|
|
|
|
#define PAINTLOCK_EVENT_DELAY 250 // 250ms. This is actually
|
|
|
|
// pref-controlled, but we use this
|
|
|
|
// value if we fail to get the pref
|
|
|
|
// for any reason.
|
|
|
|
|
|
|
|
static void sPaintSuppressionCallback(nsITimer* aTimer, void* aPresShell); // A callback for the timer.
|
|
|
|
|
2009-06-12 18:28:41 -07:00
|
|
|
// On Win32 after interupting a reflow we need to post the resume reflow
|
|
|
|
// event off a timer to avoid event starvation because posted messages are
|
|
|
|
// processed before other messages when the modal moving/sizing loop is
|
|
|
|
// running, see bug 491700 for details.
|
|
|
|
nsCOMPtr<nsITimer> mReflowContinueTimer;
|
|
|
|
static void sReflowContinueCallback(nsITimer* aTimer, void* aPresShell);
|
|
|
|
PRBool PostReflowEventOffTimer();
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
MOZ_TIMER_DECLARE(mReflowWatch) // Used for measuring time spent in reflow
|
|
|
|
MOZ_TIMER_DECLARE(mFrameCreationWatch) // Used for measuring time spent in frame creation
|
|
|
|
|
|
|
|
#ifdef MOZ_REFLOW_PERF
|
|
|
|
ReflowCountMgr * mReflowCountMgr;
|
|
|
|
#endif
|
|
|
|
|
2009-02-12 02:44:38 -08:00
|
|
|
static PRBool sDisableNonTestMouseEvents;
|
|
|
|
|
2009-03-08 12:01:02 -07:00
|
|
|
|
|
|
|
nsCOMPtr<nsIDocumentObserver> mDocumentObserverForNonDynamicContext;
|
|
|
|
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
// false if a check should be done for key/ime events that should be
|
|
|
|
// retargeted to the currently focused presshell
|
|
|
|
static PRBool sDontRetargetEvents;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
private:
|
|
|
|
|
|
|
|
PRBool InZombieDocument(nsIContent *aContent);
|
|
|
|
nsresult RetargetEventToParent(nsGUIEvent* aEvent,
|
|
|
|
nsEventStatus* aEventStatus);
|
|
|
|
|
|
|
|
//helper funcs for event handling
|
|
|
|
protected:
|
|
|
|
//protected because nsPresShellEventCB needs this.
|
|
|
|
nsIFrame* GetCurrentEventFrame();
|
|
|
|
private:
|
|
|
|
void PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent);
|
|
|
|
void PopCurrentEventInfo();
|
|
|
|
nsresult HandleEventInternal(nsEvent* aEvent, nsIView* aView,
|
|
|
|
nsEventStatus *aStatus);
|
|
|
|
nsresult HandlePositionedEvent(nsIView* aView,
|
|
|
|
nsIFrame* aTargetFrame,
|
|
|
|
nsGUIEvent* aEvent,
|
|
|
|
nsEventStatus* aEventStatus);
|
|
|
|
|
2009-01-17 12:50:00 -08:00
|
|
|
/*
|
|
|
|
* This and the next two helper methods are used to target and position the
|
|
|
|
* context menu when the keyboard shortcut is used to open it.
|
|
|
|
*
|
|
|
|
* If another menu is open, the context menu is opened relative to the
|
|
|
|
* active menuitem within the menu, or the menu itself if no item is active.
|
|
|
|
* Otherwise, if the caret is visible, the menu is opened near the caret.
|
|
|
|
* Otherwise, if a selectable list such as a listbox is focused, the
|
|
|
|
* current item within the menu is opened relative to this item.
|
|
|
|
* Otherwise, the context menu is opened at the topleft corner of the
|
|
|
|
* view.
|
|
|
|
*
|
|
|
|
* Returns true if the context menu event should fire and false if it should
|
|
|
|
* not.
|
|
|
|
*/
|
|
|
|
PRBool AdjustContextMenuKeyEvent(nsMouseEvent* aEvent);
|
|
|
|
|
|
|
|
//
|
2009-01-17 13:14:18 -08:00
|
|
|
PRBool PrepareToUseCaretPosition(nsIWidget* aEventWidget, nsIntPoint& aTargetPt);
|
2009-01-17 12:50:00 -08:00
|
|
|
|
|
|
|
// Get the selected item and coordinates in device pixels relative to root
|
|
|
|
// view for element, first ensuring the element is onscreen
|
|
|
|
void GetCurrentItemAndPositionForElement(nsIDOMElement *aCurrentEl,
|
|
|
|
nsIContent **aTargetToUse,
|
2009-01-17 13:14:18 -08:00
|
|
|
nsIntPoint& aTargetPt);
|
2009-01-17 12:50:00 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void FireResizeEvent();
|
2009-01-14 12:42:25 -08:00
|
|
|
nsRevocableEventPtr<nsRunnableMethod<PresShell> > mResizeEvent;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
typedef void (*nsPluginEnumCallback)(PresShell*, nsIContent*);
|
|
|
|
void EnumeratePlugins(nsIDOMDocument *aDocument,
|
|
|
|
const nsString &aPluginTag,
|
|
|
|
nsPluginEnumCallback aCallback);
|
|
|
|
};
|
|
|
|
|
2008-06-30 18:03:50 -07:00
|
|
|
class NS_STACK_CLASS nsPresShellEventCB : public nsDispatchingCallback
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
nsPresShellEventCB(PresShell* aPresShell) : mPresShell(aPresShell) {}
|
|
|
|
|
|
|
|
virtual void HandleEvent(nsEventChainPostVisitor& aVisitor)
|
|
|
|
{
|
|
|
|
if (aVisitor.mPresContext && aVisitor.mEvent->eventStructType != NS_EVENT) {
|
|
|
|
nsIFrame* frame = mPresShell->GetCurrentEventFrame();
|
|
|
|
if (frame) {
|
|
|
|
frame->HandleEvent(aVisitor.mPresContext,
|
|
|
|
(nsGUIEvent*) aVisitor.mEvent,
|
|
|
|
&aVisitor.mEventStatus);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsRefPtr<PresShell> mPresShell;
|
|
|
|
};
|
|
|
|
|
2009-04-22 02:35:45 -07:00
|
|
|
class nsDocumentObserverForNonDynamicPresContext : public nsStubDocumentObserver
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
nsDocumentObserverForNonDynamicPresContext(PresShell* aBaseObserver)
|
|
|
|
: mBaseObserver(aBaseObserver)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aBaseObserver, "Null document observer!");
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_DECL_ISUPPORTS
|
|
|
|
|
|
|
|
virtual void BeginUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType)
|
|
|
|
{
|
|
|
|
mBaseObserver->BeginUpdate(aDocument, aUpdateType);
|
|
|
|
}
|
|
|
|
virtual void EndUpdate(nsIDocument* aDocument, nsUpdateType aUpdateType)
|
|
|
|
{
|
|
|
|
mBaseObserver->EndUpdate(aDocument, aUpdateType);
|
|
|
|
}
|
|
|
|
virtual void BeginLoad(nsIDocument* aDocument)
|
|
|
|
{
|
|
|
|
mBaseObserver->BeginLoad(aDocument);
|
|
|
|
}
|
|
|
|
virtual void EndLoad(nsIDocument* aDocument)
|
|
|
|
{
|
|
|
|
mBaseObserver->EndLoad(aDocument);
|
|
|
|
}
|
|
|
|
virtual void ContentStatesChanged(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContent1,
|
|
|
|
nsIContent* aContent2,
|
|
|
|
PRInt32 aStateMask)
|
|
|
|
{
|
|
|
|
if ((!aContent1 || AllowMutation(aContent1)) &&
|
|
|
|
(!aContent2 || AllowMutation(aContent2))) {
|
|
|
|
mBaseObserver->ContentStatesChanged(aDocument, aContent1, aContent2,
|
|
|
|
aStateMask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// nsIMutationObserver
|
|
|
|
virtual void CharacterDataChanged(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContent,
|
|
|
|
CharacterDataChangeInfo* aInfo)
|
|
|
|
{
|
|
|
|
if (AllowMutation(aContent)) {
|
|
|
|
mBaseObserver->CharacterDataChanged(aDocument, aContent, aInfo);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
virtual void AttributeChanged(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContent,
|
|
|
|
PRInt32 aNameSpaceID,
|
|
|
|
nsIAtom* aAttribute,
|
|
|
|
PRInt32 aModType,
|
|
|
|
PRUint32 aStateMask)
|
|
|
|
{
|
|
|
|
if (AllowMutation(aContent)) {
|
|
|
|
mBaseObserver->AttributeChanged(aDocument, aContent, aNameSpaceID,
|
|
|
|
aAttribute, aModType, aStateMask);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
virtual void ContentAppended(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContainer,
|
|
|
|
PRInt32 aNewIndexInContainer)
|
|
|
|
{
|
|
|
|
if (AllowMutation(aContainer)) {
|
|
|
|
mBaseObserver->ContentAppended(aDocument, aContainer,
|
|
|
|
aNewIndexInContainer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
virtual void ContentInserted(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContainer,
|
|
|
|
nsIContent* aChild,
|
|
|
|
PRInt32 aIndexInContainer)
|
|
|
|
{
|
|
|
|
if (AllowMutation(aContainer)) {
|
|
|
|
mBaseObserver->ContentInserted(aDocument, aContainer, aChild,
|
|
|
|
aIndexInContainer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
virtual void ContentRemoved(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContainer,
|
|
|
|
nsIContent* aChild,
|
|
|
|
PRInt32 aIndexInContainer)
|
|
|
|
{
|
|
|
|
if (AllowMutation(aContainer)) {
|
|
|
|
mBaseObserver->ContentRemoved(aDocument, aContainer, aChild,
|
|
|
|
aIndexInContainer);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool AllowMutation(nsIContent* aContent) {
|
|
|
|
if(aContent && aContent->IsInDoc()) {
|
|
|
|
if (mBaseObserver->ObservesNativeAnonMutationsForPrint() &&
|
|
|
|
aContent->IsInNativeAnonymousSubtree()) {
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
// Changes to scrollbar are always ok.
|
|
|
|
nsIContent* root = aContent->GetCurrentDoc()->GetRootContent();
|
|
|
|
while (aContent && aContent->IsInNativeAnonymousSubtree()) {
|
|
|
|
nsIContent* parent = aContent->GetParent();
|
|
|
|
if (parent == root && aContent->IsNodeOfType(nsINode::eXUL)) {
|
|
|
|
nsIAtom* tag = aContent->Tag();
|
|
|
|
return tag == nsGkAtoms::scrollbar || tag == nsGkAtoms::scrollcorner;
|
|
|
|
}
|
|
|
|
aContent = parent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
protected:
|
|
|
|
nsRefPtr<PresShell> mBaseObserver;
|
|
|
|
};
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS2(nsDocumentObserverForNonDynamicPresContext,
|
|
|
|
nsIDocumentObserver,
|
|
|
|
nsIMutationObserver)
|
|
|
|
|
2009-02-12 02:44:38 -08:00
|
|
|
PRBool PresShell::sDisableNonTestMouseEvents = PR_FALSE;
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
PRBool PresShell::sDontRetargetEvents = PR_FALSE;
|
2009-02-12 02:44:38 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef PR_LOGGING
|
|
|
|
PRLogModuleInfo* PresShell::gLog;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifdef NS_DEBUG
|
|
|
|
static void
|
|
|
|
VerifyStyleTree(nsPresContext* aPresContext, nsFrameManager* aFrameManager)
|
|
|
|
{
|
|
|
|
if (nsIFrameDebug::GetVerifyStyleTreeEnable()) {
|
|
|
|
nsIFrame* rootFrame = aFrameManager->GetRootFrame();
|
|
|
|
aFrameManager->DebugVerifyStyleTree(rootFrame);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#define VERIFY_STYLE_TREE ::VerifyStyleTree(mPresContext, FrameManager())
|
|
|
|
#else
|
|
|
|
#define VERIFY_STYLE_TREE
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static PRBool gVerifyReflowEnabled;
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsIPresShell::GetVerifyReflowEnable()
|
|
|
|
{
|
|
|
|
#ifdef NS_DEBUG
|
|
|
|
static PRBool firstTime = PR_TRUE;
|
|
|
|
if (firstTime) {
|
|
|
|
firstTime = PR_FALSE;
|
|
|
|
char* flags = PR_GetEnv("GECKO_VERIFY_REFLOW_FLAGS");
|
|
|
|
if (flags) {
|
|
|
|
PRBool error = PR_FALSE;
|
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
char* comma = PL_strchr(flags, ',');
|
|
|
|
if (comma)
|
|
|
|
*comma = '\0';
|
|
|
|
|
|
|
|
PRBool found = PR_FALSE;
|
|
|
|
const VerifyReflowFlags* flag = gFlags;
|
|
|
|
const VerifyReflowFlags* limit = gFlags + NUM_VERIFY_REFLOW_FLAGS;
|
|
|
|
while (flag < limit) {
|
|
|
|
if (PL_strcasecmp(flag->name, flags) == 0) {
|
|
|
|
gVerifyReflowFlags |= flag->bit;
|
|
|
|
found = PR_TRUE;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
++flag;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (! found)
|
|
|
|
error = PR_TRUE;
|
|
|
|
|
|
|
|
if (! comma)
|
|
|
|
break;
|
|
|
|
|
|
|
|
*comma = ',';
|
|
|
|
flags = comma + 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (error)
|
|
|
|
ShowVerifyReflowFlags();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (VERIFY_REFLOW_ON & gVerifyReflowFlags) {
|
|
|
|
gVerifyReflowEnabled = PR_TRUE;
|
2007-07-07 16:52:59 -07:00
|
|
|
|
|
|
|
printf("Note: verifyreflow is enabled");
|
|
|
|
if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
|
|
|
|
printf(" (noisy)");
|
|
|
|
}
|
|
|
|
if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) {
|
|
|
|
printf(" (all)");
|
|
|
|
}
|
|
|
|
if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
|
|
|
|
printf(" (show reflow commands)");
|
|
|
|
}
|
|
|
|
if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
|
|
|
|
printf(" (noisy reflow commands)");
|
|
|
|
if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) {
|
|
|
|
printf(" (REALLY noisy reflow commands)");
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2007-07-07 16:52:59 -07:00
|
|
|
printf("\n");
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
return gVerifyReflowEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsIPresShell::SetVerifyReflowEnable(PRBool aEnabled)
|
|
|
|
{
|
|
|
|
gVerifyReflowEnabled = aEnabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32
|
|
|
|
nsIPresShell::GetVerifyReflowFlags()
|
|
|
|
{
|
|
|
|
#ifdef NS_DEBUG
|
|
|
|
return gVerifyReflowFlags;
|
|
|
|
#else
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsIPresShell::AddWeakFrame(nsWeakFrame* aWeakFrame)
|
|
|
|
{
|
|
|
|
if (aWeakFrame->GetFrame()) {
|
|
|
|
aWeakFrame->GetFrame()->AddStateBits(NS_FRAME_EXTERNAL_REFERENCE);
|
|
|
|
}
|
|
|
|
aWeakFrame->SetPreviousWeakFrame(mWeakFrames);
|
|
|
|
mWeakFrames = aWeakFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsIPresShell::RemoveWeakFrame(nsWeakFrame* aWeakFrame)
|
|
|
|
{
|
|
|
|
if (mWeakFrames == aWeakFrame) {
|
|
|
|
mWeakFrames = aWeakFrame->GetPreviousWeakFrame();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
nsWeakFrame* nextWeak = mWeakFrames;
|
|
|
|
while (nextWeak && nextWeak->GetPreviousWeakFrame() != aWeakFrame) {
|
|
|
|
nextWeak = nextWeak->GetPreviousWeakFrame();
|
|
|
|
}
|
|
|
|
if (nextWeak) {
|
|
|
|
nextWeak->SetPreviousWeakFrame(aWeakFrame->GetPreviousWeakFrame());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-11-22 12:47:33 -08:00
|
|
|
already_AddRefed<nsFrameSelection>
|
|
|
|
nsIPresShell::FrameSelection()
|
|
|
|
{
|
|
|
|
NS_IF_ADDREF(mSelection);
|
|
|
|
return mSelection;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
//----------------------------------------------------------------------
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
NS_NewPresShell(nsIPresShell** aInstancePtrResult)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(nsnull != aInstancePtrResult, "null ptr");
|
|
|
|
if (nsnull == aInstancePtrResult) {
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
|
|
|
PresShell* it = new PresShell();
|
|
|
|
if (nsnull == it) {
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
return it->QueryInterface(NS_GET_IID(nsIPresShell),
|
|
|
|
(void **) aInstancePtrResult);
|
|
|
|
}
|
|
|
|
|
|
|
|
PresShell::PresShell()
|
|
|
|
{
|
|
|
|
mSelection = nsnull;
|
|
|
|
#ifdef MOZ_REFLOW_PERF
|
|
|
|
mReflowCountMgr = new ReflowCountMgr();
|
|
|
|
mReflowCountMgr->SetPresContext(mPresContext);
|
|
|
|
mReflowCountMgr->SetPresShell(this);
|
|
|
|
#endif
|
|
|
|
#ifdef PR_LOGGING
|
|
|
|
if (! gLog)
|
|
|
|
gLog = PR_NewLogModule("PresShell");
|
|
|
|
#endif
|
|
|
|
mSelectionFlags = nsISelectionDisplay::DISPLAY_TEXT | nsISelectionDisplay::DISPLAY_IMAGES;
|
|
|
|
mIsThemeSupportDisabled = PR_FALSE;
|
|
|
|
|
|
|
|
new (this) nsFrameManager();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMPL_ISUPPORTS8(PresShell, nsIPresShell, nsIDocumentObserver,
|
|
|
|
nsIViewObserver, nsISelectionController,
|
|
|
|
nsISelectionDisplay, nsIObserver, nsISupportsWeakReference,
|
|
|
|
nsIMutationObserver)
|
|
|
|
|
|
|
|
PresShell::~PresShell()
|
|
|
|
{
|
|
|
|
if (!mHaveShutDown) {
|
|
|
|
NS_NOTREACHED("Someone did not call nsIPresShell::destroy");
|
|
|
|
Destroy();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ASSERTION(mCurrentEventContentStack.Count() == 0,
|
|
|
|
"Huh, event content left on the stack in pres shell dtor!");
|
|
|
|
NS_ASSERTION(mFirstCallbackEventRequest == nsnull &&
|
|
|
|
mLastCallbackEventRequest == nsnull,
|
|
|
|
"post-reflow queues not empty. This means we're leaking");
|
|
|
|
|
|
|
|
delete mStyleSet;
|
|
|
|
delete mFrameConstructor;
|
|
|
|
|
|
|
|
mCurrentEventContent = nsnull;
|
|
|
|
|
|
|
|
NS_IF_RELEASE(mPresContext);
|
|
|
|
NS_IF_RELEASE(mDocument);
|
2008-04-02 19:42:20 -07:00
|
|
|
NS_IF_RELEASE(mSelection);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize the presentation shell. Create view manager and style
|
|
|
|
* manager.
|
|
|
|
*/
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::Init(nsIDocument* aDocument,
|
|
|
|
nsPresContext* aPresContext,
|
|
|
|
nsIViewManager* aViewManager,
|
|
|
|
nsStyleSet* aStyleSet,
|
|
|
|
nsCompatibility aCompatMode)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(nsnull != aDocument, "null ptr");
|
|
|
|
NS_PRECONDITION(nsnull != aPresContext, "null ptr");
|
|
|
|
NS_PRECONDITION(nsnull != aViewManager, "null ptr");
|
|
|
|
nsresult result;
|
|
|
|
|
|
|
|
if ((nsnull == aDocument) || (nsnull == aPresContext) ||
|
|
|
|
(nsnull == aViewManager)) {
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
|
|
|
if (mDocument) {
|
|
|
|
NS_WARNING("PresShell double init'ed");
|
|
|
|
return NS_ERROR_ALREADY_INITIALIZED;
|
|
|
|
}
|
|
|
|
result = mStackArena.Init();
|
|
|
|
NS_ENSURE_SUCCESS(result, result);
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
if (!mFramesToDirty.Init()) {
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mDocument = aDocument;
|
|
|
|
NS_ADDREF(mDocument);
|
|
|
|
mViewManager = aViewManager;
|
|
|
|
|
|
|
|
// Create our frame constructor.
|
|
|
|
mFrameConstructor = new nsCSSFrameConstructor(mDocument, this);
|
|
|
|
NS_ENSURE_TRUE(mFrameConstructor, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
|
|
|
|
// The document viewer owns both view manager and pres shell.
|
|
|
|
mViewManager->SetViewObserver(this);
|
|
|
|
|
|
|
|
// Bind the context to the presentation shell.
|
|
|
|
mPresContext = aPresContext;
|
|
|
|
NS_ADDREF(mPresContext);
|
|
|
|
aPresContext->SetShell(this);
|
|
|
|
|
|
|
|
// Now we can initialize the style set.
|
|
|
|
result = aStyleSet->Init(aPresContext);
|
|
|
|
NS_ENSURE_SUCCESS(result, result);
|
|
|
|
|
|
|
|
// From this point on, any time we return an error we need to make
|
|
|
|
// sure to null out mStyleSet first, since an error return from this
|
|
|
|
// method will cause the caller to delete the style set, so we don't
|
|
|
|
// want to delete it in our destructor.
|
|
|
|
mStyleSet = aStyleSet;
|
|
|
|
|
|
|
|
// Notify our prescontext that it now has a compatibility mode. Note that
|
|
|
|
// this MUST happen after we set up our style set but before we create any
|
|
|
|
// frames.
|
|
|
|
mPresContext->CompatibilityModeChanged();
|
|
|
|
|
|
|
|
// setup the preference style rules (no forced reflow), and do it
|
|
|
|
// before creating any frames.
|
|
|
|
SetPreferenceStyleRules(PR_FALSE);
|
|
|
|
|
|
|
|
result = CallCreateInstance(kFrameSelectionCID, &mSelection);
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
mStyleSet = nsnull;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create and initialize the frame manager
|
|
|
|
result = FrameManager()->Init(this, mStyleSet);
|
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
NS_WARNING("Frame manager initialization failed");
|
|
|
|
mStyleSet = nsnull;
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
mSelection->Init(this, nsnull);
|
|
|
|
|
|
|
|
// Important: this has to happen after the selection has been set up
|
|
|
|
#ifdef SHOW_CARET
|
|
|
|
// make the caret
|
|
|
|
nsresult err = NS_NewCaret(getter_AddRefs(mCaret));
|
|
|
|
if (NS_SUCCEEDED(err))
|
|
|
|
{
|
|
|
|
mCaret->Init(this);
|
2007-09-22 14:06:58 -07:00
|
|
|
mOriginalCaret = mCaret;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
//SetCaretEnabled(PR_TRUE); // make it show in browser windows
|
|
|
|
#endif
|
|
|
|
//set up selection to be displayed in document
|
|
|
|
// Don't enable selection for print media
|
|
|
|
nsPresContext::nsPresContextType type = aPresContext->Type();
|
|
|
|
if (type != nsPresContext::eContext_PrintPreview &&
|
|
|
|
type != nsPresContext::eContext_Print)
|
|
|
|
SetDisplaySelection(nsISelectionController::SELECTION_DISABLED);
|
|
|
|
|
|
|
|
if (gMaxRCProcessingTime == -1) {
|
|
|
|
gMaxRCProcessingTime =
|
|
|
|
nsContentUtils::GetIntPref("layout.reflow.timeslice",
|
|
|
|
NS_MAX_REFLOW_TIME);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIObserverService> os =
|
|
|
|
do_GetService("@mozilla.org/observer-service;1", &result);
|
|
|
|
if (os) {
|
|
|
|
os->AddObserver(this, NS_LINK_VISITED_EVENT_TOPIC, PR_FALSE);
|
|
|
|
os->AddObserver(this, "agent-sheet-added", PR_FALSE);
|
|
|
|
os->AddObserver(this, "user-sheet-added", PR_FALSE);
|
|
|
|
os->AddObserver(this, "agent-sheet-removed", PR_FALSE);
|
|
|
|
os->AddObserver(this, "user-sheet-removed", PR_FALSE);
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
os->AddObserver(this, "chrome-flush-skin-caches", PR_FALSE);
|
2007-05-03 19:19:18 -07:00
|
|
|
#endif
|
|
|
|
#ifdef ACCESSIBILITY
|
|
|
|
os->AddObserver(this, "a11y-init-or-shutdown", PR_FALSE);
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// cache the drag service so we can check it during reflows
|
|
|
|
mDragService = do_GetService("@mozilla.org/widget/dragservice;1");
|
|
|
|
|
|
|
|
#ifdef MOZ_REFLOW_PERF
|
|
|
|
if (mReflowCountMgr) {
|
|
|
|
PRBool paintFrameCounts =
|
|
|
|
nsContentUtils::GetBoolPref("layout.reflow.showframecounts");
|
|
|
|
|
|
|
|
PRBool dumpFrameCounts =
|
|
|
|
nsContentUtils::GetBoolPref("layout.reflow.dumpframecounts");
|
|
|
|
|
|
|
|
PRBool dumpFrameByFrameCounts =
|
|
|
|
nsContentUtils::GetBoolPref("layout.reflow.dumpframebyframecounts");
|
|
|
|
|
|
|
|
mReflowCountMgr->SetDumpFrameCounts(dumpFrameCounts);
|
|
|
|
mReflowCountMgr->SetDumpFrameByFrameCounts(dumpFrameByFrameCounts);
|
|
|
|
mReflowCountMgr->SetPaintFrameCounts(paintFrameCounts);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::Destroy()
|
|
|
|
{
|
2009-05-14 20:08:41 -07:00
|
|
|
NS_ASSERTION(!nsContentUtils::IsSafeToRunScript(),
|
|
|
|
"destroy called on presshell while scripts not blocked");
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef MOZ_REFLOW_PERF
|
|
|
|
DumpReflows();
|
|
|
|
if (mReflowCountMgr) {
|
|
|
|
delete mReflowCountMgr;
|
|
|
|
mReflowCountMgr = nsnull;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (mHaveShutDown)
|
|
|
|
return NS_OK;
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
mContentToScrollTo = nsnull;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mPresContext) {
|
|
|
|
// We need to notify the destroying the nsPresContext to ESM for
|
|
|
|
// suppressing to use from ESM.
|
|
|
|
mPresContext->EventStateManager()->NotifyDestroyPresContext(mPresContext);
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIObserverService> os =
|
|
|
|
do_GetService("@mozilla.org/observer-service;1");
|
|
|
|
if (os) {
|
|
|
|
os->RemoveObserver(this, NS_LINK_VISITED_EVENT_TOPIC);
|
|
|
|
os->RemoveObserver(this, "agent-sheet-added");
|
|
|
|
os->RemoveObserver(this, "user-sheet-added");
|
|
|
|
os->RemoveObserver(this, "agent-sheet-removed");
|
|
|
|
os->RemoveObserver(this, "user-sheet-removed");
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
os->RemoveObserver(this, "chrome-flush-skin-caches");
|
2007-05-04 11:09:02 -07:00
|
|
|
#endif
|
|
|
|
#ifdef ACCESSIBILITY
|
|
|
|
os->RemoveObserver(this, "a11y-init-or-shutdown");
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If our paint suppression timer is still active, kill it.
|
|
|
|
if (mPaintSuppressionTimer) {
|
|
|
|
mPaintSuppressionTimer->Cancel();
|
|
|
|
mPaintSuppressionTimer = nsnull;
|
|
|
|
}
|
|
|
|
|
2009-06-12 18:28:41 -07:00
|
|
|
// Same for our reflow continuation timer
|
|
|
|
if (mReflowContinueTimer) {
|
|
|
|
mReflowContinueTimer->Cancel();
|
|
|
|
mReflowContinueTimer = nsnull;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mCaret) {
|
|
|
|
mCaret->Terminate();
|
|
|
|
mCaret = nsnull;
|
|
|
|
}
|
|
|
|
|
2008-04-02 19:42:20 -07:00
|
|
|
if (mSelection) {
|
|
|
|
mSelection->DisconnectFromPresShell();
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// release our pref style sheet, if we have one still
|
|
|
|
ClearPreferenceStyleRules();
|
|
|
|
|
|
|
|
mIsDestroying = PR_TRUE;
|
|
|
|
|
|
|
|
// We can't release all the event content in
|
|
|
|
// mCurrentEventContentStack here since there might be code on the
|
|
|
|
// stack that will release the event content too. Double release
|
|
|
|
// bad!
|
|
|
|
|
|
|
|
// The frames will be torn down, so remove them from the current
|
|
|
|
// event frame stack (since they'd be dangling references if we'd
|
|
|
|
// leave them in) and null out the mCurrentEventFrame pointer as
|
|
|
|
// well.
|
|
|
|
|
|
|
|
mCurrentEventFrame = nsnull;
|
|
|
|
|
2009-02-03 06:42:18 -08:00
|
|
|
PRInt32 i, count = mCurrentEventFrameStack.Length();
|
2007-03-22 10:30:00 -07:00
|
|
|
for (i = 0; i < count; i++) {
|
2009-02-03 06:42:18 -08:00
|
|
|
mCurrentEventFrameStack[i] = nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
if (mViewManager) {
|
|
|
|
// Clear the view manager's weak pointer back to |this| in case it
|
|
|
|
// was leaked.
|
|
|
|
mViewManager->SetViewObserver(nsnull);
|
|
|
|
mViewManager = nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
mStyleSet->BeginShutdown(mPresContext);
|
|
|
|
|
|
|
|
// This shell must be removed from the document before the frame
|
|
|
|
// hierarchy is torn down to avoid finding deleted frames through
|
|
|
|
// this presshell while the frames are being torn down
|
|
|
|
if (mDocument) {
|
|
|
|
mDocument->DeleteShell(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Revoke any pending reflow event. We need to do this and cancel
|
|
|
|
// pending reflows before we destroy the frame manager, since
|
|
|
|
// apparently frame destruction sometimes spins the event queue when
|
|
|
|
// plug-ins are involved(!).
|
|
|
|
mReflowEvent.Revoke();
|
2009-01-14 12:42:25 -08:00
|
|
|
mResizeEvent.Revoke();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
CancelAllPendingReflows();
|
2007-11-30 23:22:44 -08:00
|
|
|
CancelPostedReflowCallbacks();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Destroy the frame manager. This will destroy the frame hierarchy
|
2009-03-31 18:08:34 -07:00
|
|
|
mFrameConstructor->WillDestroyFrameTree();
|
2007-03-22 10:30:00 -07:00
|
|
|
FrameManager()->Destroy();
|
|
|
|
|
|
|
|
NS_WARN_IF_FALSE(!mWeakFrames, "Weak frames alive after destroying FrameManager");
|
|
|
|
while (mWeakFrames) {
|
|
|
|
mWeakFrames->Clear(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Let the style set do its cleanup.
|
|
|
|
mStyleSet->Shutdown(mPresContext);
|
|
|
|
|
|
|
|
if (mPresContext) {
|
|
|
|
// Clear out the prescontext's property table -- since our frame tree is
|
|
|
|
// now dead, we shouldn't be looking up any more properties in that table.
|
|
|
|
// We want to do this before we call SetShell() on the prescontext, so
|
|
|
|
// property destructors can usefully call GetPresShell() on the
|
|
|
|
// prescontext.
|
|
|
|
mPresContext->PropertyTable()->DeleteAllProperties();
|
|
|
|
|
|
|
|
// We hold a reference to the pres context, and it holds a weak link back
|
|
|
|
// to us. To avoid the pres context having a dangling reference, set its
|
|
|
|
// pres shell to NULL
|
|
|
|
mPresContext->SetShell(nsnull);
|
|
|
|
|
|
|
|
// Clear the link handler (weak reference) as well
|
|
|
|
mPresContext->SetLinkHandler(nsnull);
|
|
|
|
}
|
|
|
|
|
|
|
|
mHaveShutDown = PR_TRUE;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dynamic stack memory allocation
|
|
|
|
/* virtual */ void
|
|
|
|
PresShell::PushStackMemory()
|
|
|
|
{
|
|
|
|
mStackArena.Push();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* virtual */ void
|
|
|
|
PresShell::PopStackMemory()
|
|
|
|
{
|
|
|
|
mStackArena.Pop();
|
|
|
|
}
|
|
|
|
|
|
|
|
/* virtual */ void*
|
|
|
|
PresShell::AllocateStackMemory(size_t aSize)
|
|
|
|
{
|
|
|
|
return mStackArena.Allocate(aSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::FreeFrame(size_t aSize, void* aPtr)
|
|
|
|
{
|
|
|
|
mFrameArena.FreeFrame(aSize, aPtr);
|
|
|
|
}
|
|
|
|
|
|
|
|
void*
|
|
|
|
PresShell::AllocateFrame(size_t aSize)
|
|
|
|
{
|
|
|
|
return mFrameArena.AllocateFrame(aSize);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsIPresShell::SetAuthorStyleDisabled(PRBool aStyleDisabled)
|
|
|
|
{
|
|
|
|
if (aStyleDisabled != mStyleSet->GetAuthorStyleDisabled()) {
|
|
|
|
mStyleSet->SetAuthorStyleDisabled(aStyleDisabled);
|
|
|
|
ReconstructStyleData();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
nsIPresShell::GetAuthorStyleDisabled()
|
|
|
|
{
|
|
|
|
return mStyleSet->GetAuthorStyleDisabled();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::SetPreferenceStyleRules(PRBool aForceReflow)
|
|
|
|
{
|
|
|
|
if (!mDocument) {
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsPIDOMWindow *window = mDocument->GetWindow();
|
|
|
|
|
|
|
|
// If the document doesn't have a window there's no need to notify
|
|
|
|
// its presshell about changes to preferences since the document is
|
|
|
|
// in a state where it doesn't matter any more (see
|
|
|
|
// DocumentViewerImpl::Close()).
|
|
|
|
|
|
|
|
if (!window) {
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_PRECONDITION(mPresContext, "presContext cannot be null");
|
|
|
|
if (mPresContext) {
|
|
|
|
// first, make sure this is not a chrome shell
|
2008-09-28 12:14:28 -07:00
|
|
|
if (nsContentUtils::IsInChromeDocshell(mDocument)) {
|
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2008-09-28 12:14:28 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef DEBUG_attinasi
|
2008-09-28 12:14:28 -07:00
|
|
|
printf("Setting Preference Style Rules:\n");
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
2008-09-28 12:14:28 -07:00
|
|
|
// if here, we need to create rules for the prefs
|
|
|
|
// - this includes the background-color, the text-color,
|
|
|
|
// the link color, the visited link color and the link-underlining
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-09-28 12:14:28 -07:00
|
|
|
// first clear any exising rules
|
|
|
|
nsresult result = ClearPreferenceStyleRules();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-09-28 12:14:28 -07:00
|
|
|
// now the link rules (must come after the color rules, or links will not be correct color!)
|
|
|
|
// XXX - when there is both an override and agent pref stylesheet this won't matter,
|
|
|
|
// as the color rules will be overrides and the links rules will be agent
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = SetPrefLinkRules();
|
|
|
|
}
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = SetPrefFocusRules();
|
|
|
|
}
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = SetPrefNoScriptRule();
|
|
|
|
}
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
result = SetPrefNoFramesRule();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
#ifdef DEBUG_attinasi
|
|
|
|
printf( "Preference Style Rules set: error=%ld\n", (long)result);
|
2007-07-25 10:14:13 -07:00
|
|
|
#endif
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-07-25 10:14:13 -07:00
|
|
|
// Note that this method never needs to force any calculation; the caller
|
|
|
|
// will recalculate style if needed
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PresShell::ClearPreferenceStyleRules(void)
|
|
|
|
{
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
if (mPrefStyleSheet) {
|
|
|
|
NS_ASSERTION(mStyleSet, "null styleset entirely unexpected!");
|
|
|
|
if (mStyleSet) {
|
|
|
|
// remove the sheet from the styleset:
|
|
|
|
// - note that we have to check for success by comparing the count before and after...
|
|
|
|
#ifdef NS_DEBUG
|
|
|
|
PRInt32 numBefore = mStyleSet->SheetCount(nsStyleSet::eUserSheet);
|
|
|
|
NS_ASSERTION(numBefore > 0, "no user stylesheets in styleset, but we have one!");
|
|
|
|
#endif
|
|
|
|
mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet);
|
|
|
|
|
|
|
|
#ifdef DEBUG_attinasi
|
|
|
|
NS_ASSERTION((numBefore - 1) == mStyleSet->GetNumberOfUserStyleSheets(),
|
|
|
|
"Pref stylesheet was not removed");
|
|
|
|
printf("PrefStyleSheet removed\n");
|
|
|
|
#endif
|
|
|
|
// clear the sheet pointer: it is strictly historical now
|
2008-01-17 21:23:44 -08:00
|
|
|
mPrefStyleSheet = nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PresShell::CreatePreferenceStyleSheet(void)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(!mPrefStyleSheet, "prefStyleSheet already exists");
|
2008-01-17 21:23:44 -08:00
|
|
|
nsresult result;
|
|
|
|
mPrefStyleSheet = do_CreateInstance(kCSSStyleSheetCID, &result);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
NS_ASSERTION(mPrefStyleSheet, "null but no error");
|
|
|
|
nsCOMPtr<nsIURI> uri;
|
|
|
|
result = NS_NewURI(getter_AddRefs(uri), "about:PreferenceStyleSheet", nsnull);
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
NS_ASSERTION(uri, "null but no error");
|
2008-02-13 13:47:22 -08:00
|
|
|
result = mPrefStyleSheet->SetURIs(uri, uri, uri);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
mPrefStyleSheet->SetComplete();
|
2008-01-17 21:23:44 -08:00
|
|
|
PRUint32 index;
|
|
|
|
result =
|
|
|
|
mPrefStyleSheet->InsertRuleInternal(NS_LITERAL_STRING("@namespace url(http://www.w3.org/1999/xhtml);"),
|
|
|
|
0, &index);
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
mStyleSet->AppendStyleSheet(nsStyleSet::eUserSheet, mPrefStyleSheet);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_attinasi
|
|
|
|
printf("CreatePrefStyleSheet completed: error=%ld\n",(long)result);
|
|
|
|
#endif
|
|
|
|
|
2008-01-17 21:23:44 -08:00
|
|
|
if (NS_FAILED(result)) {
|
|
|
|
mPrefStyleSheet = nsnull;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
// XXX We want these after the @namespace rule. Does order matter
|
|
|
|
// for these rules, or can we call nsICSSStyleRule::StyleRuleCount()
|
|
|
|
// and just "append"?
|
|
|
|
static PRUint32 sInsertPrefSheetRulesAt = 1;
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
PresShell::SetPrefNoScriptRule()
|
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
|
|
|
// also handle the case where print is done from print preview
|
|
|
|
// see bug #342439 for more details
|
|
|
|
PRBool scriptEnabled = mDocument->IsScriptEnabled() ||
|
|
|
|
((mPresContext->Type() == nsPresContext::eContext_PrintPreview ||
|
|
|
|
mPresContext->Type() == nsPresContext::eContext_Print) &&
|
|
|
|
NS_PTR_TO_INT32(mDocument->GetProperty(
|
2009-03-10 12:30:50 -07:00
|
|
|
nsGkAtoms::scriptEnabledBeforePrintOrPreview)));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (scriptEnabled) {
|
|
|
|
if (!mPrefStyleSheet) {
|
|
|
|
rv = CreatePreferenceStyleSheet();
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
2008-01-17 21:23:44 -08:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
PRUint32 index = 0;
|
2008-01-17 21:23:44 -08:00
|
|
|
mPrefStyleSheet->
|
|
|
|
InsertRuleInternal(NS_LITERAL_STRING("noscript{display:none!important}"),
|
|
|
|
sInsertPrefSheetRulesAt, &index);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PresShell::SetPrefNoFramesRule(void)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(mPresContext,"null prescontext not allowed");
|
|
|
|
if (!mPresContext) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
|
|
|
if (!mPrefStyleSheet) {
|
|
|
|
rv = CreatePreferenceStyleSheet();
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
|
|
|
|
|
|
|
|
PRBool allowSubframes = PR_TRUE;
|
|
|
|
nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
|
|
|
|
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
|
|
|
|
if (docShell) {
|
|
|
|
docShell->GetAllowSubframes(&allowSubframes);
|
|
|
|
}
|
|
|
|
if (!allowSubframes) {
|
|
|
|
PRUint32 index = 0;
|
2008-01-17 21:23:44 -08:00
|
|
|
rv = mPrefStyleSheet->
|
|
|
|
InsertRuleInternal(NS_LITERAL_STRING("noframes{display:block}"),
|
|
|
|
sInsertPrefSheetRulesAt, &index);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2008-01-17 21:23:44 -08:00
|
|
|
rv = mPrefStyleSheet->
|
|
|
|
InsertRuleInternal(NS_LITERAL_STRING("frame, frameset, iframe {display:none!important}"),
|
|
|
|
sInsertPrefSheetRulesAt, &index);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PresShell::SetPrefLinkRules(void)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(mPresContext,"null prescontext not allowed");
|
|
|
|
if (!mPresContext) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
|
|
|
if (!mPrefStyleSheet) {
|
|
|
|
rv = CreatePreferenceStyleSheet();
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
|
|
|
|
|
|
|
|
// support default link colors:
|
|
|
|
// this means the link colors need to be overridable,
|
|
|
|
// which they are if we put them in the agent stylesheet,
|
|
|
|
// though if using an override sheet this will cause authors grief still
|
|
|
|
// In the agent stylesheet, they are !important when we are ignoring document colors
|
|
|
|
|
|
|
|
nscolor linkColor(mPresContext->DefaultLinkColor());
|
|
|
|
nscolor activeColor(mPresContext->DefaultActiveLinkColor());
|
|
|
|
nscolor visitedColor(mPresContext->DefaultVisitedLinkColor());
|
|
|
|
|
2007-05-16 14:10:31 -07:00
|
|
|
NS_NAMED_LITERAL_STRING(ruleClose, "}");
|
2007-03-22 10:30:00 -07:00
|
|
|
PRUint32 index = 0;
|
|
|
|
nsAutoString strColor;
|
|
|
|
|
|
|
|
// insert a rule to color links: '*|*:link {color: #RRGGBB [!important];}'
|
|
|
|
ColorToString(linkColor, strColor);
|
2008-01-17 21:23:44 -08:00
|
|
|
rv = mPrefStyleSheet->
|
|
|
|
InsertRuleInternal(NS_LITERAL_STRING("*|*:link{color:") +
|
|
|
|
strColor + ruleClose,
|
|
|
|
sInsertPrefSheetRulesAt, &index);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
// - visited links: '*|*:visited {color: #RRGGBB [!important];}'
|
|
|
|
ColorToString(visitedColor, strColor);
|
2008-01-17 21:23:44 -08:00
|
|
|
rv = mPrefStyleSheet->
|
|
|
|
InsertRuleInternal(NS_LITERAL_STRING("*|*:visited{color:") +
|
|
|
|
strColor + ruleClose,
|
|
|
|
sInsertPrefSheetRulesAt, &index);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
// - active links: '*|*:-moz-any-link:active {color: #RRGGBB [!important];}'
|
|
|
|
ColorToString(activeColor, strColor);
|
2008-01-17 21:23:44 -08:00
|
|
|
rv = mPrefStyleSheet->
|
|
|
|
InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link:active{color:") +
|
|
|
|
strColor + ruleClose,
|
|
|
|
sInsertPrefSheetRulesAt, &index);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
PRBool underlineLinks =
|
|
|
|
mPresContext->GetCachedBoolPref(kPresContext_UnderlineLinks);
|
|
|
|
|
|
|
|
if (underlineLinks) {
|
|
|
|
// create a rule to make underlining happen
|
|
|
|
// '*|*:-moz-any-link {text-decoration:[underline|none];}'
|
|
|
|
// no need for important, we want these to be overridable
|
|
|
|
// NOTE: these must go in the agent stylesheet or they cannot be
|
|
|
|
// overridden by authors
|
2008-01-17 21:23:44 -08:00
|
|
|
rv = mPrefStyleSheet->
|
|
|
|
InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link{text-decoration:underline}"),
|
|
|
|
sInsertPrefSheetRulesAt, &index);
|
2007-03-22 10:30:00 -07:00
|
|
|
} else {
|
2008-01-17 21:23:44 -08:00
|
|
|
rv = mPrefStyleSheet->
|
|
|
|
InsertRuleInternal(NS_LITERAL_STRING("*|*:-moz-any-link{text-decoration:none}"),
|
|
|
|
sInsertPrefSheetRulesAt, &index);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PresShell::SetPrefFocusRules(void)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(mPresContext,"null prescontext not allowed");
|
|
|
|
nsresult result = NS_OK;
|
|
|
|
|
|
|
|
if (!mPresContext)
|
|
|
|
result = NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result) && !mPrefStyleSheet)
|
|
|
|
result = CreatePreferenceStyleSheet();
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(result)) {
|
|
|
|
NS_ASSERTION(mPrefStyleSheet, "prefstylesheet should not be null");
|
|
|
|
|
2008-01-17 21:23:44 -08:00
|
|
|
if (mPresContext->GetUseFocusColors()) {
|
|
|
|
nscolor focusBackground(mPresContext->FocusBackgroundColor());
|
|
|
|
nscolor focusText(mPresContext->FocusTextColor());
|
|
|
|
|
|
|
|
// insert a rule to make focus the preferred color
|
|
|
|
PRUint32 index = 0;
|
|
|
|
nsAutoString strRule, strColor;
|
|
|
|
|
|
|
|
///////////////////////////////////////////////////////////////
|
|
|
|
// - focus: '*:focus
|
|
|
|
ColorToString(focusText,strColor);
|
|
|
|
strRule.AppendLiteral("*:focus,*:focus>font {color: ");
|
|
|
|
strRule.Append(strColor);
|
|
|
|
strRule.AppendLiteral(" !important; background-color: ");
|
|
|
|
ColorToString(focusBackground,strColor);
|
|
|
|
strRule.Append(strColor);
|
|
|
|
strRule.AppendLiteral(" !important; } ");
|
|
|
|
// insert the rules
|
|
|
|
result = mPrefStyleSheet->
|
|
|
|
InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
|
|
|
|
}
|
|
|
|
PRUint8 focusRingWidth = mPresContext->FocusRingWidth();
|
|
|
|
PRBool focusRingOnAnything = mPresContext->GetFocusRingOnAnything();
|
2008-10-08 16:28:31 -07:00
|
|
|
PRUint8 focusRingStyle = mPresContext->GetFocusRingStyle();
|
2008-01-17 21:23:44 -08:00
|
|
|
|
|
|
|
if ((NS_SUCCEEDED(result) && focusRingWidth != 1 && focusRingWidth <= 4 ) || focusRingOnAnything) {
|
|
|
|
PRUint32 index = 0;
|
|
|
|
nsAutoString strRule;
|
|
|
|
if (!focusRingOnAnything)
|
|
|
|
strRule.AppendLiteral("*|*:link:focus, *|*:visited"); // If we only want focus rings on the normal things like links
|
|
|
|
strRule.AppendLiteral(":focus {outline: "); // For example 3px dotted WindowText (maximum 4)
|
|
|
|
strRule.AppendInt(focusRingWidth);
|
2008-10-08 16:28:31 -07:00
|
|
|
if (focusRingStyle == 0) // solid
|
2008-10-28 22:11:40 -07:00
|
|
|
strRule.AppendLiteral("px solid -moz-mac-focusring !important; -moz-outline-radius: 3px; outline-offset: 1px; } ");
|
2008-10-08 16:28:31 -07:00
|
|
|
else // dotted
|
|
|
|
strRule.AppendLiteral("px dotted WindowText !important; } ");
|
2008-01-17 21:23:44 -08:00
|
|
|
// insert the rules
|
|
|
|
result = mPrefStyleSheet->
|
|
|
|
InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
|
|
|
|
NS_ENSURE_SUCCESS(result, result);
|
|
|
|
if (focusRingWidth != 1) {
|
|
|
|
// If the focus ring width is different from the default, fix buttons with rings
|
|
|
|
strRule.AssignLiteral("button::-moz-focus-inner, input[type=\"reset\"]::-moz-focus-inner,");
|
|
|
|
strRule.AppendLiteral("input[type=\"button\"]::-moz-focus-inner, ");
|
|
|
|
strRule.AppendLiteral("input[type=\"submit\"]::-moz-focus-inner { padding: 1px 2px 1px 2px; border: ");
|
2007-03-22 10:30:00 -07:00
|
|
|
strRule.AppendInt(focusRingWidth);
|
2008-10-08 16:28:31 -07:00
|
|
|
if (focusRingStyle == 0) // solid
|
|
|
|
strRule.AppendLiteral("px solid transparent !important; } ");
|
|
|
|
else
|
|
|
|
strRule.AppendLiteral("px dotted transparent !important; } ");
|
2008-01-17 21:23:44 -08:00
|
|
|
result = mPrefStyleSheet->
|
|
|
|
InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_ENSURE_SUCCESS(result, result);
|
|
|
|
|
2008-01-17 21:23:44 -08:00
|
|
|
strRule.AssignLiteral("button:focus::-moz-focus-inner, input[type=\"reset\"]:focus::-moz-focus-inner,");
|
|
|
|
strRule.AppendLiteral("input[type=\"button\"]:focus::-moz-focus-inner, input[type=\"submit\"]:focus::-moz-focus-inner {");
|
|
|
|
strRule.AppendLiteral("border-color: ButtonText !important; }");
|
|
|
|
result = mPrefStyleSheet->
|
|
|
|
InsertRuleInternal(strRule, sInsertPrefSheetRulesAt, &index);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::AddUserSheet(nsISupports* aSheet)
|
|
|
|
{
|
|
|
|
// Make sure this does what DocumentViewerImpl::CreateStyleSet does wrt
|
|
|
|
// ordering. We want this new sheet to come after all the existing stylesheet
|
|
|
|
// service sheets, but before other user sheets; see nsIStyleSheetService.idl
|
|
|
|
// for the ordering. Just remove and readd all the nsStyleSheetService
|
|
|
|
// sheets.
|
|
|
|
nsCOMPtr<nsIStyleSheetService> dummy =
|
|
|
|
do_GetService(NS_STYLESHEETSERVICE_CONTRACTID);
|
|
|
|
|
|
|
|
mStyleSet->BeginUpdate();
|
|
|
|
|
|
|
|
nsStyleSheetService *sheetService = nsStyleSheetService::gInstance;
|
|
|
|
nsCOMArray<nsIStyleSheet> & userSheets = *sheetService->UserStyleSheets();
|
|
|
|
PRInt32 i;
|
|
|
|
// Iterate forwards when removing so the searches for RemoveStyleSheet are as
|
|
|
|
// short as possible.
|
|
|
|
for (i = 0; i < userSheets.Count(); ++i) {
|
|
|
|
mStyleSet->RemoveStyleSheet(nsStyleSet::eUserSheet, userSheets[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now iterate backwards, so that the order of userSheets will be the same as
|
|
|
|
// the order of sheets from it in the style set.
|
|
|
|
for (i = userSheets.Count() - 1; i >= 0; --i) {
|
|
|
|
mStyleSet->PrependStyleSheet(nsStyleSet::eUserSheet, userSheets[i]);
|
|
|
|
}
|
|
|
|
|
|
|
|
mStyleSet->EndUpdate();
|
|
|
|
|
|
|
|
ReconstructStyleData();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::AddAgentSheet(nsISupports* aSheet)
|
|
|
|
{
|
|
|
|
// Make sure this does what DocumentViewerImpl::CreateStyleSet does
|
|
|
|
// wrt ordering.
|
|
|
|
nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet);
|
|
|
|
if (!sheet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mStyleSet->AppendStyleSheet(nsStyleSet::eAgentSheet, sheet);
|
|
|
|
ReconstructStyleData();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::RemoveSheet(nsStyleSet::sheetType aType, nsISupports* aSheet)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIStyleSheet> sheet = do_QueryInterface(aSheet);
|
|
|
|
if (!sheet) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mStyleSet->RemoveStyleSheet(aType, sheet);
|
|
|
|
ReconstructStyleData();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::SetDisplaySelection(PRInt16 aToggle)
|
|
|
|
{
|
|
|
|
mSelection->SetDisplaySelection(aToggle);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::GetDisplaySelection(PRInt16 *aToggle)
|
|
|
|
{
|
|
|
|
*aToggle = mSelection->GetDisplaySelection();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::GetSelection(SelectionType aType, nsISelection **aSelection)
|
|
|
|
{
|
|
|
|
if (!aSelection || !mSelection)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
*aSelection = mSelection->GetSelection(aType);
|
|
|
|
|
|
|
|
if (!(*aSelection))
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
|
|
|
|
NS_ADDREF(*aSelection);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsISelection*
|
|
|
|
PresShell::GetCurrentSelection(SelectionType aType)
|
|
|
|
{
|
|
|
|
if (!mSelection)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
return mSelection->GetSelection(aType);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::ScrollSelectionIntoView(SelectionType aType, SelectionRegion aRegion, PRBool aIsSynchronous)
|
|
|
|
{
|
|
|
|
if (!mSelection)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
return mSelection->ScrollSelectionIntoView(aType, aRegion, aIsSynchronous);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::RepaintSelection(SelectionType aType)
|
|
|
|
{
|
|
|
|
if (!mSelection)
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
|
|
|
|
return mSelection->RepaintSelection(aType);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make shell be a document observer
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::BeginObservingDocument()
|
|
|
|
{
|
2007-05-13 20:52:48 -07:00
|
|
|
if (mDocument && !mIsDestroying) {
|
2009-03-08 12:01:02 -07:00
|
|
|
if (mPresContext->IsDynamic()) {
|
|
|
|
mDocument->AddObserver(this);
|
|
|
|
} else {
|
|
|
|
mDocumentObserverForNonDynamicContext =
|
|
|
|
new nsDocumentObserverForNonDynamicPresContext(this);
|
|
|
|
NS_ENSURE_TRUE(mDocumentObserverForNonDynamicContext, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
mDocument->AddObserver(mDocumentObserverForNonDynamicContext);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mIsDocumentGone) {
|
|
|
|
NS_WARNING("Adding a presshell that was disconnected from the document "
|
|
|
|
"as a document observer? Sounds wrong...");
|
|
|
|
mIsDocumentGone = PR_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make shell stop being a document observer
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::EndObservingDocument()
|
|
|
|
{
|
|
|
|
// XXXbz do we need to tell the frame constructor that the document
|
|
|
|
// is gone, perhaps? Except for printing it's NOT gone, sometimes.
|
|
|
|
mIsDocumentGone = PR_TRUE;
|
|
|
|
if (mDocument) {
|
2009-03-08 12:01:02 -07:00
|
|
|
mDocument->RemoveObserver(mDocumentObserverForNonDynamicContext ?
|
|
|
|
mDocumentObserverForNonDynamicContext.get() :
|
|
|
|
this);
|
|
|
|
mDocumentObserverForNonDynamicContext = nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG_kipp
|
|
|
|
char* nsPresShell_ReflowStackPointerTop;
|
|
|
|
#endif
|
|
|
|
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::GetDidInitialReflow(PRBool *aDidInitialReflow)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
if (!aDidInitialReflow)
|
|
|
|
return NS_ERROR_FAILURE;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
*aDidInitialReflow = mDidInitialReflow;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::InitialReflow(nscoord aWidth, nscoord aHeight)
|
|
|
|
{
|
2007-05-01 10:15:08 -07:00
|
|
|
if (mIsDestroying) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-05-11 19:04:58 -07:00
|
|
|
if (!mDocument) {
|
|
|
|
// Nothing to do
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-10-10 10:44:43 -07:00
|
|
|
NS_ASSERTION(!mDidInitialReflow, "Why are we being called?");
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
|
|
|
|
mDidInitialReflow = PR_TRUE;
|
|
|
|
|
|
|
|
#ifdef NS_DEBUG
|
|
|
|
if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
|
|
|
|
if (mDocument) {
|
|
|
|
nsIURI *uri = mDocument->GetDocumentURI();
|
|
|
|
if (uri) {
|
|
|
|
nsCAutoString url;
|
|
|
|
uri->GetSpec(url);
|
|
|
|
printf("*** PresShell::InitialReflow (this=%p, url='%s')\n", (void*)this, url.get());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (mCaret)
|
|
|
|
mCaret->EraseCaret();
|
|
|
|
|
|
|
|
// XXX Do a full invalidate at the beginning so that invalidates along
|
|
|
|
// the way don't have region accumulation issues?
|
|
|
|
|
2007-05-01 10:15:08 -07:00
|
|
|
mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Get the root frame from the frame manager
|
2009-05-11 19:04:58 -07:00
|
|
|
// XXXbz it would be nice to move this somewhere else... like frame manager
|
|
|
|
// Init(), say. But we need to make sure our views are all set up by the
|
|
|
|
// time we do this!
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIFrame* rootFrame = FrameManager()->GetRootFrame();
|
2009-05-11 19:04:58 -07:00
|
|
|
NS_ASSERTION(!rootFrame, "How did that happen, exactly?");
|
|
|
|
if (!rootFrame) {
|
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
|
|
|
mFrameConstructor->BeginUpdate();
|
|
|
|
mFrameConstructor->ConstructRootFrame(&rootFrame);
|
|
|
|
FrameManager()->SetRootFrame(rootFrame);
|
|
|
|
mFrameConstructor->EndUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ENSURE_STATE(!mHaveShutDown);
|
|
|
|
|
|
|
|
if (!rootFrame) {
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIContent *root = mDocument->GetRootContent();
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (root) {
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Reset and start: Frame Creation: PresShell::InitialReflow(), this=%p\n",
|
|
|
|
(void*)this));
|
|
|
|
MOZ_TIMER_RESET(mFrameCreationWatch);
|
|
|
|
MOZ_TIMER_START(mFrameCreationWatch);
|
|
|
|
|
2008-03-14 16:08:57 -07:00
|
|
|
{
|
2008-12-20 06:28:19 -08:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2008-03-14 16:08:57 -07:00
|
|
|
mFrameConstructor->BeginUpdate();
|
|
|
|
|
|
|
|
// Have the style sheet processor construct frame for the root
|
|
|
|
// content object down
|
|
|
|
mFrameConstructor->ContentInserted(nsnull, root, 0, nsnull);
|
|
|
|
VERIFY_STYLE_TREE;
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: Frame Creation: PresShell::InitialReflow(), this=%p\n",
|
|
|
|
(void*)this));
|
|
|
|
MOZ_TIMER_STOP(mFrameCreationWatch);
|
2007-05-13 20:52:48 -07:00
|
|
|
|
2008-03-14 16:08:57 -07:00
|
|
|
// Something in mFrameConstructor->ContentInserted may have caused
|
|
|
|
// Destroy() to get called, bug 337586.
|
|
|
|
NS_ENSURE_STATE(!mHaveShutDown);
|
2008-03-10 19:18:15 -07:00
|
|
|
|
2008-03-14 16:08:57 -07:00
|
|
|
mFrameConstructor->EndUpdate();
|
|
|
|
}
|
2007-10-01 21:37:36 -07:00
|
|
|
|
2009-05-11 19:04:58 -07:00
|
|
|
// nsAutoScriptBlocker going out of scope may have killed us too
|
2008-03-14 16:08:57 -07:00
|
|
|
NS_ENSURE_STATE(!mHaveShutDown);
|
2008-03-10 19:18:15 -07:00
|
|
|
|
2007-05-13 20:52:48 -07:00
|
|
|
// Run the XBL binding constructors for any new frames we've constructed
|
|
|
|
mDocument->BindingManager()->ProcessAttachedQueue();
|
|
|
|
|
|
|
|
// Constructors may have killed us too
|
|
|
|
NS_ENSURE_STATE(!mHaveShutDown);
|
|
|
|
|
|
|
|
// Now flush out pending restyles before we actually reflow, in
|
|
|
|
// case XBL constructors changed styles somewhere.
|
2009-01-02 07:41:43 -08:00
|
|
|
{
|
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
|
|
|
mFrameConstructor->ProcessPendingRestyles();
|
|
|
|
}
|
2007-05-13 20:52:48 -07:00
|
|
|
|
|
|
|
// And that might have run _more_ XBL constructors
|
|
|
|
NS_ENSURE_STATE(!mHaveShutDown);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-05-11 19:04:58 -07:00
|
|
|
NS_ASSERTION(rootFrame, "How did that happen?");
|
2007-05-06 12:16:51 -07:00
|
|
|
|
2009-05-11 19:04:58 -07:00
|
|
|
// Note: Because the frame just got created, it has the NS_FRAME_IS_DIRTY
|
|
|
|
// bit set. Unset it so that FrameNeedsReflow() will work right.
|
|
|
|
NS_ASSERTION(!mDirtyRoots.Contains(rootFrame),
|
|
|
|
"Why is the root in mDirtyRoots already?");
|
2007-05-06 12:16:51 -07:00
|
|
|
|
2009-05-11 19:04:58 -07:00
|
|
|
rootFrame->RemoveStateBits(NS_FRAME_IS_DIRTY |
|
|
|
|
NS_FRAME_HAS_DIRTY_CHILDREN);
|
|
|
|
FrameNeedsReflow(rootFrame, eResize, NS_FRAME_IS_DIRTY);
|
|
|
|
|
|
|
|
NS_ASSERTION(mDirtyRoots.Contains(rootFrame),
|
|
|
|
"Should be in mDirtyRoots now");
|
|
|
|
NS_ASSERTION(mReflowEvent.IsPending(), "Why no reflow event pending?");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-04-25 19:33:02 -07:00
|
|
|
// Restore our root scroll position now if we're getting here after EndLoad
|
2007-05-01 10:15:08 -07:00
|
|
|
// got called, since this is our one chance to do it. Note that we need not
|
|
|
|
// have reflowed for this to work; when the scrollframe is finally reflowed
|
|
|
|
// it'll puick up the position we store in it here.
|
2007-04-25 19:33:02 -07:00
|
|
|
if (!mDocumentLoading) {
|
|
|
|
RestoreRootScrollPosition();
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// For printing, we just immediately unsuppress.
|
|
|
|
if (!mPresContext->IsPaginated()) {
|
|
|
|
// Kick off a one-shot timer based off our pref value. When this timer
|
|
|
|
// fires, if painting is still locked down, then we will go ahead and
|
|
|
|
// trigger a full invalidate and allow painting to proceed normally.
|
|
|
|
mPaintingSuppressed = PR_TRUE;
|
|
|
|
mPaintSuppressionTimer = do_CreateInstance("@mozilla.org/timer;1");
|
|
|
|
if (!mPaintSuppressionTimer)
|
|
|
|
// Uh-oh. We must be out of memory. No point in keeping painting locked down.
|
|
|
|
mPaintingSuppressed = PR_FALSE;
|
|
|
|
else {
|
|
|
|
// Initialize the timer.
|
|
|
|
|
|
|
|
// Default to PAINTLOCK_EVENT_DELAY if we can't get the pref value.
|
|
|
|
PRInt32 delay =
|
|
|
|
nsContentUtils::GetIntPref("nglayout.initialpaint.delay",
|
|
|
|
PAINTLOCK_EVENT_DELAY);
|
|
|
|
|
|
|
|
mPaintSuppressionTimer->InitWithFuncCallback(sPaintSuppressionCallback,
|
|
|
|
this, delay,
|
|
|
|
nsITimer::TYPE_ONE_SHOT);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK; //XXX this needs to be real. MMP
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::sPaintSuppressionCallback(nsITimer *aTimer, void* aPresShell)
|
|
|
|
{
|
2009-05-12 12:07:08 -07:00
|
|
|
nsRefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (self)
|
|
|
|
self->UnsuppressPainting();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::ResizeReflow(nscoord aWidth, nscoord aHeight)
|
|
|
|
{
|
2007-05-15 20:22:45 -07:00
|
|
|
NS_PRECONDITION(!mIsReflowing, "Shouldn't be in reflow here!");
|
2008-03-12 15:04:45 -07:00
|
|
|
NS_PRECONDITION(aWidth != NS_UNCONSTRAINEDSIZE,
|
|
|
|
"shouldn't use unconstrained widths anymore");
|
2007-05-15 20:22:45 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// If we don't have a root frame yet, that means we haven't had our initial
|
|
|
|
// reflow... If that's the case, and aWidth or aHeight is unconstrained,
|
|
|
|
// ignore them altogether.
|
|
|
|
nsIFrame* rootFrame = FrameManager()->GetRootFrame();
|
|
|
|
|
2008-03-12 15:04:45 -07:00
|
|
|
if (!rootFrame && aHeight == NS_UNCONSTRAINEDSIZE) {
|
|
|
|
// We can't do the work needed for SizeToContent without a root
|
|
|
|
// frame, and we want to return before setting the visible area.
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
|
|
}
|
|
|
|
|
2007-04-25 08:49:55 -07:00
|
|
|
mPresContext->SetVisibleArea(nsRect(0, 0, aWidth, aHeight));
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-04-25 08:49:55 -07:00
|
|
|
// There isn't anything useful we can do if the initial reflow hasn't happened
|
|
|
|
if (!rootFrame)
|
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-01-05 08:26:59 -08:00
|
|
|
NS_ASSERTION(mViewManager, "Must have view manager");
|
|
|
|
nsCOMPtr<nsIViewManager> viewManagerDeathGrip = mViewManager;
|
|
|
|
// Take this ref after viewManager so it'll make sure to go away first
|
|
|
|
nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
|
2008-01-28 20:10:59 -08:00
|
|
|
if (!GetPresContext()->SupressingResizeReflow())
|
|
|
|
{
|
|
|
|
nsIViewManager::UpdateViewBatch batch(mViewManager);
|
2007-04-25 18:34:28 -07:00
|
|
|
|
2008-01-28 20:10:59 -08:00
|
|
|
// Make sure style is up to date
|
2009-01-02 07:41:43 -08:00
|
|
|
{
|
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
|
|
|
mFrameConstructor->ProcessPendingRestyles();
|
|
|
|
}
|
2007-09-19 19:46:28 -07:00
|
|
|
|
2008-01-28 20:10:59 -08:00
|
|
|
if (!mIsDestroying) {
|
|
|
|
// XXX Do a full invalidate at the beginning so that invalidates along
|
|
|
|
// the way don't have region accumulation issues?
|
2007-09-19 19:46:28 -07:00
|
|
|
|
2008-03-10 19:18:15 -07:00
|
|
|
{
|
2008-12-20 06:28:19 -08:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2008-03-14 16:08:57 -07:00
|
|
|
WillDoReflow();
|
|
|
|
|
2008-01-28 20:10:59 -08:00
|
|
|
// Kick off a top-down reflow
|
|
|
|
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
|
|
|
|
|
|
|
|
mDirtyRoots.RemoveElement(rootFrame);
|
2009-04-21 16:53:52 -07:00
|
|
|
DoReflow(rootFrame, PR_TRUE);
|
2008-01-28 20:10:59 -08:00
|
|
|
}
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
DidDoReflow(PR_TRUE);
|
2007-09-20 16:28:26 -07:00
|
|
|
}
|
|
|
|
|
2008-01-28 20:10:59 -08:00
|
|
|
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
|
2007-04-25 03:44:29 -07:00
|
|
|
}
|
2007-04-25 08:49:55 -07:00
|
|
|
|
2008-03-12 15:04:45 -07:00
|
|
|
if (aHeight == NS_UNCONSTRAINEDSIZE) {
|
|
|
|
mPresContext->SetVisibleArea(
|
|
|
|
nsRect(0, 0, aWidth, rootFrame->GetRect().height));
|
|
|
|
}
|
|
|
|
|
2009-01-14 12:42:25 -08:00
|
|
|
if (!mIsDestroying && !mResizeEvent.IsPending()) {
|
|
|
|
nsRefPtr<nsRunnableMethod<PresShell> > resizeEvent =
|
|
|
|
NS_NEW_RUNNABLE_METHOD(PresShell, this, FireResizeEvent);
|
|
|
|
if (NS_SUCCEEDED(NS_DispatchToCurrentThread(resizeEvent))) {
|
|
|
|
mResizeEvent = resizeEvent;
|
|
|
|
}
|
2007-09-20 16:28:26 -07:00
|
|
|
}
|
2007-04-25 08:49:55 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK; //XXX this needs to be real. MMP
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::FireResizeEvent()
|
|
|
|
{
|
2009-01-14 12:42:25 -08:00
|
|
|
mResizeEvent.Revoke();
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mIsDocumentGone)
|
|
|
|
return;
|
|
|
|
|
|
|
|
//Send resize event from here.
|
|
|
|
nsEvent event(PR_TRUE, NS_RESIZE_EVENT);
|
|
|
|
nsEventStatus status = nsEventStatus_eIgnore;
|
|
|
|
|
|
|
|
nsPIDOMWindow *window = mDocument->GetWindow();
|
|
|
|
if (window) {
|
|
|
|
nsEventDispatcher::Dispatch(window, mPresContext, &event, nsnull, &status);
|
2008-11-02 11:44:55 -08:00
|
|
|
// |this| may now be destroyed
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::SetIgnoreFrameDestruction(PRBool aIgnore)
|
|
|
|
{
|
|
|
|
mIgnoreFrameDestruction = aIgnore;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::NotifyDestroyingFrame(nsIFrame* aFrame)
|
|
|
|
{
|
|
|
|
if (!mIgnoreFrameDestruction) {
|
|
|
|
mFrameConstructor->NotifyDestroyingFrame(aFrame);
|
|
|
|
|
2009-02-03 06:42:18 -08:00
|
|
|
for (PRInt32 idx = mDirtyRoots.Length(); idx; ) {
|
2007-05-15 20:22:45 -07:00
|
|
|
--idx;
|
|
|
|
if (mDirtyRoots[idx] == aFrame) {
|
|
|
|
mDirtyRoots.RemoveElementAt(idx);
|
|
|
|
}
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Notify the frame manager
|
|
|
|
FrameManager()->NotifyDestroyingFrame(aFrame);
|
|
|
|
|
|
|
|
// Remove frame properties
|
|
|
|
mPresContext->PropertyTable()->DeleteAllPropertiesFor(aFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// note that this can return a null caret, but NS_OK
|
2008-07-16 03:52:01 -07:00
|
|
|
NS_IMETHODIMP PresShell::GetCaret(nsCaret **outCaret)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
NS_ENSURE_ARG_POINTER(outCaret);
|
|
|
|
|
|
|
|
*outCaret = mCaret;
|
|
|
|
NS_IF_ADDREF(*outCaret);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP_(void) PresShell::MaybeInvalidateCaretPosition()
|
|
|
|
{
|
|
|
|
if (mCaret) {
|
|
|
|
mCaret->InvalidateOutsideCaret();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-07-16 03:52:01 -07:00
|
|
|
void PresShell::SetCaret(nsCaret *aNewCaret)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
mCaret = aNewCaret;
|
2007-09-22 14:06:58 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void PresShell::RestoreCaret()
|
|
|
|
{
|
|
|
|
mCaret = mOriginalCaret;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP PresShell::SetCaretEnabled(PRBool aInEnable)
|
|
|
|
{
|
2008-09-20 06:42:03 -07:00
|
|
|
PRBool oldEnabled = mCaretEnabled;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
mCaretEnabled = aInEnable;
|
|
|
|
|
|
|
|
if (mCaret && (mCaretEnabled != oldEnabled))
|
|
|
|
{
|
|
|
|
/* Don't change the caret's selection here! This was an evil side-effect of SetCaretEnabled()
|
|
|
|
nsCOMPtr<nsIDOMSelection> domSel;
|
|
|
|
if (NS_SUCCEEDED(GetSelection(nsISelectionController::SELECTION_NORMAL, getter_AddRefs(domSel))) && domSel)
|
|
|
|
mCaret->SetCaretDOMSelection(domSel);
|
|
|
|
*/
|
2008-07-16 03:52:01 -07:00
|
|
|
mCaret->SetCaretVisible(mCaretEnabled);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2008-07-16 03:52:01 -07:00
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP PresShell::SetCaretReadOnly(PRBool aReadOnly)
|
|
|
|
{
|
|
|
|
if (mCaret)
|
|
|
|
mCaret->SetCaretReadOnly(aReadOnly);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP PresShell::GetCaretEnabled(PRBool *aOutEnabled)
|
|
|
|
{
|
|
|
|
NS_ENSURE_ARG_POINTER(aOutEnabled);
|
|
|
|
*aOutEnabled = mCaretEnabled;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP PresShell::SetCaretVisibilityDuringSelection(PRBool aVisibility)
|
|
|
|
{
|
|
|
|
if (mCaret)
|
|
|
|
mCaret->SetVisibilityDuringSelection(aVisibility);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2008-04-08 11:52:48 -07:00
|
|
|
NS_IMETHODIMP PresShell::GetCaretVisible(PRBool *aOutIsVisible)
|
|
|
|
{
|
|
|
|
*aOutIsVisible = PR_FALSE;
|
|
|
|
if (mCaret) {
|
|
|
|
nsresult rv = mCaret->GetCaretVisible(aOutIsVisible);
|
|
|
|
NS_ENSURE_SUCCESS(rv,rv);
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHODIMP PresShell::SetSelectionFlags(PRInt16 aInEnable)
|
|
|
|
{
|
|
|
|
mSelectionFlags = aInEnable;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP PresShell::GetSelectionFlags(PRInt16 *aOutEnable)
|
|
|
|
{
|
|
|
|
if (!aOutEnable)
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
*aOutEnable = mSelectionFlags;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//implementation of nsISelectionController
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::CharacterMove(PRBool aForward, PRBool aExtend)
|
|
|
|
{
|
|
|
|
return mSelection->CharacterMove(aForward, aExtend);
|
|
|
|
}
|
|
|
|
|
2008-10-16 00:44:32 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::CharacterExtendForDelete()
|
|
|
|
{
|
|
|
|
return mSelection->CharacterExtendForDelete();
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::WordMove(PRBool aForward, PRBool aExtend)
|
|
|
|
{
|
|
|
|
return mSelection->WordMove(aForward, aExtend);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::WordExtendForDelete(PRBool aForward)
|
|
|
|
{
|
|
|
|
return mSelection->WordExtendForDelete(aForward);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::LineMove(PRBool aForward, PRBool aExtend)
|
|
|
|
{
|
|
|
|
nsresult result = mSelection->LineMove(aForward, aExtend);
|
|
|
|
// if we can't go down/up any more we must then move caret completely to
|
|
|
|
// end/beginning respectively.
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
result = CompleteMove(aForward,aExtend);
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::IntraLineMove(PRBool aForward, PRBool aExtend)
|
|
|
|
{
|
|
|
|
return mSelection->IntraLineMove(aForward, aExtend);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::PageMove(PRBool aForward, PRBool aExtend)
|
|
|
|
{
|
|
|
|
nsresult result;
|
|
|
|
nsIViewManager* viewManager = GetViewManager();
|
|
|
|
nsIScrollableView *scrollableView;
|
|
|
|
if (!viewManager)
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
result = viewManager->GetRootScrollableView(&scrollableView);
|
|
|
|
if (NS_FAILED(result))
|
|
|
|
return result;
|
|
|
|
if (!scrollableView)
|
|
|
|
return NS_ERROR_UNEXPECTED;
|
|
|
|
nsIView *scrolledView;
|
|
|
|
result = scrollableView->GetScrolledView(scrolledView);
|
|
|
|
mSelection->CommonPageMove(aForward, aExtend, scrollableView);
|
2008-02-28 07:28:37 -08:00
|
|
|
// After ScrollSelectionIntoView(), the pending notifications might be
|
|
|
|
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
|
2007-03-22 10:30:00 -07:00
|
|
|
return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL, nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::ScrollPage(PRBool aForward)
|
|
|
|
{
|
|
|
|
nsIScrollableView* scrollView = GetViewToScroll(nsLayoutUtils::eVertical);
|
|
|
|
if (scrollView) {
|
2008-12-02 05:18:08 -08:00
|
|
|
scrollView->ScrollByPages(0, aForward ? 1 : -1, NS_VMREFRESH_SMOOTHSCROLL);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::ScrollLine(PRBool aForward)
|
|
|
|
{
|
|
|
|
nsIScrollableView* scrollView = GetViewToScroll(nsLayoutUtils::eVertical);
|
|
|
|
if (scrollView) {
|
|
|
|
#ifdef MOZ_WIDGET_COCOA
|
|
|
|
// Emulate the Mac IE behavior of scrolling a minimum of 2 lines
|
|
|
|
// rather than 1. This vastly improves scrolling speed.
|
2008-12-02 05:18:08 -08:00
|
|
|
scrollView->ScrollByLines(0, aForward ? 2 : -2, NS_VMREFRESH_SMOOTHSCROLL);
|
2007-03-22 10:30:00 -07:00
|
|
|
#else
|
2008-12-02 05:18:08 -08:00
|
|
|
scrollView->ScrollByLines(0, aForward ? 1 : -1, NS_VMREFRESH_SMOOTHSCROLL);
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
|
|
|
|
|
|
|
//NEW FOR LINES
|
|
|
|
// force the update to happen now, otherwise multiple scrolls can
|
|
|
|
// occur before the update is processed. (bug #7354)
|
|
|
|
|
|
|
|
// I'd use Composite here, but it doesn't always work.
|
|
|
|
// vm->Composite();
|
|
|
|
nsIViewManager* viewManager = GetViewManager();
|
|
|
|
if (viewManager) {
|
|
|
|
viewManager->ForceUpdate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::ScrollHorizontal(PRBool aLeft)
|
|
|
|
{
|
|
|
|
nsIScrollableView* scrollView = GetViewToScroll(nsLayoutUtils::eHorizontal);
|
|
|
|
if (scrollView) {
|
2008-12-02 05:18:08 -08:00
|
|
|
scrollView->ScrollByLines(aLeft ? -1 : 1, 0, NS_VMREFRESH_SMOOTHSCROLL);
|
2007-03-22 10:30:00 -07:00
|
|
|
//NEW FOR LINES
|
|
|
|
// force the update to happen now, otherwise multiple scrolls can
|
|
|
|
// occur before the update is processed. (bug #7354)
|
|
|
|
|
|
|
|
// I'd use Composite here, but it doesn't always work.
|
|
|
|
// vm->Composite();
|
|
|
|
nsIViewManager* viewManager = GetViewManager();
|
|
|
|
if (viewManager) {
|
|
|
|
viewManager->ForceUpdate();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::CompleteScroll(PRBool aForward)
|
|
|
|
{
|
|
|
|
nsIScrollableView* scrollView = GetViewToScroll(nsLayoutUtils::eVertical);
|
|
|
|
if (scrollView) {
|
|
|
|
scrollView->ScrollByWhole(!aForward);//TRUE = top, aForward TRUE=bottom
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::CompleteMove(PRBool aForward, PRBool aExtend)
|
2007-04-14 09:51:20 -07:00
|
|
|
{
|
2008-02-28 07:28:37 -08:00
|
|
|
// Beware! This may flush notifications via synchronous
|
|
|
|
// ScrollSelectionIntoView.
|
2007-04-14 09:51:20 -07:00
|
|
|
|
2007-07-19 02:47:49 -07:00
|
|
|
nsIContent* root = mSelection->GetAncestorLimiter();
|
2007-11-12 06:46:13 -08:00
|
|
|
nsIDocument* doc;
|
|
|
|
if (root && (doc = root->GetOwnerDoc()) && doc->GetRootContent() != root) {
|
|
|
|
// Make the caret be either at the very beginning (0) or the very end of
|
|
|
|
// root. Only do this when not moving to the beginning or end of the
|
|
|
|
// document (root is null or root is the documentElement), that's handled
|
|
|
|
// below by moving to beginning or end of the scrollable view.
|
2007-07-19 02:47:49 -07:00
|
|
|
nsIContent* node = root;
|
|
|
|
PRInt32 offset = 0;
|
|
|
|
nsFrameSelection::HINT hint = nsFrameSelection::HINTLEFT;
|
|
|
|
if (aForward) {
|
|
|
|
nsIContent* next = node;
|
|
|
|
PRUint32 count;
|
|
|
|
while ((count = next->GetChildCount()) > 0) {
|
|
|
|
node = next;
|
|
|
|
offset = count;
|
|
|
|
next = next->GetChildAt(count - 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (offset > 0 && node->GetChildAt(offset - 1)->Tag() == nsGkAtoms::br) {
|
|
|
|
--offset;
|
|
|
|
hint = nsFrameSelection::HINTRIGHT; // for bug 106855
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
mSelection->HandleClick(node, offset, offset, aExtend, PR_FALSE, hint);
|
|
|
|
|
|
|
|
// HandleClick resets ancestorLimiter, so set it again.
|
|
|
|
mSelection->SetAncestorLimiter(root);
|
|
|
|
|
2008-04-22 13:48:37 -07:00
|
|
|
// After ScrollSelectionIntoView(), the pending notifications might be
|
|
|
|
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
|
|
|
|
return
|
|
|
|
ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
|
|
|
|
nsISelectionController::SELECTION_FOCUS_REGION,
|
|
|
|
PR_TRUE);
|
2007-07-19 02:47:49 -07:00
|
|
|
}
|
|
|
|
|
2008-09-17 16:28:17 -07:00
|
|
|
nsIFrame *frame = FrameConstructor()->GetRootElementFrame();
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!frame)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsPeekOffsetStruct pos = frame->GetExtremeCaretPosition(!aForward);
|
|
|
|
|
|
|
|
mSelection->HandleClick(pos.mResultContent ,pos.mContentOffset ,pos.mContentOffset/*End*/ ,aExtend, PR_FALSE, aForward);
|
2007-04-14 09:51:20 -07:00
|
|
|
|
2008-04-22 13:48:37 -07:00
|
|
|
// After ScrollSelectionIntoView(), the pending notifications might be
|
|
|
|
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
|
|
|
|
return ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
|
|
|
|
nsISelectionController::SELECTION_FOCUS_REGION,
|
|
|
|
PR_TRUE);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::SelectAll()
|
|
|
|
{
|
|
|
|
return mSelection->SelectAll();
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::CheckVisibility(nsIDOMNode *node, PRInt16 startOffset, PRInt16 EndOffset, PRBool *_retval)
|
|
|
|
{
|
|
|
|
if (!node || startOffset>EndOffset || !_retval || startOffset<0 || EndOffset<0)
|
|
|
|
return NS_ERROR_INVALID_ARG;
|
|
|
|
*_retval = PR_FALSE; //initialize return parameter
|
|
|
|
nsCOMPtr<nsIContent> content(do_QueryInterface(node));
|
|
|
|
if (!content)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
nsIFrame *frame = GetPrimaryFrameFor(content);
|
|
|
|
if (!frame) //no frame to look at so it must not be visible
|
|
|
|
return NS_OK;
|
|
|
|
//start process now to go through all frames to find startOffset. then check chars after that to see
|
|
|
|
//if anything until EndOffset is visible.
|
|
|
|
PRBool finished = PR_FALSE;
|
|
|
|
frame->CheckVisibility(mPresContext,startOffset,EndOffset,PR_TRUE,&finished, _retval);
|
|
|
|
return NS_OK;//dont worry about other return val
|
|
|
|
}
|
|
|
|
|
|
|
|
//end implementations nsISelectionController
|
|
|
|
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::StyleChangeReflow()
|
|
|
|
{
|
2007-04-25 03:44:29 -07:00
|
|
|
nsIFrame* rootFrame = FrameManager()->GetRootFrame();
|
2007-04-25 08:49:55 -07:00
|
|
|
// At the moment at least, we don't have a root frame before the initial
|
|
|
|
// reflow; it's safe to just ignore the request in that case
|
|
|
|
if (!rootFrame)
|
|
|
|
return NS_OK;
|
2007-04-25 03:44:29 -07:00
|
|
|
|
2007-05-06 12:16:51 -07:00
|
|
|
return FrameNeedsReflow(rootFrame, eStyleChange, NS_FRAME_IS_DIRTY);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame*
|
|
|
|
nsIPresShell::GetRootFrame() const
|
|
|
|
{
|
|
|
|
return FrameManager()->GetRootFrame();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame*
|
|
|
|
nsIPresShell::GetRootScrollFrame() const
|
|
|
|
{
|
|
|
|
nsIFrame* rootFrame = FrameManager()->GetRootFrame();
|
|
|
|
// Ensure root frame is a viewport frame
|
|
|
|
if (!rootFrame || nsGkAtoms::viewportFrame != rootFrame->GetType())
|
|
|
|
return nsnull;
|
|
|
|
nsIFrame* theFrame = rootFrame->GetFirstChild(nsnull);
|
|
|
|
if (!theFrame || nsGkAtoms::scrollFrame != theFrame->GetType())
|
|
|
|
return nsnull;
|
|
|
|
return theFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIScrollableFrame*
|
|
|
|
nsIPresShell::GetRootScrollFrameAsScrollable() const
|
|
|
|
{
|
|
|
|
nsIFrame* frame = GetRootScrollFrame();
|
|
|
|
if (!frame)
|
|
|
|
return nsnull;
|
2009-01-12 11:20:59 -08:00
|
|
|
nsIScrollableFrame* scrollableFrame = do_QueryFrame(frame);
|
2007-05-22 17:11:05 -07:00
|
|
|
NS_ASSERTION(scrollableFrame,
|
|
|
|
"All scroll frames must implement nsIScrollableFrame");
|
2007-03-22 10:30:00 -07:00
|
|
|
return scrollableFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::GetPageSequenceFrame(nsIPageSequenceFrame** aResult) const
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(nsnull != aResult, "null ptr");
|
|
|
|
if (nsnull == aResult) {
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
*aResult = nsnull;
|
|
|
|
nsIFrame* frame = mFrameConstructor->GetPageSequenceFrame();
|
2009-01-12 11:20:59 -08:00
|
|
|
*aResult = do_QueryFrame(frame);
|
2007-03-22 10:30:00 -07:00
|
|
|
return *aResult ? NS_OK : NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2007-09-05 01:00:40 -07:00
|
|
|
nsIFrame*
|
|
|
|
PresShell::GetFrameForPoint(nsIFrame* aFrame, nsPoint aPt)
|
|
|
|
{
|
|
|
|
return nsLayoutUtils::GetFrameForPoint(aFrame, aPt);
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void
|
|
|
|
PresShell::BeginUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
mUpdateCount++;
|
|
|
|
#endif
|
|
|
|
mFrameConstructor->BeginUpdate();
|
|
|
|
|
|
|
|
if (aUpdateType & UPDATE_STYLE)
|
|
|
|
mStyleSet->BeginUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::EndUpdate(nsIDocument *aDocument, nsUpdateType aUpdateType)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG
|
|
|
|
NS_PRECONDITION(0 != mUpdateCount, "too many EndUpdate's");
|
|
|
|
--mUpdateCount;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (aUpdateType & UPDATE_STYLE) {
|
|
|
|
mStyleSet->EndUpdate();
|
|
|
|
if (mStylesHaveChanged)
|
|
|
|
ReconstructStyleData();
|
|
|
|
}
|
|
|
|
|
|
|
|
mFrameConstructor->EndUpdate();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2007-04-25 19:33:02 -07:00
|
|
|
PresShell::RestoreRootScrollPosition()
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
// Restore frame state for the root scroll frame
|
|
|
|
nsCOMPtr<nsILayoutHistoryState> historyState =
|
2007-04-25 19:33:02 -07:00
|
|
|
mDocument->GetLayoutHistoryState();
|
2007-03-22 10:30:00 -07:00
|
|
|
// Make sure we don't reenter reflow via the sync paint that happens while
|
|
|
|
// we're scrolling to our restored position. Entering reflow for the
|
|
|
|
// scrollable frame will cause it to reenter ScrollToRestoredPosition(), and
|
|
|
|
// it'll get all confused.
|
2008-03-14 16:08:57 -07:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (historyState) {
|
|
|
|
nsIFrame* scrollFrame = GetRootScrollFrame();
|
|
|
|
if (scrollFrame) {
|
2009-01-12 11:20:59 -08:00
|
|
|
nsIScrollableFrame* scrollableFrame = do_QueryFrame(scrollFrame);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (scrollableFrame) {
|
|
|
|
FrameManager()->RestoreFrameStateFor(scrollFrame, historyState,
|
|
|
|
nsIStatefulFrame::eDocumentScrollState);
|
|
|
|
scrollableFrame->ScrollToRestoredPosition();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2007-04-25 19:33:02 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::BeginLoad(nsIDocument *aDocument)
|
|
|
|
{
|
|
|
|
#ifdef MOZ_PERF_METRICS
|
|
|
|
// Reset style resolution stopwatch maintained by style set
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Reset: Style Resolution: PresShell::BeginLoad(), this=%p\n", (void*)this));
|
|
|
|
#endif
|
|
|
|
mDocumentLoading = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::EndLoad(nsIDocument *aDocument)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(aDocument == mDocument, "Wrong document");
|
|
|
|
|
|
|
|
RestoreRootScrollPosition();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#ifdef MOZ_PERF_METRICS
|
|
|
|
// Dump reflow, style resolution and frame construction times here.
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: Reflow: PresShell::EndLoad(), this=%p\n", this));
|
|
|
|
MOZ_TIMER_STOP(mReflowWatch);
|
|
|
|
MOZ_TIMER_LOG(("Reflow time (this=%p): ", this));
|
|
|
|
MOZ_TIMER_PRINT(mReflowWatch);
|
|
|
|
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: Frame Creation: PresShell::EndLoad(), this=%p\n", this));
|
|
|
|
MOZ_TIMER_STOP(mFrameCreationWatch);
|
|
|
|
MOZ_TIMER_LOG(("Frame construction plus style resolution time (this=%p): ", this));
|
|
|
|
MOZ_TIMER_PRINT(mFrameCreationWatch);
|
|
|
|
|
|
|
|
// Print style resolution stopwatch maintained by style set
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: Style Resolution: PresShell::EndLoad(), this=%p\n", this));
|
|
|
|
#endif
|
|
|
|
mDocumentLoading = PR_FALSE;
|
|
|
|
}
|
|
|
|
|
2007-04-29 23:17:03 -07:00
|
|
|
#ifdef DEBUG
|
|
|
|
void
|
|
|
|
PresShell::VerifyHasDirtyRootAncestor(nsIFrame* aFrame)
|
|
|
|
{
|
|
|
|
// XXXbz due to bug 372769, can't actually assert anything here...
|
|
|
|
return;
|
|
|
|
|
|
|
|
// XXXbz shouldn't need this part; remove it once FrameNeedsReflow
|
|
|
|
// handles the root frame correctly.
|
|
|
|
if (!aFrame->GetParent()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure that there is a reflow root ancestor of |aFrame| that's
|
|
|
|
// in mDirtyRoots already.
|
|
|
|
while (aFrame && (aFrame->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN)) {
|
|
|
|
if (((aFrame->GetStateBits() & NS_FRAME_REFLOW_ROOT) ||
|
|
|
|
!aFrame->GetParent()) &&
|
2009-02-03 06:42:18 -08:00
|
|
|
mDirtyRoots.Contains(aFrame)) {
|
2007-04-29 23:17:03 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
aFrame = aFrame->GetParent();
|
|
|
|
}
|
|
|
|
NS_NOTREACHED("Frame has dirty bits set but isn't scheduled to be "
|
|
|
|
"reflowed?");
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHODIMP
|
2007-05-06 12:16:51 -07:00
|
|
|
PresShell::FrameNeedsReflow(nsIFrame *aFrame, IntrinsicDirty aIntrinsicDirty,
|
2007-05-06 15:50:21 -07:00
|
|
|
nsFrameState aBitToAdd)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-05-06 15:50:21 -07:00
|
|
|
NS_PRECONDITION(aBitToAdd == NS_FRAME_IS_DIRTY ||
|
|
|
|
aBitToAdd == NS_FRAME_HAS_DIRTY_CHILDREN,
|
2007-05-06 12:16:51 -07:00
|
|
|
"Unexpected bits being added");
|
2009-02-04 13:24:18 -08:00
|
|
|
NS_PRECONDITION(aIntrinsicDirty != eStyleChange ||
|
|
|
|
aBitToAdd == NS_FRAME_IS_DIRTY,
|
|
|
|
"bits don't correspond to style change reason");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-05-15 20:22:45 -07:00
|
|
|
NS_ASSERTION(!mIsReflowing, "can't mark frame dirty during reflow");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// If we've not yet done the initial reflow, then don't bother
|
|
|
|
// enqueuing a reflow command yet.
|
|
|
|
if (! mDidInitialReflow)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
// If we're already destroying, don't bother with this either.
|
|
|
|
if (mIsDestroying)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
//printf("gShellCounter: %d\n", gShellCounter++);
|
|
|
|
if (mInVerifyReflow) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2007-05-22 17:11:05 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
|
|
|
|
printf("\nPresShell@%p: frame %p needs reflow\n", (void*)this, (void*)aFrame);
|
|
|
|
if (VERIFY_REFLOW_REALLY_NOISY_RC & gVerifyReflowFlags) {
|
|
|
|
printf("Current content model:\n");
|
|
|
|
nsIContent *rootContent = mDocument->GetRootContent();
|
|
|
|
if (rootContent) {
|
|
|
|
rootContent->List(stdout, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-02-04 13:24:18 -08:00
|
|
|
nsAutoTArray<nsIFrame*, 4> subtrees;
|
|
|
|
subtrees.AppendElement(aFrame);
|
2007-06-15 22:12:58 -07:00
|
|
|
|
2009-02-04 13:24:18 -08:00
|
|
|
do {
|
|
|
|
nsIFrame *subtreeRoot = subtrees.ElementAt(subtrees.Length() - 1);
|
|
|
|
subtrees.RemoveElementAt(subtrees.Length() - 1);
|
|
|
|
|
|
|
|
// Grab |wasDirty| now so we can go ahead and update the bits on
|
|
|
|
// subtreeRoot.
|
|
|
|
PRBool wasDirty = NS_SUBTREE_DIRTY(subtreeRoot);
|
|
|
|
subtreeRoot->AddStateBits(aBitToAdd);
|
|
|
|
|
|
|
|
// Now if subtreeRoot is a reflow root we can cut off this reflow at it if
|
|
|
|
// the bit being added is NS_FRAME_HAS_DIRTY_CHILDREN.
|
|
|
|
PRBool targetFrameDirty = (aBitToAdd == NS_FRAME_IS_DIRTY);
|
2007-06-15 22:12:58 -07:00
|
|
|
|
2007-05-06 12:16:51 -07:00
|
|
|
#define FRAME_IS_REFLOW_ROOT(_f) \
|
|
|
|
((_f->GetStateBits() & NS_FRAME_REFLOW_ROOT) && \
|
2009-02-04 13:24:18 -08:00
|
|
|
(_f != subtreeRoot || !targetFrameDirty))
|
2007-05-06 12:16:51 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-02-04 13:24:18 -08:00
|
|
|
// Mark the intrinsic widths as dirty on the frame, all of its ancestors,
|
|
|
|
// and all of its descendants, if needed:
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-02-04 13:24:18 -08:00
|
|
|
if (aIntrinsicDirty != eResize) {
|
|
|
|
// Mark argument and all ancestors dirty. (Unless we hit a reflow
|
|
|
|
// root that should contain the reflow. That root could be
|
|
|
|
// subtreeRoot itself if it's not dirty, or it could be some
|
|
|
|
// ancestor of subtreeRoot.)
|
|
|
|
for (nsIFrame *a = subtreeRoot;
|
|
|
|
a && !FRAME_IS_REFLOW_ROOT(a);
|
|
|
|
a = a->GetParent())
|
|
|
|
a->MarkIntrinsicWidthsDirty();
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-02-04 13:24:18 -08:00
|
|
|
if (aIntrinsicDirty == eStyleChange) {
|
|
|
|
// Mark all descendants dirty (using an nsTArray stack rather than
|
|
|
|
// recursion).
|
|
|
|
nsAutoTArray<nsIFrame*, 32> stack;
|
|
|
|
stack.AppendElement(subtreeRoot);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
do {
|
2009-02-04 13:24:18 -08:00
|
|
|
nsIFrame *f = stack.ElementAt(stack.Length() - 1);
|
|
|
|
stack.RemoveElementAt(stack.Length() - 1);
|
|
|
|
|
|
|
|
if (f->GetType() == nsGkAtoms::placeholderFrame) {
|
|
|
|
nsIFrame *oof = nsPlaceholderFrame::GetRealFrameForPlaceholder(f);
|
|
|
|
if (!nsLayoutUtils::IsProperAncestorFrame(subtreeRoot, oof)) {
|
|
|
|
// We have another distinct subtree we need to mark.
|
|
|
|
subtrees.AppendElement(oof);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2009-02-04 13:24:18 -08:00
|
|
|
|
|
|
|
PRInt32 childListIndex = 0;
|
|
|
|
nsIAtom *childListName;
|
|
|
|
do {
|
|
|
|
childListName = f->GetAdditionalChildListName(childListIndex++);
|
|
|
|
for (nsIFrame *kid = f->GetFirstChild(childListName); kid;
|
|
|
|
kid = kid->GetNextSibling()) {
|
|
|
|
kid->MarkIntrinsicWidthsDirty();
|
|
|
|
stack.AppendElement(kid);
|
|
|
|
}
|
|
|
|
} while (childListName);
|
|
|
|
} while (stack.Length() != 0);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-02-04 13:24:18 -08:00
|
|
|
// Set NS_FRAME_HAS_DIRTY_CHILDREN bits (via nsIFrame::ChildIsDirty)
|
|
|
|
// up the tree until we reach either a frame that's already dirty or
|
|
|
|
// a reflow root.
|
|
|
|
nsIFrame *f = subtreeRoot;
|
|
|
|
for (;;) {
|
|
|
|
if (FRAME_IS_REFLOW_ROOT(f) || !f->GetParent()) {
|
|
|
|
// we've hit a reflow root or the root frame
|
|
|
|
if (!wasDirty) {
|
|
|
|
mDirtyRoots.AppendElement(f);
|
|
|
|
}
|
2007-04-29 23:17:03 -07:00
|
|
|
#ifdef DEBUG
|
2009-02-04 13:24:18 -08:00
|
|
|
else {
|
|
|
|
VerifyHasDirtyRootAncestor(f);
|
|
|
|
}
|
2007-04-29 23:17:03 -07:00
|
|
|
#endif
|
2009-02-04 13:24:18 -08:00
|
|
|
|
|
|
|
break;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-02-04 13:24:18 -08:00
|
|
|
nsIFrame *child = f;
|
|
|
|
f = f->GetParent();
|
|
|
|
wasDirty = NS_SUBTREE_DIRTY(f);
|
|
|
|
f->ChildIsDirty(child);
|
|
|
|
NS_ASSERTION(f->GetStateBits() & NS_FRAME_HAS_DIRTY_CHILDREN,
|
|
|
|
"ChildIsDirty didn't do its job");
|
|
|
|
if (wasDirty) {
|
|
|
|
// This frame was already marked dirty.
|
2007-04-29 23:17:03 -07:00
|
|
|
#ifdef DEBUG
|
2009-02-04 13:24:18 -08:00
|
|
|
VerifyHasDirtyRootAncestor(f);
|
2007-04-29 23:17:03 -07:00
|
|
|
#endif
|
2009-02-04 13:24:18 -08:00
|
|
|
break;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
2009-02-04 13:24:18 -08:00
|
|
|
} while (subtrees.Length() != 0);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-04-25 08:49:55 -07:00
|
|
|
PostReflowEvent();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
NS_IMETHODIMP_(void)
|
|
|
|
PresShell::FrameNeedsToContinueReflow(nsIFrame *aFrame)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(mIsReflowing, "Must be in reflow when marking path dirty.");
|
|
|
|
NS_PRECONDITION(mCurrentReflowRoot, "Must have a current reflow root here");
|
|
|
|
NS_ASSERTION(aFrame == mCurrentReflowRoot ||
|
|
|
|
nsLayoutUtils::IsProperAncestorFrame(mCurrentReflowRoot, aFrame),
|
|
|
|
"Frame passed in is not the descendant of mCurrentReflowRoot");
|
|
|
|
NS_ASSERTION(aFrame->GetStateBits() & NS_FRAME_IN_REFLOW,
|
|
|
|
"Frame passed in not in reflow?");
|
|
|
|
|
|
|
|
mFramesToDirty.PutEntry(aFrame);
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIScrollableView*
|
|
|
|
PresShell::GetViewToScroll(nsLayoutUtils::Direction aDirection)
|
|
|
|
{
|
|
|
|
nsIScrollableView* scrollView = nsnull;
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIContent> focusedContent;
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
|
|
|
if (fm && mDocument) {
|
|
|
|
nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(mDocument->GetWindow());
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMElement> focusedElement;
|
|
|
|
fm->GetFocusedElementForWindow(window, PR_FALSE, nsnull, getter_AddRefs(focusedElement));
|
|
|
|
focusedContent = do_QueryInterface(focusedElement);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!focusedContent && mSelection) {
|
|
|
|
nsISelection* domSelection = mSelection->
|
|
|
|
GetSelection(nsISelectionController::SELECTION_NORMAL);
|
|
|
|
if (domSelection) {
|
|
|
|
nsCOMPtr<nsIDOMNode> focusedNode;
|
|
|
|
domSelection->GetFocusNode(getter_AddRefs(focusedNode));
|
|
|
|
focusedContent = do_QueryInterface(focusedNode);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (focusedContent) {
|
|
|
|
nsIFrame* startFrame = GetPrimaryFrameFor(focusedContent);
|
|
|
|
if (startFrame) {
|
2009-01-12 11:20:59 -08:00
|
|
|
nsIScrollableViewProvider* svp = do_QueryFrame(startFrame);
|
2007-03-22 10:30:00 -07:00
|
|
|
// If this very frame provides a scroll view, start there instead of frame's
|
|
|
|
// closest view, because the scroll view may be inside a child frame.
|
|
|
|
// For example, this happens in the case of overflow:scroll.
|
|
|
|
// In that case we still use GetNearestScrollingView() because
|
|
|
|
// we need a scrolling view that matches aDirection.
|
|
|
|
nsIScrollableView* sv;
|
|
|
|
nsIView* startView = svp && (sv = svp->GetScrollableView()) ? sv->View() : startFrame->GetClosestView();
|
|
|
|
NS_ASSERTION(startView, "No view to start searching for scrollable view from");
|
|
|
|
scrollView = nsLayoutUtils::GetNearestScrollingView(startView, aDirection);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!scrollView) {
|
|
|
|
nsIViewManager* viewManager = GetViewManager();
|
|
|
|
if (viewManager) {
|
|
|
|
viewManager->GetRootScrollableView(&scrollView);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return scrollView;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::CancelAllPendingReflows()
|
|
|
|
{
|
|
|
|
mDirtyRoots.Clear();
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ACCESSIBILITY
|
|
|
|
void nsIPresShell::InvalidateAccessibleSubtree(nsIContent *aContent)
|
|
|
|
{
|
2007-05-03 19:19:18 -07:00
|
|
|
if (gIsAccessibilityActive) {
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIAccessibilityService> accService =
|
|
|
|
do_GetService("@mozilla.org/accessibilityService;1");
|
|
|
|
if (accService) {
|
|
|
|
accService->InvalidateSubtreeFor(this, aContent,
|
2007-08-10 18:44:44 -07:00
|
|
|
nsIAccessibleEvent::EVENT_ASYNCH_SIGNIFICANT_CHANGE);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::RecreateFramesFor(nsIContent* aContent)
|
|
|
|
{
|
|
|
|
NS_ENSURE_TRUE(mPresContext, NS_ERROR_FAILURE);
|
2007-05-07 21:46:55 -07:00
|
|
|
if (!mDidInitialReflow) {
|
|
|
|
// Nothing to do here. In fact, if we proceed and aContent is the
|
|
|
|
// root we will crash.
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-03-08 12:01:02 -07:00
|
|
|
if (!mPresContext->IsDynamic()) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Don't call RecreateFramesForContent since that is not exported and we want
|
|
|
|
// to keep the number of entrypoints down.
|
|
|
|
|
|
|
|
NS_ASSERTION(mViewManager, "Should have view manager");
|
2008-01-26 15:59:50 -08:00
|
|
|
nsIViewManager::UpdateViewBatch batch(mViewManager);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Have to make sure that the content notifications are flushed before we
|
|
|
|
// start messing with the frame model; otherwise we can get content doubling.
|
|
|
|
mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
|
|
|
|
|
2008-03-14 16:08:57 -07:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsStyleChangeList changeList;
|
|
|
|
changeList.AppendChange(nsnull, aContent, nsChangeHint_ReconstructFrame);
|
|
|
|
|
|
|
|
nsresult rv = mFrameConstructor->ProcessRestyledFrames(changeList);
|
2007-10-18 20:51:15 -07:00
|
|
|
|
2008-01-26 15:59:50 -08:00
|
|
|
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef ACCESSIBILITY
|
|
|
|
InvalidateAccessibleSubtree(aContent);
|
|
|
|
#endif
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2008-06-25 15:41:04 -07:00
|
|
|
void
|
|
|
|
nsIPresShell::PostRecreateFramesFor(nsIContent* aContent)
|
|
|
|
{
|
|
|
|
FrameConstructor()->PostRestyleEvent(aContent, eReStyle_Self,
|
|
|
|
nsChangeHint_ReconstructFrame);
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::ClearFrameRefs(nsIFrame* aFrame)
|
|
|
|
{
|
|
|
|
mPresContext->EventStateManager()->ClearFrameRefs(aFrame);
|
|
|
|
|
|
|
|
if (aFrame == mCurrentEventFrame) {
|
|
|
|
mCurrentEventContent = aFrame->GetContent();
|
|
|
|
mCurrentEventFrame = nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NS_DEBUG
|
|
|
|
if (aFrame == mDrawEventTargetFrame) {
|
|
|
|
mDrawEventTargetFrame = nsnull;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-02-03 06:42:18 -08:00
|
|
|
for (unsigned int i=0; i < mCurrentEventFrameStack.Length(); i++) {
|
|
|
|
if (aFrame == mCurrentEventFrameStack.ElementAt(i)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
//One of our stack frames was deleted. Get its content so that when we
|
|
|
|
//pop it we can still get its new frame from its content
|
|
|
|
nsIContent *currentEventContent = aFrame->GetContent();
|
|
|
|
mCurrentEventContentStack.ReplaceObjectAt(currentEventContent, i);
|
2009-02-03 06:42:18 -08:00
|
|
|
mCurrentEventFrameStack[i] = nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
mFramesToDirty.RemoveEntry(aFrame);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsWeakFrame* weakFrame = mWeakFrames;
|
|
|
|
while (weakFrame) {
|
|
|
|
nsWeakFrame* prev = weakFrame->GetPreviousWeakFrame();
|
|
|
|
if (weakFrame->GetFrame() == aFrame) {
|
|
|
|
// This removes weakFrame from mWeakFrames.
|
|
|
|
weakFrame->Clear(this);
|
|
|
|
}
|
|
|
|
weakFrame = prev;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::CreateRenderingContext(nsIFrame *aFrame,
|
|
|
|
nsIRenderingContext** aResult)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(nsnull != aResult, "null ptr");
|
|
|
|
if (nsnull == aResult) {
|
|
|
|
return NS_ERROR_NULL_POINTER;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIWidget* widget = nsnull;
|
|
|
|
nsPoint offset(0,0);
|
|
|
|
if (mPresContext->IsScreen()) {
|
|
|
|
// Get the widget to create the rendering context for and calculate
|
|
|
|
// the offset from the frame to it. (Calculating the offset is important
|
|
|
|
// if the frame isn't the root frame.)
|
|
|
|
nsPoint viewOffset;
|
|
|
|
nsIView* view = aFrame->GetClosestView(&viewOffset);
|
|
|
|
nsPoint widgetOffset;
|
|
|
|
widget = view->GetNearestWidget(&widgetOffset);
|
|
|
|
offset = viewOffset + widgetOffset;
|
|
|
|
} else {
|
|
|
|
nsIFrame* pageFrame = nsLayoutUtils::GetPageFrame(aFrame);
|
|
|
|
// This might not always come up with a frame, i.e. during reflow;
|
|
|
|
// that's fine, because the translation doesn't matter during reflow.
|
|
|
|
if (pageFrame)
|
|
|
|
offset = aFrame->GetOffsetTo(pageFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv;
|
|
|
|
nsIRenderingContext* result = nsnull;
|
|
|
|
nsIDeviceContext *deviceContext = mPresContext->DeviceContext();
|
|
|
|
if (widget) {
|
|
|
|
rv = deviceContext->CreateRenderingContext(widget, result);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
rv = deviceContext->CreateRenderingContext(result);
|
|
|
|
}
|
|
|
|
*aResult = result;
|
|
|
|
|
2007-05-12 06:06:16 -07:00
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
result->Translate(offset.x, offset.y);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::GoToAnchor(const nsAString& aAnchorName, PRBool aScroll)
|
|
|
|
{
|
|
|
|
if (!mDocument) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Hold a reference to the ESM in case event dispatch tears us down.
|
|
|
|
nsCOMPtr<nsIEventStateManager> esm = mPresContext->EventStateManager();
|
|
|
|
|
|
|
|
if (aAnchorName.IsEmpty()) {
|
|
|
|
NS_ASSERTION(!aScroll, "can't scroll to empty anchor name");
|
|
|
|
esm->SetContentState(nsnull, NS_EVENT_STATE_URLTARGET);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMDocument> doc = do_QueryInterface(mDocument);
|
|
|
|
nsCOMPtr<nsIDOMHTMLDocument> htmlDoc = do_QueryInterface(mDocument);
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
nsCOMPtr<nsIContent> content;
|
|
|
|
|
|
|
|
// Search for an element with a matching "id" attribute
|
|
|
|
if (doc) {
|
|
|
|
nsCOMPtr<nsIDOMElement> element;
|
|
|
|
rv = doc->GetElementById(aAnchorName, getter_AddRefs(element));
|
|
|
|
if (NS_SUCCEEDED(rv) && element) {
|
|
|
|
// Get the nsIContent interface, because that's what we need to
|
|
|
|
// get the primary frame
|
|
|
|
content = do_QueryInterface(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search for an anchor element with a matching "name" attribute
|
|
|
|
if (!content && htmlDoc) {
|
|
|
|
nsCOMPtr<nsIDOMNodeList> list;
|
|
|
|
// Find a matching list of named nodes
|
|
|
|
rv = htmlDoc->GetElementsByName(aAnchorName, getter_AddRefs(list));
|
|
|
|
if (NS_SUCCEEDED(rv) && list) {
|
|
|
|
PRUint32 i;
|
|
|
|
// Loop through the named nodes looking for the first anchor
|
|
|
|
for (i = 0; PR_TRUE; i++) {
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
rv = list->Item(i, getter_AddRefs(node));
|
|
|
|
if (!node) { // End of list
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Ensure it's an anchor element
|
|
|
|
content = do_QueryInterface(node);
|
|
|
|
if (content) {
|
|
|
|
if (content->Tag() == nsGkAtoms::a &&
|
|
|
|
content->IsNodeOfType(nsINode::eHTML)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
content = nsnull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Search for anchor in the HTML namespace with a matching name
|
|
|
|
if (!content && !htmlDoc)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNodeList> list;
|
|
|
|
NS_NAMED_LITERAL_STRING(nameSpace, "http://www.w3.org/1999/xhtml");
|
|
|
|
// Get the list of anchor elements
|
|
|
|
rv = doc->GetElementsByTagNameNS(nameSpace, NS_LITERAL_STRING("a"), getter_AddRefs(list));
|
|
|
|
if (NS_SUCCEEDED(rv) && list) {
|
|
|
|
PRUint32 i;
|
|
|
|
// Loop through the named nodes looking for the first anchor
|
|
|
|
for (i = 0; PR_TRUE; i++) {
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
rv = list->Item(i, getter_AddRefs(node));
|
|
|
|
if (!node) { // End of list
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
// Compare the name attribute
|
|
|
|
nsCOMPtr<nsIDOMElement> element = do_QueryInterface(node);
|
|
|
|
nsAutoString value;
|
|
|
|
if (element && NS_SUCCEEDED(element->GetAttribute(NS_LITERAL_STRING("name"), value))) {
|
|
|
|
if (value.Equals(aAnchorName)) {
|
|
|
|
content = do_QueryInterface(element);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMRange> jumpToRange;
|
|
|
|
nsCOMPtr<nsIXPointerResult> xpointerResult;
|
|
|
|
if (!content) {
|
|
|
|
nsCOMPtr<nsIDOMXMLDocument> xmldoc = do_QueryInterface(mDocument);
|
|
|
|
if (xmldoc) {
|
|
|
|
// Try XPointer
|
|
|
|
xmldoc->EvaluateXPointer(aAnchorName, getter_AddRefs(xpointerResult));
|
|
|
|
if (xpointerResult) {
|
|
|
|
xpointerResult->Item(0, getter_AddRefs(jumpToRange));
|
|
|
|
if (!jumpToRange) {
|
|
|
|
// We know it was an XPointer, so there is no point in
|
|
|
|
// trying any other pointer types, let's just return
|
|
|
|
// an error.
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Finally try FIXptr
|
|
|
|
if (!jumpToRange) {
|
|
|
|
xmldoc->EvaluateFIXptr(aAnchorName,getter_AddRefs(jumpToRange));
|
|
|
|
}
|
|
|
|
|
|
|
|
if (jumpToRange) {
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
jumpToRange->GetStartContainer(getter_AddRefs(node));
|
|
|
|
if (node) {
|
|
|
|
PRUint16 nodeType;
|
|
|
|
node->GetNodeType(&nodeType);
|
|
|
|
PRInt32 offset = -1;
|
|
|
|
jumpToRange->GetStartOffset(&offset);
|
|
|
|
switch (nodeType) {
|
|
|
|
case nsIDOMNode::ATTRIBUTE_NODE:
|
|
|
|
{
|
|
|
|
// XXX Assuming jumping to the ownerElement is the sanest action.
|
|
|
|
nsCOMPtr<nsIAttribute> attr = do_QueryInterface(node);
|
|
|
|
content = attr->GetContent();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case nsIDOMNode::DOCUMENT_NODE:
|
|
|
|
{
|
|
|
|
if (offset >= 0) {
|
|
|
|
nsCOMPtr<nsIDocument> document = do_QueryInterface(node);
|
|
|
|
content = document->GetChildAt(offset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case nsIDOMNode::DOCUMENT_FRAGMENT_NODE:
|
|
|
|
case nsIDOMNode::ELEMENT_NODE:
|
|
|
|
case nsIDOMNode::ENTITY_REFERENCE_NODE:
|
|
|
|
{
|
|
|
|
if (offset >= 0) {
|
|
|
|
nsCOMPtr<nsIContent> parent = do_QueryInterface(node);
|
|
|
|
content = parent->GetChildAt(offset);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case nsIDOMNode::CDATA_SECTION_NODE:
|
|
|
|
case nsIDOMNode::COMMENT_NODE:
|
|
|
|
case nsIDOMNode::TEXT_NODE:
|
|
|
|
case nsIDOMNode::PROCESSING_INSTRUCTION_NODE:
|
|
|
|
{
|
|
|
|
// XXX This should scroll to a specific position in the text.
|
|
|
|
content = do_QueryInterface(node);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
esm->SetContentState(content, NS_EVENT_STATE_URLTARGET);
|
|
|
|
|
|
|
|
if (content) {
|
|
|
|
if (aScroll) {
|
|
|
|
rv = ScrollContentIntoView(content, NS_PRESSHELL_SCROLL_TOP,
|
|
|
|
NS_PRESSHELL_SCROLL_ANYWHERE);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
2008-02-27 02:01:17 -08:00
|
|
|
|
|
|
|
nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
|
|
|
|
if (rootScroll) {
|
|
|
|
mLastAnchorScrolledTo = content;
|
|
|
|
mLastAnchorScrollPositionY = rootScroll->GetScrollPosition().y;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
// Should we select the target? This action is controlled by a
|
|
|
|
// preference: the default is to not select.
|
|
|
|
PRBool selectAnchor = nsContentUtils::GetBoolPref("layout.selectanchor");
|
|
|
|
|
|
|
|
// Even if select anchor pref is false, we must still move the
|
|
|
|
// caret there. That way tabbing will start from the new
|
|
|
|
// location
|
|
|
|
if (!jumpToRange) {
|
|
|
|
jumpToRange = do_CreateInstance(kRangeCID);
|
|
|
|
if (jumpToRange) {
|
|
|
|
while (content && content->GetChildCount() > 0) {
|
|
|
|
content = content->GetChildAt(0);
|
|
|
|
}
|
|
|
|
nsCOMPtr<nsIDOMNode> node(do_QueryInterface(content));
|
|
|
|
NS_ASSERTION(node, "No nsIDOMNode for descendant of anchor");
|
|
|
|
jumpToRange->SelectNodeContents(node);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (jumpToRange) {
|
|
|
|
// Select the anchor
|
|
|
|
nsISelection* sel = mSelection->
|
|
|
|
GetSelection(nsISelectionController::SELECTION_NORMAL);
|
|
|
|
if (sel) {
|
|
|
|
sel->RemoveAllRanges();
|
|
|
|
sel->AddRange(jumpToRange);
|
|
|
|
if (!selectAnchor) {
|
|
|
|
// Use a caret (collapsed selection) at the start of the anchor
|
|
|
|
sel->CollapseToStart();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (selectAnchor && xpointerResult) {
|
|
|
|
// Select the rest (if any) of the ranges in XPointerResult
|
|
|
|
PRUint32 count, i;
|
|
|
|
xpointerResult->GetLength(&count);
|
|
|
|
for (i = 1; i < count; i++) { // jumpToRange is i = 0
|
|
|
|
nsCOMPtr<nsIDOMRange> range;
|
|
|
|
xpointerResult->Item(i, getter_AddRefs(range));
|
|
|
|
sel->AddRange(range);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// Selection is at anchor.
|
|
|
|
// Now focus the document itself if focus is on an element within it.
|
|
|
|
nsPIDOMWindow *win = mDocument->GetWindow();
|
|
|
|
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
|
|
|
if (fm && win) {
|
|
|
|
nsCOMPtr<nsIDOMWindow> focusedWindow;
|
|
|
|
fm->GetFocusedWindow(getter_AddRefs(focusedWindow));
|
|
|
|
if (SameCOMIdentity(win, focusedWindow))
|
|
|
|
fm->ClearFocus(focusedWindow);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
rv = NS_ERROR_FAILURE; //changed to NS_OK in quirks mode if ScrollTo is called
|
|
|
|
|
|
|
|
// Scroll to the top/left if the anchor can not be
|
|
|
|
// found and it is labelled top (quirks mode only). @see bug 80784
|
|
|
|
if ((NS_LossyConvertUTF16toASCII(aAnchorName).LowerCaseEqualsLiteral("top")) &&
|
|
|
|
(mPresContext->CompatibilityMode() == eCompatibility_NavQuirks)) {
|
|
|
|
rv = NS_OK;
|
|
|
|
// Check |aScroll| after setting |rv| so we set |rv| to the same
|
|
|
|
// thing whether or not |aScroll| is true.
|
|
|
|
if (aScroll && mViewManager) {
|
|
|
|
// Get the viewport scroller
|
|
|
|
nsIScrollableView* scrollingView;
|
|
|
|
mViewManager->GetRootScrollableView(&scrollingView);
|
|
|
|
if (scrollingView) {
|
|
|
|
// Scroll to the top of the page
|
2008-11-04 06:38:16 -08:00
|
|
|
scrollingView->ScrollTo(0, 0, 0);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2008-02-27 02:01:17 -08:00
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::ScrollToAnchor()
|
|
|
|
{
|
|
|
|
if (!mLastAnchorScrolledTo)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsIScrollableFrame* rootScroll = GetRootScrollFrameAsScrollable();
|
|
|
|
if (!rootScroll ||
|
|
|
|
mLastAnchorScrollPositionY != rootScroll->GetScrollPosition().y)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsresult rv = ScrollContentIntoView(mLastAnchorScrolledTo, NS_PRESSHELL_SCROLL_TOP,
|
|
|
|
NS_PRESSHELL_SCROLL_ANYWHERE);
|
|
|
|
mLastAnchorScrolledTo = nsnull;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2007-07-15 22:53:19 -07:00
|
|
|
/*
|
|
|
|
* Helper (per-continuation) for ScrollContentIntoView.
|
|
|
|
*
|
|
|
|
* @param aFrame [in] Frame whose bounds should be unioned
|
|
|
|
* @param aVPercent [in] same as for ScrollContentIntoView
|
|
|
|
* @param aRect [inout] rect into which its bounds should be unioned
|
2007-07-15 22:55:04 -07:00
|
|
|
* @param aHaveRect [inout] whether aRect contains data yet
|
2007-07-15 22:53:19 -07:00
|
|
|
* @param aClosestScrolledView [inout] the view to which aRect is relative.
|
|
|
|
* If null, should be filled in appropriately. If non-null, the function
|
|
|
|
* will no-op if the closest scrolling view doesn't match.
|
|
|
|
*/
|
|
|
|
static void
|
|
|
|
UnionRectForClosestScrolledView(nsIFrame* aFrame,
|
|
|
|
PRIntn aVPercent,
|
|
|
|
nsRect& aRect,
|
2007-07-15 22:55:04 -07:00
|
|
|
PRBool& aHaveRect,
|
2007-07-15 22:53:19 -07:00
|
|
|
nsIView*& aClosestScrolledView)
|
|
|
|
{
|
|
|
|
nsRect frameBounds = aFrame->GetRect();
|
|
|
|
nsPoint offset;
|
|
|
|
nsIView* closestView;
|
|
|
|
aFrame->GetOffsetFromView(offset, &closestView);
|
|
|
|
frameBounds.MoveTo(offset);
|
|
|
|
|
|
|
|
// If this is an inline frame and either the bounds height is 0 (quirks
|
|
|
|
// layout model) or aVPercent is not NS_PRESSHELL_SCROLL_ANYWHERE, we need to
|
|
|
|
// change the top of the bounds to include the whole line.
|
|
|
|
if (frameBounds.height == 0 || aVPercent != NS_PRESSHELL_SCROLL_ANYWHERE) {
|
|
|
|
nsIAtom* frameType = NULL;
|
|
|
|
nsIFrame *prevFrame = aFrame;
|
|
|
|
nsIFrame *f = aFrame;
|
|
|
|
|
|
|
|
while (f &&
|
|
|
|
(frameType = f->GetType()) == nsGkAtoms::inlineFrame) {
|
|
|
|
prevFrame = f;
|
|
|
|
f = prevFrame->GetParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (f != aFrame &&
|
|
|
|
f &&
|
|
|
|
frameType == nsGkAtoms::blockFrame) {
|
|
|
|
// find the line containing aFrame and increase the top of |offset|.
|
2008-10-30 12:17:59 -07:00
|
|
|
nsAutoLineIterator lines = f->GetLineIterator();
|
2007-07-15 22:53:19 -07:00
|
|
|
if (lines) {
|
2008-10-30 12:17:59 -07:00
|
|
|
PRInt32 index = lines->FindLineContaining(prevFrame);
|
2007-07-15 22:53:19 -07:00
|
|
|
if (index >= 0) {
|
|
|
|
nsIFrame *trash1;
|
|
|
|
PRInt32 trash2;
|
|
|
|
nsRect lineBounds;
|
|
|
|
PRUint32 trash3;
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(lines->GetLine(index, &trash1, &trash2,
|
|
|
|
lineBounds, &trash3))) {
|
|
|
|
nsPoint blockOffset;
|
|
|
|
nsIView* blockView;
|
|
|
|
f->GetOffsetFromView(blockOffset, &blockView);
|
|
|
|
|
|
|
|
if (blockView == closestView) {
|
|
|
|
// XXX If views not equal, this is hard. Do we want to bother?
|
|
|
|
nscoord newoffset = lineBounds.y + blockOffset.y;
|
|
|
|
|
|
|
|
if (newoffset < frameBounds.y)
|
|
|
|
frameBounds.y = newoffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_ASSERTION(closestView && !closestView->ToScrollableView(),
|
|
|
|
"What happened to the scrolled view? "
|
|
|
|
"The frame should not be directly in the scrolling view!");
|
|
|
|
|
|
|
|
// Walk up the view hierarchy. Make sure to add the view's position
|
|
|
|
// _after_ we get the parent and see whether it's scrollable. We want to
|
|
|
|
// make sure to get the scrolled view's position after it has been scrolled.
|
|
|
|
while (closestView) {
|
|
|
|
nsIView* parent = closestView->GetParent();
|
|
|
|
if (parent && parent->ToScrollableView())
|
|
|
|
break;
|
|
|
|
frameBounds += closestView->GetPosition();
|
|
|
|
closestView = parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!aClosestScrolledView)
|
|
|
|
aClosestScrolledView = closestView;
|
|
|
|
|
|
|
|
if (aClosestScrolledView == closestView) {
|
2007-07-15 22:55:04 -07:00
|
|
|
if (aHaveRect) {
|
|
|
|
// We can't use nsRect::UnionRect since it drops empty rects on
|
|
|
|
// the floor, and we need to include them. (Thus we need
|
|
|
|
// aHaveRect to know when to drop the initial value on the floor.)
|
2008-02-27 01:46:22 -08:00
|
|
|
aRect.UnionRectIncludeEmpty(aRect, frameBounds);
|
2007-07-15 22:55:04 -07:00
|
|
|
} else {
|
|
|
|
aHaveRect = PR_TRUE;
|
|
|
|
aRect = frameBounds;
|
|
|
|
}
|
2007-07-15 22:53:19 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
/**
|
|
|
|
* This function takes a scrolling view, a rect, and a scroll position and
|
|
|
|
* attempts to scroll that rect to that position in that view. The rect
|
|
|
|
* should be in the coordinate system of the _scrolled_ view.
|
|
|
|
*/
|
|
|
|
static void ScrollViewToShowRect(nsIScrollableView* aScrollingView,
|
|
|
|
nsRect & aRect,
|
|
|
|
PRIntn aVPercent,
|
|
|
|
PRIntn aHPercent)
|
|
|
|
{
|
|
|
|
// Determine the visible rect in the scrolling view's coordinate space.
|
|
|
|
// The size of the visible area is the clip view size
|
|
|
|
nsRect visibleRect = aScrollingView->View()->GetBounds(); // get width and height
|
|
|
|
aScrollingView->GetScrollPosition(visibleRect.x, visibleRect.y);
|
|
|
|
|
|
|
|
// The actual scroll offsets
|
|
|
|
nscoord scrollOffsetX = visibleRect.x;
|
|
|
|
nscoord scrollOffsetY = visibleRect.y;
|
|
|
|
|
|
|
|
nscoord lineHeight;
|
|
|
|
aScrollingView->GetLineHeight(&lineHeight);
|
|
|
|
|
|
|
|
// See how the rect should be positioned vertically
|
|
|
|
if (NS_PRESSHELL_SCROLL_ANYWHERE == aVPercent ||
|
|
|
|
(NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE == aVPercent &&
|
|
|
|
aRect.height < lineHeight)) {
|
|
|
|
// The caller doesn't care where the frame is positioned vertically,
|
|
|
|
// so long as it's fully visible
|
|
|
|
if (aRect.y < visibleRect.y) {
|
|
|
|
// Scroll up so the frame's top edge is visible
|
|
|
|
scrollOffsetY = aRect.y;
|
|
|
|
} else if (aRect.YMost() > visibleRect.YMost()) {
|
|
|
|
// Scroll down so the frame's bottom edge is visible. Make sure the
|
|
|
|
// frame's top edge is still visible
|
|
|
|
scrollOffsetY += aRect.YMost() - visibleRect.YMost();
|
|
|
|
if (scrollOffsetY > aRect.y) {
|
|
|
|
scrollOffsetY = aRect.y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE == aVPercent) {
|
|
|
|
// Scroll only if no part of the frame is visible in this view
|
|
|
|
if (aRect.YMost() - lineHeight < visibleRect.y) {
|
|
|
|
// Scroll up so the frame's top edge is visible
|
|
|
|
scrollOffsetY = aRect.y;
|
|
|
|
} else if (aRect.y + lineHeight > visibleRect.YMost()) {
|
|
|
|
// Scroll down so the frame's bottom edge is visible. Make sure the
|
|
|
|
// frame's top edge is still visible
|
|
|
|
scrollOffsetY += aRect.YMost() - visibleRect.YMost();
|
|
|
|
if (scrollOffsetY > aRect.y) {
|
|
|
|
scrollOffsetY = aRect.y;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Align the frame edge according to the specified percentage
|
|
|
|
nscoord frameAlignY =
|
|
|
|
NSToCoordRound(aRect.y + aRect.height * (aVPercent / 100.0f));
|
|
|
|
scrollOffsetY =
|
|
|
|
NSToCoordRound(frameAlignY - visibleRect.height * (aVPercent / 100.0f));
|
|
|
|
}
|
|
|
|
|
|
|
|
// See how the frame should be positioned horizontally
|
|
|
|
if (NS_PRESSHELL_SCROLL_ANYWHERE == aHPercent ||
|
|
|
|
(NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE == aHPercent &&
|
|
|
|
aRect.width < lineHeight)) {
|
|
|
|
// The caller doesn't care where the frame is positioned horizontally,
|
|
|
|
// so long as it's fully visible
|
|
|
|
if (aRect.x < visibleRect.x) {
|
|
|
|
// Scroll left so the frame's left edge is visible
|
|
|
|
scrollOffsetX = aRect.x;
|
|
|
|
} else if (aRect.XMost() > visibleRect.XMost()) {
|
|
|
|
// Scroll right so the frame's right edge is visible. Make sure the
|
|
|
|
// frame's left edge is still visible
|
|
|
|
scrollOffsetX += aRect.XMost() - visibleRect.XMost();
|
|
|
|
if (scrollOffsetX > aRect.x) {
|
|
|
|
scrollOffsetX = aRect.x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE == aHPercent) {
|
|
|
|
// Scroll only if no part of the frame is visible in this view
|
|
|
|
// XXXbz using the line height here is odd, but there are no
|
|
|
|
// natural dimensions to use here, really....
|
|
|
|
if (aRect.XMost() - lineHeight < visibleRect.x) {
|
|
|
|
// Scroll left so the frame's left edge is visible
|
|
|
|
scrollOffsetX = aRect.x;
|
|
|
|
} else if (aRect.x + lineHeight > visibleRect.XMost()) {
|
|
|
|
// Scroll right so the frame's right edge is visible. Make sure the
|
|
|
|
// frame's left edge is still visible
|
|
|
|
scrollOffsetX += aRect.XMost() - visibleRect.XMost();
|
|
|
|
if (scrollOffsetX > aRect.x) {
|
|
|
|
scrollOffsetX = aRect.x;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
// Align the frame edge according to the specified percentage
|
|
|
|
nscoord frameAlignX =
|
|
|
|
NSToCoordRound(aRect.x + (aRect.width) * (aHPercent / 100.0f));
|
|
|
|
scrollOffsetX =
|
|
|
|
NSToCoordRound(frameAlignX - visibleRect.width * (aHPercent / 100.0f));
|
|
|
|
}
|
|
|
|
|
2008-11-04 06:38:16 -08:00
|
|
|
aScrollingView->ScrollTo(scrollOffsetX, scrollOffsetY, 0);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::ScrollContentIntoView(nsIContent* aContent,
|
|
|
|
PRIntn aVPercent,
|
2009-04-21 16:53:52 -07:00
|
|
|
PRIntn aHPercent)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
nsCOMPtr<nsIContent> content = aContent; // Keep content alive while flushing.
|
|
|
|
NS_ENSURE_TRUE(content, NS_ERROR_NULL_POINTER);
|
|
|
|
nsCOMPtr<nsIDocument> currentDoc = content->GetCurrentDoc();
|
|
|
|
NS_ENSURE_STATE(currentDoc);
|
|
|
|
|
2009-05-09 20:57:53 -07:00
|
|
|
mContentToScrollTo = aContent;
|
|
|
|
mContentScrollVPosition = aVPercent;
|
|
|
|
mContentScrollHPosition = aHPercent;
|
|
|
|
|
|
|
|
// Flush layout and attempt to scroll in the process.
|
|
|
|
currentDoc->FlushPendingNotifications(Flush_InterruptibleLayout);
|
|
|
|
|
|
|
|
// If mContentToScrollTo is non-null, that means we interrupted the reflow
|
|
|
|
// (or suppressed it altogether because we're suppressing interruptible
|
|
|
|
// flushes right now) and won't necessarily get the position correct, but do
|
|
|
|
// a best-effort scroll here. The other option would be to do this inside
|
|
|
|
// FlushPendingNotifications, but I'm not sure the repeated scrolling that
|
|
|
|
// could trigger if reflows keep getting interrupted would be more desirable
|
|
|
|
// than a single best-effort scroll followed by one final scroll on the first
|
|
|
|
// completed reflow.
|
|
|
|
if (mContentToScrollTo) {
|
|
|
|
DoScrollContentIntoView(content, aVPercent, aHPercent);
|
|
|
|
}
|
|
|
|
return NS_OK;
|
2009-04-21 16:53:52 -07:00
|
|
|
}
|
|
|
|
|
2009-05-09 20:57:53 -07:00
|
|
|
void
|
2009-04-21 16:53:52 -07:00
|
|
|
PresShell::DoScrollContentIntoView(nsIContent* aContent,
|
|
|
|
PRIntn aVPercent,
|
|
|
|
PRIntn aHPercent)
|
|
|
|
{
|
|
|
|
nsIFrame* frame = GetPrimaryFrameFor(aContent);
|
|
|
|
if (!frame) {
|
|
|
|
mContentToScrollTo = nsnull;
|
2009-05-09 20:57:53 -07:00
|
|
|
return;
|
2009-04-21 16:53:52 -07:00
|
|
|
}
|
|
|
|
|
2009-05-14 18:40:28 -07:00
|
|
|
if (frame->GetStateBits() & NS_FRAME_FIRST_REFLOW) {
|
|
|
|
// The reflow flush before this scroll got interrupted, and this frame's
|
|
|
|
// coords and size are all zero, and it has no content showing anyway.
|
|
|
|
// Don't bother scrolling to it. We'll try again when we finish up layout.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// This is a two-step process.
|
|
|
|
// Step 1: Find the bounds of the rect we want to scroll into view. For
|
|
|
|
// example, for an inline frame we may want to scroll in the whole
|
|
|
|
// line.
|
|
|
|
// Step 2: Walk the views that are parents of the frame and scroll them
|
|
|
|
// appropriately.
|
|
|
|
|
2007-07-15 22:53:19 -07:00
|
|
|
nsIView *closestView = nsnull;
|
|
|
|
nsRect frameBounds;
|
2007-07-15 22:55:04 -07:00
|
|
|
PRBool haveRect = PR_FALSE;
|
2007-07-15 22:53:19 -07:00
|
|
|
do {
|
2007-07-15 22:55:04 -07:00
|
|
|
UnionRectForClosestScrolledView(frame, aVPercent, frameBounds, haveRect,
|
|
|
|
closestView);
|
2007-07-15 22:53:19 -07:00
|
|
|
} while ((frame = frame->GetNextContinuation()));
|
2007-07-13 22:18:09 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Walk up the view hierarchy. Make sure to add the view's position
|
|
|
|
// _after_ we get the parent and see whether it's scrollable. We want to
|
|
|
|
// make sure to get the scrolled view's position after it has been scrolled.
|
|
|
|
nsIScrollableView* scrollingView = nsnull;
|
|
|
|
while (closestView) {
|
|
|
|
nsIView* parent = closestView->GetParent();
|
|
|
|
if (parent) {
|
|
|
|
scrollingView = parent->ToScrollableView();
|
|
|
|
if (scrollingView) {
|
|
|
|
ScrollViewToShowRect(scrollingView, frameBounds, aVPercent, aHPercent);
|
|
|
|
}
|
|
|
|
}
|
2007-07-12 16:48:37 -07:00
|
|
|
frameBounds += closestView->GetPosition();
|
2007-03-22 10:30:00 -07:00
|
|
|
closestView = parent;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// GetLinkLocation: copy link location to clipboard
|
|
|
|
NS_IMETHODIMP PresShell::GetLinkLocation(nsIDOMNode* aNode, nsAString& aLocationString)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_dr
|
|
|
|
printf("dr :: PresShell::GetLinkLocation\n");
|
|
|
|
#endif
|
|
|
|
|
|
|
|
NS_ENSURE_ARG_POINTER(aNode);
|
|
|
|
nsresult rv;
|
|
|
|
nsAutoString anchorText;
|
|
|
|
static char strippedChars[] = {'\t','\r','\n'};
|
|
|
|
|
|
|
|
// are we an anchor?
|
|
|
|
nsCOMPtr<nsIDOMHTMLAnchorElement> anchor(do_QueryInterface(aNode));
|
|
|
|
nsCOMPtr<nsIDOMHTMLAreaElement> area;
|
|
|
|
nsCOMPtr<nsIDOMHTMLLinkElement> link;
|
|
|
|
nsAutoString xlinkType;
|
|
|
|
if (anchor) {
|
|
|
|
rv = anchor->GetHref(anchorText);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
} else {
|
|
|
|
// area?
|
|
|
|
area = do_QueryInterface(aNode);
|
|
|
|
if (area) {
|
|
|
|
rv = area->GetHref(anchorText);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
} else {
|
|
|
|
// link?
|
|
|
|
link = do_QueryInterface(aNode);
|
|
|
|
if (link) {
|
|
|
|
rv = link->GetHref(anchorText);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
} else {
|
|
|
|
// Xlink?
|
|
|
|
nsCOMPtr<nsIDOMElement> element(do_QueryInterface(aNode));
|
|
|
|
if (element) {
|
|
|
|
NS_NAMED_LITERAL_STRING(xlinkNS,"http://www.w3.org/1999/xlink");
|
|
|
|
element->GetAttributeNS(xlinkNS,NS_LITERAL_STRING("type"),xlinkType);
|
|
|
|
if (xlinkType.EqualsLiteral("simple")) {
|
|
|
|
element->GetAttributeNS(xlinkNS,NS_LITERAL_STRING("href"),anchorText);
|
|
|
|
if (!anchorText.IsEmpty()) {
|
|
|
|
// Resolve the full URI using baseURI property
|
|
|
|
|
|
|
|
nsAutoString base;
|
|
|
|
nsCOMPtr<nsIDOM3Node> node(do_QueryInterface(aNode,&rv));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
node->GetBaseURI(base);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIIOService>
|
|
|
|
ios(do_GetService("@mozilla.org/network/io-service;1", &rv));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIURI> baseURI;
|
|
|
|
rv = ios->NewURI(NS_ConvertUTF16toUTF8(base),nsnull,nsnull,getter_AddRefs(baseURI));
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
nsCAutoString spec;
|
|
|
|
rv = baseURI->Resolve(NS_ConvertUTF16toUTF8(anchorText),spec);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
CopyUTF8toUTF16(spec, anchorText);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (anchor || area || link || xlinkType.EqualsLiteral("simple")) {
|
|
|
|
//Remove all the '\t', '\r' and '\n' from 'anchorText'
|
|
|
|
anchorText.StripChars(strippedChars);
|
|
|
|
|
|
|
|
aLocationString = anchorText;
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// if no link, fail.
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::GetSelectionForCopy(nsISelection** outSelection)
|
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
|
|
|
*outSelection = nsnull;
|
|
|
|
|
|
|
|
if (!mDocument) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> content;
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
|
|
|
if (fm) {
|
|
|
|
nsCOMPtr<nsIDOMWindow> window = do_QueryInterface(mDocument->GetWindow());
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMElement> focusedElement;
|
|
|
|
fm->GetFocusedElementForWindow(window, PR_FALSE, nsnull, getter_AddRefs(focusedElement));
|
|
|
|
content = do_QueryInterface(focusedElement);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsISelection> sel;
|
|
|
|
if (content)
|
|
|
|
{
|
|
|
|
//check to see if we need to get selection from frame
|
|
|
|
//optimization that MAY need to be expanded as more things implement their own "selection"
|
|
|
|
nsCOMPtr<nsIDOMNSHTMLInputElement> htmlInputElement(do_QueryInterface(content));
|
|
|
|
nsCOMPtr<nsIDOMNSHTMLTextAreaElement> htmlTextAreaElement(do_QueryInterface(content));
|
|
|
|
if (htmlInputElement || htmlTextAreaElement)
|
|
|
|
{
|
|
|
|
nsIFrame *htmlInputFrame = GetPrimaryFrameFor(content);
|
|
|
|
if (!htmlInputFrame) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsISelectionController> selCon;
|
|
|
|
rv = htmlInputFrame->
|
|
|
|
GetSelectionController(mPresContext,getter_AddRefs(selCon));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (!selCon) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
rv = selCon->GetSelection(nsISelectionController::SELECTION_NORMAL,
|
|
|
|
getter_AddRefs(sel));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!sel) {
|
|
|
|
sel = mSelection->GetSelection(nsISelectionController::SELECTION_NORMAL);
|
|
|
|
rv = NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
*outSelection = sel;
|
|
|
|
NS_IF_ADDREF(*outSelection);
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
2008-09-13 02:42:11 -07:00
|
|
|
/* Just hook this call into InvalidateOverflowRect */
|
|
|
|
void
|
|
|
|
PresShell::InvalidateFrameForView(nsIView *aView)
|
|
|
|
{
|
|
|
|
nsIFrame* frame = nsLayoutUtils::GetFrameFor(aView);
|
|
|
|
if (frame)
|
|
|
|
frame->InvalidateOverflowRect();
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-12-07 13:58:07 -08:00
|
|
|
NS_IMETHODIMP_(void)
|
|
|
|
PresShell::DispatchSynthMouseMove(nsGUIEvent *aEvent,
|
|
|
|
PRBool aFlushOnHoverChange)
|
|
|
|
{
|
|
|
|
PRUint32 hoverGenerationBefore = mFrameConstructor->GetHoverGeneration();
|
|
|
|
nsEventStatus status;
|
|
|
|
mViewManager->DispatchEvent(aEvent, &status);
|
|
|
|
if (aFlushOnHoverChange &&
|
|
|
|
hoverGenerationBefore != mFrameConstructor->GetHoverGeneration()) {
|
|
|
|
// Flush so that the resulting reflow happens now so that our caller
|
|
|
|
// can suppress any synthesized mouse moves caused by that reflow.
|
|
|
|
FlushPendingNotifications(Flush_Layout);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::DoGetContents(const nsACString& aMimeType, PRUint32 aFlags, PRBool aSelectionOnly, nsAString& aOutValue)
|
|
|
|
{
|
|
|
|
aOutValue.Truncate();
|
|
|
|
|
|
|
|
if (!mDocument) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsresult rv;
|
|
|
|
nsCOMPtr<nsISelection> sel;
|
|
|
|
|
|
|
|
// Now we have the selection. Make sure it's nonzero:
|
|
|
|
if (aSelectionOnly)
|
|
|
|
{
|
|
|
|
rv = GetSelectionForCopy(getter_AddRefs(sel));
|
|
|
|
if (NS_FAILED(rv)) return rv;
|
|
|
|
if (!sel) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
PRBool isCollapsed;
|
|
|
|
sel->GetIsCollapsed(&isCollapsed);
|
|
|
|
if (isCollapsed)
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// call the copy code
|
|
|
|
return nsCopySupport::GetContents(aMimeType, aFlags, sel,
|
|
|
|
mDocument, aOutValue);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::DoCopy()
|
|
|
|
{
|
|
|
|
if (!mDocument) return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsISelection> sel;
|
|
|
|
nsresult rv = GetSelectionForCopy(getter_AddRefs(sel));
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
if (!sel)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
// Now we have the selection. Make sure it's nonzero:
|
|
|
|
PRBool isCollapsed;
|
|
|
|
sel->GetIsCollapsed(&isCollapsed);
|
|
|
|
if (isCollapsed)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
// call the copy code
|
|
|
|
rv = nsCopySupport::HTMLCopy(sel, mDocument, nsIClipboard::kGlobalClipboard);
|
|
|
|
if (NS_FAILED(rv))
|
|
|
|
return rv;
|
|
|
|
|
|
|
|
// Now that we have copied, update the Paste menu item
|
|
|
|
nsPIDOMWindow *domWindow = mDocument->GetWindow();
|
|
|
|
if (domWindow)
|
|
|
|
{
|
|
|
|
domWindow->UpdateCommands(NS_LITERAL_STRING("clipboard"));
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::CaptureHistoryState(nsILayoutHistoryState** aState, PRBool aLeavingPage)
|
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
|
|
|
NS_PRECONDITION(nsnull != aState, "null state pointer");
|
|
|
|
|
|
|
|
// We actually have to mess with the docshell here, since we want to
|
|
|
|
// store the state back in it.
|
|
|
|
// XXXbz this isn't really right, since this is being called in the
|
|
|
|
// content viewer's Hide() method... by that point the docshell's
|
|
|
|
// state could be wrong. We should sort out a better ownership
|
|
|
|
// model for the layout history state.
|
|
|
|
nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
|
|
|
|
if (!container)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(container));
|
|
|
|
if (!docShell)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsILayoutHistoryState> historyState;
|
|
|
|
docShell->GetLayoutHistoryState(getter_AddRefs(historyState));
|
|
|
|
if (!historyState) {
|
|
|
|
// Create the document state object
|
|
|
|
rv = NS_NewLayoutHistoryState(getter_AddRefs(historyState));
|
|
|
|
|
|
|
|
if (NS_FAILED(rv)) {
|
|
|
|
*aState = nsnull;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
docShell->SetLayoutHistoryState(historyState);
|
|
|
|
}
|
|
|
|
|
|
|
|
*aState = historyState;
|
|
|
|
NS_IF_ADDREF(*aState);
|
|
|
|
|
|
|
|
// Capture frame state for the entire frame hierarchy
|
|
|
|
nsIFrame* rootFrame = FrameManager()->GetRootFrame();
|
|
|
|
if (!rootFrame) return NS_OK;
|
|
|
|
// Capture frame state for the root scroll frame
|
|
|
|
// Don't capture state when first creating doc element hierarchy
|
|
|
|
// As the scroll position is 0 and this will cause us to loose
|
|
|
|
// our previously saved place!
|
|
|
|
if (aLeavingPage) {
|
|
|
|
nsIFrame* scrollFrame = GetRootScrollFrame();
|
|
|
|
if (scrollFrame) {
|
|
|
|
FrameManager()->CaptureFrameStateFor(scrollFrame, historyState,
|
|
|
|
nsIStatefulFrame::eDocumentScrollState);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
FrameManager()->CaptureFrameState(rootFrame, historyState);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::IsPaintingSuppressed(PRBool* aResult)
|
|
|
|
{
|
|
|
|
*aResult = mPaintingSuppressed;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::UnsuppressAndInvalidate()
|
|
|
|
{
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
if (!mPresContext->EnsureVisible() || mHaveShutDown) {
|
2007-03-22 10:30:00 -07:00
|
|
|
// No point; we're about to be torn down anyway.
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mPaintingSuppressed = PR_FALSE;
|
|
|
|
nsIFrame* rootFrame = FrameManager()->GetRootFrame();
|
|
|
|
if (rootFrame) {
|
|
|
|
// let's assume that outline on a root frame is not supported
|
|
|
|
nsRect rect(nsPoint(0, 0), rootFrame->GetSize());
|
2008-09-18 02:47:21 -07:00
|
|
|
rootFrame->Invalidate(rect);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
// now that painting is unsuppressed, focus may be set on the document
|
|
|
|
nsPIDOMWindow *win = mDocument->GetWindow();
|
|
|
|
if (win)
|
|
|
|
win->SetReadyForFocus();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2009-05-12 12:07:08 -07:00
|
|
|
if (!mHaveShutDown && mViewManager)
|
2007-03-22 10:30:00 -07:00
|
|
|
mViewManager->SynthesizeMouseMove(PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::UnsuppressPainting()
|
|
|
|
{
|
|
|
|
if (mPaintSuppressionTimer) {
|
|
|
|
mPaintSuppressionTimer->Cancel();
|
|
|
|
mPaintSuppressionTimer = nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (mIsDocumentGone || !mPaintingSuppressed)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
// If we have reflows pending, just wait until we process
|
|
|
|
// the reflows and get all the frames where we want them
|
|
|
|
// before actually unlocking the painting. Otherwise
|
|
|
|
// go ahead and unlock now.
|
2009-02-03 06:42:18 -08:00
|
|
|
if (mDirtyRoots.Length() > 0)
|
2007-03-22 10:30:00 -07:00
|
|
|
mShouldUnsuppressPainting = PR_TRUE;
|
|
|
|
else
|
|
|
|
UnsuppressAndInvalidate();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::DisableThemeSupport()
|
|
|
|
{
|
|
|
|
// Doesn't have to be dynamic. Just set the bool.
|
|
|
|
mIsThemeSupportDisabled = PR_TRUE;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
PresShell::IsThemeSupportEnabled()
|
|
|
|
{
|
|
|
|
return !mIsThemeSupportDisabled;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Post a request to handle an arbitrary callback after reflow has finished.
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::PostReflowCallback(nsIReflowCallback* aCallback)
|
|
|
|
{
|
|
|
|
void* result = AllocateFrame(sizeof(nsCallbackEventRequest));
|
|
|
|
if (NS_UNLIKELY(!result)) {
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
nsCallbackEventRequest* request = (nsCallbackEventRequest*)result;
|
|
|
|
|
|
|
|
request->callback = aCallback;
|
|
|
|
request->next = nsnull;
|
|
|
|
|
|
|
|
if (mLastCallbackEventRequest) {
|
|
|
|
mLastCallbackEventRequest = mLastCallbackEventRequest->next = request;
|
|
|
|
} else {
|
|
|
|
mFirstCallbackEventRequest = request;
|
|
|
|
mLastCallbackEventRequest = request;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::CancelReflowCallback(nsIReflowCallback* aCallback)
|
|
|
|
{
|
|
|
|
nsCallbackEventRequest* before = nsnull;
|
|
|
|
nsCallbackEventRequest* node = mFirstCallbackEventRequest;
|
|
|
|
while(node)
|
|
|
|
{
|
|
|
|
nsIReflowCallback* callback = node->callback;
|
|
|
|
|
|
|
|
if (callback == aCallback)
|
|
|
|
{
|
|
|
|
nsCallbackEventRequest* toFree = node;
|
|
|
|
if (node == mFirstCallbackEventRequest) {
|
|
|
|
node = node->next;
|
|
|
|
mFirstCallbackEventRequest = node;
|
|
|
|
NS_ASSERTION(before == nsnull, "impossible");
|
|
|
|
} else {
|
|
|
|
node = node->next;
|
|
|
|
before->next = node;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (toFree == mLastCallbackEventRequest) {
|
|
|
|
mLastCallbackEventRequest = before;
|
|
|
|
}
|
|
|
|
|
|
|
|
FreeFrame(sizeof(nsCallbackEventRequest), toFree);
|
|
|
|
} else {
|
|
|
|
before = node;
|
|
|
|
node = node->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-11-30 23:22:44 -08:00
|
|
|
void
|
|
|
|
PresShell::CancelPostedReflowCallbacks()
|
|
|
|
{
|
|
|
|
while (mFirstCallbackEventRequest) {
|
|
|
|
nsCallbackEventRequest* node = mFirstCallbackEventRequest;
|
|
|
|
mFirstCallbackEventRequest = node->next;
|
|
|
|
if (!mFirstCallbackEventRequest) {
|
|
|
|
mLastCallbackEventRequest = nsnull;
|
|
|
|
}
|
|
|
|
nsIReflowCallback* callback = node->callback;
|
|
|
|
FreeFrame(sizeof(nsCallbackEventRequest), node);
|
|
|
|
if (callback) {
|
|
|
|
callback->ReflowCallbackCanceled();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
void
|
2009-04-21 16:53:52 -07:00
|
|
|
PresShell::HandlePostedReflowCallbacks(PRBool aInterruptible)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
PRBool shouldFlush = PR_FALSE;
|
|
|
|
|
|
|
|
while (mFirstCallbackEventRequest) {
|
|
|
|
nsCallbackEventRequest* node = mFirstCallbackEventRequest;
|
|
|
|
mFirstCallbackEventRequest = node->next;
|
|
|
|
if (!mFirstCallbackEventRequest) {
|
|
|
|
mLastCallbackEventRequest = nsnull;
|
|
|
|
}
|
|
|
|
nsIReflowCallback* callback = node->callback;
|
|
|
|
FreeFrame(sizeof(nsCallbackEventRequest), node);
|
|
|
|
if (callback) {
|
|
|
|
if (callback->ReflowFinished()) {
|
|
|
|
shouldFlush = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
mozFlushType flushType =
|
|
|
|
aInterruptible ? Flush_InterruptibleLayout : Flush_Layout;
|
2007-03-22 10:30:00 -07:00
|
|
|
if (shouldFlush)
|
2009-04-21 16:53:52 -07:00
|
|
|
FlushPendingNotifications(flushType);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::IsSafeToFlush(PRBool& aIsSafeToFlush)
|
|
|
|
{
|
2008-12-20 06:28:19 -08:00
|
|
|
aIsSafeToFlush = nsContentUtils::IsSafeToRunScript();
|
|
|
|
#ifdef DEBUG
|
2008-03-14 16:08:57 -07:00
|
|
|
// Not safe if we are reflowing or in the middle of frame construction
|
2008-12-20 06:28:19 -08:00
|
|
|
PRBool isSafeToFlush = !mIsReflowing;
|
|
|
|
// Not safe if we are painting
|
|
|
|
nsIViewManager* viewManager = GetViewManager();
|
|
|
|
if (viewManager) {
|
|
|
|
PRBool isPainting = PR_FALSE;
|
|
|
|
viewManager->IsPainting(isPainting);
|
|
|
|
if (isPainting) {
|
|
|
|
isSafeToFlush = PR_FALSE;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2008-12-20 06:28:19 -08:00
|
|
|
NS_ASSERTION(!aIsSafeToFlush || isSafeToFlush, "Missing a script blocker!");
|
|
|
|
#endif
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
NS_IMETHODIMP
|
2007-03-22 10:30:00 -07:00
|
|
|
PresShell::FlushPendingNotifications(mozFlushType aType)
|
2007-08-21 19:57:06 -07:00
|
|
|
{
|
|
|
|
NS_ASSERTION(aType >= Flush_Frames, "Why did we get called?");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
PRBool isSafeToFlush;
|
|
|
|
IsSafeToFlush(isSafeToFlush);
|
|
|
|
|
|
|
|
NS_ASSERTION(!isSafeToFlush || mViewManager, "Must have view manager");
|
2007-08-21 14:45:00 -07:00
|
|
|
// Make sure the view manager stays alive while batching view updates.
|
2008-01-26 15:59:50 -08:00
|
|
|
nsCOMPtr<nsIViewManager> viewManagerDeathGrip = mViewManager;
|
|
|
|
if (isSafeToFlush && mViewManager) {
|
2007-04-22 22:53:27 -07:00
|
|
|
// Processing pending notifications can kill us, and some callers only
|
|
|
|
// hold weak refs when calling FlushPendingNotifications(). :(
|
|
|
|
nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
|
|
|
|
|
2009-01-14 12:42:25 -08:00
|
|
|
if (mResizeEvent.IsPending()) {
|
|
|
|
FireResizeEvent();
|
|
|
|
if (mIsDestroying) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Style reresolves not in conjunction with reflows can't cause
|
|
|
|
// painting or geometry changes, so don't bother with view update
|
|
|
|
// batching if we only have style reresolve
|
2008-01-26 15:59:50 -08:00
|
|
|
nsIViewManager::UpdateViewBatch batch(mViewManager);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-09-04 01:31:01 -07:00
|
|
|
// Force flushing of any pending content notifications that might have
|
|
|
|
// queued up while our event was pending. That will ensure that we don't
|
|
|
|
// construct frames for content right now that's still waiting to be
|
|
|
|
// notified on,
|
|
|
|
mDocument->FlushPendingNotifications(Flush_ContentAndNotify);
|
2007-09-17 18:16:39 -07:00
|
|
|
|
|
|
|
// Process pending restyles, since any flush of the presshell wants
|
|
|
|
// up-to-date style data.
|
2007-09-04 01:31:01 -07:00
|
|
|
if (!mIsDestroying) {
|
2008-07-26 09:14:48 -07:00
|
|
|
mPresContext->FlushPendingMediaFeatureValuesChanged();
|
|
|
|
|
2008-12-08 08:08:05 -08:00
|
|
|
// Flush any pending update of the user font set, since that could
|
|
|
|
// cause style changes (for updating ex/ch units, and to cause a
|
|
|
|
// reflow).
|
|
|
|
mPresContext->FlushUserFontSet();
|
|
|
|
|
2009-01-02 07:41:43 -08:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2007-09-04 01:31:01 -07:00
|
|
|
mFrameConstructor->ProcessPendingRestyles();
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-09-17 18:16:39 -07:00
|
|
|
// Process whatever XBL constructors those restyles queued up. This
|
|
|
|
// ensures that onload doesn't fire too early and that we won't do extra
|
|
|
|
// reflows after those constructors run.
|
2007-09-14 12:15:44 -07:00
|
|
|
if (!mIsDestroying) {
|
|
|
|
mDocument->BindingManager()->ProcessAttachedQueue();
|
|
|
|
}
|
|
|
|
|
2007-09-17 18:16:39 -07:00
|
|
|
// Now those constructors might have posted restyle events. At the same
|
|
|
|
// time, we still need up-to-date style data. In particular, reflow
|
|
|
|
// depends on style being completely up to date. If it's not, then style
|
|
|
|
// context reparenting, which can happen during reflow, might suddenly pick
|
|
|
|
// up the new rules and we'll end up with frames whose style doesn't match
|
|
|
|
// the frame type.
|
|
|
|
if (!mIsDestroying) {
|
2009-01-02 07:41:43 -08:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2007-09-17 18:16:39 -07:00
|
|
|
mFrameConstructor->ProcessPendingRestyles();
|
|
|
|
}
|
|
|
|
|
2008-11-25 15:22:38 -08:00
|
|
|
|
2007-09-17 18:16:39 -07:00
|
|
|
// There might be more pending constructors now, but we're not going to
|
|
|
|
// worry about them. They can't be triggered during reflow, so we should
|
|
|
|
// be good.
|
2009-04-21 16:53:52 -07:00
|
|
|
|
|
|
|
if (aType >= (mSuppressInterruptibleReflows ? Flush_Layout : Flush_InterruptibleLayout) &&
|
|
|
|
!mIsDestroying) {
|
2007-04-15 17:11:34 -07:00
|
|
|
mFrameConstructor->RecalcQuotesAndCounters();
|
2008-09-09 06:43:10 -07:00
|
|
|
mViewManager->FlushDelayedResize();
|
2009-04-21 16:53:52 -07:00
|
|
|
if (ProcessReflowCommands(aType < Flush_Layout) && mContentToScrollTo) {
|
|
|
|
// We didn't get interrupted. Go ahead and scroll to our content
|
|
|
|
DoScrollContentIntoView(mContentToScrollTo, mContentScrollVPosition,
|
|
|
|
mContentScrollHPosition);
|
|
|
|
mContentToScrollTo = nsnull;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
PRUint32 updateFlags = NS_VMREFRESH_NO_SYNC;
|
2007-08-21 19:57:06 -07:00
|
|
|
if (aType >= Flush_Display) {
|
2007-03-22 10:30:00 -07:00
|
|
|
// Flushing paints, so perform the invalidates and drawing
|
|
|
|
// immediately
|
|
|
|
updateFlags = NS_VMREFRESH_IMMEDIATE;
|
|
|
|
}
|
2009-04-21 16:53:52 -07:00
|
|
|
else if (aType < Flush_InterruptibleLayout) {
|
2007-03-22 10:30:00 -07:00
|
|
|
// Not flushing reflows, so do deferred invalidates. This will keep us
|
|
|
|
// from possibly flushing out reflows due to invalidates being processed
|
|
|
|
// at the end of this view batch.
|
|
|
|
updateFlags = NS_VMREFRESH_DEFERRED;
|
|
|
|
}
|
2008-01-26 15:59:50 -08:00
|
|
|
batch.EndUpdateViewBatch(updateFlags);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::IsReflowLocked(PRBool* aIsReflowLocked)
|
|
|
|
{
|
|
|
|
*aIsReflowLocked = mIsReflowing;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::CharacterDataChanged(nsIDocument *aDocument,
|
|
|
|
nsIContent* aContent,
|
|
|
|
CharacterDataChangeInfo* aInfo)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(!mIsDocumentGone, "Unexpected CharacterDataChanged");
|
|
|
|
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
|
|
|
|
|
2008-12-20 06:28:19 -08:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2008-03-14 16:08:57 -07:00
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mCaret) {
|
|
|
|
// Invalidate the caret's current location before we call into the frame
|
|
|
|
// constructor. It is important to do this now, and not wait until the
|
|
|
|
// resulting reflow, because this call causes continuation frames of the
|
|
|
|
// text frame the caret is in to forget what part of the content they
|
|
|
|
// refer to, making it hard for them to return the correct continuation
|
|
|
|
// frame to the caret.
|
|
|
|
mCaret->InvalidateOutsideCaret();
|
|
|
|
}
|
2008-02-18 22:17:07 -08:00
|
|
|
|
|
|
|
// Call this here so it only happens for real content mutations and
|
|
|
|
// not cases when the frame constructor calls its own methods to force
|
|
|
|
// frame reconstruction.
|
|
|
|
nsIContent *container = aContent->GetParent();
|
|
|
|
PRUint32 selectorFlags =
|
|
|
|
container ? (container->GetFlags() & NODE_ALL_SELECTOR_FLAGS) : 0;
|
2008-07-22 21:50:20 -07:00
|
|
|
if (selectorFlags != 0 && !aContent->IsRootOfAnonymousSubtree()) {
|
2008-02-18 22:17:07 -08:00
|
|
|
PRUint32 index;
|
|
|
|
if (aInfo->mAppend &&
|
|
|
|
container->GetChildAt((index = container->GetChildCount() - 1)) ==
|
|
|
|
aContent)
|
|
|
|
mFrameConstructor->RestyleForAppend(container, index);
|
|
|
|
else
|
|
|
|
mFrameConstructor->RestyleForInsertOrChange(container, aContent);
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mFrameConstructor->CharacterDataChanged(aContent, aInfo->mAppend);
|
|
|
|
VERIFY_STYLE_TREE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::ContentStatesChanged(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContent1,
|
|
|
|
nsIContent* aContent2,
|
|
|
|
PRInt32 aStateMask)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentStatesChanged");
|
|
|
|
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
|
|
|
|
|
2007-07-12 13:05:45 -07:00
|
|
|
if (mDidInitialReflow) {
|
2008-12-20 06:28:19 -08:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2007-07-12 13:05:45 -07:00
|
|
|
mFrameConstructor->ContentStatesChanged(aContent1, aContent2, aStateMask);
|
|
|
|
VERIFY_STYLE_TREE;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::AttributeChanged(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContent,
|
|
|
|
PRInt32 aNameSpaceID,
|
|
|
|
nsIAtom* aAttribute,
|
2007-07-12 13:05:45 -07:00
|
|
|
PRInt32 aModType,
|
|
|
|
PRUint32 aStateMask)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
NS_PRECONDITION(!mIsDocumentGone, "Unexpected AttributeChanged");
|
|
|
|
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
|
|
|
|
|
|
|
|
// XXXwaterson it might be more elegant to wait until after the
|
|
|
|
// initial reflow to begin observing the document. That would
|
|
|
|
// squelch any other inappropriate notifications as well.
|
|
|
|
if (mDidInitialReflow) {
|
2008-12-20 06:28:19 -08:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2007-03-22 10:30:00 -07:00
|
|
|
mFrameConstructor->AttributeChanged(aContent, aNameSpaceID,
|
2007-07-12 13:05:45 -07:00
|
|
|
aAttribute, aModType, aStateMask);
|
2007-03-22 10:30:00 -07:00
|
|
|
VERIFY_STYLE_TREE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::ContentAppended(nsIDocument *aDocument,
|
|
|
|
nsIContent* aContainer,
|
|
|
|
PRInt32 aNewIndexInContainer)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentAppended");
|
|
|
|
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
|
2008-02-18 22:17:07 -08:00
|
|
|
NS_PRECONDITION(aContainer, "must have container");
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (!mDidInitialReflow) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-12-20 06:28:19 -08:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2007-03-22 10:30:00 -07:00
|
|
|
MOZ_TIMER_DEBUGLOG(("Start: Frame Creation: PresShell::ContentAppended(), this=%p\n", this));
|
|
|
|
MOZ_TIMER_START(mFrameCreationWatch);
|
|
|
|
|
2008-02-18 22:17:07 -08:00
|
|
|
// Call this here so it only happens for real content mutations and
|
|
|
|
// not cases when the frame constructor calls its own methods to force
|
|
|
|
// frame reconstruction.
|
|
|
|
mFrameConstructor->RestyleForAppend(aContainer, aNewIndexInContainer);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mFrameConstructor->ContentAppended(aContainer, aNewIndexInContainer);
|
|
|
|
VERIFY_STYLE_TREE;
|
|
|
|
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: Frame Creation: PresShell::ContentAppended(), this=%p\n", this));
|
|
|
|
MOZ_TIMER_STOP(mFrameCreationWatch);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::ContentInserted(nsIDocument* aDocument,
|
|
|
|
nsIContent* aContainer,
|
|
|
|
nsIContent* aChild,
|
|
|
|
PRInt32 aIndexInContainer)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentInserted");
|
|
|
|
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
|
|
|
|
|
|
|
|
if (!mDidInitialReflow) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-12-20 06:28:19 -08:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2008-02-18 22:17:07 -08:00
|
|
|
|
|
|
|
// Call this here so it only happens for real content mutations and
|
|
|
|
// not cases when the frame constructor calls its own methods to force
|
|
|
|
// frame reconstruction.
|
|
|
|
if (aContainer)
|
|
|
|
mFrameConstructor->RestyleForInsertOrChange(aContainer, aChild);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
mFrameConstructor->ContentInserted(aContainer, aChild,
|
2007-08-15 16:20:25 -07:00
|
|
|
aIndexInContainer, nsnull);
|
2007-03-22 10:30:00 -07:00
|
|
|
VERIFY_STYLE_TREE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::ContentRemoved(nsIDocument *aDocument,
|
|
|
|
nsIContent* aContainer,
|
|
|
|
nsIContent* aChild,
|
|
|
|
PRInt32 aIndexInContainer)
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(!mIsDocumentGone, "Unexpected ContentRemoved");
|
|
|
|
NS_PRECONDITION(aDocument == mDocument, "Unexpected aDocument");
|
|
|
|
|
|
|
|
// Make sure that the caret doesn't leave a turd where the child used to be.
|
|
|
|
if (mCaret) {
|
|
|
|
mCaret->InvalidateOutsideCaret();
|
|
|
|
}
|
|
|
|
|
|
|
|
// Notify the ESM that the content has been removed, so that
|
|
|
|
// it can clean up any state related to the content.
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
mPresContext->EventStateManager()->ContentRemoved(aDocument, aChild);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-12-20 06:28:19 -08:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2008-02-18 22:17:07 -08:00
|
|
|
|
|
|
|
// Call this here so it only happens for real content mutations and
|
|
|
|
// not cases when the frame constructor calls its own methods to force
|
|
|
|
// frame reconstruction.
|
|
|
|
if (aContainer)
|
|
|
|
mFrameConstructor->RestyleForRemove(aContainer, aChild, aIndexInContainer);
|
|
|
|
|
2007-11-07 20:39:02 -08:00
|
|
|
PRBool didReconstruct;
|
2009-06-13 05:18:06 -07:00
|
|
|
mFrameConstructor->ContentRemoved(aContainer, aChild,
|
|
|
|
aIndexInContainer, &didReconstruct);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
VERIFY_STYLE_TREE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
PresShell::ReconstructFrames(void)
|
|
|
|
{
|
2009-03-08 12:01:02 -07:00
|
|
|
if (!mPresContext || !mPresContext->IsDynamic()) {
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2008-12-20 06:28:19 -08:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2008-07-11 14:15:11 -07:00
|
|
|
mFrameConstructor->BeginUpdate();
|
2008-03-14 16:08:57 -07:00
|
|
|
nsresult rv = mFrameConstructor->ReconstructDocElementHierarchy();
|
2007-03-22 10:30:00 -07:00
|
|
|
VERIFY_STYLE_TREE;
|
2008-07-11 14:15:11 -07:00
|
|
|
mFrameConstructor->EndUpdate();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsIPresShell::ReconstructStyleDataInternal()
|
|
|
|
{
|
|
|
|
mStylesHaveChanged = PR_FALSE;
|
|
|
|
|
2008-11-25 15:08:51 -08:00
|
|
|
if (mIsDestroying) {
|
|
|
|
// We don't want to mess with restyles at this point
|
2008-11-25 13:27:54 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2008-11-25 15:22:38 -08:00
|
|
|
if (mPresContext) {
|
|
|
|
mPresContext->RebuildUserFontSet();
|
|
|
|
}
|
|
|
|
|
2008-11-25 15:08:51 -08:00
|
|
|
nsIContent* root = mDocument->GetRootContent();
|
2008-11-25 15:22:38 -08:00
|
|
|
if (!mDidInitialReflow) {
|
|
|
|
// Nothing to do here, since we have no frames yet
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!root) {
|
|
|
|
// No content to restyle
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
mFrameConstructor->PostRestyleEvent(root, eReStyle_Self, NS_STYLE_HINT_NONE);
|
|
|
|
|
|
|
|
#ifdef ACCESSIBILITY
|
|
|
|
InvalidateAccessibleSubtree(nsnull);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
nsIPresShell::ReconstructStyleDataExternal()
|
|
|
|
{
|
|
|
|
ReconstructStyleDataInternal();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::StyleSheetAdded(nsIDocument *aDocument,
|
|
|
|
nsIStyleSheet* aStyleSheet,
|
|
|
|
PRBool aDocumentSheet)
|
|
|
|
{
|
|
|
|
// We only care when enabled sheets are added
|
|
|
|
NS_PRECONDITION(aStyleSheet, "Must have a style sheet!");
|
|
|
|
PRBool applicable;
|
|
|
|
aStyleSheet->GetApplicable(applicable);
|
|
|
|
|
|
|
|
if (applicable && aStyleSheet->HasRules()) {
|
|
|
|
mStylesHaveChanged = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::StyleSheetRemoved(nsIDocument *aDocument,
|
|
|
|
nsIStyleSheet* aStyleSheet,
|
|
|
|
PRBool aDocumentSheet)
|
|
|
|
{
|
|
|
|
// We only care when enabled sheets are removed
|
|
|
|
NS_PRECONDITION(aStyleSheet, "Must have a style sheet!");
|
|
|
|
PRBool applicable;
|
|
|
|
aStyleSheet->GetApplicable(applicable);
|
|
|
|
if (applicable && aStyleSheet->HasRules()) {
|
|
|
|
mStylesHaveChanged = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::StyleSheetApplicableStateChanged(nsIDocument *aDocument,
|
|
|
|
nsIStyleSheet* aStyleSheet,
|
|
|
|
PRBool aApplicable)
|
|
|
|
{
|
|
|
|
if (aStyleSheet->HasRules()) {
|
|
|
|
mStylesHaveChanged = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::StyleRuleChanged(nsIDocument *aDocument,
|
|
|
|
nsIStyleSheet* aStyleSheet,
|
|
|
|
nsIStyleRule* aOldStyleRule,
|
|
|
|
nsIStyleRule* aNewStyleRule)
|
|
|
|
{
|
|
|
|
mStylesHaveChanged = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::StyleRuleAdded(nsIDocument *aDocument,
|
|
|
|
nsIStyleSheet* aStyleSheet,
|
|
|
|
nsIStyleRule* aStyleRule)
|
|
|
|
{
|
|
|
|
mStylesHaveChanged = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::StyleRuleRemoved(nsIDocument *aDocument,
|
|
|
|
nsIStyleSheet* aStyleSheet,
|
|
|
|
nsIStyleRule* aStyleRule)
|
|
|
|
{
|
|
|
|
mStylesHaveChanged = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame*
|
|
|
|
PresShell::GetPrimaryFrameFor(nsIContent* aContent) const
|
|
|
|
{
|
|
|
|
return FrameManager()->GetPrimaryFrameFor(aContent, -1);
|
|
|
|
}
|
|
|
|
|
2008-01-29 20:17:57 -08:00
|
|
|
nsIFrame*
|
|
|
|
PresShell::GetRealPrimaryFrameFor(nsIContent* aContent) const
|
|
|
|
{
|
|
|
|
nsIFrame *primaryFrame = FrameManager()->GetPrimaryFrameFor(aContent, -1);
|
|
|
|
if (!primaryFrame)
|
|
|
|
return nsnull;
|
|
|
|
return nsPlaceholderFrame::GetRealFrameFor(primaryFrame);
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::GetPlaceholderFrameFor(nsIFrame* aFrame,
|
|
|
|
nsIFrame** aResult) const
|
|
|
|
{
|
|
|
|
*aResult = FrameManager()->GetPlaceholderFrameFor(aFrame);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//nsIViewObserver
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::ComputeRepaintRegionForCopy(nsIView* aRootView,
|
|
|
|
nsIView* aMovingView,
|
|
|
|
nsPoint aDelta,
|
|
|
|
const nsRect& aCopyRect,
|
|
|
|
nsRegion* aRepaintRegion)
|
|
|
|
{
|
|
|
|
return nsLayoutUtils::ComputeRepaintRegionForCopy(
|
2007-07-08 00:08:04 -07:00
|
|
|
static_cast<nsIFrame*>(aRootView->GetClientData()),
|
|
|
|
static_cast<nsIFrame*>(aMovingView->GetClientData()),
|
2007-03-22 10:30:00 -07:00
|
|
|
aDelta, aCopyRect, aRepaintRegion);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
2008-12-12 02:50:31 -08:00
|
|
|
PresShell::RenderDocument(const nsRect& aRect, PRUint32 aFlags,
|
2007-05-22 17:11:05 -07:00
|
|
|
nscolor aBackgroundColor,
|
|
|
|
gfxContext* aThebesContext)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-12-12 02:50:31 -08:00
|
|
|
NS_ENSURE_TRUE(!(aFlags & RENDER_IS_UNTRUSTED), NS_ERROR_NOT_IMPLEMENTED);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-07 20:43:36 -08:00
|
|
|
gfxRect r(0, 0,
|
|
|
|
nsPresContext::AppUnitsToFloatCSSPixels(aRect.width),
|
|
|
|
nsPresContext::AppUnitsToFloatCSSPixels(aRect.height));
|
|
|
|
aThebesContext->Save();
|
|
|
|
|
2008-10-09 14:49:15 -07:00
|
|
|
aThebesContext->NewPath();
|
2008-10-10 15:30:52 -07:00
|
|
|
#ifdef MOZ_GFX_OPTIMIZE_MOBILE
|
2008-10-09 14:49:15 -07:00
|
|
|
aThebesContext->Rectangle(r, PR_TRUE);
|
|
|
|
#else
|
|
|
|
aThebesContext->Rectangle(r);
|
|
|
|
#endif
|
|
|
|
aThebesContext->Clip();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-10-09 14:49:15 -07:00
|
|
|
// we can avoid using a temporary surface if we're using OPERATOR_OVER
|
|
|
|
// and our background color has no alpha (so we'll be compositing on top
|
|
|
|
// of a fully opaque solid color region)
|
|
|
|
PRBool needsGroup = PR_TRUE;
|
|
|
|
if (aThebesContext->CurrentOperator() == gfxContext::OPERATOR_OVER &&
|
|
|
|
NS_GET_A(aBackgroundColor) == 0xff)
|
|
|
|
needsGroup = PR_FALSE;
|
|
|
|
|
|
|
|
if (needsGroup) {
|
|
|
|
aThebesContext->PushGroup(NS_GET_A(aBackgroundColor) == 0xff ?
|
|
|
|
gfxASurface::CONTENT_COLOR :
|
|
|
|
gfxASurface::CONTENT_COLOR_ALPHA);
|
|
|
|
|
|
|
|
aThebesContext->Save();
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-05-22 17:11:05 -07:00
|
|
|
// draw background color
|
|
|
|
if (NS_GET_A(aBackgroundColor) > 0) {
|
|
|
|
aThebesContext->SetColor(gfxRGBA(aBackgroundColor));
|
|
|
|
aThebesContext->SetOperator(gfxContext::OPERATOR_SOURCE);
|
|
|
|
aThebesContext->Paint();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2007-05-22 17:11:05 -07:00
|
|
|
// we want the window to be composited as a single image using
|
2008-10-09 14:49:15 -07:00
|
|
|
// whatever operator was set; set OPERATOR_OVER here, which is
|
|
|
|
// either already the case, or overrides the operator in a group.
|
|
|
|
// the original operator will be present when we PopGroup.
|
2007-05-22 17:11:05 -07:00
|
|
|
aThebesContext->SetOperator(gfxContext::OPERATOR_OVER);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
nsIFrame* rootFrame = FrameManager()->GetRootFrame();
|
2007-05-22 17:11:05 -07:00
|
|
|
if (rootFrame) {
|
2008-12-12 02:50:31 -08:00
|
|
|
nsDisplayListBuilder builder(rootFrame, PR_FALSE,
|
|
|
|
(aFlags & RENDER_CARET) != 0);
|
2007-05-22 17:11:05 -07:00
|
|
|
nsDisplayList list;
|
|
|
|
|
|
|
|
nsRect rect(aRect);
|
|
|
|
nsIFrame* rootScrollFrame = GetRootScrollFrame();
|
2008-12-12 02:50:31 -08:00
|
|
|
if ((aFlags & RENDER_IGNORE_VIEWPORT_SCROLLING) && rootScrollFrame) {
|
2007-05-22 17:11:05 -07:00
|
|
|
nsPoint pos = GetRootScrollFrameAsScrollable()->GetScrollPosition();
|
|
|
|
rect.MoveBy(-pos);
|
|
|
|
builder.SetIgnoreScrollFrame(rootScrollFrame);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-08-18 12:09:17 -07:00
|
|
|
builder.SetBackgroundOnly(PR_FALSE);
|
2007-05-22 17:11:05 -07:00
|
|
|
builder.EnterPresShell(rootFrame, rect);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-05-22 17:11:05 -07:00
|
|
|
nsresult rv = rootFrame->BuildDisplayListForStackingContext(&builder, rect, &list);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-05-22 17:11:05 -07:00
|
|
|
builder.LeavePresShell(rootFrame, rect);
|
|
|
|
|
|
|
|
if (NS_SUCCEEDED(rv)) {
|
|
|
|
// Ensure that r.x,r.y gets drawn at (0,0)
|
|
|
|
aThebesContext->Save();
|
2007-11-07 20:43:36 -08:00
|
|
|
aThebesContext->Translate(gfxPoint(-nsPresContext::AppUnitsToFloatCSSPixels(rect.x),
|
|
|
|
-nsPresContext::AppUnitsToFloatCSSPixels(rect.y)));
|
2007-05-22 17:11:05 -07:00
|
|
|
|
|
|
|
nsIDeviceContext* devCtx = mPresContext->DeviceContext();
|
2007-11-07 20:43:36 -08:00
|
|
|
gfxFloat scale = gfxFloat(devCtx->AppUnitsPerDevPixel())/nsPresContext::AppUnitsPerCSSPixel();
|
|
|
|
aThebesContext->Scale(scale, scale);
|
|
|
|
|
2007-05-22 17:11:05 -07:00
|
|
|
nsCOMPtr<nsIRenderingContext> rc;
|
|
|
|
devCtx->CreateRenderingContextInstance(*getter_AddRefs(rc));
|
|
|
|
rc->Init(devCtx, aThebesContext);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-05-22 17:11:05 -07:00
|
|
|
nsRegion region(rect);
|
|
|
|
list.OptimizeVisibility(&builder, ®ion);
|
|
|
|
list.Paint(&builder, rc, rect);
|
|
|
|
// Flush the list so we don't trigger the IsEmpty-on-destruction assertion
|
|
|
|
list.DeleteAll();
|
|
|
|
|
|
|
|
aThebesContext->Restore();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2008-10-09 14:49:15 -07:00
|
|
|
// if we had to use a group, paint it to the destination now
|
|
|
|
if (needsGroup) {
|
|
|
|
aThebesContext->Restore();
|
|
|
|
aThebesContext->PopGroupToSource();
|
|
|
|
aThebesContext->Paint();
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-11-07 20:43:36 -08:00
|
|
|
aThebesContext->Restore();
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Clip the display list aList to a range. Returns the clipped
|
|
|
|
* rectangle surrounding the range.
|
|
|
|
*/
|
|
|
|
nsRect
|
|
|
|
PresShell::ClipListToRange(nsDisplayListBuilder *aBuilder,
|
|
|
|
nsDisplayList* aList,
|
2007-08-16 20:43:00 -07:00
|
|
|
nsIRange* aRange)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
// iterate though the display items and add up the bounding boxes of each.
|
|
|
|
// This will allow the total area of the frames within the range to be
|
|
|
|
// determined. To do this, remove an item from the bottom of the list, check
|
|
|
|
// whether it should be part of the range, and if so, append it to the top
|
|
|
|
// of the temporary list tmpList. If the item is a text frame at the end of
|
|
|
|
// the selection range, wrap it in an nsDisplayClip to clip the display to
|
|
|
|
// the portion of the text frame that is part of the selection. Then, append
|
|
|
|
// the wrapper to the top of the list. Otherwise, just delete the item and
|
|
|
|
// don't append it.
|
|
|
|
nsRect surfaceRect;
|
|
|
|
nsDisplayList tmpList;
|
|
|
|
|
|
|
|
nsDisplayItem* i;
|
|
|
|
while ((i = aList->RemoveBottom())) {
|
|
|
|
// itemToInsert indiciates the item that should be inserted into the
|
|
|
|
// temporary list. If null, no item should be inserted.
|
|
|
|
nsDisplayItem* itemToInsert = nsnull;
|
|
|
|
nsIFrame* frame = i->GetUnderlyingFrame();
|
|
|
|
if (frame) {
|
|
|
|
nsIContent* content = frame->GetContent();
|
|
|
|
if (content) {
|
|
|
|
PRBool atStart = (content == aRange->GetStartParent());
|
|
|
|
PRBool atEnd = (content == aRange->GetEndParent());
|
|
|
|
if ((atStart || atEnd) && frame->GetType() == nsGkAtoms::textFrame) {
|
|
|
|
PRInt32 frameStartOffset, frameEndOffset;
|
|
|
|
frame->GetOffsets(frameStartOffset, frameEndOffset);
|
|
|
|
|
|
|
|
PRInt32 hilightStart =
|
|
|
|
atStart ? PR_MAX(aRange->StartOffset(), frameStartOffset) : frameStartOffset;
|
|
|
|
PRInt32 hilightEnd =
|
|
|
|
atEnd ? PR_MIN(aRange->EndOffset(), frameEndOffset) : frameEndOffset;
|
|
|
|
if (hilightStart < hilightEnd) {
|
|
|
|
// determine the location of the start and end edges of the range.
|
|
|
|
nsPoint startPoint, endPoint;
|
2007-08-16 20:43:00 -07:00
|
|
|
frame->GetPointFromOffset(hilightStart, &startPoint);
|
|
|
|
frame->GetPointFromOffset(hilightEnd, &endPoint);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// the clip rectangle is determined by taking the the start and
|
|
|
|
// end points of the range, offset from the reference frame.
|
|
|
|
// Because of rtl, the end point may be to the left of the
|
|
|
|
// start point, so x is set to the lowest value
|
|
|
|
nsRect textRect(aBuilder->ToReferenceFrame(frame), frame->GetSize());
|
|
|
|
nscoord x = PR_MIN(startPoint.x, endPoint.x);
|
|
|
|
textRect.x += x;
|
|
|
|
textRect.width = PR_MAX(startPoint.x, endPoint.x) - x;
|
|
|
|
surfaceRect.UnionRect(surfaceRect, textRect);
|
|
|
|
|
|
|
|
// wrap the item in an nsDisplayClip so that it can be clipped to
|
|
|
|
// the selection. If the allocation fails, fall through and delete
|
|
|
|
// the item below.
|
2008-04-18 02:21:21 -07:00
|
|
|
itemToInsert = new (aBuilder)nsDisplayClip(frame, frame, i, textRect);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// if the node is within the range, append it to the temporary list
|
|
|
|
PRBool before, after;
|
|
|
|
nsRange::CompareNodeToRange(content, aRange, &before, &after);
|
|
|
|
if (!before && !after) {
|
|
|
|
itemToInsert = i;
|
|
|
|
surfaceRect.UnionRect(surfaceRect, i->GetBounds(aBuilder));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// insert the item into the list if necessary. If the item has a child
|
|
|
|
// list, insert that as well
|
|
|
|
nsDisplayList* sublist = i->GetList();
|
|
|
|
if (itemToInsert || sublist) {
|
|
|
|
tmpList.AppendToTop(itemToInsert ? itemToInsert : i);
|
|
|
|
// if the item is a list, iterate over it as well
|
|
|
|
if (sublist)
|
|
|
|
surfaceRect.UnionRect(surfaceRect,
|
2007-08-16 20:43:00 -07:00
|
|
|
ClipListToRange(aBuilder, sublist, aRange));
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
// otherwise, just delete the item and don't readd it to the list
|
|
|
|
i->~nsDisplayItem();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// now add all the items back onto the original list again
|
|
|
|
aList->AppendToTop(&tmpList);
|
|
|
|
|
|
|
|
return surfaceRect;
|
|
|
|
}
|
|
|
|
|
|
|
|
RangePaintInfo*
|
|
|
|
PresShell::CreateRangePaintInfo(nsIDOMRange* aRange,
|
|
|
|
nsRect& aSurfaceRect)
|
|
|
|
{
|
|
|
|
RangePaintInfo* info = nsnull;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIRange> range = do_QueryInterface(aRange);
|
|
|
|
if (!range)
|
|
|
|
return nsnull;
|
|
|
|
|
2007-03-29 16:58:59 -07:00
|
|
|
nsIFrame* ancestorFrame;
|
|
|
|
nsIFrame* rootFrame = GetRootFrame();
|
|
|
|
|
|
|
|
// If the start or end of the range is the document, just use the root
|
|
|
|
// frame, otherwise get the common ancestor of the two endpoints of the
|
|
|
|
// range.
|
|
|
|
nsINode* startParent = range->GetStartParent();
|
|
|
|
nsINode* endParent = range->GetEndParent();
|
|
|
|
nsIDocument* doc = startParent->GetCurrentDoc();
|
|
|
|
if (startParent == doc || endParent == doc) {
|
|
|
|
ancestorFrame = rootFrame;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nsINode* ancestor = nsContentUtils::GetCommonAncestor(startParent, endParent);
|
|
|
|
NS_ASSERTION(!ancestor || ancestor->IsNodeOfType(nsINode::eCONTENT),
|
|
|
|
"common ancestor is not content");
|
|
|
|
if (!ancestor || !ancestor->IsNodeOfType(nsINode::eCONTENT))
|
|
|
|
return nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-07-08 00:08:04 -07:00
|
|
|
nsIContent* ancestorContent = static_cast<nsIContent*>(ancestor);
|
2007-03-29 16:58:59 -07:00
|
|
|
ancestorFrame = GetPrimaryFrameFor(ancestorContent);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-03-29 16:58:59 -07:00
|
|
|
// use the nearest ancestor frame that includes all continuations as the
|
|
|
|
// root for building the display list
|
|
|
|
while (ancestorFrame &&
|
|
|
|
nsLayoutUtils::GetNextContinuationOrSpecialSibling(ancestorFrame))
|
|
|
|
ancestorFrame = ancestorFrame->GetParent();
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (!ancestorFrame)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
info = new RangePaintInfo(range, ancestorFrame);
|
|
|
|
if (!info)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsRect ancestorRect = ancestorFrame->GetOverflowRect();
|
|
|
|
|
|
|
|
// get a display list containing the range
|
|
|
|
info->mBuilder.SetPaintAllFrames();
|
|
|
|
info->mBuilder.EnterPresShell(ancestorFrame, ancestorRect);
|
|
|
|
ancestorFrame->BuildDisplayListForStackingContext(&info->mBuilder,
|
|
|
|
ancestorRect, &info->mList);
|
|
|
|
info->mBuilder.LeavePresShell(ancestorFrame, ancestorRect);
|
|
|
|
|
2007-08-16 20:43:00 -07:00
|
|
|
nsRect rangeRect = ClipListToRange(&info->mBuilder, &info->mList, range);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// determine the offset of the reference frame for the display list
|
|
|
|
// to the root frame. This will allow the coordinates used when painting
|
|
|
|
// to all be offset from the same point
|
2007-03-29 16:58:59 -07:00
|
|
|
info->mRootOffset = ancestorFrame->GetOffsetTo(rootFrame);
|
2007-03-22 10:30:00 -07:00
|
|
|
rangeRect.MoveBy(info->mRootOffset);
|
|
|
|
aSurfaceRect.UnionRect(aSurfaceRect, rangeRect);
|
|
|
|
|
|
|
|
return info;
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<gfxASurface>
|
|
|
|
PresShell::PaintRangePaintInfo(nsTArray<nsAutoPtr<RangePaintInfo> >* aItems,
|
|
|
|
nsISelection* aSelection,
|
|
|
|
nsIRegion* aRegion,
|
|
|
|
nsRect aArea,
|
2009-01-14 19:27:09 -08:00
|
|
|
nsIntPoint& aPoint,
|
|
|
|
nsIntRect* aScreenRect)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
nsPresContext* pc = GetPresContext();
|
2007-05-17 03:14:35 -07:00
|
|
|
if (!pc || aArea.width == 0 || aArea.height == 0)
|
2007-03-22 10:30:00 -07:00
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
nsIDeviceContext* deviceContext = pc->DeviceContext();
|
|
|
|
|
|
|
|
// use the rectangle to create the surface
|
2009-05-07 19:31:04 -07:00
|
|
|
nsIntRect pixelArea = aArea.ToOutsidePixels(pc->AppUnitsPerDevPixel());
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// if the area of the image is larger than the maximum area, scale it down
|
|
|
|
float scale = 0.0;
|
|
|
|
nsIntRect rootScreenRect = GetRootFrame()->GetScreenRect();
|
|
|
|
|
|
|
|
// if the image is larger in one or both directions than half the size of
|
|
|
|
// the available screen area, scale the image down to that size.
|
|
|
|
nsRect maxSize;
|
|
|
|
deviceContext->GetClientRect(maxSize);
|
|
|
|
nscoord maxWidth = pc->AppUnitsToDevPixels(maxSize.width >> 1);
|
|
|
|
nscoord maxHeight = pc->AppUnitsToDevPixels(maxSize.height >> 1);
|
|
|
|
PRBool resize = (pixelArea.width > maxWidth || pixelArea.height > maxHeight);
|
|
|
|
if (resize) {
|
|
|
|
scale = 1.0;
|
|
|
|
// divide the maximum size by the image size in both directions. Whichever
|
|
|
|
// direction produces the smallest result determines how much should be
|
|
|
|
// scaled.
|
|
|
|
if (pixelArea.width > maxWidth)
|
|
|
|
scale = PR_MIN(scale, float(maxWidth) / pixelArea.width);
|
|
|
|
if (pixelArea.height > maxHeight)
|
|
|
|
scale = PR_MIN(scale, float(maxHeight) / pixelArea.height);
|
|
|
|
|
|
|
|
pixelArea.width = NSToIntFloor(float(pixelArea.width) * scale);
|
|
|
|
pixelArea.height = NSToIntFloor(float(pixelArea.height) * scale);
|
|
|
|
|
|
|
|
// adjust the screen position based on the rescaled size
|
|
|
|
nscoord left = rootScreenRect.x + pixelArea.x;
|
|
|
|
nscoord top = rootScreenRect.y + pixelArea.y;
|
|
|
|
aScreenRect->x = NSToIntFloor(aPoint.x - float(aPoint.x - left) * scale);
|
|
|
|
aScreenRect->y = NSToIntFloor(aPoint.y - float(aPoint.y - top) * scale);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// move aScreenRect to the position of the surface in screen coordinates
|
|
|
|
aScreenRect->MoveTo(rootScreenRect.x + pixelArea.x, rootScreenRect.y + pixelArea.y);
|
|
|
|
}
|
|
|
|
aScreenRect->width = pixelArea.width;
|
|
|
|
aScreenRect->height = pixelArea.height;
|
|
|
|
|
|
|
|
gfxImageSurface* surface =
|
|
|
|
new gfxImageSurface(gfxIntSize(pixelArea.width, pixelArea.height),
|
|
|
|
gfxImageSurface::ImageFormatARGB32);
|
2007-05-17 03:14:35 -07:00
|
|
|
if (!surface || surface->CairoStatus()) {
|
|
|
|
delete surface;
|
2007-03-22 10:30:00 -07:00
|
|
|
return nsnull;
|
2007-05-17 03:14:35 -07:00
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// clear the image
|
|
|
|
gfxContext context(surface);
|
|
|
|
context.SetOperator(gfxContext::OPERATOR_CLEAR);
|
|
|
|
context.Rectangle(gfxRect(0, 0, pixelArea.width, pixelArea.height));
|
|
|
|
context.Fill();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIRenderingContext> rc;
|
|
|
|
deviceContext->CreateRenderingContextInstance(*getter_AddRefs(rc));
|
|
|
|
rc->Init(deviceContext, surface);
|
|
|
|
|
|
|
|
if (aRegion)
|
|
|
|
rc->SetClipRegion(*aRegion, nsClipCombine_kReplace);
|
|
|
|
|
|
|
|
if (resize)
|
|
|
|
rc->Scale(scale, scale);
|
|
|
|
|
|
|
|
// translate so that points are relative to the surface area
|
|
|
|
rc->Translate(-aArea.x, -aArea.y);
|
|
|
|
|
|
|
|
// temporarily hide the selection so that text is drawn normally. If a
|
|
|
|
// selection is being rendered, use that, otherwise use the presshell's
|
|
|
|
// selection.
|
|
|
|
nsCOMPtr<nsFrameSelection> frameSelection;
|
|
|
|
if (aSelection) {
|
|
|
|
nsCOMPtr<nsISelectionPrivate> selpriv = do_QueryInterface(aSelection);
|
|
|
|
selpriv->GetFrameSelection(getter_AddRefs(frameSelection));
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
frameSelection = FrameSelection();
|
|
|
|
}
|
|
|
|
PRInt16 oldDisplaySelection = frameSelection->GetDisplaySelection();
|
|
|
|
frameSelection->SetDisplaySelection(nsISelectionController::SELECTION_HIDDEN);
|
|
|
|
|
|
|
|
// next, paint each range in the selection
|
|
|
|
PRInt32 count = aItems->Length();
|
|
|
|
for (PRInt32 i = 0; i < count; i++) {
|
|
|
|
RangePaintInfo* rangeInfo = (*aItems)[i];
|
|
|
|
// the display lists paint relative to the offset from the reference
|
|
|
|
// frame, so translate the rendering context
|
|
|
|
nsIRenderingContext::AutoPushTranslation
|
|
|
|
translate(rc, rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y);
|
|
|
|
|
|
|
|
aArea.MoveBy(-rangeInfo->mRootOffset.x, -rangeInfo->mRootOffset.y);
|
|
|
|
rangeInfo->mList.Paint(&rangeInfo->mBuilder, rc, aArea);
|
|
|
|
aArea.MoveBy(rangeInfo->mRootOffset.x, rangeInfo->mRootOffset.y);
|
|
|
|
}
|
|
|
|
|
|
|
|
// restore the old selection display state
|
|
|
|
frameSelection->SetDisplaySelection(oldDisplaySelection);
|
|
|
|
|
|
|
|
NS_ADDREF(surface);
|
|
|
|
return surface;
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<gfxASurface>
|
|
|
|
PresShell::RenderNode(nsIDOMNode* aNode,
|
|
|
|
nsIRegion* aRegion,
|
2009-01-14 19:27:09 -08:00
|
|
|
nsIntPoint& aPoint,
|
|
|
|
nsIntRect* aScreenRect)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
// area will hold the size of the surface needed to draw the node, measured
|
|
|
|
// from the root frame.
|
|
|
|
nsRect area;
|
|
|
|
nsTArray<nsAutoPtr<RangePaintInfo> > rangeItems;
|
|
|
|
|
2008-08-27 07:46:29 -07:00
|
|
|
// nothing to draw if the node isn't in a document
|
|
|
|
nsCOMPtr<nsINode> node = do_QueryInterface(aNode);
|
|
|
|
if (!node->IsInDoc())
|
|
|
|
return nsnull;
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIDOMRange> range;
|
|
|
|
NS_NewRange(getter_AddRefs(range));
|
2008-10-05 20:30:09 -07:00
|
|
|
if (NS_FAILED(range->SelectNode(aNode)))
|
|
|
|
return nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2007-08-16 20:43:00 -07:00
|
|
|
RangePaintInfo* info = CreateRangePaintInfo(range, area);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (info && !rangeItems.AppendElement(info)) {
|
|
|
|
delete info;
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aRegion) {
|
|
|
|
// combine the area with the supplied region
|
2009-01-14 19:27:09 -08:00
|
|
|
nsIntRect rrectPixels;
|
2007-03-22 10:30:00 -07:00
|
|
|
aRegion->GetBoundingBox(&rrectPixels.x, &rrectPixels.y,
|
|
|
|
&rrectPixels.width, &rrectPixels.height);
|
|
|
|
|
2009-05-07 19:31:04 -07:00
|
|
|
nsRect rrect = rrectPixels.ToAppUnits(nsPresContext::AppUnitsPerCSSPixel());
|
2007-03-22 10:30:00 -07:00
|
|
|
area.IntersectRect(area, rrect);
|
|
|
|
|
|
|
|
nsPresContext* pc = GetPresContext();
|
|
|
|
if (!pc)
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
// move the region so that it is offset from the topleft corner of the surface
|
|
|
|
aRegion->Offset(-rrectPixels.x + (rrectPixels.x - pc->AppUnitsToDevPixels(area.x)),
|
|
|
|
-rrectPixels.y + (rrectPixels.y - pc->AppUnitsToDevPixels(area.y)));
|
|
|
|
}
|
|
|
|
|
|
|
|
return PaintRangePaintInfo(&rangeItems, nsnull, aRegion, area, aPoint,
|
|
|
|
aScreenRect);
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<gfxASurface>
|
|
|
|
PresShell::RenderSelection(nsISelection* aSelection,
|
2009-01-14 19:27:09 -08:00
|
|
|
nsIntPoint& aPoint,
|
|
|
|
nsIntRect* aScreenRect)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
|
|
|
// area will hold the size of the surface needed to draw the selection,
|
|
|
|
// measured from the root frame.
|
|
|
|
nsRect area;
|
|
|
|
nsTArray<nsAutoPtr<RangePaintInfo> > rangeItems;
|
|
|
|
|
|
|
|
// iterate over each range and collect them into the rangeItems array.
|
|
|
|
// This is done so that the size of selection can be determined so as
|
|
|
|
// to allocate a surface area
|
|
|
|
PRInt32 numRanges;
|
|
|
|
aSelection->GetRangeCount(&numRanges);
|
2007-05-17 03:14:35 -07:00
|
|
|
NS_ASSERTION(numRanges > 0, "RenderSelection called with no selection");
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
for (PRInt32 r = 0; r < numRanges; r++)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMRange> range;
|
|
|
|
aSelection->GetRangeAt(r, getter_AddRefs(range));
|
|
|
|
|
2007-08-16 20:43:00 -07:00
|
|
|
RangePaintInfo* info = CreateRangePaintInfo(range, area);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (info && !rangeItems.AppendElement(info)) {
|
|
|
|
delete info;
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PaintRangePaintInfo(&rangeItems, aSelection, nsnull, area, aPoint,
|
|
|
|
aScreenRect);
|
|
|
|
}
|
|
|
|
|
2009-04-25 01:19:23 -07:00
|
|
|
void PresShell::UpdateCanvasBackground()
|
2009-04-18 00:11:29 -07:00
|
|
|
{
|
2009-04-18 01:53:27 -07:00
|
|
|
// If we have a frame tree and it has style information that
|
|
|
|
// specifies the background color of the canvas, update our local
|
|
|
|
// cache of that color.
|
|
|
|
nsIFrame* rootFrame = FrameConstructor()->GetRootElementStyleFrame();
|
|
|
|
if (rootFrame) {
|
|
|
|
const nsStyleBackground* bgStyle =
|
|
|
|
nsCSSRendering::FindRootFrameBackground(rootFrame);
|
|
|
|
mCanvasBackgroundColor = bgStyle->mBackgroundColor;
|
|
|
|
}
|
2009-04-25 01:19:23 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::Paint(nsIView* aView,
|
|
|
|
nsIRenderingContext* aRenderingContext,
|
|
|
|
const nsRegion& aDirtyRegion)
|
|
|
|
{
|
|
|
|
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Paint);
|
|
|
|
|
|
|
|
NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
|
|
|
|
NS_ASSERTION(aView, "null view");
|
|
|
|
|
|
|
|
UpdateCanvasBackground();
|
2009-02-22 17:10:23 -08:00
|
|
|
|
|
|
|
// Compute the backstop color for the view.
|
|
|
|
nscolor bgcolor;
|
|
|
|
nsIWidget* widget = aView->GetNearestWidget(nsnull);
|
|
|
|
if (widget && widget->GetTransparencyMode() != eTransparencyOpaque) {
|
|
|
|
// Within a transparent widget, so the backstop color must be
|
|
|
|
// totally transparent.
|
|
|
|
bgcolor = NS_RGBA(0,0,0,0);
|
2009-02-15 17:11:34 -08:00
|
|
|
} else {
|
2009-02-22 17:10:23 -08:00
|
|
|
// Within an opaque widget (or no widget at all), so the backstop
|
|
|
|
// color must be totally opaque. The cached canvas background
|
|
|
|
// color is not guaranteed to be opaque, but the user's default
|
|
|
|
// background as reported by the prescontext is. Composing the
|
|
|
|
// former on top of the latter prevents window flashing in between
|
|
|
|
// pages that use the same non-default background.
|
|
|
|
bgcolor = NS_ComposeColors(mPresContext->DefaultBackgroundColor(),
|
|
|
|
mCanvasBackgroundColor);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-02-22 17:10:23 -08:00
|
|
|
nsIFrame* frame = static_cast<nsIFrame*>(aView->GetClientData());
|
|
|
|
if (frame) {
|
|
|
|
nsLayoutUtils::PaintFrame(aRenderingContext, frame, aDirtyRegion, bgcolor);
|
|
|
|
} else {
|
|
|
|
aRenderingContext->SetColor(bgcolor);
|
|
|
|
aRenderingContext->FillRect(aDirtyRegion.GetBounds());
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::PaintDefaultBackground(nsIView* aView,
|
|
|
|
nsIRenderingContext* aRenderingContext,
|
|
|
|
const nsRect& aDirtyRect)
|
|
|
|
{
|
|
|
|
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Paint);
|
|
|
|
|
|
|
|
NS_ASSERTION(!mIsDestroying, "painting a destroyed PresShell");
|
|
|
|
NS_ASSERTION(aView, "null view");
|
|
|
|
|
|
|
|
// The view manager does not call this function if there is no
|
|
|
|
// widget or it is transparent. We must not look at the frame tree,
|
|
|
|
// so all we have to use is the canvas default color as set above,
|
|
|
|
// or failing that, the user's default color.
|
|
|
|
|
|
|
|
nscolor bgcolor = NS_ComposeColors(mPresContext->DefaultBackgroundColor(),
|
|
|
|
mCanvasBackgroundColor);
|
|
|
|
|
|
|
|
aRenderingContext->SetColor(bgcolor);
|
|
|
|
aRenderingContext->FillRect(aDirtyRect);
|
2008-12-13 08:07:08 -08:00
|
|
|
return NS_OK;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame*
|
|
|
|
PresShell::GetCurrentEventFrame()
|
|
|
|
{
|
2008-12-07 15:10:31 -08:00
|
|
|
if (NS_UNLIKELY(mIsDestroying)) {
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!mCurrentEventFrame && mCurrentEventContent) {
|
|
|
|
// Make sure the content still has a document reference. If not,
|
|
|
|
// then we assume it is no longer in the content tree and the
|
|
|
|
// frame shouldn't get an event, nor should we even assume its
|
|
|
|
// safe to try and find the frame.
|
|
|
|
if (mCurrentEventContent->GetDocument()) {
|
|
|
|
mCurrentEventFrame = GetPrimaryFrameFor(mCurrentEventContent);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return mCurrentEventFrame;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::GetEventTargetFrame(nsIFrame** aFrame)
|
|
|
|
{
|
|
|
|
*aFrame = GetCurrentEventFrame();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::GetEventTargetContent(nsEvent* aEvent, nsIContent** aContent)
|
|
|
|
{
|
|
|
|
if (mCurrentEventContent) {
|
|
|
|
*aContent = mCurrentEventContent;
|
|
|
|
NS_IF_ADDREF(*aContent);
|
|
|
|
} else {
|
|
|
|
nsIFrame* currentEventFrame = GetCurrentEventFrame();
|
|
|
|
if (currentEventFrame) {
|
|
|
|
currentEventFrame->GetContentForEvent(mPresContext, aEvent, aContent);
|
|
|
|
} else {
|
|
|
|
*aContent = nsnull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::PushCurrentEventInfo(nsIFrame* aFrame, nsIContent* aContent)
|
|
|
|
{
|
|
|
|
if (mCurrentEventFrame || mCurrentEventContent) {
|
2009-02-03 06:42:18 -08:00
|
|
|
mCurrentEventFrameStack.InsertElementAt(0, mCurrentEventFrame);
|
2007-03-22 10:30:00 -07:00
|
|
|
mCurrentEventContentStack.InsertObjectAt(mCurrentEventContent, 0);
|
|
|
|
}
|
|
|
|
mCurrentEventFrame = aFrame;
|
|
|
|
mCurrentEventContent = aContent;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::PopCurrentEventInfo()
|
|
|
|
{
|
|
|
|
mCurrentEventFrame = nsnull;
|
|
|
|
mCurrentEventContent = nsnull;
|
|
|
|
|
2009-02-03 06:42:18 -08:00
|
|
|
if (0 != mCurrentEventFrameStack.Length()) {
|
|
|
|
mCurrentEventFrame = mCurrentEventFrameStack.ElementAt(0);
|
2007-03-22 10:30:00 -07:00
|
|
|
mCurrentEventFrameStack.RemoveElementAt(0);
|
|
|
|
mCurrentEventContent = mCurrentEventContentStack.ObjectAt(0);
|
|
|
|
mCurrentEventContentStack.RemoveObjectAt(0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool PresShell::InZombieDocument(nsIContent *aContent)
|
|
|
|
{
|
|
|
|
// If a content node points to a null document, or the document is not
|
|
|
|
// attached to a window, then it is possibly in a zombie document,
|
|
|
|
// about to be replaced by a newly loading document.
|
|
|
|
// Such documents cannot handle DOM events.
|
|
|
|
// It might actually be in a node not attached to any document,
|
|
|
|
// in which case there is not parent presshell to retarget it to.
|
|
|
|
nsIDocument *doc = aContent->GetDocument();
|
|
|
|
return !doc || !doc->GetWindow();
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult PresShell::RetargetEventToParent(nsGUIEvent* aEvent,
|
|
|
|
nsEventStatus* aEventStatus)
|
|
|
|
{
|
|
|
|
// Send this events straight up to the parent pres shell.
|
|
|
|
// We do this for keystroke events in zombie documents or if either a frame
|
|
|
|
// or a root content is not present.
|
|
|
|
// That way at least the UI key bindings can work.
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPresShell> kungFuDeathGrip(this);
|
|
|
|
nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
|
|
|
|
if (!container)
|
|
|
|
container = do_QueryReferent(mForwardingContainer);
|
|
|
|
|
|
|
|
// Now, find the parent pres shell and send the event there
|
|
|
|
nsCOMPtr<nsIDocShellTreeItem> treeItem =
|
|
|
|
do_QueryInterface(container);
|
2008-09-28 12:14:28 -07:00
|
|
|
if (!treeItem) {
|
|
|
|
// Might have gone away, or never been around to start with
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIDocShellTreeItem> parentTreeItem;
|
|
|
|
treeItem->GetParent(getter_AddRefs(parentTreeItem));
|
|
|
|
nsCOMPtr<nsIDocShell> parentDocShell =
|
|
|
|
do_QueryInterface(parentTreeItem);
|
|
|
|
if (!parentDocShell || treeItem == parentTreeItem) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIPresShell> parentPresShell;
|
|
|
|
parentDocShell->GetPresShell(getter_AddRefs(parentPresShell));
|
|
|
|
nsCOMPtr<nsIViewObserver> parentViewObserver =
|
|
|
|
do_QueryInterface(parentPresShell);
|
|
|
|
if (!parentViewObserver) {
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Fake the event as though it'ss from the parent pres shell's root view.
|
|
|
|
nsIView *parentRootView;
|
|
|
|
parentPresShell->GetViewManager()->GetRootView(parentRootView);
|
|
|
|
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
sDontRetargetEvents = PR_TRUE;
|
|
|
|
nsresult rv = parentViewObserver->HandleEvent(parentRootView, aEvent, aEventStatus);
|
|
|
|
sDontRetargetEvents = PR_FALSE;
|
|
|
|
return rv;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2009-02-12 02:44:38 -08:00
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::DisableNonTestMouseEvents(PRBool aDisable)
|
|
|
|
{
|
|
|
|
sDisableNonTestMouseEvents = aDisable;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::HandleEvent(nsIView *aView,
|
|
|
|
nsGUIEvent* aEvent,
|
|
|
|
nsEventStatus* aEventStatus)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aView, "null view");
|
|
|
|
|
2009-02-12 02:44:38 -08:00
|
|
|
if (mIsDestroying || !nsContentUtils::IsSafeToRunScript() ||
|
|
|
|
(sDisableNonTestMouseEvents && NS_IS_MOUSE_EVENT(aEvent) &&
|
|
|
|
!(aEvent->flags & NS_EVENT_FLAG_SYNTETIC_TEST_EVENT))) {
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef ACCESSIBILITY
|
|
|
|
if (aEvent->eventStructType == NS_ACCESSIBLE_EVENT) {
|
2008-03-18 18:37:09 -07:00
|
|
|
// Accessibility events come through OS requests and not from scripts,
|
|
|
|
// so it is safe to handle here
|
2007-03-22 10:30:00 -07:00
|
|
|
return HandleEventInternal(aEvent, aView, aEventStatus);
|
|
|
|
}
|
|
|
|
#endif
|
2008-04-18 10:20:11 -07:00
|
|
|
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
// key and IME events must be targeted at the presshell for the focused frame
|
|
|
|
if (!sDontRetargetEvents &&
|
|
|
|
(NS_IS_KEY_EVENT(aEvent) || NS_IS_IME_EVENT(aEvent) ||
|
|
|
|
NS_IS_CONTEXT_MENU_KEY(aEvent))) {
|
|
|
|
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
|
|
|
if (!fm)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMWindow> window;
|
|
|
|
fm->GetFocusedWindow(getter_AddRefs(window));
|
|
|
|
|
|
|
|
// if there is no focused frame, there isn't anything to fire a key event
|
|
|
|
// at so just return
|
|
|
|
nsCOMPtr<nsPIDOMWindow> piWindow = do_QueryInterface(window);
|
|
|
|
if (!piWindow)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDocument> doc(do_QueryInterface(piWindow->GetExtantDocument()));
|
|
|
|
if (!doc)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
nsIPresShell *presShell = doc->GetPrimaryShell();
|
|
|
|
if (!presShell)
|
|
|
|
return NS_OK;
|
|
|
|
|
|
|
|
if (presShell != this) {
|
|
|
|
nsCOMPtr<nsIViewObserver> viewObserver = do_QueryInterface(presShell);
|
|
|
|
if (!viewObserver)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsIView *view;
|
|
|
|
presShell->GetViewManager()->GetRootView(view);
|
|
|
|
sDontRetargetEvents = PR_TRUE;
|
|
|
|
nsresult rv = viewObserver->HandleEvent(view, aEvent, aEventStatus);
|
|
|
|
sDontRetargetEvents = PR_FALSE;
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// Check for a theme change up front, since the frame type is irrelevant
|
|
|
|
if (aEvent->message == NS_THEMECHANGED && mPresContext) {
|
|
|
|
mPresContext->ThemeChanged();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Check for a system color change up front, since the frame type is
|
|
|
|
// irrelevant
|
|
|
|
if ((aEvent->message == NS_SYSCOLORCHANGED) && mPresContext) {
|
|
|
|
nsIViewManager* vm = GetViewManager();
|
|
|
|
if (vm) {
|
|
|
|
// Only dispatch system color change when the message originates from
|
|
|
|
// from the root views widget. This is necessary to prevent us from
|
|
|
|
// dispatching the SysColorChanged notification for each child window
|
|
|
|
// which may be redundant.
|
|
|
|
nsIView *view;
|
|
|
|
vm->GetRootView(view);
|
|
|
|
if (view == aView) {
|
|
|
|
*aEventStatus = nsEventStatus_eConsumeDoDefault;
|
|
|
|
mPresContext->SysColorChanged();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
2009-03-03 12:20:11 -08:00
|
|
|
|
2009-03-03 12:11:14 -08:00
|
|
|
if (mDocument && mDocument->EventHandlingSuppressed()) {
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
nsDelayedEvent* event = nsnull;
|
|
|
|
if (aEvent->eventStructType == NS_KEY_EVENT) {
|
|
|
|
if (aEvent->message == NS_KEY_DOWN) {
|
|
|
|
mNoDelayedKeyEvents = PR_TRUE;
|
|
|
|
} else if (!mNoDelayedKeyEvents) {
|
|
|
|
event = new nsDelayedKeyEvent(static_cast<nsKeyEvent*>(aEvent));
|
2009-05-18 02:17:45 -07:00
|
|
|
}
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
} else if (aEvent->eventStructType == NS_MOUSE_EVENT) {
|
|
|
|
if (aEvent->message == NS_MOUSE_BUTTON_DOWN) {
|
|
|
|
mNoDelayedMouseEvents = PR_TRUE;
|
|
|
|
} else if (!mNoDelayedMouseEvents && aEvent->message == NS_MOUSE_BUTTON_UP) {
|
|
|
|
event = new nsDelayedMouseEvent(static_cast<nsMouseEvent*>(aEvent));
|
2009-05-18 02:17:45 -07:00
|
|
|
}
|
2009-03-03 12:11:14 -08:00
|
|
|
}
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
if (event && !mDelayedEvents.AppendElement(event)) {
|
|
|
|
delete event;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
2009-03-03 12:11:14 -08:00
|
|
|
}
|
|
|
|
|
2007-07-08 00:08:04 -07:00
|
|
|
nsIFrame* frame = static_cast<nsIFrame*>(aView->GetClientData());
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
PRBool dispatchUsingCoordinates =
|
|
|
|
!NS_IS_KEY_EVENT(aEvent) && !NS_IS_IME_EVENT(aEvent) &&
|
2008-12-14 19:54:54 -08:00
|
|
|
!NS_IS_CONTEXT_MENU_KEY(aEvent) && !NS_IS_FOCUS_EVENT(aEvent) &&
|
|
|
|
!NS_IS_PLUGIN_EVENT(aEvent);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// if this event has no frame, we need to retarget it at a parent
|
|
|
|
// view that has a frame.
|
|
|
|
if (!frame &&
|
|
|
|
(dispatchUsingCoordinates || NS_IS_KEY_EVENT(aEvent) ||
|
|
|
|
NS_IS_IME_EVENT(aEvent))) {
|
|
|
|
nsIView* targetView = aView;
|
|
|
|
while (targetView && !targetView->GetClientData()) {
|
|
|
|
targetView = targetView->GetParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
if (targetView) {
|
|
|
|
aView = targetView;
|
2007-07-08 00:08:04 -07:00
|
|
|
frame = static_cast<nsIFrame*>(aView->GetClientData());
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (dispatchUsingCoordinates) {
|
2009-04-22 05:55:54 -07:00
|
|
|
NS_WARN_IF_FALSE(frame, "Nothing to handle this event!");
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!frame)
|
|
|
|
return NS_OK;
|
|
|
|
|
2007-03-30 14:11:41 -07:00
|
|
|
nsPresContext* framePresContext = frame->PresContext();
|
2007-03-22 10:30:00 -07:00
|
|
|
nsPresContext* rootPresContext = framePresContext->RootPresContext();
|
2007-08-02 17:09:18 -07:00
|
|
|
NS_ASSERTION(rootPresContext == mPresContext->RootPresContext(),
|
2007-03-22 10:30:00 -07:00
|
|
|
"How did we end up outside the connected prescontext/viewmanager hierarchy?");
|
|
|
|
// If we aren't starting our event dispatch from the root frame of the root prescontext,
|
|
|
|
// then someone must be capturing the mouse. In that case we don't want to search the popup
|
|
|
|
// list.
|
|
|
|
if (framePresContext == rootPresContext &&
|
|
|
|
frame == FrameManager()->GetRootFrame()) {
|
2007-07-16 07:53:32 -07:00
|
|
|
|
2007-07-23 17:04:36 -07:00
|
|
|
#ifdef MOZ_XUL
|
2007-07-16 07:53:32 -07:00
|
|
|
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
|
|
|
if (pm) {
|
2009-02-24 00:25:25 -08:00
|
|
|
nsTArray<nsIFrame*> popups = pm->GetVisiblePopups();
|
2007-11-30 23:22:44 -08:00
|
|
|
PRUint32 i;
|
2007-07-16 07:53:32 -07:00
|
|
|
// Search from top to bottom
|
|
|
|
for (i = 0; i < popups.Length(); i++) {
|
|
|
|
nsIFrame* popup = popups[i];
|
|
|
|
if (popup->GetOverflowRect().Contains(
|
|
|
|
nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, popup))) {
|
|
|
|
// The event should target the popup
|
|
|
|
frame = popup;
|
|
|
|
break;
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
2007-07-23 17:04:36 -07:00
|
|
|
#endif
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsPoint eventPoint
|
|
|
|
= nsLayoutUtils::GetEventCoordinatesRelativeTo(aEvent, frame);
|
2008-01-29 20:53:46 -08:00
|
|
|
nsIFrame* targetFrame;
|
|
|
|
{
|
|
|
|
nsAutoDisableGetUsedXAssertions disableAssert;
|
2008-11-28 11:53:33 -08:00
|
|
|
PRBool ignoreRootScrollFrame = PR_FALSE;
|
2008-10-27 13:16:23 -07:00
|
|
|
if (aEvent->eventStructType == NS_MOUSE_EVENT) {
|
2008-11-28 11:53:33 -08:00
|
|
|
ignoreRootScrollFrame = static_cast<nsMouseEvent*>(aEvent)->ignoreRootScrollFrame;
|
2008-10-27 13:16:23 -07:00
|
|
|
}
|
|
|
|
targetFrame = nsLayoutUtils::GetFrameForPoint(frame, eventPoint,
|
2008-11-28 11:53:33 -08:00
|
|
|
PR_FALSE, ignoreRootScrollFrame);
|
2008-01-29 20:53:46 -08:00
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (targetFrame) {
|
|
|
|
PresShell* shell =
|
2007-07-08 00:08:04 -07:00
|
|
|
static_cast<PresShell*>(targetFrame->PresContext()->PresShell());
|
2007-03-22 10:30:00 -07:00
|
|
|
if (shell != this) {
|
|
|
|
// Handle the event in the correct shell.
|
|
|
|
// Prevent deletion until we're done with event handling (bug 336582).
|
2007-06-23 00:39:40 -07:00
|
|
|
nsCOMPtr<nsIPresShell> kungFuDeathGrip(shell);
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIView* subshellRootView;
|
|
|
|
shell->GetViewManager()->GetRootView(subshellRootView);
|
|
|
|
// We pass the subshell's root view as the view to start from. This is
|
|
|
|
// the only correct alternative; if the event was captured then it
|
|
|
|
// must have been captured by us or some ancestor shell and we
|
|
|
|
// now ask the subshell to dispatch it normally.
|
|
|
|
return shell->HandlePositionedEvent(subshellRootView, targetFrame,
|
|
|
|
aEvent, aEventStatus);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!targetFrame) {
|
|
|
|
targetFrame = frame;
|
|
|
|
}
|
|
|
|
return HandlePositionedEvent(aView, targetFrame, aEvent, aEventStatus);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
|
|
|
if (frame) {
|
|
|
|
PushCurrentEventInfo(nsnull, nsnull);
|
|
|
|
|
|
|
|
// key and IME events go to the focused frame
|
|
|
|
if (NS_IS_KEY_EVENT(aEvent) || NS_IS_IME_EVENT(aEvent) ||
|
2008-12-14 19:54:54 -08:00
|
|
|
NS_IS_CONTEXT_MENU_KEY(aEvent) || NS_IS_PLUGIN_EVENT(aEvent)) {
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
|
|
|
if (!fm)
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIDOMElement> element;
|
|
|
|
fm->GetFocusedElement(getter_AddRefs(element));
|
|
|
|
mCurrentEventContent = do_QueryInterface(element);
|
|
|
|
|
|
|
|
// a key or IME event may come in to an inactive window. In this
|
|
|
|
// situation, look for the element that would be focused if this
|
|
|
|
// window was active.
|
|
|
|
if (!mCurrentEventContent &&
|
|
|
|
NS_TargetUnfocusedEventToLastFocusedContent(aEvent)) {
|
|
|
|
nsPIDOMWindow *win = mDocument->GetWindow();
|
|
|
|
nsCOMPtr<nsPIDOMWindow> focusedWindow;
|
|
|
|
mCurrentEventContent =
|
|
|
|
nsFocusManager::GetFocusedDescendant(win, PR_TRUE, getter_AddRefs(focusedWindow));
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
|
|
|
|
// otherwise, if there is no focused content or the focused content has
|
|
|
|
// no frame, just use the root content. This ensures that key events
|
|
|
|
// still get sent to the window properly if nothing is focused or if a
|
|
|
|
// frame goes away while it is focused.
|
|
|
|
if (!mCurrentEventContent || !GetCurrentEventFrame())
|
|
|
|
mCurrentEventContent = mDocument->GetRootContent();
|
|
|
|
mCurrentEventFrame = nsnull;
|
|
|
|
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
if (!mCurrentEventContent || InZombieDocument(mCurrentEventContent)) {
|
|
|
|
rv = RetargetEventToParent(aEvent, aEventStatus);
|
|
|
|
PopCurrentEventInfo();
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
mCurrentEventFrame = frame;
|
|
|
|
}
|
|
|
|
if (GetCurrentEventFrame()) {
|
|
|
|
rv = HandleEventInternal(aEvent, aView, aEventStatus);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NS_DEBUG
|
|
|
|
ShowEventTargetDebug();
|
|
|
|
#endif
|
|
|
|
PopCurrentEventInfo();
|
|
|
|
} else {
|
|
|
|
// Focus events need to be dispatched even if no frame was found, since
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
// we don't want the focus to be out of sync.
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (!NS_EVENT_NEEDS_FRAME(aEvent)) {
|
|
|
|
mCurrentEventFrame = nsnull;
|
|
|
|
return HandleEventInternal(aEvent, aView, aEventStatus);
|
|
|
|
}
|
|
|
|
else if (NS_IS_KEY_EVENT(aEvent)) {
|
|
|
|
// Keypress events in new blank tabs should not be completely thrown away.
|
|
|
|
// Retarget them -- the parent chrome shell might make use of them.
|
|
|
|
return RetargetEventToParent(aEvent, aEventStatus);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NS_DEBUG
|
|
|
|
void
|
|
|
|
PresShell::ShowEventTargetDebug()
|
|
|
|
{
|
|
|
|
if (nsIFrameDebug::GetShowEventTargetFrameBorder() &&
|
|
|
|
GetCurrentEventFrame()) {
|
|
|
|
if (mDrawEventTargetFrame) {
|
|
|
|
mDrawEventTargetFrame->Invalidate(
|
|
|
|
nsRect(nsPoint(0, 0), mDrawEventTargetFrame->GetSize()));
|
|
|
|
}
|
|
|
|
|
|
|
|
mDrawEventTargetFrame = mCurrentEventFrame;
|
|
|
|
mDrawEventTargetFrame->Invalidate(
|
|
|
|
nsRect(nsPoint(0, 0), mDrawEventTargetFrame->GetSize()));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
PresShell::HandlePositionedEvent(nsIView* aView,
|
|
|
|
nsIFrame* aTargetFrame,
|
|
|
|
nsGUIEvent* aEvent,
|
|
|
|
nsEventStatus* aEventStatus)
|
|
|
|
{
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
|
|
|
PushCurrentEventInfo(nsnull, nsnull);
|
|
|
|
|
|
|
|
mCurrentEventFrame = aTargetFrame;
|
|
|
|
|
|
|
|
if (mCurrentEventFrame) {
|
|
|
|
nsCOMPtr<nsIContent> targetElement;
|
|
|
|
mCurrentEventFrame->GetContentForEvent(mPresContext, aEvent,
|
|
|
|
getter_AddRefs(targetElement));
|
|
|
|
|
|
|
|
// If there is no content for this frame, target it anyway. Some
|
|
|
|
// frames can be targeted but do not have content, particularly
|
|
|
|
// windows with scrolling off.
|
|
|
|
if (targetElement) {
|
|
|
|
// Bug 103055, bug 185889: mouse events apply to *elements*, not all
|
|
|
|
// nodes. Thus we get the nearest element parent here.
|
|
|
|
// XXX we leave the frame the same even if we find an element
|
|
|
|
// parent, so that the text frame will receive the event (selection
|
|
|
|
// and friends are the ones who care about that anyway)
|
|
|
|
//
|
|
|
|
// We use weak pointers because during this tight loop, the node
|
|
|
|
// will *not* go away. And this happens on every mousemove.
|
|
|
|
while (targetElement &&
|
|
|
|
!targetElement->IsNodeOfType(nsINode::eELEMENT)) {
|
|
|
|
targetElement = targetElement->GetParent();
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we found an element, target it. Otherwise, target *nothing*.
|
|
|
|
if (!targetElement) {
|
|
|
|
mCurrentEventContent = nsnull;
|
|
|
|
mCurrentEventFrame = nsnull;
|
|
|
|
} else if (targetElement != mCurrentEventContent) {
|
|
|
|
mCurrentEventContent = targetElement;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (GetCurrentEventFrame()) {
|
|
|
|
rv = HandleEventInternal(aEvent, aView, aEventStatus);
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef NS_DEBUG
|
|
|
|
ShowEventTargetDebug();
|
|
|
|
#endif
|
|
|
|
PopCurrentEventInfo();
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::HandleEventWithTarget(nsEvent* aEvent, nsIFrame* aFrame,
|
|
|
|
nsIContent* aContent, nsEventStatus* aStatus)
|
|
|
|
{
|
|
|
|
nsresult ret;
|
|
|
|
|
|
|
|
PushCurrentEventInfo(aFrame, aContent);
|
|
|
|
ret = HandleEventInternal(aEvent, nsnull, aStatus);
|
|
|
|
PopCurrentEventInfo();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
inline PRBool
|
|
|
|
IsSynthesizedMouseMove(nsEvent* aEvent)
|
|
|
|
{
|
|
|
|
return aEvent->eventStructType == NS_MOUSE_EVENT &&
|
2007-07-08 00:08:04 -07:00
|
|
|
static_cast<nsMouseEvent*>(aEvent)->reason != nsMouseEvent::eReal;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
PresShell::HandleEventInternal(nsEvent* aEvent, nsIView *aView,
|
|
|
|
nsEventStatus* aStatus)
|
|
|
|
{
|
|
|
|
#ifdef ACCESSIBILITY
|
|
|
|
if (aEvent->eventStructType == NS_ACCESSIBLE_EVENT)
|
|
|
|
{
|
2007-07-08 00:08:04 -07:00
|
|
|
static_cast<nsAccessibleEvent*>(aEvent)->accessible = nsnull;
|
2007-03-22 10:30:00 -07:00
|
|
|
nsCOMPtr<nsIAccessibilityService> accService =
|
|
|
|
do_GetService("@mozilla.org/accessibilityService;1");
|
|
|
|
if (accService) {
|
|
|
|
nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
|
|
|
|
if (!container) {
|
|
|
|
// This presshell is not active. This often happens when a
|
|
|
|
// preshell is being held onto for fastback.
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
nsIAccessible* acc;
|
|
|
|
nsCOMPtr<nsIDOMNode> domNode(do_QueryInterface(mDocument));
|
|
|
|
NS_ASSERTION(domNode, "No dom node for doc");
|
|
|
|
accService->GetAccessibleInShell(domNode, this, &acc);
|
|
|
|
// Addref this - it's not a COM Ptr
|
|
|
|
// We'll make sure the right number of Addref's occur before
|
|
|
|
// handing this back to the accessibility client
|
2007-07-08 00:08:04 -07:00
|
|
|
static_cast<nsAccessibleEvent*>(aEvent)->accessible = acc;
|
2007-05-04 11:09:02 -07:00
|
|
|
// Ensure this is set in case a11y was activated before any
|
|
|
|
// nsPresShells existed to observe "a11y-init-or-shutdown" topic
|
|
|
|
gIsAccessibilityActive = PR_TRUE;
|
2007-03-22 10:30:00 -07:00
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
nsCOMPtr<nsIEventStateManager> manager = mPresContext->EventStateManager();
|
|
|
|
nsresult rv = NS_OK;
|
|
|
|
|
|
|
|
if (!NS_EVENT_NEEDS_FRAME(aEvent) || GetCurrentEventFrame()) {
|
|
|
|
PRBool isHandlingUserInput = PR_FALSE;
|
|
|
|
|
|
|
|
if (NS_IS_TRUSTED_EVENT(aEvent)) {
|
|
|
|
switch (aEvent->message) {
|
|
|
|
case NS_MOUSE_BUTTON_DOWN:
|
|
|
|
case NS_MOUSE_BUTTON_UP:
|
|
|
|
case NS_KEY_PRESS:
|
|
|
|
case NS_KEY_DOWN:
|
|
|
|
case NS_KEY_UP:
|
|
|
|
isHandlingUserInput = PR_TRUE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-17 12:50:00 -08:00
|
|
|
if (aEvent->message == NS_CONTEXTMENU &&
|
|
|
|
static_cast<nsMouseEvent*>(aEvent)->context == nsMouseEvent::eContextMenuKey) {
|
|
|
|
if (!AdjustContextMenuKeyEvent(static_cast<nsMouseEvent*>(aEvent)))
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
nsAutoHandlingUserInputStatePusher userInpStatePusher(isHandlingUserInput);
|
|
|
|
|
|
|
|
nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent));
|
|
|
|
|
|
|
|
// FIXME. If the event was reused, we need to clear the old target,
|
|
|
|
// bug 329430
|
|
|
|
aEvent->target = nsnull;
|
|
|
|
|
2007-11-22 07:08:13 -08:00
|
|
|
nsWeakView weakView(aView);
|
2007-03-22 10:30:00 -07:00
|
|
|
// 1. Give event to event manager for pre event state changes and
|
|
|
|
// generation of synthetic events.
|
|
|
|
rv = manager->PreHandleEvent(mPresContext, aEvent, mCurrentEventFrame,
|
|
|
|
aStatus, aView);
|
|
|
|
|
|
|
|
// 2. Give event to the DOM for third party and JS use.
|
2008-12-12 00:34:43 -08:00
|
|
|
if (GetCurrentEventFrame() && NS_SUCCEEDED(rv)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
// We want synthesized mouse moves to cause mouseover and mouseout
|
|
|
|
// DOM events (PreHandleEvent above), but not mousemove DOM events.
|
|
|
|
if (!IsSynthesizedMouseMove(aEvent)) {
|
|
|
|
nsPresShellEventCB eventCB(this);
|
|
|
|
if (mCurrentEventContent) {
|
|
|
|
nsEventDispatcher::Dispatch(mCurrentEventContent, mPresContext,
|
|
|
|
aEvent, nsnull, aStatus, &eventCB);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
nsCOMPtr<nsIContent> targetContent;
|
|
|
|
rv = mCurrentEventFrame->GetContentForEvent(mPresContext, aEvent,
|
|
|
|
getter_AddRefs(targetContent));
|
|
|
|
if (NS_SUCCEEDED(rv) && targetContent) {
|
|
|
|
nsEventDispatcher::Dispatch(targetContent, mPresContext, aEvent,
|
|
|
|
nsnull, aStatus, &eventCB);
|
|
|
|
} else if (mDocument) {
|
|
|
|
nsEventDispatcher::Dispatch(mDocument, mPresContext, aEvent,
|
|
|
|
nsnull, aStatus, nsnull);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// 3. Give event to event manager for post event state changes and
|
|
|
|
// generation of synthetic events.
|
2008-12-12 00:34:43 -08:00
|
|
|
if (!mIsDestroying && NS_SUCCEEDED(rv)) {
|
2007-11-20 14:30:27 -08:00
|
|
|
rv = manager->PostHandleEvent(mPresContext, aEvent,
|
2007-11-22 07:08:13 -08:00
|
|
|
GetCurrentEventFrame(), aStatus,
|
|
|
|
weakView.GetView());
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return rv;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Dispatch event to content only (NOT full processing)
|
|
|
|
// See also HandleEventWithTarget which does full event processing.
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::HandleDOMEventWithTarget(nsIContent* aTargetContent, nsEvent* aEvent,
|
|
|
|
nsEventStatus* aStatus)
|
|
|
|
{
|
|
|
|
PushCurrentEventInfo(nsnull, aTargetContent);
|
|
|
|
|
|
|
|
// Bug 41013: Check if the event should be dispatched to content.
|
|
|
|
// It's possible that we are in the middle of destroying the window
|
|
|
|
// and the js context is out of date. This check detects the case
|
|
|
|
// that caused a crash in bug 41013, but there may be a better way
|
|
|
|
// to handle this situation!
|
|
|
|
nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
|
|
|
|
if (container) {
|
|
|
|
|
|
|
|
// Dispatch event to content
|
|
|
|
nsEventDispatcher::Dispatch(aTargetContent, mPresContext, aEvent, nsnull,
|
|
|
|
aStatus);
|
|
|
|
}
|
|
|
|
|
|
|
|
PopCurrentEventInfo();
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2009-01-17 12:50:00 -08:00
|
|
|
PRBool
|
|
|
|
PresShell::AdjustContextMenuKeyEvent(nsMouseEvent* aEvent)
|
|
|
|
{
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
// if a menu is open, open the context menu relative to the active item on the menu.
|
|
|
|
// XXXndeakin Mac doesn't fire mouse-triggered context menus while another
|
|
|
|
// menu is open. Maybe we should prevent keyboard-tiggered context menu events too.
|
|
|
|
nsXULPopupManager* pm = nsXULPopupManager::GetInstance();
|
|
|
|
if (pm) {
|
|
|
|
nsIFrame* popupFrame = pm->GetTopPopup(ePopupTypeMenu);
|
|
|
|
if (popupFrame) {
|
|
|
|
#ifdef XP_MACOSX
|
|
|
|
// context menus should not be opened while another menu is open on Mac,
|
|
|
|
// so return false so that the event is not fired.
|
|
|
|
return PR_FALSE;
|
|
|
|
#else
|
|
|
|
nsIFrame* itemFrame =
|
|
|
|
(static_cast<nsMenuPopupFrame *>(popupFrame))->GetCurrentMenuItem();
|
|
|
|
if (!itemFrame)
|
|
|
|
itemFrame = popupFrame;
|
|
|
|
|
|
|
|
nsCOMPtr<nsIWidget> widget = popupFrame->GetWindow();
|
|
|
|
aEvent->widget = widget;
|
2009-02-18 16:11:49 -08:00
|
|
|
nsIntPoint widgetPoint = widget->WidgetToScreenOffset();
|
|
|
|
aEvent->refPoint = itemFrame->GetScreenRect().BottomLeft() - widgetPoint;
|
2009-01-17 12:50:00 -08:00
|
|
|
|
|
|
|
mCurrentEventContent = itemFrame->GetContent();
|
|
|
|
mCurrentEventFrame = itemFrame;
|
|
|
|
|
|
|
|
return PR_TRUE;
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// If we're here because of the key-equiv for showing context menus, we
|
|
|
|
// have to twiddle with the NS event to make sure the context menu comes
|
|
|
|
// up in the upper left of the relevant content area before we create
|
|
|
|
// the DOM event. Since we never call InitMouseEvent() on the event,
|
|
|
|
// the client X/Y will be 0,0. We can make use of that if the widget is null.
|
|
|
|
mViewManager->GetWidget(getter_AddRefs(aEvent->widget));
|
|
|
|
aEvent->refPoint.x = 0;
|
|
|
|
aEvent->refPoint.y = 0;
|
|
|
|
|
|
|
|
// see if we should use the caret position for the popup
|
2009-01-17 13:14:18 -08:00
|
|
|
nsIntPoint caretPoint;
|
2009-01-17 12:50:00 -08:00
|
|
|
// Beware! This may flush notifications via synchronous
|
|
|
|
// ScrollSelectionIntoView.
|
|
|
|
if (PrepareToUseCaretPosition(aEvent->widget, caretPoint)) {
|
|
|
|
// caret position is good
|
|
|
|
aEvent->refPoint = caretPoint;
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we're here because of the key-equiv for showing context menus, we
|
|
|
|
// have to reset the event target to the currently focused element. Get it
|
|
|
|
// from the focus controller.
|
Bug 178324, refactor focus by moving all focus handling into one place and simplifying it, add many tests, fixes many other bugs too numerous to mention in this small checkin comment, r=josh,smichaud,ere,dbaron,marco,neil,gavin,smaug,sr=smaug (CLOSED TREE)
2009-06-10 11:00:39 -07:00
|
|
|
nsCOMPtr<nsIDOMElement> currentFocus;
|
|
|
|
nsIFocusManager* fm = nsFocusManager::GetFocusManager();
|
|
|
|
if (fm)
|
|
|
|
fm->GetFocusedElement(getter_AddRefs(currentFocus));
|
|
|
|
|
|
|
|
// Reset event coordinates relative to focused frame in view
|
|
|
|
if (currentFocus) {
|
|
|
|
nsCOMPtr<nsIContent> currentPointElement;
|
|
|
|
GetCurrentItemAndPositionForElement(currentFocus,
|
|
|
|
getter_AddRefs(currentPointElement),
|
|
|
|
aEvent->refPoint);
|
|
|
|
if (currentPointElement) {
|
|
|
|
mCurrentEventContent = currentPointElement;
|
|
|
|
mCurrentEventFrame = nsnull;
|
|
|
|
GetCurrentEventFrame();
|
2009-01-17 12:50:00 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
// nsEventListenerManager::PrepareToUseCaretPosition
|
|
|
|
//
|
|
|
|
// This checks to see if we should use the caret position for popup context
|
|
|
|
// menus. Returns true if the caret position should be used, and the
|
|
|
|
// coordinates of that position is returned in aTargetPt. This function
|
|
|
|
// will also scroll the window as needed to make the caret visible.
|
|
|
|
//
|
|
|
|
// The event widget should be the widget that generated the event, and
|
|
|
|
// whose coordinate system the resulting event's refPoint should be
|
|
|
|
// relative to. The returned point is in device pixels realtive to the
|
|
|
|
// widget passed in.
|
|
|
|
PRBool
|
|
|
|
PresShell::PrepareToUseCaretPosition(nsIWidget* aEventWidget, nsIntPoint& aTargetPt)
|
|
|
|
{
|
|
|
|
nsresult rv;
|
|
|
|
|
|
|
|
// check caret visibility
|
|
|
|
nsRefPtr<nsCaret> caret;
|
|
|
|
rv = GetCaret(getter_AddRefs(caret));
|
|
|
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
|
|
|
NS_ENSURE_TRUE(caret, PR_FALSE);
|
|
|
|
|
|
|
|
PRBool caretVisible = PR_FALSE;
|
|
|
|
rv = caret->GetCaretVisible(&caretVisible);
|
|
|
|
if (NS_FAILED(rv) || ! caretVisible)
|
|
|
|
return PR_FALSE;
|
|
|
|
|
|
|
|
// caret selection, this is a temporary weak reference, so no refcounting is
|
|
|
|
// needed
|
|
|
|
nsISelection* domSelection = caret->GetCaretDOMSelection();
|
|
|
|
NS_ENSURE_TRUE(domSelection, PR_FALSE);
|
|
|
|
|
|
|
|
// since the match could be an anonymous textnode inside a
|
|
|
|
// <textarea> or text <input>, we need to get the outer frame
|
|
|
|
// note: frames are not refcounted
|
|
|
|
nsIFrame* frame = nsnull; // may be NULL
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
rv = domSelection->GetFocusNode(getter_AddRefs(node));
|
|
|
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
|
|
|
NS_ENSURE_TRUE(node, PR_FALSE);
|
|
|
|
nsCOMPtr<nsIContent> content(do_QueryInterface(node));
|
|
|
|
if (content) {
|
|
|
|
nsIContent* nonNative = content->FindFirstNonNativeAnonymous();
|
|
|
|
content = nonNative;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (content) {
|
|
|
|
// It seems like ScrollSelectionIntoView should be enough, but it's
|
|
|
|
// not. The problem is that scrolling the selection into view when it is
|
|
|
|
// below the current viewport will align the top line of the frame exactly
|
|
|
|
// with the bottom of the window. This is fine, BUT, the popup event causes
|
|
|
|
// the control to be re-focused which does this exact call to
|
|
|
|
// ScrollContentIntoView, which has a one-pixel disagreement of whether the
|
|
|
|
// frame is actually in view. The result is that the frame is aligned with
|
|
|
|
// the top of the window, but the menu is still at the bottom.
|
|
|
|
//
|
|
|
|
// Doing this call first forces the frame to be in view, eliminating the
|
|
|
|
// problem. The only difference in the result is that if your cursor is in
|
|
|
|
// an edit box below the current view, you'll get the edit box aligned with
|
|
|
|
// the top of the window. This is arguably better behavior anyway.
|
|
|
|
rv = ScrollContentIntoView(content, NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
|
|
|
|
NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
|
|
|
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
|
|
|
frame = GetPrimaryFrameFor(content);
|
|
|
|
NS_WARN_IF_FALSE(frame, "No frame for focused content?");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Actually scroll the selection (ie caret) into view. Note that this must
|
|
|
|
// be synchronous since we will be checking the caret position on the screen.
|
|
|
|
//
|
|
|
|
// Be easy about errors, and just don't scroll in those cases. Better to have
|
|
|
|
// the correct menu at a weird place than the wrong menu.
|
|
|
|
// After ScrollSelectionIntoView(), the pending notifications might be
|
|
|
|
// flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
|
|
|
|
nsCOMPtr<nsISelectionController> selCon;
|
|
|
|
if (frame)
|
|
|
|
frame->GetSelectionController(GetPresContext(), getter_AddRefs(selCon));
|
|
|
|
else
|
|
|
|
selCon = static_cast<nsISelectionController *>(this);
|
|
|
|
if (selCon) {
|
|
|
|
rv = selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
|
|
|
|
nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
|
|
|
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
|
|
|
}
|
|
|
|
|
|
|
|
// get caret position relative to some view (normally the same as the
|
|
|
|
// event widget view, but this is not guaranteed)
|
|
|
|
PRBool isCollapsed;
|
|
|
|
nsIView* view;
|
|
|
|
nsRect caretCoords;
|
|
|
|
rv = caret->GetCaretCoordinates(nsCaret::eRenderingViewCoordinates,
|
|
|
|
domSelection, &caretCoords, &isCollapsed,
|
|
|
|
&view);
|
|
|
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
|
|
|
|
|
|
|
// in case the view used for caret coordinates was something else, we need
|
|
|
|
// to bring those coordinates into the space of the widget view
|
|
|
|
nsIView* widgetView = nsIView::GetViewFor(aEventWidget);
|
|
|
|
NS_ENSURE_TRUE(widgetView, PR_FALSE);
|
|
|
|
nsPoint viewToWidget;
|
|
|
|
widgetView->GetNearestWidget(&viewToWidget);
|
|
|
|
nsPoint viewDelta = view->GetOffsetTo(widgetView) + viewToWidget;
|
|
|
|
|
|
|
|
// caret coordinates are in app units, convert to pixels
|
|
|
|
nsPresContext* presContext = GetPresContext();
|
|
|
|
aTargetPt.x = presContext->AppUnitsToDevPixels(viewDelta.x + caretCoords.x + caretCoords.width);
|
|
|
|
aTargetPt.y = presContext->AppUnitsToDevPixels(viewDelta.y + caretCoords.y + caretCoords.height);
|
|
|
|
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::GetCurrentItemAndPositionForElement(nsIDOMElement *aCurrentEl,
|
|
|
|
nsIContent** aTargetToUse,
|
|
|
|
nsIntPoint& aTargetPt)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIContent> focusedContent(do_QueryInterface(aCurrentEl));
|
|
|
|
ScrollContentIntoView(focusedContent, NS_PRESSHELL_SCROLL_ANYWHERE,
|
|
|
|
NS_PRESSHELL_SCROLL_ANYWHERE);
|
|
|
|
|
|
|
|
PRBool istree = PR_FALSE, checkLineHeight = PR_TRUE;
|
|
|
|
PRInt32 extraPixelsY = 0, extraTreeY = 0;
|
|
|
|
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
// Set the position to just underneath the current item for multi-select
|
|
|
|
// lists or just underneath the selected item for single-select lists. If
|
|
|
|
// the element is not a list, or there is no selection, leave the position
|
|
|
|
// as is.
|
|
|
|
nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
|
|
|
|
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
|
|
|
|
do_QueryInterface(aCurrentEl);
|
|
|
|
if (multiSelect) {
|
|
|
|
checkLineHeight = PR_FALSE;
|
|
|
|
|
|
|
|
PRInt32 currentIndex;
|
|
|
|
multiSelect->GetCurrentIndex(¤tIndex);
|
|
|
|
if (currentIndex >= 0) {
|
|
|
|
nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aCurrentEl));
|
|
|
|
if (xulElement) {
|
|
|
|
nsCOMPtr<nsIBoxObject> box;
|
|
|
|
xulElement->GetBoxObject(getter_AddRefs(box));
|
|
|
|
nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
|
|
|
|
// Tree view special case (tree items have no frames)
|
|
|
|
// Get the focused row and add its coordinates, which are already in pixels
|
|
|
|
// XXX Boris, should we create a new interface so that this doesn't
|
|
|
|
// need to know about trees? Something like nsINodelessChildCreator which
|
|
|
|
// could provide the current focus coordinates?
|
|
|
|
if (treeBox) {
|
|
|
|
treeBox->EnsureRowIsVisible(currentIndex);
|
|
|
|
PRInt32 firstVisibleRow, rowHeight;
|
|
|
|
treeBox->GetFirstVisibleRow(&firstVisibleRow);
|
|
|
|
treeBox->GetRowHeight(&rowHeight);
|
|
|
|
|
|
|
|
extraPixelsY = (currentIndex - firstVisibleRow + 1) * rowHeight;
|
|
|
|
istree = PR_TRUE;
|
|
|
|
|
|
|
|
nsCOMPtr<nsITreeColumns> cols;
|
|
|
|
treeBox->GetColumns(getter_AddRefs(cols));
|
|
|
|
if (cols) {
|
|
|
|
nsCOMPtr<nsITreeColumn> col;
|
|
|
|
cols->GetFirstColumn(getter_AddRefs(col));
|
|
|
|
if (col) {
|
|
|
|
nsCOMPtr<nsIDOMElement> colElement;
|
|
|
|
col->GetElement(getter_AddRefs(colElement));
|
|
|
|
nsCOMPtr<nsIContent> colContent(do_QueryInterface(colElement));
|
|
|
|
if (colContent) {
|
|
|
|
nsIFrame* frame = GetPrimaryFrameFor(colContent);
|
|
|
|
if (frame) {
|
|
|
|
extraTreeY = frame->GetSize().height;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
multiSelect->GetCurrentItem(getter_AddRefs(item));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// don't check menulists as the selected item will be inside a popup.
|
|
|
|
nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aCurrentEl);
|
|
|
|
if (!menulist) {
|
|
|
|
checkLineHeight = PR_FALSE;
|
|
|
|
nsCOMPtr<nsIDOMXULSelectControlElement> select =
|
|
|
|
do_QueryInterface(aCurrentEl);
|
|
|
|
if (select)
|
|
|
|
select->GetSelectedItem(getter_AddRefs(item));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (item)
|
|
|
|
focusedContent = do_QueryInterface(item);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
nsIFrame *frame = GetPrimaryFrameFor(focusedContent);
|
|
|
|
if (frame) {
|
|
|
|
nsPoint frameOrigin(0, 0);
|
|
|
|
|
|
|
|
// Get the frame's origin within its view
|
|
|
|
nsIView *view = frame->GetClosestView(&frameOrigin);
|
|
|
|
NS_ASSERTION(view, "No view for frame");
|
|
|
|
|
|
|
|
nsIView *rootView = nsnull;
|
|
|
|
mViewManager->GetRootView(rootView);
|
|
|
|
NS_ASSERTION(rootView, "No root view in pres shell");
|
|
|
|
|
|
|
|
// View's origin within its root view
|
|
|
|
frameOrigin += view->GetOffsetTo(rootView);
|
|
|
|
|
|
|
|
// Start context menu down and to the right from top left of frame
|
|
|
|
// use the lineheight. This is a good distance to move the context
|
|
|
|
// menu away from the top left corner of the frame. If we always
|
|
|
|
// used the frame height, the context menu could end up far away,
|
|
|
|
// for example when we're focused on linked images.
|
|
|
|
// On the other hand, we want to use the frame height if it's less
|
|
|
|
// than the current line height, so that the context menu appears
|
|
|
|
// associated with the correct frame.
|
|
|
|
nscoord extra = 0;
|
|
|
|
if (!istree) {
|
|
|
|
extra = frame->GetSize().height;
|
|
|
|
if (checkLineHeight) {
|
|
|
|
nsIScrollableView *scrollView =
|
|
|
|
nsLayoutUtils::GetNearestScrollingView(view, nsLayoutUtils::eEither);
|
|
|
|
if (scrollView) {
|
|
|
|
nscoord scrollViewLineHeight;
|
|
|
|
scrollView->GetLineHeight(&scrollViewLineHeight);
|
|
|
|
if (extra > scrollViewLineHeight) {
|
|
|
|
extra = scrollViewLineHeight;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsPresContext* presContext = GetPresContext();
|
|
|
|
aTargetPt.x = presContext->AppUnitsToDevPixels(frameOrigin.x);
|
|
|
|
aTargetPt.y = presContext->AppUnitsToDevPixels(
|
|
|
|
frameOrigin.y + extra + extraTreeY) + extraPixelsY;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IF_ADDREF(*aTargetToUse = focusedContent);
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::ResizeReflow(nsIView *aView, nscoord aWidth, nscoord aHeight)
|
|
|
|
{
|
|
|
|
return ResizeReflow(aWidth, aHeight);
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP_(PRBool)
|
|
|
|
PresShell::IsVisible()
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsISupports> container = mPresContext->GetContainer();
|
|
|
|
nsCOMPtr<nsIBaseWindow> bw = do_QueryInterface(container);
|
|
|
|
if (!bw)
|
|
|
|
return PR_FALSE;
|
|
|
|
PRBool res = PR_TRUE;
|
|
|
|
bw->GetVisibility(&res);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
NS_IMETHODIMP_(void)
|
|
|
|
PresShell::WillPaint()
|
|
|
|
{
|
2008-12-20 06:28:19 -08:00
|
|
|
// Don't bother reflowing if some viewmanager in our tree is painting while
|
2007-03-22 10:30:00 -07:00
|
|
|
// we still have painting suppressed.
|
2008-12-20 06:28:19 -08:00
|
|
|
if (mPaintingSuppressed) {
|
2007-03-22 10:30:00 -07:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Process reflows, if we have them, to reduce flicker due to invalidates and
|
|
|
|
// reflow being interspersed. Note that we _do_ allow this to be
|
|
|
|
// interruptible; if we can't do all the reflows it's better to flicker a bit
|
|
|
|
// than to freeze up.
|
2009-04-21 16:53:52 -07:00
|
|
|
FlushPendingNotifications(Flush_InterruptibleLayout);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
PresShell::GetAgentStyleSheets(nsCOMArray<nsIStyleSheet>& aSheets)
|
|
|
|
{
|
|
|
|
aSheets.Clear();
|
|
|
|
PRInt32 sheetCount = mStyleSet->SheetCount(nsStyleSet::eAgentSheet);
|
|
|
|
|
|
|
|
for (PRInt32 i = 0; i < sheetCount; ++i) {
|
|
|
|
nsIStyleSheet *sheet = mStyleSet->StyleSheetAt(nsStyleSet::eAgentSheet, i);
|
|
|
|
if (!aSheets.AppendObject(sheet))
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
PresShell::SetAgentStyleSheets(const nsCOMArray<nsIStyleSheet>& aSheets)
|
|
|
|
{
|
|
|
|
return mStyleSet->ReplaceSheets(nsStyleSet::eAgentSheet, aSheets);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
PresShell::AddOverrideStyleSheet(nsIStyleSheet *aSheet)
|
|
|
|
{
|
|
|
|
return mStyleSet->PrependStyleSheet(nsStyleSet::eOverrideSheet, aSheet);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
PresShell::RemoveOverrideStyleSheet(nsIStyleSheet *aSheet)
|
|
|
|
{
|
|
|
|
return mStyleSet->RemoveStyleSheet(nsStyleSet::eOverrideSheet, aSheet);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
2009-05-07 18:32:32 -07:00
|
|
|
FreezeElement(nsIContent *aContent, void *aShell)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-12-04 05:15:11 -08:00
|
|
|
#ifdef MOZ_MEDIA
|
2008-12-04 05:08:39 -08:00
|
|
|
nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aContent));
|
2009-05-07 18:32:32 -07:00
|
|
|
if (domMediaElem) {
|
|
|
|
nsHTMLMediaElement* mediaElem = static_cast<nsHTMLMediaElement*>(aContent);
|
|
|
|
mediaElem->Freeze();
|
2008-12-04 05:08:39 -08:00
|
|
|
return;
|
2009-05-07 18:32:32 -07:00
|
|
|
}
|
|
|
|
#endif
|
2008-12-04 05:08:39 -08:00
|
|
|
|
2009-05-07 18:32:32 -07:00
|
|
|
nsIPresShell* shell = static_cast<nsIPresShell*>(aShell);
|
|
|
|
nsIFrame *frame = shell->FrameManager()->GetPrimaryFrameFor(aContent, -1);
|
|
|
|
nsIObjectFrame *objectFrame = do_QueryFrame(frame);
|
|
|
|
if (objectFrame) {
|
|
|
|
objectFrame->StopPlugin();
|
|
|
|
}
|
2008-07-09 01:22:20 -07:00
|
|
|
}
|
|
|
|
|
2008-10-10 08:04:34 -07:00
|
|
|
static PRBool
|
2007-03-22 10:30:00 -07:00
|
|
|
FreezeSubDocument(nsIDocument *aDocument, void *aData)
|
|
|
|
{
|
2007-05-01 15:24:20 -07:00
|
|
|
nsIPresShell *shell = aDocument->GetPrimaryShell();
|
2007-03-22 10:30:00 -07:00
|
|
|
if (shell)
|
|
|
|
shell->Freeze();
|
|
|
|
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::Freeze()
|
|
|
|
{
|
2009-05-07 18:32:32 -07:00
|
|
|
mDocument->EnumerateFreezableElements(FreezeElement, this);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (mCaret)
|
|
|
|
mCaret->SetCaretVisible(PR_FALSE);
|
|
|
|
|
|
|
|
mPaintingSuppressed = PR_TRUE;
|
|
|
|
|
|
|
|
if (mDocument)
|
|
|
|
mDocument->EnumerateSubDocuments(FreezeSubDocument, nsnull);
|
|
|
|
}
|
|
|
|
|
2009-03-03 12:11:14 -08:00
|
|
|
void
|
|
|
|
PresShell::FireOrClearDelayedEvents(PRBool aFireEvents)
|
|
|
|
{
|
2009-05-18 02:17:45 -07:00
|
|
|
mNoDelayedMouseEvents = PR_FALSE;
|
|
|
|
mNoDelayedKeyEvents = PR_FALSE;
|
2009-03-03 12:11:14 -08:00
|
|
|
if (!aFireEvents) {
|
2009-05-18 02:17:45 -07:00
|
|
|
mDelayedEvents.Clear();
|
2009-03-03 12:11:14 -08:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2009-06-16 11:46:24 -07:00
|
|
|
if (mDocument) {
|
2009-03-03 12:11:14 -08:00
|
|
|
nsCOMPtr<nsIDocument> doc = mDocument;
|
2009-06-16 11:46:24 -07:00
|
|
|
while (!mIsDestroying && mDelayedEvents.Length() &&
|
|
|
|
!doc->EventHandlingSuppressed()) {
|
|
|
|
nsAutoPtr<nsDelayedEvent> ev(mDelayedEvents[0].forget());
|
2009-05-18 02:17:45 -07:00
|
|
|
mDelayedEvents.RemoveElementAt(0);
|
2009-06-16 11:46:24 -07:00
|
|
|
ev->Dispatch(this);
|
2009-03-03 12:11:14 -08:00
|
|
|
}
|
2009-03-03 12:20:11 -08:00
|
|
|
if (!doc->EventHandlingSuppressed()) {
|
2009-05-18 02:17:45 -07:00
|
|
|
mDelayedEvents.Clear();
|
2009-03-03 12:11:14 -08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
static void
|
2009-05-07 18:32:32 -07:00
|
|
|
ThawElement(nsIContent *aContent, void *aShell)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2008-10-28 11:48:39 -07:00
|
|
|
#ifdef MOZ_MEDIA
|
2008-12-04 05:08:39 -08:00
|
|
|
nsCOMPtr<nsIDOMHTMLMediaElement> domMediaElem(do_QueryInterface(aContent));
|
2009-05-07 18:32:32 -07:00
|
|
|
if (domMediaElem) {
|
|
|
|
nsHTMLMediaElement* mediaElem = static_cast<nsHTMLMediaElement*>(aContent);
|
|
|
|
mediaElem->Thaw();
|
2008-12-04 05:08:39 -08:00
|
|
|
return;
|
2009-05-07 18:32:32 -07:00
|
|
|
}
|
|
|
|
#endif
|
2008-12-04 05:08:39 -08:00
|
|
|
|
2009-05-07 18:32:32 -07:00
|
|
|
nsCOMPtr<nsIObjectLoadingContent> objlc(do_QueryInterface(aContent));
|
|
|
|
if (objlc) {
|
|
|
|
nsCOMPtr<nsIPluginInstance> inst;
|
|
|
|
objlc->EnsureInstantiation(getter_AddRefs(inst));
|
|
|
|
}
|
2008-07-09 01:22:20 -07:00
|
|
|
}
|
|
|
|
|
2008-10-10 08:04:34 -07:00
|
|
|
static PRBool
|
2007-03-22 10:30:00 -07:00
|
|
|
ThawSubDocument(nsIDocument *aDocument, void *aData)
|
|
|
|
{
|
2007-05-01 15:24:20 -07:00
|
|
|
nsIPresShell *shell = aDocument->GetPrimaryShell();
|
2007-03-22 10:30:00 -07:00
|
|
|
if (shell)
|
|
|
|
shell->Thaw();
|
|
|
|
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::Thaw()
|
|
|
|
{
|
2009-05-07 18:32:32 -07:00
|
|
|
mDocument->EnumerateFreezableElements(ThawElement, this);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
if (mDocument)
|
|
|
|
mDocument->EnumerateSubDocuments(ThawSubDocument, nsnull);
|
|
|
|
|
|
|
|
UnsuppressPainting();
|
|
|
|
}
|
|
|
|
|
|
|
|
//--------------------------------------------------------
|
|
|
|
// Start of protected and private methods on the PresShell
|
|
|
|
//--------------------------------------------------------
|
|
|
|
|
|
|
|
//-------------- Begin Reflow Event Definition ------------------------
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::ReflowEvent::Run() {
|
|
|
|
// Take an owning reference to the PresShell during this call to ensure
|
|
|
|
// that it doesn't get killed off prematurely.
|
|
|
|
nsRefPtr<PresShell> ps = mPresShell;
|
|
|
|
if (ps) {
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
|
|
|
|
printf("\n*** Handling reflow event: PresShell=%p, event=%p\n", (void*)ps, (void*)this);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
// NOTE: the ReflowEvent class is a friend of the PresShell class
|
|
|
|
ps->ClearReflowEventStatus();
|
|
|
|
// Set a kung fu death grip on the view manager associated with the pres shell
|
|
|
|
// before processing that pres shell's reflow commands. Fixes bug 54868.
|
|
|
|
nsCOMPtr<nsIViewManager> viewManager = ps->GetViewManager();
|
2007-08-21 19:57:06 -07:00
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
ps->mSuppressInterruptibleReflows = PR_FALSE;
|
|
|
|
|
|
|
|
#ifdef NOISY_INTERRUPTIBLE_REFLOW
|
|
|
|
printf("*** Entering reflow event (time=%lld)\n", PR_Now());
|
|
|
|
#endif /* NOISY_INTERRUPTIBLE_REFLOW */
|
|
|
|
|
|
|
|
ps->FlushPendingNotifications(Flush_InterruptibleLayout);
|
|
|
|
|
|
|
|
#ifdef NOISY_INTERRUPTIBLE_REFLOW
|
|
|
|
printf("*** Returning from reflow event (time=%lld)\n", PR_Now());
|
|
|
|
#endif /* NOISY_INTERRUPTIBLE_REFLOW */
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Now, explicitly release the pres shell before the view manager
|
|
|
|
ps = nsnull;
|
|
|
|
viewManager = nsnull;
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-------------- End Reflow Event Definition ---------------------------
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::PostReflowEvent()
|
|
|
|
{
|
|
|
|
if (mReflowEvent.IsPending() || mIsDestroying || mIsReflowing ||
|
2009-02-03 06:42:18 -08:00
|
|
|
mDirtyRoots.Length() == 0)
|
2007-03-22 10:30:00 -07:00
|
|
|
return;
|
|
|
|
|
2009-06-12 18:28:41 -07:00
|
|
|
if (!mPresContext->HasPendingInterrupt() || !PostReflowEventOffTimer()) {
|
|
|
|
DoPostReflowEvent();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::DoPostReflowEvent()
|
|
|
|
{
|
2007-03-22 10:30:00 -07:00
|
|
|
nsRefPtr<ReflowEvent> ev = new ReflowEvent(this);
|
|
|
|
if (NS_FAILED(NS_DispatchToCurrentThread(ev))) {
|
|
|
|
NS_WARNING("failed to dispatch reflow event");
|
|
|
|
} else {
|
|
|
|
mReflowEvent = ev;
|
|
|
|
#ifdef DEBUG
|
|
|
|
if (VERIFY_REFLOW_NOISY_RC & gVerifyReflowFlags) {
|
2009-06-12 18:28:41 -07:00
|
|
|
printf("\n*** PresShell::DoPostReflowEvent(), this=%p, event=%p\n",
|
|
|
|
(void*)this, (void*)ev);
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::WillDoReflow()
|
|
|
|
{
|
|
|
|
// We just reflowed, tell the caret that its frame might have moved.
|
2007-10-01 21:37:36 -07:00
|
|
|
// XXXbz that comment makes no sense
|
2007-03-22 10:30:00 -07:00
|
|
|
if (mCaret) {
|
|
|
|
mCaret->InvalidateOutsideCaret();
|
|
|
|
mCaret->UpdateCaretPosition();
|
|
|
|
}
|
2007-10-01 21:37:36 -07:00
|
|
|
|
2008-11-25 15:22:38 -08:00
|
|
|
mPresContext->FlushUserFontSet();
|
|
|
|
|
2007-10-01 21:37:36 -07:00
|
|
|
mFrameConstructor->BeginUpdate();
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2009-04-21 16:53:52 -07:00
|
|
|
PresShell::DidDoReflow(PRBool aInterruptible)
|
2007-03-22 10:30:00 -07:00
|
|
|
{
|
2007-10-01 21:37:36 -07:00
|
|
|
mFrameConstructor->EndUpdate();
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
HandlePostedReflowCallbacks(aInterruptible);
|
2007-03-22 10:30:00 -07:00
|
|
|
// Null-check mViewManager in case this happens during Destroy. See
|
|
|
|
// bugs 244435 and 238546.
|
|
|
|
if (!mPaintingSuppressed && mViewManager)
|
|
|
|
mViewManager->SynthesizeMouseMove(PR_FALSE);
|
|
|
|
if (mCaret) {
|
|
|
|
// Update the caret's position now to account for any changes created by
|
|
|
|
// the reflow.
|
|
|
|
mCaret->InvalidateOutsideCaret();
|
|
|
|
mCaret->UpdateCaretPosition();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
static PLDHashOperator
|
|
|
|
MarkFramesDirtyToRoot(nsPtrHashKey<nsIFrame>* p, void* closure)
|
|
|
|
{
|
|
|
|
nsIFrame* target = static_cast<nsIFrame*>(closure);
|
|
|
|
for (nsIFrame* f = p->GetKey(); f && !NS_SUBTREE_DIRTY(f);
|
|
|
|
f = f->GetParent()) {
|
|
|
|
f->AddStateBits(NS_FRAME_HAS_DIRTY_CHILDREN);
|
|
|
|
|
|
|
|
if (f == target) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return PL_DHASH_NEXT;
|
|
|
|
}
|
|
|
|
|
2009-06-12 18:28:41 -07:00
|
|
|
void
|
|
|
|
PresShell::sReflowContinueCallback(nsITimer* aTimer, void* aPresShell)
|
|
|
|
{
|
|
|
|
nsRefPtr<PresShell> self = static_cast<PresShell*>(aPresShell);
|
|
|
|
|
|
|
|
NS_PRECONDITION(aTimer == self->mReflowContinueTimer, "Unexpected timer");
|
|
|
|
self->mReflowContinueTimer = nsnull;
|
|
|
|
self->DoPostReflowEvent();
|
|
|
|
}
|
|
|
|
|
|
|
|
PRBool
|
|
|
|
PresShell::PostReflowEventOffTimer()
|
|
|
|
{
|
|
|
|
NS_PRECONDITION(!mReflowEvent.IsPending(), "Shouldn't get here");
|
|
|
|
if (!mReflowContinueTimer) {
|
|
|
|
mReflowContinueTimer = do_CreateInstance("@mozilla.org/timer;1");
|
|
|
|
if (!mReflowContinueTimer ||
|
|
|
|
NS_FAILED(mReflowContinueTimer->
|
|
|
|
InitWithFuncCallback(sReflowContinueCallback, this, 0,
|
|
|
|
nsITimer::TYPE_ONE_SHOT))) {
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
PRBool
|
|
|
|
PresShell::DoReflow(nsIFrame* target, PRBool aInterruptible)
|
2007-04-25 08:49:55 -07:00
|
|
|
{
|
|
|
|
nsIFrame* rootFrame = FrameManager()->GetRootFrame();
|
|
|
|
|
|
|
|
nsCOMPtr<nsIRenderingContext> rcx;
|
|
|
|
// Always create the rendering context relative to the root frame during
|
|
|
|
// reflow; otherwise, it crashes on the mac (I'm not quite sure why)
|
|
|
|
nsresult rv = CreateRenderingContext(rootFrame, getter_AddRefs(rcx));
|
|
|
|
if (NS_FAILED(rv)) {
|
2009-04-21 16:53:52 -07:00
|
|
|
NS_NOTREACHED("CreateRenderingContext failure");
|
|
|
|
return PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
mCurrentReflowRoot = target;
|
|
|
|
#endif
|
2007-04-25 08:49:55 -07:00
|
|
|
|
|
|
|
target->WillReflow(mPresContext);
|
|
|
|
|
|
|
|
// If the target frame is the root of the frame hierarchy, then
|
|
|
|
// use all the available space. If it's simply a `reflow root',
|
|
|
|
// then use the target frame's size as the available space.
|
|
|
|
nsSize size;
|
|
|
|
if (target == rootFrame)
|
|
|
|
size = mPresContext->GetVisibleArea().Size();
|
|
|
|
else
|
|
|
|
size = target->GetSize();
|
|
|
|
|
|
|
|
NS_ASSERTION(!target->GetNextInFlow() && !target->GetPrevInFlow(),
|
|
|
|
"reflow roots should never split");
|
|
|
|
|
|
|
|
// Don't pass size directly to the reflow state, since a
|
|
|
|
// constrained height implies page/column breaking.
|
2009-01-29 12:39:22 -08:00
|
|
|
nsSize reflowSize(size.width, NS_UNCONSTRAINEDSIZE);
|
2007-04-25 08:49:55 -07:00
|
|
|
nsHTMLReflowState reflowState(mPresContext, target, rcx, reflowSize);
|
|
|
|
|
|
|
|
// fix the computed height
|
|
|
|
NS_ASSERTION(reflowState.mComputedMargin == nsMargin(0, 0, 0, 0),
|
|
|
|
"reflow state should not set margin for reflow roots");
|
2007-05-01 10:15:08 -07:00
|
|
|
if (size.height != NS_UNCONSTRAINEDSIZE) {
|
2007-08-02 11:08:05 -07:00
|
|
|
nscoord computedHeight =
|
2007-05-01 10:15:08 -07:00
|
|
|
size.height - reflowState.mComputedBorderPadding.TopBottom();
|
2007-08-02 11:08:05 -07:00
|
|
|
computedHeight = PR_MAX(computedHeight, 0);
|
|
|
|
reflowState.SetComputedHeight(computedHeight);
|
2007-05-01 10:15:08 -07:00
|
|
|
}
|
2007-04-25 08:49:55 -07:00
|
|
|
NS_ASSERTION(reflowState.ComputedWidth() ==
|
|
|
|
size.width -
|
|
|
|
reflowState.mComputedBorderPadding.LeftRight(),
|
|
|
|
"reflow state computed incorrect width");
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
mPresContext->ReflowStarted(aInterruptible);
|
|
|
|
mIsReflowing = PR_TRUE;
|
|
|
|
|
2007-04-25 08:49:55 -07:00
|
|
|
nsReflowStatus status;
|
|
|
|
nsHTMLReflowMetrics desiredSize;
|
|
|
|
target->Reflow(mPresContext, desiredSize, reflowState, status);
|
|
|
|
|
|
|
|
// If an incremental reflow is initiated at a frame other than the
|
2007-05-01 10:15:08 -07:00
|
|
|
// root frame, then its desired size had better not change! If it's
|
|
|
|
// initiated at the root, then the size better not change unless its
|
|
|
|
// height was unconstrained to start with.
|
|
|
|
NS_ASSERTION((target == rootFrame && size.height == NS_UNCONSTRAINEDSIZE) ||
|
2007-04-25 08:49:55 -07:00
|
|
|
(desiredSize.width == size.width &&
|
|
|
|
desiredSize.height == size.height),
|
|
|
|
"non-root frame's desired size changed during an "
|
|
|
|
"incremental reflow");
|
|
|
|
NS_ASSERTION(desiredSize.mOverflowArea ==
|
|
|
|
nsRect(nsPoint(0, 0),
|
|
|
|
nsSize(desiredSize.width, desiredSize.height)),
|
|
|
|
"reflow roots must not have visible overflow");
|
|
|
|
NS_ASSERTION(status == NS_FRAME_COMPLETE,
|
|
|
|
"reflow roots should never split");
|
|
|
|
|
|
|
|
target->SetSize(nsSize(desiredSize.width, desiredSize.height));
|
|
|
|
|
|
|
|
nsContainerFrame::SyncFrameViewAfterReflow(mPresContext, target,
|
|
|
|
target->GetView(),
|
|
|
|
&desiredSize.mOverflowArea);
|
|
|
|
|
|
|
|
target->DidReflow(mPresContext, nsnull, NS_FRAME_REFLOW_FINISHED);
|
2007-05-01 10:15:08 -07:00
|
|
|
if (target == rootFrame && size.height == NS_UNCONSTRAINEDSIZE) {
|
|
|
|
mPresContext->SetVisibleArea(nsRect(0, 0, desiredSize.width,
|
|
|
|
desiredSize.height));
|
|
|
|
}
|
2009-04-21 16:53:52 -07:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
mCurrentReflowRoot = nsnull;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
NS_ASSERTION(mPresContext->HasPendingInterrupt() ||
|
|
|
|
mFramesToDirty.Count() == 0,
|
|
|
|
"Why do we need to dirty anything if not interrupted?");
|
|
|
|
|
|
|
|
mIsReflowing = PR_FALSE;
|
|
|
|
PRBool interrupted = mPresContext->HasPendingInterrupt();
|
|
|
|
if (interrupted) {
|
|
|
|
// Make sure target gets reflowed again.
|
|
|
|
mFramesToDirty.EnumerateEntries(&MarkFramesDirtyToRoot, target);
|
|
|
|
NS_ASSERTION(NS_SUBTREE_DIRTY(target), "Why is the target not dirty?");
|
|
|
|
mDirtyRoots.AppendElement(target);
|
|
|
|
|
|
|
|
// Clear mFramesToDirty after we've done the NS_SUBTREE_DIRTY(target)
|
|
|
|
// assertion so that if it fails it's easier to see what's going on.
|
|
|
|
#ifdef NOISY_INTERRUPTIBLE_REFLOW
|
|
|
|
printf("mFramesToDirty.Count() == %u\n", mFramesToDirty.Count());
|
|
|
|
#endif /* NOISY_INTERRUPTIBLE_REFLOW */
|
|
|
|
mFramesToDirty.Clear();
|
|
|
|
|
|
|
|
// Any FlushPendingNotifications with interruptible reflows
|
|
|
|
// should be suppressed now. We don't want to do extra reflow work
|
|
|
|
// before our reflow event happens.
|
|
|
|
mSuppressInterruptibleReflows = PR_TRUE;
|
|
|
|
PostReflowEvent();
|
|
|
|
}
|
|
|
|
|
|
|
|
return !interrupted;
|
2007-04-25 08:49:55 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
void
|
|
|
|
PresShell::DoVerifyReflow()
|
|
|
|
{
|
|
|
|
if (GetVerifyReflowEnable()) {
|
|
|
|
// First synchronously render what we have so far so that we can
|
|
|
|
// see it.
|
|
|
|
nsIView* rootView;
|
|
|
|
mViewManager->GetRootView(rootView);
|
|
|
|
mViewManager->UpdateView(rootView, NS_VMREFRESH_IMMEDIATE);
|
|
|
|
|
|
|
|
FlushPendingNotifications(Flush_Layout);
|
|
|
|
mInVerifyReflow = PR_TRUE;
|
|
|
|
PRBool ok = VerifyIncrementalReflow();
|
|
|
|
mInVerifyReflow = PR_FALSE;
|
|
|
|
if (VERIFY_REFLOW_ALL & gVerifyReflowFlags) {
|
|
|
|
printf("ProcessReflowCommands: finished (%s)\n",
|
|
|
|
ok ? "ok" : "failed");
|
|
|
|
}
|
|
|
|
|
2009-02-03 06:42:18 -08:00
|
|
|
if (0 != mDirtyRoots.Length()) {
|
2007-04-25 08:49:55 -07:00
|
|
|
printf("XXX yikes! reflow commands queued during verify-reflow\n");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
PRBool
|
2007-03-22 10:30:00 -07:00
|
|
|
PresShell::ProcessReflowCommands(PRBool aInterruptible)
|
|
|
|
{
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Start: Reflow: PresShell::ProcessReflowCommands(), this=%p\n", this));
|
|
|
|
MOZ_TIMER_START(mReflowWatch);
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
PRBool interrupted = PR_FALSE;
|
2009-02-03 06:42:18 -08:00
|
|
|
if (0 != mDirtyRoots.Length()) {
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
#ifdef DEBUG
|
2007-04-25 08:49:55 -07:00
|
|
|
if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
|
|
|
|
printf("ProcessReflowCommands: begin incremental reflow\n");
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
// If reflow is interruptible, then make a note of our deadline.
|
|
|
|
const PRIntervalTime deadline = aInterruptible
|
|
|
|
? PR_IntervalNow() + PR_MicrosecondsToInterval(gMaxRCProcessingTime)
|
|
|
|
: (PRIntervalTime)0;
|
|
|
|
|
2007-09-20 19:47:19 -07:00
|
|
|
// Scope for the reflow entry point
|
|
|
|
{
|
2008-03-14 16:08:57 -07:00
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
2009-06-11 10:09:34 -07:00
|
|
|
WillDoReflow();
|
2007-03-22 10:30:00 -07:00
|
|
|
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
|
|
|
|
|
|
|
|
do {
|
|
|
|
// Send an incremental reflow notification to the target frame.
|
2009-02-03 06:42:18 -08:00
|
|
|
PRInt32 idx = mDirtyRoots.Length() - 1;
|
|
|
|
nsIFrame *target = mDirtyRoots[idx];
|
2007-03-22 10:30:00 -07:00
|
|
|
mDirtyRoots.RemoveElementAt(idx);
|
|
|
|
|
2007-05-06 12:16:51 -07:00
|
|
|
if (!NS_SUBTREE_DIRTY(target)) {
|
2007-03-22 10:30:00 -07:00
|
|
|
// It's not dirty anymore, which probably means the notification
|
|
|
|
// was posted in the middle of a reflow (perhaps with a reflow
|
|
|
|
// root in the middle). Don't do anything.
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
interrupted = !DoReflow(target, aInterruptible);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Keep going until we're out of reflow commands, or we've run
|
2009-04-21 16:53:52 -07:00
|
|
|
// past our deadline, or we're interrupted.
|
|
|
|
} while (!interrupted && mDirtyRoots.Length() &&
|
2007-03-22 10:30:00 -07:00
|
|
|
(!aInterruptible || PR_IntervalNow() < deadline));
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
interrupted = mDirtyRoots.Length() != 0;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
2008-03-14 16:08:57 -07:00
|
|
|
// Exiting the scriptblocker might have killed us
|
|
|
|
if (!mIsDestroying) {
|
2009-04-21 16:53:52 -07:00
|
|
|
DidDoReflow(aInterruptible);
|
2008-03-14 16:08:57 -07:00
|
|
|
}
|
2007-04-25 08:49:55 -07:00
|
|
|
|
2007-09-20 16:28:26 -07:00
|
|
|
// DidDoReflow might have killed us
|
|
|
|
if (!mIsDestroying) {
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef DEBUG
|
2007-09-20 16:28:26 -07:00
|
|
|
if (VERIFY_REFLOW_DUMP_COMMANDS & gVerifyReflowFlags) {
|
|
|
|
printf("\nPresShell::ProcessReflowCommands() finished: this=%p\n",
|
|
|
|
(void*)this);
|
|
|
|
}
|
|
|
|
DoVerifyReflow();
|
2007-03-22 10:30:00 -07:00
|
|
|
#endif
|
|
|
|
|
2007-09-20 16:28:26 -07:00
|
|
|
// If any new reflow commands were enqueued during the reflow, schedule
|
|
|
|
// another reflow event to process them. Note that we want to do this
|
|
|
|
// after DidDoReflow(), since that method can change whether there are
|
|
|
|
// dirty roots around by flushing, and there's no point in posting a
|
|
|
|
// reflow event just to have the flush revoke it.
|
2009-02-03 06:42:18 -08:00
|
|
|
if (mDirtyRoots.Length())
|
2007-09-20 16:28:26 -07:00
|
|
|
PostReflowEvent();
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_TIMER_DEBUGLOG(("Stop: Reflow: PresShell::ProcessReflowCommands(), this=%p\n", this));
|
|
|
|
MOZ_TIMER_STOP(mReflowWatch);
|
|
|
|
|
2007-09-20 16:28:26 -07:00
|
|
|
if (!mIsDestroying && mShouldUnsuppressPainting &&
|
2009-02-03 06:42:18 -08:00
|
|
|
mDirtyRoots.Length() == 0) {
|
2007-03-22 10:30:00 -07:00
|
|
|
// We only unlock if we're out of reflows. It's pointless
|
|
|
|
// to unlock if reflows are still pending, since reflows
|
|
|
|
// are just going to thrash the frames around some more. By
|
|
|
|
// waiting we avoid an overeager "jitter" effect.
|
|
|
|
mShouldUnsuppressPainting = PR_FALSE;
|
|
|
|
UnsuppressAndInvalidate();
|
|
|
|
}
|
|
|
|
|
2009-04-21 16:53:52 -07:00
|
|
|
return !interrupted;
|
2007-03-22 10:30:00 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::ClearReflowEventStatus()
|
|
|
|
{
|
|
|
|
mReflowEvent.Forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
/*
|
|
|
|
* It's better to add stuff to the |DidSetStyleContext| method of the
|
|
|
|
* relevant frames than adding it here. These methods should (ideally,
|
|
|
|
* anyway) go away.
|
|
|
|
*/
|
|
|
|
|
|
|
|
// Return value says whether to walk children.
|
2008-10-10 08:04:34 -07:00
|
|
|
typedef PRBool (* frameWalkerFn)(nsIFrame *aFrame, void *aClosure);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-10-10 08:04:34 -07:00
|
|
|
static PRBool
|
2007-03-22 10:30:00 -07:00
|
|
|
ReResolveMenusAndTrees(nsIFrame *aFrame, void *aClosure)
|
|
|
|
{
|
|
|
|
// Trees have a special style cache that needs to be flushed when
|
|
|
|
// the theme changes.
|
2009-01-12 11:20:59 -08:00
|
|
|
nsTreeBodyFrame *treeBody = do_QueryFrame(aFrame);
|
2008-10-27 21:47:19 -07:00
|
|
|
if (treeBody)
|
|
|
|
treeBody->ClearStyleAndImageCaches();
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// We deliberately don't re-resolve style on a menu's popup
|
|
|
|
// sub-content, since doing so slows menus to a crawl. That means we
|
|
|
|
// have to special-case them on a skin switch, and ensure that the
|
|
|
|
// popup frames just get destroyed completely.
|
2007-07-04 08:49:38 -07:00
|
|
|
if (aFrame && aFrame->GetType() == nsGkAtoms::menuFrame)
|
2007-07-08 00:08:04 -07:00
|
|
|
(static_cast<nsMenuFrame *>(aFrame))->CloseMenu(PR_TRUE);
|
2007-03-22 10:30:00 -07:00
|
|
|
return PR_TRUE;
|
|
|
|
}
|
|
|
|
|
2008-10-10 08:04:34 -07:00
|
|
|
static PRBool
|
2007-03-22 10:30:00 -07:00
|
|
|
ReframeImageBoxes(nsIFrame *aFrame, void *aClosure)
|
|
|
|
{
|
2007-07-08 00:08:04 -07:00
|
|
|
nsStyleChangeList *list = static_cast<nsStyleChangeList*>(aClosure);
|
2007-03-22 10:30:00 -07:00
|
|
|
if (aFrame->GetType() == nsGkAtoms::imageBoxFrame) {
|
|
|
|
list->AppendChange(aFrame, aFrame->GetContent(),
|
|
|
|
NS_STYLE_HINT_FRAMECHANGE);
|
|
|
|
return PR_FALSE; // don't walk descendants
|
|
|
|
}
|
|
|
|
return PR_TRUE; // walk descendants
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
WalkFramesThroughPlaceholders(nsPresContext *aPresContext, nsIFrame *aFrame,
|
|
|
|
frameWalkerFn aFunc, void *aClosure)
|
|
|
|
{
|
|
|
|
PRBool walkChildren = (*aFunc)(aFrame, aClosure);
|
|
|
|
if (!walkChildren)
|
|
|
|
return;
|
|
|
|
|
|
|
|
PRInt32 listIndex = 0;
|
|
|
|
nsIAtom* childList = nsnull;
|
|
|
|
|
|
|
|
do {
|
|
|
|
nsIFrame *child = aFrame->GetFirstChild(childList);
|
|
|
|
while (child) {
|
|
|
|
if (!(child->GetStateBits() & NS_FRAME_OUT_OF_FLOW)) {
|
|
|
|
// only do frames that are in flow, and recur through the
|
|
|
|
// out-of-flows of placeholders.
|
|
|
|
WalkFramesThroughPlaceholders(aPresContext,
|
|
|
|
nsPlaceholderFrame::GetRealFrameFor(child),
|
|
|
|
aFunc, aClosure);
|
|
|
|
}
|
|
|
|
child = child->GetNextSibling();
|
|
|
|
}
|
|
|
|
|
|
|
|
childList = aFrame->GetAdditionalChildListName(listIndex++);
|
|
|
|
} while (childList);
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::Observe(nsISupports* aSubject,
|
|
|
|
const char* aTopic,
|
|
|
|
const PRUnichar* aData)
|
|
|
|
{
|
|
|
|
#ifdef MOZ_XUL
|
|
|
|
if (!nsCRT::strcmp(aTopic, "chrome-flush-skin-caches")) {
|
|
|
|
nsIFrame *rootFrame = FrameManager()->GetRootFrame();
|
|
|
|
// Need to null-check because "chrome-flush-skin-caches" can happen
|
|
|
|
// at interesting times during startup.
|
|
|
|
if (rootFrame) {
|
|
|
|
NS_ASSERTION(mViewManager, "View manager must exist");
|
2008-01-26 15:59:50 -08:00
|
|
|
nsIViewManager::UpdateViewBatch batch(mViewManager);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
WalkFramesThroughPlaceholders(mPresContext, rootFrame,
|
|
|
|
&ReResolveMenusAndTrees, nsnull);
|
|
|
|
|
|
|
|
// Because "chrome:" URL equality is messy, reframe image box
|
|
|
|
// frames (hack!).
|
|
|
|
nsStyleChangeList changeList;
|
|
|
|
WalkFramesThroughPlaceholders(mPresContext, rootFrame,
|
|
|
|
ReframeImageBoxes, &changeList);
|
2007-10-18 20:51:15 -07:00
|
|
|
// Mark ourselves as not safe to flush while we're doing frame
|
|
|
|
// construction.
|
2008-03-14 16:08:57 -07:00
|
|
|
{
|
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
|
|
|
mFrameConstructor->ProcessRestyledFrames(changeList);
|
|
|
|
}
|
2007-03-22 10:30:00 -07:00
|
|
|
|
2008-01-26 15:59:50 -08:00
|
|
|
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
|
2007-03-22 10:30:00 -07:00
|
|
|
#ifdef ACCESSIBILITY
|
|
|
|
InvalidateAccessibleSubtree(nsnull);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if (!nsCRT::strcmp(aTopic, NS_LINK_VISITED_EVENT_TOPIC)) {
|
|
|
|
nsCOMPtr<nsIURI> uri = do_QueryInterface(aSubject);
|
|
|
|
if (uri && mDocument) {
|
|
|
|
mDocument->NotifyURIVisitednessChanged(uri);
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nsCRT::strcmp(aTopic, "agent-sheet-added") && mStyleSet) {
|
|
|
|
AddAgentSheet(aSubject);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nsCRT::strcmp(aTopic, "user-sheet-added") && mStyleSet) {
|
|
|
|
AddUserSheet(aSubject);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nsCRT::strcmp(aTopic, "agent-sheet-removed") && mStyleSet) {
|
|
|
|
RemoveSheet(nsStyleSet::eAgentSheet, aSubject);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!nsCRT::strcmp(aTopic, "user-sheet-removed") && mStyleSet) {
|
|
|
|
RemoveSheet(nsStyleSet::eUserSheet, aSubject);
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-05-03 19:19:18 -07:00
|
|
|
#ifdef ACCESSIBILITY
|
|
|
|
if (!nsCRT::strcmp(aTopic, "a11y-init-or-shutdown")) {
|
|
|
|
gIsAccessibilityActive = aData && *aData == '1';
|
|
|
|
}
|
|
|
|
#endif
|
2007-03-22 10:30:00 -07:00
|
|
|
NS_WARNING("unrecognized topic in PresShell::Observe");
|
|
|
|
return NS_ERROR_FAILURE;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::EnumeratePlugins(nsIDOMDocument *aDocument,
|
|
|
|
const nsString &aPluginTag,
|
|
|
|
nsPluginEnumCallback aCallback)
|
|
|
|
{
|
|
|
|
nsCOMPtr<nsIDOMNodeList> nodes;
|
|
|
|
aDocument->GetElementsByTagName(aPluginTag, getter_AddRefs(nodes));
|
|
|
|
if (!nodes)
|
|
|
|
return;
|
|
|
|
|
|
|
|
PRUint32 length;
|
|
|
|
nodes->GetLength(&length);
|
|
|
|
|
|
|
|
for (PRUint32 i = 0; i < length; ++i) {
|
|
|
|
nsCOMPtr<nsIDOMNode> node;
|
|
|
|
nodes->Item(i, getter_AddRefs(node));
|
|
|
|
|
|
|
|
nsCOMPtr<nsIContent> content = do_QueryInterface(node);
|
|
|
|
if (content)
|
|
|
|
aCallback(this, content);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------
|
|
|
|
// End of protected and private methods on the PresShell
|
|
|
|
//------------------------------------------------------
|
|
|
|
|
|
|
|
// Start of DEBUG only code
|
|
|
|
|
|
|
|
#ifdef NS_DEBUG
|
|
|
|
#include "nsViewsCID.h"
|
|
|
|
#include "nsWidgetsCID.h"
|
|
|
|
#include "nsIDeviceContext.h"
|
|
|
|
#include "nsIURL.h"
|
|
|
|
#include "nsILinkHandler.h"
|
|
|
|
|
|
|
|
static NS_DEFINE_CID(kViewManagerCID, NS_VIEW_MANAGER_CID);
|
|
|
|
static NS_DEFINE_CID(kWidgetCID, NS_CHILD_CID);
|
|
|
|
|
|
|
|
static void
|
|
|
|
LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg)
|
|
|
|
{
|
|
|
|
printf("verifyreflow: ");
|
|
|
|
nsAutoString name;
|
|
|
|
if (nsnull != k1) {
|
2009-01-12 11:20:59 -08:00
|
|
|
nsIFrameDebug *frameDebug = do_QueryFrame(k1);
|
|
|
|
if (frameDebug) {
|
2007-03-22 10:30:00 -07:00
|
|
|
frameDebug->GetFrameName(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
name.Assign(NS_LITERAL_STRING("(null)"));
|
|
|
|
}
|
|
|
|
fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
|
|
|
|
|
2007-04-25 08:49:55 -07:00
|
|
|
fprintf(stdout, " %p ", (void*)k1);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
printf(" != ");
|
|
|
|
|
|
|
|
if (nsnull != k2) {
|
2009-01-12 11:20:59 -08:00
|
|
|
nsIFrameDebug *frameDebug = do_QueryFrame(k2);
|
|
|
|
if (frameDebug) {
|
2007-03-22 10:30:00 -07:00
|
|
|
frameDebug->GetFrameName(name);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
name.Assign(NS_LITERAL_STRING("(null)"));
|
|
|
|
}
|
|
|
|
fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
|
|
|
|
|
2007-04-25 08:49:55 -07:00
|
|
|
fprintf(stdout, " %p ", (void*)k2);
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
printf(" %s", aMsg);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
|
|
|
|
const nsRect& r1, const nsRect& r2)
|
|
|
|
{
|
|
|
|
printf("VerifyReflow Error:\n");
|
|
|
|
nsAutoString name;
|
2009-01-12 11:20:59 -08:00
|
|
|
nsIFrameDebug *frameDebug = do_QueryFrame(k1);
|
|
|
|
if (frameDebug) {
|
2007-03-22 10:30:00 -07:00
|
|
|
fprintf(stdout, " ");
|
|
|
|
frameDebug->GetFrameName(name);
|
|
|
|
fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
|
|
|
|
fprintf(stdout, " %p ", (void*)k1);
|
|
|
|
}
|
|
|
|
printf("{%d, %d, %d, %d}", r1.x, r1.y, r1.width, r1.height);
|
|
|
|
|
|
|
|
printf(" != \n");
|
|
|
|
|
2009-01-12 11:20:59 -08:00
|
|
|
frameDebug = do_QueryFrame(k2);
|
|
|
|
if (frameDebug) {
|
2007-03-22 10:30:00 -07:00
|
|
|
fprintf(stdout, " ");
|
|
|
|
frameDebug->GetFrameName(name);
|
|
|
|
fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
|
|
|
|
fprintf(stdout, " %p ", (void*)k2);
|
|
|
|
}
|
|
|
|
printf("{%d, %d, %d, %d}\n", r2.x, r2.y, r2.width, r2.height);
|
|
|
|
|
|
|
|
printf(" %s\n", aMsg);
|
|
|
|
}
|
|
|
|
|
2009-01-14 19:27:09 -08:00
|
|
|
static void
|
|
|
|
LogVerifyMessage(nsIFrame* k1, nsIFrame* k2, const char* aMsg,
|
|
|
|
const nsIntRect& r1, const nsIntRect& r2)
|
|
|
|
{
|
|
|
|
printf("VerifyReflow Error:\n");
|
|
|
|
nsAutoString name;
|
|
|
|
nsIFrameDebug *frameDebug = do_QueryFrame(k1);
|
|
|
|
if (frameDebug) {
|
|
|
|
fprintf(stdout, " ");
|
|
|
|
frameDebug->GetFrameName(name);
|
|
|
|
fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
|
|
|
|
fprintf(stdout, " %p ", (void*)k1);
|
|
|
|
}
|
|
|
|
printf("{%d, %d, %d, %d}", r1.x, r1.y, r1.width, r1.height);
|
|
|
|
|
|
|
|
printf(" != \n");
|
|
|
|
|
|
|
|
frameDebug = do_QueryFrame(k2);
|
|
|
|
if (frameDebug) {
|
|
|
|
fprintf(stdout, " ");
|
|
|
|
frameDebug->GetFrameName(name);
|
|
|
|
fputs(NS_LossyConvertUTF16toASCII(name).get(), stdout);
|
|
|
|
fprintf(stdout, " %p ", (void*)k2);
|
|
|
|
}
|
|
|
|
printf("{%d, %d, %d, %d}\n", r2.x, r2.y, r2.width, r2.height);
|
|
|
|
|
|
|
|
printf(" %s\n", aMsg);
|
|
|
|
}
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
static PRBool
|
|
|
|
CompareTrees(nsPresContext* aFirstPresContext, nsIFrame* aFirstFrame,
|
|
|
|
nsPresContext* aSecondPresContext, nsIFrame* aSecondFrame)
|
|
|
|
{
|
|
|
|
if (!aFirstPresContext || !aFirstFrame || !aSecondPresContext || !aSecondFrame)
|
|
|
|
return PR_TRUE;
|
2007-05-22 17:11:05 -07:00
|
|
|
// XXX Evil hack to reduce false positives; I can't seem to figure
|
|
|
|
// out how to flush scrollbar changes correctly
|
|
|
|
//if (aFirstFrame->GetType() == nsGkAtoms::scrollbarFrame)
|
|
|
|
// return PR_TRUE;
|
2007-03-22 10:30:00 -07:00
|
|
|
PRBool ok = PR_TRUE;
|
|
|
|
nsIAtom* listName = nsnull;
|
|
|
|
PRInt32 listIndex = 0;
|
|
|
|
do {
|
|
|
|
nsIFrame* k1 = aFirstFrame->GetFirstChild(listName);
|
|
|
|
nsIFrame* k2 = aSecondFrame->GetFirstChild(listName);
|
|
|
|
PRInt32 l1 = nsContainerFrame::LengthOf(k1);
|
|
|
|
PRInt32 l2 = nsContainerFrame::LengthOf(k2);
|
|
|
|
if (l1 != l2) {
|
|
|
|
ok = PR_FALSE;
|
|
|
|
LogVerifyMessage(k1, k2, "child counts don't match: ");
|
|
|
|
printf("%d != %d\n", l1, l2);
|
|
|
|
if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2009-01-14 19:27:09 -08:00
|
|
|
nsIntRect r1, r2;
|
2007-03-22 10:30:00 -07:00
|
|
|
nsIView* v1, *v2;
|
|
|
|
for (;;) {
|
|
|
|
if (((nsnull == k1) && (nsnull != k2)) ||
|
|
|
|
((nsnull != k1) && (nsnull == k2))) {
|
|
|
|
ok = PR_FALSE;
|
|
|
|
LogVerifyMessage(k1, k2, "child lists are different\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
else if (nsnull != k1) {
|
|
|
|
// Verify that the frames are the same size
|
|
|
|
if (k1->GetRect() != k2->GetRect()) {
|
|
|
|
ok = PR_FALSE;
|
|
|
|
LogVerifyMessage(k1, k2, "(frame rects)", k1->GetRect(), k2->GetRect());
|
|
|
|
}
|
|
|
|
|
|
|
|
// Make sure either both have views or neither have views; if they
|
|
|
|
// do have views, make sure the views are the same size. If the
|
|
|
|
// views have widgets, make sure they both do or neither does. If
|
|
|
|
// they do, make sure the widgets are the same size.
|
|
|
|
v1 = k1->GetView();
|
|
|
|
v2 = k2->GetView();
|
|
|
|
if (((nsnull == v1) && (nsnull != v2)) ||
|
|
|
|
((nsnull != v1) && (nsnull == v2))) {
|
|
|
|
ok = PR_FALSE;
|
|
|
|
LogVerifyMessage(k1, k2, "child views are not matched\n");
|
|
|
|
}
|
|
|
|
else if (nsnull != v1) {
|
|
|
|
if (v1->GetBounds() != v2->GetBounds()) {
|
|
|
|
LogVerifyMessage(k1, k2, "(view rects)", v1->GetBounds(), v2->GetBounds());
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIWidget* w1 = v1->GetWidget();
|
|
|
|
nsIWidget* w2 = v2->GetWidget();
|
|
|
|
if (((nsnull == w1) && (nsnull != w2)) ||
|
|
|
|
((nsnull != w1) && (nsnull == w2))) {
|
|
|
|
ok = PR_FALSE;
|
|
|
|
LogVerifyMessage(k1, k2, "child widgets are not matched\n");
|
|
|
|
}
|
|
|
|
else if (nsnull != w1) {
|
|
|
|
w1->GetBounds(r1);
|
|
|
|
w2->GetBounds(r2);
|
|
|
|
if (r1 != r2) {
|
|
|
|
LogVerifyMessage(k1, k2, "(widget rects)", r1, r2);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!ok && (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags))) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2009-01-04 11:52:38 -08:00
|
|
|
// XXX Should perhaps compare their float managers.
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Compare the sub-trees too
|
|
|
|
if (!CompareTrees(aFirstPresContext, k1, aSecondPresContext, k2)) {
|
|
|
|
ok = PR_FALSE;
|
|
|
|
if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Advance to next sibling
|
|
|
|
k1 = k1->GetNextSibling();
|
|
|
|
k2 = k2->GetNextSibling();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!ok && (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags))) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIAtom* listName1 = aFirstFrame->GetAdditionalChildListName(listIndex);
|
|
|
|
nsIAtom* listName2 = aSecondFrame->GetAdditionalChildListName(listIndex);
|
|
|
|
listIndex++;
|
|
|
|
if (listName1 != listName2) {
|
|
|
|
if (0 == (VERIFY_REFLOW_ALL & gVerifyReflowFlags)) {
|
|
|
|
ok = PR_FALSE;
|
|
|
|
}
|
|
|
|
LogVerifyMessage(k1, k2, "child list names are not matched: ");
|
|
|
|
nsAutoString tmp;
|
|
|
|
if (nsnull != listName1) {
|
|
|
|
listName1->ToString(tmp);
|
|
|
|
fputs(NS_LossyConvertUTF16toASCII(tmp).get(), stdout);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fputs("(null)", stdout);
|
|
|
|
printf(" != ");
|
|
|
|
if (nsnull != listName2) {
|
|
|
|
listName2->ToString(tmp);
|
|
|
|
fputs(NS_LossyConvertUTF16toASCII(tmp).get(), stdout);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
fputs("(null)", stdout);
|
|
|
|
printf("\n");
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
listName = listName1;
|
|
|
|
} while (ok && (listName != nsnull));
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#if 0
|
|
|
|
static nsIFrame*
|
|
|
|
FindTopFrame(nsIFrame* aRoot)
|
|
|
|
{
|
|
|
|
if (aRoot) {
|
|
|
|
nsIContent* content = aRoot->GetContent();
|
|
|
|
if (content) {
|
|
|
|
nsIAtom* tag;
|
|
|
|
content->GetTag(tag);
|
|
|
|
if (nsnull != tag) {
|
|
|
|
NS_RELEASE(tag);
|
|
|
|
return aRoot;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Try one of the children
|
|
|
|
nsIFrame* kid = aRoot->GetFirstChild(nsnull);
|
|
|
|
while (nsnull != kid) {
|
|
|
|
nsIFrame* result = FindTopFrame(kid);
|
|
|
|
if (nsnull != result) {
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
kid = kid->GetNextSibling();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nsnull;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
|
|
|
|
nsresult
|
|
|
|
PresShell::CloneStyleSet(nsStyleSet* aSet, nsStyleSet** aResult)
|
|
|
|
{
|
|
|
|
nsStyleSet *clone = new nsStyleSet();
|
|
|
|
if (!clone) {
|
|
|
|
return NS_ERROR_OUT_OF_MEMORY;
|
|
|
|
}
|
|
|
|
|
|
|
|
PRInt32 i, n = aSet->SheetCount(nsStyleSet::eOverrideSheet);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eOverrideSheet, i);
|
|
|
|
if (ss)
|
|
|
|
clone->AppendStyleSheet(nsStyleSet::eOverrideSheet, ss);
|
|
|
|
}
|
|
|
|
|
2007-05-22 17:11:05 -07:00
|
|
|
// The document expects to insert document stylesheets itself
|
|
|
|
#if 0
|
2007-03-22 10:30:00 -07:00
|
|
|
n = aSet->SheetCount(nsStyleSet::eDocSheet);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eDocSheet, i);
|
|
|
|
if (ss)
|
|
|
|
clone->AddDocStyleSheet(ss, mDocument);
|
|
|
|
}
|
2007-05-22 17:11:05 -07:00
|
|
|
#endif
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
n = aSet->SheetCount(nsStyleSet::eUserSheet);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eUserSheet, i);
|
|
|
|
if (ss)
|
|
|
|
clone->AppendStyleSheet(nsStyleSet::eUserSheet, ss);
|
|
|
|
}
|
|
|
|
|
|
|
|
n = aSet->SheetCount(nsStyleSet::eAgentSheet);
|
|
|
|
for (i = 0; i < n; i++) {
|
|
|
|
nsIStyleSheet* ss = aSet->StyleSheetAt(nsStyleSet::eAgentSheet, i);
|
|
|
|
if (ss)
|
|
|
|
clone->AppendStyleSheet(nsStyleSet::eAgentSheet, ss);
|
|
|
|
}
|
|
|
|
*aResult = clone;
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
2007-05-22 17:11:05 -07:00
|
|
|
#ifdef DEBUG_Eli
|
|
|
|
static nsresult
|
|
|
|
DumpToPNG(nsIPresShell* shell, nsAString& name) {
|
|
|
|
PRInt32 width=1000, height=1000;
|
|
|
|
nsRect r(0, 0, shell->GetPresContext()->DevPixelsToAppUnits(width),
|
|
|
|
shell->GetPresContext()->DevPixelsToAppUnits(height));
|
|
|
|
|
|
|
|
nsRefPtr<gfxImageSurface> imgSurface =
|
|
|
|
new gfxImageSurface(gfxIntSize(width, height),
|
|
|
|
gfxImageSurface::ImageFormatARGB32);
|
|
|
|
NS_ENSURE_TRUE(imgSurface, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
|
|
|
|
nsRefPtr<gfxContext> imgContext = new gfxContext(imgSurface);
|
|
|
|
|
|
|
|
nsRefPtr<gfxASurface> surface =
|
|
|
|
gfxPlatform::GetPlatform()->
|
|
|
|
CreateOffscreenSurface(gfxIntSize(width, height),
|
|
|
|
gfxASurface::ImageFormatARGB32);
|
|
|
|
NS_ENSURE_TRUE(surface, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
|
|
|
|
nsRefPtr<gfxContext> context = new gfxContext(surface);
|
|
|
|
NS_ENSURE_TRUE(context, NS_ERROR_OUT_OF_MEMORY);
|
|
|
|
|
2008-12-12 02:50:31 -08:00
|
|
|
nsresult rv = shell->RenderDocument(r, 0, NS_RGB(255, 255, 0), context);
|
2007-05-22 17:11:05 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
imgContext->DrawSurface(surface, gfxSize(width, height));
|
|
|
|
|
|
|
|
nsCOMPtr<imgIEncoder> encoder = do_CreateInstance("@mozilla.org/image/encoder;2?type=image/png");
|
|
|
|
NS_ENSURE_TRUE(encoder, NS_ERROR_FAILURE);
|
|
|
|
encoder->InitFromData(imgSurface->Data(), imgSurface->Stride() * height,
|
|
|
|
width, height, imgSurface->Stride(),
|
|
|
|
imgIEncoder::INPUT_FORMAT_HOSTARGB, EmptyString());
|
|
|
|
|
|
|
|
// XXX not sure if this is the right way to write to a file
|
|
|
|
nsCOMPtr<nsILocalFile> file = do_CreateInstance("@mozilla.org/file/local;1");
|
|
|
|
NS_ENSURE_TRUE(file, NS_ERROR_FAILURE);
|
|
|
|
rv = file->InitWithPath(name);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
PRUint32 length;
|
|
|
|
encoder->Available(&length);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIOutputStream> outputStream;
|
|
|
|
rv = NS_NewLocalFileOutputStream(getter_AddRefs(outputStream), file);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIOutputStream> bufferedOutputStream;
|
|
|
|
rv = NS_NewBufferedOutputStream(getter_AddRefs(bufferedOutputStream),
|
|
|
|
outputStream, length);
|
|
|
|
|
|
|
|
PRUint32 numWritten;
|
|
|
|
rv = bufferedOutputStream->WriteFrom(encoder, length, &numWritten);
|
|
|
|
NS_ENSURE_SUCCESS(rv, rv);
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
// After an incremental reflow, we verify the correctness by doing a
|
|
|
|
// full reflow into a fresh frame tree.
|
|
|
|
PRBool
|
|
|
|
PresShell::VerifyIncrementalReflow()
|
|
|
|
{
|
|
|
|
if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
|
|
|
|
printf("Building Verification Tree...\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Create a presentation context to view the new frame tree
|
2007-04-25 08:49:55 -07:00
|
|
|
nsCOMPtr<nsPresContext> cx =
|
|
|
|
new nsPresContext(mDocument, mPresContext->IsPaginated() ?
|
|
|
|
nsPresContext::eContext_PrintPreview :
|
|
|
|
nsPresContext::eContext_Galley);
|
|
|
|
NS_ENSURE_TRUE(cx, PR_FALSE);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
nsIDeviceContext *dc = mPresContext->DeviceContext();
|
|
|
|
nsresult rv = cx->Init(dc);
|
2007-04-25 08:49:55 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Get our scrolling preference
|
|
|
|
nsIView* rootView;
|
|
|
|
mViewManager->GetRootView(rootView);
|
|
|
|
void* nativeParentWidget = rootView->GetWidget()->GetNativeData(NS_NATIVE_WIDGET);
|
|
|
|
|
|
|
|
// Create a new view manager.
|
2007-04-25 08:49:55 -07:00
|
|
|
nsCOMPtr<nsIViewManager> vm = do_CreateInstance(kViewManagerCID);
|
|
|
|
NS_ENSURE_TRUE(vm, PR_FALSE);
|
2007-03-22 10:30:00 -07:00
|
|
|
rv = vm->Init(dc);
|
2007-04-25 08:49:55 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Create a child window of the parent that is our "root view/window"
|
|
|
|
// Create a view
|
|
|
|
nsRect tbounds = mPresContext->GetVisibleArea();
|
|
|
|
nsIView* view = vm->CreateView(tbounds, nsnull);
|
2007-04-25 08:49:55 -07:00
|
|
|
NS_ENSURE_TRUE(view, PR_FALSE);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
//now create the widget for the view
|
|
|
|
rv = view->CreateWidget(kWidgetCID, nsnull, nativeParentWidget, PR_TRUE);
|
2007-04-25 08:49:55 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
2007-03-22 10:30:00 -07:00
|
|
|
|
|
|
|
// Setup hierarchical relationship in view manager
|
|
|
|
vm->SetRootView(view);
|
|
|
|
|
|
|
|
// Make the new presentation context the same size as our
|
|
|
|
// presentation context.
|
|
|
|
nsRect r = mPresContext->GetVisibleArea();
|
|
|
|
cx->SetVisibleArea(r);
|
|
|
|
|
|
|
|
// Create a new presentation shell to view the document. Use the
|
|
|
|
// exact same style information that this document has.
|
|
|
|
nsAutoPtr<nsStyleSet> newSet;
|
|
|
|
rv = CloneStyleSet(mStyleSet, getter_Transfers(newSet));
|
2007-04-25 08:49:55 -07:00
|
|
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
|
|
|
nsCOMPtr<nsIPresShell> sh;
|
|
|
|
rv = mDocument->CreateShell(cx, vm, newSet, getter_AddRefs(sh));
|
|
|
|
NS_ENSURE_SUCCESS(rv, PR_FALSE);
|
|
|
|
newSet.forget();
|
|
|
|
// Note that after we create the shell, we must make sure to destroy it
|
2007-03-22 10:30:00 -07:00
|
|
|
sh->SetVerifyReflowEnable(PR_FALSE); // turn off verify reflow while we're reflowing the test frame tree
|
2007-04-25 08:49:55 -07:00
|
|
|
vm->SetViewObserver((nsIViewObserver *)((PresShell*)sh.get()));
|
2008-12-20 06:28:19 -08:00
|
|
|
{
|
|
|
|
nsAutoScriptBlocker scriptBlocker;
|
|
|
|
sh->InitialReflow(r.width, r.height);
|
|
|
|
}
|
2007-04-25 08:49:55 -07:00
|
|
|
mDocument->BindingManager()->ProcessAttachedQueue();
|
|
|
|
sh->FlushPendingNotifications(Flush_Layout);
|
2007-03-22 10:30:00 -07:00
|
|
|
sh->SetVerifyReflowEnable(PR_TRUE); // turn on verify reflow again now that we're done reflowing the test frame tree
|
2007-05-22 17:11:05 -07:00
|
|
|
// Force the non-primary presshell to unsuppress; it doesn't want to normally
|
|
|
|
// because it thinks it's hidden
|
|
|
|
((PresShell*)sh.get())->mPaintingSuppressed = PR_FALSE;
|
2007-03-22 10:30:00 -07:00
|
|
|
if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
|
|
|
|
printf("Verification Tree built, comparing...\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now that the document has been reflowed, use its frame tree to
|
|
|
|
// compare against our frame tree.
|
|
|
|
nsIFrame* root1 = FrameManager()->GetRootFrame();
|
|
|
|
nsIFrame* root2 = sh->FrameManager()->GetRootFrame();
|
|
|
|
PRBool ok = CompareTrees(mPresContext, root1, cx, root2);
|
|
|
|
if (!ok && (VERIFY_REFLOW_NOISY & gVerifyReflowFlags)) {
|
|
|
|
printf("Verify reflow failed, primary tree:\n");
|
2009-01-12 11:20:59 -08:00
|
|
|
nsIFrameDebug* frameDebug = do_QueryFrame(root1);
|
|
|
|
if (frameDebug) {
|
2007-03-22 10:30:00 -07:00
|
|
|
frameDebug->List(stdout, 0);
|
|
|
|
}
|
|
|
|
printf("Verification tree:\n");
|
2009-01-12 11:20:59 -08:00
|
|
|
frameDebug = do_QueryFrame(root2);
|
|
|
|
if (frameDebug) {
|
2007-03-22 10:30:00 -07:00
|
|
|
frameDebug->List(stdout, 0);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2007-05-22 17:11:05 -07:00
|
|
|
#ifdef DEBUG_Eli
|
|
|
|
// Sample code for dumping page to png
|
|
|
|
// XXX Needs to be made more flexible
|
|
|
|
if (!ok) {
|
|
|
|
nsString stra;
|
|
|
|
static int num = 0;
|
|
|
|
stra.AppendLiteral("C:\\mozilla\\mozilla\\debug\\filea");
|
|
|
|
stra.AppendInt(num);
|
|
|
|
stra.AppendLiteral(".png");
|
|
|
|
DumpToPNG(sh, stra);
|
|
|
|
nsString strb;
|
|
|
|
strb.AppendLiteral("C:\\mozilla\\mozilla\\debug\\fileb");
|
|
|
|
strb.AppendInt(num);
|
|
|
|
strb.AppendLiteral(".png");
|
|
|
|
DumpToPNG(this, strb);
|
|
|
|
++num;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
2007-03-22 10:30:00 -07:00
|
|
|
sh->EndObservingDocument();
|
2007-04-25 08:49:55 -07:00
|
|
|
sh->Destroy();
|
2007-03-22 10:30:00 -07:00
|
|
|
if (VERIFY_REFLOW_NOISY & gVerifyReflowFlags) {
|
|
|
|
printf("Finished Verifying Reflow...\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
return ok;
|
|
|
|
}
|
|
|
|
|
|
|
|
// Layout debugging hooks
|
|
|
|
void
|
|
|
|
PresShell::ListStyleContexts(nsIFrame *aRootFrame, FILE *out, PRInt32 aIndent)
|
|
|
|
{
|
|
|
|
nsStyleContext *sc = aRootFrame->GetStyleContext();
|
|
|
|
if (sc)
|
|
|
|
sc->List(out, aIndent);
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::ListStyleSheets(FILE *out, PRInt32 aIndent)
|
|
|
|
{
|
|
|
|
PRInt32 sheetCount = mStyleSet->SheetCount(nsStyleSet::eDocSheet);
|
|
|
|
for (PRInt32 i = 0; i < sheetCount; ++i) {
|
|
|
|
mStyleSet->StyleSheetAt(nsStyleSet::eDocSheet, i)->List(out, aIndent);
|
|
|
|
fputs("\n", out);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
PresShell::VerifyStyleTree()
|
|
|
|
{
|
|
|
|
VERIFY_STYLE_TREE;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
//=============================================================
|
|
|
|
//=============================================================
|
|
|
|
//-- Debug Reflow Counts
|
|
|
|
//=============================================================
|
|
|
|
//=============================================================
|
|
|
|
#ifdef MOZ_REFLOW_PERF
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::DumpReflows()
|
|
|
|
{
|
|
|
|
if (mReflowCountMgr) {
|
|
|
|
nsCAutoString uriStr;
|
|
|
|
if (mDocument) {
|
|
|
|
nsIURI *uri = mDocument->GetDocumentURI();
|
|
|
|
if (uri) {
|
|
|
|
uri->GetPath(uriStr);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mReflowCountMgr->DisplayTotals(uriStr.get());
|
|
|
|
mReflowCountMgr->DisplayHTMLTotals(uriStr.get());
|
|
|
|
mReflowCountMgr->DisplayDiffsInTotals("Differences");
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::CountReflows(const char * aName, nsIFrame * aFrame)
|
|
|
|
{
|
|
|
|
if (mReflowCountMgr) {
|
|
|
|
mReflowCountMgr->Add(aName, aFrame);
|
|
|
|
}
|
|
|
|
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::PaintCount(const char * aName, nsIRenderingContext* aRenderingContext, nsPresContext* aPresContext, nsIFrame * aFrame, PRUint32 aColor)
|
|
|
|
{
|
|
|
|
if (mReflowCountMgr) {
|
|
|
|
mReflowCountMgr->PaintCount(aName, aRenderingContext, aPresContext, aFrame, aColor);
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//-------------------------------------------------------------
|
|
|
|
NS_IMETHODIMP
|
|
|
|
PresShell::SetPaintFrameCount(PRBool aPaintFrameCounts)
|
|
|
|
{
|
|
|
|
if (mReflowCountMgr) {
|
|
|
|
mReflowCountMgr->SetPaintFrameCounts(aPaintFrameCounts);
|
|
|
|
}
|
|
|
|
return NS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
//-- Reflow Counter Classes Impls
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
ReflowCounter::ReflowCounter(ReflowCountMgr * aMgr) :
|
|
|
|
mMgr(aMgr)
|
|
|
|
{
|
|
|
|
ClearTotals();
|
|
|
|
SetTotalsCache();
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
ReflowCounter::~ReflowCounter()
|
|
|
|
{
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCounter::ClearTotals()
|
|
|
|
{
|
|
|
|
mTotal = 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCounter::SetTotalsCache()
|
|
|
|
{
|
|
|
|
mCacheTotal = mTotal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCounter::CalcDiffInTotals()
|
|
|
|
{
|
|
|
|
mCacheTotal = mTotal - mCacheTotal;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCounter::DisplayTotals(const char * aStr)
|
|
|
|
{
|
|
|
|
DisplayTotals(mTotal, aStr?aStr:"Totals");
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCounter::DisplayDiffTotals(const char * aStr)
|
|
|
|
{
|
|
|
|
DisplayTotals(mCacheTotal, aStr?aStr:"Diff Totals");
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCounter::DisplayHTMLTotals(const char * aStr)
|
|
|
|
{
|
|
|
|
DisplayHTMLTotals(mTotal, aStr?aStr:"Totals");
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCounter::DisplayTotals(PRUint32 aTotal, const char * aTitle)
|
|
|
|
{
|
|
|
|
// figure total
|
|
|
|
if (aTotal == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
ReflowCounter * gTots = (ReflowCounter *)mMgr->LookUp(kGrandTotalsStr);
|
|
|
|
|
|
|
|
printf("%25s\t", aTitle);
|
|
|
|
printf("%d\t", aTotal);
|
|
|
|
if (gTots != this && aTotal > 0) {
|
|
|
|
gTots->Add(aTotal);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCounter::DisplayHTMLTotals(PRUint32 aTotal, const char * aTitle)
|
|
|
|
{
|
|
|
|
if (aTotal == 0) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
ReflowCounter * gTots = (ReflowCounter *)mMgr->LookUp(kGrandTotalsStr);
|
|
|
|
FILE * fd = mMgr->GetOutFile();
|
|
|
|
if (!fd) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
fprintf(fd, "<tr><td><center>%s</center></td>", aTitle);
|
|
|
|
fprintf(fd, "<td><center>%d</center></td></tr>\n", aTotal);
|
|
|
|
|
|
|
|
if (gTots != this && aTotal > 0) {
|
|
|
|
gTots->Add(aTotal);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
//-- ReflowCountMgr
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
ReflowCountMgr::ReflowCountMgr()
|
|
|
|
{
|
|
|
|
mCounts = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
|
|
|
|
PL_CompareValues, nsnull, nsnull);
|
|
|
|
mIndiFrameCounts = PL_NewHashTable(10, PL_HashString, PL_CompareStrings,
|
|
|
|
PL_CompareValues, nsnull, nsnull);
|
|
|
|
mCycledOnce = PR_FALSE;
|
|
|
|
mDumpFrameCounts = PR_FALSE;
|
|
|
|
mDumpFrameByFrameCounts = PR_FALSE;
|
|
|
|
mPaintFrameByFrameCounts = PR_FALSE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
ReflowCountMgr::~ReflowCountMgr()
|
|
|
|
{
|
|
|
|
CleanUp();
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
ReflowCounter * ReflowCountMgr::LookUp(const char * aName)
|
|
|
|
{
|
|
|
|
if (nsnull != mCounts) {
|
|
|
|
ReflowCounter * counter = (ReflowCounter *)PL_HashTableLookup(mCounts, aName);
|
|
|
|
return counter;
|
|
|
|
}
|
|
|
|
return nsnull;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCountMgr::Add(const char * aName, nsIFrame * aFrame)
|
|
|
|
{
|
|
|
|
NS_ASSERTION(aName != nsnull, "Name shouldn't be null!");
|
|
|
|
|
|
|
|
if (mDumpFrameCounts && nsnull != mCounts) {
|
|
|
|
ReflowCounter * counter = (ReflowCounter *)PL_HashTableLookup(mCounts, aName);
|
|
|
|
if (counter == nsnull) {
|
|
|
|
counter = new ReflowCounter(this);
|
|
|
|
NS_ASSERTION(counter != nsnull, "null ptr");
|
|
|
|
char * name = NS_strdup(aName);
|
|
|
|
NS_ASSERTION(name != nsnull, "null ptr");
|
|
|
|
PL_HashTableAdd(mCounts, name, counter);
|
|
|
|
}
|
|
|
|
counter->Add();
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((mDumpFrameByFrameCounts || mPaintFrameByFrameCounts) &&
|
|
|
|
nsnull != mIndiFrameCounts &&
|
|
|
|
aFrame != nsnull) {
|
|
|
|
char * key = new char[16];
|
|
|
|
sprintf(key, "%p", (void*)aFrame);
|
|
|
|
IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(mIndiFrameCounts, key);
|
|
|
|
if (counter == nsnull) {
|
|
|
|
counter = new IndiReflowCounter(this);
|
|
|
|
NS_ASSERTION(counter != nsnull, "null ptr");
|
|
|
|
counter->mFrame = aFrame;
|
|
|
|
counter->mName.AssignASCII(aName);
|
|
|
|
PL_HashTableAdd(mIndiFrameCounts, key, counter);
|
|
|
|
}
|
|
|
|
// this eliminates extra counts from super classes
|
|
|
|
if (counter != nsnull && counter->mName.EqualsASCII(aName)) {
|
|
|
|
counter->mCount++;
|
|
|
|
counter->mCounter.Add(1);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCountMgr::PaintCount(const char * aName,
|
|
|
|
nsIRenderingContext* aRenderingContext,
|
|
|
|
nsPresContext* aPresContext,
|
|
|
|
nsIFrame* aFrame,
|
|
|
|
PRUint32 aColor)
|
|
|
|
{
|
|
|
|
if (mPaintFrameByFrameCounts &&
|
|
|
|
nsnull != mIndiFrameCounts &&
|
|
|
|
aFrame != nsnull) {
|
|
|
|
char * key = new char[16];
|
|
|
|
sprintf(key, "%p", (void*)aFrame);
|
|
|
|
IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(mIndiFrameCounts, key);
|
|
|
|
if (counter != nsnull && counter->mName.EqualsASCII(aName)) {
|
|
|
|
aRenderingContext->PushState();
|
|
|
|
nsFont font("Times", NS_FONT_STYLE_NORMAL, NS_FONT_VARIANT_NORMAL,
|
2009-01-29 12:39:18 -08:00
|
|
|
NS_FONT_WEIGHT_NORMAL, NS_FONT_STRETCH_NORMAL, 0,
|
2007-03-22 10:30:00 -07:00
|
|
|
nsPresContext::CSSPixelsToAppUnits(11));
|
|
|
|
|
|
|
|
nsCOMPtr<nsIFontMetrics> fm = aPresContext->GetMetricsFor(font);
|
|
|
|
aRenderingContext->SetFont(fm);
|
|
|
|
char buf[16];
|
|
|
|
sprintf(buf, "%d", counter->mCount);
|
|
|
|
nscoord x = 0, y;
|
|
|
|
nscoord width, height;
|
|
|
|
aRenderingContext->SetTextRunRTL(PR_FALSE);
|
|
|
|
aRenderingContext->GetWidth((char*)buf, width);
|
|
|
|
fm->GetHeight(height);
|
|
|
|
fm->GetMaxAscent(y);
|
|
|
|
|
|
|
|
PRUint32 color;
|
|
|
|
PRUint32 color2;
|
|
|
|
if (aColor != 0) {
|
|
|
|
color = aColor;
|
|
|
|
color2 = NS_RGB(0,0,0);
|
|
|
|
} else {
|
|
|
|
PRUint8 rc = 0, gc = 0, bc = 0;
|
|
|
|
if (counter->mCount < 5) {
|
|
|
|
rc = 255;
|
|
|
|
gc = 255;
|
|
|
|
} else if ( counter->mCount < 11) {
|
|
|
|
gc = 255;
|
|
|
|
} else {
|
|
|
|
rc = 255;
|
|
|
|
}
|
|
|
|
color = NS_RGB(rc,gc,bc);
|
|
|
|
color2 = NS_RGB(rc/2,gc/2,bc/2);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsRect rect(0,0, width+15, height+15);
|
|
|
|
aRenderingContext->SetColor(NS_RGB(0,0,0));
|
|
|
|
aRenderingContext->FillRect(rect);
|
|
|
|
aRenderingContext->SetColor(color2);
|
|
|
|
aRenderingContext->DrawString(buf, strlen(buf), x+15,y+15);
|
|
|
|
aRenderingContext->SetColor(color);
|
|
|
|
aRenderingContext->DrawString(buf, strlen(buf), x,y);
|
|
|
|
|
|
|
|
aRenderingContext->PopState();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
PRIntn ReflowCountMgr::RemoveItems(PLHashEntry *he, PRIntn i, void *arg)
|
|
|
|
{
|
|
|
|
char *str = (char *)he->key;
|
|
|
|
ReflowCounter * counter = (ReflowCounter *)he->value;
|
|
|
|
delete counter;
|
|
|
|
NS_Free(str);
|
|
|
|
|
|
|
|
return HT_ENUMERATE_REMOVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
PRIntn ReflowCountMgr::RemoveIndiItems(PLHashEntry *he, PRIntn i, void *arg)
|
|
|
|
{
|
|
|
|
char *str = (char *)he->key;
|
|
|
|
IndiReflowCounter * counter = (IndiReflowCounter *)he->value;
|
|
|
|
delete counter;
|
|
|
|
NS_Free(str);
|
|
|
|
|
|
|
|
return HT_ENUMERATE_REMOVE;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCountMgr::CleanUp()
|
|
|
|
{
|
|
|
|
if (nsnull != mCounts) {
|
|
|
|
PL_HashTableEnumerateEntries(mCounts, RemoveItems, nsnull);
|
|
|
|
PL_HashTableDestroy(mCounts);
|
|
|
|
mCounts = nsnull;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (nsnull != mIndiFrameCounts) {
|
|
|
|
PL_HashTableEnumerateEntries(mIndiFrameCounts, RemoveIndiItems, nsnull);
|
|
|
|
PL_HashTableDestroy(mIndiFrameCounts);
|
|
|
|
mIndiFrameCounts = nsnull;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
PRIntn ReflowCountMgr::DoSingleTotal(PLHashEntry *he, PRIntn i, void *arg)
|
|
|
|
{
|
|
|
|
char *str = (char *)he->key;
|
|
|
|
ReflowCounter * counter = (ReflowCounter *)he->value;
|
|
|
|
|
|
|
|
counter->DisplayTotals(str);
|
|
|
|
|
|
|
|
return HT_ENUMERATE_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCountMgr::DoGrandTotals()
|
|
|
|
{
|
|
|
|
if (nsnull != mCounts) {
|
|
|
|
ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
|
|
|
|
if (gTots == nsnull) {
|
|
|
|
gTots = new ReflowCounter(this);
|
|
|
|
PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
|
|
|
|
} else {
|
|
|
|
gTots->ClearTotals();
|
|
|
|
}
|
|
|
|
|
|
|
|
printf("\t\t\t\tTotal\n");
|
|
|
|
for (PRUint32 i=0;i<78;i++) {
|
|
|
|
printf("-");
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
PL_HashTableEnumerateEntries(mCounts, DoSingleTotal, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void RecurseIndiTotals(nsPresContext* aPresContext,
|
|
|
|
PLHashTable * aHT,
|
|
|
|
nsIFrame * aParentFrame,
|
|
|
|
PRInt32 aLevel)
|
|
|
|
{
|
|
|
|
if (aParentFrame == nsnull) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
char key[16];
|
|
|
|
sprintf(key, "%p", (void*)aParentFrame);
|
|
|
|
IndiReflowCounter * counter = (IndiReflowCounter *)PL_HashTableLookup(aHT, key);
|
|
|
|
if (counter) {
|
|
|
|
counter->mHasBeenOutput = PR_TRUE;
|
|
|
|
char * name = ToNewCString(counter->mName);
|
|
|
|
for (PRInt32 i=0;i<aLevel;i++) printf(" ");
|
|
|
|
printf("%s - %p [%d][", name, (void*)aParentFrame, counter->mCount);
|
|
|
|
printf("%d", counter->mCounter.GetTotal());
|
|
|
|
printf("]\n");
|
|
|
|
nsMemory::Free(name);
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIFrame* child = aParentFrame->GetFirstChild(nsnull);
|
|
|
|
while (child) {
|
|
|
|
RecurseIndiTotals(aPresContext, aHT, child, aLevel+1);
|
|
|
|
child = child->GetNextSibling();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
PRIntn ReflowCountMgr::DoSingleIndi(PLHashEntry *he, PRIntn i, void *arg)
|
|
|
|
{
|
|
|
|
IndiReflowCounter * counter = (IndiReflowCounter *)he->value;
|
|
|
|
if (counter && !counter->mHasBeenOutput) {
|
|
|
|
char * name = ToNewCString(counter->mName);
|
|
|
|
printf("%s - %p [%d][", name, (void*)counter->mFrame, counter->mCount);
|
|
|
|
printf("%d", counter->mCounter.GetTotal());
|
|
|
|
printf("]\n");
|
|
|
|
nsMemory::Free(name);
|
|
|
|
}
|
|
|
|
return HT_ENUMERATE_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCountMgr::DoIndiTotalsTree()
|
|
|
|
{
|
|
|
|
if (nsnull != mCounts) {
|
|
|
|
printf("\n------------------------------------------------\n");
|
|
|
|
printf("-- Individual Frame Counts\n");
|
|
|
|
printf("------------------------------------------------\n");
|
|
|
|
|
|
|
|
if (mPresShell) {
|
|
|
|
nsIFrame * rootFrame = mPresShell->FrameManager()->GetRootFrame();
|
|
|
|
RecurseIndiTotals(mPresContext, mIndiFrameCounts, rootFrame, 0);
|
|
|
|
printf("------------------------------------------------\n");
|
|
|
|
printf("-- Individual Counts of Frames not in Root Tree\n");
|
|
|
|
printf("------------------------------------------------\n");
|
|
|
|
PL_HashTableEnumerateEntries(mIndiFrameCounts, DoSingleIndi, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
PRIntn ReflowCountMgr::DoSingleHTMLTotal(PLHashEntry *he, PRIntn i, void *arg)
|
|
|
|
{
|
|
|
|
char *str = (char *)he->key;
|
|
|
|
ReflowCounter * counter = (ReflowCounter *)he->value;
|
|
|
|
|
|
|
|
counter->DisplayHTMLTotals(str);
|
|
|
|
|
|
|
|
return HT_ENUMERATE_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCountMgr::DoGrandHTMLTotals()
|
|
|
|
{
|
|
|
|
if (nsnull != mCounts) {
|
|
|
|
ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
|
|
|
|
if (gTots == nsnull) {
|
|
|
|
gTots = new ReflowCounter(this);
|
|
|
|
PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
|
|
|
|
} else {
|
|
|
|
gTots->ClearTotals();
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char * title[] = {"Class", "Reflows"};
|
|
|
|
fprintf(mFD, "<tr>");
|
|
|
|
for (PRUint32 i=0; i < NS_ARRAY_LENGTH(title); i++) {
|
|
|
|
fprintf(mFD, "<td><center><b>%s<b></center></td>", title[i]);
|
|
|
|
}
|
|
|
|
fprintf(mFD, "</tr>\n");
|
|
|
|
PL_HashTableEnumerateEntries(mCounts, DoSingleHTMLTotal, this);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------
|
|
|
|
void ReflowCountMgr::DisplayTotals(const char * aStr)
|
|
|
|
{
|
|
|
|
#ifdef DEBUG_rods
|
|
|
|
printf("%s\n", aStr?aStr:"No name");
|
|
|
|
#endif
|
|
|
|
if (mDumpFrameCounts) {
|
|
|
|
DoGrandTotals();
|
|
|
|
}
|
|
|
|
if (mDumpFrameByFrameCounts) {
|
|
|
|
DoIndiTotalsTree();
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
//------------------------------------
|
|
|
|
void ReflowCountMgr::DisplayHTMLTotals(const char * aStr)
|
|
|
|
{
|
|
|
|
#ifdef WIN32x // XXX NOT XP!
|
|
|
|
char name[1024];
|
|
|
|
|
|
|
|
char * sptr = strrchr(aStr, '/');
|
|
|
|
if (sptr) {
|
|
|
|
sptr++;
|
|
|
|
strcpy(name, sptr);
|
|
|
|
char * eptr = strrchr(name, '.');
|
|
|
|
if (eptr) {
|
|
|
|
*eptr = 0;
|
|
|
|
}
|
|
|
|
strcat(name, "_stats.html");
|
|
|
|
}
|
|
|
|
mFD = fopen(name, "w");
|
|
|
|
if (mFD) {
|
|
|
|
fprintf(mFD, "<html><head><title>Reflow Stats</title></head><body>\n");
|
|
|
|
const char * title = aStr?aStr:"No name";
|
|
|
|
fprintf(mFD, "<center><b>%s</b><br><table border=1 style=\"background-color:#e0e0e0\">", title);
|
|
|
|
DoGrandHTMLTotals();
|
|
|
|
fprintf(mFD, "</center></table>\n");
|
|
|
|
fprintf(mFD, "</body></html>\n");
|
|
|
|
fclose(mFD);
|
|
|
|
mFD = nsnull;
|
|
|
|
}
|
|
|
|
#endif // not XP!
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
PRIntn ReflowCountMgr::DoClearTotals(PLHashEntry *he, PRIntn i, void *arg)
|
|
|
|
{
|
|
|
|
ReflowCounter * counter = (ReflowCounter *)he->value;
|
|
|
|
counter->ClearTotals();
|
|
|
|
|
|
|
|
return HT_ENUMERATE_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCountMgr::ClearTotals()
|
|
|
|
{
|
|
|
|
PL_HashTableEnumerateEntries(mCounts, DoClearTotals, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCountMgr::ClearGrandTotals()
|
|
|
|
{
|
|
|
|
if (nsnull != mCounts) {
|
|
|
|
ReflowCounter * gTots = (ReflowCounter *)PL_HashTableLookup(mCounts, kGrandTotalsStr);
|
|
|
|
if (gTots == nsnull) {
|
|
|
|
gTots = new ReflowCounter(this);
|
|
|
|
PL_HashTableAdd(mCounts, NS_strdup(kGrandTotalsStr), gTots);
|
|
|
|
} else {
|
|
|
|
gTots->ClearTotals();
|
|
|
|
gTots->SetTotalsCache();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
PRIntn ReflowCountMgr::DoDisplayDiffTotals(PLHashEntry *he, PRIntn i, void *arg)
|
|
|
|
{
|
|
|
|
PRBool cycledOnce = (arg != 0);
|
|
|
|
|
|
|
|
char *str = (char *)he->key;
|
|
|
|
ReflowCounter * counter = (ReflowCounter *)he->value;
|
|
|
|
|
|
|
|
if (cycledOnce) {
|
|
|
|
counter->CalcDiffInTotals();
|
|
|
|
counter->DisplayDiffTotals(str);
|
|
|
|
}
|
|
|
|
counter->SetTotalsCache();
|
|
|
|
|
|
|
|
return HT_ENUMERATE_NEXT;
|
|
|
|
}
|
|
|
|
|
|
|
|
//------------------------------------------------------------------
|
|
|
|
void ReflowCountMgr::DisplayDiffsInTotals(const char * aStr)
|
|
|
|
{
|
|
|
|
if (mCycledOnce) {
|
|
|
|
printf("Differences\n");
|
|
|
|
for (PRInt32 i=0;i<78;i++) {
|
|
|
|
printf("-");
|
|
|
|
}
|
|
|
|
printf("\n");
|
|
|
|
ClearGrandTotals();
|
|
|
|
}
|
|
|
|
PL_HashTableEnumerateEntries(mCounts, DoDisplayDiffTotals, (void *)mCycledOnce);
|
|
|
|
|
|
|
|
mCycledOnce = PR_TRUE;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif // MOZ_REFLOW_PERF
|
|
|
|
|
|
|
|
// make a color string like #RRGGBB
|
|
|
|
void ColorToString(nscolor aColor, nsAutoString &aString)
|
|
|
|
{
|
|
|
|
char buf[8];
|
|
|
|
|
|
|
|
PR_snprintf(buf, sizeof(buf), "#%02x%02x%02x",
|
|
|
|
NS_GET_R(aColor), NS_GET_G(aColor), NS_GET_B(aColor));
|
|
|
|
CopyASCIItoUTF16(buf, aString);
|
|
|
|
}
|
2008-01-21 23:39:06 -08:00
|
|
|
|
|
|
|
nsIFrame* nsIPresShell::GetAbsoluteContainingBlock(nsIFrame *aFrame)
|
|
|
|
{
|
|
|
|
return FrameConstructor()->GetAbsoluteContainingBlock(aFrame);
|
|
|
|
}
|