layout/generic/nsImageMap.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

     1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     2 /* This Source Code Form is subject to the terms of the Mozilla Public
     3  * License, v. 2.0. If a copy of the MPL was not distributed with this
     4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     6 /* code for HTML client-side image maps */
     8 #include "nsImageMap.h"
    10 #include "mozilla/dom/Element.h"
    11 #include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
    12 #include "nsString.h"
    13 #include "nsReadableUtils.h"
    14 #include "nsRenderingContext.h"
    15 #include "nsPresContext.h"
    16 #include "nsNameSpaceManager.h"
    17 #include "nsGkAtoms.h"
    18 #include "nsImageFrame.h"
    19 #include "nsCoord.h"
    20 #include "nsIScriptError.h"
    21 #include "nsIStringBundle.h"
    22 #include "nsContentUtils.h"
    24 #ifdef ACCESSIBILITY
    25 #include "nsAccessibilityService.h"
    26 #endif
    28 using namespace mozilla;
    30 class Area {
    31 public:
    32   Area(nsIContent* aArea);
    33   virtual ~Area();
    35   virtual void ParseCoords(const nsAString& aSpec);
    37   virtual bool IsInside(nscoord x, nscoord y) const = 0;
    38   virtual void Draw(nsIFrame* aFrame, nsRenderingContext& aRC) = 0;
    39   virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) = 0;
    41   void HasFocus(bool aHasFocus);
    43   nsCOMPtr<nsIContent> mArea;
    44   nscoord* mCoords;
    45   int32_t mNumCoords;
    46   bool mHasFocus;
    47 };
    49 Area::Area(nsIContent* aArea)
    50   : mArea(aArea)
    51 {
    52   MOZ_COUNT_CTOR(Area);
    53   NS_PRECONDITION(mArea, "How did that happen?");
    54   mCoords = nullptr;
    55   mNumCoords = 0;
    56   mHasFocus = false;
    57 }
    59 Area::~Area()
    60 {
    61   MOZ_COUNT_DTOR(Area);
    62   delete [] mCoords;
    63 }
    65 #include <stdlib.h>
    67 inline bool
    68 is_space(char c)
    69 {
    70   return (c == ' ' ||
    71           c == '\f' ||
    72           c == '\n' ||
    73           c == '\r' ||
    74           c == '\t' ||
    75           c == '\v');
    76 }
    78 static void logMessage(nsIContent*      aContent,
    79                        const nsAString& aCoordsSpec,
    80                        int32_t          aFlags,
    81                        const char* aMessageName) {
    82   nsIDocument* doc = aContent->OwnerDoc();
    84   nsContentUtils::ReportToConsole(
    85      aFlags, NS_LITERAL_CSTRING("ImageMap"), doc,
    86      nsContentUtils::eLAYOUT_PROPERTIES,
    87      aMessageName,
    88      nullptr,  /* params */
    89      0, /* params length */
    90      nullptr,
    91      PromiseFlatString(NS_LITERAL_STRING("coords=\"") +
    92                        aCoordsSpec +
    93                        NS_LITERAL_STRING("\""))); /* source line */
    94 }
    96 void Area::ParseCoords(const nsAString& aSpec)
    97 {
    98   char* cp = ToNewCString(aSpec);
    99   if (cp) {
   100     char *tptr;
   101     char *n_str;
   102     int32_t i, cnt;
   103     int32_t *value_list;
   105     /*
   106      * Nothing in an empty list
   107      */
   108     mNumCoords = 0;
   109     mCoords = nullptr;
   110     if (*cp == '\0')
   111     {
   112       nsMemory::Free(cp);
   113       return;
   114     }
   116     /*
   117      * Skip beginning whitespace, all whitespace is empty list.
   118      */
   119     n_str = cp;
   120     while (is_space(*n_str))
   121     {
   122       n_str++;
   123     }
   124     if (*n_str == '\0')
   125     {
   126       nsMemory::Free(cp);
   127       return;
   128     }
   130     /*
   131      * Make a pass where any two numbers separated by just whitespace
   132      * are given a comma separator.  Count entries while passing.
   133      */
   134     cnt = 0;
   135     while (*n_str != '\0')
   136     {
   137       bool has_comma;
   139       /*
   140        * Skip to a separator
   141        */
   142       tptr = n_str;
   143       while (!is_space(*tptr) && *tptr != ',' && *tptr != '\0')
   144       {
   145         tptr++;
   146       }
   147       n_str = tptr;
   149       /*
   150        * If no more entries, break out here
   151        */
   152       if (*n_str == '\0')
   153       {
   154         break;
   155       }
   157       /*
   158        * Skip to the end of the separator, noting if we have a
   159        * comma.
   160        */
   161       has_comma = false;
   162       while (is_space(*tptr) || *tptr == ',')
   163       {
   164         if (*tptr == ',')
   165         {
   166           if (!has_comma)
   167           {
   168             has_comma = true;
   169           }
   170           else
   171           {
   172             break;
   173           }
   174         }
   175         tptr++;
   176       }
   177       /*
   178        * If this was trailing whitespace we skipped, we are done.
   179        */
   180       if ((*tptr == '\0') && !has_comma)
   181       {
   182         break;
   183       }
   184       /*
   185        * Else if the separator is all whitespace, and this is not the
   186        * end of the string, add a comma to the separator.
   187        */
   188       else if (!has_comma)
   189       {
   190         *n_str = ',';
   191       }
   193       /*
   194        * count the entry skipped.
   195        */
   196       cnt++;
   198       n_str = tptr;
   199     }
   200     /*
   201      * count the last entry in the list.
   202      */
   203     cnt++;
   205     /*
   206      * Allocate space for the coordinate array.
   207      */
   208     value_list = new nscoord[cnt];
   209     if (!value_list)
   210     {
   211       nsMemory::Free(cp);
   212       return;
   213     }
   215     /*
   216      * Second pass to copy integer values into list.
   217      */
   218     tptr = cp;
   219     for (i=0; i<cnt; i++)
   220     {
   221       char *ptr;
   223       ptr = strchr(tptr, ',');
   224       if (ptr)
   225       {
   226         *ptr = '\0';
   227       }
   228       /*
   229        * Strip whitespace in front of number because I don't
   230        * trust atoi to do it on all platforms.
   231        */
   232       while (is_space(*tptr))
   233       {
   234         tptr++;
   235       }
   236       if (*tptr == '\0')
   237       {
   238         value_list[i] = 0;
   239       }
   240       else
   241       {
   242         value_list[i] = (nscoord) ::atoi(tptr);
   243       }
   244       if (ptr)
   245       {
   246         *ptr = ',';
   247         tptr = ptr + 1;
   248       }
   249     }
   251     mNumCoords = cnt;
   252     mCoords = value_list;
   254     nsMemory::Free(cp);
   255   }
   256 }
   258 void Area::HasFocus(bool aHasFocus)
   259 {
   260   mHasFocus = aHasFocus;
   261 }
   263 //----------------------------------------------------------------------
   265 class DefaultArea : public Area {
   266 public:
   267   DefaultArea(nsIContent* aArea);
   269   virtual bool IsInside(nscoord x, nscoord y) const MOZ_OVERRIDE;
   270   virtual void Draw(nsIFrame* aFrame, nsRenderingContext& aRC) MOZ_OVERRIDE;
   271   virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) MOZ_OVERRIDE;
   272 };
   274 DefaultArea::DefaultArea(nsIContent* aArea)
   275   : Area(aArea)
   276 {
   277 }
   279 bool DefaultArea::IsInside(nscoord x, nscoord y) const
   280 {
   281   return true;
   282 }
   284 void DefaultArea::Draw(nsIFrame* aFrame, nsRenderingContext& aRC)
   285 {
   286   if (mHasFocus) {
   287     nsRect r = aFrame->GetRect();
   288     r.MoveTo(0, 0);
   289     nscoord x1 = r.x;
   290     nscoord y1 = r.y;
   291     const nscoord kOnePixel = nsPresContext::CSSPixelsToAppUnits(1);
   292     nscoord x2 = r.XMost() - kOnePixel;
   293     nscoord y2 = r.YMost() - kOnePixel;
   294     // XXX aRC.DrawRect(r) result is ugly, that's why we use DrawLine.
   295     aRC.DrawLine(x1, y1, x1, y2);
   296     aRC.DrawLine(x1, y2, x2, y2);
   297     aRC.DrawLine(x1, y1, x2, y1);
   298     aRC.DrawLine(x2, y1, x2, y2);
   299   }
   300 }
   302 void DefaultArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
   303 {
   304   aRect = aFrame->GetRect();
   305   aRect.MoveTo(0, 0);
   306 }
   308 //----------------------------------------------------------------------
   310 class RectArea : public Area {
   311 public:
   312   RectArea(nsIContent* aArea);
   314   virtual void ParseCoords(const nsAString& aSpec) MOZ_OVERRIDE;
   315   virtual bool IsInside(nscoord x, nscoord y) const MOZ_OVERRIDE;
   316   virtual void Draw(nsIFrame* aFrame, nsRenderingContext& aRC) MOZ_OVERRIDE;
   317   virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) MOZ_OVERRIDE;
   318 };
   320 RectArea::RectArea(nsIContent* aArea)
   321   : Area(aArea)
   322 {
   323 }
   325 void RectArea::ParseCoords(const nsAString& aSpec)
   326 {
   327   Area::ParseCoords(aSpec);
   329   bool saneRect = true;
   330   int32_t flag = nsIScriptError::warningFlag;
   331   if (mNumCoords >= 4) {
   332     if (mCoords[0] > mCoords[2]) {
   333       // x-coords in reversed order
   334       nscoord x = mCoords[2];
   335       mCoords[2] = mCoords[0];
   336       mCoords[0] = x;
   337       saneRect = false;
   338     }
   340     if (mCoords[1] > mCoords[3]) {
   341       // y-coords in reversed order
   342       nscoord y = mCoords[3];
   343       mCoords[3] = mCoords[1];
   344       mCoords[1] = y;
   345       saneRect = false;
   346     }
   348     if (mNumCoords > 4) {
   349       // Someone missed the concept of a rect here
   350       saneRect = false;
   351     }
   352   } else {
   353     saneRect = false;
   354     flag = nsIScriptError::errorFlag;
   355   }
   357   if (!saneRect) {
   358     logMessage(mArea, aSpec, flag, "ImageMapRectBoundsError");
   359   }
   360 }
   362 bool RectArea::IsInside(nscoord x, nscoord y) const
   363 {
   364   if (mNumCoords >= 4) {       // Note: > is for nav compatibility
   365     nscoord x1 = mCoords[0];
   366     nscoord y1 = mCoords[1];
   367     nscoord x2 = mCoords[2];
   368     nscoord y2 = mCoords[3];
   369     NS_ASSERTION(x1 <= x2 && y1 <= y2,
   370                  "Someone screwed up RectArea::ParseCoords");
   371     if ((x >= x1) && (x <= x2) && (y >= y1) && (y <= y2)) {
   372       return true;
   373     }
   374   }
   375   return false;
   376 }
   378 void RectArea::Draw(nsIFrame* aFrame, nsRenderingContext& aRC)
   379 {
   380   if (mHasFocus) {
   381     if (mNumCoords >= 4) {
   382       nscoord x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
   383       nscoord y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
   384       nscoord x2 = nsPresContext::CSSPixelsToAppUnits(mCoords[2]);
   385       nscoord y2 = nsPresContext::CSSPixelsToAppUnits(mCoords[3]);
   386       NS_ASSERTION(x1 <= x2 && y1 <= y2,
   387                    "Someone screwed up RectArea::ParseCoords");
   388       aRC.DrawLine(x1, y1, x1, y2);
   389       aRC.DrawLine(x1, y2, x2, y2);
   390       aRC.DrawLine(x1, y1, x2, y1);
   391       aRC.DrawLine(x2, y1, x2, y2);
   392     }
   393   }
   394 }
   396 void RectArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
   397 {
   398   if (mNumCoords >= 4) {
   399     nscoord x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
   400     nscoord y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
   401     nscoord x2 = nsPresContext::CSSPixelsToAppUnits(mCoords[2]);
   402     nscoord y2 = nsPresContext::CSSPixelsToAppUnits(mCoords[3]);
   403     NS_ASSERTION(x1 <= x2 && y1 <= y2,
   404                  "Someone screwed up RectArea::ParseCoords");
   406     aRect.SetRect(x1, y1, x2, y2);
   407   }
   408 }
   410 //----------------------------------------------------------------------
   412 class PolyArea : public Area {
   413 public:
   414   PolyArea(nsIContent* aArea);
   416   virtual void ParseCoords(const nsAString& aSpec) MOZ_OVERRIDE;
   417   virtual bool IsInside(nscoord x, nscoord y) const MOZ_OVERRIDE;
   418   virtual void Draw(nsIFrame* aFrame, nsRenderingContext& aRC) MOZ_OVERRIDE;
   419   virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) MOZ_OVERRIDE;
   420 };
   422 PolyArea::PolyArea(nsIContent* aArea)
   423   : Area(aArea)
   424 {
   425 }
   427 void PolyArea::ParseCoords(const nsAString& aSpec)
   428 {
   429   Area::ParseCoords(aSpec);
   431   if (mNumCoords >= 2) {
   432     if (mNumCoords & 1U) {
   433       logMessage(mArea,
   434                  aSpec,
   435                  nsIScriptError::warningFlag,
   436                  "ImageMapPolyOddNumberOfCoords");
   437     }
   438   } else {
   439     logMessage(mArea,
   440                aSpec,
   441                nsIScriptError::errorFlag,
   442                "ImageMapPolyWrongNumberOfCoords");
   443   }
   444 }
   446 bool PolyArea::IsInside(nscoord x, nscoord y) const
   447 {
   448   if (mNumCoords >= 6) {
   449     int32_t intersects = 0;
   450     nscoord wherex = x;
   451     nscoord wherey = y;
   452     int32_t totalv = mNumCoords / 2;
   453     int32_t totalc = totalv * 2;
   454     nscoord xval = mCoords[totalc - 2];
   455     nscoord yval = mCoords[totalc - 1];
   456     int32_t end = totalc;
   457     int32_t pointer = 1;
   459     if ((yval >= wherey) != (mCoords[pointer] >= wherey)) {
   460       if ((xval >= wherex) == (mCoords[0] >= wherex)) {
   461         intersects += (xval >= wherex) ? 1 : 0;
   462       } else {
   463         intersects += ((xval - (yval - wherey) *
   464                         (mCoords[0] - xval) /
   465                         (mCoords[pointer] - yval)) >= wherex) ? 1 : 0;
   466       }
   467     }
   469     // XXX I wonder what this is doing; this is a translation of ptinpoly.c
   470     while (pointer < end)  {
   471       yval = mCoords[pointer];
   472       pointer += 2;
   473       if (yval >= wherey)  {
   474         while((pointer < end) && (mCoords[pointer] >= wherey))
   475           pointer+=2;
   476         if (pointer >= end)
   477           break;
   478         if ((mCoords[pointer-3] >= wherex) ==
   479             (mCoords[pointer-1] >= wherex)) {
   480           intersects += (mCoords[pointer-3] >= wherex) ? 1 : 0;
   481         } else {
   482           intersects +=
   483             ((mCoords[pointer-3] - (mCoords[pointer-2] - wherey) *
   484               (mCoords[pointer-1] - mCoords[pointer-3]) /
   485               (mCoords[pointer] - mCoords[pointer - 2])) >= wherex) ? 1:0;
   486         }
   487       }  else  {
   488         while((pointer < end) && (mCoords[pointer] < wherey))
   489           pointer+=2;
   490         if (pointer >= end)
   491           break;
   492         if ((mCoords[pointer-3] >= wherex) ==
   493             (mCoords[pointer-1] >= wherex)) {
   494           intersects += (mCoords[pointer-3] >= wherex) ? 1:0;
   495         } else {
   496           intersects +=
   497             ((mCoords[pointer-3] - (mCoords[pointer-2] - wherey) *
   498               (mCoords[pointer-1] - mCoords[pointer-3]) /
   499               (mCoords[pointer] - mCoords[pointer - 2])) >= wherex) ? 1:0;
   500         }
   501       }
   502     }
   503     if ((intersects & 1) != 0) {
   504       return true;
   505     }
   506   }
   507   return false;
   508 }
   510 void PolyArea::Draw(nsIFrame* aFrame, nsRenderingContext& aRC)
   511 {
   512   if (mHasFocus) {
   513     if (mNumCoords >= 6) {
   514       nscoord x0 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
   515       nscoord y0 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
   516       nscoord x1, y1;
   517       for (int32_t i = 2; i < mNumCoords; i += 2) {
   518         x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[i]);
   519         y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[i+1]);
   520         aRC.DrawLine(x0, y0, x1, y1);
   521         x0 = x1;
   522         y0 = y1;
   523       }
   524       x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
   525       y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
   526       aRC.DrawLine(x0, y0, x1, y1);
   527     }
   528   }
   529 }
   531 void PolyArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
   532 {
   533   if (mNumCoords >= 6) {
   534     nscoord x1, x2, y1, y2, xtmp, ytmp;
   535     x1 = x2 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
   536     y1 = y2 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
   537     for (int32_t i = 2; i < mNumCoords; i += 2) {
   538       xtmp = nsPresContext::CSSPixelsToAppUnits(mCoords[i]);
   539       ytmp = nsPresContext::CSSPixelsToAppUnits(mCoords[i+1]);
   540       x1 = x1 < xtmp ? x1 : xtmp;
   541       y1 = y1 < ytmp ? y1 : ytmp;
   542       x2 = x2 > xtmp ? x2 : xtmp;
   543       y2 = y2 > ytmp ? y2 : ytmp;
   544     }
   546     aRect.SetRect(x1, y1, x2, y2);
   547   }
   548 }
   550 //----------------------------------------------------------------------
   552 class CircleArea : public Area {
   553 public:
   554   CircleArea(nsIContent* aArea);
   556   virtual void ParseCoords(const nsAString& aSpec) MOZ_OVERRIDE;
   557   virtual bool IsInside(nscoord x, nscoord y) const MOZ_OVERRIDE;
   558   virtual void Draw(nsIFrame* aFrame, nsRenderingContext& aRC) MOZ_OVERRIDE;
   559   virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) MOZ_OVERRIDE;
   560 };
   562 CircleArea::CircleArea(nsIContent* aArea)
   563   : Area(aArea)
   564 {
   565 }
   567 void CircleArea::ParseCoords(const nsAString& aSpec)
   568 {
   569   Area::ParseCoords(aSpec);
   571   bool wrongNumberOfCoords = false;
   572   int32_t flag = nsIScriptError::warningFlag;
   573   if (mNumCoords >= 3) {
   574     if (mCoords[2] < 0) {
   575       logMessage(mArea,
   576                  aSpec,
   577                  nsIScriptError::errorFlag,
   578                  "ImageMapCircleNegativeRadius");
   579     }
   581     if (mNumCoords > 3) {
   582       wrongNumberOfCoords = true;
   583     }
   584   } else {
   585     wrongNumberOfCoords = true;
   586     flag = nsIScriptError::errorFlag;
   587   }
   589   if (wrongNumberOfCoords) {
   590     logMessage(mArea,
   591                aSpec,
   592                flag,
   593                "ImageMapCircleWrongNumberOfCoords");
   594   }
   595 }
   597 bool CircleArea::IsInside(nscoord x, nscoord y) const
   598 {
   599   // Note: > is for nav compatibility
   600   if (mNumCoords >= 3) {
   601     nscoord x1 = mCoords[0];
   602     nscoord y1 = mCoords[1];
   603     nscoord radius = mCoords[2];
   604     if (radius < 0) {
   605       return false;
   606     }
   607     nscoord dx = x1 - x;
   608     nscoord dy = y1 - y;
   609     nscoord dist = (dx * dx) + (dy * dy);
   610     if (dist <= (radius * radius)) {
   611       return true;
   612     }
   613   }
   614   return false;
   615 }
   617 void CircleArea::Draw(nsIFrame* aFrame, nsRenderingContext& aRC)
   618 {
   619   if (mHasFocus) {
   620     if (mNumCoords >= 3) {
   621       nscoord x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
   622       nscoord y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
   623       nscoord radius = nsPresContext::CSSPixelsToAppUnits(mCoords[2]);
   624       if (radius < 0) {
   625         return;
   626       }
   627       nscoord x = x1 - radius;
   628       nscoord y = y1 - radius;
   629       nscoord w = 2 * radius;
   630       aRC.DrawEllipse(x, y, w, w);
   631     }
   632   }
   633 }
   635 void CircleArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
   636 {
   637   if (mNumCoords >= 3) {
   638     nscoord x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
   639     nscoord y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
   640     nscoord radius = nsPresContext::CSSPixelsToAppUnits(mCoords[2]);
   641     if (radius < 0) {
   642       return;
   643     }
   645     aRect.SetRect(x1 - radius, y1 - radius, x1 + radius, y1 + radius);
   646   }
   647 }
   649 //----------------------------------------------------------------------
   652 nsImageMap::nsImageMap() :
   653   mImageFrame(nullptr),
   654   mContainsBlockContents(false)
   655 {
   656 }
   658 nsImageMap::~nsImageMap()
   659 {
   660   NS_ASSERTION(mAreas.Length() == 0, "Destroy was not called");
   661 }
   663 NS_IMPL_ISUPPORTS(nsImageMap,
   664                   nsIMutationObserver,
   665                   nsIDOMEventListener)
   667 nsresult
   668 nsImageMap::GetBoundsForAreaContent(nsIContent *aContent,
   669                                     nsRect& aBounds)
   670 {
   671   NS_ENSURE_TRUE(aContent && mImageFrame, NS_ERROR_INVALID_ARG);
   673   // Find the Area struct associated with this content node, and return bounds
   674   uint32_t i, n = mAreas.Length();
   675   for (i = 0; i < n; i++) {
   676     Area* area = mAreas.ElementAt(i);
   677     if (area->mArea == aContent) {
   678       aBounds = nsRect();
   679       area->GetRect(mImageFrame, aBounds);
   680       return NS_OK;
   681     }
   682   }
   683   return NS_ERROR_FAILURE;
   684 }
   686 void
   687 nsImageMap::FreeAreas()
   688 {
   689   uint32_t i, n = mAreas.Length();
   690   for (i = 0; i < n; i++) {
   691     Area* area = mAreas.ElementAt(i);
   692     if (area->mArea->IsInDoc()) {
   693       NS_ASSERTION(area->mArea->GetPrimaryFrame() == mImageFrame,
   694                    "Unexpected primary frame");
   696       area->mArea->SetPrimaryFrame(nullptr);
   697     }
   699     area->mArea->RemoveSystemEventListener(NS_LITERAL_STRING("focus"), this,
   700                                            false);
   701     area->mArea->RemoveSystemEventListener(NS_LITERAL_STRING("blur"), this,
   702                                            false);
   703     delete area;
   704   }
   705   mAreas.Clear();
   706 }
   708 nsresult
   709 nsImageMap::Init(nsImageFrame* aImageFrame, nsIContent* aMap)
   710 {
   711   NS_PRECONDITION(aMap, "null ptr");
   712   if (!aMap) {
   713     return NS_ERROR_NULL_POINTER;
   714   }
   715   mImageFrame = aImageFrame;
   717   mMap = aMap;
   718   mMap->AddMutationObserver(this);
   720   // "Compile" the areas in the map into faster access versions
   721   return UpdateAreas();
   722 }
   725 nsresult
   726 nsImageMap::SearchForAreas(nsIContent* aParent, bool& aFoundArea,
   727                            bool& aFoundAnchor)
   728 {
   729   nsresult rv = NS_OK;
   730   uint32_t i, n = aParent->GetChildCount();
   732   // Look for <area> or <a> elements. We'll use whichever type we find first.
   733   for (i = 0; i < n; i++) {
   734     nsIContent *child = aParent->GetChildAt(i);
   736     if (child->IsHTML()) {
   737       // If we haven't determined that the map element contains an
   738       // <a> element yet, then look for <area>.
   739       if (!aFoundAnchor && child->Tag() == nsGkAtoms::area) {
   740         aFoundArea = true;
   741         rv = AddArea(child);
   742         NS_ENSURE_SUCCESS(rv, rv);
   744         // Continue to next child. This stops mContainsBlockContents from
   745         // getting set. It also makes us ignore children of <area>s which
   746         // is consistent with how we react to dynamic insertion of such
   747         // children.
   748         continue;
   749       }
   750       // If we haven't determined that the map element contains an
   751       // <area> element yet, then look for <a>.
   752       if (!aFoundArea && child->Tag() == nsGkAtoms::a) {
   753         aFoundAnchor = true;
   754         rv = AddArea(child);
   755         NS_ENSURE_SUCCESS(rv, rv);
   756       }
   757     }
   759     if (child->IsElement()) {
   760       mContainsBlockContents = true;
   761       rv = SearchForAreas(child, aFoundArea, aFoundAnchor);
   762       NS_ENSURE_SUCCESS(rv, rv);
   763     }
   764   }
   766   return NS_OK;
   767 }
   769 nsresult
   770 nsImageMap::UpdateAreas()
   771 {
   772   // Get rid of old area data
   773   FreeAreas();
   775   bool foundArea = false;
   776   bool foundAnchor = false;
   777   mContainsBlockContents = false;
   779   nsresult rv = SearchForAreas(mMap, foundArea, foundAnchor);
   780 #ifdef ACCESSIBILITY
   781   if (NS_SUCCEEDED(rv)) {
   782     nsAccessibilityService* accService = GetAccService();
   783     if (accService) {
   784       accService->UpdateImageMap(mImageFrame);
   785     }
   786   }
   787 #endif
   788   return rv;
   789 }
   791 nsresult
   792 nsImageMap::AddArea(nsIContent* aArea)
   793 {
   794   static nsIContent::AttrValuesArray strings[] =
   795     {&nsGkAtoms::rect, &nsGkAtoms::rectangle,
   796      &nsGkAtoms::circle, &nsGkAtoms::circ,
   797      &nsGkAtoms::_default,
   798      &nsGkAtoms::poly, &nsGkAtoms::polygon,
   799      nullptr};
   801   Area* area;
   802   switch (aArea->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::shape,
   803                                  strings, eIgnoreCase)) {
   804   case nsIContent::ATTR_VALUE_NO_MATCH:
   805   case nsIContent::ATTR_MISSING:
   806   case 0:
   807   case 1:
   808     area = new RectArea(aArea);
   809     break;
   810   case 2:
   811   case 3:
   812     area = new CircleArea(aArea);
   813     break;
   814   case 4:
   815     area = new DefaultArea(aArea);
   816     break;
   817   case 5:
   818   case 6:
   819     area = new PolyArea(aArea);
   820     break;
   821   default:
   822     area = nullptr;
   823     NS_NOTREACHED("FindAttrValueIn returned an unexpected value.");
   824     break;
   825   }
   826   if (!area)
   827     return NS_ERROR_OUT_OF_MEMORY;
   829   //Add focus listener to track area focus changes
   830   aArea->AddSystemEventListener(NS_LITERAL_STRING("focus"), this, false,
   831                                 false);
   832   aArea->AddSystemEventListener(NS_LITERAL_STRING("blur"), this, false,
   833                                 false);
   835   // This is a nasty hack.  It needs to go away: see bug 135040.  Once this is
   836   // removed, the code added to RestyleManager::RestyleElement,
   837   // nsCSSFrameConstructor::ContentRemoved (both hacks there), and
   838   // RestyleManager::ProcessRestyledFrames to work around this issue can
   839   // be removed.
   840   aArea->SetPrimaryFrame(mImageFrame);
   842   nsAutoString coords;
   843   aArea->GetAttr(kNameSpaceID_None, nsGkAtoms::coords, coords);
   844   area->ParseCoords(coords);
   845   mAreas.AppendElement(area);
   846   return NS_OK;
   847 }
   849 nsIContent*
   850 nsImageMap::GetArea(nscoord aX, nscoord aY) const
   851 {
   852   NS_ASSERTION(mMap, "Not initialized");
   853   uint32_t i, n = mAreas.Length();
   854   for (i = 0; i < n; i++) {
   855     Area* area = mAreas.ElementAt(i);
   856     if (area->IsInside(aX, aY)) {
   857       return area->mArea;
   858     }
   859   }
   861   return nullptr;
   862 }
   864 nsIContent*
   865 nsImageMap::GetAreaAt(uint32_t aIndex) const
   866 {
   867   return mAreas.ElementAt(aIndex)->mArea;
   868 }
   870 void
   871 nsImageMap::Draw(nsIFrame* aFrame, nsRenderingContext& aRC)
   872 {
   873   uint32_t i, n = mAreas.Length();
   874   for (i = 0; i < n; i++) {
   875     Area* area = mAreas.ElementAt(i);
   876     area->Draw(aFrame, aRC);
   877   }
   878 }
   880 void
   881 nsImageMap::MaybeUpdateAreas(nsIContent *aContent)
   882 {
   883   if (aContent == mMap || mContainsBlockContents) {
   884     UpdateAreas();
   885   }
   886 }
   888 void
   889 nsImageMap::AttributeChanged(nsIDocument*  aDocument,
   890                              dom::Element* aElement,
   891                              int32_t       aNameSpaceID,
   892                              nsIAtom*      aAttribute,
   893                              int32_t       aModType)
   894 {
   895   // If the parent of the changing content node is our map then update
   896   // the map.  But only do this if the node is an HTML <area> or <a>
   897   // and the attribute that's changing is "shape" or "coords" -- those
   898   // are the only cases we care about.
   899   if ((aElement->NodeInfo()->Equals(nsGkAtoms::area) ||
   900        aElement->NodeInfo()->Equals(nsGkAtoms::a)) &&
   901       aElement->IsHTML() &&
   902       aNameSpaceID == kNameSpaceID_None &&
   903       (aAttribute == nsGkAtoms::shape ||
   904        aAttribute == nsGkAtoms::coords)) {
   905     MaybeUpdateAreas(aElement->GetParent());
   906   } else if (aElement == mMap &&
   907              aNameSpaceID == kNameSpaceID_None &&
   908              (aAttribute == nsGkAtoms::name ||
   909               aAttribute == nsGkAtoms::id) &&
   910              mImageFrame) {
   911     // ID or name has changed. Let ImageFrame recreate ImageMap.
   912     mImageFrame->DisconnectMap();
   913   }
   914 }
   916 void
   917 nsImageMap::ContentAppended(nsIDocument *aDocument,
   918                             nsIContent* aContainer,
   919                             nsIContent* aFirstNewContent,
   920                             int32_t     /* unused */)
   921 {
   922   MaybeUpdateAreas(aContainer);
   923 }
   925 void
   926 nsImageMap::ContentInserted(nsIDocument *aDocument,
   927                             nsIContent* aContainer,
   928                             nsIContent* aChild,
   929                             int32_t /* unused */)
   930 {
   931   MaybeUpdateAreas(aContainer);
   932 }
   934 void
   935 nsImageMap::ContentRemoved(nsIDocument *aDocument,
   936                            nsIContent* aContainer,
   937                            nsIContent* aChild,
   938                            int32_t aIndexInContainer,
   939                            nsIContent* aPreviousSibling)
   940 {
   941   MaybeUpdateAreas(aContainer);
   942 }
   944 void
   945 nsImageMap::ParentChainChanged(nsIContent* aContent)
   946 {
   947   NS_ASSERTION(aContent == mMap,
   948                "Unexpected ParentChainChanged notification!");
   949   if (mImageFrame) {
   950     mImageFrame->DisconnectMap();
   951   }
   952 }
   954 nsresult
   955 nsImageMap::HandleEvent(nsIDOMEvent* aEvent)
   956 {
   957   nsAutoString eventType;
   958   aEvent->GetType(eventType);
   959   bool focus = eventType.EqualsLiteral("focus");
   960   NS_ABORT_IF_FALSE(focus == !eventType.EqualsLiteral("blur"),
   961                     "Unexpected event type");
   963   //Set which one of our areas changed focus
   964   nsCOMPtr<nsIContent> targetContent = do_QueryInterface(
   965     aEvent->InternalDOMEvent()->GetTarget());
   966   if (!targetContent) {
   967     return NS_OK;
   968   }
   969   uint32_t i, n = mAreas.Length();
   970   for (i = 0; i < n; i++) {
   971     Area* area = mAreas.ElementAt(i);
   972     if (area->mArea == targetContent) {
   973       //Set or Remove internal focus
   974       area->HasFocus(focus);
   975       //Now invalidate the rect
   976       if (mImageFrame) {
   977         mImageFrame->InvalidateFrame();
   978       }
   979       break;
   980     }
   981   }
   982   return NS_OK;
   983 }
   985 void
   986 nsImageMap::Destroy(void)
   987 {
   988   FreeAreas();
   989   mImageFrame = nullptr;
   990   mMap->RemoveMutationObserver(this);
   991 }

mercurial