зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1773865 - Dispatch an event on the window document when a pinch zoom gesture ends. r=botond,smaug,NeilDeakin
Differential Revision: https://phabricator.services.mozilla.com/D149283
This commit is contained in:
Родитель
e540da8de1
Коммит
acc5c41175
|
@ -50,6 +50,7 @@ var FullZoom = {
|
|||
init: function FullZoom_init() {
|
||||
gBrowser.addEventListener("DoZoomEnlargeBy10", this);
|
||||
gBrowser.addEventListener("DoZoomReduceBy10", this);
|
||||
window.addEventListener("MozScaleGestureComplete", this);
|
||||
|
||||
// Register ourselves with the service so we know when our pref changes.
|
||||
this._cps2 = Cc["@mozilla.org/content-pref/service;1"].getService(
|
||||
|
@ -86,6 +87,7 @@ var FullZoom = {
|
|||
this._cps2.removeObserverForName(this.name, this);
|
||||
gBrowser.removeEventListener("DoZoomEnlargeBy10", this);
|
||||
gBrowser.removeEventListener("DoZoomReduceBy10", this);
|
||||
window.removeEventListener("MozScaleGestureComplete", this);
|
||||
},
|
||||
|
||||
// Event Handlers
|
||||
|
@ -100,6 +102,11 @@ var FullZoom = {
|
|||
case "DoZoomReduceBy10":
|
||||
this.changeZoomBy(this._getTargetedBrowser(event), -0.1);
|
||||
break;
|
||||
case "MozScaleGestureComplete": {
|
||||
let nonDefaultScalingZoom = event.detail != 1.0;
|
||||
this.updateCommands(nonDefaultScalingZoom);
|
||||
break;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -323,7 +330,18 @@ var FullZoom = {
|
|||
|
||||
// update state of zoom menu items
|
||||
|
||||
updateCommands: function FullZoom_updateCommands() {
|
||||
/**
|
||||
* Updates the current windows Zoom commands for zooming in, zooming out
|
||||
* and resetting the zoom level. Dispatches a ZoomCommandsUpdated event on
|
||||
* the window when the commands have been updated.
|
||||
*
|
||||
* @param {boolean} [forceResetEnabled=false]
|
||||
* Set to true if the zoom reset command should be enabled regardless of
|
||||
* whether or not the ZoomManager.zoom level is at 1.0. This is specifically
|
||||
* for when using scaling zoom via the pinch gesture which doesn't cause
|
||||
* the ZoomManager.zoom level to change.
|
||||
*/
|
||||
updateCommands: function FullZoom_updateCommands(forceResetEnabled = false) {
|
||||
let zoomLevel = ZoomManager.zoom;
|
||||
let reduceCmd = document.getElementById("cmd_fullZoomReduce");
|
||||
if (zoomLevel == ZoomManager.MIN) {
|
||||
|
@ -340,7 +358,7 @@ var FullZoom = {
|
|||
}
|
||||
|
||||
let resetCmd = document.getElementById("cmd_fullZoomReset");
|
||||
if (zoomLevel == 1) {
|
||||
if (zoomLevel == 1 && !forceResetEnabled) {
|
||||
resetCmd.setAttribute("disabled", "true");
|
||||
} else {
|
||||
resetCmd.removeAttribute("disabled");
|
||||
|
|
|
@ -60,6 +60,10 @@ add_task(async () => {
|
|||
const TEST_PAGE_URL =
|
||||
"data:text/html;charset=utf-8,<body>test_zoom_levels</body>";
|
||||
|
||||
const winUtils = Services.wm.getMostRecentWindow("").windowUtils;
|
||||
const layerManager = winUtils.layerManagerType;
|
||||
const softwareWebRenderEnabled = layerManager == "WebRender (Software)";
|
||||
|
||||
await BrowserTestUtils.withNewTab(TEST_PAGE_URL, async browser => {
|
||||
let currentZoom = await FullZoomHelper.getGlobalValue();
|
||||
Assert.equal(
|
||||
|
@ -79,6 +83,15 @@ add_task(async () => {
|
|||
// and the other without.
|
||||
for (let textZoom of [true, false]) {
|
||||
info(`Running variation with textZoom set to ${textZoom}`);
|
||||
|
||||
if (!textZoom && softwareWebRenderEnabled) {
|
||||
todo(
|
||||
false,
|
||||
"Skipping full zoom when using software WebRender (bug 1775498)"
|
||||
);
|
||||
continue;
|
||||
}
|
||||
|
||||
await SpecialPowers.pushPrefEnv({
|
||||
set: [["browser.zoom.full", !textZoom]],
|
||||
});
|
||||
|
|
|
@ -147,6 +147,9 @@ class GeckoContentController {
|
|||
|
||||
virtual void CancelAutoscroll(const ScrollableLayerGuid& aGuid) = 0;
|
||||
|
||||
virtual void NotifyScaleGestureComplete(const ScrollableLayerGuid& aGuid,
|
||||
float aScale) = 0;
|
||||
|
||||
virtual void UpdateOverscrollVelocity(const ScrollableLayerGuid& aGuid,
|
||||
float aX, float aY,
|
||||
bool aIsRootContent) {}
|
||||
|
|
|
@ -738,6 +738,7 @@ AsyncPanZoomController::AsyncPanZoomController(
|
|||
kViewportMaxScale / ParentLayerToScreenScale(1)),
|
||||
mLastSampleTime(GetFrameTime()),
|
||||
mLastCheckerboardReport(GetFrameTime()),
|
||||
mLastNotifiedZoom(),
|
||||
mOverscrollEffect(MakeUnique<OverscrollEffect>(*this)),
|
||||
mState(NOTHING),
|
||||
mX(this),
|
||||
|
@ -4386,6 +4387,14 @@ void AsyncPanZoomController::RequestContentRepaint(
|
|||
RepaintRequest request(aFrameMetrics, aDisplayportMargins, aUpdateType,
|
||||
animationType, mScrollGeneration);
|
||||
|
||||
if (request.IsRootContent() && request.GetZoom() != mLastNotifiedZoom &&
|
||||
mState != PINCHING && mState != ANIMATING_ZOOM) {
|
||||
controller->NotifyScaleGestureComplete(
|
||||
GetGuid(),
|
||||
(request.GetZoom() / request.GetDevPixelsPerCSSPixel()).scale);
|
||||
mLastNotifiedZoom = request.GetZoom();
|
||||
}
|
||||
|
||||
// If we're trying to paint what we already think is painted, discard this
|
||||
// request since it's a pointless paint.
|
||||
if (request.GetDisplayPortMargins().WithinEpsilonOf(
|
||||
|
|
|
@ -1081,6 +1081,10 @@ class AsyncPanZoomController {
|
|||
// to allow panning by moving multiple fingers (thus moving the focus point).
|
||||
ParentLayerPoint mLastZoomFocus;
|
||||
|
||||
// Stores the previous zoom level at which we last sent a ScaleGestureComplete
|
||||
// notification.
|
||||
CSSToParentLayerScale mLastNotifiedZoom;
|
||||
|
||||
RefPtr<AsyncPanZoomAnimation> mAnimation;
|
||||
|
||||
UniquePtr<OverscrollEffectBase> mOverscrollEffect;
|
||||
|
|
|
@ -154,6 +154,8 @@ class MockContentController : public GeckoContentController {
|
|||
MOCK_METHOD1(NotifyAsyncAutoscrollRejected,
|
||||
void(const ScrollableLayerGuid::ViewID&));
|
||||
MOCK_METHOD1(CancelAutoscroll, void(const ScrollableLayerGuid&));
|
||||
MOCK_METHOD2(NotifyScaleGestureComplete,
|
||||
void(const ScrollableLayerGuid&, float aScale));
|
||||
};
|
||||
|
||||
class MockContentControllerDelayed : public MockContentController {
|
||||
|
|
|
@ -8,10 +8,13 @@
|
|||
|
||||
#include "gfxPlatform.h" // For gfxPlatform::UseTiling
|
||||
|
||||
#include "mozilla/AsyncEventDispatcher.h"
|
||||
#include "mozilla/EventForwards.h"
|
||||
#include "mozilla/dom/CustomEvent.h"
|
||||
#include "mozilla/dom/Element.h"
|
||||
#include "mozilla/dom/MouseEventBinding.h"
|
||||
#include "mozilla/dom/BrowserParent.h"
|
||||
#include "mozilla/dom/ScriptSettings.h"
|
||||
#include "mozilla/IntegerPrintfMacros.h"
|
||||
#include "mozilla/layers/RepaintRequest.h"
|
||||
#include "mozilla/layers/WebRenderLayerManager.h"
|
||||
|
@ -29,6 +32,7 @@
|
|||
#include "nsIScrollableFrame.h"
|
||||
#include "nsLayoutUtils.h"
|
||||
#include "nsPrintfCString.h"
|
||||
#include "nsPIDOMWindow.h"
|
||||
#include "nsRefreshDriver.h"
|
||||
#include "nsString.h"
|
||||
#include "nsView.h"
|
||||
|
@ -869,6 +873,38 @@ void APZCCallbackHelper::CancelAutoscroll(
|
|||
data.get());
|
||||
}
|
||||
|
||||
/* static */
|
||||
void APZCCallbackHelper::NotifyScaleGestureComplete(
|
||||
const nsCOMPtr<nsIWidget>& aWidget, float aScale) {
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (nsView* view = nsView::GetViewFor(aWidget)) {
|
||||
if (PresShell* presShell = view->GetPresShell()) {
|
||||
dom::Document* doc = presShell->GetDocument();
|
||||
MOZ_ASSERT(doc);
|
||||
if (nsPIDOMWindowInner* win = doc->GetInnerWindow()) {
|
||||
dom::AutoJSAPI jsapi;
|
||||
if (!jsapi.Init(win)) {
|
||||
return;
|
||||
}
|
||||
|
||||
JSContext* cx = jsapi.cx();
|
||||
JS::Rooted<JS::Value> detail(cx, JS::Float32Value(aScale));
|
||||
RefPtr<dom::CustomEvent> event =
|
||||
NS_NewDOMCustomEvent(doc, nullptr, nullptr);
|
||||
event->InitCustomEvent(cx, u"MozScaleGestureComplete"_ns,
|
||||
/* CanBubble */ true,
|
||||
/* Cancelable */ false, detail);
|
||||
event->SetTrusted(true);
|
||||
AsyncEventDispatcher* dispatcher = new AsyncEventDispatcher(doc, event);
|
||||
dispatcher->mOnlyChromeDispatch = ChromeOnlyDispatch::eYes;
|
||||
|
||||
dispatcher->PostDOMEvent();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* static */
|
||||
void APZCCallbackHelper::NotifyPinchGesture(
|
||||
PinchGestureInput::PinchGestureType aType,
|
||||
|
|
|
@ -162,6 +162,8 @@ class APZCCallbackHelper {
|
|||
const ScrollableLayerGuid::ViewID& aScrollId);
|
||||
|
||||
static void CancelAutoscroll(const ScrollableLayerGuid::ViewID& aScrollId);
|
||||
static void NotifyScaleGestureComplete(const nsCOMPtr<nsIWidget>& aWidget,
|
||||
float aScale);
|
||||
|
||||
/*
|
||||
* Check if the scrollable frame is currently in the middle of a main thread
|
||||
|
|
|
@ -330,3 +330,23 @@ void ChromeProcessController::CancelAutoscroll(
|
|||
|
||||
APZCCallbackHelper::CancelAutoscroll(aGuid.mScrollId);
|
||||
}
|
||||
|
||||
void ChromeProcessController::NotifyScaleGestureComplete(
|
||||
const ScrollableLayerGuid& aGuid, float aScale) {
|
||||
if (!mUIThread->IsOnCurrentThread()) {
|
||||
mUIThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid, float>(
|
||||
"layers::ChromeProcessController::NotifyScaleGestureComplete", this,
|
||||
&ChromeProcessController::NotifyScaleGestureComplete, aGuid, aScale));
|
||||
return;
|
||||
}
|
||||
|
||||
if (mWidget) {
|
||||
// Dispatch the call to APZCCallbackHelper::NotifyScaleGestureComplete
|
||||
// to the main thread so that it runs asynchronously from the current call.
|
||||
// This is because the call can run arbitrary JS code, which can also spin
|
||||
// the event loop and cause undesirable re-entrancy in APZ.
|
||||
mUIThread->Dispatch(NewRunnableFunction(
|
||||
"layers::ChromeProcessController::NotifyScaleGestureComplete",
|
||||
&APZCCallbackHelper::NotifyScaleGestureComplete, mWidget, aScale));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -75,6 +75,8 @@ class ChromeProcessController : public mozilla::layers::GeckoContentController {
|
|||
void NotifyAsyncAutoscrollRejected(
|
||||
const ScrollableLayerGuid::ViewID& aScrollId) override;
|
||||
void CancelAutoscroll(const ScrollableLayerGuid& aGuid) override;
|
||||
void NotifyScaleGestureComplete(const ScrollableLayerGuid& aGuid,
|
||||
float aScale) override;
|
||||
|
||||
PresShell* GetTopLevelPresShell() const override { return GetPresShell(); }
|
||||
|
||||
|
|
|
@ -97,6 +97,12 @@ void ContentProcessController::CancelAutoscroll(
|
|||
MOZ_ASSERT_UNREACHABLE("Unexpected message to content process");
|
||||
}
|
||||
|
||||
void ContentProcessController::NotifyScaleGestureComplete(
|
||||
const ScrollableLayerGuid& aGuid, float aScale) {
|
||||
// This should never get called
|
||||
MOZ_ASSERT_UNREACHABLE("Unexpected message to content process");
|
||||
}
|
||||
|
||||
bool ContentProcessController::IsRepaintThread() { return NS_IsMainThread(); }
|
||||
|
||||
void ContentProcessController::DispatchToRepaintThread(
|
||||
|
|
|
@ -72,6 +72,9 @@ class ContentProcessController final : public GeckoContentController {
|
|||
|
||||
void CancelAutoscroll(const ScrollableLayerGuid& aGuid) override;
|
||||
|
||||
void NotifyScaleGestureComplete(const ScrollableLayerGuid& aGuid,
|
||||
float aScale) override;
|
||||
|
||||
bool IsRepaintThread() override;
|
||||
|
||||
void DispatchToRepaintThread(already_AddRefed<Runnable> aTask) override;
|
||||
|
|
|
@ -201,5 +201,19 @@ mozilla::ipc::IPCResult APZCTreeManagerChild::RecvCancelAutoscroll(
|
|||
return IPC_OK();
|
||||
}
|
||||
|
||||
mozilla::ipc::IPCResult APZCTreeManagerChild::RecvNotifyScaleGestureComplete(
|
||||
const ScrollableLayerGuid::ViewID& aScrollId, float aScale) {
|
||||
// This will only get sent from the GPU process to the parent process, so
|
||||
// this function should never get called in the content process.
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
MOZ_ASSERT(NS_IsMainThread());
|
||||
|
||||
if (mCompositorSession && mCompositorSession->GetWidget()) {
|
||||
APZCCallbackHelper::NotifyScaleGestureComplete(
|
||||
mCompositorSession->GetWidget(), aScale);
|
||||
}
|
||||
return IPC_OK();
|
||||
}
|
||||
|
||||
} // namespace layers
|
||||
} // namespace mozilla
|
||||
|
|
|
@ -85,6 +85,9 @@ class APZCTreeManagerChild : public IAPZCTreeManager,
|
|||
mozilla::ipc::IPCResult RecvCancelAutoscroll(
|
||||
const ScrollableLayerGuid::ViewID& aScrollId);
|
||||
|
||||
mozilla::ipc::IPCResult RecvNotifyScaleGestureComplete(
|
||||
const ScrollableLayerGuid::ViewID& aScrollId, float aScale);
|
||||
|
||||
virtual ~APZCTreeManagerChild();
|
||||
|
||||
private:
|
||||
|
|
|
@ -83,6 +83,8 @@ child:
|
|||
Modifiers aModifiers);
|
||||
|
||||
async CancelAutoscroll(ViewID aScrollId);
|
||||
|
||||
async NotifyScaleGestureComplete(ViewID aScrollId, float aScale);
|
||||
};
|
||||
|
||||
} // namespace gfx
|
||||
|
|
|
@ -399,6 +399,56 @@ void RemoteContentController::CancelAutoscrollCrossProcess(
|
|||
}
|
||||
}
|
||||
|
||||
void RemoteContentController::NotifyScaleGestureComplete(
|
||||
const ScrollableLayerGuid& aGuid, float aScale) {
|
||||
if (XRE_GetProcessType() == GeckoProcessType_GPU) {
|
||||
NotifyScaleGestureCompleteCrossProcess(aGuid, aScale);
|
||||
} else {
|
||||
NotifyScaleGestureCompleteInProcess(aGuid, aScale);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteContentController::NotifyScaleGestureCompleteInProcess(
|
||||
const ScrollableLayerGuid& aGuid, float aScale) {
|
||||
MOZ_ASSERT(XRE_IsParentProcess());
|
||||
|
||||
if (!NS_IsMainThread()) {
|
||||
NS_DispatchToMainThread(NewRunnableMethod<ScrollableLayerGuid, float>(
|
||||
"layers::RemoteContentController::NotifyScaleGestureCompleteInProcess",
|
||||
this, &RemoteContentController::NotifyScaleGestureCompleteInProcess,
|
||||
aGuid, aScale));
|
||||
return;
|
||||
}
|
||||
|
||||
RefPtr<GeckoContentController> rootController =
|
||||
CompositorBridgeParent::GetGeckoContentControllerForRoot(aGuid.mLayersId);
|
||||
if (rootController) {
|
||||
rootController->NotifyScaleGestureComplete(aGuid, aScale);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteContentController::NotifyScaleGestureCompleteCrossProcess(
|
||||
const ScrollableLayerGuid& aGuid, float aScale) {
|
||||
MOZ_ASSERT(XRE_IsGPUProcess());
|
||||
|
||||
if (!mCompositorThread->IsOnCurrentThread()) {
|
||||
mCompositorThread->Dispatch(NewRunnableMethod<ScrollableLayerGuid, float>(
|
||||
"layers::RemoteContentController::"
|
||||
"NotifyScaleGestureCompleteCrossProcess",
|
||||
this, &RemoteContentController::NotifyScaleGestureCompleteCrossProcess,
|
||||
aGuid, aScale));
|
||||
return;
|
||||
}
|
||||
|
||||
// The raw pointer to APZCTreeManagerParent is ok here because we are on the
|
||||
// compositor thread.
|
||||
if (APZCTreeManagerParent* parent =
|
||||
CompositorBridgeParent::GetApzcTreeManagerParentForRoot(
|
||||
aGuid.mLayersId)) {
|
||||
Unused << parent->SendNotifyScaleGestureComplete(aGuid.mScrollId, aScale);
|
||||
}
|
||||
}
|
||||
|
||||
void RemoteContentController::ActorDestroy(ActorDestroyReason aWhy) {
|
||||
// This controller could possibly be kept alive longer after this
|
||||
// by a RefPtr, but it is no longer valid to send messages.
|
||||
|
|
|
@ -81,6 +81,9 @@ class RemoteContentController : public GeckoContentController,
|
|||
|
||||
void CancelAutoscroll(const ScrollableLayerGuid& aScrollId) override;
|
||||
|
||||
void NotifyScaleGestureComplete(const ScrollableLayerGuid& aGuid,
|
||||
float aScale) override;
|
||||
|
||||
void ActorDestroy(ActorDestroyReason aWhy) override;
|
||||
|
||||
void Destroy() override;
|
||||
|
@ -106,6 +109,10 @@ class RemoteContentController : public GeckoContentController,
|
|||
|
||||
void CancelAutoscrollInProcess(const ScrollableLayerGuid& aScrollId);
|
||||
void CancelAutoscrollCrossProcess(const ScrollableLayerGuid& aScrollId);
|
||||
void NotifyScaleGestureCompleteInProcess(const ScrollableLayerGuid& aGuid,
|
||||
float aScale);
|
||||
void NotifyScaleGestureCompleteCrossProcess(const ScrollableLayerGuid& aGuid,
|
||||
float aScale);
|
||||
};
|
||||
|
||||
} // namespace layers
|
||||
|
|
Загрузка…
Ссылка в новой задаче