Bug 979692 - Add fixes for hitregion hit detection. r=roc

This commit is contained in:
Rik Cabanier 2014-05-18 19:43:00 -04:00
parent 1e437be22f
commit c820dfd497
4 changed files with 78 additions and 73 deletions

View File

@ -82,7 +82,9 @@
#include "mozilla/MouseEvents.h"
#include "mozilla/unused.h"
#include "mozilla/Preferences.h"
#include "mozilla/dom/CanvasRenderingContext2D.h"
#include "mozilla/dom/Element.h"
#include "mozilla/dom/HTMLCanvasElement.h"
#include "mozilla/dom/TreeWalker.h"
using namespace mozilla;
@ -921,22 +923,27 @@ Accessible::GetBoundsRect(nsRect& aTotalBounds, nsIFrame** aBoundingFrame)
{
nsIFrame* frame = GetFrame();
if (frame && mContent) {
nsRect* hitRegionRect = static_cast<nsRect*>(mContent->GetProperty(nsGkAtoms::hitregion));
bool* hasHitRegionRect = static_cast<bool*>(mContent->GetProperty(nsGkAtoms::hitregion));
if (hitRegionRect) {
if (hasHitRegionRect && mContent->IsElement()) {
// This is for canvas fallback content
// Find a canvas frame the found hit region is relative to.
nsIFrame* canvasFrame = frame->GetParent();
while (canvasFrame && (canvasFrame->GetType() != nsGkAtoms::HTMLCanvasFrame))
canvasFrame = canvasFrame->GetParent();
if (canvasFrame) {
canvasFrame = nsLayoutUtils::GetClosestFrameOfType(canvasFrame, nsGkAtoms::HTMLCanvasFrame);
}
// make the canvas the bounding frame
if (canvasFrame) {
*aBoundingFrame = canvasFrame;
dom::HTMLCanvasElement *canvas =
dom::HTMLCanvasElement::FromContent(canvasFrame->GetContent());
aTotalBounds = *hitRegionRect;
return;
// get the bounding rect of the hit region
if (canvas && canvas->CountContexts() &&
canvas->GetContextAtIndex(0)->GetHitRegionRect(mContent->AsElement(), aTotalBounds)) {
return;
}
}
}

View File

@ -112,6 +112,9 @@ public:
NS_IMETHOD SetContextOptions(JSContext* aCx, JS::Handle<JS::Value> aOptions) { return NS_OK; }
// return true and fills in the bounding rect if elementis a child and has a hit region.
virtual bool GetHitRegionRect(mozilla::dom::Element* aElement, nsRect& aRect) { return false; }
//
// shmem support
//

View File

@ -606,15 +606,6 @@ CanvasRenderingContext2D::ParseColor(const nsAString& aString,
return true;
}
#ifdef ACCESSIBILITY
PLDHashOperator
CanvasRenderingContext2D::RemoveHitRegionProperty(RegionInfo* aEntry, void*)
{
aEntry->mElement->DeleteProperty(nsGkAtoms::hitregion);
return PL_DHASH_NEXT;
}
#endif
nsresult
CanvasRenderingContext2D::Reset()
{
@ -632,10 +623,7 @@ CanvasRenderingContext2D::Reset()
mStream = nullptr;
// reset hit regions
#ifdef ACCESSIBILITY
mHitRegionsOptions.EnumerateEntries(RemoveHitRegionProperty, nullptr);
#endif
mHitRegionsOptions.Clear();
mHitRegionsOptions.ClearAndRetainStorage();
// Since the target changes the backing texture will change, and this will
// no longer be valid.
@ -2450,24 +2438,6 @@ CanvasRenderingContext2D::MeasureText(const nsAString& rawText,
void
CanvasRenderingContext2D::AddHitRegion(const HitRegionOptions& options, ErrorResult& error)
{
// remove old hit region first
RemoveHitRegion(options.mId);
// for now, we require a fallback element
if (options.mControl == NULL) {
error.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return;
}
#ifdef ACCESSIBILITY
// check if the control is a descendant of our canvas
HTMLCanvasElement* canvas = GetCanvas();
bool isDescendant = true;
if (!canvas || !nsContentUtils::ContentIsDescendantOf(options.mControl, canvas)) {
isDescendant = false;
}
#endif
// check if the path is valid
EnsureUserSpacePath(CanvasWindingRule::Nonzero);
if(!mPath) {
@ -2483,35 +2453,66 @@ CanvasRenderingContext2D::AddHitRegion(const HitRegionOptions& options, ErrorRes
return;
}
#ifdef ACCESSIBILITY
if (isDescendant) {
nsRect* nsBounds = new nsRect();
gfxRect rect(bounds.x, bounds.y, bounds.width, bounds.height);
*nsBounds = nsLayoutUtils::RoundGfxRectToAppRect(rect, AppUnitsPerCSSPixel());
options.mControl->DeleteProperty(nsGkAtoms::hitregion);
options.mControl->SetProperty(nsGkAtoms::hitregion, nsBounds,
nsINode::DeleteProperty<nsRect>);
}
#endif
// remove old hit region first
RemoveHitRegion(options.mId);
// finally, add the region to the list if it has an ID
if (options.mId.Length() != 0) {
mHitRegionsOptions.PutEntry(options.mId)->mElement = options.mControl;
if (options.mControl) {
// also remove regions with this control
for (unsigned int x = 0; x < mHitRegionsOptions.Length(); x++) {
RegionInfo& info = mHitRegionsOptions[x];
if (info.mElement == options.mControl) {
mHitRegionsOptions.RemoveElementAt(x);
break;
}
}
#ifdef ACCESSIBILITY
options.mControl->SetProperty(nsGkAtoms::hitregion, new bool(true),
nsINode::DeleteProperty<bool>);
#endif
}
// finally, add the region to the list
RegionInfo info;
info.mId = options.mId;
info.mElement = options.mControl;
RefPtr<PathBuilder> pathBuilder = mPath->TransformedCopyToBuilder(mTarget->GetTransform());
info.mPath = pathBuilder->Finish();
mHitRegionsOptions.InsertElementAt(0, info);
}
void
CanvasRenderingContext2D::RemoveHitRegion(const nsAString& id)
{
RegionInfo* info = mHitRegionsOptions.GetEntry(id);
if (!info) {
return;
if (id.Length() == 0) {
return;
}
for (unsigned int x = 0; x < mHitRegionsOptions.Length(); x++) {
RegionInfo& info = mHitRegionsOptions[x];
if (info.mId == id) {
mHitRegionsOptions.RemoveElementAt(x);
return;
}
}
}
bool
CanvasRenderingContext2D::GetHitRegionRect(Element* aElement, nsRect& aRect)
{
for (unsigned int x = 0; x < mHitRegionsOptions.Length(); x++) {
RegionInfo& info = mHitRegionsOptions[x];
if (info.mElement == aElement) {
mgfx::Rect bounds(info.mPath->GetBounds());
gfxRect rect(bounds.x, bounds.y, bounds.width, bounds.height);
aRect = nsLayoutUtils::RoundGfxRectToAppRect(rect, AppUnitsPerCSSPixel());
return true;
}
}
#ifdef ACCESSIBILITY
info->mElement->DeleteProperty(nsGkAtoms::hitregion);
#endif
mHitRegionsOptions.RemoveEntry(id);
return false;
}
/**

View File

@ -537,6 +537,9 @@ public:
virtual void GetImageBuffer(uint8_t** aImageBuffer, int32_t* aFormat);
// return true and fills in the bound rect if element has a hit region.
bool GetHitRegionRect(Element* aElement, nsRect& aRect);
protected:
nsresult GetImageDataArray(JSContext* aCx, int32_t aX, int32_t aY,
uint32_t aWidth, uint32_t aHeight,
@ -753,25 +756,16 @@ protected:
/**
* State information for hit regions
*/
struct RegionInfo : public nsStringHashKey
struct RegionInfo
{
RegionInfo(const nsAString& aKey) :
nsStringHashKey(&aKey)
{
}
RegionInfo(const nsAString *aKey) :
nsStringHashKey(aKey)
{
}
nsString mId;
// fallback element for a11y
nsRefPtr<Element> mElement;
// Path of the hit region in the 2d context coordinate space (not user space)
RefPtr<gfx::Path> mPath;
};
#ifdef ACCESSIBILITY
static PLDHashOperator RemoveHitRegionProperty(RegionInfo* aEntry, void* aData);
#endif
nsTHashtable<RegionInfo> mHitRegionsOptions;
nsTArray<RegionInfo> mHitRegionsOptions;
/**
* Returns true if a shadow should be drawn along with a