layout/generic/nsImageMap.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/layout/generic/nsImageMap.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,991 @@
     1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
     1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.7 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.8 +
     1.9 +/* code for HTML client-side image maps */
    1.10 +
    1.11 +#include "nsImageMap.h"
    1.12 +
    1.13 +#include "mozilla/dom/Element.h"
    1.14 +#include "mozilla/dom/Event.h" // for nsIDOMEvent::InternalDOMEvent()
    1.15 +#include "nsString.h"
    1.16 +#include "nsReadableUtils.h"
    1.17 +#include "nsRenderingContext.h"
    1.18 +#include "nsPresContext.h"
    1.19 +#include "nsNameSpaceManager.h"
    1.20 +#include "nsGkAtoms.h"
    1.21 +#include "nsImageFrame.h"
    1.22 +#include "nsCoord.h"
    1.23 +#include "nsIScriptError.h"
    1.24 +#include "nsIStringBundle.h"
    1.25 +#include "nsContentUtils.h"
    1.26 +
    1.27 +#ifdef ACCESSIBILITY
    1.28 +#include "nsAccessibilityService.h"
    1.29 +#endif
    1.30 +
    1.31 +using namespace mozilla;
    1.32 +
    1.33 +class Area {
    1.34 +public:
    1.35 +  Area(nsIContent* aArea);
    1.36 +  virtual ~Area();
    1.37 +
    1.38 +  virtual void ParseCoords(const nsAString& aSpec);
    1.39 +
    1.40 +  virtual bool IsInside(nscoord x, nscoord y) const = 0;
    1.41 +  virtual void Draw(nsIFrame* aFrame, nsRenderingContext& aRC) = 0;
    1.42 +  virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) = 0;
    1.43 +
    1.44 +  void HasFocus(bool aHasFocus);
    1.45 +
    1.46 +  nsCOMPtr<nsIContent> mArea;
    1.47 +  nscoord* mCoords;
    1.48 +  int32_t mNumCoords;
    1.49 +  bool mHasFocus;
    1.50 +};
    1.51 +
    1.52 +Area::Area(nsIContent* aArea)
    1.53 +  : mArea(aArea)
    1.54 +{
    1.55 +  MOZ_COUNT_CTOR(Area);
    1.56 +  NS_PRECONDITION(mArea, "How did that happen?");
    1.57 +  mCoords = nullptr;
    1.58 +  mNumCoords = 0;
    1.59 +  mHasFocus = false;
    1.60 +}
    1.61 +
    1.62 +Area::~Area()
    1.63 +{
    1.64 +  MOZ_COUNT_DTOR(Area);
    1.65 +  delete [] mCoords;
    1.66 +}
    1.67 +
    1.68 +#include <stdlib.h>
    1.69 +
    1.70 +inline bool
    1.71 +is_space(char c)
    1.72 +{
    1.73 +  return (c == ' ' ||
    1.74 +          c == '\f' ||
    1.75 +          c == '\n' ||
    1.76 +          c == '\r' ||
    1.77 +          c == '\t' ||
    1.78 +          c == '\v');
    1.79 +}
    1.80 +
    1.81 +static void logMessage(nsIContent*      aContent,
    1.82 +                       const nsAString& aCoordsSpec,
    1.83 +                       int32_t          aFlags,
    1.84 +                       const char* aMessageName) {
    1.85 +  nsIDocument* doc = aContent->OwnerDoc();
    1.86 +
    1.87 +  nsContentUtils::ReportToConsole(
    1.88 +     aFlags, NS_LITERAL_CSTRING("ImageMap"), doc,
    1.89 +     nsContentUtils::eLAYOUT_PROPERTIES,
    1.90 +     aMessageName,
    1.91 +     nullptr,  /* params */
    1.92 +     0, /* params length */
    1.93 +     nullptr,
    1.94 +     PromiseFlatString(NS_LITERAL_STRING("coords=\"") +
    1.95 +                       aCoordsSpec +
    1.96 +                       NS_LITERAL_STRING("\""))); /* source line */
    1.97 +}
    1.98 +
    1.99 +void Area::ParseCoords(const nsAString& aSpec)
   1.100 +{
   1.101 +  char* cp = ToNewCString(aSpec);
   1.102 +  if (cp) {
   1.103 +    char *tptr;
   1.104 +    char *n_str;
   1.105 +    int32_t i, cnt;
   1.106 +    int32_t *value_list;
   1.107 +
   1.108 +    /*
   1.109 +     * Nothing in an empty list
   1.110 +     */
   1.111 +    mNumCoords = 0;
   1.112 +    mCoords = nullptr;
   1.113 +    if (*cp == '\0')
   1.114 +    {
   1.115 +      nsMemory::Free(cp);
   1.116 +      return;
   1.117 +    }
   1.118 +
   1.119 +    /*
   1.120 +     * Skip beginning whitespace, all whitespace is empty list.
   1.121 +     */
   1.122 +    n_str = cp;
   1.123 +    while (is_space(*n_str))
   1.124 +    {
   1.125 +      n_str++;
   1.126 +    }
   1.127 +    if (*n_str == '\0')
   1.128 +    {
   1.129 +      nsMemory::Free(cp);
   1.130 +      return;
   1.131 +    }
   1.132 +
   1.133 +    /*
   1.134 +     * Make a pass where any two numbers separated by just whitespace
   1.135 +     * are given a comma separator.  Count entries while passing.
   1.136 +     */
   1.137 +    cnt = 0;
   1.138 +    while (*n_str != '\0')
   1.139 +    {
   1.140 +      bool has_comma;
   1.141 +
   1.142 +      /*
   1.143 +       * Skip to a separator
   1.144 +       */
   1.145 +      tptr = n_str;
   1.146 +      while (!is_space(*tptr) && *tptr != ',' && *tptr != '\0')
   1.147 +      {
   1.148 +        tptr++;
   1.149 +      }
   1.150 +      n_str = tptr;
   1.151 +
   1.152 +      /*
   1.153 +       * If no more entries, break out here
   1.154 +       */
   1.155 +      if (*n_str == '\0')
   1.156 +      {
   1.157 +        break;
   1.158 +      }
   1.159 +
   1.160 +      /*
   1.161 +       * Skip to the end of the separator, noting if we have a
   1.162 +       * comma.
   1.163 +       */
   1.164 +      has_comma = false;
   1.165 +      while (is_space(*tptr) || *tptr == ',')
   1.166 +      {
   1.167 +        if (*tptr == ',')
   1.168 +        {
   1.169 +          if (!has_comma)
   1.170 +          {
   1.171 +            has_comma = true;
   1.172 +          }
   1.173 +          else
   1.174 +          {
   1.175 +            break;
   1.176 +          }
   1.177 +        }
   1.178 +        tptr++;
   1.179 +      }
   1.180 +      /*
   1.181 +       * If this was trailing whitespace we skipped, we are done.
   1.182 +       */
   1.183 +      if ((*tptr == '\0') && !has_comma)
   1.184 +      {
   1.185 +        break;
   1.186 +      }
   1.187 +      /*
   1.188 +       * Else if the separator is all whitespace, and this is not the
   1.189 +       * end of the string, add a comma to the separator.
   1.190 +       */
   1.191 +      else if (!has_comma)
   1.192 +      {
   1.193 +        *n_str = ',';
   1.194 +      }
   1.195 +
   1.196 +      /*
   1.197 +       * count the entry skipped.
   1.198 +       */
   1.199 +      cnt++;
   1.200 +
   1.201 +      n_str = tptr;
   1.202 +    }
   1.203 +    /*
   1.204 +     * count the last entry in the list.
   1.205 +     */
   1.206 +    cnt++;
   1.207 +
   1.208 +    /*
   1.209 +     * Allocate space for the coordinate array.
   1.210 +     */
   1.211 +    value_list = new nscoord[cnt];
   1.212 +    if (!value_list)
   1.213 +    {
   1.214 +      nsMemory::Free(cp);
   1.215 +      return;
   1.216 +    }
   1.217 +
   1.218 +    /*
   1.219 +     * Second pass to copy integer values into list.
   1.220 +     */
   1.221 +    tptr = cp;
   1.222 +    for (i=0; i<cnt; i++)
   1.223 +    {
   1.224 +      char *ptr;
   1.225 +
   1.226 +      ptr = strchr(tptr, ',');
   1.227 +      if (ptr)
   1.228 +      {
   1.229 +        *ptr = '\0';
   1.230 +      }
   1.231 +      /*
   1.232 +       * Strip whitespace in front of number because I don't
   1.233 +       * trust atoi to do it on all platforms.
   1.234 +       */
   1.235 +      while (is_space(*tptr))
   1.236 +      {
   1.237 +        tptr++;
   1.238 +      }
   1.239 +      if (*tptr == '\0')
   1.240 +      {
   1.241 +        value_list[i] = 0;
   1.242 +      }
   1.243 +      else
   1.244 +      {
   1.245 +        value_list[i] = (nscoord) ::atoi(tptr);
   1.246 +      }
   1.247 +      if (ptr)
   1.248 +      {
   1.249 +        *ptr = ',';
   1.250 +        tptr = ptr + 1;
   1.251 +      }
   1.252 +    }
   1.253 +
   1.254 +    mNumCoords = cnt;
   1.255 +    mCoords = value_list;
   1.256 +
   1.257 +    nsMemory::Free(cp);
   1.258 +  }
   1.259 +}
   1.260 +
   1.261 +void Area::HasFocus(bool aHasFocus)
   1.262 +{
   1.263 +  mHasFocus = aHasFocus;
   1.264 +}
   1.265 +
   1.266 +//----------------------------------------------------------------------
   1.267 +
   1.268 +class DefaultArea : public Area {
   1.269 +public:
   1.270 +  DefaultArea(nsIContent* aArea);
   1.271 +
   1.272 +  virtual bool IsInside(nscoord x, nscoord y) const MOZ_OVERRIDE;
   1.273 +  virtual void Draw(nsIFrame* aFrame, nsRenderingContext& aRC) MOZ_OVERRIDE;
   1.274 +  virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) MOZ_OVERRIDE;
   1.275 +};
   1.276 +
   1.277 +DefaultArea::DefaultArea(nsIContent* aArea)
   1.278 +  : Area(aArea)
   1.279 +{
   1.280 +}
   1.281 +
   1.282 +bool DefaultArea::IsInside(nscoord x, nscoord y) const
   1.283 +{
   1.284 +  return true;
   1.285 +}
   1.286 +
   1.287 +void DefaultArea::Draw(nsIFrame* aFrame, nsRenderingContext& aRC)
   1.288 +{
   1.289 +  if (mHasFocus) {
   1.290 +    nsRect r = aFrame->GetRect();
   1.291 +    r.MoveTo(0, 0);
   1.292 +    nscoord x1 = r.x;
   1.293 +    nscoord y1 = r.y;
   1.294 +    const nscoord kOnePixel = nsPresContext::CSSPixelsToAppUnits(1);
   1.295 +    nscoord x2 = r.XMost() - kOnePixel;
   1.296 +    nscoord y2 = r.YMost() - kOnePixel;
   1.297 +    // XXX aRC.DrawRect(r) result is ugly, that's why we use DrawLine.
   1.298 +    aRC.DrawLine(x1, y1, x1, y2);
   1.299 +    aRC.DrawLine(x1, y2, x2, y2);
   1.300 +    aRC.DrawLine(x1, y1, x2, y1);
   1.301 +    aRC.DrawLine(x2, y1, x2, y2);
   1.302 +  }
   1.303 +}
   1.304 +
   1.305 +void DefaultArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
   1.306 +{
   1.307 +  aRect = aFrame->GetRect();
   1.308 +  aRect.MoveTo(0, 0);
   1.309 +}
   1.310 +
   1.311 +//----------------------------------------------------------------------
   1.312 +
   1.313 +class RectArea : public Area {
   1.314 +public:
   1.315 +  RectArea(nsIContent* aArea);
   1.316 +
   1.317 +  virtual void ParseCoords(const nsAString& aSpec) MOZ_OVERRIDE;
   1.318 +  virtual bool IsInside(nscoord x, nscoord y) const MOZ_OVERRIDE;
   1.319 +  virtual void Draw(nsIFrame* aFrame, nsRenderingContext& aRC) MOZ_OVERRIDE;
   1.320 +  virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) MOZ_OVERRIDE;
   1.321 +};
   1.322 +
   1.323 +RectArea::RectArea(nsIContent* aArea)
   1.324 +  : Area(aArea)
   1.325 +{
   1.326 +}
   1.327 +
   1.328 +void RectArea::ParseCoords(const nsAString& aSpec)
   1.329 +{
   1.330 +  Area::ParseCoords(aSpec);
   1.331 +
   1.332 +  bool saneRect = true;
   1.333 +  int32_t flag = nsIScriptError::warningFlag;
   1.334 +  if (mNumCoords >= 4) {
   1.335 +    if (mCoords[0] > mCoords[2]) {
   1.336 +      // x-coords in reversed order
   1.337 +      nscoord x = mCoords[2];
   1.338 +      mCoords[2] = mCoords[0];
   1.339 +      mCoords[0] = x;
   1.340 +      saneRect = false;
   1.341 +    }
   1.342 +
   1.343 +    if (mCoords[1] > mCoords[3]) {
   1.344 +      // y-coords in reversed order
   1.345 +      nscoord y = mCoords[3];
   1.346 +      mCoords[3] = mCoords[1];
   1.347 +      mCoords[1] = y;
   1.348 +      saneRect = false;
   1.349 +    }
   1.350 +
   1.351 +    if (mNumCoords > 4) {
   1.352 +      // Someone missed the concept of a rect here
   1.353 +      saneRect = false;
   1.354 +    }
   1.355 +  } else {
   1.356 +    saneRect = false;
   1.357 +    flag = nsIScriptError::errorFlag;
   1.358 +  }
   1.359 +
   1.360 +  if (!saneRect) {
   1.361 +    logMessage(mArea, aSpec, flag, "ImageMapRectBoundsError");
   1.362 +  }
   1.363 +}
   1.364 +
   1.365 +bool RectArea::IsInside(nscoord x, nscoord y) const
   1.366 +{
   1.367 +  if (mNumCoords >= 4) {       // Note: > is for nav compatibility
   1.368 +    nscoord x1 = mCoords[0];
   1.369 +    nscoord y1 = mCoords[1];
   1.370 +    nscoord x2 = mCoords[2];
   1.371 +    nscoord y2 = mCoords[3];
   1.372 +    NS_ASSERTION(x1 <= x2 && y1 <= y2,
   1.373 +                 "Someone screwed up RectArea::ParseCoords");
   1.374 +    if ((x >= x1) && (x <= x2) && (y >= y1) && (y <= y2)) {
   1.375 +      return true;
   1.376 +    }
   1.377 +  }
   1.378 +  return false;
   1.379 +}
   1.380 +
   1.381 +void RectArea::Draw(nsIFrame* aFrame, nsRenderingContext& aRC)
   1.382 +{
   1.383 +  if (mHasFocus) {
   1.384 +    if (mNumCoords >= 4) {
   1.385 +      nscoord x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
   1.386 +      nscoord y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
   1.387 +      nscoord x2 = nsPresContext::CSSPixelsToAppUnits(mCoords[2]);
   1.388 +      nscoord y2 = nsPresContext::CSSPixelsToAppUnits(mCoords[3]);
   1.389 +      NS_ASSERTION(x1 <= x2 && y1 <= y2,
   1.390 +                   "Someone screwed up RectArea::ParseCoords");
   1.391 +      aRC.DrawLine(x1, y1, x1, y2);
   1.392 +      aRC.DrawLine(x1, y2, x2, y2);
   1.393 +      aRC.DrawLine(x1, y1, x2, y1);
   1.394 +      aRC.DrawLine(x2, y1, x2, y2);
   1.395 +    }
   1.396 +  }
   1.397 +}
   1.398 +
   1.399 +void RectArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
   1.400 +{
   1.401 +  if (mNumCoords >= 4) {
   1.402 +    nscoord x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
   1.403 +    nscoord y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
   1.404 +    nscoord x2 = nsPresContext::CSSPixelsToAppUnits(mCoords[2]);
   1.405 +    nscoord y2 = nsPresContext::CSSPixelsToAppUnits(mCoords[3]);
   1.406 +    NS_ASSERTION(x1 <= x2 && y1 <= y2,
   1.407 +                 "Someone screwed up RectArea::ParseCoords");
   1.408 +
   1.409 +    aRect.SetRect(x1, y1, x2, y2);
   1.410 +  }
   1.411 +}
   1.412 +
   1.413 +//----------------------------------------------------------------------
   1.414 +
   1.415 +class PolyArea : public Area {
   1.416 +public:
   1.417 +  PolyArea(nsIContent* aArea);
   1.418 +
   1.419 +  virtual void ParseCoords(const nsAString& aSpec) MOZ_OVERRIDE;
   1.420 +  virtual bool IsInside(nscoord x, nscoord y) const MOZ_OVERRIDE;
   1.421 +  virtual void Draw(nsIFrame* aFrame, nsRenderingContext& aRC) MOZ_OVERRIDE;
   1.422 +  virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) MOZ_OVERRIDE;
   1.423 +};
   1.424 +
   1.425 +PolyArea::PolyArea(nsIContent* aArea)
   1.426 +  : Area(aArea)
   1.427 +{
   1.428 +}
   1.429 +
   1.430 +void PolyArea::ParseCoords(const nsAString& aSpec)
   1.431 +{
   1.432 +  Area::ParseCoords(aSpec);
   1.433 +
   1.434 +  if (mNumCoords >= 2) {
   1.435 +    if (mNumCoords & 1U) {
   1.436 +      logMessage(mArea,
   1.437 +                 aSpec,
   1.438 +                 nsIScriptError::warningFlag,
   1.439 +                 "ImageMapPolyOddNumberOfCoords");
   1.440 +    }
   1.441 +  } else {
   1.442 +    logMessage(mArea,
   1.443 +               aSpec,
   1.444 +               nsIScriptError::errorFlag,
   1.445 +               "ImageMapPolyWrongNumberOfCoords");
   1.446 +  }
   1.447 +}
   1.448 +
   1.449 +bool PolyArea::IsInside(nscoord x, nscoord y) const
   1.450 +{
   1.451 +  if (mNumCoords >= 6) {
   1.452 +    int32_t intersects = 0;
   1.453 +    nscoord wherex = x;
   1.454 +    nscoord wherey = y;
   1.455 +    int32_t totalv = mNumCoords / 2;
   1.456 +    int32_t totalc = totalv * 2;
   1.457 +    nscoord xval = mCoords[totalc - 2];
   1.458 +    nscoord yval = mCoords[totalc - 1];
   1.459 +    int32_t end = totalc;
   1.460 +    int32_t pointer = 1;
   1.461 +
   1.462 +    if ((yval >= wherey) != (mCoords[pointer] >= wherey)) {
   1.463 +      if ((xval >= wherex) == (mCoords[0] >= wherex)) {
   1.464 +        intersects += (xval >= wherex) ? 1 : 0;
   1.465 +      } else {
   1.466 +        intersects += ((xval - (yval - wherey) *
   1.467 +                        (mCoords[0] - xval) /
   1.468 +                        (mCoords[pointer] - yval)) >= wherex) ? 1 : 0;
   1.469 +      }
   1.470 +    }
   1.471 +
   1.472 +    // XXX I wonder what this is doing; this is a translation of ptinpoly.c
   1.473 +    while (pointer < end)  {
   1.474 +      yval = mCoords[pointer];
   1.475 +      pointer += 2;
   1.476 +      if (yval >= wherey)  {
   1.477 +        while((pointer < end) && (mCoords[pointer] >= wherey))
   1.478 +          pointer+=2;
   1.479 +        if (pointer >= end)
   1.480 +          break;
   1.481 +        if ((mCoords[pointer-3] >= wherex) ==
   1.482 +            (mCoords[pointer-1] >= wherex)) {
   1.483 +          intersects += (mCoords[pointer-3] >= wherex) ? 1 : 0;
   1.484 +        } else {
   1.485 +          intersects +=
   1.486 +            ((mCoords[pointer-3] - (mCoords[pointer-2] - wherey) *
   1.487 +              (mCoords[pointer-1] - mCoords[pointer-3]) /
   1.488 +              (mCoords[pointer] - mCoords[pointer - 2])) >= wherex) ? 1:0;
   1.489 +        }
   1.490 +      }  else  {
   1.491 +        while((pointer < end) && (mCoords[pointer] < wherey))
   1.492 +          pointer+=2;
   1.493 +        if (pointer >= end)
   1.494 +          break;
   1.495 +        if ((mCoords[pointer-3] >= wherex) ==
   1.496 +            (mCoords[pointer-1] >= wherex)) {
   1.497 +          intersects += (mCoords[pointer-3] >= wherex) ? 1:0;
   1.498 +        } else {
   1.499 +          intersects +=
   1.500 +            ((mCoords[pointer-3] - (mCoords[pointer-2] - wherey) *
   1.501 +              (mCoords[pointer-1] - mCoords[pointer-3]) /
   1.502 +              (mCoords[pointer] - mCoords[pointer - 2])) >= wherex) ? 1:0;
   1.503 +        }
   1.504 +      }
   1.505 +    }
   1.506 +    if ((intersects & 1) != 0) {
   1.507 +      return true;
   1.508 +    }
   1.509 +  }
   1.510 +  return false;
   1.511 +}
   1.512 +
   1.513 +void PolyArea::Draw(nsIFrame* aFrame, nsRenderingContext& aRC)
   1.514 +{
   1.515 +  if (mHasFocus) {
   1.516 +    if (mNumCoords >= 6) {
   1.517 +      nscoord x0 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
   1.518 +      nscoord y0 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
   1.519 +      nscoord x1, y1;
   1.520 +      for (int32_t i = 2; i < mNumCoords; i += 2) {
   1.521 +        x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[i]);
   1.522 +        y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[i+1]);
   1.523 +        aRC.DrawLine(x0, y0, x1, y1);
   1.524 +        x0 = x1;
   1.525 +        y0 = y1;
   1.526 +      }
   1.527 +      x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
   1.528 +      y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
   1.529 +      aRC.DrawLine(x0, y0, x1, y1);
   1.530 +    }
   1.531 +  }
   1.532 +}
   1.533 +
   1.534 +void PolyArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
   1.535 +{
   1.536 +  if (mNumCoords >= 6) {
   1.537 +    nscoord x1, x2, y1, y2, xtmp, ytmp;
   1.538 +    x1 = x2 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
   1.539 +    y1 = y2 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
   1.540 +    for (int32_t i = 2; i < mNumCoords; i += 2) {
   1.541 +      xtmp = nsPresContext::CSSPixelsToAppUnits(mCoords[i]);
   1.542 +      ytmp = nsPresContext::CSSPixelsToAppUnits(mCoords[i+1]);
   1.543 +      x1 = x1 < xtmp ? x1 : xtmp;
   1.544 +      y1 = y1 < ytmp ? y1 : ytmp;
   1.545 +      x2 = x2 > xtmp ? x2 : xtmp;
   1.546 +      y2 = y2 > ytmp ? y2 : ytmp;
   1.547 +    }
   1.548 +
   1.549 +    aRect.SetRect(x1, y1, x2, y2);
   1.550 +  }
   1.551 +}
   1.552 +
   1.553 +//----------------------------------------------------------------------
   1.554 +
   1.555 +class CircleArea : public Area {
   1.556 +public:
   1.557 +  CircleArea(nsIContent* aArea);
   1.558 +
   1.559 +  virtual void ParseCoords(const nsAString& aSpec) MOZ_OVERRIDE;
   1.560 +  virtual bool IsInside(nscoord x, nscoord y) const MOZ_OVERRIDE;
   1.561 +  virtual void Draw(nsIFrame* aFrame, nsRenderingContext& aRC) MOZ_OVERRIDE;
   1.562 +  virtual void GetRect(nsIFrame* aFrame, nsRect& aRect) MOZ_OVERRIDE;
   1.563 +};
   1.564 +
   1.565 +CircleArea::CircleArea(nsIContent* aArea)
   1.566 +  : Area(aArea)
   1.567 +{
   1.568 +}
   1.569 +
   1.570 +void CircleArea::ParseCoords(const nsAString& aSpec)
   1.571 +{
   1.572 +  Area::ParseCoords(aSpec);
   1.573 +
   1.574 +  bool wrongNumberOfCoords = false;
   1.575 +  int32_t flag = nsIScriptError::warningFlag;
   1.576 +  if (mNumCoords >= 3) {
   1.577 +    if (mCoords[2] < 0) {
   1.578 +      logMessage(mArea,
   1.579 +                 aSpec,
   1.580 +                 nsIScriptError::errorFlag,
   1.581 +                 "ImageMapCircleNegativeRadius");
   1.582 +    }
   1.583 +
   1.584 +    if (mNumCoords > 3) {
   1.585 +      wrongNumberOfCoords = true;
   1.586 +    }
   1.587 +  } else {
   1.588 +    wrongNumberOfCoords = true;
   1.589 +    flag = nsIScriptError::errorFlag;
   1.590 +  }
   1.591 +
   1.592 +  if (wrongNumberOfCoords) {
   1.593 +    logMessage(mArea,
   1.594 +               aSpec,
   1.595 +               flag,
   1.596 +               "ImageMapCircleWrongNumberOfCoords");
   1.597 +  }
   1.598 +}
   1.599 +
   1.600 +bool CircleArea::IsInside(nscoord x, nscoord y) const
   1.601 +{
   1.602 +  // Note: > is for nav compatibility
   1.603 +  if (mNumCoords >= 3) {
   1.604 +    nscoord x1 = mCoords[0];
   1.605 +    nscoord y1 = mCoords[1];
   1.606 +    nscoord radius = mCoords[2];
   1.607 +    if (radius < 0) {
   1.608 +      return false;
   1.609 +    }
   1.610 +    nscoord dx = x1 - x;
   1.611 +    nscoord dy = y1 - y;
   1.612 +    nscoord dist = (dx * dx) + (dy * dy);
   1.613 +    if (dist <= (radius * radius)) {
   1.614 +      return true;
   1.615 +    }
   1.616 +  }
   1.617 +  return false;
   1.618 +}
   1.619 +
   1.620 +void CircleArea::Draw(nsIFrame* aFrame, nsRenderingContext& aRC)
   1.621 +{
   1.622 +  if (mHasFocus) {
   1.623 +    if (mNumCoords >= 3) {
   1.624 +      nscoord x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
   1.625 +      nscoord y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
   1.626 +      nscoord radius = nsPresContext::CSSPixelsToAppUnits(mCoords[2]);
   1.627 +      if (radius < 0) {
   1.628 +        return;
   1.629 +      }
   1.630 +      nscoord x = x1 - radius;
   1.631 +      nscoord y = y1 - radius;
   1.632 +      nscoord w = 2 * radius;
   1.633 +      aRC.DrawEllipse(x, y, w, w);
   1.634 +    }
   1.635 +  }
   1.636 +}
   1.637 +
   1.638 +void CircleArea::GetRect(nsIFrame* aFrame, nsRect& aRect)
   1.639 +{
   1.640 +  if (mNumCoords >= 3) {
   1.641 +    nscoord x1 = nsPresContext::CSSPixelsToAppUnits(mCoords[0]);
   1.642 +    nscoord y1 = nsPresContext::CSSPixelsToAppUnits(mCoords[1]);
   1.643 +    nscoord radius = nsPresContext::CSSPixelsToAppUnits(mCoords[2]);
   1.644 +    if (radius < 0) {
   1.645 +      return;
   1.646 +    }
   1.647 +
   1.648 +    aRect.SetRect(x1 - radius, y1 - radius, x1 + radius, y1 + radius);
   1.649 +  }
   1.650 +}
   1.651 +
   1.652 +//----------------------------------------------------------------------
   1.653 +
   1.654 +
   1.655 +nsImageMap::nsImageMap() :
   1.656 +  mImageFrame(nullptr),
   1.657 +  mContainsBlockContents(false)
   1.658 +{
   1.659 +}
   1.660 +
   1.661 +nsImageMap::~nsImageMap()
   1.662 +{
   1.663 +  NS_ASSERTION(mAreas.Length() == 0, "Destroy was not called");
   1.664 +}
   1.665 +
   1.666 +NS_IMPL_ISUPPORTS(nsImageMap,
   1.667 +                  nsIMutationObserver,
   1.668 +                  nsIDOMEventListener)
   1.669 +
   1.670 +nsresult
   1.671 +nsImageMap::GetBoundsForAreaContent(nsIContent *aContent,
   1.672 +                                    nsRect& aBounds)
   1.673 +{
   1.674 +  NS_ENSURE_TRUE(aContent && mImageFrame, NS_ERROR_INVALID_ARG);
   1.675 +
   1.676 +  // Find the Area struct associated with this content node, and return bounds
   1.677 +  uint32_t i, n = mAreas.Length();
   1.678 +  for (i = 0; i < n; i++) {
   1.679 +    Area* area = mAreas.ElementAt(i);
   1.680 +    if (area->mArea == aContent) {
   1.681 +      aBounds = nsRect();
   1.682 +      area->GetRect(mImageFrame, aBounds);
   1.683 +      return NS_OK;
   1.684 +    }
   1.685 +  }
   1.686 +  return NS_ERROR_FAILURE;
   1.687 +}
   1.688 +
   1.689 +void
   1.690 +nsImageMap::FreeAreas()
   1.691 +{
   1.692 +  uint32_t i, n = mAreas.Length();
   1.693 +  for (i = 0; i < n; i++) {
   1.694 +    Area* area = mAreas.ElementAt(i);
   1.695 +    if (area->mArea->IsInDoc()) {
   1.696 +      NS_ASSERTION(area->mArea->GetPrimaryFrame() == mImageFrame,
   1.697 +                   "Unexpected primary frame");
   1.698 +
   1.699 +      area->mArea->SetPrimaryFrame(nullptr);
   1.700 +    }
   1.701 +
   1.702 +    area->mArea->RemoveSystemEventListener(NS_LITERAL_STRING("focus"), this,
   1.703 +                                           false);
   1.704 +    area->mArea->RemoveSystemEventListener(NS_LITERAL_STRING("blur"), this,
   1.705 +                                           false);
   1.706 +    delete area;
   1.707 +  }
   1.708 +  mAreas.Clear();
   1.709 +}
   1.710 +
   1.711 +nsresult
   1.712 +nsImageMap::Init(nsImageFrame* aImageFrame, nsIContent* aMap)
   1.713 +{
   1.714 +  NS_PRECONDITION(aMap, "null ptr");
   1.715 +  if (!aMap) {
   1.716 +    return NS_ERROR_NULL_POINTER;
   1.717 +  }
   1.718 +  mImageFrame = aImageFrame;
   1.719 +
   1.720 +  mMap = aMap;
   1.721 +  mMap->AddMutationObserver(this);
   1.722 +
   1.723 +  // "Compile" the areas in the map into faster access versions
   1.724 +  return UpdateAreas();
   1.725 +}
   1.726 +
   1.727 +
   1.728 +nsresult
   1.729 +nsImageMap::SearchForAreas(nsIContent* aParent, bool& aFoundArea,
   1.730 +                           bool& aFoundAnchor)
   1.731 +{
   1.732 +  nsresult rv = NS_OK;
   1.733 +  uint32_t i, n = aParent->GetChildCount();
   1.734 +
   1.735 +  // Look for <area> or <a> elements. We'll use whichever type we find first.
   1.736 +  for (i = 0; i < n; i++) {
   1.737 +    nsIContent *child = aParent->GetChildAt(i);
   1.738 +
   1.739 +    if (child->IsHTML()) {
   1.740 +      // If we haven't determined that the map element contains an
   1.741 +      // <a> element yet, then look for <area>.
   1.742 +      if (!aFoundAnchor && child->Tag() == nsGkAtoms::area) {
   1.743 +        aFoundArea = true;
   1.744 +        rv = AddArea(child);
   1.745 +        NS_ENSURE_SUCCESS(rv, rv);
   1.746 +
   1.747 +        // Continue to next child. This stops mContainsBlockContents from
   1.748 +        // getting set. It also makes us ignore children of <area>s which
   1.749 +        // is consistent with how we react to dynamic insertion of such
   1.750 +        // children.
   1.751 +        continue;
   1.752 +      }
   1.753 +      // If we haven't determined that the map element contains an
   1.754 +      // <area> element yet, then look for <a>.
   1.755 +      if (!aFoundArea && child->Tag() == nsGkAtoms::a) {
   1.756 +        aFoundAnchor = true;
   1.757 +        rv = AddArea(child);
   1.758 +        NS_ENSURE_SUCCESS(rv, rv);
   1.759 +      }
   1.760 +    }
   1.761 +
   1.762 +    if (child->IsElement()) {
   1.763 +      mContainsBlockContents = true;
   1.764 +      rv = SearchForAreas(child, aFoundArea, aFoundAnchor);
   1.765 +      NS_ENSURE_SUCCESS(rv, rv);
   1.766 +    }
   1.767 +  }
   1.768 +
   1.769 +  return NS_OK;
   1.770 +}
   1.771 +
   1.772 +nsresult
   1.773 +nsImageMap::UpdateAreas()
   1.774 +{
   1.775 +  // Get rid of old area data
   1.776 +  FreeAreas();
   1.777 +
   1.778 +  bool foundArea = false;
   1.779 +  bool foundAnchor = false;
   1.780 +  mContainsBlockContents = false;
   1.781 +
   1.782 +  nsresult rv = SearchForAreas(mMap, foundArea, foundAnchor);
   1.783 +#ifdef ACCESSIBILITY
   1.784 +  if (NS_SUCCEEDED(rv)) {
   1.785 +    nsAccessibilityService* accService = GetAccService();
   1.786 +    if (accService) {
   1.787 +      accService->UpdateImageMap(mImageFrame);
   1.788 +    }
   1.789 +  }
   1.790 +#endif
   1.791 +  return rv;
   1.792 +}
   1.793 +
   1.794 +nsresult
   1.795 +nsImageMap::AddArea(nsIContent* aArea)
   1.796 +{
   1.797 +  static nsIContent::AttrValuesArray strings[] =
   1.798 +    {&nsGkAtoms::rect, &nsGkAtoms::rectangle,
   1.799 +     &nsGkAtoms::circle, &nsGkAtoms::circ,
   1.800 +     &nsGkAtoms::_default,
   1.801 +     &nsGkAtoms::poly, &nsGkAtoms::polygon,
   1.802 +     nullptr};
   1.803 +
   1.804 +  Area* area;
   1.805 +  switch (aArea->FindAttrValueIn(kNameSpaceID_None, nsGkAtoms::shape,
   1.806 +                                 strings, eIgnoreCase)) {
   1.807 +  case nsIContent::ATTR_VALUE_NO_MATCH:
   1.808 +  case nsIContent::ATTR_MISSING:
   1.809 +  case 0:
   1.810 +  case 1:
   1.811 +    area = new RectArea(aArea);
   1.812 +    break;
   1.813 +  case 2:
   1.814 +  case 3:
   1.815 +    area = new CircleArea(aArea);
   1.816 +    break;
   1.817 +  case 4:
   1.818 +    area = new DefaultArea(aArea);
   1.819 +    break;
   1.820 +  case 5:
   1.821 +  case 6:
   1.822 +    area = new PolyArea(aArea);
   1.823 +    break;
   1.824 +  default:
   1.825 +    area = nullptr;
   1.826 +    NS_NOTREACHED("FindAttrValueIn returned an unexpected value.");
   1.827 +    break;
   1.828 +  }
   1.829 +  if (!area)
   1.830 +    return NS_ERROR_OUT_OF_MEMORY;
   1.831 +
   1.832 +  //Add focus listener to track area focus changes
   1.833 +  aArea->AddSystemEventListener(NS_LITERAL_STRING("focus"), this, false,
   1.834 +                                false);
   1.835 +  aArea->AddSystemEventListener(NS_LITERAL_STRING("blur"), this, false,
   1.836 +                                false);
   1.837 +
   1.838 +  // This is a nasty hack.  It needs to go away: see bug 135040.  Once this is
   1.839 +  // removed, the code added to RestyleManager::RestyleElement,
   1.840 +  // nsCSSFrameConstructor::ContentRemoved (both hacks there), and
   1.841 +  // RestyleManager::ProcessRestyledFrames to work around this issue can
   1.842 +  // be removed.
   1.843 +  aArea->SetPrimaryFrame(mImageFrame);
   1.844 +
   1.845 +  nsAutoString coords;
   1.846 +  aArea->GetAttr(kNameSpaceID_None, nsGkAtoms::coords, coords);
   1.847 +  area->ParseCoords(coords);
   1.848 +  mAreas.AppendElement(area);
   1.849 +  return NS_OK;
   1.850 +}
   1.851 +
   1.852 +nsIContent*
   1.853 +nsImageMap::GetArea(nscoord aX, nscoord aY) const
   1.854 +{
   1.855 +  NS_ASSERTION(mMap, "Not initialized");
   1.856 +  uint32_t i, n = mAreas.Length();
   1.857 +  for (i = 0; i < n; i++) {
   1.858 +    Area* area = mAreas.ElementAt(i);
   1.859 +    if (area->IsInside(aX, aY)) {
   1.860 +      return area->mArea;
   1.861 +    }
   1.862 +  }
   1.863 +
   1.864 +  return nullptr;
   1.865 +}
   1.866 +
   1.867 +nsIContent*
   1.868 +nsImageMap::GetAreaAt(uint32_t aIndex) const
   1.869 +{
   1.870 +  return mAreas.ElementAt(aIndex)->mArea;
   1.871 +}
   1.872 +
   1.873 +void
   1.874 +nsImageMap::Draw(nsIFrame* aFrame, nsRenderingContext& aRC)
   1.875 +{
   1.876 +  uint32_t i, n = mAreas.Length();
   1.877 +  for (i = 0; i < n; i++) {
   1.878 +    Area* area = mAreas.ElementAt(i);
   1.879 +    area->Draw(aFrame, aRC);
   1.880 +  }
   1.881 +}
   1.882 +
   1.883 +void
   1.884 +nsImageMap::MaybeUpdateAreas(nsIContent *aContent)
   1.885 +{
   1.886 +  if (aContent == mMap || mContainsBlockContents) {
   1.887 +    UpdateAreas();
   1.888 +  }
   1.889 +}
   1.890 +
   1.891 +void
   1.892 +nsImageMap::AttributeChanged(nsIDocument*  aDocument,
   1.893 +                             dom::Element* aElement,
   1.894 +                             int32_t       aNameSpaceID,
   1.895 +                             nsIAtom*      aAttribute,
   1.896 +                             int32_t       aModType)
   1.897 +{
   1.898 +  // If the parent of the changing content node is our map then update
   1.899 +  // the map.  But only do this if the node is an HTML <area> or <a>
   1.900 +  // and the attribute that's changing is "shape" or "coords" -- those
   1.901 +  // are the only cases we care about.
   1.902 +  if ((aElement->NodeInfo()->Equals(nsGkAtoms::area) ||
   1.903 +       aElement->NodeInfo()->Equals(nsGkAtoms::a)) &&
   1.904 +      aElement->IsHTML() &&
   1.905 +      aNameSpaceID == kNameSpaceID_None &&
   1.906 +      (aAttribute == nsGkAtoms::shape ||
   1.907 +       aAttribute == nsGkAtoms::coords)) {
   1.908 +    MaybeUpdateAreas(aElement->GetParent());
   1.909 +  } else if (aElement == mMap &&
   1.910 +             aNameSpaceID == kNameSpaceID_None &&
   1.911 +             (aAttribute == nsGkAtoms::name ||
   1.912 +              aAttribute == nsGkAtoms::id) &&
   1.913 +             mImageFrame) {
   1.914 +    // ID or name has changed. Let ImageFrame recreate ImageMap.
   1.915 +    mImageFrame->DisconnectMap();
   1.916 +  }
   1.917 +}
   1.918 +
   1.919 +void
   1.920 +nsImageMap::ContentAppended(nsIDocument *aDocument,
   1.921 +                            nsIContent* aContainer,
   1.922 +                            nsIContent* aFirstNewContent,
   1.923 +                            int32_t     /* unused */)
   1.924 +{
   1.925 +  MaybeUpdateAreas(aContainer);
   1.926 +}
   1.927 +
   1.928 +void
   1.929 +nsImageMap::ContentInserted(nsIDocument *aDocument,
   1.930 +                            nsIContent* aContainer,
   1.931 +                            nsIContent* aChild,
   1.932 +                            int32_t /* unused */)
   1.933 +{
   1.934 +  MaybeUpdateAreas(aContainer);
   1.935 +}
   1.936 +
   1.937 +void
   1.938 +nsImageMap::ContentRemoved(nsIDocument *aDocument,
   1.939 +                           nsIContent* aContainer,
   1.940 +                           nsIContent* aChild,
   1.941 +                           int32_t aIndexInContainer,
   1.942 +                           nsIContent* aPreviousSibling)
   1.943 +{
   1.944 +  MaybeUpdateAreas(aContainer);
   1.945 +}
   1.946 +
   1.947 +void
   1.948 +nsImageMap::ParentChainChanged(nsIContent* aContent)
   1.949 +{
   1.950 +  NS_ASSERTION(aContent == mMap,
   1.951 +               "Unexpected ParentChainChanged notification!");
   1.952 +  if (mImageFrame) {
   1.953 +    mImageFrame->DisconnectMap();
   1.954 +  }
   1.955 +}
   1.956 +
   1.957 +nsresult
   1.958 +nsImageMap::HandleEvent(nsIDOMEvent* aEvent)
   1.959 +{
   1.960 +  nsAutoString eventType;
   1.961 +  aEvent->GetType(eventType);
   1.962 +  bool focus = eventType.EqualsLiteral("focus");
   1.963 +  NS_ABORT_IF_FALSE(focus == !eventType.EqualsLiteral("blur"),
   1.964 +                    "Unexpected event type");
   1.965 +
   1.966 +  //Set which one of our areas changed focus
   1.967 +  nsCOMPtr<nsIContent> targetContent = do_QueryInterface(
   1.968 +    aEvent->InternalDOMEvent()->GetTarget());
   1.969 +  if (!targetContent) {
   1.970 +    return NS_OK;
   1.971 +  }
   1.972 +  uint32_t i, n = mAreas.Length();
   1.973 +  for (i = 0; i < n; i++) {
   1.974 +    Area* area = mAreas.ElementAt(i);
   1.975 +    if (area->mArea == targetContent) {
   1.976 +      //Set or Remove internal focus
   1.977 +      area->HasFocus(focus);
   1.978 +      //Now invalidate the rect
   1.979 +      if (mImageFrame) {
   1.980 +        mImageFrame->InvalidateFrame();
   1.981 +      }
   1.982 +      break;
   1.983 +    }
   1.984 +  }
   1.985 +  return NS_OK;
   1.986 +}
   1.987 +
   1.988 +void
   1.989 +nsImageMap::Destroy(void)
   1.990 +{
   1.991 +  FreeAreas();
   1.992 +  mImageFrame = nullptr;
   1.993 +  mMap->RemoveMutationObserver(this);
   1.994 +}

mercurial