Back to index

lightning-sunbird  0.9+nobinonly
nsSVGForeignObjectFrame.cpp
Go to the documentation of this file.
00001 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
00002 /* ***** BEGIN LICENSE BLOCK *****
00003  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00004  *
00005  * The contents of this file are subject to the Mozilla Public License Version
00006  * 1.1 (the "License"); you may not use this file except in compliance with
00007  * the License. You may obtain a copy of the License at
00008  * http://www.mozilla.org/MPL/
00009  *
00010  * Software distributed under the License is distributed on an "AS IS" basis,
00011  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00012  * for the specific language governing rights and limitations under the
00013  * License.
00014  *
00015  * The Original Code is the Mozilla SVG project.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Crocodile Clips Ltd..
00019  * Portions created by the Initial Developer are Copyright (C) 2001
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Alex Fritze <alex.fritze@crocodile-clips.com> (original author)
00024  *
00025  * Alternatively, the contents of this file may be used under the terms of
00026  * either of the GNU General Public License Version 2 or later (the "GPL"),
00027  * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00028  * in which case the provisions of the GPL or the LGPL are applicable instead
00029  * of those above. If you wish to allow use of your version of this file only
00030  * under the terms of either the GPL or the LGPL, and not to allow others to
00031  * use your version of this file under the terms of the MPL, indicate your
00032  * decision by deleting the provisions above and replace them with the notice
00033  * and other provisions required by the GPL or the LGPL. If you do not delete
00034  * the provisions above, a recipient may use your version of this file under
00035  * the terms of any one of the MPL, the GPL or the LGPL.
00036  *
00037  * ***** END LICENSE BLOCK ***** */
00038 
00039 #include "nsBlockFrame.h"
00040 #include "nsIDOMSVGGElement.h"
00041 #include "nsPresContext.h"
00042 #include "nsISVGChildFrame.h"
00043 #include "nsISVGContainerFrame.h"
00044 #include "nsISVGRendererCanvas.h"
00045 #include "nsWeakReference.h"
00046 #include "nsISVGValue.h"
00047 #include "nsISVGValueObserver.h"
00048 #include "nsIDOMSVGTransformable.h"
00049 #include "nsIDOMSVGAnimTransformList.h"
00050 #include "nsIDOMSVGTransformList.h"
00051 #include "nsIDOMSVGAnimatedLength.h"
00052 #include "nsIDOMSVGLength.h"
00053 #include "nsIDOMSVGForeignObjectElem.h"
00054 #include "nsIDOMSVGMatrix.h"
00055 #include "nsIDOMSVGSVGElement.h"
00056 #include "nsIDOMSVGPoint.h"
00057 #include "nsSpaceManager.h"
00058 #include "nsISVGRendererRegion.h"
00059 #include "nsISVGRenderer.h"
00060 #include "nsISVGOuterSVGFrame.h"
00061 #include "nsTransform2D.h"
00062 #include "nsSVGPoint.h"
00063 #include "nsSVGRect.h"
00064 #include "nsSVGMatrix.h"
00065 #include "nsLayoutAtoms.h"
00066 
00067 typedef nsBlockFrame nsSVGForeignObjectFrameBase;
00068 
00069 class nsSVGForeignObjectFrame : public nsSVGForeignObjectFrameBase,
00070                                 public nsISVGContainerFrame,
00071                                 public nsISVGChildFrame,
00072                                 public nsISVGValueObserver,
00073                                 public nsSupportsWeakReference
00074 {
00075   friend nsresult
00076   NS_NewSVGForeignObjectFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsIFrame** aNewFrame);
00077 protected:
00078   nsSVGForeignObjectFrame();
00079   virtual ~nsSVGForeignObjectFrame();
00080   nsresult Init();
00081   
00082   // nsISupports interface:
00083   NS_IMETHOD QueryInterface(const nsIID& aIID, void** aInstancePtr);
00084 private:
00085   NS_IMETHOD_(nsrefcnt) AddRef() { return NS_OK; }
00086   NS_IMETHOD_(nsrefcnt) Release() { return NS_OK; }  
00087 public:
00088   // nsIFrame:  
00089   NS_IMETHOD Init(nsPresContext*  aPresContext,
00090                   nsIContent*      aContent,
00091                   nsIFrame*        aParent,
00092                   nsStyleContext*  aContext,
00093                   nsIFrame*        aPrevInFlow);
00094 
00095   NS_IMETHOD Reflow(nsPresContext*          aPresContext,
00096                     nsHTMLReflowMetrics&     aDesiredSize,
00097                     const nsHTMLReflowState& aReflowState,
00098                     nsReflowStatus&          aStatus);
00099 
00100   NS_IMETHOD  AppendFrames(nsIAtom*        aListName,
00101                            nsIFrame*       aFrameList);
00102   
00103   NS_IMETHOD  InsertFrames(nsIAtom*        aListName,
00104                            nsIFrame*       aPrevFrame,
00105                            nsIFrame*       aFrameList);
00106   
00107   NS_IMETHOD  RemoveFrame(nsIAtom*        aListName,
00108                           nsIFrame*       aOldFrame);
00109   
00110   NS_IMETHOD  ReplaceFrame(nsIAtom*        aListName,
00111                            nsIFrame*       aOldFrame,
00112                            nsIFrame*       aNewFrame);
00113 
00119   // XXX Need to make sure that any of the code examining
00120   // frametypes, particularly code looking at block and area
00121   // also handles foreignObject before we return our own frametype
00122   // virtual nsIAtom* GetType() const;
00123   virtual PRBool IsFrameOfType(PRUint32 aFlags) const;
00124 
00125 #ifdef DEBUG
00126   NS_IMETHOD GetFrameName(nsAString& aResult) const
00127   {
00128     return MakeFrameName(NS_LITERAL_STRING("SVGForeignObject"), aResult);
00129   }
00130 #endif
00131 
00132   // nsISVGValueObserver
00133   NS_IMETHOD WillModifySVGObservable(nsISVGValue* observable,
00134                                      nsISVGValue::modificationType aModType);
00135   NS_IMETHOD DidModifySVGObservable (nsISVGValue* observable,
00136                                      nsISVGValue::modificationType aModType);
00137 
00138   // nsISupportsWeakReference
00139   // implementation inherited from nsSupportsWeakReference
00140   
00141   // nsISVGChildFrame interface:
00142   NS_IMETHOD PaintSVG(nsISVGRendererCanvas* canvas, const nsRect& dirtyRectTwips);
00143   NS_IMETHOD GetFrameForPointSVG(float x, float y, nsIFrame** hit);  
00144   NS_IMETHOD_(already_AddRefed<nsISVGRendererRegion>) GetCoveredRegion();
00145   NS_IMETHOD InitialUpdate();
00146   NS_IMETHOD NotifyCanvasTMChanged();
00147   NS_IMETHOD NotifyRedrawSuspended();
00148   NS_IMETHOD NotifyRedrawUnsuspended();
00149   NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
00150   NS_IMETHOD GetBBox(nsIDOMSVGRect **_retval);
00151   
00152   // nsISVGContainerFrame interface:
00153   nsISVGOuterSVGFrame *GetOuterSVGFrame();
00154   already_AddRefed<nsIDOMSVGMatrix> GetCanvasTM();
00155   already_AddRefed<nsSVGCoordCtxProvider> GetCoordContextProvider();
00156   
00157 protected:
00158   // implementation helpers:
00159   void Update();
00160   already_AddRefed<nsISVGRendererRegion> DoReflow();
00161   float GetPxPerTwips();
00162   float GetTwipsPerPx();
00163   void TransformPoint(float& x, float& y);
00164   void TransformVector(float& x, float& y);
00165 
00166   PRBool mIsDirty;
00167   nsCOMPtr<nsIDOMSVGLength> mX;
00168   nsCOMPtr<nsIDOMSVGLength> mY;
00169   nsCOMPtr<nsIDOMSVGLength> mWidth;
00170   nsCOMPtr<nsIDOMSVGLength> mHeight;
00171   nsCOMPtr<nsIDOMSVGMatrix> mCanvasTM;
00172   PRBool mPropagateTransform;
00173 };
00174 
00175 //----------------------------------------------------------------------
00176 // Implementation
00177 
00178 nsresult
00179 NS_NewSVGForeignObjectFrame(nsIPresShell* aPresShell, nsIContent* aContent, nsIFrame** aNewFrame)
00180 {
00181   *aNewFrame = nsnull;
00182   
00183   nsCOMPtr<nsIDOMSVGForeignObjectElement> foreignObject = do_QueryInterface(aContent);
00184   if (!foreignObject) {
00185 #ifdef DEBUG
00186     printf("warning: trying to construct an SVGForeignObjectFrame for a content element that doesn't support the right interfaces\n");
00187 #endif
00188     return NS_ERROR_FAILURE;
00189   }
00190   
00191   nsSVGForeignObjectFrame* it = new (aPresShell) nsSVGForeignObjectFrame;
00192   if (nsnull == it)
00193     return NS_ERROR_OUT_OF_MEMORY;
00194 
00195   *aNewFrame = it;
00196 
00197   return NS_OK;
00198 }
00199 
00200 nsSVGForeignObjectFrame::nsSVGForeignObjectFrame()
00201   : mIsDirty(PR_TRUE), mPropagateTransform(PR_TRUE)
00202 {
00203 }
00204 
00205 nsSVGForeignObjectFrame::~nsSVGForeignObjectFrame()
00206 {
00207 //   nsCOMPtr<nsIDOMSVGTransformable> transformable = do_QueryInterface(mContent);
00208 //   NS_ASSERTION(transformable, "wrong content element");
00209 //   nsCOMPtr<nsIDOMSVGAnimatedTransformList> transforms;
00210 //   transformable->GetTransform(getter_AddRefs(transforms));
00211 //   nsCOMPtr<nsISVGValue> value = do_QueryInterface(transforms);
00212 //   NS_ASSERTION(value, "interface not found");
00213 //   if (value)
00214 //     value->RemoveObserver(this);
00215   nsCOMPtr<nsISVGValue> value;
00216   if (mX && (value = do_QueryInterface(mX)))
00217       value->RemoveObserver(this);
00218   if (mY && (value = do_QueryInterface(mY)))
00219       value->RemoveObserver(this);
00220   if (mWidth && (value = do_QueryInterface(mWidth)))
00221       value->RemoveObserver(this);
00222   if (mHeight && (value = do_QueryInterface(mHeight)))
00223       value->RemoveObserver(this);
00224 }
00225 
00226 nsresult nsSVGForeignObjectFrame::Init()
00227 {
00228   nsCOMPtr<nsIDOMSVGForeignObjectElement> foreignObject = do_QueryInterface(mContent);
00229   NS_ASSERTION(foreignObject, "wrong content element");
00230   
00231   {
00232     nsCOMPtr<nsIDOMSVGAnimatedLength> length;
00233     foreignObject->GetX(getter_AddRefs(length));
00234     length->GetAnimVal(getter_AddRefs(mX));
00235     NS_ASSERTION(mX, "no x");
00236     if (!mX) return NS_ERROR_FAILURE;
00237     nsCOMPtr<nsISVGValue> value = do_QueryInterface(mX);
00238     if (value)
00239       value->AddObserver(this);
00240   }
00241 
00242   {
00243     nsCOMPtr<nsIDOMSVGAnimatedLength> length;
00244     foreignObject->GetY(getter_AddRefs(length));
00245     length->GetAnimVal(getter_AddRefs(mY));
00246     NS_ASSERTION(mY, "no y");
00247     if (!mY) return NS_ERROR_FAILURE;
00248     nsCOMPtr<nsISVGValue> value = do_QueryInterface(mY);
00249     if (value)
00250       value->AddObserver(this);
00251   }
00252 
00253   {
00254     nsCOMPtr<nsIDOMSVGAnimatedLength> length;
00255     foreignObject->GetWidth(getter_AddRefs(length));
00256     length->GetAnimVal(getter_AddRefs(mWidth));
00257     NS_ASSERTION(mWidth, "no width");
00258     if (!mWidth) return NS_ERROR_FAILURE;
00259     nsCOMPtr<nsISVGValue> value = do_QueryInterface(mWidth);
00260     if (value)
00261       value->AddObserver(this);
00262   }
00263 
00264   {
00265     nsCOMPtr<nsIDOMSVGAnimatedLength> length;
00266     foreignObject->GetHeight(getter_AddRefs(length));
00267     length->GetAnimVal(getter_AddRefs(mHeight));
00268     NS_ASSERTION(mHeight, "no height");
00269     if (!mHeight) return NS_ERROR_FAILURE;
00270     nsCOMPtr<nsISVGValue> value = do_QueryInterface(mHeight);
00271     if (value)
00272       value->AddObserver(this);
00273   }
00274   
00275 // XXX 
00276 //   nsCOMPtr<nsIDOMSVGTransformable> transformable = do_QueryInterface(mContent);
00277 //   NS_ASSERTION(transformable, "wrong content element");
00278 //   nsCOMPtr<nsIDOMSVGAnimatedTransformList> transforms;
00279 //   transformable->GetTransform(getter_AddRefs(transforms));
00280 //   nsCOMPtr<nsISVGValue> value = do_QueryInterface(transforms);
00281 //   NS_ASSERTION(value, "interface not found");
00282 //   if (value)
00283 //     value->AddObserver(this);
00284 
00285   // XXX for some reason updating fails when done here. Why is this too early?
00286   // anyway - we use a less desirable mechanism now of updating in paint().
00287 //  Update(); 
00288   
00289   return NS_OK;
00290 }
00291 
00292 //----------------------------------------------------------------------
00293 // nsISupports methods
00294 
00295 NS_INTERFACE_MAP_BEGIN(nsSVGForeignObjectFrame)
00296   NS_INTERFACE_MAP_ENTRY(nsISVGChildFrame)
00297   NS_INTERFACE_MAP_ENTRY(nsISVGContainerFrame)
00298   NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
00299   NS_INTERFACE_MAP_ENTRY(nsISVGValueObserver)
00300 NS_INTERFACE_MAP_END_INHERITING(nsSVGForeignObjectFrameBase)
00301 
00302 
00303 //----------------------------------------------------------------------
00304 // nsIFrame methods
00305 NS_IMETHODIMP
00306 nsSVGForeignObjectFrame::Init(nsPresContext*  aPresContext,
00307                   nsIContent*      aContent,
00308                   nsIFrame*        aParent,
00309                   nsStyleContext*  aContext,
00310                   nsIFrame*        aPrevInFlow)
00311 {
00312   nsresult rv;
00313   rv = nsSVGForeignObjectFrameBase::Init(aPresContext, aContent, aParent,
00314                              aContext, aPrevInFlow);
00315 
00316   Init();
00317 
00318   return rv;
00319 }
00320 
00321 NS_IMETHODIMP
00322 nsSVGForeignObjectFrame::Reflow(nsPresContext*          aPresContext,
00323                                 nsHTMLReflowMetrics&     aDesiredSize,
00324                                 const nsHTMLReflowState& aReflowState,
00325                                 nsReflowStatus&          aStatus)
00326 {
00327 #ifdef DEBUG
00328   printf("nsSVGForeignObjectFrame(%p)::Reflow\n", this);
00329 #endif
00330   
00331   float twipsPerPx = GetTwipsPerPx();
00332   
00333   NS_ENSURE_TRUE(mX && mY && mWidth && mHeight, NS_ERROR_FAILURE);
00334 
00335   float x, y, width, height;
00336   mX->GetValue(&x);
00337   mY->GetValue(&y);
00338   mWidth->GetValue(&width);
00339   mHeight->GetValue(&height);
00340 
00341   // transform x,y,width,height according to the current canvastm:
00342   // XXX we're ignoring rotation at the moment
00343 
00344   // (x, y): (left, top) -> (center_x, center_y)
00345   x+=width/2.0f;
00346   y+=height/2.0f;
00347 
00348   // (x, y): (cx, cy) -> (cx', cy')
00349   TransformPoint(x, y);
00350   
00351   // find new ex & ey unit vectors
00352   float e1x = 1.0f, e1y = 0.0f, e2x = 0.0f, e2y = 1.0f;
00353   TransformVector(e1x, e1y);
00354   TransformVector(e2x, e2y);
00355   // adopt the scale of them for (w,h)
00356   width  *= (float)sqrt(e1x*e1x + e1y*e1y);
00357   height *= (float)sqrt(e2x*e2x + e2y*e2y);
00358   
00359   // (x, y): (cx', cy') -> (left', top')
00360   x -= width/2.0f;
00361   y -= height/2.0f;
00362 
00363   // move ourselves to (x,y):
00364   SetPosition(nsPoint((nscoord) (x*twipsPerPx), (nscoord) (y*twipsPerPx)));
00365   // Xxx: if zewe have a view, move that 
00366   
00367   // create a new reflow state, setting our max size to (width,height):
00368   nsSize availableSpace((nscoord)(width*twipsPerPx), (nscoord)(height*twipsPerPx));
00369   nsHTMLReflowState sizedReflowState(aPresContext,
00370                                      aReflowState,
00371                                      this,
00372                                      availableSpace);
00373 
00374   // XXX Not sure the dirty-state should be cleared here. We should
00375   // re-examine how the reflow is driven from nsSVGOuterSVGFrame. I
00376   // think we're missing a call to DidReflow somewhere ?
00377   mState &= ~NS_FRAME_IS_DIRTY;
00378   
00379   // leverage our base class' reflow function to do all the work:
00380   return nsSVGForeignObjectFrameBase::Reflow(aPresContext, aDesiredSize,
00381                                              sizedReflowState, aStatus);
00382 }
00383 
00384 NS_IMETHODIMP
00385 nsSVGForeignObjectFrame::AppendFrames(nsIAtom*        aListName,
00386                                       nsIFrame*       aFrameList)
00387 {
00388 #ifdef DEBUG
00389   printf("**nsSVGForeignObjectFrame::AppendFrames()\n");
00390 #endif
00391        nsresult rv;
00392        rv = nsSVGForeignObjectFrameBase::AppendFrames(aListName, aFrameList);
00393        Update();
00394        return rv;
00395 }
00396 
00397 NS_IMETHODIMP
00398 nsSVGForeignObjectFrame::InsertFrames(nsIAtom*        aListName,
00399                                       nsIFrame*       aPrevFrame,
00400                                       nsIFrame*       aFrameList)
00401 {
00402 #ifdef DEBUG
00403   printf("**nsSVGForeignObjectFrame::InsertFrames()\n");
00404 #endif
00405        nsresult rv;
00406        rv = nsSVGForeignObjectFrameBase::InsertFrames(aListName, aPrevFrame,
00407                                                    aFrameList);
00408        Update();
00409        return rv;
00410 }
00411 
00412 NS_IMETHODIMP
00413 nsSVGForeignObjectFrame::RemoveFrame(nsIAtom*        aListName,
00414                                      nsIFrame*       aOldFrame)
00415 {
00416        nsresult rv;
00417        rv = nsSVGForeignObjectFrameBase::RemoveFrame(aListName, aOldFrame);
00418        Update();
00419        return rv;
00420 }
00421 
00422 NS_IMETHODIMP
00423 nsSVGForeignObjectFrame::ReplaceFrame(nsIAtom*        aListName,
00424                                       nsIFrame*       aOldFrame,
00425                                       nsIFrame*       aNewFrame)
00426 {
00427 #ifdef DEBUG
00428   printf("**nsSVGForeignObjectFrame::ReplaceFrame()\n");
00429 #endif
00430        nsresult rv;
00431        rv = nsSVGForeignObjectFrameBase::ReplaceFrame(aListName, aOldFrame,
00432                                                    aNewFrame);
00433        Update();
00434        return rv;
00435 }
00436 
00437 // XXX Need to make sure that any of the code examining
00438 // frametypes, particularly code looking at block and area
00439 // also handles foreignObject before we return our own frametype
00440 // nsIAtom *
00441 // nsSVGForeignObjectFrame::GetType() const
00442 // {
00443 //   return nsLayoutAtoms::svgForeignObjectFrame;
00444 // }
00445 
00446 PRBool
00447 nsSVGForeignObjectFrame::IsFrameOfType(PRUint32 aFlags) const
00448 {
00449   return !(aFlags & ~(nsIFrame::eSVG | nsIFrame::eSVGForeignObject));
00450 }
00451 
00452 //----------------------------------------------------------------------
00453 // nsISVGValueObserver methods:
00454 
00455 NS_IMETHODIMP
00456 nsSVGForeignObjectFrame::WillModifySVGObservable(nsISVGValue* observable,
00457                                                  nsISVGValue::modificationType aModType)
00458 {
00459   return NS_OK;
00460 }
00461 
00462 
00463 NS_IMETHODIMP
00464 nsSVGForeignObjectFrame::DidModifySVGObservable (nsISVGValue* observable,
00465                                                  nsISVGValue::modificationType aModType)
00466 {
00467   Update();
00468   
00469   return NS_OK;
00470 }
00471 
00472 
00473 //----------------------------------------------------------------------
00474 // nsISVGChildFrame methods
00475 
00476 NS_IMETHODIMP
00477 nsSVGForeignObjectFrame::PaintSVG(nsISVGRendererCanvas* canvas, const nsRect& dirtyRectTwips)
00478 {
00479   if (mIsDirty) {
00480     nsCOMPtr<nsISVGRendererRegion> region = DoReflow();
00481   }
00482 
00483   nsPresContext *presContext = GetPresContext();
00484 
00485   nsRect r(mRect);
00486   if (!r.IntersectRect(dirtyRectTwips, r))
00487     return PR_TRUE;
00488   
00489   float pxPerTwips = GetPxPerTwips();
00490   r.x*=pxPerTwips;
00491   r.y*=pxPerTwips;
00492   r.width*=pxPerTwips;
00493   r.height*=pxPerTwips;
00494   
00495   nsCOMPtr<nsIRenderingContext> ctx;
00496   canvas->LockRenderingContext(r, getter_AddRefs(ctx));
00497 
00498   if (!ctx) {
00499     NS_WARNING("Can't render foreignObject element!");
00500     return NS_ERROR_FAILURE;
00501   }
00502   
00503   nsRect dirtyRect = dirtyRectTwips;
00504   dirtyRect.x -= mRect.x;
00505   dirtyRect.y -= mRect.y;
00506 
00507   // As described in nsContainerFrame, don't use PushState/PopState;
00508   // instead save/restore the modified transform components:
00509   float xMatrix;
00510   float yMatrix;
00511   nsTransform2D *theTransform;
00512   ctx->GetCurrentTransform(theTransform);
00513   theTransform->GetTranslation(&xMatrix, &yMatrix);
00514   ctx->Translate(mRect.x, mRect.y);
00515   
00516   nsSVGForeignObjectFrameBase::Paint(presContext,
00517                                      *ctx,
00518                                      dirtyRect,
00519                                      NS_FRAME_PAINT_LAYER_BACKGROUND,
00520                                      0);
00521   
00522   nsSVGForeignObjectFrameBase::Paint(presContext,
00523                                      *ctx,
00524                                      dirtyRect,
00525                                      NS_FRAME_PAINT_LAYER_FLOATS,
00526                                      0);
00527   
00528   nsSVGForeignObjectFrameBase::Paint(presContext,
00529                                      *ctx,
00530                                      dirtyRect,
00531                                      NS_FRAME_PAINT_LAYER_FOREGROUND,
00532                                      0);
00533 
00534   theTransform->SetTranslation(xMatrix, yMatrix);
00535   ctx = nsnull;
00536   canvas->UnlockRenderingContext();
00537   
00538   return NS_OK;
00539 }
00540 
00541 
00542 NS_IMETHODIMP
00543 nsSVGForeignObjectFrame::GetFrameForPointSVG(float x, float y, nsIFrame** hit)
00544 {
00545   *hit = nsnull;
00546 
00547   nsPoint p( (nscoord)(x*GetTwipsPerPx()),
00548              (nscoord)(y*GetTwipsPerPx()));
00549 
00550   nsresult rv;
00551 
00552   rv = nsSVGForeignObjectFrameBase::GetFrameForPoint(p,
00553                                                NS_FRAME_PAINT_LAYER_FOREGROUND,
00554                                                hit);
00555   if (NS_SUCCEEDED(rv) && *hit) return rv;
00556 
00557   rv = nsSVGForeignObjectFrameBase::GetFrameForPoint(p,
00558                                                NS_FRAME_PAINT_LAYER_FLOATS,
00559                                                hit);
00560   if (NS_SUCCEEDED(rv) && *hit) return rv;
00561 
00562   return nsSVGForeignObjectFrameBase::GetFrameForPoint(p,
00563                                                NS_FRAME_PAINT_LAYER_BACKGROUND,
00564                                                hit);
00565 }
00566 
00567 NS_IMETHODIMP_(already_AddRefed<nsISVGRendererRegion>)
00568 nsSVGForeignObjectFrame::GetCoveredRegion()
00569 {
00570   // get a region from our mRect
00571   
00572   //  if (mRect.width==0 || mRect.height==0) return nsnull;
00573   nsISVGOuterSVGFrame *outerSVGFrame = GetOuterSVGFrame();
00574   if (!outerSVGFrame) {
00575     NS_ERROR("null outerSVGFrame");
00576     return nsnull;
00577   }
00578   
00579   nsCOMPtr<nsISVGRenderer> renderer;
00580   outerSVGFrame->GetRenderer(getter_AddRefs(renderer));
00581   
00582   float pxPerTwips = GetPxPerTwips();
00583 
00584   nsISVGRendererRegion *region = nsnull;
00585   renderer->CreateRectRegion((mRect.x-1) * pxPerTwips,
00586                              (mRect.y-1) * pxPerTwips,
00587                              (mRect.width+2) * pxPerTwips,
00588                              (mRect.height+2) * pxPerTwips,
00589                              &region);
00590   NS_ASSERTION(region, "could not create region");
00591   return region;
00592   
00593 }
00594 
00595 NS_IMETHODIMP
00596 nsSVGForeignObjectFrame::InitialUpdate()
00597 {
00598 //  Update();
00599   return NS_OK;
00600 }
00601 
00602 NS_IMETHODIMP
00603 nsSVGForeignObjectFrame::NotifyCanvasTMChanged()
00604 {
00605   mCanvasTM = nsnull;
00606   Update();
00607   return NS_OK;
00608 }
00609 
00610 NS_IMETHODIMP
00611 nsSVGForeignObjectFrame::NotifyRedrawSuspended()
00612 {
00613   return NS_OK;
00614 }
00615 
00616 NS_IMETHODIMP
00617 nsSVGForeignObjectFrame::NotifyRedrawUnsuspended()
00618 {
00619   if (mIsDirty) {
00620     nsCOMPtr<nsISVGRendererRegion> dirtyRegion = DoReflow();
00621     if (dirtyRegion) {
00622       nsISVGOuterSVGFrame *outerSVGFrame = GetOuterSVGFrame();
00623       if (outerSVGFrame)
00624         outerSVGFrame->InvalidateRegion(dirtyRegion, PR_TRUE);
00625     }
00626   }
00627   return NS_OK;
00628 }
00629 
00630 NS_IMETHODIMP
00631 nsSVGForeignObjectFrame::SetMatrixPropagation(PRBool aPropagate)
00632 {
00633   mPropagateTransform = aPropagate;
00634   return NS_OK;
00635 }
00636 
00637 NS_IMETHODIMP
00638 nsSVGForeignObjectFrame::GetBBox(nsIDOMSVGRect **_retval)
00639 {
00640   *_retval = nsnull;
00641 
00642   float x[4], y[4], width, height;
00643   mX->GetValue(&x[0]);
00644   mY->GetValue(&y[0]);
00645   mWidth->GetValue(&width);
00646   mHeight->GetValue(&height);
00647 
00648   x[1] = x[0] + width;
00649   y[1] = y[0];
00650   x[2] = x[0] + width;
00651   y[2] = y[0] + height;
00652   x[3] = x[0];
00653   y[3] = y[0] + height;
00654 
00655   TransformPoint(x[0], y[0]);
00656   TransformPoint(x[1], y[1]);
00657   TransformPoint(x[2], y[2]);
00658   TransformPoint(x[3], y[3]);
00659 
00660   float xmin, xmax, ymin, ymax;
00661   xmin = xmax = x[0];
00662   ymin = ymax = y[0];
00663   for (int i=1; i<4; i++) {
00664     if (x[i] < xmin)
00665       xmin = x[i];
00666     if (y[i] < ymin)
00667       ymin = y[i];
00668     if (x[i] > xmax)
00669       xmax = x[i];
00670     if (y[i] > ymax)
00671       ymax = y[i];
00672   }
00673 
00674   return NS_NewSVGRect(_retval, xmin, ymin, xmax - xmin, ymax - ymin);
00675 }
00676 
00677 //----------------------------------------------------------------------
00678 // nsISVGContainerFrame methods:
00679 
00680 nsISVGOuterSVGFrame *
00681 nsSVGForeignObjectFrame::GetOuterSVGFrame()
00682 {
00683   NS_ASSERTION(mParent, "null parent");
00684 
00685   nsISVGContainerFrame *containerFrame;
00686   mParent->QueryInterface(NS_GET_IID(nsISVGContainerFrame), (void**)&containerFrame);
00687   if (!containerFrame) {
00688     NS_ERROR("invalid parent");
00689     return nsnull;
00690   }
00691   
00692   return containerFrame->GetOuterSVGFrame();  
00693 }
00694 
00695 already_AddRefed<nsIDOMSVGMatrix>
00696 nsSVGForeignObjectFrame::GetCanvasTM()
00697 {
00698   if (!mPropagateTransform) {
00699     nsIDOMSVGMatrix *retval;
00700     NS_NewSVGMatrix(&retval);
00701     return retval;
00702   }
00703 
00704   if (!mCanvasTM) {
00705     // get our parent's tm and append local transforms (if any):
00706     NS_ASSERTION(mParent, "null parent");
00707     nsISVGContainerFrame *containerFrame;
00708     mParent->QueryInterface(NS_GET_IID(nsISVGContainerFrame), (void**)&containerFrame);
00709     if (!containerFrame) {
00710       NS_ERROR("invalid parent");
00711       return nsnull;
00712     }
00713     nsCOMPtr<nsIDOMSVGMatrix> parentTM = containerFrame->GetCanvasTM();
00714     NS_ASSERTION(parentTM, "null TM");
00715 
00716     // got the parent tm, now check for local tm:
00717     nsCOMPtr<nsIDOMSVGMatrix> localTM;
00718     {
00719       nsCOMPtr<nsIDOMSVGTransformable> transformable = do_QueryInterface(mContent);
00720       NS_ASSERTION(transformable, "wrong content element");
00721       nsCOMPtr<nsIDOMSVGAnimatedTransformList> atl;
00722       transformable->GetTransform(getter_AddRefs(atl));
00723       NS_ASSERTION(atl, "null animated transform list");
00724       nsCOMPtr<nsIDOMSVGTransformList> transforms;
00725       atl->GetAnimVal(getter_AddRefs(transforms));
00726       NS_ASSERTION(transforms, "null transform list");
00727       PRUint32 numberOfItems;
00728       transforms->GetNumberOfItems(&numberOfItems);
00729       if (numberOfItems>0)
00730         transforms->GetConsolidationMatrix(getter_AddRefs(localTM));
00731     }
00732     
00733     if (localTM)
00734       parentTM->Multiply(localTM, getter_AddRefs(mCanvasTM));
00735     else
00736       mCanvasTM = parentTM;
00737   }
00738 
00739   nsIDOMSVGMatrix* retval = mCanvasTM.get();
00740   NS_IF_ADDREF(retval);
00741   return retval;
00742 }
00743 
00744 already_AddRefed<nsSVGCoordCtxProvider>
00745 nsSVGForeignObjectFrame::GetCoordContextProvider()
00746 {
00747   NS_ASSERTION(mParent, "null parent");
00748   
00749   nsISVGContainerFrame *containerFrame;
00750   mParent->QueryInterface(NS_GET_IID(nsISVGContainerFrame), (void**)&containerFrame);
00751   if (!containerFrame) {
00752     NS_ERROR("invalid container");
00753     return nsnull;
00754   }
00755 
00756   return containerFrame->GetCoordContextProvider();  
00757 }
00758 
00759 
00760 //----------------------------------------------------------------------
00761 // Implementation helpers
00762 
00763 void nsSVGForeignObjectFrame::Update()
00764 {
00765 #ifdef DEBUG
00766   printf("**nsSVGForeignObjectFrame::Update()\n");
00767 #endif
00768 
00769   mIsDirty = PR_TRUE;
00770 
00771   nsISVGOuterSVGFrame *outerSVGFrame = GetOuterSVGFrame();
00772   if (!outerSVGFrame) {
00773     NS_ERROR("null outerSVGFrame");
00774     return;
00775   }
00776   
00777   PRBool suspended;
00778   outerSVGFrame->IsRedrawSuspended(&suspended);
00779   if (!suspended) {
00780     nsCOMPtr<nsISVGRendererRegion> dirtyRegion = DoReflow();
00781     if (dirtyRegion) {
00782       outerSVGFrame->InvalidateRegion(dirtyRegion, PR_TRUE);
00783     }
00784   }  
00785 }
00786 
00787 already_AddRefed<nsISVGRendererRegion>
00788 nsSVGForeignObjectFrame::DoReflow()
00789 {
00790 #ifdef DEBUG
00791   printf("**nsSVGForeignObjectFrame::DoReflow()\n");
00792 #endif
00793 
00794   nsPresContext *presContext = GetPresContext();
00795 
00796   // remember the area we have to invalidate after this reflow:
00797   nsCOMPtr<nsISVGRendererRegion> area_before = GetCoveredRegion();
00798   NS_ASSERTION(area_before, "could not get covered region");
00799   
00800   // initiate a synchronous reflow here and now:  
00801   nsSize availableSpace(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
00802   nsCOMPtr<nsIRenderingContext> renderingContext;
00803   nsIPresShell* presShell = presContext->PresShell();
00804   NS_ASSERTION(presShell, "null presShell");
00805   presShell->CreateRenderingContext(this,getter_AddRefs(renderingContext));
00806   
00807   // XXX we always pass this off as an initial reflow. is that a problem?
00808   nsHTMLReflowState reflowState(presContext, this, eReflowReason_Initial,
00809                                 renderingContext, availableSpace);
00810 
00811   nsSpaceManager* spaceManager = new nsSpaceManager(presShell, this);
00812   if (!spaceManager) {
00813     NS_ERROR("Could not create space manager");
00814     return nsnull;
00815   }
00816   reflowState.mSpaceManager = spaceManager;
00817    
00818   nsHTMLReflowMetrics desiredSize(nsnull);
00819   nsReflowStatus status;
00820   
00821   WillReflow(presContext);
00822   Reflow(presContext, desiredSize, reflowState, status);
00823   SetSize(nsSize(desiredSize.width, desiredSize.height));
00824   DidReflow(presContext, &reflowState, NS_FRAME_REFLOW_FINISHED);
00825 
00826   mIsDirty = PR_FALSE;
00827 
00828   nsCOMPtr<nsISVGRendererRegion> area_after = GetCoveredRegion();
00829   nsISVGRendererRegion *dirtyRegion;
00830   area_before->Combine(area_after, &dirtyRegion);
00831 
00832   return dirtyRegion;
00833 }
00834 
00835 float nsSVGForeignObjectFrame::GetPxPerTwips()
00836 {
00837   float val = GetTwipsPerPx();
00838   
00839   NS_ASSERTION(val!=0.0f, "invalid px/twips");  
00840   if (val == 0.0) val = 1e-20f;
00841   
00842   return 1.0f/val;
00843 }
00844 
00845 float nsSVGForeignObjectFrame::GetTwipsPerPx()
00846 {
00847   return GetPresContext()->ScaledPixelsToTwips();
00848 }
00849 
00850 void nsSVGForeignObjectFrame::TransformPoint(float& x, float& y)
00851 {
00852   nsCOMPtr<nsIDOMSVGMatrix> ctm = GetCanvasTM();
00853   if (!ctm)
00854     return;
00855 
00856   // XXX this is absurd! we need to add another method (interface
00857   // even?) to nsIDOMSVGMatrix to make this easier. (something like
00858   // nsIDOMSVGMatrix::TransformPoint(float*x,float*y))
00859   
00860   nsCOMPtr<nsIDOMSVGPoint> point;
00861   NS_NewSVGPoint(getter_AddRefs(point), x, y);
00862   if (!point)
00863     return;
00864 
00865   nsCOMPtr<nsIDOMSVGPoint> xfpoint;
00866   point->MatrixTransform(ctm, getter_AddRefs(xfpoint));
00867   if (!xfpoint)
00868     return;
00869 
00870   xfpoint->GetX(&x);
00871   xfpoint->GetY(&y);
00872 }
00873 
00874 void nsSVGForeignObjectFrame::TransformVector(float& x, float& y)
00875 {
00876   // XXX This is crazy. What we really want is
00877   // nsIDOMSVGMatrix::TransformVector(x,y);
00878   
00879   float x0=0.0f, y0=0.0f;
00880   TransformPoint(x0, y0);
00881   TransformPoint(x,y);
00882   x -= x0;
00883   y -= y0;
00884 }
00885 
00886 
00887