diff --git a/b2g/app/Makefile.in b/b2g/app/Makefile.in index 3bfbe1df17c5..7fe9ef55e22d 100644 --- a/b2g/app/Makefile.in +++ b/b2g/app/Makefile.in @@ -30,7 +30,7 @@ LIBS += \ $(DEPTH)/widget/gonk/libdisplay/$(LIB_PREFIX)display.$(LIB_SUFFIX) \ $(MOZ_ZLIB_LIBS) \ $(NULL) -ifeq (17,$(ANDROID_VERSION)) +ifeq (18,$(ANDROID_VERSION)) LIBS += \ -lgui \ -lsuspend \ diff --git a/b2g/config/gaia.json b/b2g/config/gaia.json index e3a497cbd870..01a9409af9cb 100644 --- a/b2g/config/gaia.json +++ b/b2g/config/gaia.json @@ -1,4 +1,4 @@ { - "revision": "10f16f6a15f8a1c4b910548b4d0968baaac21c91", + "revision": "7e8284bea36c2d9307f919090922d3885f67eafc", "repo_path": "/integration/gaia-central" } diff --git a/configure.in b/configure.in index 805e27cbec44..6f708e50bcc0 100644 --- a/configure.in +++ b/configure.in @@ -214,7 +214,7 @@ if test -n "$gonkdir" ; then AC_DEFINE(MOZ_OMX_DECODER) AC_SUBST(MOZ_OMX_DECODER) ;; - 17) + 18) GONK_INCLUDES="-I$gonkdir/frameworks/native/include" ;; *) diff --git a/dom/browser-element/BrowserElementPanning.js b/dom/browser-element/BrowserElementPanning.js index abe8ced256bb..cf7cf7e371d0 100644 --- a/dom/browser-element/BrowserElementPanning.js +++ b/dom/browser-element/BrowserElementPanning.js @@ -45,16 +45,21 @@ const ContentPanning = { this.watchedEventsType = 'mouse'; } - let els = Cc["@mozilla.org/eventlistenerservice;1"] - .getService(Ci.nsIEventListenerService); + // If we are using an AsyncPanZoomController for the parent frame, + // it will handle subframe scrolling too. We don't need to listen for + // these events. + if (!this._asyncPanZoomForViewportFrame) { + let els = Cc["@mozilla.org/eventlistenerservice;1"] + .getService(Ci.nsIEventListenerService); - events.forEach(function(type) { - // Using the system group for mouse/touch events to avoid - // missing events if .stopPropagation() has been called. - els.addSystemEventListener(global, type, - this.handleEvent.bind(this), - /* useCapture = */ false); - }.bind(this)); + events.forEach(function(type) { + // Using the system group for mouse/touch events to avoid + // missing events if .stopPropagation() has been called. + els.addSystemEventListener(global, type, + this.handleEvent.bind(this), + /* useCapture = */ false); + }.bind(this)); + } addMessageListener("Viewport:Change", this._recvViewportChange.bind(this)); addMessageListener("Gesture:DoubleTap", this._recvDoubleTap.bind(this)); diff --git a/dom/contacts/fallback/ContactDB.jsm b/dom/contacts/fallback/ContactDB.jsm index 9821f6b33edc..495a913e5e73 100644 --- a/dom/contacts/fallback/ContactDB.jsm +++ b/dom/contacts/fallback/ContactDB.jsm @@ -905,9 +905,6 @@ ContactDB.prototype = { } xIndex++; } - if (!x) { - return sortOrder == "descending" ? 1 : -1; - } while (yIndex < sortBy.length && !y) { y = b.properties[sortBy[yIndex]]; if (y) { @@ -915,6 +912,18 @@ ContactDB.prototype = { } yIndex++; } + if (!x) { + if (!y) { + let px, py; + px = JSON.stringify(a.published); + py = JSON.stringify(b.published); + if (px && py) { + return px.localeCompare(py); + } + } else { + return sortOrder == 'descending' ? 1 : -1; + } + } if (!y) { return sortOrder == "ascending" ? 1 : -1; } diff --git a/dom/contacts/tests/test_contacts_basics.html b/dom/contacts/tests/test_contacts_basics.html index 63566f29beac..fffbb9a369df 100644 --- a/dom/contacts/tests/test_contacts_basics.html +++ b/dom/contacts/tests/test_contacts_basics.html @@ -61,6 +61,24 @@ var c5 = { nickname: "empty" }; +var c6 = { + name: "e", + familyName: ["e","e","e"], + givenName: ["e","e","e"], +}; + +var c7 = { + name: "e", + familyName: ["e","e","e"], + givenName: ["e","e","e"], +}; + +var c8 = { + name: "e", + familyName: ["e","e","e"], + givenName: ["e","e","e"], +}; + var adr1 = { type: "work", streetAddress: "street 1", @@ -1338,6 +1356,64 @@ var steps = [ } req.onerror = onFailure; }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(); + createResult1.init(c7); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c7, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(); + createResult1.init(c6); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c6, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting"); + createResult1 = new mozContact(); + createResult1.init(c8); + req = navigator.mozContacts.save(createResult1); + req.onsuccess = function () { + ok(createResult1.id, "The contact now has an ID."); + checkContacts(c8, createResult1); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Test sorting with published"); + var options = {sortBy: "familyName", + sortOrder: "descending"}; + req = navigator.mozContacts.find(options); + req.onsuccess = function () { + is(req.result.length, 3, "3 results"); + ok(req.result[0].published < req.result[1].published, "Right sorting order"); + ok(req.result[1].published < req.result[2].published, "Right sorting order"); + next(); + }; + req.onerror = onFailure; + }, + function () { + ok(true, "Deleting database"); + req = mozContacts.clear() + req.onsuccess = function () { + ok(true, "Deleted the database"); + next(); + } + req.onerror = onFailure; + }, function () { ok(true, "Adding a new contact with properties2"); createResult2 = new mozContact(); diff --git a/dom/ipc/TabChild.cpp b/dom/ipc/TabChild.cpp index 4359b3860046..2a658ba4fe71 100644 --- a/dom/ipc/TabChild.cpp +++ b/dom/ipc/TabChild.cpp @@ -1032,6 +1032,20 @@ TabChild::GetDOMWindowUtils() return utils.forget(); } +already_AddRefed +TabChild::GetDOMWindowUtils(nsIContent* content) +{ + nsCOMPtr utils; + nsIDocument* doc = content->GetCurrentDoc(); + if (doc) { + nsCOMPtr window = doc->GetDefaultView(); + if (window) { + utils = do_GetInterface(window); + } + } + return utils.forget(); +} + static nsInterfaceHashtable, nsIDialogParamBlock> gActiveDialogs; NS_IMETHODIMP @@ -1459,32 +1473,34 @@ TabChild::DispatchMessageManagerMessage(const nsAString& aMessageName, aMessageName, false, &cloneData, nullptr, nullptr); } -static void -ScrollWindowTo(nsIDOMWindow* aWindow, const CSSPoint& aPoint) -{ - nsGlobalWindow* window = static_cast(aWindow); - nsIScrollableFrame* sf = window->GetScrollFrame(); - - if (sf) { - sf->ScrollToCSSPixelsApproximate(aPoint); - } -} - bool TabChild::RecvUpdateFrame(const FrameMetrics& aFrameMetrics) { - nsCOMPtr utils(GetDOMWindowUtils()); + MOZ_ASSERT(aFrameMetrics.mScrollId != FrameMetrics::NULL_SCROLL_ID); + if (aFrameMetrics.mScrollId == FrameMetrics::ROOT_SCROLL_ID) { uint32_t presShellId; + nsCOMPtr utils(GetDOMWindowUtils()); nsresult rv = utils->GetPresShellId(&presShellId); MOZ_ASSERT(NS_SUCCEEDED(rv)); - if (NS_SUCCEEDED(rv) && aFrameMetrics.mPresShellId != presShellId) { - // We've recieved a message that is out of date and we want to ignore. - // However we can't reply without painting so we reply by painting the - // exact same thing as we did before. - return ProcessUpdateFrame(mLastMetrics); + + if (NS_SUCCEEDED(rv) && aFrameMetrics.mPresShellId == presShellId) { + return ProcessUpdateFrame(aFrameMetrics); } - return ProcessUpdateFrame(aFrameMetrics); + } else { + // aFrameMetrics.mScrollId is not FrameMetrics::ROOT_SCROLL_ID, + // so we are trying to update a subframe. This requires special handling. + nsCOMPtr content = nsLayoutUtils::FindContentFor( + aFrameMetrics.mScrollId); + if (content) { + return ProcessUpdateSubframe(content, aFrameMetrics); + } + } + + // We've recieved a message that is out of date and we want to ignore. + // However we can't reply without painting so we reply by painting the + // exact same thing as we did before. + return ProcessUpdateFrame(mLastMetrics); } bool @@ -1533,51 +1549,60 @@ TabChild::ProcessUpdateFrame(const FrameMetrics& aFrameMetrics) nsCOMPtr utils(GetDOMWindowUtils()); nsCOMPtr window = do_GetInterface(mWebNav); + // set the scroll port size, which determines the scroll range utils->SetScrollPositionClampingScrollPortSize( cssCompositedRect.width, cssCompositedRect.height); - ScrollWindowTo(window, aFrameMetrics.mScrollOffset); - LayoutDeviceToLayerScale resolution = - aFrameMetrics.CalculateResolution() - / aFrameMetrics.mDevPixelsPerCSSPixel - * ScreenToLayerScale(1); + + // scroll the window to the desired spot + nsIScrollableFrame* sf = static_cast(window.get())->GetScrollFrame(); + if (sf) { + sf->ScrollToCSSPixelsApproximate(aFrameMetrics.mScrollOffset); + } + + // set the resolution + LayoutDeviceToLayerScale resolution = aFrameMetrics.CalculateResolution() + / aFrameMetrics.mDevPixelsPerCSSPixel * ScreenToLayerScale(1); utils->SetResolution(resolution.scale, resolution.scale); - if (aFrameMetrics.mScrollId != FrameMetrics::NULL_SCROLL_ID) { - SetDisplayPort(aFrameMetrics); + // and set the display port + nsCOMPtr domDoc; + mWebNav->GetDocument(getter_AddRefs(domDoc)); + if (domDoc) { + nsCOMPtr element; + domDoc->GetDocumentElement(getter_AddRefs(element)); + if (element) { + utils->SetDisplayPortForElement( + aFrameMetrics.mDisplayPort.x, aFrameMetrics.mDisplayPort.y, + aFrameMetrics.mDisplayPort.width, aFrameMetrics.mDisplayPort.height, + element); + } } mLastMetrics = aFrameMetrics; - return true; } -void -TabChild::SetDisplayPort(const FrameMetrics& aFrameMetrics) +bool +TabChild::ProcessUpdateSubframe(nsIContent* aContent, + const FrameMetrics& aMetrics) { - nsCOMPtr domDoc; - mWebNav->GetDocument(getter_AddRefs(domDoc)); - if (!domDoc) { - return; + // scroll the frame to the desired spot + nsIScrollableFrame* scrollFrame = nsLayoutUtils::FindScrollableFrameFor(aMetrics.mScrollId); + if (scrollFrame) { + scrollFrame->ScrollToCSSPixelsApproximate(aMetrics.mScrollOffset); } - // nsLayoutUtils::FindContentFor() doesn't provide a look-up for the root - // scroll idea. This is because the root scroll ID could refer to a different - // element in the DOM when navigating to a new document. - nsCOMPtr element; - if (aFrameMetrics.mScrollId == FrameMetrics::ROOT_SCROLL_ID) { - domDoc->GetDocumentElement(getter_AddRefs(element)); - } else { - element = do_QueryInterface(nsLayoutUtils::FindContentFor( - aFrameMetrics.mScrollId)); - } - - if (element) { - nsCOMPtr utils(GetDOMWindowUtils()); + nsCOMPtr utils(GetDOMWindowUtils(aContent)); + nsCOMPtr element = do_QueryInterface(aContent); + if (utils && element) { + // and set the display port utils->SetDisplayPortForElement( - aFrameMetrics.mDisplayPort.x, aFrameMetrics.mDisplayPort.y, - aFrameMetrics.mDisplayPort.width, aFrameMetrics.mDisplayPort.height, + aMetrics.mDisplayPort.x, aMetrics.mDisplayPort.y, + aMetrics.mDisplayPort.width, aMetrics.mDisplayPort.height, element); } + + return true; } bool diff --git a/dom/ipc/TabChild.h b/dom/ipc/TabChild.h index eca8af55bb7a..e982da01546f 100644 --- a/dom/ipc/TabChild.h +++ b/dom/ipc/TabChild.h @@ -378,10 +378,7 @@ private: void DestroyWindow(); void SetProcessNameToAppName(); bool ProcessUpdateFrame(const mozilla::layers::FrameMetrics& aFrameMetrics); - - // Update the DOM with the given display port. Finds the element based on - // the aFrameMetrics.mScrollId. - void SetDisplayPort(const FrameMetrics& aFrameMetrics); + bool ProcessUpdateSubframe(nsIContent* aContent, const FrameMetrics& aMetrics); // Call RecvShow(nsIntSize(0, 0)) and block future calls to RecvShow(). void DoFakeShow(); @@ -426,7 +423,11 @@ private: bool* aWindowIsNew, nsIDOMWindow** aReturn); + // Get the DOMWindowUtils for the top-level window in this tab. already_AddRefed GetDOMWindowUtils(); + // Get the DOMWindowUtils for the window corresponding to the givent content + // element. This might be an iframe inside the tab, for instance. + already_AddRefed GetDOMWindowUtils(nsIContent* aContent); class CachedFileDescriptorInfo; class CachedFileDescriptorCallbackRunnable; diff --git a/dom/network/tests/marionette/manifest.ini b/dom/network/tests/marionette/manifest.ini index d61be3dc847f..a8adccf5f0ec 100644 --- a/dom/network/tests/marionette/manifest.ini +++ b/dom/network/tests/marionette/manifest.ini @@ -13,5 +13,6 @@ disabled = Bug 808783 [test_mobile_data_location.js] [test_mobile_data_state.js] [test_mobile_mmi.js] +[test_mobile_roaming_preference.js] [test_call_barring_get_option.js] [test_call_barring_set_error.js] diff --git a/dom/network/tests/marionette/test_mobile_roaming_preference.js b/dom/network/tests/marionette/test_mobile_roaming_preference.js new file mode 100644 index 000000000000..96c8ce4888e3 --- /dev/null +++ b/dom/network/tests/marionette/test_mobile_roaming_preference.js @@ -0,0 +1,98 @@ +/* Any copyright is dedicated to the Public Domain. + http://creativecommons.org/publicdomain/zero/1.0/ */ + +MARIONETTE_TIMEOUT = 60000; + +SpecialPowers.addPermission("mobileconnection", true, document); + +let connection = navigator.mozMobileConnection; +ok(connection instanceof MozMobileConnection, + "connection is instanceof " + connection.constructor); + +function failedToSetRoamingPreference(mode, expectedErrorMessage, callback) { + let request = connection.setRoamingPreference(mode); + + ok(request instanceof DOMRequest, + "request instanceof " + request.constructor); + + request.onsuccess = function onsuccess() { + ok(false, "Should not be here !!"); + + callback(); + } + + request.onerror = function onerror() { + is(request.error.name, expectedErrorMessage); + + callback(); + } +} + +function testSetRoamingPreferenceWithNullValue() { + log("test setRoamingPreference(null)"); + + failedToSetRoamingPreference(null, "InvalidParameter", runNextTest); +} + +function testSetRoamingPreferenceWithInvalidValue() { + log("test setRoamingPreference(\"InvalidValue\")"); + + failedToSetRoamingPreference("InvalidValue", "InvalidParameter", runNextTest); +} + +function testSetRoamingPreferenceToHome() { + log("test setRoamingPreference(\"home\")"); + + // TODO: Bug 896394. + // Currently emulator run as GSM mode by default. So we expect to get a + // 'RequestNotSupported' error here. + failedToSetRoamingPreference("home", "RequestNotSupported", runNextTest); +} + +function testGetRoamingPreference() { + log("test getRoamingPreference()"); + + // TODO: Bug 896394. + // Currently emulator run as GSM mode by default. So we expect to get a + // 'RequestNotSupported' error here. + let request = connection.getRoamingPreference(); + + ok(request instanceof DOMRequest, + "request instanceof " + request.constructor); + + request.onsuccess = function onsuccess() { + ok(false, "Should not be here !!"); + + runNextTest(); + } + + request.onerror = function onerror() { + is(request.error.name, "RequestNotSupported"); + + runNextTest(); + } +} + +let tests = [ + testSetRoamingPreferenceWithNullValue, + testSetRoamingPreferenceWithInvalidValue, + testSetRoamingPreferenceToHome, + testGetRoamingPreference +]; + +function runNextTest() { + let test = tests.shift(); + if (!test) { + cleanUp(); + return; + } + + test(); +} + +function cleanUp() { + SpecialPowers.removePermission("mobileconnection", document); + finish(); +} + +runNextTest(); \ No newline at end of file diff --git a/dom/system/gonk/ril_worker.js b/dom/system/gonk/ril_worker.js index c251bd7b1076..781125609866 100644 --- a/dom/system/gonk/ril_worker.js +++ b/dom/system/gonk/ril_worker.js @@ -1616,7 +1616,7 @@ let RIL = { if (roamingMode === -1) { options.errorMsg = GECKO_ERROR_INVALID_PARAMETER; - this.sendDOMMessage(options); + this.sendChromeMessage(options); return; } @@ -5649,22 +5649,22 @@ RIL[REQUEST_CDMA_SET_SUBSCRIPTION_SOURCE] = null; RIL[REQUEST_CDMA_SET_ROAMING_PREFERENCE] = function REQUEST_CDMA_SET_ROAMING_PREFERENCE(length, options) { if (options.rilRequestError) { options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError]; - this.sendDOMMessage(options); + this.sendChromeMessage(options); return; } - this.sendDOMMessage(options); + this.sendChromeMessage(options); }; RIL[REQUEST_CDMA_QUERY_ROAMING_PREFERENCE] = function REQUEST_CDMA_QUERY_ROAMING_PREFERENCE(length, options) { if (options.rilRequestError) { options.errorMsg = RIL_ERROR_TO_GECKO_ERROR[options.rilRequestError]; - this.sendDOMMessage(options); + this.sendChromeMessage(options); return; } let mode = Buf.readUint32List(); options.mode = CDMA_ROAMING_PREFERENCE_TO_GECKO[mode[0]]; - this.sendDOMMessage(options); + this.sendChromeMessage(options); }; RIL[REQUEST_SET_TTY_MODE] = null; RIL[REQUEST_QUERY_TTY_MODE] = null; diff --git a/dom/wappush/src/gonk/CpPduHelper.jsm b/dom/wappush/src/gonk/CpPduHelper.jsm index 0c68a03740e9..0172e51c56a9 100644 --- a/dom/wappush/src/gonk/CpPduHelper.jsm +++ b/dom/wappush/src/gonk/CpPduHelper.jsm @@ -86,21 +86,29 @@ this.PduHelper = { /** * Tag tokens * - * @see WAP-183-ProvCont-20010724-A, clause 8.1 + * @see WAP-183-ProvCont-20010724-A, clause 8.1 for code page 0 + * @see OMA-WAP-TS-ProvCont-V1_1-20090421-C, clause 7.1 for code page 1 */ const CP_TAG_FIELDS = (function () { let names = {}; - function add(name, number) { + function add(name, codepage, number) { let entry = { name: name, number: number, }; - names[number] = entry; + if (!names[codepage]) { + names[codepage] = {}; + } + names[codepage][number] = entry; } - add("wap-provisioningdoc", 0x05); - add("characteristic", 0x06); - add("parm", 0x07); + // Code page 0 + add("wap-provisioningdoc", 0, 0x05); + add("characteristic", 0, 0x06); + add("parm", 0, 0x07); + // Code page 1 + add("characteristic", 1, 0x06); + add("parm", 1, 0x07); return names; })(); @@ -108,155 +116,221 @@ const CP_TAG_FIELDS = (function () { /** * Attribute Tokens * - * @see WAP-183-ProvCont-20010724-A, clause 8.2 + * @see WAP-183-ProvCont-20010724-A, clause 8.2 for code page 0 + * @see OMA-WAP-TS-ProvCont-V1_1-20090421-C, clause 7.2 for code page 1 */ const CP_ATTRIBUTE_FIELDS = (function () { let names = {}; - function add(name, value, number) { + function add(name, value, codepage, number) { let entry = { name: name, value: value, number: number, }; - names[number] = entry; + if (!names[codepage]) { + names[codepage] = {}; + } + names[codepage][number] = entry; } - add("name", "", 0x05); - add("value", "", 0x06); - add("name", "NAME", 0x07); - add("name", "NAP-ADDRESS", 0x08); - add("name", "NAP-ADDRTYPE", 0x09); - add("name", "CALLTYPE", 0x0A); - add("name", "VALIDUNTIL", 0x0B); - add("name", "AUTHTYPE", 0x0C); - add("name", "AUTHNAME", 0x0D); - add("name", "AUTHSECRET", 0x0E); - add("name", "LINGER", 0x0F); - add("name", "BEARER", 0x10); - add("name", "NAPID", 0x11); - add("name", "COUNTRY", 0x12); - add("name", "NETWORK", 0x13); - add("name", "INTERNET", 0x14); - add("name", "PROXY-ID", 0x15); - add("name", "PROXY-PROVIDER-ID", 0x16); - add("name", "DOMAIN", 0x17); - add("name", "PROVURL", 0x18); - add("name", "PXAUTH-TYPE", 0x19); - add("name", "PXAUTH-ID", 0x1A); - add("name", "PXAUTH-PW", 0x1B); - add("name", "STARTPAGE", 0x1C); - add("name", "BASAUTH-ID", 0x1D); - add("name", "BASAUTH-PW", 0x1E); - add("name", "PUSHENABLED", 0x1F); - add("name", "PXADDR", 0x20); - add("name", "PXADDRTYPE", 0x21); - add("name", "TO-NAPID", 0x22); - add("name", "PORTNBR", 0x23); - add("name", "SERVICE", 0x24); - add("name", "LINKSPEED", 0x25); - add("name", "DNLINKSPEED", 0x26); - add("name", "LOCAL-ADDR", 0x27); - add("name", "LOCAL-ADDRTYPE", 0x28); - add("name", "CONTEXT-ALLOW", 0x29); - add("name", "TRUST", 0x2A); - add("name", "MASTER", 0x2B); - add("name", "SID", 0x2C); - add("name", "SOC", 0x2D); - add("name", "WSP-VERSION", 0x2E); - add("name", "PHYSICAL-PROXY-ID", 0x2F); - add("name", "CLIENT-ID", 0x30); - add("name", "DELIVERY-ERR-PDU", 0x31); - add("name", "DELIVERY-ORDER", 0x32); - add("name", "TRAFFIC-CLASS", 0x33); - add("name", "MAX-SDU-SIZE", 0x34); - add("name", "MAX-BITRATE-UPLINK", 0x35); - add("name", "MAX-BITRATE-DNLINK", 0x36); - add("name", "RESIDUAL-BER", 0x37); - add("name", "SDU-ERROR-RATIO", 0x38); - add("name", "TRAFFIC-HANDL-PRIO", 0x39); - add("name", "TRANSFER-DELAY", 0x3A); - add("name", "GUARANTEED-BITRATE-UPLINK", 0x3B); - add("name", "GUARANTEED-BITRATE-DNLINK", 0x3C); - add("version", "", 0x45); - add("version", "1.0", 0x46); - add("type", "", 0x50); - add("type", "PXLOGICAL", 0x51); - add("type", "PXPHYSICAL", 0x52); - add("type", "PORT", 0x53); - add("type", "VALIDITY", 0x54); - add("type", "NAPDEF", 0x55); - add("type", "BOOTSTRAP", 0x56); + // Code page 0 + add("name", "", 0, 0x05); + add("value", "", 0, 0x06); + add("name", "NAME", 0, 0x07); + add("name", "NAP-ADDRESS", 0, 0x08); + add("name", "NAP-ADDRTYPE", 0, 0x09); + add("name", "CALLTYPE", 0, 0x0A); + add("name", "VALIDUNTIL", 0, 0x0B); + add("name", "AUTHTYPE", 0, 0x0C); + add("name", "AUTHNAME", 0, 0x0D); + add("name", "AUTHSECRET", 0, 0x0E); + add("name", "LINGER", 0, 0x0F); + add("name", "BEARER", 0, 0x10); + add("name", "NAPID", 0, 0x11); + add("name", "COUNTRY", 0, 0x12); + add("name", "NETWORK", 0, 0x13); + add("name", "INTERNET", 0, 0x14); + add("name", "PROXY-ID", 0, 0x15); + add("name", "PROXY-PROVIDER-ID", 0, 0x16); + add("name", "DOMAIN", 0, 0x17); + add("name", "PROVURL", 0, 0x18); + add("name", "PXAUTH-TYPE", 0, 0x19); + add("name", "PXAUTH-ID", 0, 0x1A); + add("name", "PXAUTH-PW", 0, 0x1B); + add("name", "STARTPAGE", 0, 0x1C); + add("name", "BASAUTH-ID", 0, 0x1D); + add("name", "BASAUTH-PW", 0, 0x1E); + add("name", "PUSHENABLED", 0, 0x1F); + add("name", "PXADDR", 0, 0x20); + add("name", "PXADDRTYPE", 0, 0x21); + add("name", "TO-NAPID", 0, 0x22); + add("name", "PORTNBR", 0, 0x23); + add("name", "SERVICE", 0, 0x24); + add("name", "LINKSPEED", 0, 0x25); + add("name", "DNLINKSPEED", 0, 0x26); + add("name", "LOCAL-ADDR", 0, 0x27); + add("name", "LOCAL-ADDRTYPE", 0, 0x28); + add("name", "CONTEXT-ALLOW", 0, 0x29); + add("name", "TRUST", 0, 0x2A); + add("name", "MASTER", 0, 0x2B); + add("name", "SID", 0, 0x2C); + add("name", "SOC", 0, 0x2D); + add("name", "WSP-VERSION", 0, 0x2E); + add("name", "PHYSICAL-PROXY-ID", 0, 0x2F); + add("name", "CLIENT-ID", 0, 0x30); + add("name", "DELIVERY-ERR-PDU", 0, 0x31); + add("name", "DELIVERY-ORDER", 0, 0x32); + add("name", "TRAFFIC-CLASS", 0, 0x33); + add("name", "MAX-SDU-SIZE", 0, 0x34); + add("name", "MAX-BITRATE-UPLINK", 0, 0x35); + add("name", "MAX-BITRATE-DNLINK", 0, 0x36); + add("name", "RESIDUAL-BER", 0, 0x37); + add("name", "SDU-ERROR-RATIO", 0, 0x38); + add("name", "TRAFFIC-HANDL-PRIO", 0, 0x39); + add("name", "TRANSFER-DELAY", 0, 0x3A); + add("name", "GUARANTEED-BITRATE-UPLINK", 0, 0x3B); + add("name", "GUARANTEED-BITRATE-DNLINK", 0, 0x3C); + add("version", "", 0, 0x45); + add("version", "1.0", 0, 0x46); + add("type", "", 0, 0x50); + add("type", "PXLOGICAL", 0, 0x51); + add("type", "PXPHYSICAL", 0, 0x52); + add("type", "PORT", 0, 0x53); + add("type", "VALIDITY", 0, 0x54); + add("type", "NAPDEF", 0, 0x55); + add("type", "BOOTSTRAP", 0, 0x56); /* * Mark out VENDORCONFIG so if it is contained in message, parse * will failed and raw data is returned. */ -// add("type", "VENDORCONFIG", 0x57); - add("type", "CLIENTIDENTITY", 0x58); - add("type", "PXAUTHINFO", 0x59); - add("type", "NAPAUTHINFO", 0x5A); +// add("type", "VENDORCONFIG", 0, 0x57); + add("type", "CLIENTIDENTITY", 0, 0x58); + add("type", "PXAUTHINFO", 0, 0x59); + add("type", "NAPAUTHINFO", 0, 0x5A); + + // Code page 1 + add("name", "", 1, 0x05); + add("value", "", 1, 0x06); + add("name", "NAME", 1, 0x07); + add("name", "INTERNET", 1, 0x14); + add("name", "STARTPAGE", 1, 0x1C); + add("name", "TO-NAPID", 1, 0x22); + add("name", "PORTNBR", 1, 0x23); + add("name", "SERVICE", 1, 0x24); + add("name", "AACCEPT", 1, 0x2E); + add("name", "AAUTHDATA", 1, 0x2F); + add("name", "AAUTHLEVEL", 1, 0x30); + add("name", "AAUTHNAME", 1, 0x31); + add("name", "AAUTHSECRET", 1, 0x32); + add("name", "AAUTHTYPE", 1, 0x33); + add("name", "ADDR", 1, 0x34); + add("name", "ADDRTYPE", 1, 0x35); + add("name", "APPID", 1, 0x36); + add("name", "APROTOCOL", 1, 0x37); + add("name", "PROVIDER-ID", 1, 0x38); + add("name", "TO-PROXY", 1, 0x39); + add("name", "URI", 1, 0x3A); + add("name", "RULE", 1, 0x3B); + add("type", "", 1, 0x50); + add("type", "PORT", 1, 0x53); + add("type", "APPLICATION", 1, 0x55); + add("type", "APPADDR", 1, 0x56); + add("type", "APPAUTH", 1, 0x57); + add("type", "CLIENTIDENTITY", 1, 0x58); + add("type", "RESOURCE", 1, 0x59); return names; })(); +/** + * Value Tokens + * + * @see WAP-183-ProvCont-20010724-A, clause 8.3 for code page 0 + * @see OMA-WAP-TS-ProvCont-V1_1-20090421-C, clause 7.3 for code page 1 + */ const CP_VALUE_FIELDS = (function () { let names = {}; - function add(value, number) { + function add(value, codepage, number) { let entry = { value: value, number: number, }; - names[number] = entry; + if (!names[codepage]) { + names[codepage] = {}; + } + names[codepage][number] = entry; } - add("IPV4", 0x85); - add("IPV6", 0x86); - add("E164", 0x87); - add("ALPHA", 0x88); - add("APN", 0x89); - add("SCODE", 0x8A); - add("TETRA-ITSI", 0x8B); - add("MAN", 0x8C); - add("ANALOG-MODEM", 0x90); - add("V.120", 0x91); - add("V.110", 0x92); - add("X.31", 0x93); - add("BIT-TRANSPARENT", 0x94); - add("DIRECT-ASYNCHRONOUS-DATA-SERVICE", 0x95); - add("PAP", 0x9A); - add("CHAP", 0x9B); - add("HTTP-BASIC", 0x9C); - add("HTTP-DIGEST", 0x9D); - add("WTLS-SS", 0x9E); - add("GSM-USSD", 0xA2); - add("GSM-SMS", 0xA3); - add("ANSI-136-GUTS", 0xA4); - add("IS-95-CDMA-SMS", 0xA5); - add("IS-95-CDMA-CSD", 0xA6); - add("IS-95-CDMA-PAC", 0xA7); - add("ANSI-136-CSD", 0xA8); - add("ANSI-136-GPRS", 0xA9); - add("GSM-CSD", 0xAA); - add("GSM-GPRS", 0xAB); - add("AMPS-CDPD", 0xAC); - add("PDC-CSD", 0xAD); - add("PDC-PACKET", 0xAE); - add("IDEN-SMS", 0xAF); - add("IDEN-CSD", 0xB0); - add("IDEN-PACKET", 0xB1); - add("FLEX/REFLEX", 0xB2); - add("PHS-SMS", 0xB3); - add("PHS-CSD", 0xB4); - add("TETRA-SDS", 0xB5); - add("TETRA-PACKET", 0xB6); - add("ANSI-136-GHOST", 0xB7); - add("MOBITEX-MPAK", 0xB8); - add("AUTOBOUDING", 0xC5); - add("CL-WSP", 0xCA); - add("CO-WSP", 0xCB); - add("CL-SEC-WSP", 0xCC); - add("CO-SEC-WSP", 0xCD); - add("CL-SEC-WTA", 0xCE); - add("CO-SEC-WTA", 0xCF); + // Code page 0 + add("IPV4", 0, 0x85); + add("IPV6", 0, 0x86); + add("E164", 0, 0x87); + add("ALPHA", 0, 0x88); + add("APN", 0, 0x89); + add("SCODE", 0, 0x8A); + add("TETRA-ITSI", 0, 0x8B); + add("MAN", 0, 0x8C); + add("ANALOG-MODEM", 0, 0x90); + add("V.120", 0, 0x91); + add("V.110", 0, 0x92); + add("X.31", 0, 0x93); + add("BIT-TRANSPARENT", 0, 0x94); + add("DIRECT-ASYNCHRONOUS-DATA-SERVICE", 0, 0x95); + add("PAP", 0, 0x9A); + add("CHAP", 0, 0x9B); + add("HTTP-BASIC", 0, 0x9C); + add("HTTP-DIGEST", 0, 0x9D); + add("WTLS-SS", 0, 0x9E); + add("MD5", 0, 0x9F); // Added in OMA, 7.3.3 + add("GSM-USSD", 0, 0xA2); + add("GSM-SMS", 0, 0xA3); + add("ANSI-136-GUTS", 0, 0xA4); + add("IS-95-CDMA-SMS", 0, 0xA5); + add("IS-95-CDMA-CSD", 0, 0xA6); + add("IS-95-CDMA-PAC", 0, 0xA7); + add("ANSI-136-CSD", 0, 0xA8); + add("ANSI-136-GPRS", 0, 0xA9); + add("GSM-CSD", 0, 0xAA); + add("GSM-GPRS", 0, 0xAB); + add("AMPS-CDPD", 0, 0xAC); + add("PDC-CSD", 0, 0xAD); + add("PDC-PACKET", 0, 0xAE); + add("IDEN-SMS", 0, 0xAF); + add("IDEN-CSD", 0, 0xB0); + add("IDEN-PACKET", 0, 0xB1); + add("FLEX/REFLEX", 0, 0xB2); + add("PHS-SMS", 0, 0xB3); + add("PHS-CSD", 0, 0xB4); + add("TETRA-SDS", 0, 0xB5); + add("TETRA-PACKET", 0, 0xB6); + add("ANSI-136-GHOST", 0, 0xB7); + add("MOBITEX-MPAK", 0, 0xB8); + add("CDMA2000-1X-SIMPLE-IP", 0, 0xB9); // Added in OMA, 7.3.4 + add("CDMA2000-1X-MOBILE-IP", 0, 0xBA); // Added in OMA, 7.3.4 + add("AUTOBOUDING", 0, 0xC5); + add("CL-WSP", 0, 0xCA); + add("CO-WSP", 0, 0xCB); + add("CL-SEC-WSP", 0, 0xCC); + add("CO-SEC-WSP", 0, 0xCD); + add("CL-SEC-WTA", 0, 0xCE); + add("CO-SEC-WTA", 0, 0xCF); + add("OTA-HTTP-TO", 0, 0xD0); // Added in OMA, 7.3.6 + add("OTA-HTTP-TLS-TO", 0, 0xD1); // Added in OMA, 7.3.6 + add("OTA-HTTP-PO", 0, 0xD2); // Added in OMA, 7.3.6 + add("OTA-HTTP-TLS-PO", 0, 0xD3); // Added in OMA, 7.3.6 + add("AAA", 0, 0xE0); // Added in OMA, 7.3.8 + add("HA", 0, 0xE1); // Added in OMA, 7.3.8 + + // Code page 1 + add("IPV6", 1, 0x86); + add("E164", 1, 0x87); + add("ALPHA", 1, 0x88); + add("APPSRV", 1, 0x8D); + add("OBEX", 1, 0x8E); + add(",", 1, 0x90); + add("HTTP-", 1, 0x91); + add("BASIC", 1, 0x92); + add("DIGEST", 1, 0x93); return names; })(); diff --git a/dom/wappush/src/gonk/SiPduHelper.jsm b/dom/wappush/src/gonk/SiPduHelper.jsm index dc8bd5b3c8e4..69367d0c2299 100644 --- a/dom/wappush/src/gonk/SiPduHelper.jsm +++ b/dom/wappush/src/gonk/SiPduHelper.jsm @@ -134,18 +134,21 @@ this.PduHelper = { */ const SI_TAG_FIELDS = (function () { let names = {}; - function add(name, number) { + function add(name, codepage, number) { let entry = { name: name, number: number, }; - names[number] = entry; + if (!names[codepage]) { + names[codepage] = {}; + } + names[codepage][number] = entry; } - add("si", 0x05); - add("indication", 0x06); - add("info", 0x07); - add("item", 0x08); + add("si", 0, 0x05); + add("indication", 0, 0x06); + add("info", 0, 0x07); + add("item", 0, 0x08); return names; })(); @@ -157,47 +160,53 @@ const SI_TAG_FIELDS = (function () { */ const SI_ATTRIBUTE_FIELDS = (function () { let names = {}; - function add(name, value, number) { + function add(name, value, codepage, number) { let entry = { name: name, value: value, number: number, }; - names[number] = entry; + if (!names[codepage]) { + names[codepage] = {}; + } + names[codepage][number] = entry; } - add("action", "signal-none", 0x05); - add("action", "signal-low", 0x06); - add("action", "signal-medium", 0x07); - add("action", "signal-high", 0x08); - add("action", "delete", 0x09); - add("created", "", 0x0A); - add("href", "", 0x0B); - add("href", "http://", 0x0C); - add("href", "http://www.", 0x0D); - add("href", "https://", 0x0E); - add("href", "https://www.", 0x0F); - add("si-expires", "", 0x10); - add("si-id", "", 0x11); - add("class", "", 0x12); + add("action", "signal-none", 0, 0x05); + add("action", "signal-low", 0, 0x06); + add("action", "signal-medium", 0, 0x07); + add("action", "signal-high", 0, 0x08); + add("action", "delete", 0, 0x09); + add("created", "", 0, 0x0A); + add("href", "", 0, 0x0B); + add("href", "http://", 0, 0x0C); + add("href", "http://www.", 0, 0x0D); + add("href", "https://", 0, 0x0E); + add("href", "https://www.", 0, 0x0F); + add("si-expires", "", 0, 0x10); + add("si-id", "", 0, 0x11); + add("class", "", 0, 0x12); return names; })(); const SI_VALUE_FIELDS = (function () { let names = {}; - function add(value, number) { + function add(value, codepage, number) { let entry = { value: value, number: number, }; - names[number] = entry; + if (!names[codepage]) { + names[codepage] = {}; + } + names[codepage][number] = entry; } - add(".com/", 0x85); - add(".edu/", 0x86); - add(".net/", 0x87); - add(".org/", 0x88); + add(".com/", 0, 0x85); + add(".edu/", 0, 0x86); + add(".net/", 0, 0x87); + add(".org/", 0, 0x88); return names; })(); diff --git a/dom/wappush/src/gonk/SlPduHelper.jsm b/dom/wappush/src/gonk/SlPduHelper.jsm index eba294318198..1973b9cb4ef8 100644 --- a/dom/wappush/src/gonk/SlPduHelper.jsm +++ b/dom/wappush/src/gonk/SlPduHelper.jsm @@ -90,15 +90,18 @@ this.PduHelper = { */ const SL_TAG_FIELDS = (function () { let names = {}; - function add(name, number) { + function add(name, codepage, number) { let entry = { name: name, number: number, }; - names[number] = entry; + if (!names[codepage]) { + names[codepage] = {}; + } + names[codepage][number] = entry; } - add("sl", 0x05); + add("sl", 0, 0x05); return names; })(); @@ -110,41 +113,47 @@ const SL_TAG_FIELDS = (function () { */ const SL_ATTRIBUTE_FIELDS = (function () { let names = {}; - function add(name, value, number) { + function add(name, value, codepage, number) { let entry = { name: name, value: value, number: number, }; - names[number] = entry; + if (!names[codepage]) { + names[codepage] = {}; + } + names[codepage][number] = entry; } - add("action", "execute-low", 0x05); - add("action", "execute-high", 0x06); - add("action", "cache", 0x07); - add("href", "", 0x08); - add("href", "http://", 0x09); - add("href", "http://www.", 0x0A); - add("href", "https://", 0x0B); - add("href", "https://www.", 0x0C); + add("action", "execute-low", 0, 0x05); + add("action", "execute-high", 0, 0x06); + add("action", "cache", 0, 0x07); + add("href", "", 0, 0x08); + add("href", "http://", 0, 0x09); + add("href", "http://www.", 0, 0x0A); + add("href", "https://", 0, 0x0B); + add("href", "https://www.", 0, 0x0C); return names; })(); const SL_VALUE_FIELDS = (function () { let names = {}; - function add(value, number) { + function add(value, codepage, number) { let entry = { value: value, number: number, }; - names[number] = entry; + if (!names[codepage]) { + names[codepage] = {}; + } + names[codepage][number] = entry; } - add(".com/", 0x85); - add(".edu/", 0x86); - add(".net/", 0x87); - add(".org/", 0x88); + add(".com/", 0, 0x85); + add(".edu/", 0, 0x86); + add(".net/", 0, 0x87); + add(".org/", 0, 0x88); return names; })(); diff --git a/dom/wappush/src/gonk/WapPushManager.js b/dom/wappush/src/gonk/WapPushManager.js index 96e87b0a4420..217bb989b60c 100644 --- a/dom/wappush/src/gonk/WapPushManager.js +++ b/dom/wappush/src/gonk/WapPushManager.js @@ -62,8 +62,8 @@ this.WapPushManager = { let appid = options.headers["x-wap-application-id"]; if (!appid) { + // Assume message without applicatioin ID is WAP Push debug("Push message doesn't contains X-Wap-Application-Id."); - return; } // MMS diff --git a/dom/wappush/src/gonk/WbxmlPduHelper.jsm b/dom/wappush/src/gonk/WbxmlPduHelper.jsm index 67d231881aa4..f58577b2e9b4 100644 --- a/dom/wappush/src/gonk/WbxmlPduHelper.jsm +++ b/dom/wappush/src/gonk/WbxmlPduHelper.jsm @@ -23,6 +23,7 @@ const TAG_TOKEN_VALUE_MASK = 0x3F; * * @see WAP-192-WBXML-20010725-A, clause 7.1 */ +const CODE_PAGE_SWITCH_TOKEN = 0x00; const TAG_END_TOKEN = 0x01; const INLINE_STRING_TOKEN = 0x03; const STRING_TABLE_TOKEN = 0x83; @@ -34,11 +35,53 @@ this.DEBUG_ALL = false; // Enable debug message for WBXML decoder core. this.DEBUG = DEBUG_ALL | false; +/** + * Handle WBXML code page switch. + * + * @param data + * A wrapped object containing raw PDU data. + * @param decodeInfo + * Internal information for decode process. + * + * @see WAP-192-WBXML-20010725-A, clause 5.8.4.7.2 and 5.8.1 + */ +this.WbxmlCodePageSwitch = { + decode: function decode_wbxml_code_page_switch(data, decodeInfo) { + let codePage = WSP.Octet.decode(data); + + if (decodeInfo.currentState === "tag") { + decodeInfo.currentTokenList.tag = decodeInfo.tokenList.tag[codePage]; + + if (!decodeInfo.currentTokenList.tag) { + throw new Error("Invalid tag code page: " + codePage + "."); + } + + return ""; + } + + if (decodeInfo.currentState === "attr") { + decodeInfo.currentTokenList.attr = decodeInfo.tokenList.attr[codePage]; + decodeInfo.currentTokenList.value = decodeInfo.tokenList.value[codePage]; + + if (!decodeInfo.currentTokenList.attr || + !decodeInfo.currentTokenList.value) { + throw new Error("Invalid attr code page: " + codePage + "."); + } + + return ""; + } + + throw new Error("Invalid decoder state: " + decodeInfo.currentState + "."); + }, +}; + /** * Parse end WBXML encoded message. * * @param data * A wrapped object containing raw PDU data. + * @param decodeInfo + * Internal information for decode process. * * @see WAP-192-WBXML-20010725-A, clause 5.8.4.7.1 * @@ -84,6 +127,8 @@ this.WbxmlStringTable = { * * @param data * A wrapped object containing raw PDU data. + * @param decodeInfo + * Internal information for decode process. * * @see WAP-192-WBXML-20010725-A, clause 5.8.4.1 * @@ -106,6 +151,8 @@ this.WbxmlInlineString = { * * @param data * A wrapped object containing raw PDU data. + * @param decodeInfo + * Internal information for decode process. * * @see WAP-192-WBXML-20010725-A, clause 5.8.4.6 * @@ -135,11 +182,20 @@ this.PduHelper = { parseWbxml: function parseWbxml_wbxml(data, decodeInfo, appToken) { // Parse token definition to my structure. - let tagTokenList = appToken.tagTokenList; - let attrTokenList = appToken.attrTokenList; - let valueTokenList = appToken.valueTokenList; - - decodeInfo.tagStack = []; // tag decode stack + decodeInfo.tokenList = { + tag: appToken.tagTokenList, + attr: appToken.attrTokenList, + value: appToken.valueTokenList + }; + decodeInfo.tagStack = []; // tag decode stack + decodeInfo.currentTokenList = { + tag: decodeInfo.tokenList.tag[0], + attr: decodeInfo.tokenList.attr[0], + value: decodeInfo.tokenList.value[0] + }; + decodeInfo.currentState = "tag"; // Current decoding state, "tag" or "attr" + // Used to read corresponding code page + // initial state : "tag" // Merge global tag tokens into single list, so we don't have // to search two lists every time. @@ -156,6 +212,9 @@ this.PduHelper = { // Decode content, might be a new tag token, an end of tag token, or an // inline string. + // Switch to tag domain + decodeInfo.currentState = "tag"; + let tagToken = WSP.Octet.decode(data); let tagTokenValue = tagToken & TAG_TOKEN_VALUE_MASK; @@ -170,7 +229,7 @@ this.PduHelper = { } // Check if application tag token is valid - tagInfo = tagTokenList[tagTokenValue]; + tagInfo = decodeInfo.currentTokenList.tag[tagTokenValue]; if (!tagInfo) { throw new Error("Unsupported WBXML token: " + tagTokenValue + "."); } @@ -181,6 +240,9 @@ this.PduHelper = { // Decode attributes, might be a new attribute token, a value token, // or an inline string + // Switch to attr/value domain + decodeInfo.currentState = "attr"; + let attrSeperator = ""; while (data.offset < data.array.length) { let attrToken = WSP.Octet.decode(data); @@ -195,14 +257,14 @@ this.PduHelper = { } // Check if attribute token is valid - attrInfo = attrTokenList[attrToken]; + attrInfo = decodeInfo.currentTokenList.attr[attrToken]; if (attrInfo) { content += attrSeperator + " " + attrInfo.name + "=\"" + attrInfo.value; attrSeperator = "\""; continue; } - attrInfo = valueTokenList[attrToken]; + attrInfo = decodeInfo.currentTokenList.value[attrToken]; if (attrInfo) { content += attrInfo.value; continue; @@ -325,10 +387,11 @@ const WBXML_GLOBAL_TOKENS = (function () { names[number] = entry; } - add(TAG_END_TOKEN, WbxmlEnd); - add(INLINE_STRING_TOKEN, WbxmlInlineString); - add(STRING_TABLE_TOKEN, WbxmlStringTable); - add(OPAQUE_TOKEN, WbxmlOpaque); + add(CODE_PAGE_SWITCH_TOKEN, WbxmlCodePageSwitch); + add(TAG_END_TOKEN, WbxmlEnd); + add(INLINE_STRING_TOKEN, WbxmlInlineString); + add(STRING_TABLE_TOKEN, WbxmlStringTable); + add(OPAQUE_TOKEN, WbxmlOpaque); return names; })(); diff --git a/dom/wappush/tests/test_cp_pdu_helper.js b/dom/wappush/tests/test_cp_pdu_helper.js index c43f280eb734..f98ddaffaad8 100644 --- a/dom/wappush/tests/test_cp_pdu_helper.js +++ b/dom/wappush/tests/test_cp_pdu_helper.js @@ -357,3 +357,143 @@ add_test(function test_cp_parse_wbxml() { run_next_test(); }); + +/** + * CP compressed by WBXML with code page switch + */ +add_test(function test_cp_parse_wbxml_code_page() { + let wbxml_code_page_data_array = new Uint8Array([ + 0x03, 0x0B, 0x6A, 0x00, 0x45, 0xC6, 0x56, 0x01, + 0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F, + 0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x01, + 0xC6, 0x00, 0x01, 0x55, 0x01, 0x87, 0x36, 0x00, + 0x00, 0x06, 0x03, 0x77, 0x32, 0x00, 0x01, 0x87, + 0x00, 0x01, 0x39, 0x00, 0x00, 0x06, 0x03, 0x57, + 0x50, 0x52, 0x4F, 0x58, 0x59, 0x00, 0x01, 0x87, + 0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F, 0x65, + 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0xC6, 0x00, + 0x01, 0x59, 0x01, 0x87, 0x3A, 0x00, 0x00, 0x06, + 0x03, 0x68, 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, + 0x77, 0x61, 0x70, 0x2E, 0x65, 0x6D, 0x6F, 0x6D, + 0x65, 0x2E, 0x6E, 0x65, 0x74, 0x2F, 0x00, 0x01, + 0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F, + 0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x87, + 0x1C, 0x01, 0x01, 0x01, 0xC6, 0x00, 0x01, 0x55, + 0x01, 0x87, 0x36, 0x00, 0x00, 0x06, 0x03, 0x77, + 0x34, 0x00, 0x01, 0x87, 0x00, 0x01, 0x39, 0x00, + 0x00, 0x06, 0x03, 0x4D, 0x50, 0x52, 0x4F, 0x58, + 0x59, 0x00, 0x01, 0x87, 0x00, 0x01, 0x34, 0x00, + 0x00, 0x06, 0x03, 0x68, 0x74, 0x74, 0x70, 0x3A, + 0x2F, 0x2F, 0x6D, 0x6D, 0x73, 0x3A, 0x38, 0x30, + 0x30, 0x32, 0x00, 0x01, 0x01, 0xC6, 0x51, 0x01, + 0x87, 0x15, 0x06, 0x03, 0x57, 0x50, 0x52, 0x4F, + 0x58, 0x59, 0x00, 0x01, 0x87, 0x07, 0x06, 0x03, + 0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F, 0x6D, + 0x65, 0x00, 0x01, 0x87, 0x1C, 0x06, 0x03, 0x68, + 0x74, 0x74, 0x70, 0x3A, 0x2F, 0x2F, 0x77, 0x61, + 0x70, 0x2E, 0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x2E, + 0x6E, 0x65, 0x74, 0x2F, 0x00, 0x01, 0xC6, 0x52, + 0x01, 0x87, 0x2F, 0x06, 0x03, 0x50, 0x52, 0x4F, + 0x58, 0x59, 0x31, 0x00, 0x01, 0x87, 0x20, 0x06, + 0x03, 0x31, 0x30, 0x2E, 0x31, 0x2E, 0x31, 0x2E, + 0x31, 0x00, 0x01, 0x87, 0x21, 0x06, 0x85, 0x01, + 0x87, 0x22, 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F, + 0x65, 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0xC6, + 0x53, 0x01, 0x87, 0x23, 0x06, 0x03, 0x38, 0x30, + 0x38, 0x30, 0x00, 0x01, 0x01, 0x01, 0x01, 0xC6, + 0x51, 0x01, 0x87, 0x15, 0x06, 0x03, 0x4D, 0x50, + 0x52, 0x4F, 0x58, 0x59, 0x00, 0x01, 0x87, 0x07, + 0x06, 0x03, 0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D, + 0x53, 0x00, 0x01, 0xC6, 0x52, 0x01, 0x87, 0x2F, + 0x06, 0x03, 0x50, 0x52, 0x4F, 0x58, 0x59, 0x32, + 0x00, 0x01, 0x87, 0x20, 0x06, 0x03, 0x31, 0x30, + 0x2E, 0x31, 0x2E, 0x31, 0x2E, 0x31, 0x00, 0x01, + 0x87, 0x21, 0x06, 0x85, 0x01, 0x87, 0x22, 0x06, + 0x03, 0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D, 0x53, + 0x00, 0x01, 0xC6, 0x53, 0x01, 0x87, 0x23, 0x06, + 0x03, 0x38, 0x30, 0x38, 0x30, 0x00, 0x01, 0x01, + 0x01, 0x01, 0xC6, 0x55, 0x01, 0x87, 0x11, 0x06, + 0x03, 0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F, + 0x6D, 0x65, 0x00, 0x01, 0x87, 0x07, 0x06, 0x03, + 0x43, 0x48, 0x54, 0x5F, 0x65, 0x6D, 0x6F, 0x6D, + 0x65, 0x00, 0x01, 0x87, 0x10, 0x06, 0xAB, 0x01, + 0x87, 0x08, 0x06, 0x03, 0x65, 0x6D, 0x6F, 0x6D, + 0x65, 0x00, 0x01, 0x87, 0x09, 0x06, 0x89, 0x01, + 0x01, 0xC6, 0x55, 0x01, 0x87, 0x11, 0x06, 0x03, + 0x43, 0x48, 0x54, 0x5F, 0x4D, 0x4D, 0x53, 0x00, + 0x01, 0x87, 0x07, 0x06, 0x03, 0x43, 0x48, 0x54, + 0x5F, 0x4D, 0x4D, 0x53, 0x00, 0x01, 0x87, 0x10, + 0x06, 0xAB, 0x01, 0x87, 0x08, 0x06, 0x03, 0x65, + 0x6D, 0x6F, 0x6D, 0x65, 0x00, 0x01, 0x87, 0x09, + 0x06, 0x89, 0x01, 0x01, 0x01 + ]); + let wbxml_content = + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + + test_parser(wbxml_code_page_data_array, "application/vnd.wap.connectivity-wbxml", { + contentType: "text/vnd.wap.connectivity-xml", + content: wbxml_content + }); + + run_next_test(); +}); diff --git a/gfx/layers/Compositor.cpp b/gfx/layers/Compositor.cpp index faaad0d34e14..4b67f947ff1d 100644 --- a/gfx/layers/Compositor.cpp +++ b/gfx/layers/Compositor.cpp @@ -13,11 +13,17 @@ namespace layers { /* static */ LayersBackend Compositor::sBackend = LAYERS_NONE; /* static */ LayersBackend Compositor::GetBackend() +{ + AssertOnCompositorThread(); + return sBackend; +} + +/* static */ void +Compositor::AssertOnCompositorThread() { MOZ_ASSERT(CompositorParent::CompositorLoop() == MessageLoop::current(), "Can only call this from the compositor thread!"); - return sBackend; } void diff --git a/gfx/layers/Compositor.h b/gfx/layers/Compositor.h index 03fc0979c4c8..f7d6bfbf1365 100644 --- a/gfx/layers/Compositor.h +++ b/gfx/layers/Compositor.h @@ -398,6 +398,12 @@ public: virtual nsIWidget* GetWidget() const { return nullptr; } virtual const nsIntSize& GetWidgetSize() = 0; + /** + * Debug-build assertion that can be called to ensure code is running on the + * compositor thread. + */ + static void AssertOnCompositorThread(); + /** * We enforce that there can only be one Compositor backend type off the main * thread at the same time. The backend type in use can be checked with this diff --git a/gfx/layers/composite/APZCTreeManager.cpp b/gfx/layers/composite/APZCTreeManager.cpp new file mode 100644 index 000000000000..d00d44a00c0b --- /dev/null +++ b/gfx/layers/composite/APZCTreeManager.cpp @@ -0,0 +1,386 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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 "APZCTreeManager.h" +#include "AsyncCompositionManager.h" // for ViewTransform +#include "LayerManagerComposite.h" // for AsyncCompositionManager.h +#include "Compositor.h" + +#define APZC_LOG(...) +// #define APZC_LOG(args...) printf_stderr(args) + +namespace mozilla { +namespace layers { + +APZCTreeManager::APZCTreeManager() + : mTreeLock("APZCTreeLock") +{ + MOZ_ASSERT(NS_IsMainThread()); + AsyncPanZoomController::InitializeGlobalState(); +} + +void +APZCTreeManager::AssertOnCompositorThread() +{ + Compositor::AssertOnCompositorThread(); +} + +/* Flatten the tree of APZC instances into the given nsTArray */ +static void +Collect(AsyncPanZoomController* aApzc, nsTArray< nsRefPtr >* aCollection) +{ + if (aApzc) { + aCollection->AppendElement(aApzc); + Collect(aApzc->GetLastChild(), aCollection); + Collect(aApzc->GetPrevSibling(), aCollection); + } +} + +void +APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor, Layer* aRoot, + bool aIsFirstPaint, uint64_t aFirstPaintLayersId) +{ + AssertOnCompositorThread(); + + MonitorAutoLock lock(mTreeLock); + + // We do this business with collecting the entire tree into an array because otherwise + // it's very hard to determine which APZC instances need to be destroyed. In the worst + // case, there are two scenarios: (a) a layer with an APZC is removed from the layer + // tree and (b) a layer with an APZC is moved in the layer tree from one place to a + // completely different place. In scenario (a) we would want to destroy the APZC while + // walking the layer tree and noticing that the layer/APZC is no longer there. But if + // we do that then we run into a problem in scenario (b) because we might encounter that + // layer later during the walk. To handle both of these we have to 'remember' that the + // layer was not found, and then do the destroy only at the end of the tree walk after + // we are sure that the layer was removed and not just transplanted elsewhere. Doing that + // as part of a recursive tree walk is hard and so maintaining a list and removing + // APZCs that are still alive is much simpler. + nsTArray< nsRefPtr > apzcsToDestroy; + Collect(mRootApzc, &apzcsToDestroy); + mRootApzc = nullptr; + + if (aRoot) { + UpdatePanZoomControllerTree(aCompositor, + aRoot, + // aCompositor is null in gtest scenarios + aCompositor ? aCompositor->RootLayerTreeId() : 0, + nullptr, nullptr, + aIsFirstPaint, aFirstPaintLayersId, + &apzcsToDestroy); + } + + for (int i = apzcsToDestroy.Length() - 1; i >= 0; i--) { + APZC_LOG("Destroying APZC at %p\n", apzcsToDestroy[i].get()); + apzcsToDestroy[i]->Destroy(); + } +} + +AsyncPanZoomController* +APZCTreeManager::UpdatePanZoomControllerTree(CompositorParent* aCompositor, + Layer* aLayer, uint64_t aLayersId, + AsyncPanZoomController* aParent, + AsyncPanZoomController* aNextSibling, + bool aIsFirstPaint, uint64_t aFirstPaintLayersId, + nsTArray< nsRefPtr >* aApzcsToDestroy) +{ + ContainerLayer* container = aLayer->AsContainerLayer(); + AsyncPanZoomController* controller = nullptr; + if (container) { + if (container->GetFrameMetrics().IsScrollable()) { + const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId); + if (state && state->mController.get()) { + // If we get here, aLayer is a scrollable container layer and somebody + // has registered a GeckoContentController for it, so we need to ensure + // it has an APZC instance to manage its scrolling. + + controller = container->GetAsyncPanZoomController(); + if (!controller) { + controller = new AsyncPanZoomController(aLayersId, state->mController, + AsyncPanZoomController::USE_GESTURE_DETECTOR); + controller->SetCompositorParent(aCompositor); + } else { + // If there was already an APZC for the layer clear the tree pointers + // so that it doesn't continue pointing to APZCs that should no longer + // be in the tree. These pointers will get reset properly as we continue + // building the tree. Also remove it from the set of APZCs that are going + // to be destroyed, because it's going to remain active. + aApzcsToDestroy->RemoveElement(controller); + controller->SetPrevSibling(nullptr); + controller->SetLastChild(nullptr); + } + APZC_LOG("Using APZC %p for layer %p with identifiers %lld %lld\n", controller, aLayer, aLayersId, container->GetFrameMetrics().mScrollId); + + controller->NotifyLayersUpdated(container->GetFrameMetrics(), + aIsFirstPaint && (aLayersId == aFirstPaintLayersId)); + + gfx3DMatrix transform = container->GetEffectiveTransform(); + LayerRect visible = container->GetFrameMetrics().mViewport * container->GetFrameMetrics().LayersPixelsPerCSSPixel(); + gfxRect transformed = transform.TransformBounds(gfxRect(visible.x, visible.y, visible.width, visible.height)); + controller->SetVisibleRegion(transformed); + APZC_LOG("Setting rect(%f %f %f %f) as visible region for %p\n", transformed.x, transformed.y, + transformed.width, transformed.height, + controller); + + // Bind the APZC instance into the tree of APZCs + if (aNextSibling) { + aNextSibling->SetPrevSibling(controller); + } else if (aParent) { + aParent->SetLastChild(controller); + } else { + mRootApzc = controller; + } + + // Let this controller be the parent of other controllers when we recurse downwards + aParent = controller; + } + } + + container->SetAsyncPanZoomController(controller); + } + + uint64_t childLayersId = (aLayer->AsRefLayer() ? aLayer->AsRefLayer()->GetReferentId() : aLayersId); + AsyncPanZoomController* next = nullptr; + for (Layer* child = aLayer->GetLastChild(); child; child = child->GetPrevSibling()) { + next = UpdatePanZoomControllerTree(aCompositor, child, childLayersId, aParent, next, + aIsFirstPaint, aFirstPaintLayersId, aApzcsToDestroy); + } + + // Return the APZC that should be the sibling of other APZCs as we continue + // moving towards the first child at this depth in the layer tree. + // If this layer doesn't have a controller, we promote any APZCs in the subtree + // upwards. Otherwise we fall back to the aNextSibling that was passed in. + if (controller) { + return controller; + } + if (next) { + return next; + } + return aNextSibling; +} + +nsEventStatus +APZCTreeManager::ReceiveInputEvent(const InputData& aEvent) +{ + nsRefPtr apzc; + switch (aEvent.mInputType) { + case MULTITOUCH_INPUT: { + const MultiTouchInput& multiTouchInput = aEvent.AsMultiTouchInput(); + apzc = GetTargetAPZC(ScreenPoint(multiTouchInput.mTouches[0].mScreenPoint)); + break; + } case PINCHGESTURE_INPUT: { + const PinchGestureInput& pinchInput = aEvent.AsPinchGestureInput(); + apzc = GetTargetAPZC(pinchInput.mFocusPoint); + break; + } case TAPGESTURE_INPUT: { + const TapGestureInput& tapInput = aEvent.AsTapGestureInput(); + apzc = GetTargetAPZC(ScreenPoint(tapInput.mPoint)); + break; + } default: { + // leave apzc as nullptr + break; + } + } + if (apzc) { + return apzc->ReceiveInputEvent(aEvent); + } + return nsEventStatus_eIgnore; +} + +nsEventStatus +APZCTreeManager::ReceiveInputEvent(const nsInputEvent& aEvent, + nsInputEvent* aOutEvent) +{ + MOZ_ASSERT(NS_IsMainThread()); + + nsRefPtr apzc; + switch (aEvent.eventStructType) { + case NS_TOUCH_EVENT: { + const nsTouchEvent& touchEvent = static_cast(aEvent); + if (touchEvent.touches.Length() > 0) { + nsIntPoint point = touchEvent.touches[0]->mRefPoint; + apzc = GetTargetAPZC(ScreenPoint::FromUnknownPoint(gfx::Point(point.x, point.y))); + } + break; + } case NS_MOUSE_EVENT: { + const nsMouseEvent& mouseEvent = static_cast(aEvent); + apzc = GetTargetAPZC(ScreenPoint::FromUnknownPoint(gfx::Point(mouseEvent.refPoint.x, + mouseEvent.refPoint.y))); + break; + } default: { + // leave apzc as nullptr + break; + } + } + if (apzc) { + return apzc->ReceiveInputEvent(aEvent, aOutEvent); + } + return nsEventStatus_eIgnore; +} + +void +APZCTreeManager::UpdateCompositionBounds(const ScrollableLayerGuid& aGuid, + const ScreenIntRect& aCompositionBounds) +{ + nsRefPtr apzc = GetTargetAPZC(aGuid); + if (apzc) { + apzc->UpdateCompositionBounds(aCompositionBounds); + } +} + +void +APZCTreeManager::CancelDefaultPanZoom(const ScrollableLayerGuid& aGuid) +{ + nsRefPtr apzc = GetTargetAPZC(aGuid); + if (apzc) { + apzc->CancelDefaultPanZoom(); + } +} + +void +APZCTreeManager::DetectScrollableSubframe(const ScrollableLayerGuid& aGuid) +{ + nsRefPtr apzc = GetTargetAPZC(aGuid); + if (apzc) { + apzc->DetectScrollableSubframe(); + } +} + +void +APZCTreeManager::ZoomToRect(const ScrollableLayerGuid& aGuid, + const CSSRect& aRect) +{ + nsRefPtr apzc = GetTargetAPZC(aGuid); + if (apzc) { + apzc->ZoomToRect(aRect); + } +} + +void +APZCTreeManager::ContentReceivedTouch(const ScrollableLayerGuid& aGuid, + bool aPreventDefault) +{ + nsRefPtr apzc = GetTargetAPZC(aGuid); + if (apzc) { + apzc->ContentReceivedTouch(aPreventDefault); + } +} + +void +APZCTreeManager::UpdateZoomConstraints(const ScrollableLayerGuid& aGuid, + bool aAllowZoom, + float aMinScale, + float aMaxScale) +{ + nsRefPtr apzc = GetTargetAPZC(aGuid); + if (apzc) { + apzc->UpdateZoomConstraints(aAllowZoom, aMinScale, aMaxScale); + } +} + +void +APZCTreeManager::UpdateScrollOffset(const ScrollableLayerGuid& aGuid, + const CSSPoint& aScrollOffset) +{ + nsRefPtr apzc = GetTargetAPZC(aGuid); + if (apzc) { + apzc->UpdateScrollOffset(aScrollOffset); + } +} + +void +APZCTreeManager::CancelAnimation(const ScrollableLayerGuid &aGuid) +{ + nsRefPtr apzc = GetTargetAPZC(aGuid); + if (apzc) { + apzc->CancelAnimation(); + } +} + +void +APZCTreeManager::ClearTree() +{ + MonitorAutoLock lock(mTreeLock); + + // This can be done as part of a tree walk but it's easier to + // just re-use the Collect method that we need in other places. + // If this is too slow feel free to change it to a recursive walk. + nsTArray< nsRefPtr > apzcsToDestroy; + Collect(mRootApzc, &apzcsToDestroy); + for (int i = apzcsToDestroy.Length() - 1; i >= 0; i--) { + apzcsToDestroy[i]->Destroy(); + } + mRootApzc = nullptr; +} + +already_AddRefed +APZCTreeManager::GetTargetAPZC(const ScrollableLayerGuid& aGuid) +{ + MonitorAutoLock lock(mTreeLock); + nsRefPtr target; + // The root may have siblings, check those too + for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) { + target = FindTargetAPZC(apzc, aGuid); + if (target) { + break; + } + } + return target.forget(); +} + +already_AddRefed +APZCTreeManager::GetTargetAPZC(const ScreenPoint& aPoint) +{ + MonitorAutoLock lock(mTreeLock); + nsRefPtr target; + // The root may have siblings, so check those too + gfxPoint point(aPoint.x, aPoint.y); + for (AsyncPanZoomController* apzc = mRootApzc; apzc; apzc = apzc->GetPrevSibling()) { + target = GetAPZCAtPoint(apzc, point); + if (target) { + break; + } + } + return target.forget(); +} + +AsyncPanZoomController* +APZCTreeManager::FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid) { + // This walks the tree in depth-first, reverse order, so that it encounters + // APZCs front-to-back on the screen. + for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) { + AsyncPanZoomController* match = FindTargetAPZC(child, aGuid); + if (match) { + return match; + } + } + + if (aApzc->Matches(aGuid)) { + return aApzc; + } + return nullptr; +} + +AsyncPanZoomController* +APZCTreeManager::GetAPZCAtPoint(AsyncPanZoomController* aApzc, gfxPoint aHitTestPoint) +{ + // This walks the tree in depth-first, reverse order, so that it encounters + // APZCs front-to-back on the screen. + ViewTransform apzcTransform = aApzc->GetCurrentAsyncTransform(); + gfxPoint untransformed = gfx3DMatrix(apzcTransform).Inverse().ProjectPoint(aHitTestPoint); + for (AsyncPanZoomController* child = aApzc->GetLastChild(); child; child = child->GetPrevSibling()) { + AsyncPanZoomController* match = GetAPZCAtPoint(child, untransformed); + if (match) { + return match; + } + } + if (aApzc->VisibleRegionContains(untransformed)) { + return aApzc; + } + return nullptr; +} + +} +} diff --git a/gfx/layers/composite/APZCTreeManager.h b/gfx/layers/composite/APZCTreeManager.h new file mode 100644 index 000000000000..de8c55b4221d --- /dev/null +++ b/gfx/layers/composite/APZCTreeManager.h @@ -0,0 +1,275 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* 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/. */ + +#ifndef mozilla_layers_APZCTreeManager_h +#define mozilla_layers_APZCTreeManager_h + +#include "mozilla/layers/AsyncPanZoomController.h" +#include "Layers.h" +#include "CompositorParent.h" + +namespace mozilla { +namespace layers { + +class AsyncPanZoomController; +class CompositorParent; + +/** + * This class allows us to uniquely identify a scrollable layer. The + * mLayersId identifies the layer tree (corresponding to a child process + * and/or tab) that the scrollable layer belongs to. The mPresShellId + * is a temporal identifier (corresponding to the document loaded that + * contains the scrollable layer, which may change over time). The + * mScrollId corresponds to the actual frame that is scrollable. + */ +struct ScrollableLayerGuid { + uint64_t mLayersId; + uint32_t mPresShellId; + FrameMetrics::ViewID mScrollId; + + ScrollableLayerGuid(uint64_t aLayersId, uint32_t aPresShellId, + FrameMetrics::ViewID aScrollId) + : mLayersId(aLayersId) + , mPresShellId(aPresShellId) + , mScrollId(aScrollId) + { + MOZ_COUNT_CTOR(ScrollableLayerGuid); + } + + ScrollableLayerGuid(uint64_t aLayersId, const FrameMetrics& aMetrics) + : mLayersId(aLayersId) + , mPresShellId(aMetrics.mPresShellId) + , mScrollId(aMetrics.mScrollId) + { + MOZ_COUNT_CTOR(ScrollableLayerGuid); + } + + ScrollableLayerGuid(uint64_t aLayersId) + : mLayersId(aLayersId) + , mPresShellId(0) + , mScrollId(FrameMetrics::ROOT_SCROLL_ID) + { + MOZ_COUNT_CTOR(ScrollableLayerGuid); + // TODO: get rid of this constructor once all callers know their + // presShellId and scrollId + } + + ~ScrollableLayerGuid() + { + MOZ_COUNT_DTOR(ScrollableLayerGuid); + } + + bool operator==(const ScrollableLayerGuid& other) const + { + return mLayersId == other.mLayersId + && mPresShellId == other.mPresShellId + && mScrollId == other.mScrollId; + } + + bool operator!=(const ScrollableLayerGuid& other) const + { + return !(*this == other); + } +}; + +/** + * This class manages the tree of AsyncPanZoomController instances. There is one + * instance of this class owned by each CompositorParent, and it contains as + * many AsyncPanZoomController instances as there are scrollable container layers. + * This class generally lives on the compositor thread, although some functions + * may be called from other threads as noted; thread safety is ensured internally. + * + * The bulk of the work of this class happens as part of the UpdatePanZoomControllerTree + * function, which is when a layer tree update is received by the compositor. + * This function walks through the layer tree and creates a tree of APZC instances + * to match the scrollable container layers. APZC instances may be preserved across + * calls to this function if the corresponding layers are still present in the layer + * tree. + * + * The other functions on this class are used by various pieces of client code to + * notify the APZC instances of events relevant to them. This includes, for example, + * user input events that drive panning and zooming, changes to the scroll viewport + * area, and changes to pan/zoom constraints. + * + * Note that the ClearTree function MUST be called when this class is no longer needed; + * see the method documentation for details. + */ +class APZCTreeManager { + NS_INLINE_DECL_THREADSAFE_REFCOUNTING(APZCTreeManager) + +public: + APZCTreeManager(); + virtual ~APZCTreeManager() {} + + /** + * Rebuild the APZC tree based on the layer update that just came up. Preserve + * APZC instances where possible, but retire those whose layers are no longer + * in the layer tree. + * + * This must be called on the compositor thread as it walks the layer tree. + * + * @param aCompositor A pointer to the compositor parent instance that owns + * this APZCTreeManager + * @param aRoot The root of the (full) layer tree + * @param aFirstPaintLayersId The layers id of the subtree to which aIsFirstPaint + * applies. + * @param aIsFirstPaint True if the layers update that this is called in response + * to included a first-paint. If this is true, the part of + * the tree that is affected by the first-paint flag is + * indicated by the aFirstPaintLayersId parameter. + */ + void UpdatePanZoomControllerTree(CompositorParent* aCompositor, Layer* aRoot, + bool aIsFirstPaint, uint64_t aFirstPaintLayersId); + + /** + * General handler for incoming input events. Manipulates the frame metrics + * based on what type of input it is. For example, a PinchGestureEvent will + * cause scaling. This should only be called externally to this class. + * HandleInputEvent() should be used internally. + */ + nsEventStatus ReceiveInputEvent(const InputData& aEvent); + + /** + * Special handler for nsInputEvents. Also sets |aOutEvent| (which is assumed + * to be an already-existing instance of an nsInputEvent which may be an + * nsTouchEvent) to have its touch points in DOM space. This is so that the + * touches can be passed through the DOM and content can handle them. + * + * NOTE: Be careful of invoking the nsInputEvent variant. This can only be + * called on the main thread. See widget/InputData.h for more information on + * why we have InputData and nsInputEvent separated. + */ + nsEventStatus ReceiveInputEvent(const nsInputEvent& aEvent, + nsInputEvent* aOutEvent); + + /** + * Updates the composition bounds, i.e. the dimensions of the final size of + * the frame this is tied to during composition onto, in device pixels. In + * general, this will just be: + * { x = 0, y = 0, width = surface.width, height = surface.height }, however + * there is no hard requirement for this. + */ + void UpdateCompositionBounds(const ScrollableLayerGuid& aGuid, + const ScreenIntRect& aCompositionBounds); + + /** + * We are scrolling a subframe, so disable our machinery until we hit + * a touch end or a new touch start. This prevents us from accidentally + * panning both the subframe and the parent frame. + * + * XXX/bug 775452: We should eventually be supporting async scrollable + * subframes. + */ + void CancelDefaultPanZoom(const ScrollableLayerGuid& aGuid); + + /** + * We have found a scrollable subframe, so we need to delay the scrolling + * gesture executed and let subframe do the scrolling first. + */ + void DetectScrollableSubframe(const ScrollableLayerGuid& aGuid); + + /** + * Kicks an animation to zoom to a rect. This may be either a zoom out or zoom + * in. The actual animation is done on the compositor thread after being set + * up. |aRect| must be given in CSS pixels, relative to the document. + */ + void ZoomToRect(const ScrollableLayerGuid& aGuid, + const CSSRect& aRect); + + /** + * If we have touch listeners, this should always be called when we know + * definitively whether or not content has preventDefaulted any touch events + * that have come in. If |aPreventDefault| is true, any touch events in the + * queue will be discarded. + */ + void ContentReceivedTouch(const ScrollableLayerGuid& aGuid, + bool aPreventDefault); + + /** + * Updates any zoom constraints contained in the tag. + * We try to obey everything it asks us elsewhere, but here we only handle + * minimum-scale, maximum-scale, and user-scalable. + */ + void UpdateZoomConstraints(const ScrollableLayerGuid& aGuid, + bool aAllowZoom, + float aMinScale, + float aMaxScale); + + /** + * Update mFrameMetrics.mScrollOffset to the given offset. + * This is necessary in cases where a scroll is not caused by user + * input (for example, a content scrollTo()). + */ + void UpdateScrollOffset(const ScrollableLayerGuid& aGuid, + const CSSPoint& aScrollOffset); + + /** + * Cancels any currently running animation. Note that all this does is set the + * state of the AsyncPanZoomController back to NOTHING, but it is the + * animation's responsibility to check this before advancing. + */ + void CancelAnimation(const ScrollableLayerGuid &aGuid); + + /** + * Calls Destroy() on all APZC instances attached to the tree, and resets the + * tree back to empty. This function may be called multiple times during the + * lifetime of this APZCTreeManager, but it must always be called at least once + * when this APZCTreeManager is no longer needed. Failing to call this function + * may prevent objects from being freed properly. + */ + void ClearTree(); + +protected: + /** + * Debug-build assertion that can be called to ensure code is running on the + * compositor thread. + */ + virtual void AssertOnCompositorThread(); + +public: + /* Some helper functions to find an APZC given some identifying input. These functions + lock the tree of APZCs while they find the right one, and then return an addref'd + pointer to it. This allows caller code to just use the target APZC without worrying + about it going away. These are public for testing code and generally should not be + used by other production code. + */ + already_AddRefed GetTargetAPZC(const ScrollableLayerGuid& aGuid); + already_AddRefed GetTargetAPZC(const ScreenPoint& aPoint); +private: + /* Recursive helpers */ + AsyncPanZoomController* FindTargetAPZC(AsyncPanZoomController* aApzc, const ScrollableLayerGuid& aGuid); + AsyncPanZoomController* GetAPZCAtPoint(AsyncPanZoomController* aApzc, gfxPoint aHitTestPoint); + + /** + * Recursive helper function to build the APZC tree. The tree of APZC instances has + * the same shape as the layer tree, but excludes all the layers that are not scrollable. + * Note that this means APZCs corresponding to layers at different depths in the tree + * may end up becoming siblings. It also means that the "root" APZC may have siblings. + * This function walks the layer tree backwards through siblings and constructs the APZC + * tree also as a last-child-prev-sibling tree because that simplifies the hit detection + * code. + */ + AsyncPanZoomController* UpdatePanZoomControllerTree(CompositorParent* aCompositor, + Layer* aLayer, uint64_t aLayersId, + AsyncPanZoomController* aParent, + AsyncPanZoomController* aNextSibling, + bool aIsFirstPaint, + uint64_t aFirstPaintLayersId, + nsTArray< nsRefPtr >* aApzcsToDestroy); + +private: + /* Whenever walking or mutating the tree rooted at mRootApzc, mTreeLock must be held. + * This lock does not need to be held while manipulating a single APZC instance in + * isolation (that is, if its tree pointers are not being accessed or mutated). The + * lock also needs to be held when accessing the mRootApzc instance variable, as that + * is considered part of the APZC tree management state. */ + mozilla::Monitor mTreeLock; + nsRefPtr mRootApzc; +}; + +} +} + +#endif // mozilla_layers_PanZoomController_h diff --git a/gfx/layers/composite/AsyncCompositionManager.cpp b/gfx/layers/composite/AsyncCompositionManager.cpp index b136e659759f..09bcddaf56dc 100644 --- a/gfx/layers/composite/AsyncCompositionManager.cpp +++ b/gfx/layers/composite/AsyncCompositionManager.cpp @@ -51,7 +51,6 @@ WalkTheTree(Layer* aLayer, if (RefLayer* ref = aLayer->AsRefLayer()) { if (const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(ref->GetReferentId())) { if (Layer* referent = state->mRoot) { - ContainerLayer *referentAsContainer = referent->AsContainerLayer(); if (!ref->GetVisibleRegion().IsEmpty()) { ScreenOrientation chromeOrientation = aTargetConfig.orientation(); ScreenOrientation contentOrientation = state->mTargetConfig.orientation(); @@ -63,16 +62,8 @@ WalkTheTree(Layer* aLayer, if (OP == Resolve) { ref->ConnectReferentLayer(referent); - if (referentAsContainer) { - if (AsyncPanZoomController* apzc = state->mController) { - referentAsContainer->SetAsyncPanZoomController(apzc); - } - } } else { ref->DetachReferentLayer(referent); - if (referentAsContainer) { - referentAsContainer->SetAsyncPanZoomController(nullptr); - } } } } @@ -437,7 +428,6 @@ AsyncCompositionManager::ApplyAsyncContentTransformToTree(TimeStamp aCurrentFram ScreenPoint scrollOffset; *aWantNextFrame |= controller->SampleContentTransformForFrame(aCurrentFrame, - container, &treeTransform, scrollOffset); diff --git a/gfx/layers/ipc/AsyncPanZoomController.cpp b/gfx/layers/ipc/AsyncPanZoomController.cpp index 50776b667cf9..80037626ab68 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.cpp +++ b/gfx/layers/ipc/AsyncPanZoomController.cpp @@ -106,6 +106,18 @@ static float gYSkateSizeMultiplier = 3.5f; static float gXStationarySizeMultiplier = 1.5f; static float gYStationarySizeMultiplier = 2.5f; +/** + * The time period in ms that throttles mozbrowserasyncscroll event. + * Default is 100ms if there is no "apzc.asyncscroll.throttle" in preference. + */ + +static int gAsyncScrollThrottleTime = 100; +/** + * The timeout in ms for mAsyncScrollTimeoutTask delay task. + * Default is 300ms if there is no "apzc.asyncscroll.timeout" in preference. + */ +static int gAsyncScrollTimeout = 300; + static TimeStamp sFrameTime; static TimeStamp @@ -121,8 +133,16 @@ AsyncPanZoomController::SetFrameTime(const TimeStamp& aTime) { sFrameTime = aTime; } -static void ReadAZPCPrefs() +/*static*/ void +AsyncPanZoomController::InitializeGlobalState() { + MOZ_ASSERT(NS_IsMainThread()); + + static bool sInitialized = false; + if (sInitialized) + return; + sInitialized = true; + Preferences::AddIntVarCache(&gPanRepaintInterval, "gfx.azpc.pan_repaint_interval", gPanRepaintInterval); Preferences::AddIntVarCache(&gFlingRepaintInterval, "gfx.azpc.fling_repaint_interval", gFlingRepaintInterval); Preferences::AddFloatVarCache(&gMinSkateSpeed, "gfx.azpc.min_skate_speed", gMinSkateSpeed); @@ -133,36 +153,22 @@ static void ReadAZPCPrefs() Preferences::AddFloatVarCache(&gYSkateSizeMultiplier, "gfx.azpc.y_skate_size_multiplier", gYSkateSizeMultiplier); Preferences::AddFloatVarCache(&gXStationarySizeMultiplier, "gfx.azpc.x_stationary_size_multiplier", gXStationarySizeMultiplier); Preferences::AddFloatVarCache(&gYStationarySizeMultiplier, "gfx.azpc.y_stationary_size_multiplier", gYStationarySizeMultiplier); + Preferences::AddIntVarCache(&gAsyncScrollThrottleTime, "apzc.asyncscroll.throttle", gAsyncScrollThrottleTime); + Preferences::AddIntVarCache(&gAsyncScrollTimeout, "apzc.asyncscroll.timeout", gAsyncScrollTimeout); + + gComputedTimingFunction = new ComputedTimingFunction(); + gComputedTimingFunction->Init( + nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE)); + ClearOnShutdown(&gComputedTimingFunction); } -class ReadAZPCPref MOZ_FINAL : public nsRunnable { -public: - NS_IMETHOD Run() - { - ReadAZPCPrefs(); - return NS_OK; - } -}; - -static void InitAZPCPrefs() -{ - static bool sInitialized = false; - if (sInitialized) - return; - - sInitialized = true; - if (NS_IsMainThread()) { - ReadAZPCPrefs(); - } else { - // We have to dispatch an event to the main thread to read the pref. - NS_DispatchToMainThread(new ReadAZPCPref()); - } -} - -AsyncPanZoomController::AsyncPanZoomController(GeckoContentController* aGeckoContentController, +AsyncPanZoomController::AsyncPanZoomController(uint64_t aLayersId, + GeckoContentController* aGeckoContentController, GestureBehavior aGestures) - : mPaintThrottler(GetFrameTime()), + : mLayersId(aLayersId), + mPaintThrottler(GetFrameTime()), mGeckoContentController(aGeckoContentController), + mRefPtrMonitor("RefPtrMonitor"), mTouchListenerTimeoutTask(nullptr), mX(this), mY(this), @@ -176,45 +182,48 @@ AsyncPanZoomController::AsyncPanZoomController(GeckoContentController* aGeckoCon mLastAsyncScrollOffset(0, 0), mCurrentAsyncScrollOffset(0, 0), mAsyncScrollTimeoutTask(nullptr), - mAsyncScrollThrottleTime(100), - mAsyncScrollTimeout(300), mDPI(72), mDisableNextTouchBatch(false), mHandlingTouchQueue(false), mDelayPanning(false) { - MOZ_ASSERT(NS_IsMainThread()); MOZ_COUNT_CTOR(AsyncPanZoomController); - InitAZPCPrefs(); - if (aGestures == USE_GESTURE_DETECTOR) { mGestureEventListener = new GestureEventListener(this); } SetDPI(mDPI); - - if (!gComputedTimingFunction) { - gComputedTimingFunction = new ComputedTimingFunction(); - gComputedTimingFunction->Init( - nsTimingFunction(NS_STYLE_TRANSITION_TIMING_FUNCTION_EASE)); - ClearOnShutdown(&gComputedTimingFunction); - } - - Preferences::GetUint("apzc.asyncscroll.throttle", &mAsyncScrollThrottleTime); - Preferences::GetUint("apzc.asyncscroll.timeout", &mAsyncScrollTimeout); } AsyncPanZoomController::~AsyncPanZoomController() { MOZ_COUNT_DTOR(AsyncPanZoomController); } +already_AddRefed +AsyncPanZoomController::GetGeckoContentController() { + MonitorAutoLock lock(mRefPtrMonitor); + nsRefPtr controller = mGeckoContentController; + return controller.forget(); +} + +already_AddRefed +AsyncPanZoomController::GetGestureEventListener() { + MonitorAutoLock lock(mRefPtrMonitor); + nsRefPtr listener = mGestureEventListener; + return listener.forget(); +} + void AsyncPanZoomController::Destroy() { - // These memebrs can only be used on the controller/UI thread. - mGeckoContentController = nullptr; - mGestureEventListener = nullptr; + { // scope the lock + MonitorAutoLock lock(mRefPtrMonitor); + mGeckoContentController = nullptr; + mGestureEventListener = nullptr; + } + mPrevSibling = nullptr; + mLastChild = nullptr; } /* static */float @@ -332,8 +341,9 @@ nsEventStatus AsyncPanZoomController::ReceiveInputEvent(const InputData& aEvent) nsEventStatus AsyncPanZoomController::HandleInputEvent(const InputData& aEvent) { nsEventStatus rv = nsEventStatus_eIgnore; - if (mGestureEventListener && !mDisableNextTouchBatch) { - rv = mGestureEventListener->HandleInputEvent(aEvent); + nsRefPtr listener = GetGestureEventListener(); + if (listener && !mDisableNextTouchBatch) { + rv = listener->HandleInputEvent(aEvent); if (rv == nsEventStatus_eConsumeNoDefault) return rv; } @@ -659,12 +669,13 @@ nsEventStatus AsyncPanZoomController::OnScaleEnd(const PinchGestureInput& aEvent } nsEventStatus AsyncPanZoomController::OnLongPress(const TapGestureInput& aEvent) { - if (mGeckoContentController) { + nsRefPtr controller = GetGeckoContentController(); + if (controller) { MonitorAutoLock monitor(mMonitor); CSSToScreenScale resolution = mFrameMetrics.CalculateResolution(); CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, resolution); - mGeckoContentController->HandleLongTap(gfx::RoundedToInt(point)); + controller->HandleLongTap(gfx::RoundedToInt(point)); return nsEventStatus_eConsumeNoDefault; } return nsEventStatus_eIgnore; @@ -675,25 +686,27 @@ nsEventStatus AsyncPanZoomController::OnSingleTapUp(const TapGestureInput& aEven } nsEventStatus AsyncPanZoomController::OnSingleTapConfirmed(const TapGestureInput& aEvent) { - if (mGeckoContentController) { + nsRefPtr controller = GetGeckoContentController(); + if (controller) { MonitorAutoLock monitor(mMonitor); CSSToScreenScale resolution = mFrameMetrics.CalculateResolution(); CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, resolution); - mGeckoContentController->HandleSingleTap(gfx::RoundedToInt(point)); + controller->HandleSingleTap(gfx::RoundedToInt(point)); return nsEventStatus_eConsumeNoDefault; } return nsEventStatus_eIgnore; } nsEventStatus AsyncPanZoomController::OnDoubleTap(const TapGestureInput& aEvent) { - if (mGeckoContentController) { + nsRefPtr controller = GetGeckoContentController(); + if (controller) { MonitorAutoLock monitor(mMonitor); if (mAllowZoom) { CSSToScreenScale resolution = mFrameMetrics.CalculateResolution(); CSSPoint point = WidgetSpaceToCompensatedViewportSpace(aEvent.mPoint, resolution); - mGeckoContentController->HandleDoubleTap(gfx::RoundedToInt(point)); + controller->HandleDoubleTap(gfx::RoundedToInt(point)); } return nsEventStatus_eConsumeNoDefault; @@ -1019,12 +1032,15 @@ void AsyncPanZoomController::RequestContentRepaint() { // This message is compressed, so fire whether or not we already have a paint // queued up. We need to know whether or not a paint was requested anyways, // for the purposes of content calling window.scrollTo(). - mPaintThrottler.PostTask( - FROM_HERE, - NewRunnableMethod(mGeckoContentController.get(), - &GeckoContentController::RequestContentRepaint, - mFrameMetrics), - GetFrameTime()); + nsRefPtr controller = GetGeckoContentController(); + if (controller) { + mPaintThrottler.PostTask( + FROM_HERE, + NewRunnableMethod(controller.get(), + &GeckoContentController::RequestContentRepaint, + mFrameMetrics), + GetFrameTime()); + } mFrameMetrics.mPresShellId = mLastContentPaintMetrics.mPresShellId; mLastPaintRequestMetrics = mFrameMetrics; @@ -1043,7 +1059,6 @@ AsyncPanZoomController::FireAsyncScrollOnTimeout() } bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSampleTime, - ContainerLayer* aLayer, ViewTransform* aNewTransform, ScreenPoint& aScrollOffset) { // The eventual return value of this function. The compositor needs to know @@ -1053,10 +1068,6 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa // responsibility to schedule a composite. bool requestAnimationFrame = false; - LayerPoint metricsScrollOffset; - CSSPoint scrollOffset; - CSSToScreenScale localScale; - const FrameMetrics& frame = aLayer->GetFrameMetrics(); { MonitorAutoLock mon(mMonitor); @@ -1103,17 +1114,9 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa break; } - // Current local transform; this is not what's painted but rather - // what PZC has transformed due to touches like panning or - // pinching. Eventually, the root layer transform will become this - // during runtime, but we must wait for Gecko to repaint. - localScale = mFrameMetrics.CalculateResolution(); + aScrollOffset = mFrameMetrics.mScrollOffset * mFrameMetrics.CalculateResolution(); + *aNewTransform = GetCurrentAsyncTransformInternal(); - if (frame.IsScrollable()) { - metricsScrollOffset = frame.GetScrollOffsetInLayerPixels(); - } - - scrollOffset = mFrameMetrics.mScrollOffset; mCurrentAsyncScrollOffset = mFrameMetrics.mScrollOffset; } @@ -1129,7 +1132,7 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa // with the last event. // Otherwise, start a timer to fire the event sAsyncScrollTimeout ms from now. TimeDuration delta = aSampleTime - mLastAsyncScrollTime; - if (delta.ToMilliseconds() > mAsyncScrollThrottleTime && + if (delta.ToMilliseconds() > gAsyncScrollThrottleTime && mCurrentAsyncScrollOffset != mLastAsyncScrollOffset) { MonitorAutoLock monitor(mMonitor); mLastAsyncScrollTime = aSampleTime; @@ -1141,25 +1144,35 @@ bool AsyncPanZoomController::SampleContentTransformForFrame(const TimeStamp& aSa NewRunnableMethod(this, &AsyncPanZoomController::FireAsyncScrollOnTimeout); MessageLoop::current()->PostDelayedTask(FROM_HERE, mAsyncScrollTimeoutTask, - mAsyncScrollTimeout); + gAsyncScrollTimeout); } - CSSToLayerScale paintedScale = frame.mDevPixelsPerCSSPixel * frame.mResolution; - LayerPoint translation = (scrollOffset * paintedScale) - metricsScrollOffset; - *aNewTransform = ViewTransform(-translation, localScale / frame.mDevPixelsPerCSSPixel); - aScrollOffset = scrollOffset * localScale; - mLastSampleTime = aSampleTime; return requestAnimationFrame; } -void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFrame, bool aIsFirstPaint) { +ViewTransform AsyncPanZoomController::GetCurrentAsyncTransform() { + MonitorAutoLock mon(mMonitor); + return GetCurrentAsyncTransformInternal(); +} + +ViewTransform AsyncPanZoomController::GetCurrentAsyncTransformInternal() { + LayerPoint metricsScrollOffset; + if (mLastContentPaintMetrics.IsScrollable()) { + metricsScrollOffset = mLastContentPaintMetrics.GetScrollOffsetInLayerPixels(); + } + CSSToScreenScale localScale = mFrameMetrics.CalculateResolution(); + LayerPoint translation = mFrameMetrics.GetScrollOffsetInLayerPixels() - metricsScrollOffset; + return ViewTransform(-translation, localScale / mLastContentPaintMetrics.mDevPixelsPerCSSPixel); +} + +void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint) { MonitorAutoLock monitor(mMonitor); - mLastContentPaintMetrics = aViewportFrame; + mLastContentPaintMetrics = aLayerMetrics; - mFrameMetrics.mMayHaveTouchListeners = aViewportFrame.mMayHaveTouchListeners; + mFrameMetrics.mMayHaveTouchListeners = aLayerMetrics.mMayHaveTouchListeners; // TODO: Once a mechanism for calling UpdateScrollOffset() when content does // a scrollTo() is implemented for B2G (bug 895905), this block can be removed. @@ -1176,7 +1189,7 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr case FLING: case TOUCHING: case WAITING_LISTENERS: - mFrameMetrics.mScrollOffset = aViewportFrame.mScrollOffset; + mFrameMetrics.mScrollOffset = aLayerMetrics.mScrollOffset; break; // Don't clobber if we're in other states. default: @@ -1187,12 +1200,12 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr mPaintThrottler.TaskComplete(GetFrameTime()); bool needContentRepaint = false; - if (aViewportFrame.mCompositionBounds.width == mFrameMetrics.mCompositionBounds.width && - aViewportFrame.mCompositionBounds.height == mFrameMetrics.mCompositionBounds.height) { + if (aLayerMetrics.mCompositionBounds.width == mFrameMetrics.mCompositionBounds.width && + aLayerMetrics.mCompositionBounds.height == mFrameMetrics.mCompositionBounds.height) { // Remote content has sync'd up to the composition geometry // change, so we can accept the viewport it's calculated. CSSToScreenScale previousResolution = mFrameMetrics.CalculateResolution(); - mFrameMetrics.mViewport = aViewportFrame.mViewport; + mFrameMetrics.mViewport = aLayerMetrics.mViewport; CSSToScreenScale newResolution = mFrameMetrics.CalculateResolution(); needContentRepaint |= (previousResolution != newResolution); } @@ -1204,11 +1217,15 @@ void AsyncPanZoomController::NotifyLayersUpdated(const FrameMetrics& aViewportFr mX.CancelTouch(); mY.CancelTouch(); - mFrameMetrics = aViewportFrame; + // XXX If this is the very first time we're getting a layers update we need to + // trigger another repaint, or the B2G browser shows stale content. This needs + // to be investigated and fixed. + needContentRepaint |= (mFrameMetrics.IsDefault() && !aLayerMetrics.IsDefault()); + mFrameMetrics = aLayerMetrics; mState = NOTHING; - } else if (!mFrameMetrics.mScrollableRect.IsEqualEdges(aViewportFrame.mScrollableRect)) { - mFrameMetrics.mScrollableRect = aViewportFrame.mScrollableRect; + } else if (!mFrameMetrics.mScrollableRect.IsEqualEdges(aLayerMetrics.mScrollableRect)) { + mFrameMetrics.mScrollableRect = aLayerMetrics.mScrollableRect; } if (needContentRepaint) { @@ -1242,8 +1259,9 @@ void AsyncPanZoomController::UpdateCompositionBounds(const ScreenIntRect& aCompo void AsyncPanZoomController::CancelDefaultPanZoom() { mDisableNextTouchBatch = true; - if (mGestureEventListener) { - mGestureEventListener->CancelGesture(); + nsRefPtr listener = GetGestureEventListener(); + if (listener) { + listener->CancelGesture(); } } @@ -1424,83 +1442,44 @@ void AsyncPanZoomController::UpdateZoomConstraints(bool aAllowZoom, } void AsyncPanZoomController::PostDelayedTask(Task* aTask, int aDelayMs) { - if (!mGeckoContentController) { - return; + nsRefPtr controller = GetGeckoContentController(); + if (controller) { + controller->PostDelayedTask(aTask, aDelayMs); } - - mGeckoContentController->PostDelayedTask(aTask, aDelayMs); } void AsyncPanZoomController::SendAsyncScrollEvent() { - if (!mGeckoContentController) { + nsRefPtr controller = GetGeckoContentController(); + if (!controller) { return; } + FrameMetrics::ViewID scrollId; CSSRect contentRect; CSSSize scrollableSize; { + // XXX bug 890932 - there should be a lock here. but it causes a deadlock. + scrollId = mFrameMetrics.mScrollId; scrollableSize = mFrameMetrics.mScrollableRect.Size(); contentRect = mFrameMetrics.CalculateCompositedRectInCssPixels(); contentRect.MoveTo(mCurrentAsyncScrollOffset); } - mGeckoContentController->SendAsyncScrollDOMEvent(contentRect, scrollableSize); + controller->SendAsyncScrollDOMEvent(scrollId, contentRect, scrollableSize); } -static void GetAPZCAtPointOnSubtree(const ContainerLayer& aLayerIn, - const gfxPoint& aPoint, - AsyncPanZoomController** aApzcOut, - LayerIntPoint* aRelativePointOut) -{ - // Making layers const correct is very slow because it requires - // a near clobber of the tree. Once const correct is further along - // remove this cast. - ContainerLayer& aLayer = const_cast(aLayerIn); - gfx3DMatrix transform = aLayer.GetLocalTransform().Inverse(); - gfxPoint layerPoint = transform.Transform(aPoint); - - // iterate over the children first. They are better match then the parent - Layer* currLayer = aLayer.GetLastChild(); - while (currLayer) { - if (currLayer->AsContainerLayer()) { - GetAPZCAtPointOnSubtree(*currLayer->AsContainerLayer(), layerPoint, aApzcOut, aRelativePointOut); - } - if (*aApzcOut) { - return; - } - currLayer = currLayer->GetPrevSibling(); - } - - if (aLayer.GetFrameMetrics().IsScrollable()) { - const FrameMetrics& frame = aLayer.GetFrameMetrics(); - LayerRect layerViewport = frame.mViewport * frame.LayersPixelsPerCSSPixel(); - bool intersect = layerViewport.Contains(layerPoint.x, layerPoint.y); - - if (intersect) { - *aApzcOut = aLayer.GetAsyncPanZoomController(); - *aRelativePointOut = LayerIntPoint(NS_lround(layerPoint.x), NS_lround(layerPoint.y)); - } - } - -} - -void AsyncPanZoomController::GetAPZCAtPoint(const ContainerLayer& aLayerTree, - const ScreenIntPoint& aPoint, - AsyncPanZoomController** aApzcOut, - LayerIntPoint* aRelativePointOut) -{ - *aApzcOut = nullptr; - - gfxPoint point(aPoint.x, aPoint.y); - - GetAPZCAtPointOnSubtree(aLayerTree, point, aApzcOut, aRelativePointOut); -} - -void AsyncPanZoomController::UpdateScrollOffset(CSSPoint aScrollOffset) +void AsyncPanZoomController::UpdateScrollOffset(const CSSPoint& aScrollOffset) { MonitorAutoLock monitor(mMonitor); mFrameMetrics.mScrollOffset = aScrollOffset; } +bool AsyncPanZoomController::Matches(const ScrollableLayerGuid& aGuid) +{ + // TODO: also check the presShellId and mScrollId, once those are + // fully propagated everywhere in RenderFrameParent and AndroidJNI. + return aGuid.mLayersId == mLayersId; +} + } } diff --git a/gfx/layers/ipc/AsyncPanZoomController.h b/gfx/layers/ipc/AsyncPanZoomController.h index be28064c7978..183e6643511f 100644 --- a/gfx/layers/ipc/AsyncPanZoomController.h +++ b/gfx/layers/ipc/AsyncPanZoomController.h @@ -14,12 +14,14 @@ #include "InputData.h" #include "Axis.h" #include "TaskThrottler.h" +#include "mozilla/layers/APZCTreeManager.h" #include "base/message_loop.h" namespace mozilla { namespace layers { +struct ScrollableLayerGuid; class CompositorParent; class GestureEventListener; class ContainerLayer; @@ -45,7 +47,7 @@ class ViewTransform; * asynchronously scrolled subframes, we want to have one AsyncPanZoomController * per frame. */ -class AsyncPanZoomController MOZ_FINAL { +class AsyncPanZoomController { NS_INLINE_DECL_THREADSAFE_REFCOUNTING(AsyncPanZoomController) typedef mozilla::MonitorAutoLock MonitorAutoLock; @@ -68,19 +70,25 @@ public: */ static float GetTouchStartTolerance(); - AsyncPanZoomController(GeckoContentController* aController, + AsyncPanZoomController(uint64_t aLayersId, + GeckoContentController* aController, GestureBehavior aGestures = DEFAULT_GESTURES); ~AsyncPanZoomController(); // -------------------------------------------------------------------------- - // These methods must only be called on the controller/UI thread. + // These methods must only be called on the gecko thread. // /** - * Shut down the controller/UI thread state and prepare to be - * deleted (which may happen from any thread). + * Read the various prefs and do any global initialization for all APZC instances. + * This must be run on the gecko thread before any APZC instances are actually + * used for anything meaningful. */ - void Destroy(); + static void InitializeGlobalState(); + + // -------------------------------------------------------------------------- + // These methods must only be called on the controller/UI thread. + // /** * General handler for incoming input events. Manipulates the frame metrics @@ -166,26 +174,25 @@ public: * idempotent. For example, a fling transform can be applied each time this is * called (though not necessarily). |aSampleTime| is the time that this is * sampled at; this is used for interpolating animations. Calling this sets a - * new transform in |aNewTransform| which should be applied directly to the - * shadow layer of the frame (do not multiply it in as the code already does - * this internally with |aLayer|'s transform). + * new transform in |aNewTransform| which should be multiplied to the transform + * in the shadow layer corresponding to this APZC. * * Return value indicates whether or not any currently running animation * should continue. That is, if true, the compositor should schedule another * composite. */ bool SampleContentTransformForFrame(const TimeStamp& aSampleTime, - ContainerLayer* aLayer, ViewTransform* aNewTransform, ScreenPoint& aScrollOffset); /** - * A shadow layer update has arrived. |aViewportFrame| is the new FrameMetrics - * for the top-level frame. |aIsFirstPaint| is a flag passed from the shadow + * A shadow layer update has arrived. |aLayerMetrics| is the new FrameMetrics + * for the container layer corresponding to this APZC. + * |aIsFirstPaint| is a flag passed from the shadow * layers code indicating that the frame metrics being sent with this call are * the initial metrics and the initial paint of the frame has just happened. */ - void NotifyLayersUpdated(const FrameMetrics& aViewportFrame, bool aIsFirstPaint); + void NotifyLayersUpdated(const FrameMetrics& aLayerMetrics, bool aIsFirstPaint); /** * The platform implementation must set the compositor parent so that we can @@ -197,6 +204,24 @@ public: // These methods can be called from any thread. // + /** + * Shut down the controller/UI thread state and prepare to be + * deleted (which may happen from any thread). + */ + void Destroy(); + + /** + * Returns the incremental transformation corresponding to the async pan/zoom + * in progress. That is, when this transform is multiplied with the layer's + * existing transform, it will make the layer appear with the desired pan/zoom + * amount. + */ + ViewTransform GetCurrentAsyncTransform(); +private: + /* Internal method of above. Callers to this MUST hold the monitor. */ + ViewTransform GetCurrentAsyncTransformInternal(); +public: + /** * Sets the DPI of the device for use within panning and zooming logic. It is * a platform responsibility to set this on initialization of this class and @@ -234,6 +259,11 @@ public: */ nsEventStatus HandleInputEvent(const InputData& aEvent); + /** + * Returns true if this APZC instance is for the layer identified by the guid. + */ + bool Matches(const ScrollableLayerGuid& aGuid); + /** * Sync panning and zooming animation using a fixed frame time. * This will ensure that we animate the APZC correctly with other external @@ -241,23 +271,12 @@ public: */ static void SetFrameTime(const TimeStamp& aMilliseconds); - /** - * Transform and intersect aPoint with the layer tree returning the appropriate - * AsyncPanZoomController for this point. - * aRelativePointOut Return the point transformed into the layer coordinates - * relative to the scroll origin for this layer. - */ - static void GetAPZCAtPoint(const ContainerLayer& aLayerTree, - const ScreenIntPoint& aPoint, - AsyncPanZoomController** aApzcOut, - LayerIntPoint* aRelativePointOut); - /** * Update mFrameMetrics.mScrollOffset to the given offset. * This is necessary in cases where a scroll is not caused by user * input (for example, a content scrollTo()). */ - void UpdateScrollOffset(CSSPoint aScrollOffset); + void UpdateScrollOffset(const CSSPoint& aScrollOffset); /** * Cancels any currently running animation. Note that all this does is set the @@ -492,16 +511,39 @@ private: */ void SetState(PanZoomState aState); + uint64_t mLayersId; nsRefPtr mCompositorParent; TaskThrottler mPaintThrottler; + + /* Access to the following two fields is protected by the mRefPtrMonitor, + since they are accessed on the UI thread but can be cleared on the + compositor thread. */ nsRefPtr mGeckoContentController; nsRefPtr mGestureEventListener; + Monitor mRefPtrMonitor; + /* Utility functions that return a addrefed pointer to the corresponding fields. */ + already_AddRefed GetGeckoContentController(); + already_AddRefed GetGestureEventListener(); + +protected: // Both |mFrameMetrics| and |mLastContentPaintMetrics| are protected by the // monitor. Do not read from or modify either of them without locking. FrameMetrics mFrameMetrics; + + // 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. + Monitor mMonitor; + +private: + // Metrics of the container layer corresponding to this APZC. This is + // stored here so that it is accessible from the UI/controller thread. // These are the metrics at last content paint, the most recent - // values we were notified of in NotifyLayersUpdate(). + // values we were notified of in NotifyLayersUpdate(). Since it represents + // the Gecko state, it should be used as a basis for untransformation when + // sending messages back to Gecko. FrameMetrics mLastContentPaintMetrics; // The last metrics that we requested a paint for. These are used to make sure // that we're not requesting a paint of the same thing that's already drawn. @@ -532,14 +574,6 @@ private: 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 // frame. TimeStamp mLastSampleTime; @@ -571,14 +605,6 @@ private: // 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; // Flag used to determine whether or not we should disable handling of the @@ -598,6 +624,37 @@ private: bool mDelayPanning; friend class Axis; + + /* The functions and members in this section are used to build a tree + * structure out of APZC instances. This tree can only be walked or + * manipulated while holding the lock in the associated APZCTreeManager + * instance. + */ +public: + void SetLastChild(AsyncPanZoomController* child) { mLastChild = child; } + void SetPrevSibling(AsyncPanZoomController* sibling) { mPrevSibling = sibling; } + AsyncPanZoomController* GetLastChild() const { return mLastChild; } + AsyncPanZoomController* GetPrevSibling() const { return mPrevSibling; } +private: + nsRefPtr mLastChild; + nsRefPtr mPrevSibling; + + /* The functions and members in this section are used to maintain the + * area that this APZC instance is responsible for. This is used when + * hit-testing to see which APZC instance should handle touch events. + */ +public: + void SetVisibleRegion(gfxRect rect) { mVisibleRegion = rect; } + + bool VisibleRegionContains(const gfxPoint& aPoint) const { + return mVisibleRegion.Contains(aPoint.x, aPoint.y); + } + +private: + /* This is the viewport of the layer that this APZC corresponds to, but + * post-transform. In other words, it is in the coordinate space of its + * parent layer. */ + gfxRect mVisibleRegion; }; } diff --git a/gfx/layers/ipc/CompositorParent.cpp b/gfx/layers/ipc/CompositorParent.cpp index 0cc8e95108b5..955dc44aa69c 100644 --- a/gfx/layers/ipc/CompositorParent.cpp +++ b/gfx/layers/ipc/CompositorParent.cpp @@ -8,7 +8,6 @@ #include "mozilla/DebugOnly.h" -#include "AsyncPanZoomController.h" #include "AutoOpenSurface.h" #include "CompositorParent.h" #include "mozilla/layers/CompositorOGL.h" @@ -155,6 +154,10 @@ CompositorParent::CompositorParent(nsIWidget* aWidget, CompositorLoop()->PostTask(FROM_HERE, NewRunnableFunction(&AddCompositor, this, &mCompositorID)); + mRootLayerTreeID = AllocateLayerTreeId(); + sIndirectLayerTrees[mRootLayerTreeID].mParent = this; + + mApzcTreeManager = new APZCTreeManager(); ++sCompositorThreadRefCount; } @@ -170,6 +173,12 @@ CompositorParent::IsInCompositorThread() return CompositorThreadID() == PlatformThread::CurrentId(); } +uint64_t +CompositorParent::RootLayerTreeId() +{ + return mRootLayerTreeID; +} + CompositorParent::~CompositorParent() { MOZ_COUNT_DTOR(CompositorParent); @@ -186,6 +195,9 @@ CompositorParent::Destroy() // Ensure that the layer manager is destructed on the compositor thread. mLayerManager = nullptr; mCompositionManager = nullptr; + mApzcTreeManager->ClearTree(); + mApzcTreeManager = nullptr; + sIndirectLayerTrees.erase(mRootLayerTreeID); } void @@ -404,8 +416,13 @@ CompositorParent::ScheduleTask(CancelableTask* task, int time) } void -CompositorParent::NotifyShadowTreeTransaction() +CompositorParent::NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint) { + if (mApzcTreeManager) { + AutoResolveRefLayers resolve(mCompositionManager); + mApzcTreeManager->UpdatePanZoomControllerTree(this, mLayerManager->GetRoot(), aIsFirstPaint, aId); + } + if (mLayerManager) { LayerManagerComposite* managerComposite = mLayerManager->AsLayerManagerComposite(); if (managerComposite) { @@ -559,6 +576,12 @@ CompositorParent::ShadowLayersUpdated(LayerTransactionParent* aLayerTree, mCompositionManager->Updated(isFirstPaint, aTargetConfig); Layer* root = aLayerTree->GetRoot(); mLayerManager->SetRoot(root); + + if (mApzcTreeManager) { + AutoResolveRefLayers resolve(mCompositionManager); + mApzcTreeManager->UpdatePanZoomControllerTree(this, root, isFirstPaint, mRootLayerTreeID); + } + if (root) { SetShadowProperties(root); if (mIsTesting) { @@ -704,7 +727,7 @@ CompositorParent::AllocateLayerTreeId() { MOZ_ASSERT(CompositorLoop()); MOZ_ASSERT(NS_IsMainThread()); - static uint64_t ids; + static uint64_t ids = 0; return ++ids; } @@ -724,20 +747,30 @@ CompositorParent::DeallocateLayerTreeId(uint64_t aId) static void UpdateControllerForLayersId(uint64_t aLayersId, - AsyncPanZoomController* aController) + GeckoContentController* aController) { - // Adopt ref given to us by SetPanZoomControllerForLayerTree() + // Adopt ref given to us by SetControllerForLayerTree() sIndirectLayerTrees[aLayersId].mController = - already_AddRefed(aController); + already_AddRefed(aController); +} - // Notify the AsyncPanZoomController about the current compositor so that it - // can request composites off the compositor thread. - aController->SetCompositorParent(sIndirectLayerTrees[aLayersId].mParent); +ScopedLayerTreeRegistration::ScopedLayerTreeRegistration(uint64_t aLayersId, + Layer* aRoot, + GeckoContentController* aController) + : mLayersId(aLayersId) +{ + sIndirectLayerTrees[aLayersId].mRoot = aRoot; + sIndirectLayerTrees[aLayersId].mController = aController; +} + +ScopedLayerTreeRegistration::~ScopedLayerTreeRegistration() +{ + sIndirectLayerTrees.erase(mLayersId); } /*static*/ void -CompositorParent::SetPanZoomControllerForLayerTree(uint64_t aLayersId, - AsyncPanZoomController* aController) +CompositorParent::SetControllerForLayerTree(uint64_t aLayersId, + GeckoContentController* aController) { // This ref is adopted by UpdateControllerForLayersId(). aController->AddRef(); @@ -747,6 +780,16 @@ CompositorParent::SetPanZoomControllerForLayerTree(uint64_t aLayersId, aController)); } +/*static*/ APZCTreeManager* +CompositorParent::GetAPZCTreeManager(uint64_t aLayersId) +{ + const CompositorParent::LayerTreeState* state = CompositorParent::GetIndirectShadowTree(aLayersId); + if (state && state->mParent) { + return state->mParent->mApzcTreeManager; + } + return nullptr; +} + /** * This class handles layer updates pushed directly from child * processes to the compositor thread. It's associated with a @@ -831,16 +874,10 @@ CompositorParent::Create(Transport* aTransport, ProcessId aOtherProcess) } static void -UpdateIndirectTree(uint64_t aId, Layer* aRoot, const TargetConfig& aTargetConfig, bool isFirstPaint) +UpdateIndirectTree(uint64_t aId, Layer* aRoot, const TargetConfig& aTargetConfig) { sIndirectLayerTrees[aId].mRoot = aRoot; sIndirectLayerTrees[aId].mTargetConfig = aTargetConfig; - ContainerLayer* rootContainer = aRoot->AsContainerLayer(); - if (rootContainer) { - if (AsyncPanZoomController* apzc = sIndirectLayerTrees[aId].mController) { - apzc->NotifyLayersUpdated(rootContainer->GetFrameMetrics(), isFirstPaint); - } - } } /* static */ const CompositorParent::LayerTreeState* @@ -912,9 +949,9 @@ CrossProcessCompositorParent::ShadowLayersUpdated( if (shadowRoot) { SetShadowProperties(shadowRoot); } - UpdateIndirectTree(id, shadowRoot, aTargetConfig, isFirstPaint); + UpdateIndirectTree(id, shadowRoot, aTargetConfig); - sIndirectLayerTrees[id].mParent->NotifyShadowTreeTransaction(); + sIndirectLayerTrees[id].mParent->NotifyShadowTreeTransaction(id, isFirstPaint); } void diff --git a/gfx/layers/ipc/CompositorParent.h b/gfx/layers/ipc/CompositorParent.h index 3b8612639d91..392687f42763 100644 --- a/gfx/layers/ipc/CompositorParent.h +++ b/gfx/layers/ipc/CompositorParent.h @@ -17,6 +17,7 @@ #include "mozilla/layers/PCompositorParent.h" #include "mozilla/layers/PLayerTransactionParent.h" +#include "mozilla/layers/APZCTreeManager.h" #include "base/thread.h" #include "mozilla/Monitor.h" #include "mozilla/TimeStamp.h" @@ -31,12 +32,23 @@ class Thread; namespace mozilla { namespace layers { -class AsyncPanZoomController; +class APZCTreeManager; class Layer; class LayerManagerComposite; class AsyncCompositionManager; struct TextureFactoryIdentifier; +struct ScopedLayerTreeRegistration +{ + ScopedLayerTreeRegistration(uint64_t aLayersId, + Layer* aRoot, + GeckoContentController* aController); + ~ScopedLayerTreeRegistration(); + +private: + uint64_t mLayersId; +}; + class CompositorParent : public PCompositorParent, public ShadowLayersManager { @@ -89,7 +101,13 @@ public: bool ScheduleResumeOnCompositorThread(int width, int height); virtual void ScheduleComposition(); - void NotifyShadowTreeTransaction(); + void NotifyShadowTreeTransaction(uint64_t aId, bool aIsFirstPaint); + + /** + * Returns the unique layer tree identifier that corresponds to the root + * tree of this compositor. + */ + uint64_t RootLayerTreeId(); /** * Returns a pointer to the compositor corresponding to the given ID. @@ -128,13 +146,19 @@ public: static void DeallocateLayerTreeId(uint64_t aId); /** - * Set aController as the pan/zoom controller for the tree referred + * Set aController as the pan/zoom callback for the subtree referred * to by aLayersId. * * Must run on content main thread. */ - static void SetPanZoomControllerForLayerTree(uint64_t aLayersId, - AsyncPanZoomController* aController); + static void SetControllerForLayerTree(uint64_t aLayersId, + GeckoContentController* aController); + + /** + * This returns a reference to the APZCTreeManager to which + * pan/zoom-related events can be sent. + */ + static APZCTreeManager* GetAPZCTreeManager(uint64_t aLayersId); /** * A new child process has been configured to push transactions @@ -154,7 +178,7 @@ public: struct LayerTreeState { nsRefPtr mRoot; - nsRefPtr mController; + nsRefPtr mController; CompositorParent *mParent; TargetConfig mTargetConfig; }; @@ -264,10 +288,13 @@ private: mozilla::Monitor mResumeCompositionMonitor; uint64_t mCompositorID; + uint64_t mRootLayerTreeID; bool mOverrideComposeReadiness; CancelableTask* mForceCompositionTask; + nsRefPtr mApzcTreeManager; + DISALLOW_EVIL_CONSTRUCTORS(CompositorParent); }; diff --git a/gfx/layers/ipc/GeckoContentController.h b/gfx/layers/ipc/GeckoContentController.h index eeaa97919ad0..718a83c9bca6 100644 --- a/gfx/layers/ipc/GeckoContentController.h +++ b/gfx/layers/ipc/GeckoContentController.h @@ -51,7 +51,8 @@ public: * |aContentRect| is in CSS pixels, relative to the current cssPage. * |aScrollableSize| is the current content width/height in CSS pixels. */ - virtual void SendAsyncScrollDOMEvent(const CSSRect &aContentRect, + virtual void SendAsyncScrollDOMEvent(FrameMetrics::ViewID aScrollId, + const CSSRect &aContentRect, const CSSSize &aScrollableSize) = 0; /** diff --git a/gfx/layers/moz.build b/gfx/layers/moz.build index 09d6a214d264..6fb002493f83 100644 --- a/gfx/layers/moz.build +++ b/gfx/layers/moz.build @@ -105,6 +105,7 @@ EXPORTS.mozilla.layers += [ 'client/ImageClient.h', 'client/TextureClient.h', 'client/TiledContentClient.h', + 'composite/APZCTreeManager.h', 'composite/AsyncCompositionManager.h', 'composite/CanvasLayerComposite.h', 'composite/ColorLayerComposite.h', @@ -170,6 +171,7 @@ if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'gonk': ] CPP_SOURCES += [ + 'APZCTreeManager.cpp', 'AsyncCompositionManager.cpp', 'AsyncPanZoomController.cpp', 'Axis.cpp', diff --git a/gfx/tests/gtest/TestAsyncPanZoomController.cpp b/gfx/tests/gtest/TestAsyncPanZoomController.cpp index 9a8ac51bab7b..885a79e5ee48 100644 --- a/gfx/tests/gtest/TestAsyncPanZoomController.cpp +++ b/gfx/tests/gtest/TestAsyncPanZoomController.cpp @@ -10,6 +10,8 @@ #include "mozilla/layers/AsyncPanZoomController.h" #include "mozilla/layers/LayerManagerComposite.h" #include "mozilla/layers/GeckoContentController.h" +#include "mozilla/layers/CompositorParent.h" +#include "mozilla/layers/APZCTreeManager.h" #include "Layers.h" #include "TestLayers.h" @@ -24,7 +26,7 @@ public: MOCK_METHOD1(HandleDoubleTap, void(const CSSIntPoint&)); MOCK_METHOD1(HandleSingleTap, void(const CSSIntPoint&)); MOCK_METHOD1(HandleLongTap, void(const CSSIntPoint&)); - MOCK_METHOD2(SendAsyncScrollDOMEvent, void(const CSSRect &aContentRect, const CSSSize &aScrollableSize)); + MOCK_METHOD3(SendAsyncScrollDOMEvent, void(FrameMetrics::ViewID aScrollId, const CSSRect &aContentRect, const CSSSize &aScrollableSize)); MOCK_METHOD2(PostDelayedTask, void(Task* aTask, int aDelayMs)); }; @@ -39,6 +41,23 @@ class TestAPZCContainerLayer : public ContainerLayer { void RepositionChild(Layer* aChild, Layer* aAfter) {} }; +class TestAsyncPanZoomController : public AsyncPanZoomController { +public: + TestAsyncPanZoomController(uint64_t aLayersId, MockContentController* mcc) + : AsyncPanZoomController(aLayersId, mcc) + {} + + void SetFrameMetrics(const FrameMetrics& metrics) { + MonitorAutoLock lock(mMonitor); + mFrameMetrics = metrics; + } +}; + +class TestAPZCTreeManager : public APZCTreeManager { +protected: + void AssertOnCompositorThread() MOZ_OVERRIDE { /* no-op */ } +}; + static FrameMetrics TestFrameMetrics() { FrameMetrics fm; @@ -90,21 +109,20 @@ void ApzcPan(AsyncPanZoomController* apzc, int& aTime, int aTouchStartY, int aTo TEST(AsyncPanZoomController, Constructor) { // RefCounted class can't live in the stack nsRefPtr mcc = new MockContentController(); - nsRefPtr apzc = new AsyncPanZoomController(mcc); - apzc->NotifyLayersUpdated(TestFrameMetrics(), true); + nsRefPtr apzc = new TestAsyncPanZoomController(0, mcc); + apzc->SetFrameMetrics(TestFrameMetrics()); } TEST(AsyncPanZoomController, SimpleTransform) { TimeStamp testStartTime = TimeStamp::Now(); // RefCounted class can't live in the stack nsRefPtr mcc = new MockContentController(); - nsRefPtr apzc = new AsyncPanZoomController(mcc); - apzc->NotifyLayersUpdated(TestFrameMetrics(), true); + nsRefPtr apzc = new TestAsyncPanZoomController(0, mcc); + apzc->SetFrameMetrics(TestFrameMetrics()); - TestAPZCContainerLayer layer; ScreenPoint pointOut; ViewTransform viewTransformOut; - apzc->SampleContentTransformForFrame(testStartTime, &layer, &viewTransformOut, pointOut); + apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); EXPECT_EQ(pointOut, ScreenPoint()); EXPECT_EQ(viewTransformOut, ViewTransform()); @@ -131,7 +149,8 @@ TEST(AsyncPanZoomController, ComplexTransform) { // sides. nsRefPtr mcc = new MockContentController(); - nsRefPtr apzc = new AsyncPanZoomController(mcc); + nsRefPtr apzc = new TestAsyncPanZoomController(0, mcc); + nsRefPtr childApzc = new TestAsyncPanZoomController(0, mcc); const char* layerTreeSyntax = "c(c)"; // LayerID 0 1 @@ -174,39 +193,41 @@ TEST(AsyncPanZoomController, ComplexTransform) { // the CSS transform on the child layer does not affect the SampleContentTransformForFrame code // initial transform + apzc->SetFrameMetrics(metrics); apzc->NotifyLayersUpdated(metrics, true); - apzc->SampleContentTransformForFrame(testStartTime, layers[0]->AsContainerLayer(), &viewTransformOut, pointOut); + apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); EXPECT_EQ(ViewTransform(LayerPoint(), LayoutDeviceToScreenScale(2)), viewTransformOut); EXPECT_EQ(ScreenPoint(60, 60), pointOut); - apzc->NotifyLayersUpdated(childMetrics, true); - apzc->SampleContentTransformForFrame(testStartTime, layers[1]->AsContainerLayer(), &viewTransformOut, pointOut); + childApzc->SetFrameMetrics(childMetrics); + childApzc->NotifyLayersUpdated(childMetrics, true); + childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); EXPECT_EQ(ViewTransform(LayerPoint(), LayoutDeviceToScreenScale(2)), viewTransformOut); EXPECT_EQ(ScreenPoint(60, 60), pointOut); // do an async scroll by 5 pixels and check the transform metrics.mScrollOffset += CSSPoint(5, 0); - apzc->NotifyLayersUpdated(metrics, true); - apzc->SampleContentTransformForFrame(testStartTime, layers[0]->AsContainerLayer(), &viewTransformOut, pointOut); + apzc->SetFrameMetrics(metrics); + apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), LayoutDeviceToScreenScale(2)), viewTransformOut); EXPECT_EQ(ScreenPoint(90, 60), pointOut); childMetrics.mScrollOffset += CSSPoint(5, 0); - apzc->NotifyLayersUpdated(childMetrics, true); - apzc->SampleContentTransformForFrame(testStartTime, layers[1]->AsContainerLayer(), &viewTransformOut, pointOut); + childApzc->SetFrameMetrics(childMetrics); + childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), LayoutDeviceToScreenScale(2)), viewTransformOut); EXPECT_EQ(ScreenPoint(90, 60), pointOut); // do an async zoom of 1.5x and check the transform metrics.mZoom.scale *= 1.5f; - apzc->NotifyLayersUpdated(metrics, true); - apzc->SampleContentTransformForFrame(testStartTime, layers[0]->AsContainerLayer(), &viewTransformOut, pointOut); + apzc->SetFrameMetrics(metrics); + apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), LayoutDeviceToScreenScale(3)), viewTransformOut); EXPECT_EQ(ScreenPoint(135, 90), pointOut); childMetrics.mZoom.scale *= 1.5f; - apzc->NotifyLayersUpdated(childMetrics, true); - apzc->SampleContentTransformForFrame(testStartTime, layers[0]->AsContainerLayer(), &viewTransformOut, pointOut); + childApzc->SetFrameMetrics(childMetrics); + childApzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); EXPECT_EQ(ViewTransform(LayerPoint(-30, 0), LayoutDeviceToScreenScale(3)), viewTransformOut); EXPECT_EQ(ScreenPoint(135, 90), pointOut); } @@ -216,28 +237,29 @@ TEST(AsyncPanZoomController, Pan) { AsyncPanZoomController::SetFrameTime(testStartTime); nsRefPtr mcc = new MockContentController(); - nsRefPtr apzc = new AsyncPanZoomController(mcc); + nsRefPtr apzc = new TestAsyncPanZoomController(0, mcc); + + apzc->SetFrameMetrics(TestFrameMetrics()); apzc->NotifyLayersUpdated(TestFrameMetrics(), true); - EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_)).Times(4); + EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(4); EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); int time = 0; int touchStart = 50; int touchEnd = 10; - TestAPZCContainerLayer layer; ScreenPoint pointOut; ViewTransform viewTransformOut; // Pan down ApzcPan(apzc, time, touchStart, touchEnd); - apzc->SampleContentTransformForFrame(testStartTime, &layer, &viewTransformOut, pointOut); + apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); EXPECT_EQ(pointOut, ScreenPoint(0, -(touchEnd-touchStart))); EXPECT_NE(viewTransformOut, ViewTransform()); // Pan back ApzcPan(apzc, time, touchEnd, touchStart); - apzc->SampleContentTransformForFrame(testStartTime, &layer, &viewTransformOut, pointOut); + apzc->SampleContentTransformForFrame(testStartTime, &viewTransformOut, pointOut); EXPECT_EQ(pointOut, ScreenPoint()); EXPECT_EQ(viewTransformOut, ViewTransform()); } @@ -247,16 +269,17 @@ TEST(AsyncPanZoomController, Fling) { AsyncPanZoomController::SetFrameTime(testStartTime); nsRefPtr mcc = new MockContentController(); - nsRefPtr apzc = new AsyncPanZoomController(mcc); + nsRefPtr apzc = new TestAsyncPanZoomController(0, mcc); + + apzc->SetFrameMetrics(TestFrameMetrics()); apzc->NotifyLayersUpdated(TestFrameMetrics(), true); - EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_)).Times(2); + EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(2); EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); int time = 0; int touchStart = 50; int touchEnd = 10; - TestAPZCContainerLayer layer; ScreenPoint pointOut; ViewTransform viewTransformOut; @@ -264,7 +287,7 @@ TEST(AsyncPanZoomController, Fling) { ApzcPan(apzc, time, touchStart, touchEnd); ScreenPoint lastPoint; for (int i = 1; i < 50; i+=1) { - apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(i), &layer, &viewTransformOut, pointOut); + apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(i), &viewTransformOut, pointOut); EXPECT_GT(pointOut.y, lastPoint.y); lastPoint = pointOut; } @@ -275,23 +298,24 @@ TEST(AsyncPanZoomController, OverScrollPanning) { AsyncPanZoomController::SetFrameTime(testStartTime); nsRefPtr mcc = new MockContentController(); - nsRefPtr apzc = new AsyncPanZoomController(mcc); + nsRefPtr apzc = new TestAsyncPanZoomController(0, mcc); + + apzc->SetFrameMetrics(TestFrameMetrics()); apzc->NotifyLayersUpdated(TestFrameMetrics(), true); - EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_)).Times(3); + EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(3); EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); // Pan sufficiently to hit overscroll behavior int time = 0; int touchStart = 500; int touchEnd = 10; - TestAPZCContainerLayer layer; ScreenPoint pointOut; ViewTransform viewTransformOut; // Pan down ApzcPan(apzc, time, touchStart, touchEnd); - apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(1000), &layer, &viewTransformOut, pointOut); + apzc->SampleContentTransformForFrame(testStartTime+TimeDuration::FromMilliseconds(1000), &viewTransformOut, pointOut); EXPECT_EQ(pointOut, ScreenPoint(0, 90)); } @@ -322,124 +346,102 @@ CreateTestLayerTree(nsRefPtr& aLayerManager, nsTArrayAsContainerLayer(); + FrameMetrics metrics; + metrics.mScrollId = aScrollId; + nsIntRect layerBound = aLayer->GetVisibleRegion().GetBounds(); + metrics.mCompositionBounds = ScreenIntRect(layerBound.x, layerBound.y, + layerBound.width, layerBound.height); + metrics.mViewport = CSSRect(layerBound.x, layerBound.y, + layerBound.width, layerBound.height); + container->SetFrameMetrics(metrics); + + // when we do the next tree update, a new APZC will be created for this layer, + // and that will invoke these functions once. + EXPECT_CALL(*mcc, SendAsyncScrollDOMEvent(_,_,_)).Times(1); + EXPECT_CALL(*mcc, RequestContentRepaint(_)).Times(1); +} + +TEST(APZCTreeManager, GetAPZCAtPoint) { nsTArray > layers; nsRefPtr lm; nsRefPtr root = CreateTestLayerTree(lm, layers); TimeStamp testStartTime = TimeStamp::Now(); AsyncPanZoomController::SetFrameTime(testStartTime); - nsRefPtr mcc = new MockContentController(); - nsRefPtr apzcMain = new AsyncPanZoomController(mcc); - nsRefPtr apzcSub3 = new AsyncPanZoomController(mcc); - nsRefPtr apzcSub4 = new AsyncPanZoomController(mcc); - nsRefPtr apzcSub7 = new AsyncPanZoomController(mcc); - apzcMain->NotifyLayersUpdated(TestFrameMetrics(), true); + ScopedLayerTreeRegistration controller(0, root, mcc); - nsIntRect layerBound; - ScreenIntPoint touchPoint(20, 20); - AsyncPanZoomController* apzcOut; - LayerIntPoint relativePointOut; - - FrameMetrics scrollable; + nsRefPtr manager = new TestAPZCTreeManager(); // No APZC attached so hit testing will return no APZC at (20,20) - AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint, - &apzcOut, &relativePointOut); - + nsRefPtr hit = manager->GetTargetAPZC(ScreenPoint(20, 20)); AsyncPanZoomController* nullAPZC = nullptr; - EXPECT_EQ(apzcOut, nullAPZC); + EXPECT_EQ(nullAPZC, hit.get()); // Now we have a root APZC that will match the page - scrollable.mScrollId = FrameMetrics::ROOT_SCROLL_ID; - layerBound = root->GetVisibleRegion().GetBounds(); - scrollable.mViewport = CSSRect(layerBound.x, layerBound.y, - layerBound.width, layerBound.height); - root->AsContainerLayer()->SetFrameMetrics(scrollable); - root->AsContainerLayer()->SetAsyncPanZoomController(apzcMain); - AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint, - &apzcOut, &relativePointOut); - EXPECT_EQ(apzcOut, apzcMain.get()); - EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut); + SetScrollableFrameMetrics(root, FrameMetrics::ROOT_SCROLL_ID, mcc); + manager->UpdatePanZoomControllerTree(nullptr, root, 0, false); + hit = manager->GetTargetAPZC(ScreenPoint(15, 15)); + EXPECT_EQ(root->AsContainerLayer()->GetAsyncPanZoomController(), hit.get()); + // expect hit point at LayerIntPoint(15, 15) // Now we have a sub APZC with a better fit - scrollable.mScrollId = FrameMetrics::START_SCROLL_ID; - layerBound = layers[3]->GetVisibleRegion().GetBounds(); - scrollable.mViewport = CSSRect(layerBound.x, layerBound.y, - layerBound.width, layerBound.height); - layers[3]->AsContainerLayer()->SetFrameMetrics(scrollable); - layers[3]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub3); - AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint, - &apzcOut, &relativePointOut); - EXPECT_EQ(apzcOut, apzcSub3.get()); - EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut); + SetScrollableFrameMetrics(layers[3], FrameMetrics::START_SCROLL_ID, mcc); + manager->UpdatePanZoomControllerTree(nullptr, root, 0, false); + EXPECT_NE(root->AsContainerLayer()->GetAsyncPanZoomController(), layers[3]->AsContainerLayer()->GetAsyncPanZoomController()); + hit = manager->GetTargetAPZC(ScreenPoint(15, 15)); + EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get()); + // expect hit point at LayerIntPoint(15, 15) // Now test hit testing when we have two scrollable layers - touchPoint = ScreenIntPoint(15,15); - AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint, - &apzcOut, &relativePointOut); - EXPECT_EQ(apzcOut, apzcSub3.get()); // We haven't bound apzcSub4 yet - scrollable.mScrollId++; - layerBound = layers[4]->GetVisibleRegion().GetBounds(); - scrollable.mViewport = CSSRect(layerBound.x, layerBound.y, - layerBound.width, layerBound.height); - layers[4]->AsContainerLayer()->SetFrameMetrics(scrollable); - layers[4]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub4); - AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint, - &apzcOut, &relativePointOut); - EXPECT_EQ(apzcOut, apzcSub4.get()); - EXPECT_EQ(LayerIntPoint(15, 15), relativePointOut); + hit = manager->GetTargetAPZC(ScreenPoint(15, 15)); + EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get()); + SetScrollableFrameMetrics(layers[4], FrameMetrics::START_SCROLL_ID + 1, mcc); + manager->UpdatePanZoomControllerTree(nullptr, root, 0, false); + hit = manager->GetTargetAPZC(ScreenPoint(15, 15)); + EXPECT_EQ(layers[4]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get()); + // expect hit point at LayerIntPoint(15, 15) - // Hit test ouside the reach of apzc3/4 but inside apzcMain - touchPoint = ScreenIntPoint(90,90); - AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint, - &apzcOut, &relativePointOut); - EXPECT_EQ(apzcOut, apzcMain.get()); - EXPECT_EQ(LayerIntPoint(90, 90), relativePointOut); + // Hit test ouside the reach of layer[3,4] but inside root + hit = manager->GetTargetAPZC(ScreenPoint(90, 90)); + EXPECT_EQ(root->AsContainerLayer()->GetAsyncPanZoomController(), hit.get()); + // expect hit point at LayerIntPoint(90, 90) // Hit test ouside the reach of any layer - touchPoint = ScreenIntPoint(1000,10); - AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint, - &apzcOut, &relativePointOut); - EXPECT_EQ(apzcOut, nullAPZC); - touchPoint = ScreenIntPoint(-1000,10); - AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint, - &apzcOut, &relativePointOut); - EXPECT_EQ(apzcOut, nullAPZC); + hit = manager->GetTargetAPZC(ScreenPoint(1000, 10)); + EXPECT_EQ(nullAPZC, hit.get()); + hit = manager->GetTargetAPZC(ScreenPoint(-1000, 10)); + EXPECT_EQ(nullAPZC, hit.get()); // Test layer transform gfx3DMatrix transform; transform.ScalePost(0.1, 0.1, 1); root->SetBaseTransform(transform); + root->ComputeEffectiveTransforms(gfx3DMatrix()); + manager->UpdatePanZoomControllerTree(nullptr, root, 0, false); + hit = manager->GetTargetAPZC(ScreenPoint(50, 50)); // This point is now outside the root layer + EXPECT_EQ(nullAPZC, hit.get()); - touchPoint = ScreenIntPoint(50,50); // This point is now outside the root layer - AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint, - &apzcOut, &relativePointOut); - EXPECT_EQ(apzcOut, nullAPZC); - - touchPoint = ScreenIntPoint(2,2); - AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint, - &apzcOut, &relativePointOut); - EXPECT_EQ(apzcOut, apzcSub4.get()); - EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut); + hit = manager->GetTargetAPZC(ScreenPoint(2, 2)); + EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get()); + // expect hit point at LayerPoint(20, 20) // Scale layer[4] outside the range layers[4]->SetBaseTransform(transform); // layer 4 effective visible screenrect: (0.05, 0.05, 0.2, 0.2) // Does not contain (2, 2) - AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint, - &apzcOut, &relativePointOut); - EXPECT_EQ(apzcOut, apzcSub3.get()); - EXPECT_EQ(LayerIntPoint(20, 20), relativePointOut); + root->ComputeEffectiveTransforms(gfx3DMatrix()); + manager->UpdatePanZoomControllerTree(nullptr, root, 0, false); + hit = manager->GetTargetAPZC(ScreenPoint(2, 2)); + EXPECT_EQ(layers[3]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get()); + // expect hit point at LayerPoint(20, 20) // Transformation chain to layer 7 - scrollable.mScrollId++; - layerBound = layers[7]->GetVisibleRegion().GetBounds(); - scrollable.mViewport = CSSRect(layerBound.x, layerBound.y, - layerBound.width, layerBound.height); - layers[7]->AsContainerLayer()->SetFrameMetrics(scrollable); - layers[7]->AsContainerLayer()->SetAsyncPanZoomController(apzcSub7); + SetScrollableFrameMetrics(layers[7], FrameMetrics::START_SCROLL_ID + 2, mcc); gfx3DMatrix translateTransform; translateTransform.Translate(gfxPoint3D(10, 10, 0)); @@ -453,12 +455,14 @@ TEST(AsyncPanZoomController, GetAPZCAtPoint) { translateTransform3.ScalePost(1,15,1); layers[7]->SetBaseTransform(translateTransform3); - touchPoint = ScreenIntPoint(1,45); + root->ComputeEffectiveTransforms(gfx3DMatrix()); + manager->UpdatePanZoomControllerTree(nullptr, root, 0, false); // layer 7 effective visible screenrect (0,16,4,60) but clipped by parent layers - AsyncPanZoomController::GetAPZCAtPoint(*root->AsContainerLayer(), touchPoint, - &apzcOut, &relativePointOut); - EXPECT_EQ(apzcOut, apzcSub7.get()); - EXPECT_EQ(LayerIntPoint(20, 29), relativePointOut); + hit = manager->GetTargetAPZC(ScreenPoint(1, 45)); + EXPECT_EQ(layers[7]->AsContainerLayer()->GetAsyncPanZoomController(), hit.get()); + // expect hit point at LayerPoint(20, 29) + + manager->ClearTree(); } diff --git a/gfx/tests/gtest/TestLayers.cpp b/gfx/tests/gtest/TestLayers.cpp index bd1c5cb327cc..c1364f069354 100644 --- a/gfx/tests/gtest/TestLayers.cpp +++ b/gfx/tests/gtest/TestLayers.cpp @@ -48,6 +48,7 @@ public: } virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface) { + DefaultComputeEffectiveTransforms(aTransformToSurface); } virtual void RepositionChild(Layer* aChild, Layer* aAfter) { @@ -92,10 +93,6 @@ public: return TYPE_THEBES; } - virtual void ComputeEffectiveTransforms(const gfx3DMatrix& aTransformToSurface) { - MOZ_CRASH(); - } - virtual void InvalidateRegion(const nsIntRegion& aRegion) { MOZ_CRASH(); } @@ -236,6 +233,9 @@ already_AddRefed CreateLayerTree( lastLayer = layer; } } + if (rootLayer) { + rootLayer->ComputeEffectiveTransforms(gfx3DMatrix()); + } return rootLayer.forget(); } diff --git a/layout/base/nsLayoutUtils.cpp b/layout/base/nsLayoutUtils.cpp index 6553f41775b4..62d7785f1bb0 100644 --- a/layout/base/nsLayoutUtils.cpp +++ b/layout/base/nsLayoutUtils.cpp @@ -469,6 +469,23 @@ nsLayoutUtils::FindContentFor(ViewID aId) } } +nsIScrollableFrame* +nsLayoutUtils::FindScrollableFrameFor(ViewID aId) +{ + nsIContent* content = FindContentFor(aId); + if (!content) { + return nullptr; + } + + nsIFrame* scrolledFrame = content->GetPrimaryFrame(); + if (scrolledFrame && content->OwnerDoc()->GetRootElement() == content) { + // The content is the root element of a subdocument, so return the root scrollable + // for the subdocument. + scrolledFrame = scrolledFrame->PresContext()->PresShell()->GetRootScrollFrame(); + } + return scrolledFrame ? scrolledFrame->GetScrollTargetFrame() : nullptr; +} + bool nsLayoutUtils::GetDisplayPort(nsIContent* aContent, nsRect *aResult) { diff --git a/layout/base/nsLayoutUtils.h b/layout/base/nsLayoutUtils.h index 3d689464ed30..313738005098 100644 --- a/layout/base/nsLayoutUtils.h +++ b/layout/base/nsLayoutUtils.h @@ -76,6 +76,11 @@ public: */ static nsIContent* FindContentFor(ViewID aId); + /** + * Find the scrollable frame for a given ID. + */ + static nsIScrollableFrame* FindScrollableFrameFor(ViewID aId); + /** * Get display port for the given element. */ diff --git a/layout/ipc/RenderFrameParent.cpp b/layout/ipc/RenderFrameParent.cpp index dd1183fb4a8b..b44eaa286cd4 100644 --- a/layout/ipc/RenderFrameParent.cpp +++ b/layout/ipc/RenderFrameParent.cpp @@ -15,7 +15,7 @@ #endif //MOZ_ENABLE_D3D9_LAYER #include "mozilla/BrowserElementParent.h" #include "mozilla/dom/TabParent.h" -#include "mozilla/layers/AsyncPanZoomController.h" +#include "mozilla/layers/APZCTreeManager.h" #include "mozilla/layers/CompositorParent.h" #include "mozilla/layers/LayerTransactionParent.h" #include "nsContentUtils.h" @@ -552,7 +552,8 @@ public: void ClearRenderFrame() { mRenderFrame = nullptr; } - virtual void SendAsyncScrollDOMEvent(const CSSRect& aContentRect, + virtual void SendAsyncScrollDOMEvent(FrameMetrics::ViewID aScrollId, + const CSSRect& aContentRect, const CSSSize& aContentSize) MOZ_OVERRIDE { if (MessageLoop::current() != mUILoop) { @@ -560,10 +561,10 @@ public: FROM_HERE, NewRunnableMethod(this, &RemoteContentController::SendAsyncScrollDOMEvent, - aContentRect, aContentSize)); + aScrollId, aContentRect, aContentSize)); return; } - if (mRenderFrame) { + if (mRenderFrame && aScrollId == FrameMetrics::ROOT_SCROLL_ID) { TabParent* browser = static_cast(mRenderFrame->Manager()); BrowserElementParent::DispatchAsyncScrollEvent(browser, aContentRect, aContentSize); @@ -620,14 +621,24 @@ RenderFrameParent::RenderFrameParent(nsFrameLoader* aFrameLoader, } if (aScrollingBehavior == ASYNC_PAN_ZOOM) { mContentController = new RemoteContentController(this); - mPanZoomController = new AsyncPanZoomController( - mContentController, AsyncPanZoomController::USE_GESTURE_DETECTOR); - CompositorParent::SetPanZoomControllerForLayerTree(mLayersId, - mPanZoomController); + CompositorParent::SetControllerForLayerTree(mLayersId, mContentController); } } } +APZCTreeManager* +RenderFrameParent::GetApzcTreeManager() +{ + // We can't get a ref to the APZCTreeManager until after the child is + // created and the static getter knows which CompositorParent is + // instantiated with this layers ID. That's why try to fetch it when + // we first need it and cache the result. + if (!mApzcTreeManager) { + mApzcTreeManager = CompositorParent::GetAPZCTreeManager(mLayersId); + } + return mApzcTreeManager.get(); +} + RenderFrameParent::~RenderFrameParent() {} @@ -787,17 +798,17 @@ void RenderFrameParent::NotifyInputEvent(const nsInputEvent& aEvent, nsInputEvent* aOutEvent) { - if (mPanZoomController) { - mPanZoomController->ReceiveInputEvent(aEvent, aOutEvent); + if (GetApzcTreeManager()) { + GetApzcTreeManager()->ReceiveInputEvent(aEvent, aOutEvent); } } void RenderFrameParent::NotifyDimensionsChanged(ScreenIntSize size) { - if (mPanZoomController) { - mPanZoomController->UpdateCompositionBounds( - ScreenIntRect(ScreenIntPoint(), size)); + if (GetApzcTreeManager()) { + GetApzcTreeManager()->UpdateCompositionBounds(ScrollableLayerGuid(mLayersId), + ScreenIntRect(ScreenIntPoint(), size)); } } @@ -810,7 +821,7 @@ RenderFrameParent::ActorDestroy(ActorDestroyReason why) // Stop our content controller from requesting repaints of our // content. mContentController->ClearRenderFrame(); - mPanZoomController->Destroy(); + // TODO: notify the compositor? } } @@ -836,8 +847,8 @@ RenderFrameParent::RecvNotifyCompositorTransaction() bool RenderFrameParent::RecvCancelDefaultPanZoom() { - if (mPanZoomController) { - mPanZoomController->CancelDefaultPanZoom(); + if (GetApzcTreeManager()) { + GetApzcTreeManager()->CancelDefaultPanZoom(ScrollableLayerGuid(mLayersId)); } return true; } @@ -845,8 +856,8 @@ RenderFrameParent::RecvCancelDefaultPanZoom() bool RenderFrameParent::RecvDetectScrollableSubframe() { - if (mPanZoomController) { - mPanZoomController->DetectScrollableSubframe(); + if (GetApzcTreeManager()) { + GetApzcTreeManager()->DetectScrollableSubframe(ScrollableLayerGuid(mLayersId)); } return true; } @@ -972,24 +983,27 @@ RenderFrameParent::BuildDisplayList(nsDisplayListBuilder* aBuilder, void RenderFrameParent::ZoomToRect(const CSSRect& aRect) { - if (mPanZoomController) { - mPanZoomController->ZoomToRect(aRect); + if (GetApzcTreeManager()) { + GetApzcTreeManager()->ZoomToRect(ScrollableLayerGuid(mLayersId), + aRect); } } void RenderFrameParent::ContentReceivedTouch(bool aPreventDefault) { - if (mPanZoomController) { - mPanZoomController->ContentReceivedTouch(aPreventDefault); + if (GetApzcTreeManager()) { + GetApzcTreeManager()->ContentReceivedTouch(ScrollableLayerGuid(mLayersId), + aPreventDefault); } } void RenderFrameParent::UpdateZoomConstraints(bool aAllowZoom, float aMinZoom, float aMaxZoom) { - if (mPanZoomController) { - mPanZoomController->UpdateZoomConstraints(aAllowZoom, aMinZoom, aMaxZoom); + if (GetApzcTreeManager()) { + GetApzcTreeManager()->UpdateZoomConstraints(ScrollableLayerGuid(mLayersId), + aAllowZoom, aMinZoom, aMaxZoom); } } diff --git a/layout/ipc/RenderFrameParent.h b/layout/ipc/RenderFrameParent.h index 161bec159b50..04e7b88feec5 100644 --- a/layout/ipc/RenderFrameParent.h +++ b/layout/ipc/RenderFrameParent.h @@ -25,7 +25,7 @@ namespace mozilla { class InputEvent; namespace layers { -class AsyncPanZoomController; +class APZCTreeManager; class GestureEventListener; class TargetConfig; class LayerTransactionParent; @@ -132,11 +132,13 @@ private: nsRefPtr mFrameLoader; nsRefPtr mContainer; // When our scrolling behavior is ASYNC_PAN_ZOOM, we have a nonnull - // AsyncPanZoomController. It's associated with the shadow layer - // tree on the compositor thread. - nsRefPtr mPanZoomController; + // APZCTreeManager. It's used to manipulate the shadow layer tree + // on the compositor thread. + nsRefPtr mApzcTreeManager; nsRefPtr mContentController; + layers::APZCTreeManager* GetApzcTreeManager(); + // This contains the views for all the scrollable frames currently in the // painted region of our remote content. ViewMap mContentViews; diff --git a/toolkit/library/Makefile.in b/toolkit/library/Makefile.in index e5ac009f49e1..1f082d4a3614 100644 --- a/toolkit/library/Makefile.in +++ b/toolkit/library/Makefile.in @@ -96,6 +96,9 @@ endif #} ifdef MOZ_B2G_BT #{ STATIC_LIBS += mozdbus_s +ifeq (gonk,$(MOZ_WIDGET_TOOLKIT)) +OS_LIBS += -ldbus +endif endif #} ifneq ($(strip $(MOZ_B2G_RIL)$(MOZ_B2G_BT)),) #{ @@ -446,7 +449,6 @@ OS_LIBS += \ -lsysutils \ -lcamera_client \ -lsensorservice \ - -ldbus \ -lstagefright \ -lstagefright_foundation \ -lstagefright_omx \ diff --git a/widget/android/AndroidBridge.cpp b/widget/android/AndroidBridge.cpp index c530b8c3195a..35bda9e9f29e 100644 --- a/widget/android/AndroidBridge.cpp +++ b/widget/android/AndroidBridge.cpp @@ -2850,7 +2850,9 @@ AndroidBridge::HandleLongTap(const CSSIntPoint& aPoint) } void -AndroidBridge::SendAsyncScrollDOMEvent(const CSSRect& aContentRect, const CSSSize& aScrollableSize) +AndroidBridge::SendAsyncScrollDOMEvent(mozilla::layers::FrameMetrics::ViewID aScrollId, + const CSSRect& aContentRect, + const CSSSize& aScrollableSize) { // FIXME implement this } diff --git a/widget/android/AndroidBridge.h b/widget/android/AndroidBridge.h index b200cd04d63f..25e931ce2960 100644 --- a/widget/android/AndroidBridge.h +++ b/widget/android/AndroidBridge.h @@ -601,7 +601,9 @@ public: void HandleDoubleTap(const CSSIntPoint& aPoint) MOZ_OVERRIDE; void HandleSingleTap(const CSSIntPoint& aPoint) MOZ_OVERRIDE; void HandleLongTap(const CSSIntPoint& aPoint) MOZ_OVERRIDE; - void SendAsyncScrollDOMEvent(const CSSRect& aContentRect, const CSSSize& aScrollableSize) MOZ_OVERRIDE; + void SendAsyncScrollDOMEvent(mozilla::layers::FrameMetrics::ViewID aScrollId, + const CSSRect& aContentRect, + const CSSSize& aScrollableSize) MOZ_OVERRIDE; void PostDelayedTask(Task* aTask, int aDelayMs) MOZ_OVERRIDE; int64_t RunDelayedTasks(); }; diff --git a/widget/android/AndroidJNI.cpp b/widget/android/AndroidJNI.cpp index 307560984066..e7a5c91f412c 100644 --- a/widget/android/AndroidJNI.cpp +++ b/widget/android/AndroidJNI.cpp @@ -35,7 +35,7 @@ #include "mozilla/dom/mobilemessage/Types.h" #include "mozilla/dom/mobilemessage/PSms.h" #include "mozilla/dom/mobilemessage/SmsParent.h" -#include "mozilla/layers/AsyncPanZoomController.h" +#include "mozilla/layers/APZCTreeManager.h" #include "nsIMobileMessageDatabaseService.h" #include "nsPluginInstanceOwner.h" #include "nsSurfaceTexture.h" @@ -847,10 +847,10 @@ Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime(JNIEnv *jenv, jclass jc) NS_EXPORT void JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_abortAnimation(JNIEnv* env, jobject instance) { - AsyncPanZoomController* controller = nsWindow::GetPanZoomController(); - if (controller) { - controller->CancelAnimation(); - } + APZCTreeManager *controller = nsWindow::GetAPZCTreeManager(); + if (controller) { + controller->CancelAnimation(ScrollableLayerGuid(nsWindow::RootLayerTreeId())); + } } NS_EXPORT void JNICALL @@ -865,13 +865,12 @@ Java_org_mozilla_gecko_gfx_NativePanZoomController_init(JNIEnv* env, jobject ins MOZ_ASSERT(false, "Registering a new NPZC when we already have one"); env->DeleteGlobalRef(oldRef); } - nsWindow::SetPanZoomController(new AsyncPanZoomController(AndroidBridge::Bridge(), AsyncPanZoomController::USE_GESTURE_DETECTOR)); } NS_EXPORT void JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_handleTouchEvent(JNIEnv* env, jobject instance, jobject event) { - AsyncPanZoomController *controller = nsWindow::GetPanZoomController(); + APZCTreeManager *controller = nsWindow::GetAPZCTreeManager(); if (controller) { AndroidGeckoEvent* wrapper = AndroidGeckoEvent::MakeFromJavaObject(env, event); const MultiTouchInput& input = wrapper->MakeMultiTouchInput(nsWindow::TopWindow()); @@ -905,7 +904,6 @@ Java_org_mozilla_gecko_gfx_NativePanZoomController_destroy(JNIEnv* env, jobject return; } - nsWindow::SetPanZoomController(nullptr); jobject oldRef = AndroidBridge::Bridge()->SetNativePanZoomController(NULL); if (!oldRef) { MOZ_ASSERT(false, "Clearing a non-existent NPZC"); @@ -917,9 +915,9 @@ Java_org_mozilla_gecko_gfx_NativePanZoomController_destroy(JNIEnv* env, jobject NS_EXPORT void JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_notifyDefaultActionPrevented(JNIEnv* env, jobject instance, jboolean prevented) { - AsyncPanZoomController *controller = nsWindow::GetPanZoomController(); + APZCTreeManager *controller = nsWindow::GetAPZCTreeManager(); if (controller) { - controller->ContentReceivedTouch(prevented); + controller->ContentReceivedTouch(ScrollableLayerGuid(nsWindow::RootLayerTreeId()), prevented); } } @@ -946,9 +944,9 @@ Java_org_mozilla_gecko_gfx_NativePanZoomController_getOverScrollMode(JNIEnv* env NS_EXPORT void JNICALL Java_org_mozilla_gecko_gfx_NativePanZoomController_updateScrollOffset(JNIEnv* env, jobject instance, jfloat cssX, jfloat cssY) { - AsyncPanZoomController* controller = nsWindow::GetPanZoomController(); + APZCTreeManager *controller = nsWindow::GetAPZCTreeManager(); if (controller) { - controller->UpdateScrollOffset(CSSPoint(cssX, cssY)); + controller->UpdateScrollOffset(ScrollableLayerGuid(nsWindow::RootLayerTreeId()), CSSPoint(cssX, cssY)); } } diff --git a/widget/android/nsWindow.cpp b/widget/android/nsWindow.cpp index e6c4d7a4c384..4a688296d35f 100644 --- a/widget/android/nsWindow.cpp +++ b/widget/android/nsWindow.cpp @@ -2417,11 +2417,11 @@ nsWindow::DrawWindowOverlay(LayerManager* aManager, nsIntRect aRect) // off-main-thread compositor fields and functions +nsRefPtr nsWindow::sApzcTreeManager = 0; nsRefPtr nsWindow::sLayerManager = 0; nsRefPtr nsWindow::sCompositorParent = 0; nsRefPtr nsWindow::sCompositorChild = 0; bool nsWindow::sCompositorPaused = true; -nsRefPtr nsWindow::sApzc = 0; void nsWindow::SetCompositor(mozilla::layers::LayerManager* aLayerManager, @@ -2496,52 +2496,30 @@ nsWindow::NeedsPaint() return nsIWidget::NeedsPaint(); } -class AndroidCompositorParent : public CompositorParent { -public: - AndroidCompositorParent(nsIWidget* aWidget, bool aRenderToEGLSurface, - int aSurfaceWidth, int aSurfaceHeight) - : CompositorParent(aWidget, aRenderToEGLSurface, aSurfaceWidth, aSurfaceHeight) - { - if (nsWindow::GetPanZoomController()) { - nsWindow::GetPanZoomController()->SetCompositorParent(this); - } - } - - virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree, const TargetConfig& aTargetConfig, - bool isFirstPaint) MOZ_OVERRIDE - { - CompositorParent::ShadowLayersUpdated(aLayerTree, aTargetConfig, isFirstPaint); - Layer* targetLayer = GetLayerManager()->GetPrimaryScrollableLayer(); - AsyncPanZoomController* controller = nsWindow::GetPanZoomController(); - if (targetLayer && targetLayer->AsContainerLayer() && controller) { - targetLayer->AsContainerLayer()->SetAsyncPanZoomController(controller); - controller->NotifyLayersUpdated(targetLayer->AsContainerLayer()->GetFrameMetrics(), isFirstPaint); - } - } -}; - CompositorParent* nsWindow::NewCompositorParent(int aSurfaceWidth, int aSurfaceHeight) { - return new AndroidCompositorParent(this, true, aSurfaceWidth, aSurfaceHeight); + return new CompositorParent(this, true, aSurfaceWidth, aSurfaceHeight); } -void -nsWindow::SetPanZoomController(AsyncPanZoomController* apzc) +mozilla::layers::APZCTreeManager* +nsWindow::GetAPZCTreeManager() { - if (sApzc) { - sApzc->SetCompositorParent(nullptr); - sApzc = nullptr; - } - if (apzc) { - sApzc = apzc; - sApzc->SetCompositorParent(sCompositorParent); + if (!sApzcTreeManager) { + CompositorParent* compositor = sCompositorParent; + if (!compositor) { + return nullptr; + } + uint64_t rootLayerTreeId = compositor->RootLayerTreeId(); + CompositorParent::SetControllerForLayerTree(rootLayerTreeId, AndroidBridge::Bridge()); + sApzcTreeManager = CompositorParent::GetAPZCTreeManager(rootLayerTreeId); } + return sApzcTreeManager; } -AsyncPanZoomController* -nsWindow::GetPanZoomController() +uint64_t +nsWindow::RootLayerTreeId() { - return sApzc; + MOZ_ASSERT(sCompositorParent); + return sCompositorParent->RootLayerTreeId(); } - diff --git a/widget/android/nsWindow.h b/widget/android/nsWindow.h index 8979655ad217..bce79a77299e 100644 --- a/widget/android/nsWindow.h +++ b/widget/android/nsWindow.h @@ -23,7 +23,7 @@ namespace mozilla { class CompositorParent; class CompositorChild; class LayerManager; - class AsyncPanZoomController; + class APZCTreeManager; } } @@ -155,8 +155,9 @@ public: static void ScheduleResumeComposition(int width, int height); static void ForceIsFirstPaint(); static float ComputeRenderIntegrity(); - static void SetPanZoomController(mozilla::layers::AsyncPanZoomController* apzc); - static mozilla::layers::AsyncPanZoomController* GetPanZoomController(); + static mozilla::layers::APZCTreeManager* GetAPZCTreeManager(); + /* RootLayerTreeId() can only be called when GetAPZCTreeManager() returns non-null */ + static uint64_t RootLayerTreeId(); virtual bool WidgetPaintsBackground(); @@ -231,11 +232,11 @@ private: mozilla::AndroidLayerRendererFrame mLayerRendererFrame; + static nsRefPtr sApzcTreeManager; static nsRefPtr sLayerManager; static nsRefPtr sCompositorParent; static nsRefPtr sCompositorChild; static bool sCompositorPaused; - static nsRefPtr sApzc; }; #endif /* NSWINDOW_H_ */ diff --git a/widget/gonk/libdisplay/FramebufferSurface.cpp b/widget/gonk/libdisplay/FramebufferSurface.cpp index ea0ec72b4131..b41c46f487ef 100644 --- a/widget/gonk/libdisplay/FramebufferSurface.cpp +++ b/widget/gonk/libdisplay/FramebufferSurface.cpp @@ -29,7 +29,6 @@ #include #include -#include #include #include "FramebufferSurface.h" diff --git a/widget/gonk/libdisplay/GonkDisplayJB.cpp b/widget/gonk/libdisplay/GonkDisplayJB.cpp index ce826e92f550..11fb231587b6 100644 --- a/widget/gonk/libdisplay/GonkDisplayJB.cpp +++ b/widget/gonk/libdisplay/GonkDisplayJB.cpp @@ -14,7 +14,7 @@ */ #include "GonkDisplayJB.h" -#include +#include #include #include @@ -99,7 +99,7 @@ GonkDisplayJB::GonkDisplayJB() mAlloc = new GraphicBufferAlloc(); mFBSurface = new FramebufferSurface(0, mWidth, mHeight, surfaceformat, mAlloc); - sp stc = new SurfaceTextureClient(static_cast >(mFBSurface->getBufferQueue())); + sp stc = new Surface(static_cast >(mFBSurface->getBufferQueue())); mSTClient = stc; mList = (hwc_display_contents_1_t *)malloc(sizeof(*mList) + (sizeof(hwc_layer_1_t)*2)); @@ -166,8 +166,8 @@ GonkDisplayJB::SwapBuffers(EGLDisplay dpy, EGLSurface sur) StopBootAnimation(); mBootAnimBuffer = nullptr; - mList->dpy = dpy; - mList->sur = sur; + mList->outbuf = nullptr; + mList->outbufAcquireFenceFd = -1; eglSwapBuffers(dpy, sur); return Post(mFBSurface->lastHandle, mFBSurface->lastFenceFD); } @@ -189,8 +189,11 @@ GonkDisplayJB::Post(buffer_handle_t buf, int fence) mList->flags = HWC_GEOMETRY_CHANGED; mList->hwLayers[0].compositionType = HWC_BACKGROUND; mList->hwLayers[0].hints = 0; - mList->hwLayers[0].flags = 0; + /* Skip this layer so the hwc module doesn't complain about null handles */ + mList->hwLayers[0].flags = HWC_SKIP_LAYER; mList->hwLayers[0].backgroundColor = {0}; + /* hwc module checks displayFrame even though it shouldn't */ + mList->hwLayers[0].displayFrame = r; mList->hwLayers[1].compositionType = HWC_FRAMEBUFFER_TARGET; mList->hwLayers[1].hints = 0; mList->hwLayers[1].flags = 0; @@ -200,9 +203,10 @@ GonkDisplayJB::Post(buffer_handle_t buf, int fence) mList->hwLayers[1].sourceCrop = r; mList->hwLayers[1].displayFrame = r; mList->hwLayers[1].visibleRegionScreen.numRects = 1; - mList->hwLayers[1].visibleRegionScreen.rects = &mList->hwLayers[0].sourceCrop; + mList->hwLayers[1].visibleRegionScreen.rects = &mList->hwLayers[1].sourceCrop; mList->hwLayers[1].acquireFenceFd = fence; mList->hwLayers[1].releaseFenceFd = -1; + mList->hwLayers[1].planeAlpha = 0xFF; mHwc->prepare(mHwc, HWC_NUM_DISPLAY_TYPES, displays); int err = mHwc->set(mHwc, HWC_NUM_DISPLAY_TYPES, displays); mFBSurface->setReleaseFenceFd(mList->hwLayers[1].releaseFenceFd); diff --git a/widget/gonk/libdisplay/moz.build b/widget/gonk/libdisplay/moz.build index 960c3d26adc6..ea186b6d6552 100644 --- a/widget/gonk/libdisplay/moz.build +++ b/widget/gonk/libdisplay/moz.build @@ -20,7 +20,7 @@ CPP_SOURCES += [ 'BootAnimation.cpp', ] -if CONFIG['ANDROID_VERSION'] == '17': +if CONFIG['ANDROID_VERSION'] == '18': CPP_SOURCES += [ 'FramebufferSurface.cpp', 'GraphicBufferAlloc.cpp', diff --git a/widget/gonk/nsAppShell.cpp b/widget/gonk/nsAppShell.cpp index cdc084eb117f..00452d1a3693 100644 --- a/widget/gonk/nsAppShell.cpp +++ b/widget/gonk/nsAppShell.cpp @@ -52,6 +52,7 @@ #include "libui/EventHub.h" #include "libui/InputReader.h" #include "libui/InputDispatcher.h" +#include "cutils/properties.h" #include "GeckoProfiler.h" @@ -78,6 +79,9 @@ bool gDrawRequest = false; static nsAppShell *gAppShell = NULL; static int epollfd = 0; static int signalfds[2] = {0}; +static bool sDevInputAudioJack; +static int32_t sHeadphoneState; +static int32_t sMicrophoneState; NS_IMPL_ISUPPORTS_INHERITED1(nsAppShell, nsBaseAppShell, nsIObserver) @@ -245,6 +249,41 @@ maybeSendKeyEvent(int keyCode, bool pressed, uint64_t timeMs) } } +class SwitchEventRunnable : public nsRunnable { +public: + SwitchEventRunnable(hal::SwitchEvent& aEvent) : mEvent(aEvent) + {} + + NS_IMETHOD Run() + { + hal::NotifySwitchChange(mEvent); + return NS_OK; + } +private: + hal::SwitchEvent mEvent; +}; + +static void +updateHeadphoneSwitch() +{ + hal::SwitchEvent event; + + switch (sHeadphoneState) { + case AKEY_STATE_UP: + event.status() = hal::SWITCH_STATE_OFF; + break; + case AKEY_STATE_DOWN: + event.status() = sMicrophoneState == AKEY_STATE_DOWN ? + hal::SWITCH_STATE_HEADPHONE : hal::SWITCH_STATE_HEADSET; + break; + default: + return; + } + + event.device() = hal::SWITCH_HEADPHONES; + NS_DispatchToMainThread(new SwitchEventRunnable(event)); +} + class GeckoPointerController : public PointerControllerInterface { float mX; float mY; @@ -553,6 +592,19 @@ GeckoInputDispatcher::notifyMotion(const NotifyMotionArgs* args) void GeckoInputDispatcher::notifySwitch(const NotifySwitchArgs* args) { + if (!sDevInputAudioJack) + return; + + switch (args->switchCode) { + case SW_HEADPHONE_INSERT: + sHeadphoneState = args->switchValue; + updateHeadphoneSwitch(); + break; + case SW_MICROPHONE_INSERT: + sMicrophoneState = args->switchValue; + updateHeadphoneSwitch(); + break; + } } void GeckoInputDispatcher::notifyDeviceReset(const NotifyDeviceResetArgs* args) @@ -661,6 +713,12 @@ nsAppShell::Observe(nsISupports* aSubject, return nsBaseAppShell::Observe(aSubject, aTopic, aData); } + if (sDevInputAudioJack) { + sHeadphoneState = mReader->getSwitchState(-1, AINPUT_SOURCE_SWITCH, SW_HEADPHONE_INSERT); + sMicrophoneState = mReader->getSwitchState(-1, AINPUT_SOURCE_SWITCH, SW_MICROPHONE_INSERT); + updateHeadphoneSwitch(); + } + mEnableDraw = true; NotifyEvent(); return NS_OK; @@ -680,6 +738,12 @@ nsAppShell::Exit() void nsAppShell::InitInputDevices() { + char value[PROPERTY_VALUE_MAX]; + property_get("ro.moz.devinputjack", value, "0"); + sDevInputAudioJack = !strcmp(value, "1"); + sHeadphoneState = AKEY_STATE_UNKNOWN; + sMicrophoneState = AKEY_STATE_UNKNOWN; + mEventHub = new EventHub(); mReaderPolicy = new GeckoInputReaderPolicy(); mReaderPolicy->setDisplayInfo(); diff --git a/widget/windows/winrt/MetroWidget.cpp b/widget/windows/winrt/MetroWidget.cpp index db362f58f4cd..bd6dabf4e7d3 100644 --- a/widget/windows/winrt/MetroWidget.cpp +++ b/widget/windows/winrt/MetroWidget.cpp @@ -140,7 +140,7 @@ namespace { NS_IMPL_ISUPPORTS_INHERITED0(MetroWidget, nsBaseWidget) -nsRefPtr MetroWidget::sAPZC; +nsRefPtr MetroWidget::sAPZC; MetroWidget::MetroWidget() : mTransparencyMode(eTransparencyOpaque), @@ -166,7 +166,6 @@ MetroWidget::~MetroWidget() // Global shutdown if (!gInstanceCount) { - MetroWidget::sAPZC->Destroy(); MetroWidget::sAPZC = nullptr; nsTextStore::Terminate(); } // !gInstanceCount @@ -813,22 +812,8 @@ public: observerService->AddObserver(this, "viewport-needs-updating", false); } - if (MetroWidget::sAPZC) { - MetroWidget::sAPZC->SetCompositorParent(this); - } - } - - virtual void ShadowLayersUpdated(LayerTransactionParent* aLayerTree, const TargetConfig& aTargetConfig, - bool isFirstPaint) MOZ_OVERRIDE - { - CompositorParent::ShadowLayersUpdated(aLayerTree, aTargetConfig, isFirstPaint); - Layer* targetLayer = GetLayerManager()->GetPrimaryScrollableLayer(); - if (targetLayer && targetLayer->AsContainerLayer() && MetroWidget::sAPZC && - targetLayer->AsContainerLayer()->GetFrameMetrics().IsScrollable()) { - targetLayer->AsContainerLayer()->SetAsyncPanZoomController(MetroWidget::sAPZC); - MetroWidget::sAPZC->NotifyLayersUpdated(targetLayer->AsContainerLayer()->GetFrameMetrics(), - isFirstPaint); - } + CompositorParent::SetControllerForLayerTree(RootLayerTreeId(), aMetroWidget); + MetroWidget::sAPZC = CompositorParent::GetAPZCTreeManager(RootLayerTreeId()); } NS_IMETHODIMP Observe(nsISupports *subject, const char *topic, const PRUnichar *data) @@ -846,7 +831,6 @@ public: mozilla::gfx::Point(0.0f, 0.0f), mozilla::gfx::Point(0.0f, 0.0f), 0.0); - MetroWidget::sAPZC->NotifyLayersUpdated(frameMetrics, true); mMetroWidget->RequestContentRepaint(frameMetrics); } } @@ -910,10 +894,6 @@ MetroWidget::GetLayerManager(PLayerTransactionChild* aShadowManager, if (ShouldUseOffMainThreadCompositing()) { NS_ASSERTION(aShadowManager == nullptr, "Async Compositor not supported with e10s"); CreateCompositor(); - if (ShouldUseAPZC()) { - sAPZC = new AsyncPanZoomController(this, AsyncPanZoomController::USE_GESTURE_DETECTOR); - sAPZC->SetCompositorParent(mCompositorParent); - } } else if (ShouldUseMainThreadD3D10Manager()) { nsRefPtr layerManager = new mozilla::layers::LayerManagerD3D10(this); @@ -1457,7 +1437,7 @@ MetroWidget::HandleLongTap(const CSSIntPoint& aPoint) } void -MetroWidget::SendAsyncScrollDOMEvent(const CSSRect &aContentRect, const CSSSize &aScrollableSize) +MetroWidget::SendAsyncScrollDOMEvent(FrameMetrics::ViewID aScrollId, const CSSRect &aContentRect, const CSSSize &aScrollableSize) { LogFunction(); } diff --git a/widget/windows/winrt/MetroWidget.h b/widget/windows/winrt/MetroWidget.h index 163df024dc41..e008a05250cc 100644 --- a/widget/windows/winrt/MetroWidget.h +++ b/widget/windows/winrt/MetroWidget.h @@ -23,7 +23,7 @@ #endif #include "mozilla/layers/CompositorParent.h" #include "mozilla/layers/GeckoContentController.h" -#include "mozilla/layers/AsyncPanZoomController.h" +#include "mozilla/layers/APZCTreeManager.h" #include "mozilla/layers/LayerManagerComposite.h" #include "Units.h" #include "MetroInput.h" @@ -191,7 +191,7 @@ public: virtual void HandleDoubleTap(const mozilla::CSSIntPoint& aPoint); virtual void HandleSingleTap(const mozilla::CSSIntPoint& aPoint); virtual void HandleLongTap(const mozilla::CSSIntPoint& aPoint); - virtual void SendAsyncScrollDOMEvent(const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize); + virtual void SendAsyncScrollDOMEvent(mozilla::layers::FrameMetrics::ViewID aScrollId, const mozilla::CSSRect &aContentRect, const mozilla::CSSSize &aScrollableSize); virtual void PostDelayedTask(Task* aTask, int aDelayMs); virtual void HandlePanBegin(); virtual void HandlePanEnd(); @@ -237,5 +237,5 @@ protected: mozilla::layers::FrameMetrics mFrameMetrics; public: - static nsRefPtr sAPZC; + static nsRefPtr sAPZC; };