Back out 1ac4cb2e7c32 to c631f9c3e9a9 (bug 598482) for Android reftest failures

This commit is contained in:
Phil Ringnalda 2011-12-23 22:21:58 -08:00
parent eef3a778a8
commit 2e8e3cc006
53 changed files with 1070 additions and 562 deletions

View File

@ -1146,9 +1146,6 @@ nsEventStateManager::PreHandleEvent(nsPresContext* aPresContext,
GenerateDragGesture(aPresContext, (nsMouseEvent*)aEvent);
UpdateCursor(aPresContext, aEvent, mCurrentTarget, aStatus);
GenerateMouseEnterExit((nsGUIEvent*)aEvent);
// Flush pending layout changes, so that later mouse move events
// will go to the right nodes.
FlushPendingEvents(aPresContext);
break;
case NS_DRAGDROP_GESTURE:
if (mClickHoldContextMenu) {

View File

@ -113,7 +113,6 @@ _TEST_FILES = \
test_bug689564.html \
test_bug698929.html \
test_eventctors.html \
test_bug635465.html \
$(NULL)
#bug 585630

View File

@ -1,90 +0,0 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=635465
-->
<head>
<meta charset="utf-8">
<title>Test for Bug 635465</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
<style type="text/css">
#item {
position: relative;
}
.s-menu-section-submenu {
position: absolute;
display: none;
}
.open .s-menu-section-submenu {
display: block;
}
</style>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=635465">Mozilla Bug 635465</a>
<div id="display">
<div class="item" id="item"
onmouseover="showSubmenu(event)" onmouseout="hideSubmenu(event)">
<a href="#" id="firsthover">Hover me</a>
<div class="s-menu-section-submenu" id="menu">
<a href="#" id="secondhover">Now hover me</a>
</div>
</div>
</div>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript;version=1.8">
/** Test for Bug 635465 **/
function showSubmenu(event) {
var item = document.getElementById('item');
var width = item.offsetWidth; // IT WORKS IF YOU REMOVE THIS LINE
item.className='open';
}
function hideSubmenu(event) {
document.getElementById('item').className='';
}
SimpleTest.waitForExplicitFinish();
function executeTests() {
// First flush out layout of firsthover
ok($("firsthover").getBoundingClientRect().height > 0, true,
"Should have a nonzero height before hover");
// Now trigger a mouseover on firsthover
synthesizeMouseAtCenter($("firsthover"), { type: "mousemove" });
ok($("secondhover").getBoundingClientRect().height > 0, true,
"Should have a nonzero height for submenu after hover");
// Now determine where secondhover is hanging out
var rect = $("secondhover").getBoundingClientRect();
synthesizeMouseAtCenter($("secondhover"), { type: "mousemove" });
// And another mouseover one pixel to the right of where the center used to be
synthesizeMouseAtPoint(rect.left + rect.width/2 + 1,
rect.top + rect.height/2,
{ type: "mousemove" });
ok($("secondhover").getBoundingClientRect().height > 0, true,
"Should have a nonzero height for submenu after second hover");
// And check computed display of the menu
is(getComputedStyle($("menu"), "").display, "block", "Should have block display");
SimpleTest.finish();
}
SimpleTest.waitForFocus(executeTests);
</script>
</pre>
</body>
</html>

View File

@ -1160,6 +1160,10 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
// All nsTextControlFrames are widgets
editorFlags |= nsIPlaintextEditor::eEditorWidgetMask;
// Use async reflow and painting for text widgets to improve
// performance.
editorFlags |= nsIPlaintextEditor::eEditorUseAsyncUpdatesMask;
// Spell check is diabled at creation time. It is enabled once
// the editor comes into focus.
editorFlags |= nsIPlaintextEditor::eEditorSkipSpellCheck;
@ -1310,7 +1314,12 @@ nsTextEditorState::PrepareEditor(const nsAString *aValue)
// editor for us.
if (!defaultValue.IsEmpty()) {
rv = newEditor->SetFlags(editorFlags);
// Avoid causing reentrant painting and reflowing by telling the editor
// that we don't want it to force immediate view refreshes or force
// immediate reflows during any editor calls.
rv = newEditor->SetFlags(editorFlags |
nsIPlaintextEditor::eEditorUseAsyncUpdatesMask);
NS_ENSURE_SUCCESS(rv, rv);
// Now call SetValue() which will make the necessary editor calls to set
@ -1828,6 +1837,7 @@ nsTextEditorState::SetValue(const nsAString& aValue, bool aUserInput)
flags = savedFlags;
flags &= ~(nsIPlaintextEditor::eEditorDisabledMask);
flags &= ~(nsIPlaintextEditor::eEditorReadonlyMask);
flags |= nsIPlaintextEditor::eEditorUseAsyncUpdatesMask;
flags |= nsIPlaintextEditor::eEditorDontEchoPassword;
mEditor->SetFlags(flags);

View File

@ -4732,7 +4732,8 @@ nsDocShell::Repaint(bool aForce)
nsIViewManager* viewManager = presShell->GetViewManager();
NS_ENSURE_TRUE(viewManager, NS_ERROR_FAILURE);
NS_ENSURE_SUCCESS(viewManager->InvalidateAllViews(), NS_ERROR_FAILURE);
// what about aForce ?
NS_ENSURE_SUCCESS(viewManager->UpdateAllViews(0), NS_ERROR_FAILURE);
return NS_OK;
}
@ -7323,7 +7324,7 @@ nsDocShell::RestoreFromHistory()
// call Thaw. So we issue the invalidate here.
newRootView = newVM->GetRootView();
if (newRootView) {
newVM->InvalidateView(newRootView);
newVM->UpdateView(newRootView, NS_VMREFRESH_NO_SYNC);
}
}
}

View File

@ -761,6 +761,27 @@ nsDOMWindowUtils::CycleCollect(nsICycleCollectorListener *aListener)
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::ProcessUpdates()
{
nsPresContext* presContext = GetPresContext();
if (!presContext)
return NS_ERROR_UNEXPECTED;
nsIPresShell* shell = presContext->GetPresShell();
if (!shell)
return NS_ERROR_UNEXPECTED;
nsIViewManager *viewManager = shell->GetViewManager();
if (!viewManager)
return NS_ERROR_UNEXPECTED;
nsIViewManager::UpdateViewBatch batch;
batch.BeginUpdateViewBatch(viewManager);
batch.EndUpdateViewBatch(NS_VMREFRESH_IMMEDIATE);
return NS_OK;
}
NS_IMETHODIMP
nsDOMWindowUtils::SendSimpleGestureEvent(const nsAString& aType,
float aX,

View File

@ -69,7 +69,7 @@ interface nsIDOMBlob;
interface nsIDOMFile;
interface nsIFile;
[scriptable, uuid(b9c1f815-c2f2-4607-a060-6a8566581927)]
[scriptable, uuid(3af3c5ce-6f2a-47e7-acd0-555ed576fa82)]
interface nsIDOMWindowUtils : nsISupports {
/**
@ -378,6 +378,12 @@ interface nsIDOMWindowUtils : nsISupports {
*/
void cycleCollect([optional] in nsICycleCollectorListener aListener);
/**
* Force processing of any queued paints
*/
void processUpdates();
/** Synthesize a simple gesture event for a window. The event types
* supported are: MozSwipeGesture, MozMagnifyGestureStart,
* MozMagnifyGestureUpdate, MozMagnifyGesture, MozRotateGestureStart,

View File

@ -623,7 +623,8 @@ NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRect(NPRect *invalidRect)
if (mWidget) {
mWidget->Invalidate(nsIntRect(invalidRect->left, invalidRect->top,
invalidRect->right - invalidRect->left,
invalidRect->bottom - invalidRect->top));
invalidRect->bottom - invalidRect->top),
false);
return NS_OK;
}
#endif
@ -655,6 +656,12 @@ NS_IMETHODIMP nsPluginInstanceOwner::InvalidateRegion(NPRegion invalidRegion)
NS_IMETHODIMP nsPluginInstanceOwner::ForceRedraw()
{
NS_ENSURE_TRUE(mObjectFrame, NS_ERROR_NULL_POINTER);
nsIView* view = mObjectFrame->GetView();
if (view) {
return view->GetViewManager()->Composite();
}
return NS_OK;
}

View File

@ -37,7 +37,7 @@
#include "nsISupports.idl"
[scriptable, uuid(07b6d070-ccea-4a00-84b4-f4b94dd9eb52)]
[scriptable, uuid(05d312ef-8914-494e-91c9-2be8ed7f8e29)]
interface nsIPlaintextEditor : nsISupports
{
@ -56,27 +56,29 @@ interface nsIPlaintextEditor : nsISupports
const long eEditorFilterInputMask = 0x0020;
// use mail-compose editing rules
const long eEditorMailMask = 0x0040;
// prevent immediate reflows and view refreshes
const long eEditorUseAsyncUpdatesMask = 0x0080;
// allow the editor to set font: monospace on the root node
const long eEditorEnableWrapHackMask = 0x0080;
const long eEditorEnableWrapHackMask = 0x0100;
// bit for widgets (form elements)
const long eEditorWidgetMask = 0x0100;
const long eEditorWidgetMask = 0x0200;
// this HTML editor should not create css styles
const long eEditorNoCSSMask = 0x0200;
const long eEditorNoCSSMask = 0x0400;
// whether HTML document specific actions are executed or not.
// e.g., if this flag is set, the editor doesn't handle Tab key.
// besides, anchors of HTML are not clickable.
const long eEditorAllowInteraction = 0x0400;
const long eEditorAllowInteraction = 0x0800;
// when this is set, the characters in password editor are always masked.
// see bug 530367 for the detail.
const long eEditorDontEchoPassword = 0x0800;
const long eEditorDontEchoPassword = 0x1000;
// when this flag is set, the internal direction of the editor is RTL.
// if neither of the direction flags are set, the direction is determined
// from the text control's content node.
const long eEditorRightToLeft = 0x1000;
const long eEditorRightToLeft = 0x2000;
// when this flag is set, the internal direction of the editor is LTR.
const long eEditorLeftToRight = 0x2000;
const long eEditorLeftToRight = 0x4000;
// when this flag is set, the editor's text content is not spell checked.
const long eEditorSkipSpellCheck = 0x4000;
const long eEditorSkipSpellCheck = 0x8000;
/*
* The valid values for newlines handling.

View File

@ -4198,6 +4198,15 @@ nsresult nsEditor::BeginUpdateViewBatch()
nsCOMPtr<nsISelectionPrivate> selPrivate(do_QueryInterface(selection));
selPrivate->StartBatchChanges();
}
// Turn off view updating.
nsCOMPtr<nsIPresShell> ps = GetPresShell();
if (ps) {
nsCOMPtr<nsIViewManager> viewManager = ps->GetViewManager();
if (viewManager) {
mBatch.BeginUpdateViewBatch(viewManager);
}
}
}
mUpdateCount++;
@ -4233,6 +4242,20 @@ nsresult nsEditor::EndUpdateViewBatch()
StCaretHider caretHider(caret);
PRUint32 flags = 0;
GetFlags(&flags);
// Turn view updating back on.
PRUint32 updateFlag = NS_VMREFRESH_IMMEDIATE;
// If we're doing async updates, use NS_VMREFRESH_DEFERRED here, so that
// the reflows we caused will get processed before the invalidates.
if (flags & nsIPlaintextEditor::eEditorUseAsyncUpdatesMask) {
updateFlag = NS_VMREFRESH_DEFERRED;
}
mBatch.EndUpdateViewBatch(updateFlag);
// Turn selection updating and notifications back on.
nsCOMPtr<nsISelection>selection;

View File

@ -689,6 +689,11 @@ public:
return (mFlags & nsIPlaintextEditor::eEditorMailMask) != 0;
}
bool UseAsyncUpdate() const
{
return (mFlags & nsIPlaintextEditor::eEditorUseAsyncUpdatesMask) != 0;
}
bool IsWrapHackEnabled() const
{
return (mFlags & nsIPlaintextEditor::eEditorEnableWrapHackMask) != 0;
@ -763,6 +768,7 @@ protected:
nsWeakPtr mSelConWeak; // weak reference to the nsISelectionController
PRInt32 mUpdateCount;
nsIViewManager::UpdateViewBatch mBatch;
// Spellchecking
enum Tristate {

View File

@ -7634,6 +7634,7 @@ static bool gInApplyRenderingChangeToTree = false;
static void
DoApplyRenderingChangeToTree(nsIFrame* aFrame,
nsIViewManager* aViewManager,
nsFrameManager* aFrameManager,
nsChangeHint aChange);
@ -7642,7 +7643,7 @@ DoApplyRenderingChangeToTree(nsIFrame* aFrame,
* This rect is relative to aFrame's parent
*/
static void
UpdateViewsForTree(nsIFrame* aFrame,
UpdateViewsForTree(nsIFrame* aFrame, nsIViewManager* aViewManager,
nsFrameManager* aFrameManager,
nsChangeHint aChange)
{
@ -7669,19 +7670,19 @@ UpdateViewsForTree(nsIFrame* aFrame,
nsIFrame* outOfFlowFrame =
nsPlaceholderFrame::GetRealFrameForPlaceholder(child);
do {
DoApplyRenderingChangeToTree(outOfFlowFrame, aFrameManager,
aChange);
DoApplyRenderingChangeToTree(outOfFlowFrame, aViewManager,
aFrameManager, aChange);
} while ((outOfFlowFrame = outOfFlowFrame->GetNextContinuation()));
} else if (lists.CurrentID() == nsIFrame::kPopupList) {
DoApplyRenderingChangeToTree(child, aFrameManager,
aChange);
DoApplyRenderingChangeToTree(child, aViewManager,
aFrameManager, aChange);
} else { // regular frame
if ((child->GetStateBits() & NS_FRAME_HAS_CONTAINER_LAYER) &&
(aChange & nsChangeHint_RepaintFrame)) {
FrameLayerBuilder::InvalidateThebesLayerContents(child,
child->GetVisualOverflowRectRelativeToSelf());
}
UpdateViewsForTree(child, aFrameManager, aChange);
UpdateViewsForTree(child, aViewManager, aFrameManager, aChange);
}
}
}
@ -7690,6 +7691,7 @@ UpdateViewsForTree(nsIFrame* aFrame,
static void
DoApplyRenderingChangeToTree(nsIFrame* aFrame,
nsIViewManager* aViewManager,
nsFrameManager* aFrameManager,
nsChangeHint aChange)
{
@ -7701,7 +7703,7 @@ DoApplyRenderingChangeToTree(nsIFrame* aFrame,
// frame doesn't have a view, find the nearest containing view
// (adjusting r's coordinate system to reflect the nesting) and
// update there.
UpdateViewsForTree(aFrame, aFrameManager, aChange);
UpdateViewsForTree(aFrame, aViewManager, aFrameManager, aChange);
// if frame has view, will already be invalidated
if (aChange & nsChangeHint_RepaintFrame) {
@ -7764,18 +7766,25 @@ ApplyRenderingChangeToTree(nsPresContext* aPresContext,
NS_ASSERTION(aFrame, "root frame must paint");
}
nsIViewManager* viewManager = shell->GetViewManager();
// Trigger rendering updates by damaging this frame and any
// continuations of this frame.
// XXX this needs to detect the need for a view due to an opacity change and deal with it...
nsIViewManager::UpdateViewBatch batch(viewManager);
#ifdef DEBUG
gInApplyRenderingChangeToTree = true;
#endif
DoApplyRenderingChangeToTree(aFrame, shell->FrameManager(), aChange);
DoApplyRenderingChangeToTree(aFrame, viewManager, shell->FrameManager(),
aChange);
#ifdef DEBUG
gInApplyRenderingChangeToTree = false;
#endif
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
}
/**
@ -7816,8 +7825,13 @@ InvalidateCanvasIfNeeded(nsIPresShell* presShell, nsIContent* node)
// XHTML or something), but chances are we want to. Play it safe.
// Invalidate the viewport.
// Wrap this in a DEFERRED view update batch so we don't try to
// flush out layout here
nsIViewManager::UpdateViewBatch batch(presShell->GetViewManager());
nsIFrame* rootFrame = presShell->GetRootFrame();
rootFrame->InvalidateFrameSubtree();
batch.EndUpdateViewBatch(NS_VMREFRESH_DEFERRED);
}
nsresult
@ -11584,7 +11598,7 @@ nsCSSFrameConstructor::RebuildAllStyleData(nsChangeHint aExtraHint)
return;
// Make sure that the viewmanager will outlive the presshell
nsCOMPtr<nsIViewManager> vm = mPresShell->GetViewManager();
nsIViewManager::UpdateViewBatch batch(mPresShell->GetViewManager());
// Processing the style changes could cause a flush that propagates to
// the parent frame and thus destroys the pres shell.
@ -11600,6 +11614,7 @@ nsCSSFrameConstructor::RebuildAllStyleData(nsChangeHint aExtraHint)
// so we can recalculate while maintaining rule tree immutability
nsresult rv = mPresShell->StyleSet()->BeginReconstruct();
if (NS_FAILED(rv)) {
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
return;
}
@ -11634,6 +11649,7 @@ nsCSSFrameConstructor::RebuildAllStyleData(nsChangeHint aExtraHint)
// reconstructed will still have their old style context pointers
// until they are destroyed).
mPresShell->StyleSet()->EndReconstruct();
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
}
void

View File

@ -1554,8 +1554,9 @@ DocumentViewerImpl::Destroy()
// The invalidate that removing this view causes is dropped because
// the Freeze call above sets painting to be suppressed for our
// document. So we do it ourselves and make it happen.
vm->InvalidateViewNoSuppression(rootView,
rootView->GetBounds() - rootView->GetPosition());
vm->UpdateViewNoSuppression(rootView,
rootView->GetBounds() - rootView->GetPosition(),
NS_VMREFRESH_NO_SYNC);
nsIView *rootViewParent = rootView->GetParent();
if (rootViewParent) {
@ -2808,6 +2809,8 @@ DocumentViewerImpl::SetTextZoom(float aTextZoom)
mTextZoom = aTextZoom;
nsIViewManager::UpdateViewBatch batch(GetViewManager());
// Set the text zoom on all children of mContainer (even if our zoom didn't
// change, our children's zoom may be different, though it would be unusual).
// Do this first, in case kids are auto-sizing and post reflow commands on
@ -2824,6 +2827,8 @@ DocumentViewerImpl::SetTextZoom(float aTextZoom)
// And do the external resources
mDocument->EnumerateExternalResources(SetExtResourceTextZoom, &ZoomInfo);
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
return NS_OK;
}
@ -2845,6 +2850,8 @@ DocumentViewerImpl::SetMinFontSize(PRInt32 aMinFontSize)
mMinFontSize = aMinFontSize;
nsIViewManager::UpdateViewBatch batch(GetViewManager());
// Set the min font on all children of mContainer (even if our min font didn't
// change, our children's min font may be different, though it would be unusual).
// Do this first, in case kids are auto-sizing and post reflow commands on
@ -2861,6 +2868,8 @@ DocumentViewerImpl::SetMinFontSize(PRInt32 aMinFontSize)
mDocument->EnumerateExternalResources(SetExtResourceMinFontSize,
NS_INT32_TO_PTR(aMinFontSize));
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
return NS_OK;
}
@ -2883,6 +2892,7 @@ DocumentViewerImpl::SetFullZoom(float aFullZoom)
nsCOMPtr<nsIPresShell> shell = pc->GetPresShell();
NS_ENSURE_TRUE(shell, NS_OK);
nsIViewManager::UpdateViewBatch batch(shell->GetViewManager());
if (!mPrintPreviewZoomed) {
mOriginalPrintPreviewScale = pc->GetPrintPreviewScale();
mPrintPreviewZoomed = true;
@ -2901,12 +2911,15 @@ DocumentViewerImpl::SetFullZoom(float aFullZoom)
nsRect rect(nsPoint(0, 0), rootFrame->GetSize());
rootFrame->Invalidate(rect);
}
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
return NS_OK;
}
#endif
mPageZoom = aFullZoom;
nsIViewManager::UpdateViewBatch batch(GetViewManager());
struct ZoomInfo ZoomInfo = { aFullZoom };
CallChildren(SetChildFullZoom, &ZoomInfo);
@ -2918,6 +2931,8 @@ DocumentViewerImpl::SetFullZoom(float aFullZoom)
// And do the external resources
mDocument->EnumerateExternalResources(SetExtResourceFullZoom, &ZoomInfo);
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
return NS_OK;
}

View File

@ -141,8 +141,8 @@ typedef struct CapturingContentInfo {
} CapturingContentInfo;
#define NS_IPRESSHELL_IID \
{ 0x3ab5b116, 0x2d73, 0x431c, \
{ 0x9a, 0x4b, 0x6c, 0x91, 0x9e, 0x42, 0x45, 0xc3 } }
{ 0x4e23d557, 0x741a, 0x4fd0,\
{ 0x91, 0x52, 0x34, 0xe2, 0xb4, 0xef, 0xe8, 0x2e } }
// Constants for ScrollContentIntoView() function
#define NS_PRESSHELL_SCROLL_TOP 0
@ -1148,7 +1148,6 @@ public:
virtual bool ShouldIgnoreInvalidation() = 0;
virtual void WillPaint(bool aWillSendDidPaint) = 0;
virtual void DidPaint() = 0;
virtual void ScheduleViewManagerFlush() = 0;
virtual void ClearMouseCaptureOnView(nsIView* aView) = 0;
virtual bool IsVisible() = 0;
virtual void DispatchSynthMouseMove(nsGUIEvent *aEvent, bool aFlushOnHoverChange) = 0;

View File

@ -1056,12 +1056,6 @@ nsPresContext::SetShell(nsIPresShell* aShell)
mAnimationManager->Disconnect();
mAnimationManager = nsnull;
}
if (IsRoot()) {
// Have to cancel our plugin geometry timer, because the
// callback for that depends on a non-null presshell.
static_cast<nsRootPresContext*>(this)->CancelUpdatePluginGeometryTimer();
}
}
}
@ -2328,7 +2322,6 @@ nsRootPresContext::~nsRootPresContext()
NS_ASSERTION(mRegisteredPlugins.Count() == 0,
"All plugins should have been unregistered");
CancelDidPaintTimer();
CancelUpdatePluginGeometryTimer();
}
void
@ -2569,9 +2562,6 @@ nsRootPresContext::UpdatePluginGeometry()
if (!mNeedsToUpdatePluginGeometry)
return;
mNeedsToUpdatePluginGeometry = false;
// Cancel out mUpdatePluginGeometryTimer so it doesn't do a random
// update when we don't actually want one.
CancelUpdatePluginGeometryTimer();
nsIFrame* f = mUpdatePluginGeometryForFrame;
if (f) {
@ -2593,10 +2583,33 @@ nsRootPresContext::UpdatePluginGeometry()
DidApplyPluginGeometryUpdates();
}
static void
UpdatePluginGeometryCallback(nsITimer *aTimer, void *aClosure)
void
nsRootPresContext::SynchronousPluginGeometryUpdate()
{
static_cast<nsRootPresContext*>(aClosure)->UpdatePluginGeometry();
if (!mNeedsToUpdatePluginGeometry) {
// Nothing to do
return;
}
// Force synchronous paint
nsIPresShell* shell = GetPresShell();
if (!shell)
return;
nsIFrame* rootFrame = shell->GetRootFrame();
if (!rootFrame)
return;
nsCOMPtr<nsIWidget> widget = rootFrame->GetNearestWidget();
if (!widget)
return;
// Force synchronous paint of a single pixel, just to force plugin
// updates to be flushed. Doing plugin updates during paint is the best
// way to ensure that plugin updates are in sync with our content.
widget->Invalidate(nsIntRect(0,0,1,1), true);
// Update plugin geometry just in case that invalidate didn't work
// (e.g. if none of the widget is visible, it might not have processed
// a paint event). Normally this won't need to do anything.
UpdatePluginGeometry();
}
void
@ -2606,23 +2619,15 @@ nsRootPresContext::RequestUpdatePluginGeometry(nsIFrame* aFrame)
return;
if (!mNeedsToUpdatePluginGeometry) {
// We'll update the plugin geometry during the next paint in this
// presContext (either from nsPresShell::WillPaint or from
// nsPresShell::DidPaint, depending on the platform) or on the next
// layout flush, whichever comes first. But we may not have anyone
// flush layout, and paints might get optimized away if the old
// plugin geometry covers the whole canvas, so set a backup timer to
// do this too. We want to make sure this won't fire before our
// normal paint notifications, if those would update the geometry,
// so set it for double the refresh driver interval.
mUpdatePluginGeometryTimer = do_CreateInstance("@mozilla.org/timer;1");
if (mUpdatePluginGeometryTimer) {
mUpdatePluginGeometryTimer->
InitWithFuncCallback(UpdatePluginGeometryCallback, this,
nsRefreshDriver::DefaultInterval() * 2,
nsITimer::TYPE_ONE_SHOT);
}
mNeedsToUpdatePluginGeometry = true;
// Dispatch a Gecko event to ensure plugin geometry gets updated
// XXX this really should be done through the refresh driver, once
// all painting happens in the refresh driver
nsCOMPtr<nsIRunnable> event =
NS_NewRunnableMethod(this, &nsRootPresContext::SynchronousPluginGeometryUpdate);
NS_DispatchToMainThread(event);
mUpdatePluginGeometryForFrame = aFrame;
mUpdatePluginGeometryForFrame->PresContext()->
SetContainsUpdatePluginGeometryFrame(true);

View File

@ -1265,6 +1265,14 @@ public:
virtual bool IsRoot() { return true; }
/**
* This method is called off an event to force the plugin geometry to
* be updated. First we try to paint, since updating plugin geometry
* during paint is best for keeping plugins in sync with content.
* But we also force geometry updates in case painting doesn't work.
*/
void SynchronousPluginGeometryUpdate();
/**
* Call this after reflow and scrolling to ensure that the geometry
* of any windowed plugins is updated. aFrame is the root of the
@ -1337,17 +1345,7 @@ protected:
nsRootPresContext* mPresContext;
};
friend class nsPresContext;
void CancelUpdatePluginGeometryTimer()
{
if (mUpdatePluginGeometryTimer) {
mUpdatePluginGeometryTimer->Cancel();
mUpdatePluginGeometryTimer = nsnull;
}
}
nsCOMPtr<nsITimer> mNotifyDidPaintTimer;
nsCOMPtr<nsITimer> mUpdatePluginGeometryTimer;
nsTHashtable<nsPtrHashKey<nsObjectFrame> > mRegisteredPlugins;
// if mNeedsToUpdatePluginGeometry is set, then this is the frame to
// use as the root of the subtree to search for plugin updates, or

View File

@ -1268,8 +1268,6 @@ PresShell::Destroy()
// before we destroy the frame manager, since apparently frame destruction
// sometimes spins the event queue when plug-ins are involved(!).
rd->RemoveLayoutFlushObserver(this);
rd->RevokeViewManagerFlush();
mResizeEvent.Revoke();
if (mAsyncResizeTimerIsActive) {
mAsyncResizeEventTimer->Cancel();
@ -2130,6 +2128,8 @@ PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight)
if (!GetPresContext()->SupressingResizeReflow())
{
nsIViewManager::UpdateViewBatch batch(mViewManager);
// 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);
@ -2152,7 +2152,6 @@ PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight)
// Kick off a top-down reflow
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
nsIViewManager::AutoDisableRefresh refreshBlocker(mViewManager);
mDirtyRoots.RemoveElement(rootFrame);
DoReflow(rootFrame, true);
@ -2160,6 +2159,8 @@ PresShell::ResizeReflowIgnoreOverride(nscoord aWidth, nscoord aHeight)
DidDoReflow(true);
}
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
}
rootFrame = FrameManager()->GetRootFrame();
@ -2477,6 +2478,17 @@ PresShell::ScrollLine(bool aForward)
scrollFrame->ScrollBy(nsIntPoint(0, aForward ? lineCount : -lineCount),
nsIScrollableFrame::LINES,
nsIScrollableFrame::SMOOTH);
//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;
}
@ -2490,6 +2502,16 @@ PresShell::ScrollHorizontal(bool aLeft)
scrollFrame->ScrollBy(nsIntPoint(aLeft ? -1 : 1, 0),
nsIScrollableFrame::LINES,
nsIScrollableFrame::SMOOTH);
//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;
}
@ -2967,6 +2989,7 @@ PresShell::RecreateFramesFor(nsIContent* aContent)
// to keep the number of entrypoints down.
NS_ASSERTION(mViewManager, "Should have view manager");
nsIViewManager::UpdateViewBatch batch(mViewManager);
// Have to make sure that the content notifications are flushed before we
// start messing with the frame model; otherwise we can get content doubling.
@ -2982,6 +3005,7 @@ PresShell::RecreateFramesFor(nsIContent* aContent)
nsresult rv = mFrameConstructor->ProcessRestyledFrames(changeList);
--mChangeNestCount;
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
return rv;
}
@ -3622,18 +3646,6 @@ nsresult PresShell::GetLinkLocation(nsIDOMNode* aNode, nsAString& aLocationStrin
return NS_ERROR_FAILURE;
}
void
PresShell::ScheduleViewManagerFlush()
{
nsPresContext* presContext = GetPresContext();
if (presContext) {
presContext->RefreshDriver()->ScheduleViewManagerFlush();
}
if (mDocument) {
mDocument->SetNeedLayoutFlush();
}
}
void
PresShell::DispatchSynthMouseMove(nsGUIEvent *aEvent,
bool aFlushOnHoverChange)
@ -4004,7 +4016,7 @@ PresShell::FlushPendingNotifications(mozFlushType aType)
}
NS_ASSERTION(!isSafeToFlush || mViewManager, "Must have view manager");
// Make sure the view manager stays alive.
// Make sure the view manager stays alive while batching view updates.
nsCOMPtr<nsIViewManager> viewManagerDeathGrip = mViewManager;
if (isSafeToFlush && mViewManager) {
// Processing pending notifications can kill us, and some callers only
@ -4018,6 +4030,11 @@ PresShell::FlushPendingNotifications(mozFlushType aType)
}
}
// 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
nsIViewManager::UpdateViewBatch batch(mViewManager);
// We need to make sure external resource documents are flushed too (for
// example, svg filters that reference a filter in an external document
// need the frames in the external document to be constructed for the
@ -4110,11 +4127,15 @@ PresShell::FlushPendingNotifications(mozFlushType aType)
if (rootPresContext) {
rootPresContext->UpdatePluginGeometry();
}
}
if (!mIsDestroying) {
mViewManager->UpdateWidgetGeometry();
}
PRUint32 updateFlags = NS_VMREFRESH_NO_SYNC;
if (aType >= Flush_Display) {
// Flushing paints, so perform the invalidates and drawing
// immediately
updateFlags = NS_VMREFRESH_IMMEDIATE;
}
batch.EndUpdateViewBatch(updateFlags);
}
}
@ -7371,7 +7392,7 @@ PresShell::DoVerifyReflow()
// First synchronously render what we have so far so that we can
// see it.
nsIView* rootView = mViewManager->GetRootView();
mViewManager->InvalidateView(rootView);
mViewManager->UpdateView(rootView, NS_VMREFRESH_IMMEDIATE);
FlushPendingNotifications(Flush_Layout);
mInVerifyReflow = true;
@ -7418,7 +7439,6 @@ PresShell::ProcessReflowCommands(bool aInterruptible)
nsAutoScriptBlocker scriptBlocker;
WillDoReflow();
AUTO_LAYOUT_PHASE_ENTRY_POINT(GetPresContext(), Reflow);
nsIViewManager::AutoDisableRefresh refreshBlocker(mViewManager);
do {
// Send an incremental reflow notification to the target frame.
@ -7566,6 +7586,7 @@ PresShell::Observe(nsISupports* aSubject,
// at interesting times during startup.
if (rootFrame) {
NS_ASSERTION(mViewManager, "View manager must exist");
nsIViewManager::UpdateViewBatch batch(mViewManager);
nsWeakFrame weakRoot(rootFrame);
// Have to make sure that the content notifications are flushed before we
@ -7590,6 +7611,7 @@ PresShell::Observe(nsISupports* aSubject,
--mChangeNestCount;
}
}
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
}
return NS_OK;
}

View File

@ -330,7 +330,6 @@ public:
virtual bool ShouldIgnoreInvalidation();
virtual void WillPaint(bool aWillSendDidPaint);
virtual void DidPaint();
virtual void ScheduleViewManagerFlush();
virtual void DispatchSynthMouseMove(nsGUIEvent *aEvent, bool aFlushOnHoverChange);
virtual void ClearMouseCaptureOnView(nsIView* aView);
virtual bool IsVisible();

View File

@ -55,7 +55,6 @@
#include "jsapi.h"
#include "nsContentUtils.h"
#include "mozilla/Preferences.h"
#include "nsIViewManager.h"
using mozilla::TimeStamp;
using mozilla::TimeDuration;
@ -74,13 +73,6 @@ nsRefreshDriver::InitializeStatics()
"layout.frame_rate.precise",
false);
}
/* static */ PRInt32
nsRefreshDriver::DefaultInterval()
{
return NSToIntRound(1000.0 / DEFAULT_FRAME_RATE);
}
// Compute the interval to use for the refresh driver timer, in
// milliseconds
PRInt32
@ -120,7 +112,6 @@ nsRefreshDriver::nsRefreshDriver(nsPresContext *aPresContext)
mThrottled(false),
mTestControllingRefreshes(false),
mTimerIsPrecise(false),
mViewManagerFlushIsPending(false),
mLastTimerInterval(0)
{
mRequests.Init();
@ -279,7 +270,6 @@ nsRefreshDriver::ObserverCount() const
sum += mStyleFlushObservers.Length();
sum += mLayoutFlushObservers.Length();
sum += mFrameRequestCallbackDocs.Length();
sum += mViewManagerFlushIsPending;
return sum;
}
@ -441,11 +431,6 @@ nsRefreshDriver::Notify(nsITimer *aTimer)
EnsureTimerStarted(false);
}
if (mViewManagerFlushIsPending) {
mViewManagerFlushIsPending = false;
mPresContext->GetPresShell()->GetViewManager()->ProcessPendingUpdates();
}
if (mThrottled ||
(mTimerIsPrecise !=
(GetRefreshTimerType() == nsITimer::TYPE_REPEATING_PRECISE_CAN_SKIP))) {

View File

@ -176,17 +176,6 @@ public:
return mLayoutFlushObservers.Contains(aShell);
}
/**
* Remember whether our presshell's view manager needs a flush
*/
void ScheduleViewManagerFlush() {
mViewManagerFlushIsPending = true;
EnsureTimerStarted(false);
}
void RevokeViewManagerFlush() {
mViewManagerFlushIsPending = false;
}
/**
* Add a document for which we have nsIFrameRequestCallbacks
*/
@ -238,11 +227,6 @@ public:
mozFlushType aFlushType);
#endif
/**
* Default interval the refresh driver uses, in ms.
*/
static PRInt32 DefaultInterval();
private:
typedef nsTObserverArray<nsARefreshObserver*> ObserverArray;
typedef nsTHashtable<nsISupportsHashKey> RequestTable;
@ -281,7 +265,6 @@ private:
a precise timer. If mTimer is null, this boolean's value can be
anything. */
bool mTimerIsPrecise;
bool mViewManagerFlushIsPending;
// separate arrays for each flush type we support
ObserverArray mObservers[3];

View File

@ -387,6 +387,15 @@ nsComboboxControlFrame::SetFocus(bool aOn, bool aRepaint)
// rect to be drawn. This is much faster than ReResolvingStyle
// Bug 32920
Invalidate(nsRect(0,0,mRect.width,mRect.height));
// Make sure the content area gets updated for where the dropdown was
// This is only needed for embedding, the focus may go to
// the chrome that is not part of the Gecko system (Bug 83493)
// XXX this is rather inefficient
nsIViewManager* vm = PresContext()->GetPresShell()->GetViewManager();
if (vm) {
vm->UpdateAllViews(NS_VMREFRESH_NO_SYNC);
}
}
void

View File

@ -4642,16 +4642,23 @@ nsIFrame::InvalidateRoot(const nsRect& aDamageRect, PRUint32 aFlags)
}
}
PRUint32 flags =
(aFlags & INVALIDATE_IMMEDIATE) ? NS_VMREFRESH_IMMEDIATE : NS_VMREFRESH_NO_SYNC;
nsRect rect = aDamageRect;
nsRegion* excludeRegion = static_cast<nsRegion*>
(Properties().Get(DeferInvalidatesProperty()));
if (excludeRegion && (aFlags & INVALIDATE_EXCLUDE_CURRENT_PAINT)) {
if (excludeRegion) {
flags = NS_VMREFRESH_DEFERRED;
if (aFlags & INVALIDATE_EXCLUDE_CURRENT_PAINT) {
nsRegion r;
r.Sub(rect, *excludeRegion);
if (r.IsEmpty())
return;
rect = r.GetBounds();
}
}
if (!(aFlags & INVALIDATE_NO_UPDATE_LAYER_TREE)) {
AddStateBits(NS_FRAME_UPDATE_LAYER_TREE);
@ -4659,7 +4666,7 @@ nsIFrame::InvalidateRoot(const nsRect& aDamageRect, PRUint32 aFlags)
nsIView* view = GetView();
NS_ASSERTION(view, "This can only be called on frames with views");
view->GetViewManager()->InvalidateViewNoSuppression(view, rect);
view->GetViewManager()->UpdateViewNoSuppression(view, rect, flags);
}
void

View File

@ -1533,6 +1533,19 @@ nsHTMLFramesetFrame::MouseDrag(nsPresContext* aPresContext,
ENSURE_TRUE(weakFrame.IsAlive());
if (change != 0) {
mDrag.Reset(mDragger->mVertical, mDragger->mPrevNeighbor, change, this);
nsIFrame* parentFrame = GetParent();
if (!parentFrame) {
return;
}
// Update the view immediately (make drag appear snappier)
nsIViewManager* vm = aPresContext->GetPresShell()->GetViewManager();
if (vm) {
nsIView* root = vm->GetRootView();
if (root) {
vm->UpdateView(root, NS_VMREFRESH_IMMEDIATE);
}
}
}
}

View File

@ -562,7 +562,7 @@ void nsLayoutDebuggingTools::ForceRefresh()
return;
nsIView* root = vm->GetRootView();
if (root) {
vm->InvalidateView(root);
vm->UpdateView(root, NS_VMREFRESH_IMMEDIATE);
}
}

View File

@ -559,6 +559,15 @@ nsListBoxBodyFrame::ScrollByLines(PRInt32 aNumLines)
ScrollToIndex(scrollIndex);
// we have to do a sync update for mac because if we scroll too quickly
// w/out going back to the main event loop we can easily scroll the wrong
// bits and it looks like garbage (bug 63465).
// XXXbz is this seriously still needed?
// I'd use Composite here, but it doesn't always work.
// vm->Composite();
PresContext()->GetPresShell()->GetViewManager()->ForceUpdate();
return NS_OK;
}

View File

@ -145,24 +145,6 @@ function _parseModifiers(aEvent)
* aWindow is optional, and defaults to the current window object.
*/
function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
{
var rect = aTarget.getBoundingClientRect();
synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
aEvent, aWindow);
}
/*
* Synthesize a mouse event at a particular point in aWindow.
*
* aEvent is an object which may contain the properties:
* shiftKey, ctrlKey, altKey, metaKey, accessKey, clickCount, button, type
*
* If the type is specified, an mouse event of that type is fired. Otherwise,
* a mousedown followed by a mouse up is performed.
*
* aWindow is optional, and defaults to the current window object.
*/
function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
{
var utils = _getDOMWindowUtils(aWindow);
@ -171,6 +153,11 @@ function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
var clickCount = aEvent.clickCount || 1;
var modifiers = _parseModifiers(aEvent);
var rect = aTarget.getBoundingClientRect();
var left = rect.left + aOffsetX;
var top = rect.top + aOffsetY;
if (aEvent.type) {
utils.sendMouseEvent(aEvent.type, left, top, button, clickCount, modifiers);
}

View File

@ -48,8 +48,8 @@ class nsRegion;
class nsDeviceContext;
#define NS_IVIEWMANAGER_IID \
{ 0x540610a6, 0x4fdd, 0x4ae3, \
{ 0x9b, 0xdb, 0xa6, 0x4d, 0x8b, 0xca, 0x02, 0x0f } }
{ 0x1262a33f, 0xc19f, 0x4e5b, \
{ 0x85, 0x00, 0xab, 0xf3, 0x7d, 0xcf, 0x30, 0x1d } }
class nsIViewManager : public nsISupports
{
@ -116,12 +116,23 @@ public:
*/
NS_IMETHOD FlushDelayedResize(bool aDoReflow) = 0;
/**
* Called to force a redrawing of any dirty areas.
*/
// XXXbz why is this exposed? Shouldn't update view batches handle this?
// It's not like Composite() does what's expected inside a view update batch
// anyway, since dirty areas may not have been invalidated on the widget yet
// and widget changes may not have been propagated yet. Maybe this should
// call FlushPendingInvalidates()?
NS_IMETHOD Composite(void) = 0;
/**
* Called to inform the view manager that the entire area of a view
* is dirty and needs to be redrawn.
* @param aView view to paint. should be root view
* @param aUpdateFlags see bottom of nsIViewManager.h for description
*/
NS_IMETHOD InvalidateView(nsIView *aView) = 0;
NS_IMETHOD UpdateView(nsIView *aView, PRUint32 aUpdateFlags) = 0;
/**
* Called to inform the view manager that some portion of a view is dirty and
@ -129,13 +140,17 @@ public:
* space. Does not check for paint suppression.
* @param aView view to paint. should be root view
* @param rect rect to mark as damaged
* @param aUpdateFlags see bottom of nsIViewManager.h for description
*/
NS_IMETHOD InvalidateViewNoSuppression(nsIView *aView, const nsRect &aRect) = 0;
NS_IMETHOD UpdateViewNoSuppression(nsIView *aView, const nsRect &aRect,
PRUint32 aUpdateFlags) = 0;
/**
* Called to inform the view manager that it should invalidate all views.
* Called to inform the view manager that it should redraw all views.
* @param aView view to paint. should be root view
* @param aUpdateFlags see bottom of nsIViewManager.h for description
*/
NS_IMETHOD InvalidateAllViews() = 0;
NS_IMETHOD UpdateAllViews(PRUint32 aUpdateFlags) = 0;
/**
* Called to dispatch an event to the appropriate view. Often called
@ -258,42 +273,76 @@ public:
*/
NS_IMETHOD GetDeviceContext(nsDeviceContext *&aContext) = 0;
/**
* A stack class for disallowing changes that would enter painting. For
* example, popup widgets shouldn't be resized during reflow, since doing so
* might cause synchronous painting inside reflow which is forbidden.
* While refresh is disabled, widget geometry changes are deferred and will
* be handled later, either from the refresh driver or from an NS_WILL_PAINT
* event.
* We don't want to defer widget geometry changes all the time. Resizing a
* popup from script doesn't need to be deferred, for example, especially
* since popup widget geometry is observable from script and expected to
* update synchronously.
*/
class NS_STACK_CLASS AutoDisableRefresh {
class UpdateViewBatch {
public:
AutoDisableRefresh(nsIViewManager* aVM) {
UpdateViewBatch() {}
/**
* prevents the view manager from refreshing. allows UpdateView()
* to notify widgets of damaged regions that should be repainted
* when the batch is ended. Call EndUpdateViewBatch on this object
* before it is destroyed
* @return error status
*/
UpdateViewBatch(nsIViewManager* aVM) {
if (aVM) {
mRootVM = aVM->IncrementDisableRefreshCount();
mRootVM = aVM->BeginUpdateViewBatch();
}
}
~AutoDisableRefresh() {
if (mRootVM) {
mRootVM->DecrementDisableRefreshCount();
~UpdateViewBatch() {
NS_ASSERTION(!mRootVM, "Someone forgot to call EndUpdateViewBatch!");
}
/**
* See the constructor, this lets you "fill in" a blank UpdateViewBatch.
*/
void BeginUpdateViewBatch(nsIViewManager* aVM) {
NS_ASSERTION(!mRootVM, "already started a batch!");
if (aVM) {
mRootVM = aVM->BeginUpdateViewBatch();
}
}
/**
* allow the view manager to refresh any damaged areas accumulated
* after the BeginUpdateViewBatch() call. this may cause a
* synchronous paint to occur inside the call if aUpdateFlags
* NS_VMREFRESH_IMMEDIATE is set.
*
* If this is not the outermost view batch command, then this does
* nothing except that the specified flags are remembered. When the
* outermost batch finally ends, we merge together all the flags for the
* inner batches in the following way:
* -- If any batch specified NS_VMREFRESH_IMMEDIATE, then we use that flag
* (i.e. there is a synchronous paint under the last EndUpdateViewBatch)
* -- Otherwise if any batch specified NS_VMREFERSH_DEFERRED, then we use
* that flag (i.e. invalidation is deferred until the processing of an
* Invalidate PLEvent)
* -- Otherwise all batches specified NS_VMREFRESH_NO_SYNC and we honor
* that; all widgets are invalidated normally and will be painted the next
* time the toolkit chooses to update them.
*
* @param aUpdateFlags see bottom of nsIViewManager.h for
* description @return error status
*/
void EndUpdateViewBatch(PRUint32 aUpdateFlags) {
if (!mRootVM)
return;
mRootVM->EndUpdateViewBatch(aUpdateFlags);
mRootVM = nsnull;
}
private:
AutoDisableRefresh(const AutoDisableRefresh& aOther);
const AutoDisableRefresh& operator=(const AutoDisableRefresh& aOther);
UpdateViewBatch(const UpdateViewBatch& aOther);
const UpdateViewBatch& operator=(const UpdateViewBatch& aOther);
nsCOMPtr<nsIViewManager> mRootVM;
};
private:
friend class AutoDisableRefresh;
friend class UpdateViewBatch;
virtual nsIViewManager* IncrementDisableRefreshCount() = 0;
virtual void DecrementDisableRefreshCount() = 0;
virtual nsIViewManager* BeginUpdateViewBatch(void) = 0;
NS_IMETHOD EndUpdateViewBatch(PRUint32 aUpdateFlags) = 0;
public:
/**
@ -302,6 +351,16 @@ public:
*/
NS_IMETHOD GetRootWidget(nsIWidget **aWidget) = 0;
/**
* Force update of view manager widget
* Callers should use UpdateView(view, NS_VMREFRESH_IMMEDIATE) in most cases instead
* @result error status
*/
// XXXbz Callers seem to be confused about this one... and it doesn't play
// right with view update batching at all (will miss updates). Maybe this
// should call FlushPendingInvalidates()?
NS_IMETHOD ForceUpdate() = 0;
/**
* Indicate whether the viewmanager is currently painting
*
@ -324,19 +383,25 @@ public:
* the nearest enclosing popup or the root view for the root document.
*/
static nsIView* GetDisplayRootFor(nsIView* aView);
/**
* Flush the accumulated dirty region to the widget and update widget
* geometry.
*/
virtual void ProcessPendingUpdates()=0;
/**
* Just update widget geometry without flushing the dirty region
*/
virtual void UpdateWidgetGeometry() = 0;
};
NS_DEFINE_STATIC_IID_ACCESSOR(nsIViewManager, NS_IVIEWMANAGER_IID)
// Paint timing mode flags
// intermediate: do no special timing processing; repaint when the
// toolkit issues an expose event (which will happen *before* PLEvent
// processing). This is essentially the default.
#define NS_VMREFRESH_NO_SYNC 0
// least immediate: we suppress invalidation, storing dirty areas in
// views, and post an Invalidate PLEvent. The Invalidate event gets
// processed after toolkit events such as window resize events!
// This is only usable with EndUpdateViewBatch and EnableRefresh.
#define NS_VMREFRESH_DEFERRED 0x0001
// most immediate: force a call to nsViewManager::Composite, which
// synchronously updates the window(s) right away before returning
#define NS_VMREFRESH_IMMEDIATE 0x0002
#endif // nsIViewManager_h___

View File

@ -351,7 +351,7 @@ void nsView::SetPosition(nscoord aX, nscoord aY)
NS_ASSERTION(GetParent() || (aX == 0 && aY == 0),
"Don't try to move the root widget to something non-zero");
ResetWidgetBounds(true, false);
ResetWidgetBounds(true, true, false);
}
void nsIView::SetInvalidationDimensions(const nsRect* aRect)
@ -359,23 +359,22 @@ void nsIView::SetInvalidationDimensions(const nsRect* aRect)
return Impl()->SetInvalidationDimensions(aRect);
}
void nsView::ResetWidgetBounds(bool aRecurse, bool aForceSync)
{
void nsView::ResetWidgetBounds(bool aRecurse, bool aMoveOnly,
bool aInvalidateChangedSize) {
if (mWindow) {
if (!aForceSync) {
// Don't change widget geometry synchronously, since that can
// cause synchronous painting.
// If our view manager has refresh disabled, then do nothing; the view
// manager will set our position when refresh is reenabled. Just let it
// know that it has pending updates.
if (!mViewManager->IsRefreshEnabled()) {
mViewManager->PostPendingUpdate();
} else {
DoResetWidgetBounds(false, true);
}
return;
}
if (aRecurse) {
DoResetWidgetBounds(aMoveOnly, aInvalidateChangedSize);
} else if (aRecurse) {
// reposition any widgets under this view
for (nsView* v = GetFirstChild(); v; v = v->GetNextSibling()) {
v->ResetWidgetBounds(true, aForceSync);
v->ResetWidgetBounds(true, aMoveOnly, aInvalidateChangedSize);
}
}
}
@ -493,7 +492,7 @@ void nsView::SetDimensions(const nsRect& aRect, bool aPaint, bool aResizeWidget)
mDimBounds = dims;
if (aResizeWidget) {
ResetWidgetBounds(false, false);
ResetWidgetBounds(false, false, aPaint);
}
}

View File

@ -181,7 +181,7 @@ public:
void SetTopMost(bool aTopMost) { aTopMost ? mVFlags |= NS_VIEW_FLAG_TOPMOST : mVFlags &= ~NS_VIEW_FLAG_TOPMOST; }
bool IsTopMost() { return((mVFlags & NS_VIEW_FLAG_TOPMOST) != 0); }
void ResetWidgetBounds(bool aRecurse, bool aForceSync);
void ResetWidgetBounds(bool aRecurse, bool aMoveOnly, bool aInvalidateChangedSize);
void AssertNoWindow();
void NotifyEffectiveVisibilityChanged(bool aEffectivelyVisible);

View File

@ -80,6 +80,21 @@
#define NSCOORD_NONE PR_INT32_MIN
void
nsViewManager::PostInvalidateEvent()
{
NS_ASSERTION(IsRootVM(), "Caller screwed up");
if (!mInvalidateEvent.IsPending()) {
nsRefPtr<nsInvalidateEvent> ev = new nsInvalidateEvent(this);
if (NS_FAILED(NS_DispatchToCurrentThread(ev))) {
NS_WARNING("failed to dispatch nsInvalidateEvent");
} else {
mInvalidateEvent = ev;
}
}
}
#undef DEBUG_MOUSE_LOCATION
PRInt32 nsViewManager::mVMCount = 0;
@ -105,8 +120,8 @@ nsViewManager::nsViewManager()
// NOTE: we use a zeroing operator new, so all data members are
// assumed to be cleared here.
mHasPendingUpdates = false;
mHasPendingWidgetGeometryChanges = false;
mRecursiveRefreshPending = false;
mUpdateBatchFlags = 0;
}
nsViewManager::~nsViewManager()
@ -117,6 +132,10 @@ nsViewManager::~nsViewManager()
mRootView = nsnull;
}
// Make sure to revoke pending events for all viewmanagers, since some events
// are posted by a non-root viewmanager.
mInvalidateEvent.Revoke();
if (!IsRootVM()) {
// We have a strong ref to mRootViewManager
NS_RELEASE(mRootViewManager);
@ -342,6 +361,9 @@ void nsViewManager::Refresh(nsView *aView, nsIWidget *aWidget,
NS_ASSERTION(aView == nsView::GetViewFor(aWidget), "view widget mismatch");
NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
if (! IsRefreshEnabled())
return;
// damageRegion is the damaged area, in twips, relative to the view origin
nsRegion damageRegion = aRegion.ToAppUnits(AppUnitsPerDevPixel());
// move region from widget coordinates into view coordinates
@ -374,8 +396,10 @@ void nsViewManager::Refresh(nsView *aView, nsIWidget *aWidget,
}
if (RootViewManager()->mRecursiveRefreshPending) {
// Unset this flag first, since if aUpdateFlags includes NS_VMREFRESH_IMMEDIATE
// we'll reenter this code from the UpdateAllViews call.
RootViewManager()->mRecursiveRefreshPending = false;
InvalidateAllViews();
UpdateAllViews(0);
}
}
@ -396,8 +420,7 @@ void nsViewManager::RenderViews(nsView *aView, nsIWidget *aWidget,
}
}
void nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
bool aFlushDirtyRegion)
void nsViewManager::ProcessPendingUpdates(nsView* aView, bool aDoInvalidate)
{
NS_ASSERTION(IsRootVM(), "Updates will be missed");
@ -407,28 +430,21 @@ void nsViewManager::ProcessPendingUpdatesForView(nsView* aView,
}
if (aView->HasWidget()) {
aView->ResetWidgetBounds(false, true);
aView->ResetWidgetBounds(false, false, true);
}
// process pending updates in child view.
for (nsView* childView = aView->GetFirstChild(); childView;
childView = childView->GetNextSibling()) {
ProcessPendingUpdatesForView(childView, aFlushDirtyRegion);
ProcessPendingUpdates(childView, aDoInvalidate);
}
if (aDoInvalidate && aView->HasNonEmptyDirtyRegion()) {
// Push out updates after we've processed the children; ensures that
// damage is applied based on the final widget geometry
if (aFlushDirtyRegion) {
FlushDirtyRegionToWidget(aView);
}
}
void nsViewManager::FlushDirtyRegionToWidget(nsView* aView)
{
if (!aView->HasNonEmptyDirtyRegion())
return;
NS_ASSERTION(IsRefreshEnabled(), "Cannot process pending updates with refresh disabled");
nsRegion* dirtyRegion = aView->GetDirtyRegion();
if (dirtyRegion) {
nsView* nearestViewWithWidget = aView;
while (!nearestViewWithWidget->HasWidget() &&
nearestViewWithWidget->GetParent()) {
@ -437,57 +453,73 @@ void nsViewManager::FlushDirtyRegionToWidget(nsView* aView)
nsRegion r =
ConvertRegionBetweenViews(*dirtyRegion, aView, nearestViewWithWidget);
nsViewManager* widgetVM = nearestViewWithWidget->GetViewManager();
widgetVM->InvalidateWidgetArea(nearestViewWithWidget, r);
widgetVM->
UpdateWidgetArea(nearestViewWithWidget,
nearestViewWithWidget->GetWidget(), r, nsnull);
dirtyRegion->SetEmpty();
}
NS_IMETHODIMP nsViewManager::InvalidateView(nsIView *aView)
{
// Mark the entire view as damaged
return InvalidateView(aView, aView->GetDimensions());
}
static void
AddDirtyRegion(nsView *aView, const nsRegion &aDamagedRegion)
{
nsRegion* dirtyRegion = aView->GetDirtyRegion();
if (!dirtyRegion)
return;
dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
dirtyRegion->SimplifyOutward(8);
}
void
nsViewManager::PostPendingUpdate()
{
nsViewManager* rootVM = RootViewManager();
rootVM->mHasPendingUpdates = true;
rootVM->mHasPendingWidgetGeometryChanges = true;
if (rootVM->mPresShell) {
rootVM->mPresShell->ScheduleViewManagerFlush();
}
}
}
NS_IMETHODIMP nsViewManager::Composite()
{
if (!IsRootVM()) {
return RootViewManager()->Composite();
}
ForceUpdate();
ClearUpdateCount();
return NS_OK;
}
NS_IMETHODIMP nsViewManager::UpdateView(nsIView *aView, PRUint32 aUpdateFlags)
{
// Mark the entire view as damaged
return UpdateView(aView, aView->GetDimensions(), aUpdateFlags);
}
/**
* @param aWidget the widget for aWidgetView; in some cases the widget
* is being managed directly by the frame system, so aWidgetView->GetWidget()
* will return null but nsView::GetViewFor(aWidget) returns aWidgetview
* @param aDamagedRegion this region, relative to aWidgetView, is invalidated in
* every widget child of aWidgetView, plus aWidgetView's own widget
* @param aIgnoreWidgetView if non-null, the aIgnoreWidgetView's widget and its
* children are not updated.
*/
void
nsViewManager::InvalidateWidgetArea(nsView *aWidgetView,
const nsRegion &aDamagedRegion)
nsViewManager::UpdateWidgetArea(nsView *aWidgetView, nsIWidget* aWidget,
const nsRegion &aDamagedRegion,
nsView* aIgnoreWidgetView)
{
NS_ASSERTION(aWidgetView->GetViewManager() == this,
"InvalidateWidgetArea called on view we don't own");
nsIWidget* widget = aWidgetView->GetWidget();
"UpdateWidgetArea called on view we don't own");
#if 0
nsRect dbgBounds = aDamagedRegion.GetBounds();
printf("InvalidateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
printf("UpdateWidgetArea view:%X (%d) widget:%X region: %d, %d, %d, %d\n",
aWidgetView, aWidgetView->IsAttachedToTopLevel(),
widget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
aWidget, dbgBounds.x, dbgBounds.y, dbgBounds.width, dbgBounds.height);
#endif
if (!IsRefreshEnabled()) {
// accumulate this rectangle in the view's dirty region, so we can
// process it later.
nsRegion* dirtyRegion = aWidgetView->GetDirtyRegion();
if (!dirtyRegion) return;
dirtyRegion->Or(*dirtyRegion, aDamagedRegion);
// Don't let dirtyRegion grow beyond 8 rects
dirtyRegion->SimplifyOutward(8);
nsViewManager* rootVM = RootViewManager();
rootVM->mHasPendingUpdates = true;
rootVM->IncrementUpdateCount();
return;
// this should only happen at the top level, and this result
// should not be consumed by top-level callers, so it doesn't
// really matter what we return
}
// If the bounds don't overlap at all, there's nothing to do
nsRegion intersection;
intersection.And(aWidgetView->GetInvalidationDimensions(), aDamagedRegion);
@ -496,14 +528,19 @@ nsViewManager::InvalidateWidgetArea(nsView *aWidgetView,
}
// If the widget is hidden, it don't cover nothing
if (widget) {
if (aWidget) {
bool visible;
widget->IsVisible(visible);
aWidget->IsVisible(visible);
if (!visible)
return;
}
if (!widget) {
if (aWidgetView == aIgnoreWidgetView) {
// the widget for aIgnoreWidgetView (and its children) should be treated as already updated.
return;
}
if (!aWidget) {
// The root view or a scrolling view might not have a widget
// (for example, during printing). We get here when we scroll
// during printing to show selected options in a listbox, for example.
@ -514,8 +551,8 @@ nsViewManager::InvalidateWidgetArea(nsView *aWidgetView,
// accumulate the union of all the child widget areas, or at least
// some subset of that.
nsRegion children;
if (widget->GetTransparencyMode() != eTransparencyTransparent) {
for (nsIWidget* childWidget = widget->GetFirstChild();
if (aWidget->GetTransparencyMode() != eTransparencyTransparent) {
for (nsIWidget* childWidget = aWidget->GetFirstChild();
childWidget;
childWidget = childWidget->GetNextSibling()) {
nsView* view = nsView::GetViewFor(childWidget);
@ -554,10 +591,12 @@ nsViewManager::InvalidateWidgetArea(nsView *aWidgetView,
leftOver.Sub(intersection, children);
if (!leftOver.IsEmpty()) {
NS_ASSERTION(IsRefreshEnabled(), "Can only get here with refresh enabled, I hope");
const nsRect* r;
for (nsRegionRectIterator iter(leftOver); (r = iter.Next());) {
nsIntRect bounds = ViewToWidget(aWidgetView, *r);
widget->Invalidate(bounds);
aWidget->Invalidate(bounds, false);
}
}
}
@ -576,7 +615,8 @@ ShouldIgnoreInvalidation(nsViewManager* aVM)
return false;
}
nsresult nsViewManager::InvalidateView(nsIView *aView, const nsRect &aRect)
nsresult nsViewManager::UpdateView(nsIView *aView, const nsRect &aRect,
PRUint32 aUpdateFlags)
{
// If painting is suppressed in the presshell or an ancestor drop all
// invalidates, it will invalidate everything when it unsuppresses.
@ -584,18 +624,19 @@ nsresult nsViewManager::InvalidateView(nsIView *aView, const nsRect &aRect)
return NS_OK;
}
return InvalidateViewNoSuppression(aView, aRect);
return UpdateViewNoSuppression(aView, aRect, aUpdateFlags);
}
NS_IMETHODIMP nsViewManager::InvalidateViewNoSuppression(nsIView *aView,
const nsRect &aRect)
NS_IMETHODIMP nsViewManager::UpdateViewNoSuppression(nsIView *aView,
const nsRect &aRect,
PRUint32 aUpdateFlags)
{
NS_PRECONDITION(nsnull != aView, "null view");
nsView* view = static_cast<nsView*>(aView);
NS_ASSERTION(view->GetViewManager() == this,
"InvalidateViewNoSuppression called on view we don't own");
"UpdateView called on view we don't own");
nsRect damagedRect(aRect);
if (damagedRect.IsEmpty()) {
@ -611,36 +652,42 @@ NS_IMETHODIMP nsViewManager::InvalidateViewNoSuppression(nsIView *aView,
PRInt32 rootAPD = displayRootVM->AppUnitsPerDevPixel();
PRInt32 APD = AppUnitsPerDevPixel();
damagedRect = damagedRect.ConvertAppUnitsRoundOut(APD, rootAPD);
displayRootVM->UpdateWidgetArea(displayRoot, displayRoot->GetWidget(),
nsRegion(damagedRect), nsnull);
// accumulate this rectangle in the view's dirty region, so we can
// process it later.
AddDirtyRegion(displayRoot, nsRegion(damagedRect));
// Schedule an invalidation flush with the refresh driver.
PostPendingUpdate();
RootViewManager()->IncrementUpdateCount();
if (!IsRefreshEnabled()) {
return NS_OK;
}
NS_IMETHODIMP nsViewManager::InvalidateAllViews()
{
if (RootViewManager() != this) {
return RootViewManager()->InvalidateAllViews();
}
InvalidateViews(mRootView);
// See if we should do an immediate refresh or wait
if (aUpdateFlags & NS_VMREFRESH_IMMEDIATE) {
Composite();
}
return NS_OK;
}
void nsViewManager::InvalidateViews(nsView *aView)
NS_IMETHODIMP nsViewManager::UpdateAllViews(PRUint32 aUpdateFlags)
{
// Invalidate this view.
InvalidateView(aView);
if (RootViewManager() != this) {
return RootViewManager()->UpdateAllViews(aUpdateFlags);
}
// Invalidate all children as well.
UpdateViews(mRootView, aUpdateFlags);
return NS_OK;
}
void nsViewManager::UpdateViews(nsView *aView, PRUint32 aUpdateFlags)
{
// update this view.
UpdateView(aView, aUpdateFlags);
// update all children as well.
nsView* childView = aView->GetFirstChild();
while (nsnull != childView) {
childView->GetViewManager()->InvalidateViews(childView);
childView->GetViewManager()->UpdateViews(childView, aUpdateFlags);
childView = childView->GetNextSibling();
}
}
@ -753,20 +800,32 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent,
break;
case NS_WILL_PAINT:
case NS_PAINT:
{
nsPaintEvent *event = static_cast<nsPaintEvent*>(aEvent);
if (!aView || !mContext)
break;
*aStatus = nsEventStatus_eConsumeNoDefault;
nsPaintEvent *event = static_cast<nsPaintEvent*>(aEvent);
if (aEvent->message == NS_PAINT && event->region.IsEmpty())
break;
NS_ASSERTION(static_cast<nsView*>(aView) ==
nsView::GetViewFor(event->widget),
"view/widget mismatch");
// The region is in device units, and it's in the coordinate space of
// its associated widget.
// Refresh the view
if (IsRefreshEnabled()) {
nsRefPtr<nsViewManager> rootVM = RootViewManager();
// If an ancestor widget was hidden and then shown, we could
// have a delayed resize to handle.
bool didResize = false;
for (nsViewManager *vm = this; vm;
vm = vm->mRootView->GetParent()
? vm->mRootView->GetParent()->GetViewManager()
@ -775,51 +834,97 @@ NS_IMETHODIMP nsViewManager::DispatchEvent(nsGUIEvent *aEvent,
vm->mRootView->IsEffectivelyVisible() &&
mPresShell && mPresShell->IsVisible()) {
vm->FlushDelayedResize(true);
vm->InvalidateView(vm->mRootView);
// Paint later.
vm->UpdateView(vm->mRootView, NS_VMREFRESH_NO_SYNC);
didResize = true;
// not sure if it's valid for us to claim that we
// ignored this, but we're going to do so anyway, since
// we didn't actually paint anything
*aStatus = nsEventStatus_eIgnore;
}
}
// Flush things like reflows and plugin widget geometry updates by
// calling WillPaint on observer presShells.
nsRefPtr<nsViewManager> rootVM = RootViewManager();
if (mPresShell) {
rootVM->CallWillPaintOnObservers(event->willSendDidPaint);
}
// Flush view widget geometry updates and invalidations.
rootVM->ProcessPendingUpdates();
}
break;
if (!didResize) {
//NS_ASSERTION(view->IsEffectivelyVisible(), "painting an invisible view");
case NS_PAINT:
{
if (!aView || !mContext)
break;
// Notify view observers that we're about to paint.
// Make sure to not send WillPaint notifications while scrolling.
nsCOMPtr<nsIWidget> widget;
rootVM->GetRootWidget(getter_AddRefs(widget));
bool transparentWindow = false;
if (widget)
transparentWindow = widget->GetTransparencyMode() == eTransparencyTransparent;
*aStatus = nsEventStatus_eConsumeNoDefault;
nsPaintEvent *event = static_cast<nsPaintEvent*>(aEvent);
nsView* view = static_cast<nsView*>(aView);
NS_ASSERTION(view == nsView::GetViewFor(event->widget),
"view/widget mismatch");
NS_ASSERTION(IsPaintingAllowed(),
"shouldn't be receiving paint events while painting is "
"disallowed!");
if (!transparentWindow) {
if (mPresShell) {
// Do an update view batch. Make sure not to do it DEFERRED,
// since that would effectively delay any invalidates that are
// triggered by the WillPaint notification (they'd happen when
// the invalid event fires, which is later than the reflow
// event would fire and could end up being after some timer
// events, leading to frame dropping in DHTML). Note that the
// observer may try to reenter this code from inside
// WillPaint() by trying to do a synchronous paint, but since
// refresh will be disabled it won't be able to do the paint.
// We should really sort out the rules on our synch painting
// api....
UpdateViewBatch batch(this);
rootVM->CallWillPaintOnObservers(event->willSendDidPaint);
batch.EndUpdateViewBatch(NS_VMREFRESH_NO_SYNC);
if (!event->didSendWillPaint) {
// Send NS_WILL_PAINT event ourselves.
nsPaintEvent willPaintEvent(true, NS_WILL_PAINT, event->widget);
willPaintEvent.willSendDidPaint = event->willSendDidPaint;
DispatchEvent(&willPaintEvent, view, aStatus);
// Get the view pointer again since NS_WILL_PAINT might have
// destroyed it during CallWillPaintOnObservers (bug 378273).
view = nsView::GetViewFor(event->widget);
// Get the view pointer again since the code above might have
// destroyed it (bug 378273).
view = nsView::GetViewFor(aEvent->widget);
}
}
// Make sure to sync up any widget geometry changes we
// have pending before we paint.
if (rootVM->mHasPendingUpdates) {
rootVM->ProcessPendingUpdates(mRootView, false);
}
if (!view || event->region.IsEmpty())
break;
// Paint.
if (view && aEvent->message == NS_PAINT) {
Refresh(view, event->widget, event->region);
}
}
} else if (aEvent->message == NS_PAINT) {
// since we got an NS_PAINT event, we need to
// draw something so we don't get blank areas,
// unless there's no widget or it's transparent.
nsRegion rgn = event->region.ToAppUnits(AppUnitsPerDevPixel());
rgn.MoveBy(-aView->ViewToWidgetOffset());
RenderViews(static_cast<nsView*>(aView), event->widget, rgn,
event->region, true, event->willSendDidPaint);
// Clients like the editor can trigger multiple
// reflows during what the user perceives as a single
// edit operation, so it disables view manager
// refreshing until the edit operation is complete
// so that users don't see the intermediate steps.
//
// Unfortunately some of these reflows can trigger
// nsScrollPortView and nsScrollingView Scroll() calls
// which in most cases force an immediate BitBlt and
// synchronous paint to happen even if the view manager's
// refresh is disabled. (Bug 97674)
//
// Calling UpdateView() here, is necessary to add
// the exposed region specified in the synchronous paint
// event to the view's damaged region so that it gets
// painted properly when refresh is enabled.
//
// Note that calling UpdateView() here was deemed
// to have the least impact on performance, since the
// other alternative was to make Scroll() post an
// async paint event for the *entire* ScrollPort or
// ScrollingView's viewable area. (See bug 97674 for this
// alternate patch.)
UpdateView(aView, rgn.GetBounds(), NS_VMREFRESH_NO_SYNC);
}
break;
}
@ -1071,7 +1176,7 @@ NS_IMETHODIMP nsViewManager::InsertChild(nsIView *aParent, nsIView *aChild, nsIV
//and mark this area as dirty if the view is visible...
if (nsViewVisibility_kHide != child->GetVisibility())
child->GetViewManager()->InvalidateView(child);
child->GetViewManager()->UpdateView(child, NS_VMREFRESH_NO_SYNC);
}
return NS_OK;
}
@ -1094,7 +1199,7 @@ NS_IMETHODIMP nsViewManager::RemoveChild(nsIView *aChild)
if (nsnull != parent) {
NS_ASSERTION(child->GetViewManager() == this ||
parent->GetViewManager() == this, "wrong view manager");
child->GetViewManager()->InvalidateView(child);
child->GetViewManager()->UpdateView(child, NS_VMREFRESH_NO_SYNC);
parent->RemoveChild(child);
}
@ -1116,8 +1221,9 @@ NS_IMETHODIMP nsViewManager::MoveViewTo(nsIView *aView, nscoord aX, nscoord aY)
nsView* parentView = view->GetParent();
if (parentView) {
nsViewManager* parentVM = parentView->GetViewManager();
parentVM->InvalidateView(parentView, oldBounds);
parentVM->InvalidateView(parentView, view->GetBoundsInParentUnits());
parentVM->UpdateView(parentView, oldBounds, NS_VMREFRESH_NO_SYNC);
parentVM->UpdateView(parentView, view->GetBoundsInParentUnits(),
NS_VMREFRESH_NO_SYNC);
}
}
}
@ -1125,33 +1231,34 @@ NS_IMETHODIMP nsViewManager::MoveViewTo(nsIView *aView, nscoord aX, nscoord aY)
}
void nsViewManager::InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
nscoord aY1, nscoord aY2, bool aInCutOut) {
PRUint32 aUpdateFlags, nscoord aY1, nscoord aY2, bool aInCutOut) {
nscoord height = aY2 - aY1;
if (aRect.x < aCutOut.x) {
nsRect r(aRect.x, aY1, aCutOut.x - aRect.x, height);
InvalidateView(aView, r);
UpdateView(aView, r, aUpdateFlags);
}
if (!aInCutOut && aCutOut.x < aCutOut.XMost()) {
nsRect r(aCutOut.x, aY1, aCutOut.width, height);
InvalidateView(aView, r);
UpdateView(aView, r, aUpdateFlags);
}
if (aCutOut.XMost() < aRect.XMost()) {
nsRect r(aCutOut.XMost(), aY1, aRect.XMost() - aCutOut.XMost(), height);
InvalidateView(aView, r);
UpdateView(aView, r, aUpdateFlags);
}
}
void nsViewManager::InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut) {
void nsViewManager::InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
PRUint32 aUpdateFlags) {
NS_ASSERTION(aView->GetViewManager() == this,
"InvalidateRectDifference called on view we don't own");
if (aRect.y < aCutOut.y) {
InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aRect.y, aCutOut.y, false);
InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aRect.y, aCutOut.y, false);
}
if (aCutOut.y < aCutOut.YMost()) {
InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aCutOut.y, aCutOut.YMost(), true);
InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aCutOut.y, aCutOut.YMost(), true);
}
if (aCutOut.YMost() < aRect.YMost()) {
InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aCutOut.YMost(), aRect.YMost(), false);
InvalidateHorizontalBandDifference(aView, aRect, aCutOut, aUpdateFlags, aCutOut.YMost(), aRect.YMost(), false);
}
}
@ -1175,13 +1282,14 @@ NS_IMETHODIMP nsViewManager::ResizeView(nsIView *aView, const nsRect &aRect, boo
view->SetDimensions(aRect, true);
nsViewManager* parentVM = parentView->GetViewManager();
if (!aRepaintExposedAreaOnly) {
// Invalidate the union of the old and new size
InvalidateView(view, aRect);
parentVM->InvalidateView(parentView, oldBounds);
//Invalidate the union of the old and new size
UpdateView(view, aRect, NS_VMREFRESH_NO_SYNC);
parentVM->UpdateView(parentView, oldBounds, NS_VMREFRESH_NO_SYNC);
} else {
InvalidateRectDifference(view, aRect, oldDimensions);
InvalidateRectDifference(view, aRect, oldDimensions, NS_VMREFRESH_NO_SYNC);
nsRect newBounds = view->GetBoundsInParentUnits();
parentVM->InvalidateRectDifference(parentView, oldBounds, newBounds);
parentVM->InvalidateRectDifference(parentView, oldBounds, newBounds,
NS_VMREFRESH_NO_SYNC);
}
}
}
@ -1220,11 +1328,12 @@ NS_IMETHODIMP nsViewManager::SetViewVisibility(nsIView *aView, nsViewVisibility
nsView* parentView = view->GetParent();
if (parentView) {
parentView->GetViewManager()->
InvalidateView(parentView, view->GetBoundsInParentUnits());
UpdateView(parentView, view->GetBoundsInParentUnits(),
NS_VMREFRESH_NO_SYNC);
}
}
else {
InvalidateView(view);
UpdateView(view, NS_VMREFRESH_NO_SYNC);
}
}
}
@ -1232,6 +1341,37 @@ NS_IMETHODIMP nsViewManager::SetViewVisibility(nsIView *aView, nsViewVisibility
return NS_OK;
}
void nsViewManager::UpdateWidgetsForView(nsView* aView)
{
NS_PRECONDITION(aView, "Must have view!");
// No point forcing an update if invalidations have been suppressed.
if (!IsRefreshEnabled())
return;
nsWeakView parentWeakView = aView;
if (aView->HasWidget()) {
aView->GetWidget()->Update(); // Flushes Layout!
if (!parentWeakView.IsAlive()) {
return;
}
}
nsView* childView = aView->GetFirstChild();
while (childView) {
nsWeakView childWeakView = childView;
UpdateWidgetsForView(childView);
if (NS_LIKELY(childWeakView.IsAlive())) {
childView = childView->GetNextSibling();
}
else {
// The current view was destroyed - restart at the first child if the
// parent is still alive.
childView = parentWeakView.IsAlive() ? aView->GetFirstChild() : nsnull;
}
}
}
bool nsViewManager::IsViewInserted(nsView *aView)
{
if (mRootView == aView) {
@ -1275,7 +1415,7 @@ NS_IMETHODIMP nsViewManager::SetViewZIndex(nsIView *aView, bool aAutoZIndex, PRI
if (oldidx != aZIndex || oldTopMost != aTopMost ||
oldIsAuto != aAutoZIndex) {
InvalidateView(view);
UpdateView(view, NS_VMREFRESH_NO_SYNC);
}
return rv;
@ -1288,24 +1428,68 @@ NS_IMETHODIMP nsViewManager::GetDeviceContext(nsDeviceContext *&aContext)
return NS_OK;
}
nsIViewManager*
nsViewManager::IncrementDisableRefreshCount()
void nsViewManager::TriggerRefresh(PRUint32 aUpdateFlags)
{
if (!IsRootVM()) {
return RootViewManager()->IncrementDisableRefreshCount();
RootViewManager()->TriggerRefresh(aUpdateFlags);
return;
}
++mRefreshDisableCount;
if (mUpdateBatchCnt > 0)
return;
// nested batching can combine IMMEDIATE with DEFERRED. Favour
// IMMEDIATE over DEFERRED and DEFERRED over NO_SYNC. We need to
// check for IMMEDIATE before checking mHasPendingUpdates, because
// the latter might be false as far as gecko is concerned but the OS
// might still have queued up expose events that it hasn't sent yet.
if (aUpdateFlags & NS_VMREFRESH_IMMEDIATE) {
FlushPendingInvalidates();
Composite();
} else if (!mHasPendingUpdates) {
// Nothing to do
} else if (aUpdateFlags & NS_VMREFRESH_DEFERRED) {
PostInvalidateEvent();
} else { // NO_SYNC
FlushPendingInvalidates();
}
}
nsIViewManager* nsViewManager::BeginUpdateViewBatch(void)
{
if (!IsRootVM()) {
return RootViewManager()->BeginUpdateViewBatch();
}
if (mUpdateBatchCnt == 0) {
mUpdateBatchFlags = 0;
}
++mUpdateBatchCnt;
return this;
}
void
nsViewManager::DecrementDisableRefreshCount()
NS_IMETHODIMP nsViewManager::EndUpdateViewBatch(PRUint32 aUpdateFlags)
{
NS_ASSERTION(IsRootVM(), "Should only be called on root");
--mRefreshDisableCount;
NS_ASSERTION(mRefreshDisableCount >= 0, "Invalid refresh disable count!");
--mUpdateBatchCnt;
NS_ASSERTION(mUpdateBatchCnt >= 0, "Invalid batch count!");
if (mUpdateBatchCnt < 0)
{
mUpdateBatchCnt = 0;
return NS_ERROR_FAILURE;
}
mUpdateBatchFlags |= aUpdateFlags;
if (mUpdateBatchCnt == 0) {
TriggerRefresh(mUpdateBatchFlags);
}
return NS_OK;
}
NS_IMETHODIMP nsViewManager::GetRootWidget(nsIWidget **aWidget)
@ -1325,6 +1509,20 @@ NS_IMETHODIMP nsViewManager::GetRootWidget(nsIWidget **aWidget)
return NS_OK;
}
NS_IMETHODIMP nsViewManager::ForceUpdate()
{
if (!IsRootVM()) {
return RootViewManager()->ForceUpdate();
}
// Walk the view tree looking for widgets, and call Update() on each one
if (mRootView) {
UpdateWidgetsForView(mRootView);
}
return NS_OK;
}
nsIntRect nsViewManager::ViewToWidget(nsView *aView, const nsRect &aRect) const
{
NS_ASSERTION(aView->GetViewManager() == this, "wrong view manager");
@ -1349,38 +1547,26 @@ nsViewManager::IsPainting(bool& aIsPainting)
}
void
nsViewManager::ProcessPendingUpdates()
nsViewManager::FlushPendingInvalidates()
{
if (!IsRootVM()) {
RootViewManager()->ProcessPendingUpdates();
return;
}
NS_ASSERTION(IsRootVM(), "Must be root VM for this to be called!");
NS_ASSERTION(mUpdateBatchCnt == 0, "Must not be in an update batch!");
if (mHasPendingUpdates) {
ProcessPendingUpdatesForView(mRootView, true);
ProcessPendingUpdates(mRootView, true);
mHasPendingUpdates = false;
}
}
void
nsViewManager::UpdateWidgetGeometry()
{
if (!IsRootVM()) {
RootViewManager()->UpdateWidgetGeometry();
return;
}
if (mHasPendingWidgetGeometryChanges) {
ProcessPendingUpdatesForView(mRootView, false);
mHasPendingWidgetGeometryChanges = false;
}
}
void
nsViewManager::CallWillPaintOnObservers(bool aWillSendDidPaint)
{
NS_PRECONDITION(IsRootVM(), "Must be root VM for this to be called!");
NS_PRECONDITION(mUpdateBatchCnt > 0, "Must be in an update batch!");
#ifdef DEBUG
PRInt32 savedUpdateBatchCnt = mUpdateBatchCnt;
#endif
PRInt32 index;
for (index = 0; index < mVMCount; index++) {
nsViewManager* vm = (nsViewManager*)gViewManagers->ElementAt(index);
@ -1390,6 +1576,8 @@ nsViewManager::CallWillPaintOnObservers(bool aWillSendDidPaint)
nsCOMPtr<nsIPresShell> shell = vm->GetPresShell();
if (shell) {
shell->WillPaint(aWillSendDidPaint);
NS_ASSERTION(mUpdateBatchCnt == savedUpdateBatchCnt,
"Observer did not end view batch?");
}
}
}
@ -1416,6 +1604,24 @@ nsViewManager::CallDidPaintOnObservers()
}
}
void
nsViewManager::ProcessInvalidateEvent()
{
NS_ASSERTION(IsRootVM(),
"Incorrectly targeted invalidate event");
// If we're in the middle of an update batch, just repost the event,
// to be processed when the batch ends.
bool processEvent = (mUpdateBatchCnt == 0);
if (processEvent) {
FlushPendingInvalidates();
}
mInvalidateEvent.Forget();
if (!processEvent) {
// We didn't actually process this event... post a new one
PostInvalidateEvent();
}
}
NS_IMETHODIMP
nsViewManager::GetLastUserEventTime(PRUint32& aTime)
{

View File

@ -53,27 +53,36 @@
/**
Invalidation model:
1) Callers call into the view manager and ask it to invalidate a view.
1) Callers call into the view manager and ask it to update a view.
2) The view manager finds the "right" widget for the view, henceforth called
the root widget.
3) The view manager traverses descendants of the root widget and for each
one that needs invalidation stores the rect to invalidate on the widget's
view (batching).
one that needs invalidation either
4) The dirty region is flushed to the right widget when
ProcessPendingUpdates is called from the RefreshDriver.
a) Calls Invalidate() on the widget (no batching)
or
b) Stores the rect to invalidate on the widget's view (batching)
// XXXbz we want to change this a bit. See bug 243726
4) When batching, the call to end the batch either processes the pending
Invalidate() calls on the widgets or posts an event to do so.
It's important to note that widgets associated to views outside this view
manager can end up being invalidated during step 3. Therefore, the end of a
view update batch really needs to traverse the entire view tree, to ensure
that those invalidates happen.
To cope with this, invalidation processing and should only happen on the
root viewmanager.
To cope with this, invalidate event processing and view update batch
handling should only happen on the root viewmanager. This means the root
view manager is the only thing keeping track of mUpdateCnt. As a result,
Composite() calls should also be forwarded to the root view manager.
*/
class nsInvalidateEvent;
class nsViewManager : public nsIViewManager {
public:
nsViewManager();
@ -95,9 +104,12 @@ public:
NS_IMETHOD SetWindowDimensions(nscoord width, nscoord height);
NS_IMETHOD FlushDelayedResize(bool aDoReflow);
NS_IMETHOD InvalidateView(nsIView *aView);
NS_IMETHOD InvalidateViewNoSuppression(nsIView *aView, const nsRect &aRect);
NS_IMETHOD InvalidateAllViews();
NS_IMETHOD Composite(void);
NS_IMETHOD UpdateView(nsIView *aView, PRUint32 aUpdateFlags);
NS_IMETHOD UpdateViewNoSuppression(nsIView *aView, const nsRect &aRect,
PRUint32 aUpdateFlags);
NS_IMETHOD UpdateAllViews(PRUint32 aUpdateFlags);
NS_IMETHOD DispatchEvent(nsGUIEvent *aEvent,
nsIView* aTargetView, nsEventStatus* aStatus);
@ -125,30 +137,27 @@ public:
NS_IMETHOD GetDeviceContext(nsDeviceContext *&aContext);
virtual nsIViewManager* IncrementDisableRefreshCount();
virtual void DecrementDisableRefreshCount();
virtual nsIViewManager* BeginUpdateViewBatch(void);
NS_IMETHOD EndUpdateViewBatch(PRUint32 aUpdateFlags);
NS_IMETHOD GetRootWidget(nsIWidget **aWidget);
NS_IMETHOD ForceUpdate();
NS_IMETHOD IsPainting(bool& aIsPainting);
NS_IMETHOD GetLastUserEventTime(PRUint32& aTime);
void ProcessInvalidateEvent();
static PRUint32 gLastUserEventTime;
/* Update the cached RootViewManager pointer on this view manager. */
void InvalidateHierarchy();
virtual void ProcessPendingUpdates();
virtual void UpdateWidgetGeometry();
protected:
virtual ~nsViewManager();
private:
void FlushPendingInvalidates();
void ProcessPendingUpdatesForView(nsView *aView,
bool aFlushDirtyRegion = true);
void FlushDirtyRegionToWidget(nsView* aView);
void ProcessPendingUpdates(nsView *aView, bool aDoInvalidate);
/**
* Call WillPaint() on all view observers under this vm root.
*/
@ -156,9 +165,13 @@ private:
void CallDidPaintOnObservers();
void ReparentChildWidgets(nsIView* aView, nsIWidget *aNewWidget);
void ReparentWidgets(nsIView* aView, nsIView *aParent);
void InvalidateWidgetArea(nsView *aWidgetView, const nsRegion &aDamagedRegion);
void UpdateWidgetArea(nsView *aWidgetView, nsIWidget* aWidget,
const nsRegion &aDamagedRegion,
nsView* aIgnoreWidgetView);
void InvalidateViews(nsView *aView);
void UpdateViews(nsView *aView, PRUint32 aUpdateFlags);
void TriggerRefresh(PRUint32 aUpdateFlags);
// aView is the view for aWidget and aRegion is relative to aWidget.
void Refresh(nsView *aView, nsIWidget *aWidget, const nsIntRegion& aRegion);
@ -168,14 +181,20 @@ private:
const nsRegion& aRegion, const nsIntRegion& aIntRegion,
bool aPaintDefaultBackground, bool aWillSendDidPaint);
void InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut);
void InvalidateRectDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut, PRUint32 aUpdateFlags);
void InvalidateHorizontalBandDifference(nsView *aView, const nsRect& aRect, const nsRect& aCutOut,
nscoord aY1, nscoord aY2, bool aInCutOut);
PRUint32 aUpdateFlags, nscoord aY1, nscoord aY2, bool aInCutOut);
// Utilities
bool IsViewInserted(nsView *aView);
/**
* Function to recursively call Update() on all widgets belonging to
* a view or its kids.
*/
void UpdateWidgetsForView(nsView* aView);
/**
* Intersects aRect with aView's bounds and then transforms it from aView's
* coordinate system to the coordinate system of the widget attached to
@ -185,6 +204,31 @@ private:
void DoSetWindowDimensions(nscoord aWidth, nscoord aHeight);
// Safety helpers
void IncrementUpdateCount() {
NS_ASSERTION(IsRootVM(),
"IncrementUpdateCount called on non-root viewmanager");
++mUpdateCnt;
}
void DecrementUpdateCount() {
NS_ASSERTION(IsRootVM(),
"DecrementUpdateCount called on non-root viewmanager");
--mUpdateCnt;
}
PRInt32 UpdateCount() const {
NS_ASSERTION(IsRootVM(),
"DecrementUpdateCount called on non-root viewmanager");
return mUpdateCnt;
}
void ClearUpdateCount() {
NS_ASSERTION(IsRootVM(),
"DecrementUpdateCount called on non-root viewmanager");
mUpdateCnt = 0;
}
bool IsPainting() const {
return RootViewManager()->mPainting;
}
@ -193,21 +237,18 @@ private:
RootViewManager()->mPainting = aPainting;
}
nsresult InvalidateView(nsIView *aView, const nsRect &aRect);
nsresult UpdateView(nsIView *aView, const nsRect &aRect, PRUint32 aUpdateFlags);
public: // NOT in nsIViewManager, so private to the view module
nsView* GetRootViewImpl() const { return mRootView; }
nsViewManager* RootViewManager() const { return mRootViewManager; }
bool IsRootVM() const { return this == RootViewManager(); }
// Whether synchronous painting is allowed at the moment. For example,
// widget geometry changes can cause synchronous painting, so they need to
// be deferred while refresh is disabled.
bool IsPaintingAllowed() { return RootViewManager()->mRefreshDisableCount == 0; }
bool IsRefreshEnabled() { return RootViewManager()->mUpdateBatchCnt == 0; }
// Call this when you need to let the viewmanager know that it now has
// pending updates.
void PostPendingUpdate();
void PostPendingUpdate() { RootViewManager()->mHasPendingUpdates = true; }
PRUint32 AppUnitsPerDevPixel() const
{
@ -227,16 +268,21 @@ private:
// never null (if we have no ancestors, it will be |this|).
nsViewManager *mRootViewManager;
nsRevocableEventPtr<nsInvalidateEvent> mInvalidateEvent;
// The following members should not be accessed directly except by
// the root view manager. Some have accessor functions to enforce
// this, as noted.
PRInt32 mRefreshDisableCount;
// Use IncrementUpdateCount(), DecrementUpdateCount(), UpdateCount(),
// ClearUpdateCount() on the root viewmanager to access mUpdateCnt.
PRInt32 mUpdateCnt;
PRInt32 mUpdateBatchCnt;
PRUint32 mUpdateBatchFlags;
// Use IsPainting() and SetPainting() to access mPainting.
bool mPainting;
bool mRecursiveRefreshPending;
bool mHasPendingUpdates;
bool mHasPendingWidgetGeometryChanges;
bool mInScroll;
//from here to public should be static and locked... MMP
@ -244,6 +290,24 @@ private:
//list of view managers
static nsVoidArray *gViewManagers;
void PostInvalidateEvent();
};
class nsInvalidateEvent : public nsRunnable {
public:
nsInvalidateEvent(class nsViewManager *vm) : mViewManager(vm) {
NS_ASSERTION(mViewManager, "null parameter");
}
void Revoke() { mViewManager = nsnull; }
NS_IMETHOD Run() {
if (mViewManager)
mViewManager->ProcessInvalidateEvent();
return NS_OK;
}
protected:
class nsViewManager *mViewManager;
};
#endif /* nsViewManager_h___ */

View File

@ -737,15 +737,13 @@ class nsPaintEvent : public nsGUIEvent
public:
nsPaintEvent(bool isTrusted, PRUint32 msg, nsIWidget *w)
: nsGUIEvent(isTrusted, msg, w, NS_PAINT_EVENT),
willSendDidPaint(false),
didSendWillPaint(false)
willSendDidPaint(false)
{
}
// area that needs repainting
nsIntRegion region;
bool willSendDidPaint;
bool didSendWillPaint;
};
/**

View File

@ -118,8 +118,8 @@ typedef nsEventStatus (* EVENT_CALLBACK)(nsGUIEvent *event);
#endif
#define NS_IWIDGET_IID \
{ 0xba20ac65, 0xb2a6, 0x4052, \
{ 0xa4, 0xcb, 0x65, 0x40, 0xf8, 0x87, 0x9c, 0x55 } }
{ 0x6ca77c11, 0xade7, 0x4715, \
{ 0x82, 0xe0, 0xfe, 0xae, 0x42, 0xca, 0x5b, 0x1f } }
/*
* Window shadow styles
* Also used for the -moz-window-shadow CSS property
@ -1021,10 +1021,21 @@ class nsIWidget : public nsISupports {
NS_IMETHOD MakeFullScreen(bool aFullScreen) = 0;
/**
* Invalidate a specified rect for a widget so that it will be repainted
* later.
* Invalidate a specified rect for a widget and repaints it.
*
* @param aIsSynchronouse true then repaint synchronously. If false repaint later.
* @see #Update()
*/
NS_IMETHOD Invalidate(const nsIntRect & aRect) = 0;
NS_IMETHOD Invalidate(const nsIntRect & aRect, bool aIsSynchronous) = 0;
/**
* Force a synchronous repaint of the window if there are dirty rects.
*
* @see Invalidate()
*/
NS_IMETHOD Update() = 0;
enum LayerManagerPersistence
{

View File

@ -589,13 +589,20 @@ nsWindow::IsEnabled(bool *aState)
}
NS_IMETHODIMP
nsWindow::Invalidate(const nsIntRect &aRect)
nsWindow::Invalidate(const nsIntRect &aRect,
bool aIsSynchronous)
{
AndroidGeckoEvent *event = new AndroidGeckoEvent(AndroidGeckoEvent::DRAW, aRect);
nsAppShell::gAppShell->PostEvent(event);
return NS_OK;
}
NS_IMETHODIMP
nsWindow::Update()
{
return NS_OK;
}
nsWindow*
nsWindow::FindTopLevel()
{

View File

@ -120,7 +120,9 @@ public:
NS_IMETHOD SetSizeMode(PRInt32 aMode);
NS_IMETHOD Enable(bool aState);
NS_IMETHOD IsEnabled(bool *aState);
NS_IMETHOD Invalidate(const nsIntRect &aRect);
NS_IMETHOD Invalidate(const nsIntRect &aRect,
bool aIsSynchronous);
NS_IMETHOD Update();
NS_IMETHOD SetFocus(bool aRaise = false);
NS_IMETHOD GetScreenBounds(nsIntRect &aRect);
virtual nsIntPoint WidgetToScreenOffset();

View File

@ -418,7 +418,7 @@ public:
NS_IMETHOD SetFocus(bool aRaise);
NS_IMETHOD GetBounds(nsIntRect &aRect);
NS_IMETHOD Invalidate(const nsIntRect &aRect);
NS_IMETHOD Invalidate(const nsIntRect &aRect, bool aIsSynchronous);
virtual void* GetNativeData(PRUint32 aDataType);
virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations);
@ -429,6 +429,7 @@ public:
{ return aStatus == nsEventStatus_eConsumeNoDefault; }
NS_IMETHOD DispatchEvent(nsGUIEvent* event, nsEventStatus & aStatus);
NS_IMETHOD Update();
virtual bool GetShouldAccelerate();
NS_IMETHOD SetCursor(nsCursor aCursor);

View File

@ -1398,7 +1398,7 @@ static void blinkRgn(RgnHandle rgn)
#endif
// Invalidate this component's visible area
NS_IMETHODIMP nsChildView::Invalidate(const nsIntRect &aRect)
NS_IMETHODIMP nsChildView::Invalidate(const nsIntRect &aRect, bool aIsSynchronous)
{
NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
@ -1408,7 +1408,10 @@ NS_IMETHODIMP nsChildView::Invalidate(const nsIntRect &aRect)
NSRect r;
nsCocoaUtils::GeckoRectToNSRect(aRect, r);
if ([NSView focusView]) {
if (aIsSynchronous) {
[mView displayRect:r];
}
else if ([NSView focusView]) {
// if a view is focussed (i.e. being drawn), then postpone the invalidate so that we
// don't lose it.
[mView setNeedsPendingDisplayInRect:r];
@ -1439,6 +1442,15 @@ inline PRUint16 COLOR8TOCOLOR16(PRUint8 color8)
return (color8 << 8) | color8; /* (color8 * 257) == (color8 * 0x0101) */
}
// The OS manages repaints well enough on its own, so we don't have to
// flush them out here. In other words, the OS will automatically call
// displayIfNeeded at the appropriate times, so we don't need to do it
// ourselves. See bmo bug 459319.
NS_IMETHODIMP nsChildView::Update()
{
return NS_OK;
}
#pragma mark -
nsresult nsChildView::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
@ -2508,7 +2520,6 @@ NSEvent* gLastDragMouseDownEvent = nil;
#endif
// Create the event so we can fill in its region
nsPaintEvent paintEvent(true, NS_PAINT, mGeckoChild);
paintEvent.didSendWillPaint = true;
nsIntRect boundingRect =
nsIntRect(aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height);

View File

@ -252,7 +252,8 @@ public:
NS_IMETHOD SetTitle(const nsAString& aTitle);
NS_IMETHOD Invalidate(const nsIntRect &aRect);
NS_IMETHOD Invalidate(const nsIntRect &aRect, bool aIsSynchronous);
NS_IMETHOD Update();
virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations);
virtual LayerManager* GetLayerManager(PLayersChild* aShadowManager = nsnull,
LayersBackend aBackendHint = LayerManager::LAYERS_NONE,

View File

@ -1279,10 +1279,18 @@ NS_IMETHODIMP nsCocoaWindow::SetTitle(const nsAString& aTitle)
NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
}
NS_IMETHODIMP nsCocoaWindow::Invalidate(const nsIntRect & aRect)
NS_IMETHODIMP nsCocoaWindow::Invalidate(const nsIntRect & aRect, bool aIsSynchronous)
{
if (mPopupContentView)
return mPopupContentView->Invalidate(aRect);
return mPopupContentView->Invalidate(aRect, aIsSynchronous);
return NS_OK;
}
NS_IMETHODIMP nsCocoaWindow::Update()
{
if (mPopupContentView)
return mPopupContentView->Update();
return NS_OK;
}

View File

@ -116,7 +116,7 @@ nsNativeThemeGTK::RefreshWidgetWindow(nsIFrame* aFrame)
if (!vm)
return;
vm->InvalidateAllViews();
vm->UpdateAllViews(NS_VMREFRESH_NO_SYNC);
}
static bool IsFrameContentNodeInNamespace(nsIFrame *aFrame, PRUint32 aNamespace)

View File

@ -1736,7 +1736,8 @@ nsWindow::SetCursor(imgIContainer* aCursor,
}
NS_IMETHODIMP
nsWindow::Invalidate(const nsIntRect &aRect)
nsWindow::Invalidate(const nsIntRect &aRect,
bool aIsSynchronous)
{
if (!mGdkWindow)
return NS_OK;
@ -1747,14 +1748,30 @@ nsWindow::Invalidate(const nsIntRect &aRect)
rect.width = aRect.width;
rect.height = aRect.height;
LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d\n", (void *)this,
rect.x, rect.y, rect.width, rect.height));
LOGDRAW(("Invalidate (rect) [%p]: %d %d %d %d (sync: %d)\n", (void *)this,
rect.x, rect.y, rect.width, rect.height, aIsSynchronous));
gdk_window_invalidate_rect(mGdkWindow, &rect, FALSE);
if (aIsSynchronous)
gdk_window_process_updates(mGdkWindow, FALSE);
return NS_OK;
}
NS_IMETHODIMP
nsWindow::Update()
{
if (!mGdkWindow)
return NS_OK;
LOGDRAW(("Update [%p] %p\n", this, mGdkWindow));
gdk_window_process_updates(mGdkWindow, FALSE);
// Send the updates to the server.
gdk_display_flush(gdk_window_get_display(mGdkWindow));
return NS_OK;
}
void*
nsWindow::GetNativeData(PRUint32 aDataType)
{

View File

@ -177,7 +177,9 @@ public:
NS_IMETHOD SetCursor(nsCursor aCursor);
NS_IMETHOD SetCursor(imgIContainer* aCursor,
PRUint32 aHotspotX, PRUint32 aHotspotY);
NS_IMETHOD Invalidate(const nsIntRect &aRect);
NS_IMETHOD Invalidate(const nsIntRect &aRect,
bool aIsSynchronous);
NS_IMETHOD Update();
virtual void* GetNativeData(PRUint32 aDataType);
NS_IMETHOD SetTitle(const nsAString& aTitle);
NS_IMETHOD SetIcon(const nsAString& aIconSpec);

View File

@ -617,12 +617,28 @@ NS_METHOD nsWindow::SetFocus(bool aRaise)
//-----------------------------------------------------------------------------
NS_METHOD nsWindow::Invalidate(const nsIntRect& aRect)
NS_METHOD nsWindow::Invalidate(const nsIntRect& aRect, bool aIsSynchronous)
{
if (mWnd) {
RECTL rcl = {aRect.x, aRect.y, aRect.x + aRect.width, aRect.y + aRect.height};
NS2PM(rcl);
WinInvalidateRect(mWnd, &rcl, false);
#if 0
if (aIsSynchronous) {
Update();
}
#endif
}
return NS_OK;
}
//-----------------------------------------------------------------------------
// Force a synchronous repaint of the window.
NS_IMETHODIMP nsWindow::Update()
{
if (mWnd) {
WinUpdateWindow(mWnd);
}
return NS_OK;
}

View File

@ -175,7 +175,9 @@ public:
NS_IMETHOD Show(bool aState);
NS_IMETHOD IsVisible(bool& aState);
NS_IMETHOD SetFocus(bool aRaise);
NS_IMETHOD Invalidate(const nsIntRect& aRect);
NS_IMETHOD Invalidate(const nsIntRect& aRect,
bool aIsSynchronous);
NS_IMETHOD Update();
gfxASurface* GetThebesSurface();
virtual void* GetNativeData(PRUint32 aDataType);
virtual void FreeNativeData(void* aDatum, PRUint32 aDataType);

View File

@ -751,10 +751,11 @@ nsWindow::SetCursor(imgIContainer* aCursor,
}
NS_IMETHODIMP
nsWindow::Invalidate(const nsIntRect &aRect)
nsWindow::Invalidate(const nsIntRect &aRect,
bool aIsSynchronous)
{
LOGDRAW(("Invalidate (rect) [%p,%p]: %d %d %d %d\n", (void *)this,
(void*)mWidget,aRect.x, aRect.y, aRect.width, aRect.height));
LOGDRAW(("Invalidate (rect) [%p,%p]: %d %d %d %d (sync: %d)\n", (void *)this,
(void*)mWidget,aRect.x, aRect.y, aRect.width, aRect.height, aIsSynchronous));
if (!mWidget)
return NS_OK;
@ -763,6 +764,19 @@ nsWindow::Invalidate(const nsIntRect &aRect)
mWidget->update(aRect.x, aRect.y, aRect.width, aRect.height);
// QGraphicsItems cannot trigger a repaint themselves, so we start it on the view
if (aIsSynchronous) {
QWidget *widget = GetViewWidget();
if (widget)
widget->repaint();
}
return NS_OK;
}
NS_IMETHODIMP
nsWindow::Update()
{
return NS_OK;
}

View File

@ -173,7 +173,9 @@ public:
NS_IMETHOD GetHasTransparentBackground(bool& aTransparent);
NS_IMETHOD HideWindowChrome(bool aShouldHide);
NS_IMETHOD MakeFullScreen(bool aFullScreen);
NS_IMETHOD Invalidate(const nsIntRect &aRect);
NS_IMETHOD Invalidate(const nsIntRect &aRect,
bool aIsSynchronous);
NS_IMETHOD Update();
virtual void* GetNativeData(PRUint32 aDataType);
NS_IMETHOD SetTitle(const nsAString& aTitle);

View File

@ -1280,12 +1280,8 @@ NS_METHOD nsWindow::Show(bool bState)
}
#ifdef MOZ_XUL
if (!wasVisible && bState) {
Invalidate();
if (syncInvalidate) {
::UpdateWindow(mWnd);
}
}
if (!wasVisible && bState)
Invalidate(syncInvalidate);
#endif
return NS_OK;
@ -1491,7 +1487,7 @@ NS_METHOD nsWindow::Resize(PRInt32 aWidth, PRInt32 aHeight, bool aRepaint)
}
if (aRepaint)
Invalidate();
Invalidate(false);
return NS_OK;
}
@ -1530,7 +1526,7 @@ NS_METHOD nsWindow::Resize(PRInt32 aX, PRInt32 aY, PRInt32 aWidth, PRInt32 aHeig
}
if (aRepaint)
Invalidate();
Invalidate(false);
return NS_OK;
}
@ -2021,7 +2017,7 @@ nsWindow::ResetLayout()
OnResize(evRect);
// Invalidate and update
Invalidate();
Invalidate(false);
}
// Internally track the caption status via a window property. Required
@ -2657,7 +2653,8 @@ NS_IMETHODIMP nsWindow::HideWindowChrome(bool aShouldHide)
**************************************************************/
// Invalidate this component visible area
NS_METHOD nsWindow::Invalidate(bool aEraseBackground,
NS_METHOD nsWindow::Invalidate(bool aIsSynchronous,
bool aEraseBackground,
bool aUpdateNCArea,
bool aIncludeChildren)
{
@ -2669,6 +2666,7 @@ NS_METHOD nsWindow::Invalidate(bool aEraseBackground,
debug_DumpInvalidate(stdout,
this,
nsnull,
aIsSynchronous,
nsCAutoString("noname"),
(PRInt32) mWnd);
#endif // WIDGET_DEBUG_OUTPUT
@ -2677,6 +2675,9 @@ NS_METHOD nsWindow::Invalidate(bool aEraseBackground,
if (aEraseBackground) {
flags |= RDW_ERASE;
}
if (aIsSynchronous) {
flags |= RDW_UPDATENOW;
}
if (aUpdateNCArea) {
flags |= RDW_FRAME;
}
@ -2689,7 +2690,7 @@ NS_METHOD nsWindow::Invalidate(bool aEraseBackground,
}
// Invalidate this component visible area
NS_METHOD nsWindow::Invalidate(const nsIntRect & aRect)
NS_METHOD nsWindow::Invalidate(const nsIntRect & aRect, bool aIsSynchronous)
{
if (mWnd)
{
@ -2697,6 +2698,7 @@ NS_METHOD nsWindow::Invalidate(const nsIntRect & aRect)
debug_DumpInvalidate(stdout,
this,
&aRect,
aIsSynchronous,
nsCAutoString("noname"),
(PRInt32) mWnd);
#endif // WIDGET_DEBUG_OUTPUT
@ -2709,6 +2711,10 @@ NS_METHOD nsWindow::Invalidate(const nsIntRect & aRect)
rect.bottom = aRect.y + aRect.height;
VERIFY(::InvalidateRect(mWnd, &rect, FALSE));
if (aIsSynchronous) {
VERIFY(::UpdateWindow(mWnd));
}
}
return NS_OK;
}
@ -2748,7 +2754,7 @@ nsWindow::MakeFullScreen(bool aFullScreen)
if (visible) {
Show(true);
Invalidate();
Invalidate(false);
}
// Notify the taskbar that we have exited full screen mode.
@ -2765,6 +2771,26 @@ nsWindow::MakeFullScreen(bool aFullScreen)
return rv;
}
/**************************************************************
*
* SECTION: nsIWidget::Update
*
* Force a synchronous repaint of the window.
*
**************************************************************/
NS_IMETHODIMP nsWindow::Update()
{
nsresult rv = NS_OK;
// updates can come through for windows no longer holding an mWnd during
// deletes triggered by JavaScript in buttons with mouse feedback
if (mWnd)
VERIFY(::UpdateWindow(mWnd));
return rv;
}
/**************************************************************
*
* SECTION: Native data storage
@ -4655,7 +4681,7 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam,
// Invalidate the window so that the repaint will
// pick up the new theme.
Invalidate(true, true, true);
Invalidate(true, true, true, true);
}
break;
@ -5334,7 +5360,7 @@ bool nsWindow::ProcessMessage(UINT msg, WPARAM &wParam, LPARAM &lParam,
BroadcastMsg(mWnd, WM_DWMCOMPOSITIONCHANGED);
DispatchStandardEvent(NS_THEMECHANGED);
UpdateGlass();
Invalidate(true, true, true);
Invalidate(true, true, true, true);
break;
#endif
@ -7210,7 +7236,7 @@ nsWindow::ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
r.Sub(bounds, configuration.mBounds);
r.MoveBy(-bounds.x,
-bounds.y);
w->Invalidate(r.GetBounds());
w->Invalidate(r.GetBounds(), false);
}
}
rv = w->SetWindowClipRegion(configuration.mClipRegion, false);
@ -7430,7 +7456,7 @@ bool nsWindow::OnResize(nsIntRect &aWindowRect)
#ifdef CAIRO_HAS_D2D_SURFACE
if (mD2DWindowSurface) {
mD2DWindowSurface = NULL;
Invalidate();
Invalidate(false);
}
#endif

View File

@ -142,10 +142,12 @@ public:
virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations);
NS_IMETHOD MakeFullScreen(bool aFullScreen);
NS_IMETHOD HideWindowChrome(bool aShouldHide);
NS_IMETHOD Invalidate(bool aEraseBackground = false,
NS_IMETHOD Invalidate(bool aIsSynchronous,
bool aEraseBackground = false,
bool aUpdateNCArea = false,
bool aIncludeChildren = false);
NS_IMETHOD Invalidate(const nsIntRect & aRect);
NS_IMETHOD Invalidate(const nsIntRect & aRect, bool aIsSynchronous);
NS_IMETHOD Update();
virtual void* GetNativeData(PRUint32 aDataType);
virtual void FreeNativeData(void * data, PRUint32 aDataType);
NS_IMETHOD SetTitle(const nsAString& aTitle);

View File

@ -310,7 +310,6 @@ bool nsWindow::OnPaint(HDC aDC, PRUint32 aNestingLevel)
#endif
event.region = GetRegionToPaint(forceRepaint, ps, hDC);
event.willSendDidPaint = true;
event.didSendWillPaint = true;
if (!event.region.IsEmpty() && mEventCallback)
{
@ -575,7 +574,7 @@ bool nsWindow::OnPaint(HDC aDC, PRUint32 aNestingLevel)
// When our device was removed, we should have gfxWindowsPlatform
// check if its render mode is up to date!
gfxWindowsPlatform::GetPlatform()->UpdateRenderMode();
Invalidate();
Invalidate(false);
}
}
break;
@ -586,7 +585,7 @@ bool nsWindow::OnPaint(HDC aDC, PRUint32 aNestingLevel)
gfxWindowsPlatform::GetPlatform()->UpdateRenderMode();
LayerManagerD3D10 *layerManagerD3D10 = static_cast<mozilla::layers::LayerManagerD3D10*>(GetLayerManager());
if (layerManagerD3D10->device() != gfxWindowsPlatform::GetPlatform()->GetD3D10Device()) {
Invalidate();
Invalidate(false);
} else {
result = DispatchWindowEvent(&event, eventStatus);
}

View File

@ -56,7 +56,7 @@ InvalidateRegion(nsIWidget* aWidget, const nsIntRegion& aRegion)
{
nsIntRegionRectIterator it(aRegion);
while(const nsIntRect* r = it.Next()) {
aWidget->Invalidate(*r);
aWidget->Invalidate(*r, false/*async*/);
}
}
@ -228,20 +228,25 @@ PuppetWidget::SetFocus(bool aRaise)
}
NS_IMETHODIMP
PuppetWidget::Invalidate(const nsIntRect& aRect)
PuppetWidget::Invalidate(const nsIntRect& aRect, bool aIsSynchronous)
{
#ifdef DEBUG
debug_DumpInvalidate(stderr, this, &aRect,
debug_DumpInvalidate(stderr, this, &aRect, aIsSynchronous,
nsCAutoString("PuppetWidget"), nsnull);
#endif
if (mChild) {
return mChild->Invalidate(aRect);
return mChild->Invalidate(aRect, aIsSynchronous);
}
mDirtyRegion.Or(mDirtyRegion, aRect);
if (!mDirtyRegion.IsEmpty() && !mPaintTask.IsPending()) {
if (mDirtyRegion.IsEmpty()) {
return NS_OK;
} else if (aIsSynchronous) {
DispatchPaintEvent();
return NS_OK;
} else if (!mPaintTask.IsPending()) {
mPaintTask = new PaintTask(this);
return NS_DispatchToCurrentThread(mPaintTask.get());
}
@ -249,6 +254,19 @@ PuppetWidget::Invalidate(const nsIntRect& aRect)
return NS_OK;
}
NS_IMETHODIMP
PuppetWidget::Update()
{
if (mChild) {
return mChild->Update();
}
if (mDirtyRegion.IsEmpty()) {
return NS_OK;
}
return DispatchPaintEvent();
}
void
PuppetWidget::InitEvent(nsGUIEvent& event, nsIntPoint* aPoint)
{

View File

@ -123,7 +123,9 @@ public:
virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations)
{ return NS_OK; }
NS_IMETHOD Invalidate(const nsIntRect& aRect);
NS_IMETHOD Invalidate(const nsIntRect& aRect, bool aIsSynchronous);
NS_IMETHOD Update();
// This API is going away, steer clear.
virtual void Scroll(const nsIntPoint& aDelta,

View File

@ -1469,6 +1469,7 @@ nsBaseWidget::debug_DumpPaintEvent(FILE * aFileOut,
nsBaseWidget::debug_DumpInvalidate(FILE * aFileOut,
nsIWidget * aWidget,
const nsIntRect * aRect,
bool aIsSynchronous,
const nsCAutoString & aWidgetName,
PRInt32 aWindowID)
{
@ -1501,6 +1502,10 @@ nsBaseWidget::debug_DumpInvalidate(FILE * aFileOut,
"none");
}
fprintf(aFileOut,
" sync=%s",
(const char *) (aIsSynchronous ? "yes" : "no "));
fprintf(aFileOut,"\n");
}
//////////////////////////////////////////////////////////////

View File

@ -297,6 +297,7 @@ protected:
static void debug_DumpInvalidate(FILE * aFileOut,
nsIWidget * aWidget,
const nsIntRect * aRect,
bool aIsSynchronous,
const nsCAutoString & aWidgetName,
PRInt32 aWindowID);