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 +}