Bug 805746 - Send CustomScroll domevent to browser.js. r=cjones

This commit is contained in:
Benjamin Chen 2012-12-25 14:09:34 +08:00
parent 95f613b3e2
commit f016ff4111
13 changed files with 362 additions and 35 deletions

View File

@ -496,6 +496,7 @@ using mozilla::dom::indexedDB::IDBWrapperCache;
#include "DOMError.h"
#include "DOMRequest.h"
#include "nsIOpenWindowEventDetail.h"
#include "nsIAsyncScrollEventDetail.h"
#include "nsIDOMGlobalObjectConstructor.h"
#include "nsIDOMCanvasRenderingContext2D.h"
#include "DOMFileHandle.h"
@ -1544,6 +1545,8 @@ static nsDOMClassInfoData sClassInfoData[] = {
EVENTTARGET_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(OpenWindowEventDetail, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(AsyncScrollEventDetail, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA_WITH_NAME(DOMFileHandle, FileHandle, nsEventTargetSH,
EVENTTARGET_SCRIPTABLE_FLAGS)
@ -4016,6 +4019,10 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_MAP_ENTRY(nsIOpenWindowEventDetail)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(AsyncScrollEventDetail, nsIAsyncScrollEventDetail)
DOM_CLASSINFO_MAP_ENTRY(nsIAsyncScrollEventDetail)
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(DOMFileHandle, nsIDOMFileHandle)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMFileHandle)
DOM_CLASSINFO_MAP_END

View File

@ -453,6 +453,7 @@ DOMCI_CLASS(CameraCapabilities)
DOMCI_CLASS(DOMError)
DOMCI_CLASS(DOMRequest)
DOMCI_CLASS(OpenWindowEventDetail)
DOMCI_CLASS(AsyncScrollEventDetail)
DOMCI_CLASS(DOMFileHandle)
DOMCI_CLASS(FileRequest)

View File

@ -19,6 +19,7 @@
#include "nsIDOMCustomEvent.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsVariant.h"
#include "nsAsyncScrollEventDetail.h"
using mozilla::dom::Element;
using mozilla::dom::TabParent;
@ -68,6 +69,40 @@ CreateIframe(Element* aOpenerFrameElement, const nsAString& aName, bool aRemote)
return popupFrameElement.forget();
}
bool
DispatchCustomDOMEvent(Element* aFrameElement, const nsAString& aEventName,
nsISupports *aDetailValue)
{
NS_ENSURE_TRUE(aFrameElement, false);
nsIPresShell *shell = aFrameElement->OwnerDoc()->GetShell();
nsRefPtr<nsPresContext> presContext;
if (shell) {
presContext = shell->GetPresContext();
}
nsCOMPtr<nsIDOMEvent> domEvent;
nsEventDispatcher::CreateEvent(presContext, nullptr,
NS_LITERAL_STRING("customevent"),
getter_AddRefs(domEvent));
NS_ENSURE_TRUE(domEvent, false);
nsCOMPtr<nsIWritableVariant> detailVariant = new nsVariant();
nsresult rv = detailVariant->SetAsISupports(aDetailValue);
NS_ENSURE_SUCCESS(rv, false);
nsCOMPtr<nsIDOMCustomEvent> customEvent = do_QueryInterface(domEvent);
NS_ENSURE_TRUE(customEvent, false);
customEvent->InitCustomEvent(aEventName,
/* bubbles = */ true,
/* cancelable = */ false,
detailVariant);
customEvent->SetTrusted(true);
// Dispatch the event.
nsEventStatus status = nsEventStatus_eIgnore;
rv = nsEventDispatcher::DispatchDOMEvent(aFrameElement, nullptr,
domEvent, presContext, &status);
return NS_SUCCEEDED(rv);
}
/**
* Dispatch a mozbrowseropenwindow event to the given opener frame element.
* The "popup iframe" (event.detail.frameElement) will be |aPopupFrameElement|.
@ -90,40 +125,15 @@ DispatchOpenWindowEvent(Element* aOpenerFrameElement,
nsRefPtr<nsOpenWindowEventDetail> detail =
new nsOpenWindowEventDetail(aURL, aName, aFeatures,
aPopupFrameElement->AsDOMNode());
nsCOMPtr<nsIWritableVariant> detailVariant = new nsVariant();
nsresult rv = detailVariant->SetAsISupports(detail);
NS_ENSURE_SUCCESS(rv, false);
// Create the CustomEvent.
nsIPresShell *shell = aOpenerFrameElement->OwnerDoc()->GetShell();
nsRefPtr<nsPresContext> presContext;
if (shell) {
presContext = shell->GetPresContext();
}
nsCOMPtr<nsIDOMEvent> domEvent;
nsEventDispatcher::CreateEvent(presContext, nullptr,
NS_LITERAL_STRING("customevent"),
getter_AddRefs(domEvent));
NS_ENSURE_TRUE(domEvent, false);
nsCOMPtr<nsIDOMCustomEvent> customEvent = do_QueryInterface(domEvent);
NS_ENSURE_TRUE(customEvent, false);
customEvent->InitCustomEvent(NS_LITERAL_STRING("mozbrowseropenwindow"),
/* bubbles = */ true,
/* cancelable = */ false,
detailVariant);
customEvent->SetTrusted(true);
// Dispatch the event.
nsEventStatus status = nsEventStatus_eIgnore;
rv = nsEventDispatcher::DispatchDOMEvent(aOpenerFrameElement, nullptr,
domEvent, presContext, &status);
NS_ENSURE_SUCCESS(rv, false);
bool dispatchSucceeded =
DispatchCustomDOMEvent(aOpenerFrameElement,
NS_LITERAL_STRING("mozbrowseropenwindow"),
detail);
// If the iframe is not in some document's DOM at this point, the embedder
// has "blocked" the popup.
return aPopupFrameElement->IsInDoc();
return (dispatchSucceeded && aPopupFrameElement->IsInDoc());
}
} // anonymous namespace
@ -131,8 +141,8 @@ DispatchOpenWindowEvent(Element* aOpenerFrameElement,
namespace mozilla {
/*static*/ bool
BrowserElementParent::OpenWindowOOP(mozilla::dom::TabParent* aOpenerTabParent,
mozilla::dom::TabParent* aPopupTabParent,
BrowserElementParent::OpenWindowOOP(TabParent* aOpenerTabParent,
TabParent* aPopupTabParent,
const nsAString& aURL,
const nsAString& aName,
const nsAString& aFeatures)
@ -230,4 +240,21 @@ BrowserElementParent::OpenWindowInProcess(nsIDOMWindow* aOpenerWindow,
return !!*aReturnWindow;
}
bool
BrowserElementParent::DispatchAsyncScrollEvent(TabParent* aTabParent,
const gfx::Rect& aContentRect,
const gfx::Size& aContentSize)
{
nsIDOMElement* element = aTabParent->GetOwnerElement();
nsCOMPtr<Element> frameElement = do_QueryInterface(element);
// Create the event's detail object.
nsRefPtr<nsAsyncScrollEventDetail> detail =
new nsAsyncScrollEventDetail(aContentRect.x, aContentRect.y,
aContentRect.width, aContentRect.height,
aContentSize.width, aContentSize.height);
return DispatchCustomDOMEvent(frameElement,
NS_LITERAL_STRING("mozbrowserasyncscroll"),
detail);
}
} // namespace mozilla

View File

@ -16,6 +16,11 @@ namespace dom {
class TabParent;
}
namespace gfx{
struct Rect;
struct Size;
}
/**
* BrowserElementParent implements a portion of the parent-process side of
* <iframe mozbrowser>.
@ -63,8 +68,8 @@ public:
* window.open request.
*/
static bool
OpenWindowOOP(mozilla::dom::TabParent* aOpenerTabParent,
mozilla::dom::TabParent* aPopupTabParent,
OpenWindowOOP(dom::TabParent* aOpenerTabParent,
dom::TabParent* aPopupTabParent,
const nsAString& aURL,
const nsAString& aName,
const nsAString& aFeatures);
@ -86,6 +91,26 @@ public:
const nsAString& aName,
const nsACString& aFeatures,
nsIDOMWindow** aReturnWindow);
/**
* Fire a mozbrowserasyncscroll CustomEvent on the given TabParent's frame element.
* This event's detail is an instance of nsIAsyncScrollEventDetail.
*
* @param aContentRect: The portion of the page which is currently visible
* onscreen in CSS pixels.
*
* @param aContentSize: The content width/height in CSS pixels.
*
* aContentRect.top + aContentRect.height may be larger than aContentSize.height.
* This indicates that the content is over-scrolled, which occurs when the
* page "rubber-bands" after being scrolled all the way to the bottom.
* Similarly, aContentRect.left + aContentRect.width may be greater than
* contentSize.width, and both left and top may be negative.
*/
static bool
DispatchAsyncScrollEvent(dom::TabParent* aTabParent,
const gfx::Rect& aContentRect,
const gfx::Size& aContentSize);
};
} // namespace mozilla

View File

@ -22,10 +22,12 @@ TEST_DIRS += mochitest
XPIDLSRCS = \
nsIOpenWindowEventDetail.idl \
nsIAsyncScrollEventDetail.idl \
$(NULL)
EXPORTS = \
nsOpenWindowEventDetail.h \
nsAsyncScrollEventDetail.h \
$(NULL)
EXPORTS_NAMESPACES = mozilla
@ -35,6 +37,7 @@ EXPORTS_mozilla = \
CPPSRCS = \
nsOpenWindowEventDetail.cpp \
nsAsyncScrollEventDetail.cpp \
BrowserElementParent.cpp \
$(NULL)

View File

@ -0,0 +1,62 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsAsyncScrollEventDetail.h"
#include "nsDOMClassInfoID.h"
#include "nsIDOMClassInfo.h"
#include "nsIClassInfo.h"
#include "nsDOMClassInfo.h"
NS_IMPL_ADDREF(nsAsyncScrollEventDetail)
NS_IMPL_RELEASE(nsAsyncScrollEventDetail)
NS_INTERFACE_MAP_BEGIN(nsAsyncScrollEventDetail)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY(nsIAsyncScrollEventDetail)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(AsyncScrollEventDetail)
NS_INTERFACE_MAP_END
DOMCI_DATA(AsyncScrollEventDetail, nsAsyncScrollEventDetail)
/* readonly attribute float top; */
NS_IMETHODIMP nsAsyncScrollEventDetail::GetTop(float *aTop)
{
*aTop = mTop;
return NS_OK;
}
/* readonly attribute float left; */
NS_IMETHODIMP nsAsyncScrollEventDetail::GetLeft(float *aLeft)
{
*aLeft = mLeft;
return NS_OK;
}
/* readonly attribute float width; */
NS_IMETHODIMP nsAsyncScrollEventDetail::GetWidth(float *aWidth)
{
*aWidth = mWidth;
return NS_OK;
}
/* readonly attribute float height; */
NS_IMETHODIMP nsAsyncScrollEventDetail::GetHeight(float *aHeight)
{
*aHeight = mHeight;
return NS_OK;
}
/* readonly attribute float scrollWidth; */
NS_IMETHODIMP nsAsyncScrollEventDetail::GetScrollWidth(float *aScrollWidth)
{
*aScrollWidth = mScrollWidth;
return NS_OK;
}
/* readonly attribute float scrollHeight; */
NS_IMETHODIMP nsAsyncScrollEventDetail::GetScrollHeight(float *aScrollHeight)
{
*aScrollHeight = mScrollHeight;
return NS_OK;
}

View File

@ -0,0 +1,36 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsIAsyncScrollEventDetail.h"
/**
* When we send a mozbrowserasyncscroll event (an instance of CustomEvent), we
* use an instance of this class as the event's detail.
*/
class nsAsyncScrollEventDetail : public nsIAsyncScrollEventDetail
{
public:
nsAsyncScrollEventDetail(const float left, const float top,
const float width, const float height,
const float contentWidth, const float contentHeigh)
: mTop(top)
, mLeft(left)
, mWidth(width)
, mHeight(height)
, mScrollWidth(contentWidth)
, mScrollHeight(contentHeigh)
{}
NS_DECL_ISUPPORTS
NS_DECL_NSIASYNCSCROLLEVENTDETAIL
private:
virtual ~nsAsyncScrollEventDetail() {}
const float mTop;
const float mLeft;
const float mWidth;
const float mHeight;
const float mScrollWidth;
const float mScrollHeight;
};

View File

@ -0,0 +1,29 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "nsISupports.idl"
/**
* When we send a mozbrowserasyncscroll event (an instance of CustomEvent), we
* use an instance of this interface as the event's detail.
* [left, top, width, height]: The portion of the page which is currently
* visible onscreen in CSS pixels.
* [scrollWidth, scrollHeight]: The content width/height in CSS pixels.
*
* top + height may be larger than scrollHeight.
* This indicates that the content is over-scrolled, which occurs when the
* page "rubber-bands" after being scrolled all the way to the bottom.
* Similarly, left + width may be greater than scrollWidth,
* and both left and top may be negative.
*/
[scriptable, uuid(d0c13577-31e6-4701-b9b7-3535bbe19fe6)]
interface nsIAsyncScrollEventDetail : nsISupports
{
readonly attribute float top;
readonly attribute float left;
readonly attribute float width;
readonly attribute float height;
readonly attribute float scrollWidth;
readonly attribute float scrollHeight;
};

View File

@ -528,7 +528,8 @@ var interfaceNamesInGlobalScope =
"RTCPeerConnection",
"LocalMediaStream",
"CSSConditionRule",
"CSSGroupingRule"
"CSSGroupingRule",
"AsyncScrollEventDetail"
]
for (var i in SpecialPowers.Components.interfaces) {

View File

@ -92,11 +92,18 @@ AsyncPanZoomController::AsyncPanZoomController(GeckoContentController* aGeckoCon
mMonitor("AsyncPanZoomController"),
mLastSampleTime(TimeStamp::Now()),
mState(NOTHING),
mLastAsyncScrollTime(TimeStamp::Now()),
mLastAsyncScrollOffset(0, 0),
mCurrentAsyncScrollOffset(0, 0),
mAsyncScrollTimeoutTask(nullptr),
mAsyncScrollThrottleTime(100),
mAsyncScrollTimeout(300),
mDPI(72),
mWaitingForContentToPaint(false),
mDisableNextTouchBatch(false),
mHandlingTouchQueue(false)
{
MOZ_ASSERT(NS_IsMainThread());
if (aGestures == USE_GESTURE_DETECTOR) {
mGestureEventListener = new GestureEventListener(this);
}
@ -109,6 +116,9 @@ AsyncPanZoomController::AsyncPanZoomController(GeckoContentController* aGeckoCon
nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE));
ClearOnShutdown(&gComputedTimingFunction);
}
Preferences::GetUint("apzc.asyncscroll.throttle", &mAsyncScrollThrottleTime);
Preferences::GetUint("apzc.asyncscroll.timeout", &mAsyncScrollTimeout);
}
AsyncPanZoomController::~AsyncPanZoomController() {
@ -367,6 +377,11 @@ nsEventStatus AsyncPanZoomController::OnTouchEnd(const MultiTouchInput& aEvent)
return nsEventStatus_eIgnore;
}
{
MonitorAutoLock monitor(mMonitor);
SendAsyncScrollEvent();
}
switch (mState) {
case FLING:
// Should never happen.
@ -679,6 +694,7 @@ bool AsyncPanZoomController::DoFling(const TimeDuration& aDelta) {
// Bring the resolution back in sync with the zoom, in case we scaled down
// the zoom while accelerating.
SetZoomAndResolution(mFrameMetrics.mZoom.width);
SendAsyncScrollEvent();
RequestContentRepaint();
mState = NOTHING;
return false;
@ -953,6 +969,8 @@ void AsyncPanZoomController::RequestContentRepaint() {
return;
}
SendAsyncScrollEvent();
// Cache the zoom since we're temporarily changing it for
// acceleration-scaled painting.
gfxFloat actualZoom = mFrameMetrics.mZoom.width;
@ -979,6 +997,16 @@ void AsyncPanZoomController::RequestContentRepaint() {
mFrameMetrics.mZoom = gfxSize(actualZoom, actualZoom);
}
void
AsyncPanZoomController::FireAsyncScrollOnTimeout()
{
if (mCurrentAsyncScrollOffset != mLastAsyncScrollOffset) {
MonitorAutoLock monitor(mMonitor);
SendAsyncScrollEvent();
}
mAsyncScrollTimeoutTask = nullptr;
}
bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSampleTime,
ContainerLayer* aLayer,
ViewTransform* aNewTransform) {
@ -1036,6 +1064,7 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
// Bring the resolution in sync with the zoom.
SetZoomAndResolution(mFrameMetrics.mZoom.width);
mState = NOTHING;
SendAsyncScrollEvent();
RequestContentRepaint();
}
@ -1056,6 +1085,34 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa
}
scrollOffset = gfxPoint(mFrameMetrics.mScrollOffset.x, mFrameMetrics.mScrollOffset.y);
mCurrentAsyncScrollOffset = mFrameMetrics.mScrollOffset;
}
// Cancel the mAsyncScrollTimeoutTask because we will fire a
// mozbrowserasyncscroll event or renew the mAsyncScrollTimeoutTask again.
if (mAsyncScrollTimeoutTask) {
mAsyncScrollTimeoutTask->Cancel();
mAsyncScrollTimeoutTask = nullptr;
}
// Fire the mozbrowserasyncscroll event immediately if it's been
// sAsyncScrollThrottleTime ms since the last time we fired the event and the
// current scroll offset is different than the mLastAsyncScrollOffset we sent
// with the last event.
// Otherwise, start a timer to fire the event sAsyncScrollTimeout ms from now.
TimeDuration delta = aSampleTime - mLastAsyncScrollTime;
if (delta.ToMilliseconds() > mAsyncScrollThrottleTime &&
mCurrentAsyncScrollOffset != mLastAsyncScrollOffset) {
MonitorAutoLock monitor(mMonitor);
mLastAsyncScrollTime = aSampleTime;
mLastAsyncScrollOffset = mCurrentAsyncScrollOffset;
SendAsyncScrollEvent();
}
else {
mAsyncScrollTimeoutTask =
NewRunnableMethod(this, &AsyncPanZoomController::FireAsyncScrollOnTimeout);
MessageLoop::current()->PostDelayedTask(FROM_HERE,
mAsyncScrollTimeoutTask,
mAsyncScrollTimeout);
}
nsIntPoint scrollCompensation(
@ -1312,5 +1369,22 @@ void AsyncPanZoomController::UpdateZoomConstraints(bool aAllowZoom,
mMaxZoom = aMaxZoom;
}
void AsyncPanZoomController::SendAsyncScrollEvent() {
if (!mGeckoContentController) {
return;
}
gfx::Rect contentRect;
gfx::Size scrollableSize;
{
scrollableSize = gfx::Size(mFrameMetrics.mScrollableRect.width,
mFrameMetrics.mScrollableRect.height);
contentRect =
AsyncPanZoomController::CalculateCompositedRectInCssPixels(mFrameMetrics);
contentRect.MoveTo(mCurrentAsyncScrollOffset);
}
mGeckoContentController->SendAsyncScrollDOMEvent(contentRect, scrollableSize);
}
}
}

View File

@ -227,6 +227,12 @@ public:
static gfx::Rect CalculateCompositedRectInCssPixels(const FrameMetrics& aMetrics);
/**
* Send an mozbrowserasyncscroll event.
* *** The monitor must be held while calling this.
*/
void SendAsyncScrollEvent();
protected:
/**
* Internal handler for ReceiveInputEvent(). Does all the actual work.
@ -439,6 +445,14 @@ protected:
*/
void SetZoomAndResolution(float aScale);
/**
* Timeout function for mozbrowserasyncscroll event. Because we throttle
* mozbrowserasyncscroll events in some conditions, this function ensures
* that the last mozbrowserasyncscroll event will be fired after a period of
* time.
*/
void FireAsyncScrollOnTimeout();
private:
enum PanZoomState {
NOTHING, /* no touch-start events received */
@ -532,6 +546,27 @@ private:
// previous paints.
TimeStamp mPreviousPaintStartTime;
// The last time and offset we fire the mozbrowserasyncscroll event when
// compositor has sampled the content transform for this frame.
TimeStamp mLastAsyncScrollTime;
gfx::Point mLastAsyncScrollOffset;
// The current offset drawn on the screen, it may not be sent since we have
// throttling policy for mozbrowserasyncscroll event.
gfx::Point mCurrentAsyncScrollOffset;
// The delay task triggered by the throttling mozbrowserasyncscroll event
// ensures the last mozbrowserasyncscroll event is always been fired.
CancelableTask* mAsyncScrollTimeoutTask;
// The time period in ms that throttles mozbrowserasyncscroll event.
// Default is 100ms if there is no "apzc.asyncscroll.throttle" in preference.
uint32_t mAsyncScrollThrottleTime;
// The timeout in ms for mAsyncScrollTimeoutTask delay task.
// Default is 300ms if there is no "apzc.asyncscroll.timeout" in preference.
uint32_t mAsyncScrollTimeout;
int mDPI;
// Stores the current paint status of the frame that we're managing. Repaints

View File

@ -44,6 +44,14 @@ public:
*/
virtual void HandleLongTap(const nsIntPoint& aPoint) = 0;
/**
* Requests sending a mozbrowserasyncscroll domevent to embedder.
* |aContentRect| is in CSS pixels, relative to the current cssPage.
* |aScrollableSize| is the current content width/height in CSS pixels.
*/
virtual void SendAsyncScrollDOMEvent(const gfx::Rect &aContentRect,
const gfx::Size &aScrollableSize) = 0;
GeckoContentController() {}
virtual ~GeckoContentController() {}
};

View File

@ -13,6 +13,7 @@
#ifdef MOZ_ENABLE_D3D9_LAYER
# include "LayerManagerD3D9.h"
#endif //MOZ_ENABLE_D3D9_LAYER
#include "mozilla/BrowserElementParent.h"
#include "mozilla/dom/TabParent.h"
#include "mozilla/layers/AsyncPanZoomController.h"
#include "mozilla/layers/CompositorParent.h"
@ -550,6 +551,24 @@ public:
void ClearRenderFrame() { mRenderFrame = nullptr; }
virtual void SendAsyncScrollDOMEvent(const gfx::Rect& aContentRect,
const gfx::Size& aContentSize) MOZ_OVERRIDE
{
if (MessageLoop::current() != mUILoop) {
mUILoop->PostTask(
FROM_HERE,
NewRunnableMethod(this,
&RemoteContentController::SendAsyncScrollDOMEvent,
aContentRect, aContentSize));
return;
}
if (mRenderFrame) {
TabParent* browser = static_cast<TabParent*>(mRenderFrame->Manager());
BrowserElementParent::DispatchAsyncScrollEvent(browser, aContentRect,
aContentSize);
}
}
private:
void DoRequestContentRepaint(const FrameMetrics& aFrameMetrics)
{