Bug 746502: Add support for <meta name=viewport> on B2G/async panning and zooming r=cjones,smaug

This commit is contained in:
Doug Sherk 2012-09-28 22:18:18 -04:00
parent 05643b327a
commit 34d6e81bb8
10 changed files with 497 additions and 74 deletions

View File

@ -220,28 +220,6 @@ const ContentPanning = {
metrics.cssPageRect.y,
metrics.cssPageRect.width,
metrics.cssPageRect.height);
let cwu = content.QueryInterface(Ci.nsIInterfaceRequestor).getInterface(Ci.nsIDOMWindowUtils);
if (this._compositionWidth != compositionWidth || this._compositionHeight != compositionHeight) {
cwu.setCSSViewport(compositionWidth, compositionHeight);
this._compositionWidth = compositionWidth;
this._compositionHeight = compositionHeight;
}
// Set scroll position
cwu.setScrollPositionClampingScrollPortSize(
compositionWidth / metrics.zoom, compositionHeight / metrics.zoom);
content.scrollTo(x, y);
cwu.setResolution(displayPort.resolution, displayPort.resolution);
let element = null;
if (content.document && (element = content.document.documentElement)) {
cwu.setDisplayPortForElement(displayPort.x,
displayPort.y,
displayPort.width,
displayPort.height,
element);
}
},
_recvDoubleTap: function(data) {

View File

@ -266,6 +266,12 @@ parent:
*/
ContentReceivedTouch(bool aPreventDefault);
/**
* Updates any zoom constraints on the parent and anything tied to it. This
* is useful for control logic that resides outside of the remote browser.
*/
UpdateZoomConstraints(bool aAllowZoom, float aMinZoom, float aMaxZoom);
__delete__();
child:

View File

@ -18,6 +18,7 @@
#include "mozilla/dom/PContentChild.h"
#include "mozilla/dom/PContentDialogChild.h"
#include "mozilla/ipc/DocumentRendererChild.h"
#include "mozilla/layers/AsyncPanZoomController.h"
#include "mozilla/layers/CompositorChild.h"
#include "mozilla/layers/PLayersChild.h"
#include "mozilla/layout/RenderFrameChild.h"
@ -29,10 +30,12 @@
#include "nsContentUtils.h"
#include "nsEmbedCID.h"
#include "nsEventListenerManager.h"
#include "nsGenericElement.h"
#include "nsIAppsService.h"
#include "nsIBaseWindow.h"
#include "nsIComponentManager.h"
#include "nsIDOMClassInfo.h"
#include "nsIDOMElement.h"
#include "nsIDOMEvent.h"
#include "nsIDOMWindow.h"
#include "nsIDOMWindowUtils.h"
@ -50,6 +53,8 @@
#include "nsIServiceManager.h"
#include "nsISupportsImpl.h"
#include "nsIURI.h"
#include "nsIURIFixup.h"
#include "nsCDefaultURIFixup.h"
#include "nsIView.h"
#include "nsIWebBrowser.h"
#include "nsIWebBrowserFocus.h"
@ -85,6 +90,12 @@ using namespace mozilla::widget;
NS_IMPL_ISUPPORTS1(ContentListener, nsIDOMEventListener)
static const nsIntSize kDefaultViewportSize(980, 480);
static const char CANCEL_DEFAULT_PAN_ZOOM[] = "cancel-default-pan-zoom";
static const char BROWSER_ZOOM_TO_RECT[] = "browser-zoom-to-rect";
static const char BEFORE_FIRST_PAINT[] = "before-first-paint";
NS_IMETHODIMP
ContentListener::HandleEvent(nsIDOMEvent* aEvent)
{
@ -151,28 +162,45 @@ TabChild::TabChild(uint32_t aChromeFlags, bool aIsBrowserElement,
, mTabChildGlobal(nullptr)
, mChromeFlags(aChromeFlags)
, mOuterRect(0, 0, 0, 0)
, mInnerSize(0, 0)
, mOldViewportWidth(0.0f)
, mLastBackgroundColor(NS_RGB(255, 255, 255))
, mAppId(aAppId)
, mDidFakeShow(false)
, mIsBrowserElement(aIsBrowserElement)
, mNotified(false)
, mContentDocumentIsDisplayed(false)
, mTriedBrowserInit(false)
{
printf("creating %d!\n", NS_IsMainThread());
}
nsresult
NS_IMETHODIMP
TabChild::HandleEvent(nsIDOMEvent* aEvent)
{
nsAutoString eventType;
aEvent->GetType(eventType);
if (eventType.EqualsLiteral("DOMMetaAdded")) {
// This meta data may or may not have been a meta viewport tag. If it was,
// we should handle it immediately.
HandlePossibleMetaViewportChange();
}
return NS_OK;
}
NS_IMETHODIMP
TabChild::Observe(nsISupports *aSubject,
const char *aTopic,
const PRUnichar *aData)
{
if (!strcmp(aTopic, "cancel-default-pan-zoom")) {
if (!strcmp(aTopic, CANCEL_DEFAULT_PAN_ZOOM)) {
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aSubject));
nsCOMPtr<nsITabChild> tabChild(GetTabChildFrom(docShell));
if (tabChild == this) {
mRemoteFrame->CancelDefaultPanZoom();
}
} else if (!strcmp(aTopic, "browser-zoom-to-rect")) {
} else if (!strcmp(aTopic, BROWSER_ZOOM_TO_RECT)) {
nsCOMPtr<nsIDocShell> docShell(do_QueryInterface(aSubject));
nsCOMPtr<nsITabChild> tabChild(GetTabChildFrom(docShell));
if (tabChild == this) {
@ -182,11 +210,262 @@ TabChild::Observe(nsISupports *aSubject,
&rect.x, &rect.y, &rect.width, &rect.height);
SendZoomToRect(rect);
}
} else if (!strcmp(aTopic, BEFORE_FIRST_PAINT)) {
if (IsAsyncPanZoomEnabled()) {
nsCOMPtr<nsIDocument> subject(do_QueryInterface(aSubject));
nsCOMPtr<nsIDOMDocument> domDoc;
mWebNav->GetDocument(getter_AddRefs(domDoc));
nsCOMPtr<nsIDocument> doc(do_QueryInterface(domDoc));
if (SameCOMIdentity(subject, doc)) {
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
mContentDocumentIsDisplayed = true;
// Reset CSS viewport and zoom to default on new page, then calculate them
// properly using the actual metadata from the page.
SetCSSViewport(kDefaultViewportSize.width, kDefaultViewportSize.height);
// Calculate a really simple resolution that we probably won't be
// keeping, as well as putting the scroll offset back to the top-left of
// the page.
float resolution = float(mInnerSize.width) / float(kDefaultViewportSize.width);
mLastMetrics.mZoom.width = mLastMetrics.mZoom.height =
mLastMetrics.mResolution.width = mLastMetrics.mResolution.height =
resolution;
mLastMetrics.mScrollOffset = gfx::Point(0, 0);
utils->SetResolution(resolution, resolution);
HandlePossibleMetaViewportChange();
}
}
}
return NS_OK;
}
NS_IMETHODIMP
TabChild::OnStateChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
uint32_t aStateFlags,
nsresult aStatus)
{
NS_NOTREACHED("not implemented in TabChild");
return NS_OK;
}
NS_IMETHODIMP
TabChild::OnProgressChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
int32_t aCurSelfProgress,
int32_t aMaxSelfProgress,
int32_t aCurTotalProgress,
int32_t aMaxTotalProgress)
{
NS_NOTREACHED("not implemented in TabChild");
return NS_OK;
}
NS_IMETHODIMP
TabChild::OnLocationChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
nsIURI *aLocation,
uint32_t aFlags)
{
if (!IsAsyncPanZoomEnabled()) {
return NS_OK;
}
nsCOMPtr<nsIDOMWindow> window;
aWebProgress->GetDOMWindow(getter_AddRefs(window));
if (!window) {
return NS_OK;
}
nsCOMPtr<nsIDOMDocument> progressDoc;
window->GetDocument(getter_AddRefs(progressDoc));
if (!progressDoc) {
return NS_OK;
}
nsCOMPtr<nsIDOMDocument> domDoc;
mWebNav->GetDocument(getter_AddRefs(domDoc));
if (!domDoc || !SameCOMIdentity(domDoc, progressDoc)) {
return NS_OK;
}
nsCOMPtr<nsIURIFixup> urifixup(do_GetService(NS_URIFIXUP_CONTRACTID));
if (!urifixup) {
return NS_OK;
}
nsCOMPtr<nsIURI> exposableURI;
urifixup->CreateExposableURI(aLocation, getter_AddRefs(exposableURI));
if (!exposableURI) {
return NS_OK;
}
if (!(aFlags & nsIWebProgressListener::LOCATION_CHANGE_SAME_DOCUMENT)) {
mContentDocumentIsDisplayed = false;
} else if (mLastURI != nullptr) {
bool exposableEqualsLast, exposableEqualsNew;
exposableURI->Equals(mLastURI.get(), &exposableEqualsLast);
exposableURI->Equals(aLocation, &exposableEqualsNew);
if (exposableEqualsLast && !exposableEqualsNew) {
mContentDocumentIsDisplayed = false;
}
}
return NS_OK;
}
NS_IMETHODIMP
TabChild::OnStatusChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
nsresult aStatus,
const PRUnichar* aMessage)
{
NS_NOTREACHED("not implemented in TabChild");
return NS_OK;
}
NS_IMETHODIMP
TabChild::OnSecurityChange(nsIWebProgress* aWebProgress,
nsIRequest* aRequest,
uint32_t aState)
{
NS_NOTREACHED("not implemented in TabChild");
return NS_OK;
}
void
TabChild::SetCSSViewport(float aWidth, float aHeight)
{
mOldViewportWidth = aWidth;
if (mContentDocumentIsDisplayed) {
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
utils->SetCSSViewport(aWidth, aHeight);
}
}
void
TabChild::HandlePossibleMetaViewportChange()
{
if (!IsAsyncPanZoomEnabled()) {
return;
}
nsCOMPtr<nsIDOMDocument> domDoc;
mWebNav->GetDocument(getter_AddRefs(domDoc));
nsCOMPtr<nsIDocument> document(do_QueryInterface(domDoc));
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
ViewportInfo viewportMetaData =
nsContentUtils::GetViewportInfo(document, mInnerSize.width, mInnerSize.height);
SendUpdateZoomConstraints(viewportMetaData.allowZoom,
viewportMetaData.minZoom,
viewportMetaData.maxZoom);
float screenW = mInnerSize.width;
float screenH = mInnerSize.height;
float viewportW = viewportMetaData.width;
float viewportH = viewportMetaData.height;
// We're not being displayed in any way; don't bother doing anything because
// that will just confuse future adjustments.
if (!screenW || !screenH) {
return;
}
// Make sure the viewport height is not shorter than the window when the page
// is zoomed out to show its full width. Note that before we set the viewport
// width, the "full width" of the page isn't properly defined, so that's why
// we have to call SetCSSViewport twice - once to set the width, and the
// second time to figure out the height based on the layout at that width.
float oldBrowserWidth = mOldViewportWidth;
mLastMetrics.mViewport.width = viewportMetaData.width;
mLastMetrics.mViewport.height = viewportMetaData.height;
if (!oldBrowserWidth) {
oldBrowserWidth = kDefaultViewportSize.width;
}
SetCSSViewport(viewportW, viewportH);
// If this page has not been painted yet, then this must be getting run
// because a meta-viewport element was added (via the DOMMetaAdded handler).
// in this case, we should not do anything that forces a reflow (see bug
// 759678) such as requesting the page size or sending a viewport update. this
// code will get run again in the before-first-paint handler and that point we
// will run though all of it. the reason we even bother executing up to this
// point on the DOMMetaAdded handler is so that scripts that use
// window.innerWidth before they are painted have a correct value (bug
// 771575).
if (!mContentDocumentIsDisplayed) {
return;
}
float minScale = 1.0f;
nsCOMPtr<nsIDOMElement> htmlDOMElement = do_QueryInterface(document->GetHtmlElement());
nsCOMPtr<nsIDOMElement> bodyDOMElement = do_QueryInterface(document->GetBodyElement());
PRInt32 htmlWidth = 0, htmlHeight = 0;
if (htmlDOMElement) {
htmlDOMElement->GetScrollWidth(&htmlWidth);
htmlDOMElement->GetScrollHeight(&htmlHeight);
}
PRInt32 bodyWidth = 0, bodyHeight = 0;
if (bodyDOMElement) {
bodyDOMElement->GetScrollWidth(&bodyWidth);
bodyDOMElement->GetScrollHeight(&bodyHeight);
}
float pageWidth = NS_MAX(htmlWidth, bodyWidth);
float pageHeight = NS_MAX(htmlHeight, bodyHeight);
minScale = mInnerSize.width / pageWidth;
minScale = clamped((double)minScale, viewportMetaData.minZoom, viewportMetaData.maxZoom);
viewportH = NS_MAX(viewportH, screenH / minScale);
SetCSSViewport(viewportW, viewportH);
// This change to the zoom accounts for all types of changes I can conceive:
// 1. screen size changes, CSS viewport does not (pages with no meta viewport
// or a fixed size viewport)
// 2. screen size changes, CSS viewport also does (pages with a device-width
// viewport)
// 3. screen size remains constant, but CSS viewport changes (meta viewport
// tag is added or removed)
// 4. neither screen size nor CSS viewport changes
//
// In all of these cases, we maintain how much actual content is visible
// within the screen width. Note that "actual content" may be different with
// respect to CSS pixels because of the CSS viewport size changing.
int32_t oldScreenWidth = mLastMetrics.mCompositionBounds.width;
if (!oldScreenWidth) {
oldScreenWidth = mInnerSize.width;
}
float zoomScale = (screenW * oldBrowserWidth) / (oldScreenWidth * viewportW);
float zoom = clamped(double(mLastMetrics.mZoom.width * zoomScale),
viewportMetaData.minZoom, viewportMetaData.maxZoom);
utils->SetResolution(zoom, zoom);
FrameMetrics metrics(mLastMetrics);
metrics.mViewport = gfx::Rect(0.0f, 0.0f, viewportW, viewportH);
metrics.mScrollableRect = gfx::Rect(0.0f, 0.0f, pageWidth, pageHeight);
metrics.mCompositionBounds = nsIntRect(0, 0, mInnerSize.width, mInnerSize.height);
metrics.mZoom.width = metrics.mZoom.height =
metrics.mResolution.width = metrics.mResolution.height = zoom;
metrics.mDisplayPort = AsyncPanZoomController::CalculatePendingDisplayPort(
metrics,
gfx::Point(0.0f, 0.0f));
// Force a repaint with these metrics. This, among other things, sets the
// displayport, so we start with async painting.
RecvUpdateFrame(metrics);
}
nsresult
TabChild::Init()
{
@ -238,6 +517,12 @@ TabChild::Init()
"DNS prefetching enable step.");
}
nsCOMPtr<nsIDocShell> docShell = do_GetInterface(mWebNav);
MOZ_ASSERT(docShell);
nsCOMPtr<nsIWebProgress> webProgress = do_GetInterface(docShell);
NS_ENSURE_TRUE(webProgress, NS_ERROR_FAILURE);
webProgress->AddProgressListener(this, nsIWebProgress::NOTIFY_LOCATION);
return NS_OK;
}
@ -266,8 +551,11 @@ NS_INTERFACE_MAP_BEGIN(TabChild)
NS_INTERFACE_MAP_ENTRY(nsIWebBrowserChromeFocus)
NS_INTERFACE_MAP_ENTRY(nsIInterfaceRequestor)
NS_INTERFACE_MAP_ENTRY(nsIWindowProvider)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventListener)
NS_INTERFACE_MAP_ENTRY(nsIWebProgressListener)
NS_INTERFACE_MAP_ENTRY(nsITabChild)
NS_INTERFACE_MAP_ENTRY(nsIDialogCreator)
NS_INTERFACE_MAP_ENTRY(nsIObserver)
NS_INTERFACE_MAP_ENTRY(nsSupportsWeakReference)
NS_INTERFACE_MAP_END
@ -799,6 +1087,7 @@ TabChild::RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size)
mOuterRect.width = rect.width;
mOuterRect.height = rect.height;
mInnerSize = size;
mWidget->Resize(0, 0, size.width, size.height,
true);
@ -806,6 +1095,8 @@ TabChild::RecvUpdateDimensions(const nsRect& rect, const nsIntSize& size)
baseWin->SetPositionAndSize(0, 0, size.width, size.height,
true);
HandlePossibleMetaViewportChange();
return true;
}
@ -842,6 +1133,8 @@ TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
return true;
}
// The BrowserElementScrolling helper must know about these updated metrics
// for other functions it performs, such as double tap handling.
nsCString data;
data += nsPrintfCString("{ \"x\" : %d", NS_lround(aFrameMetrics.mScrollOffset.x));
data += nsPrintfCString(", \"y\" : %d", NS_lround(aFrameMetrics.mScrollOffset.y));
@ -871,6 +1164,32 @@ TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics)
DispatchMessageManagerMessage(NS_LITERAL_STRING("Viewport:Change"), data);
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
nsCOMPtr<nsIDOMWindow> window = do_GetInterface(mWebNav);
utils->SetScrollPositionClampingScrollPortSize(
aFrameMetrics.mCompositionBounds.width / aFrameMetrics.mZoom.width,
aFrameMetrics.mCompositionBounds.height / aFrameMetrics.mZoom.width);
window->ScrollTo(aFrameMetrics.mScrollOffset.x,
aFrameMetrics.mScrollOffset.y);
utils->SetResolution(aFrameMetrics.mResolution.width,
aFrameMetrics.mResolution.width);
nsCOMPtr<nsIDOMDocument> domDoc;
nsCOMPtr<nsIDOMElement> docElement;
mWebNav->GetDocument(getter_AddRefs(domDoc));
if (domDoc) {
domDoc->GetDocumentElement(getter_AddRefs(docElement));
if (docElement) {
utils->SetDisplayPortForElement(
aFrameMetrics.mDisplayPort.x, aFrameMetrics.mDisplayPort.y,
aFrameMetrics.mDisplayPort.width, aFrameMetrics.mDisplayPort.height,
docElement);
}
}
mLastMetrics = aFrameMetrics;
return true;
}
@ -928,8 +1247,7 @@ TabChild::RecvMouseEvent(const nsString& aType,
const int32_t& aModifiers,
const bool& aIgnoreRootScrollFrame)
{
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mWebNav);
nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
NS_ENSURE_TRUE(utils, true);
utils->SendMouseEvent(aType, aX, aY, aButton, aClickCount, aModifiers,
aIgnoreRootScrollFrame, 0, 0);
@ -1030,8 +1348,7 @@ TabChild::RecvKeyEvent(const nsString& aType,
const int32_t& aModifiers,
const bool& aPreventDefault)
{
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mWebNav);
nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
NS_ENSURE_TRUE(utils, true);
bool ignored = false;
utils->SendKeyEvent(aType, aKeyCode, aCharCode,
@ -1281,6 +1598,13 @@ TabChild::RecvDestroy()
);
}
nsCOMPtr<nsIObserverService> observerService =
do_GetService(NS_OBSERVERSERVICE_CONTRACTID);
observerService->RemoveObserver(this, CANCEL_DEFAULT_PAN_ZOOM);
observerService->RemoveObserver(this, BROWSER_ZOOM_TO_RECT);
observerService->RemoveObserver(this, BEFORE_FIRST_PAINT);
// XXX what other code in ~TabChild() should we be running here?
DestroyWindow();
@ -1319,7 +1643,7 @@ TabChild::InitTabChildGlobal(FrameScriptLoading aScriptLoading)
mTabChildGlobal = scope;
nsISupports* scopeSupports = NS_ISUPPORTS_CAST(nsIDOMEventTarget*, scope);
NS_ENSURE_TRUE(InitTabChildGlobalInternal(scopeSupports), false);
scope->Init();
@ -1327,6 +1651,8 @@ TabChild::InitTabChildGlobal(FrameScriptLoading aScriptLoading)
nsCOMPtr<nsPIWindowRoot> root = do_QueryInterface(chromeHandler);
NS_ENSURE_TRUE(root, false);
root->SetParentTarget(scope);
chromeHandler->AddEventListener(NS_LITERAL_STRING("DOMMetaAdded"), this, false);
}
if (aScriptLoading != DONT_LOAD_SCRIPTS && !mTriedBrowserInit) {
@ -1391,10 +1717,13 @@ TabChild::InitRenderingState()
if (observerService) {
observerService->AddObserver(this,
"cancel-default-pan-zoom",
CANCEL_DEFAULT_PAN_ZOOM,
false);
observerService->AddObserver(this,
"browser-zoom-to-rect",
BROWSER_ZOOM_TO_RECT,
false);
observerService->AddObserver(this,
BEFORE_FIRST_PAINT,
false);
}

View File

@ -36,9 +36,11 @@
#include "nsNetUtil.h"
#include "nsFrameMessageManager.h"
#include "nsIScriptContext.h"
#include "nsIWebProgressListener.h"
#include "nsDOMEventTargetHelper.h"
#include "nsIDialogCreator.h"
#include "nsIDialogParamBlock.h"
#include "nsIDOMWindowUtils.h"
#include "nsIPresShell.h"
#include "nsIPrincipal.h"
#include "nsIScriptObjectPrincipal.h"
@ -141,6 +143,8 @@ class TabChild : public PBrowserChild,
public nsIWebBrowserChromeFocus,
public nsIInterfaceRequestor,
public nsIWindowProvider,
public nsIDOMEventListener,
public nsIWebProgressListener,
public nsSupportsWeakReference,
public nsIDialogCreator,
public nsITabChild,
@ -175,6 +179,8 @@ public:
NS_DECL_NSIWEBBROWSERCHROMEFOCUS
NS_DECL_NSIINTERFACEREQUESTOR
NS_DECL_NSIWINDOWPROVIDER
NS_DECL_NSIDOMEVENTLISTENER
NS_DECL_NSIWEBPROGRESSLISTENER
NS_DECL_NSIDIALOGCREATOR
NS_DECL_NSITABCHILD
NS_DECL_NSIOBSERVER
@ -328,6 +334,16 @@ private:
// Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow().
void DoFakeShow();
// Wrapper for nsIDOMWindowUtils.setCSSViewport(). This updates some state
// variables local to this class before setting it.
void SetCSSViewport(float aX, float aY);
// Recalculates the display state, including the CSS viewport. This should
// be called whenever we believe the meta viewport data on a document may
// have changed. If it didn't change, this function doesn't do anything.
// However, it should not be called all the time as it is fairly expensive.
void HandlePossibleMetaViewportChange();
// Wraps up a JSON object as a structured clone and sends it to the browser
// chrome script.
//
@ -347,18 +363,30 @@ private:
bool* aWindowIsNew,
nsIDOMWindow** aReturn);
nsIDOMWindowUtils* GetDOMWindowUtils()
{
nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(mWebNav);
nsCOMPtr<nsIDOMWindowUtils> utils = do_GetInterface(window);
return utils;
}
nsCOMPtr<nsIWebNavigation> mWebNav;
nsCOMPtr<nsIWidget> mWidget;
nsCOMPtr<nsIURI> mLastURI;
FrameMetrics mLastMetrics;
RenderFrameChild* mRemoteFrame;
nsRefPtr<TabChildGlobal> mTabChildGlobal;
uint32_t mChromeFlags;
nsIntRect mOuterRect;
nsIntSize mInnerSize;
float mOldViewportWidth;
nscolor mLastBackgroundColor;
ScrollingBehavior mScrolling;
uint32_t mAppId;
bool mDidFakeShow;
bool mIsBrowserElement;
bool mNotified;
bool mContentDocumentIsDisplayed;
bool mTriedBrowserInit;
DISALLOW_EVIL_CONSTRUCTORS(TabChild);

View File

@ -1199,6 +1199,17 @@ TabParent::RecvZoomToRect(const gfxRect& aRect)
return true;
}
bool
TabParent::RecvUpdateZoomConstraints(const bool& aAllowZoom,
const float& aMinZoom,
const float& aMaxZoom)
{
if (RenderFrameParent* rfp = GetRenderFrame()) {
rfp->UpdateZoomConstraints(aAllowZoom, aMinZoom, aMaxZoom);
}
return true;
}
bool
TabParent::RecvContentReceivedTouch(const bool& aPreventDefault)
{

View File

@ -136,6 +136,9 @@ public:
virtual bool RecvGetDPI(float* aValue);
virtual bool RecvGetWidgetNativeData(WindowsHandle* aValue);
virtual bool RecvZoomToRect(const gfxRect& aRect);
virtual bool RecvUpdateZoomConstraints(const bool& aAllowZoom,
const float& aMinZoom,
const float& aMaxZoom);
virtual bool RecvContentReceivedTouch(const bool& aPreventDefault);
virtual PContentDialogParent* AllocPContentDialog(const uint32_t& aType,
const nsCString& aName,

View File

@ -85,6 +85,9 @@ AsyncPanZoomController::AsyncPanZoomController(GeckoContentController* aGeckoCon
mTouchListenerTimeoutTask(nullptr),
mX(this),
mY(this),
mAllowZoom(true),
mMinZoom(MIN_ZOOM),
mMaxZoom(MAX_ZOOM),
mMonitor("AsyncPanZoomController"),
mLastSampleTime(TimeStamp::Now()),
mState(NOTHING),
@ -409,6 +412,10 @@ nsEventStatus AsyncPanZoomController::OnTouchCancel(const MultiTouchInput& aEven
}
nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEvent) {
if (!mAllowZoom) {
return nsEventStatus_eConsumeNoDefault;
}
SetState(PINCHING);
mLastZoomFocus = aEvent.mFocusPoint;
@ -416,6 +423,10 @@ nsEventStatus AsyncPanZoomController::OnScaleBegin(const PinchGestureInput& aEve
}
nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
if (mState != PINCHING) {
return nsEventStatus_eConsumeNoDefault;
}
float prevSpan = aEvent.mPreviousSpan;
if (fabsf(prevSpan) <= EPSILON || fabsf(aEvent.mCurrentSpan) <= EPSILON) {
// We're still handling it; we've just decided to throw this event away.
@ -447,14 +458,14 @@ nsEventStatus AsyncPanZoomController::OnScale(const PinchGestureInput& aEvent) {
float neededDisplacementX = 0, neededDisplacementY = 0;
// Only do the scaling if we won't go over 8x zoom in or out.
bool doScale = (scale < MAX_ZOOM && spanRatio > 1.0f) || (scale > MIN_ZOOM && spanRatio < 1.0f);
bool doScale = (scale < mMaxZoom && spanRatio > 1.0f) || (scale > mMinZoom && spanRatio < 1.0f);
// If this zoom will take it over 8x zoom in either direction, but it's not
// already there, then normalize it.
if (scale * spanRatio > MAX_ZOOM) {
spanRatio = scale / MAX_ZOOM;
} else if (scale * spanRatio < MIN_ZOOM) {
spanRatio = scale / MIN_ZOOM;
if (scale * spanRatio > mMaxZoom) {
spanRatio = scale / mMaxZoom;
} else if (scale * spanRatio < mMinZoom) {
spanRatio = scale / mMinZoom;
}
if (doScale) {
@ -550,10 +561,13 @@ nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent)
if (mGeckoContentController) {
MonitorAutoLock monitor(mMonitor);
gfx::Point point = WidgetSpaceToCompensatedViewportSpace(
gfx::Point(aEvent.mPoint.x, aEvent.mPoint.y),
mFrameMetrics.mZoom.width);
mGeckoContentController->HandleDoubleTap(nsIntPoint(NS_lround(point.x), NS_lround(point.y)));
if (mAllowZoom) {
gfx::Point point = WidgetSpaceToCompensatedViewportSpace(
gfx::Point(aEvent.mPoint.x, aEvent.mPoint.y),
mFrameMetrics.mZoom.width);
mGeckoContentController->HandleDoubleTap(nsIntPoint(NS_lround(point.x), NS_lround(point.y)));
}
return nsEventStatus_eConsumeNoDefault;
}
return nsEventStatus_eIgnore;
@ -728,13 +742,15 @@ bool AsyncPanZoomController::EnlargeDisplayPortAlongAxis(float aCompositionBound
return false;
}
const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort() {
float scale = mFrameMetrics.mZoom.width;
nsIntRect compositionBounds = mFrameMetrics.mCompositionBounds;
const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort(
const FrameMetrics& aFrameMetrics,
const gfx::Point& aVelocity)
{
float scale = aFrameMetrics.mZoom.width;
nsIntRect compositionBounds = aFrameMetrics.mCompositionBounds;
compositionBounds.ScaleInverseRoundIn(scale);
gfx::Point scrollOffset = mFrameMetrics.mScrollOffset;
gfx::Point velocity = GetVelocityVector();
gfx::Point scrollOffset = aFrameMetrics.mScrollOffset;
const float STATIONARY_SIZE_MULTIPLIER = 2.0f;
gfx::Rect displayPort(0, 0,
@ -745,9 +761,9 @@ const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort() {
// then we want to paint a larger area in the direction of that motion so that
// it's less likely to checkerboard.
bool enlargedX = EnlargeDisplayPortAlongAxis(
compositionBounds.width, velocity.x, &displayPort.x, &displayPort.width);
compositionBounds.width, aVelocity.x, &displayPort.x, &displayPort.width);
bool enlargedY = EnlargeDisplayPortAlongAxis(
compositionBounds.height, velocity.y, &displayPort.y, &displayPort.height);
compositionBounds.height, aVelocity.y, &displayPort.y, &displayPort.height);
if (!enlargedX && !enlargedY) {
displayPort.x = -displayPort.width / 4;
@ -760,7 +776,7 @@ const gfx::Rect AsyncPanZoomController::CalculatePendingDisplayPort() {
gfx::Rect shiftedDisplayPort = displayPort;
shiftedDisplayPort.MoveBy(scrollOffset.x, scrollOffset.y);
displayPort = shiftedDisplayPort.Intersect(mFrameMetrics.mScrollableRect);
displayPort = shiftedDisplayPort.Intersect(aFrameMetrics.mScrollableRect);
displayPort.MoveBy(-scrollOffset.x, -scrollOffset.y);
return displayPort;
@ -781,7 +797,8 @@ void AsyncPanZoomController::ScheduleComposite() {
}
void AsyncPanZoomController::RequestContentRepaint() {
mFrameMetrics.mDisplayPort = CalculatePendingDisplayPort();
mFrameMetrics.mDisplayPort =
CalculatePendingDisplayPort(mFrameMetrics, GetVelocityVector());
gfx::Point oldScrollOffset = mLastPaintRequestMetrics.mScrollOffset,
newScrollOffset = mFrameMetrics.mScrollOffset;
@ -958,11 +975,6 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr
mFrameMetrics.mZoom = mFrameMetrics.mResolution;
SetPageRect(mFrameMetrics.mScrollableRect);
// Bug 776413/fixme: Request a repaint as soon as a page is loaded so that
// we get a larger displayport. This is very bad because we're wasting a
// paint and not initializating the displayport correctly.
RequestContentRepaint();
mState = NOTHING;
} else if (!mFrameMetrics.mScrollableRect.IsEqualEdges(aViewportFrame.mScrollableRect)) {
mFrameMetrics.mScrollableRect = aViewportFrame.mScrollableRect;
@ -977,9 +989,25 @@ const FrameMetrics& AsyncPanZoomController::GetFrameMetrics() {
void AsyncPanZoomController::UpdateCompositionBounds(const nsIntRect& aCompositionBounds) {
MonitorAutoLock mon(mMonitor);
FrameMetrics metrics = GetFrameMetrics();
metrics.mCompositionBounds = aCompositionBounds;
mFrameMetrics = metrics;
nsIntRect oldCompositionBounds = mFrameMetrics.mCompositionBounds;
mFrameMetrics.mCompositionBounds = aCompositionBounds;
// If the window had 0 dimensions before, or does now, we don't want to
// repaint or update the zoom since we'll run into rendering issues and/or
// divide-by-zero. This manifests itself as the screen flashing. If the page
// has gone out of view, the buffer will be cleared elsewhere anyways.
if (aCompositionBounds.width && aCompositionBounds.height &&
oldCompositionBounds.width && oldCompositionBounds.height) {
// Alter the zoom such that we can see the same width of the page as we used
// to be able to.
SetZoomAndResolution(mFrameMetrics.mResolution.width *
aCompositionBounds.width /
oldCompositionBounds.width);
// Repaint on a rotation so that our new resolution gets properly updated.
RequestContentRepaint();
}
}
void AsyncPanZoomController::CancelDefaultPanZoom() {
@ -1043,7 +1071,9 @@ void AsyncPanZoomController::ZoomToRect(const gfxRect& aRect) {
NS_MIN(compositionBounds.width / zoomToRect.width, compositionBounds.height / zoomToRect.height);
mEndZoomToMetrics.mZoom.width = mEndZoomToMetrics.mZoom.height =
clamped(mEndZoomToMetrics.mZoom.width, MIN_ZOOM, MAX_ZOOM);
clamped(float(mEndZoomToMetrics.mZoom.width),
mMinZoom,
mMaxZoom);
// Recalculate the zoom to rect using the new dimensions.
zoomToRect.width = compositionBounds.width / mEndZoomToMetrics.mZoom.width;
@ -1117,5 +1147,13 @@ void AsyncPanZoomController::SetZoomAndResolution(float aScale) {
mFrameMetrics.mZoom.width = mFrameMetrics.mZoom.height = aScale;
}
void AsyncPanZoomController::UpdateZoomConstraints(bool aAllowZoom,
float aMinZoom,
float aMaxZoom) {
mAllowZoom = aAllowZoom;
mMinZoom = aMinZoom;
mMaxZoom = aMaxZoom;
}
}
}

View File

@ -14,6 +14,7 @@
#include "mozilla/TimeStamp.h"
#include "InputData.h"
#include "Axis.h"
#include "nsContentUtils.h"
#include "base/message_loop.h"
@ -130,6 +131,13 @@ public:
*/
void ContentReceivedTouch(bool aPreventDefault);
/**
* Updates any zoom constraints contained in the <meta name="viewport"> tag.
* We try to obey everything it asks us elsewhere, but here we only handle
* minimum-scale, maximum-scale, and user-scalable.
*/
void UpdateZoomConstraints(bool aAllowZoom, float aMinScale, float aMaxScale);
// --------------------------------------------------------------------------
// These methods must only be called on the compositor thread.
//
@ -189,6 +197,16 @@ public:
*/
int GetDPI();
/**
* Recalculates the displayport. Ideally, this should paint an area bigger
* than the composite-to dimensions so that when you scroll down, you don't
* checkerboard immediately. This includes a bunch of logic, including
* algorithms to bias painting in the direction of the velocity.
*/
static const gfx::Rect CalculatePendingDisplayPort(
const FrameMetrics& aFrameMetrics,
const gfx::Point& aVelocity);
protected:
/**
* Internal handler for ReceiveInputEvent(). Does all the actual work.
@ -339,15 +357,6 @@ protected:
*/
void TrackTouch(const MultiTouchInput& aEvent);
/**
* Recalculates the displayport. Ideally, this should paint an area bigger
* than the actual screen. The viewport refers to the size of the screen,
* while the displayport is the area actually painted by Gecko. We paint
* a larger area than the screen so that when you scroll down, you don't
* checkerboard immediately.
*/
const gfx::Rect CalculatePendingDisplayPort();
/**
* Attempts to enlarge the displayport along a single axis. Returns whether or
* not the displayport was enlarged. This will fail in circumstances where the
@ -356,8 +365,10 @@ protected:
* |aDisplayPortLength|. If enlarged, these will be updated with the new
* metrics.
*/
bool EnlargeDisplayPortAlongAxis(float aCompositionBounds, float aVelocity,
float* aDisplayPortOffset, float* aDisplayPortLength);
static bool EnlargeDisplayPortAlongAxis(float aCompositionBounds,
float aVelocity,
float* aDisplayPortOffset,
float* aDisplayPortLength);
/**
* Utility function to send updated FrameMetrics to Gecko so that it can paint
@ -471,10 +482,19 @@ private:
AxisX mX;
AxisY mY;
// Protects |mFrameMetrics|, |mLastContentPaintMetrics| and |mState|. Before
// manipulating |mFrameMetrics| or |mLastContentPaintMetrics|, the monitor
// should be held. When setting |mState|, either the SetState() function can
// be used, or the monitor can be held and then |mState| updated.
// Most up-to-date constraints on zooming. These should always be reasonable
// values; for example, allowing a min zoom of 0.0 can cause very bad things
// to happen.
bool mAllowZoom;
float mMinZoom;
float mMaxZoom;
// Protects |mFrameMetrics|, |mLastContentPaintMetrics|, |mState| and
// |mMetaViewportInfo|. Before manipulating |mFrameMetrics| or
// |mLastContentPaintMetrics|, the monitor should be held. When setting
// |mState|, either the SetState() function can be used, or the monitor can be
// held and then |mState| updated. |mMetaViewportInfo| should be updated
// using UpdateMetaViewport().
Monitor mMonitor;
// The last time the compositor has sampled the content transform for this

View File

@ -913,6 +913,14 @@ RenderFrameParent::ContentReceivedTouch(bool aPreventDefault)
}
}
void
RenderFrameParent::UpdateZoomConstraints(bool aAllowZoom, float aMinZoom, float aMaxZoom)
{
if (mPanZoomController) {
mPanZoomController->UpdateZoomConstraints(aAllowZoom, aMinZoom, aMaxZoom);
}
}
} // namespace layout
} // namespace mozilla

View File

@ -101,6 +101,8 @@ public:
void ContentReceivedTouch(bool aPreventDefault);
void UpdateZoomConstraints(bool aAllowZoom, float aMinZoom, float aMaxZoom);
protected:
void ActorDestroy(ActorDestroyReason why) MOZ_OVERRIDE;