Back to index

lightning-sunbird  0.9+nobinonly
nsImageMap.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 mozilla.org code.
00016  *
00017  * The Initial Developer of the Original Code is
00018  * Netscape Communications Corporation.
00019  * Portions created by the Initial Developer are Copyright (C) 1998
00020  * the Initial Developer. All Rights Reserved.
00021  *
00022  * Contributor(s):
00023  *   Mats Palmgren <mats.palmgren@bredband.net>
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 #include "nsImageMap.h"
00039 #include "nsString.h"
00040 #include "nsReadableUtils.h"
00041 #include "nsVoidArray.h"
00042 #include "nsIRenderingContext.h"
00043 #include "nsPresContext.h"
00044 #include "nsIURL.h"
00045 #include "nsIURL.h"
00046 #include "nsIServiceManager.h"
00047 #include "nsNetUtil.h"
00048 #include "nsTextFragment.h"
00049 #include "nsIContent.h"
00050 #include "nsIDOMHTMLElement.h"
00051 #include "nsIDOMHTMLMapElement.h"
00052 #include "nsIDOMHTMLAreaElement.h"
00053 #include "nsIDOMHTMLAnchorElement.h"
00054 #include "nsIDOMHTMLCollection.h"
00055 #include "nsIDocument.h"
00056 #include "nsINameSpaceManager.h"
00057 #include "nsHTMLAtoms.h"
00058 #include "nsIDOMEventReceiver.h"
00059 #include "nsIPresShell.h"
00060 #include "nsIFrame.h"
00061 #include "nsFrameManager.h"
00062 #include "nsIViewManager.h"
00063 #include "nsCoord.h"
00064 #include "nsIImageMap.h"
00065 #include "nsIConsoleService.h"
00066 #include "nsIScriptError.h"
00067 #include "nsIStringBundle.h"
00068 #include "nsIDocument.h"
00069 #include "nsContentUtils.h"
00070 
00071 static NS_DEFINE_CID(kCStringBundleServiceCID, NS_STRINGBUNDLESERVICE_CID);
00072 
00073 class Area {
00074 public:
00075   Area(nsIContent* aArea);
00076   virtual ~Area();
00077 
00078   virtual void ParseCoords(const nsAString& aSpec);
00079 
00080   virtual PRBool IsInside(nscoord x, nscoord y) const = 0;
00081   virtual void Draw(nsPresContext* aCX,
00082                     nsIRenderingContext& aRC) = 0;
00083   virtual void GetRect(nsPresContext* aCX, nsRect& aRect) = 0;
00084 
00085   void HasFocus(PRBool aHasFocus);
00086 
00087   void GetHREF(nsAString& aHref) const;
00088   void GetArea(nsIContent** aArea) const;
00089 
00090   nsCOMPtr<nsIContent> mArea;
00091   nscoord* mCoords;
00092   PRInt32 mNumCoords;
00093   PRPackedBool mHasFocus;
00094 };
00095 
00096 MOZ_DECL_CTOR_COUNTER(Area)
00097 
00098 Area::Area(nsIContent* aArea)
00099   : mArea(aArea)
00100 {
00101   MOZ_COUNT_CTOR(Area);
00102   mCoords = nsnull;
00103   mNumCoords = 0;
00104   mHasFocus = PR_FALSE;
00105 }
00106 
00107 Area::~Area()
00108 {
00109   MOZ_COUNT_DTOR(Area);
00110   delete [] mCoords;
00111 }
00112 
00113 void 
00114 Area::GetHREF(nsAString& aHref) const
00115 {
00116   aHref.Truncate();
00117   if (mArea) {
00118     mArea->GetAttr(kNameSpaceID_None, nsHTMLAtoms::href, aHref);
00119   }
00120 }
00121  
00122 void 
00123 Area::GetArea(nsIContent** aArea) const
00124 {
00125   *aArea = mArea;
00126   NS_IF_ADDREF(*aArea);
00127 }
00128 
00129 #include <stdlib.h>
00130 
00131 inline PRBool
00132 is_space(char c)
00133 {
00134   return (c == ' ' ||
00135           c == '\f' ||
00136           c == '\n' ||
00137           c == '\r' ||
00138           c == '\t' ||
00139           c == '\v');
00140 }
00141 
00142 static void logMessage(nsIContent*      aContent,
00143                        const nsAString& aCoordsSpec,
00144                        PRInt32          aFlags,
00145                        const char* aMessageName) {
00146   nsIURI* documentURI = nsnull;
00147   nsIDocument* doc = aContent->GetOwnerDoc();
00148   if (doc) {
00149     documentURI = doc->GetDocumentURI();
00150   }
00151   nsContentUtils::ReportToConsole(
00152      nsContentUtils::eLAYOUT_PROPERTIES,
00153      aMessageName,
00154      nsnull,  /* params */
00155      0, /* params length */
00156      documentURI,
00157      PromiseFlatString(NS_LITERAL_STRING("coords=\"") +
00158                        aCoordsSpec +
00159                        NS_LITERAL_STRING("\"")), /* source line */
00160      0, /* line number */
00161      0, /* column number */
00162      aFlags,
00163      "ImageMap");
00164 }
00165 
00166 // XXX straight copy from laymap.c
00167 static nscoord* lo_parse_coord_list(char *str, PRInt32* value_cnt)
00168 {
00169   char *tptr;
00170   char *n_str;
00171   PRInt32 i, cnt;
00172   PRInt32 *value_list;
00173 
00174   /*
00175    * Nothing in an empty list
00176    */
00177   *value_cnt = 0;
00178   if (!str || *str == '\0')
00179   {
00180     return nsnull;
00181   }
00182 
00183   /*
00184    * Skip beginning whitespace, all whitespace is empty list.
00185    */
00186   n_str = str;
00187   while (is_space(*n_str))
00188   {
00189     n_str++;
00190   }
00191   if (*n_str == '\0')
00192   {
00193     return nsnull;
00194   }
00195 
00196   /*
00197    * Make a pass where any two numbers separated by just whitespace
00198    * are given a comma separator.  Count entries while passing.
00199    */
00200   cnt = 0;
00201   while (*n_str != '\0')
00202   {
00203     PRBool has_comma;
00204 
00205     /*
00206      * Skip to a separator
00207      */
00208     tptr = n_str;
00209     while (!is_space(*tptr) && *tptr != ',' && *tptr != '\0')
00210     {
00211       tptr++;
00212     }
00213     n_str = tptr;
00214 
00215     /*
00216      * If no more entries, break out here
00217      */
00218     if (*n_str == '\0')
00219     {
00220       break;
00221     }
00222 
00223     /*
00224      * Skip to the end of the separator, noting if we have a
00225      * comma.
00226      */
00227     has_comma = PR_FALSE;
00228     while (is_space(*tptr) || *tptr == ',')
00229     {
00230       if (*tptr == ',')
00231       {
00232         if (has_comma == PR_FALSE)
00233         {
00234           has_comma = PR_TRUE;
00235         }
00236         else
00237         {
00238           break;
00239         }
00240       }
00241       tptr++;
00242     }
00243     /*
00244      * If this was trailing whitespace we skipped, we are done.
00245      */
00246     if ((*tptr == '\0')&&(has_comma == PR_FALSE))
00247     {
00248       break;
00249     }
00250     /*
00251      * Else if the separator is all whitespace, and this is not the
00252      * end of the string, add a comma to the separator.
00253      */
00254     else if (has_comma == PR_FALSE)
00255     {
00256       *n_str = ',';
00257     }
00258 
00259     /*
00260      * count the entry skipped.
00261      */
00262     cnt++;
00263 
00264     n_str = tptr;
00265   }
00266   /*
00267    * count the last entry in the list.
00268    */
00269   cnt++;
00270  
00271   /*
00272    * Allocate space for the coordinate array.
00273    */
00274   value_list = new nscoord[cnt];
00275   if (!value_list)
00276   {
00277     return nsnull;
00278   }
00279 
00280   /*
00281    * Second pass to copy integer values into list.
00282    */
00283   tptr = str;
00284   for (i=0; i<cnt; i++)
00285   {
00286     char *ptr;
00287 
00288     ptr = strchr(tptr, ',');
00289     if (ptr)
00290     {
00291       *ptr = '\0';
00292     }
00293     /*
00294      * Strip whitespace in front of number because I don't
00295      * trust atoi to do it on all platforms.
00296      */
00297     while (is_space(*tptr))
00298     {
00299       tptr++;
00300     }
00301     if (*tptr == '\0')
00302     {
00303       value_list[i] = 0;
00304     }
00305     else
00306     {
00307       value_list[i] = (nscoord) ::atoi(tptr);
00308     }
00309     if (ptr)
00310     {
00311       *ptr = ',';
00312       tptr = ptr + 1;
00313     }
00314   }
00315 
00316   *value_cnt = cnt;
00317   return value_list;
00318 }
00319 
00320 void Area::ParseCoords(const nsAString& aSpec)
00321 {
00322   char* cp = ToNewCString(aSpec);
00323   if (cp) {
00324     mCoords = lo_parse_coord_list(cp, &mNumCoords);
00325     NS_Free(cp);
00326   }
00327 }
00328 
00329 void Area::HasFocus(PRBool aHasFocus)
00330 {
00331   mHasFocus = aHasFocus;
00332 }
00333 
00334 //----------------------------------------------------------------------
00335 
00336 class DefaultArea : public Area {
00337 public:
00338   DefaultArea(nsIContent* aArea);
00339 
00340   virtual PRBool IsInside(nscoord x, nscoord y) const;
00341   virtual void Draw(nsPresContext* aCX,
00342                     nsIRenderingContext& aRC);
00343   virtual void GetRect(nsPresContext* aCX, nsRect& aRect);
00344 };
00345 
00346 DefaultArea::DefaultArea(nsIContent* aArea)
00347   : Area(aArea)
00348 {
00349 }
00350 
00351 PRBool DefaultArea::IsInside(nscoord x, nscoord y) const
00352 {
00353   return PR_TRUE;
00354 }
00355 
00356 void DefaultArea::Draw(nsPresContext* aCX, nsIRenderingContext& aRC)
00357 {
00358 }
00359 
00360 void DefaultArea::GetRect(nsPresContext* aCX, nsRect& aRect)
00361 {
00362 }
00363 
00364 //----------------------------------------------------------------------
00365 
00366 class RectArea : public Area {
00367 public:
00368   RectArea(nsIContent* aArea);
00369 
00370   virtual void ParseCoords(const nsAString& aSpec);
00371   virtual PRBool IsInside(nscoord x, nscoord y) const;
00372   virtual void Draw(nsPresContext* aCX,
00373                     nsIRenderingContext& aRC);
00374   virtual void GetRect(nsPresContext* aCX, nsRect& aRect);
00375 };
00376 
00377 RectArea::RectArea(nsIContent* aArea)
00378   : Area(aArea)
00379 {
00380 }
00381 
00382 void RectArea::ParseCoords(const nsAString& aSpec)
00383 {
00384   Area::ParseCoords(aSpec);
00385 
00386   PRBool saneRect = PR_TRUE;
00387   PRInt32 flag = nsIScriptError::warningFlag;
00388   if (mNumCoords >= 4) {
00389     if (mCoords[0] > mCoords[2]) {
00390       // x-coords in reversed order
00391       nscoord x = mCoords[2];
00392       mCoords[2] = mCoords[0];
00393       mCoords[0] = x;
00394       saneRect = PR_FALSE;
00395     }
00396   
00397     if (mCoords[1] > mCoords[3]) {
00398       // y-coords in reversed order
00399       nscoord y = mCoords[3];
00400       mCoords[3] = mCoords[1];
00401       mCoords[1] = y;
00402       saneRect = PR_FALSE;
00403     }
00404 
00405     if (mNumCoords > 4) {
00406       // Someone missed the concept of a rect here
00407       saneRect = PR_FALSE;
00408     }
00409   } else {
00410     saneRect = PR_FALSE;
00411     flag = nsIScriptError::errorFlag;
00412   }
00413 
00414   if (!saneRect) {
00415     logMessage(mArea, aSpec, flag, "ImageMapRectBoundsError");
00416   }
00417 }
00418 
00419 PRBool RectArea::IsInside(nscoord x, nscoord y) const
00420 {
00421   if (mNumCoords >= 4) {       // Note: > is for nav compatabilty
00422     nscoord x1 = mCoords[0];
00423     nscoord y1 = mCoords[1];
00424     nscoord x2 = mCoords[2];
00425     nscoord y2 = mCoords[3];
00426     NS_ASSERTION(x1 <= x2 && y1 <= y2,
00427                  "Someone screwed up RectArea::ParseCoords");
00428     if ((x >= x1) && (x <= x2) && (y >= y1) && (y <= y2)) {
00429       return PR_TRUE;
00430     }
00431   }
00432   return PR_FALSE;
00433 }
00434 
00435 void RectArea::Draw(nsPresContext* aCX, nsIRenderingContext& aRC)
00436 {
00437   if (mHasFocus) {
00438     if (mNumCoords >= 4) {
00439       float p2t;
00440       p2t = aCX->PixelsToTwips();
00441       nscoord x1 = NSIntPixelsToTwips(mCoords[0], p2t);
00442       nscoord y1 = NSIntPixelsToTwips(mCoords[1], p2t);
00443       nscoord x2 = NSIntPixelsToTwips(mCoords[2], p2t);
00444       nscoord y2 = NSIntPixelsToTwips(mCoords[3], p2t);
00445       NS_ASSERTION(x1 <= x2 && y1 <= y2,
00446                    "Someone screwed up RectArea::ParseCoords");
00447       aRC.DrawLine(x1, y1, x1, y2);
00448       aRC.DrawLine(x1, y2, x2, y2);
00449       aRC.DrawLine(x1, y1, x2, y1);
00450       aRC.DrawLine(x2, y1, x2, y2);
00451     }
00452   }
00453 }
00454 
00455 void RectArea::GetRect(nsPresContext* aCX, nsRect& aRect)
00456 {
00457   if (mNumCoords >= 4) {
00458     float p2t;
00459     p2t = aCX->PixelsToTwips();
00460     nscoord x1 = NSIntPixelsToTwips(mCoords[0], p2t);
00461     nscoord y1 = NSIntPixelsToTwips(mCoords[1], p2t);
00462     nscoord x2 = NSIntPixelsToTwips(mCoords[2], p2t);
00463     nscoord y2 = NSIntPixelsToTwips(mCoords[3], p2t);
00464     NS_ASSERTION(x1 <= x2 && y1 <= y2,
00465                  "Someone screwed up RectArea::ParseCoords");
00466 
00467     aRect.SetRect(x1, y1, x2, y2);
00468   }
00469 }
00470 
00471 //----------------------------------------------------------------------
00472 
00473 class PolyArea : public Area {
00474 public:
00475   PolyArea(nsIContent* aArea);
00476 
00477   virtual void ParseCoords(const nsAString& aSpec);
00478   virtual PRBool IsInside(nscoord x, nscoord y) const;
00479   virtual void Draw(nsPresContext* aCX,
00480                     nsIRenderingContext& aRC);
00481   virtual void GetRect(nsPresContext* aCX, nsRect& aRect);
00482 };
00483 
00484 PolyArea::PolyArea(nsIContent* aArea)
00485   : Area(aArea)
00486 {
00487 }
00488 
00489 void PolyArea::ParseCoords(const nsAString& aSpec)
00490 {
00491   Area::ParseCoords(aSpec);
00492 
00493   if (mNumCoords >= 2) {
00494     if (mNumCoords & 1U) {
00495       logMessage(mArea,
00496                  aSpec,
00497                  nsIScriptError::warningFlag,
00498                  "ImageMapPolyOddNumberOfCoords");
00499     }
00500   } else {
00501     logMessage(mArea,
00502                aSpec,
00503                nsIScriptError::errorFlag,
00504                "ImageMapPolyWrongNumberOfCoords");
00505   }
00506 }
00507 
00508 PRBool PolyArea::IsInside(nscoord x, nscoord y) const
00509 {
00510   if (mNumCoords >= 6) {
00511     PRInt32 intersects = 0;
00512     nscoord wherex = x;
00513     nscoord wherey = y;
00514     PRInt32 totalv = mNumCoords / 2;
00515     PRInt32 totalc = totalv * 2;
00516     nscoord xval = mCoords[totalc - 2];
00517     nscoord yval = mCoords[totalc - 1];
00518     PRInt32 end = totalc;
00519     PRInt32 pointer = 1;
00520 
00521     if ((yval >= wherey) != (mCoords[pointer] >= wherey))
00522       if ((xval >= wherex) == (mCoords[0] >= wherex))
00523         intersects += (xval >= wherex) ? 1 : 0;
00524       else
00525         intersects += ((xval - (yval - wherey) *
00526                         (mCoords[0] - xval) /
00527                         (mCoords[pointer] - yval)) >= wherex) ? 1 : 0;
00528 
00529     // XXX I wonder what this is doing; this is a translation of ptinpoly.c
00530     while (pointer < end)  {
00531       yval = mCoords[pointer];
00532       pointer += 2;
00533       if (yval >= wherey)  {
00534         while((pointer < end) && (mCoords[pointer] >= wherey))
00535           pointer+=2;
00536         if (pointer >= end)
00537           break;
00538         if ((mCoords[pointer-3] >= wherex) ==
00539             (mCoords[pointer-1] >= wherex)) {
00540           intersects += (mCoords[pointer-3] >= wherex) ? 1 : 0;
00541         } else {
00542           intersects +=
00543             ((mCoords[pointer-3] - (mCoords[pointer-2] - wherey) *
00544               (mCoords[pointer-1] - mCoords[pointer-3]) /
00545               (mCoords[pointer] - mCoords[pointer - 2])) >= wherex) ? 1:0;
00546         }
00547       }  else  {
00548         while((pointer < end) && (mCoords[pointer] < wherey))
00549           pointer+=2;
00550         if (pointer >= end)
00551           break;
00552         if ((mCoords[pointer-3] >= wherex) ==
00553             (mCoords[pointer-1] >= wherex)) {
00554           intersects += (mCoords[pointer-3] >= wherex) ? 1:0;
00555         } else {
00556           intersects +=
00557             ((mCoords[pointer-3] - (mCoords[pointer-2] - wherey) *
00558               (mCoords[pointer-1] - mCoords[pointer-3]) /
00559               (mCoords[pointer] - mCoords[pointer - 2])) >= wherex) ? 1:0;
00560         }
00561       }
00562     }
00563     if ((intersects & 1) != 0) {
00564       return PR_TRUE;
00565     }
00566   }
00567   return PR_FALSE;
00568 }
00569 
00570 void PolyArea::Draw(nsPresContext* aCX, nsIRenderingContext& aRC)
00571 {
00572   if (mHasFocus) {
00573     if (mNumCoords >= 6) {
00574       float p2t;
00575       p2t = aCX->PixelsToTwips();
00576       nscoord x0 = NSIntPixelsToTwips(mCoords[0], p2t);
00577       nscoord y0 = NSIntPixelsToTwips(mCoords[1], p2t);
00578       nscoord x1, y1;
00579       for (PRInt32 i = 2; i < mNumCoords; i += 2) {
00580         x1 = NSIntPixelsToTwips(mCoords[i], p2t);
00581         y1 = NSIntPixelsToTwips(mCoords[i+1], p2t);
00582         aRC.DrawLine(x0, y0, x1, y1);
00583         x0 = x1;
00584         y0 = y1;
00585       }
00586       x1 = NSIntPixelsToTwips(mCoords[0], p2t);
00587       y1 = NSIntPixelsToTwips(mCoords[1], p2t);
00588       aRC.DrawLine(x0, y0, x1, y1);
00589     }
00590   }
00591 }
00592 
00593 void PolyArea::GetRect(nsPresContext* aCX, nsRect& aRect)
00594 {
00595   if (mNumCoords >= 6) {
00596     float p2t;
00597     p2t = aCX->PixelsToTwips();
00598     nscoord x1, x2, y1, y2, xtmp, ytmp;
00599     x1 = x2 = NSIntPixelsToTwips(mCoords[0], p2t);
00600     y1 = y2 = NSIntPixelsToTwips(mCoords[1], p2t);
00601     for (PRInt32 i = 2; i < mNumCoords; i += 2) {
00602       xtmp = NSIntPixelsToTwips(mCoords[i], p2t);
00603       ytmp = NSIntPixelsToTwips(mCoords[i+1], p2t);
00604       x1 = x1 < xtmp ? x1 : xtmp;
00605       y1 = y1 < ytmp ? y1 : ytmp;
00606       x2 = x2 > xtmp ? x2 : xtmp;
00607       y2 = y2 > ytmp ? y2 : ytmp;
00608     }
00609 
00610     aRect.SetRect(x1, y1, x2, y2);
00611   }
00612 }
00613 
00614 //----------------------------------------------------------------------
00615 
00616 class CircleArea : public Area {
00617 public:
00618   CircleArea(nsIContent* aArea);
00619 
00620   virtual void ParseCoords(const nsAString& aSpec);
00621   virtual PRBool IsInside(nscoord x, nscoord y) const;
00622   virtual void Draw(nsPresContext* aCX,
00623                     nsIRenderingContext& aRC);
00624   virtual void GetRect(nsPresContext* aCX, nsRect& aRect);
00625 };
00626 
00627 CircleArea::CircleArea(nsIContent* aArea)
00628   : Area(aArea)
00629 {
00630 }
00631 
00632 void CircleArea::ParseCoords(const nsAString& aSpec)
00633 {
00634   Area::ParseCoords(aSpec);
00635 
00636   PRBool wrongNumberOfCoords = PR_FALSE;
00637   PRInt32 flag = nsIScriptError::warningFlag;
00638   if (mNumCoords >= 3) {
00639     if (mCoords[2] < 0) {
00640       logMessage(mArea,
00641                  aSpec,
00642                  nsIScriptError::errorFlag,
00643                  "ImageMapCircleNegativeRadius");
00644     }
00645   
00646     if (mNumCoords > 3) {
00647       wrongNumberOfCoords = PR_TRUE;
00648     }
00649   } else {
00650     wrongNumberOfCoords = PR_TRUE;
00651     flag = nsIScriptError::errorFlag;
00652   }
00653 
00654   if (wrongNumberOfCoords) {
00655     logMessage(mArea,
00656                aSpec,
00657                flag,
00658                "ImageMapCircleWrongNumberOfCoords");
00659   }
00660 }
00661 
00662 PRBool CircleArea::IsInside(nscoord x, nscoord y) const
00663 {
00664   // Note: > is for nav compatabilty
00665   if (mNumCoords >= 3) {
00666     nscoord x1 = mCoords[0];
00667     nscoord y1 = mCoords[1];
00668     nscoord radius = mCoords[2];
00669     if (radius < 0) {
00670       return PR_FALSE;
00671     }
00672     nscoord dx = x1 - x;
00673     nscoord dy = y1 - y;
00674     nscoord dist = (dx * dx) + (dy * dy);
00675     if (dist <= (radius * radius)) {
00676       return PR_TRUE;
00677     }
00678   }
00679   return PR_FALSE;
00680 }
00681 
00682 void CircleArea::Draw(nsPresContext* aCX, nsIRenderingContext& aRC)
00683 {
00684   if (mHasFocus) {
00685     if (mNumCoords >= 3) {
00686       float p2t;
00687       p2t = aCX->PixelsToTwips();
00688       nscoord x1 = NSIntPixelsToTwips(mCoords[0], p2t);
00689       nscoord y1 = NSIntPixelsToTwips(mCoords[1], p2t);
00690       nscoord radius = NSIntPixelsToTwips(mCoords[2], p2t);
00691       if (radius < 0) {
00692         return;
00693       }
00694       nscoord x = x1 - radius;
00695       nscoord y = y1 - radius;
00696       nscoord w = 2 * radius;
00697       aRC.DrawEllipse(x, y, w, w);
00698     }
00699   }
00700 }
00701 
00702 void CircleArea::GetRect(nsPresContext* aCX, nsRect& aRect)
00703 {
00704   if (mNumCoords >= 3) {
00705     float p2t;
00706     p2t = aCX->PixelsToTwips();
00707     nscoord x1 = NSIntPixelsToTwips(mCoords[0], p2t);
00708     nscoord y1 = NSIntPixelsToTwips(mCoords[1], p2t);
00709     nscoord radius = NSIntPixelsToTwips(mCoords[2], p2t);
00710     if (radius < 0) {
00711       return;
00712     }
00713 
00714     aRect.SetRect(x1 - radius, y1 - radius, x1 + radius, y1 + radius);
00715   }
00716 }
00717 
00718 //----------------------------------------------------------------------
00719 
00720 
00721 nsImageMap::nsImageMap() :
00722   mPresShell(nsnull),
00723   mImageFrame(nsnull),
00724   mDocument(nsnull),
00725   mContainsBlockContents(PR_FALSE)
00726 {
00727 }
00728 
00729 nsImageMap::~nsImageMap()
00730 {
00731   NS_ASSERTION(mAreas.Count() == 0, "Destroy was not called");
00732 }
00733 
00734 NS_IMPL_ISUPPORTS4(nsImageMap,
00735                    nsIDocumentObserver,
00736                    nsIDOMFocusListener,
00737                    nsIDOMEventListener,
00738                    nsIImageMap)
00739 
00740 NS_IMETHODIMP
00741 nsImageMap::GetBoundsForAreaContent(nsIContent *aContent, 
00742                                    nsPresContext* aPresContext, 
00743                                    nsRect& aBounds)
00744 {
00745   // Find the Area struct associated with this content node, and return bounds
00746   PRInt32 i, n = mAreas.Count();
00747   for (i = 0; i < n; i++) {
00748     Area* area = (Area*) mAreas.ElementAt(i);
00749     if (area->mArea == aContent) {
00750       area->GetRect(aPresContext, aBounds);
00751       return NS_OK;
00752     }
00753   }
00754   return NS_ERROR_FAILURE;
00755 }
00756 
00757 void
00758 nsImageMap::FreeAreas()
00759 {
00760   nsFrameManager *frameManager = mPresShell->FrameManager();
00761 
00762   PRInt32 i, n = mAreas.Count();
00763   for (i = 0; i < n; i++) {
00764     Area* area = (Area*) mAreas.ElementAt(i);
00765     frameManager->RemoveAsPrimaryFrame(area->mArea, mImageFrame);
00766 
00767     nsCOMPtr<nsIContent> areaContent;
00768     area->GetArea(getter_AddRefs(areaContent));
00769     if (areaContent) {
00770       nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(areaContent));
00771       if (rec) {
00772         rec->RemoveEventListenerByIID(this, NS_GET_IID(nsIDOMFocusListener));
00773       }
00774     }
00775     delete area;
00776   }
00777   mAreas.Clear();
00778 }
00779 
00780 nsresult
00781 nsImageMap::Init(nsIPresShell* aPresShell, nsIFrame* aImageFrame, nsIDOMHTMLMapElement* aMap)
00782 {
00783   NS_PRECONDITION(nsnull != aMap, "null ptr");
00784   if (nsnull == aMap) {
00785     return NS_ERROR_NULL_POINTER;
00786   }
00787   mPresShell = aPresShell;
00788   mImageFrame = aImageFrame;
00789 
00790   nsresult rv;
00791   mMap = do_QueryInterface(aMap, &rv);
00792   NS_ASSERTION(mMap, "aMap is not an nsIContent!");
00793   mDocument = mMap->GetDocument();
00794   if (mPresShell) {
00795     NS_STATIC_CAST(nsIPresShell_MOZILLA_1_8_BRANCH2*, mPresShell)->AddObserver(this);
00796   }
00797 
00798   // "Compile" the areas in the map into faster access versions
00799   rv = UpdateAreas();
00800   return rv;
00801 }
00802 
00803 
00804 nsresult
00805 nsImageMap::UpdateAreasForBlock(nsIContent* aParent, PRBool* aFoundAnchor)
00806 {
00807   nsresult rv = NS_OK;
00808   PRUint32 i, n = aParent->GetChildCount();
00809 
00810   for (i = 0; (i < n) && NS_SUCCEEDED(rv); i++) {
00811     nsIContent *child = aParent->GetChildAt(i);
00812 
00813     nsCOMPtr<nsIDOMHTMLAnchorElement> area = do_QueryInterface(child);
00814     if (area) {
00815       *aFoundAnchor = PR_TRUE;
00816       rv = AddArea(child);
00817     }
00818     else {
00819       rv = UpdateAreasForBlock(child, aFoundAnchor);
00820     }
00821   }
00822   
00823   return rv;
00824 }
00825 
00826 nsresult
00827 nsImageMap::UpdateAreas()
00828 {
00829   // Get rid of old area data
00830   FreeAreas();
00831 
00832   PRUint32 i, n = mMap->GetChildCount();
00833   PRBool containsBlock = PR_FALSE, containsArea = PR_FALSE;
00834 
00835   for (i = 0; i < n; i++) {
00836     nsIContent *child = mMap->GetChildAt(i);
00837 
00838     // Only look at elements and not text, comments, etc.
00839     if (!child->IsContentOfType(nsIContent::eHTML))
00840       continue;
00841 
00842     // First check if this map element contains an AREA element.
00843     // If so, we only look for AREA elements
00844     if (!containsBlock) {
00845       nsCOMPtr<nsIDOMHTMLAreaElement> area = do_QueryInterface(child);
00846       if (area) {
00847         containsArea = PR_TRUE;
00848         AddArea(child);
00849       }
00850     }
00851       
00852     // If we haven't determined that the map element contains an
00853     // AREA element yet, the look for a block element with children
00854     // that are anchors.
00855     if (!containsArea) {
00856       UpdateAreasForBlock(child, &containsBlock);
00857 
00858       if (containsBlock)
00859         mContainsBlockContents = PR_TRUE;
00860     }
00861   }
00862 
00863   return NS_OK;
00864 }
00865 
00866 nsresult
00867 nsImageMap::AddArea(nsIContent* aArea)
00868 {
00869   nsAutoString shape, coords;
00870   aArea->GetAttr(kNameSpaceID_None, nsHTMLAtoms::shape, shape);
00871   aArea->GetAttr(kNameSpaceID_None, nsHTMLAtoms::coords, coords);
00872 
00873   Area* area;
00874   if (shape.IsEmpty() ||
00875       shape.LowerCaseEqualsLiteral("rect") ||
00876       shape.LowerCaseEqualsLiteral("rectangle")) {
00877     area = new RectArea(aArea);
00878   }
00879   else if (shape.LowerCaseEqualsLiteral("poly") ||
00880            shape.LowerCaseEqualsLiteral("polygon")) {
00881     area = new PolyArea(aArea);
00882   }
00883   else if (shape.LowerCaseEqualsLiteral("circle") ||
00884            shape.LowerCaseEqualsLiteral("circ")) {
00885     area = new CircleArea(aArea);
00886   }
00887   else if (shape.LowerCaseEqualsLiteral("default")) {
00888     area = new DefaultArea(aArea);
00889   }
00890   else {
00891     // Unknown area type; bail
00892     return NS_OK;
00893   }
00894   if (!area)
00895     return NS_ERROR_OUT_OF_MEMORY;
00896 
00897   //Add focus listener to track area focus changes
00898   nsCOMPtr<nsIDOMEventReceiver> rec(do_QueryInterface(aArea));
00899   if (rec) {
00900     rec->AddEventListenerByIID(this, NS_GET_IID(nsIDOMFocusListener));
00901   }
00902 
00903   mPresShell->FrameManager()->SetPrimaryFrameFor(aArea, mImageFrame);
00904   aArea->SetMayHaveFrame(PR_TRUE);
00905   NS_ASSERTION(aArea->MayHaveFrame(), "SetMayHaveFrame failed?");
00906 
00907   area->ParseCoords(coords);
00908   mAreas.AppendElement(area);
00909   return NS_OK;
00910 }
00911 
00912 PRBool
00913 nsImageMap::IsInside(nscoord aX, nscoord aY,
00914                      nsIContent** aContent) const
00915 {
00916   NS_ASSERTION(mMap, "Not initialized");
00917   PRInt32 i, n = mAreas.Count();
00918   for (i = 0; i < n; i++) {
00919     Area* area = (Area*) mAreas.ElementAt(i);
00920     if (area->IsInside(aX, aY)) {
00921       area->GetArea(aContent);
00922 
00923       return PR_TRUE;
00924     }
00925   }
00926 
00927   return PR_FALSE;
00928 }
00929 
00930 void
00931 nsImageMap::Draw(nsPresContext* aCX, nsIRenderingContext& aRC)
00932 {
00933   PRInt32 i, n = mAreas.Count();
00934   for (i = 0; i < n; i++) {
00935     Area* area = (Area*) mAreas.ElementAt(i);
00936     area->Draw(aCX, aRC);
00937   }
00938 }
00939 
00940 void
00941 nsImageMap::MaybeUpdateAreas(nsIContent *aContent)
00942 {
00943   if (aContent && (aContent == mMap ||
00944                    (mContainsBlockContents &&
00945                     nsContentUtils::ContentIsDescendantOf(aContent, mMap)))) {
00946     UpdateAreas();
00947   }
00948 }
00949 
00950 void
00951 nsImageMap::AttributeChanged(nsIDocument *aDocument,
00952                              nsIContent*  aContent,
00953                              PRInt32      aNameSpaceID,
00954                              nsIAtom*     aAttribute,
00955                              PRInt32      aModType)
00956 {
00957   // If the parent of the changing content node is our map then update
00958   // the map.
00959   MaybeUpdateAreas(aContent->GetParent());
00960 }
00961 
00962 void
00963 nsImageMap::ContentAppended(nsIDocument *aDocument,
00964                             nsIContent* aContainer,
00965                             PRInt32     aNewIndexInContainer)
00966 {
00967   MaybeUpdateAreas(aContainer);
00968 }
00969 
00970 void
00971 nsImageMap::ContentInserted(nsIDocument *aDocument,
00972                             nsIContent* aContainer,
00973                             nsIContent* aChild,
00974                             PRInt32 aIndexInContainer)
00975 {
00976   MaybeUpdateAreas(aContainer);
00977 }
00978 
00979 void
00980 nsImageMap::ContentRemoved(nsIDocument *aDocument,
00981                            nsIContent* aContainer,
00982                            nsIContent* aChild,
00983                            PRInt32 aIndexInContainer)
00984 {
00985   MaybeUpdateAreas(aContainer);
00986 }
00987 
00988 nsresult
00989 nsImageMap::Focus(nsIDOMEvent* aEvent)
00990 {
00991   return ChangeFocus(aEvent, PR_TRUE);
00992 }
00993 
00994 nsresult
00995 nsImageMap::Blur(nsIDOMEvent* aEvent)
00996 {
00997   return ChangeFocus(aEvent, PR_FALSE);
00998 }
00999 
01000 nsresult
01001 nsImageMap::ChangeFocus(nsIDOMEvent* aEvent, PRBool aFocus) {
01002   //Set which one of our areas changed focus
01003   nsCOMPtr<nsIDOMEventTarget> target;
01004   if (NS_SUCCEEDED(aEvent->GetTarget(getter_AddRefs(target))) && target) {
01005     nsCOMPtr<nsIContent> targetContent(do_QueryInterface(target));
01006     if (targetContent) {
01007       PRInt32 i, n = mAreas.Count();
01008       for (i = 0; i < n; i++) {
01009         Area* area = (Area*) mAreas.ElementAt(i);
01010         nsCOMPtr<nsIContent> areaContent;
01011         area->GetArea(getter_AddRefs(areaContent));
01012         if (areaContent) {
01013           if (areaContent.get() == targetContent.get()) {
01014             //Set or Remove internal focus
01015             area->HasFocus(aFocus);
01016             //Now invalidate the rect
01017             nsCOMPtr<nsIDocument> doc = targetContent->GetDocument();
01018             //This check is necessary to see if we're still attached to the doc
01019             if (doc) {
01020               nsIPresShell *presShell = doc->GetShellAt(0);
01021               if (presShell) {
01022                 nsIFrame* imgFrame;
01023                 if (NS_SUCCEEDED(presShell->GetPrimaryFrameFor(targetContent, &imgFrame)) && imgFrame) {
01024                   nsPresContext *presContext = presShell->GetPresContext();
01025                   if (presContext) {
01026                     nsRect dmgRect;
01027                     area->GetRect(presContext, dmgRect);
01028                     imgFrame->Invalidate(dmgRect, PR_TRUE);
01029                   }
01030                 }
01031               }
01032             }
01033           }
01034         }
01035       }
01036     }
01037   }
01038   return NS_OK;
01039 }
01040 
01041 nsresult
01042 nsImageMap::HandleEvent(nsIDOMEvent* aEvent)
01043 {
01044   return NS_OK;
01045 }
01046 
01047 void
01048 nsImageMap::Destroy(void)
01049 {
01050   FreeAreas();
01051   if (mPresShell) {
01052     NS_STATIC_CAST(nsIPresShell_MOZILLA_1_8_BRANCH2*, mPresShell)->RemoveObserver(this);
01053   }
01054 }