зеркало из https://github.com/mozilla/gecko-dev.git
Bug 979692 - Add fixes for hitregion hit detection. r=roc
This commit is contained in:
Родитель
ec972af3a6
Коммит
c797577640
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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
|
||||
|
|
Загрузка…
Ссылка в новой задаче