LCOV - code coverage report
Current view: top level - layout/generic - nsImageMap.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 472 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 57 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13