mirror of
https://github.com/roytam1/palemoon27.git
synced 2026-05-26 14:30:27 +00:00
09bea9b394
- Bug 1215921 - Range Analysis: Recover observable operands if they are recoverable. r=jandem,sunfish (5d0387ba66)
- Bug 1221923 - Remove pointless "initialized" static in VMFunction::addToFunctions; r=Waldo (fbf61fff54)
- Bug 12228397 - Bail out when dividing large unsigned integer by one. r=sunfish (cbd9d34bcf)
- Bug 1229292 - IonMonkey: MIPS64: Fix skip nops in conditional branches. r=huangwenjun06 (2244600084)
- Bug 1090957 - IonMonkey: MIPS: Implement atomics in MacroAssembler. r=lth (29399e4a33)
- Bug 1229989 - IonMonkey: MIPS: Fix patchCall for mips32. r=luke (1ae6f20686)
- Bug 1213747 - IonMonkey: MIPS: Import MIPS64 support into MoveEmitter-mips-shared. r=nbp f=rankov (16d46fe32e)
- Bug 1233863 - ARM64: Record the correct branch offset. r=sstangl (b2f940056e)
- Bug 1233884 - Remove unused CompactBufferWriters from MozBaseAssembler. r=jolesen (e37860be67)
- Bug 1233863 - ARM64: Avoid BumpSystemStackPointer(). r=sstangl (5c06e601c1)
- Bug 1229802 - Simplify MacroAssemblerARMCompat::decBranchPtr. r=nbp (a908c62e5b)
- bit of Bug 1217515 - Clean up the way touch event support is configured (2cfd0e4c65)
- Bug 1221459 - Remove SelectionStateChangedEvent. r=mtseng, r=smaug (9a827ab9fc)
- Bug 1209479 - Map Android back button to nsIWebNavigation::GoBack() for the current active frame r=fabrice (be41e7b8da)
- Bug 1165841 - Part 1: add window into nsINfcEventListener. r=dimi (475a56ab28)
- Bug 1165841 - Part 2: move window-related code to nsNfc.js. r=dimi (e2a0487b75)
- Bug 1165841 - Part 3: remove _rfState and init from NfcContentHelper. r=dimi (b8be95012d)
- Bug 1165841 - Part 4: using eventListeners. r=dimi (a22ff6d56c)
- Bug 1165841 - Part 5: remove listener when window is destroyed. r=dimi (48716e2ba1)
- Bug 1168292 - Part 1: update SYSTEM_APP_ID. r=dimi (ecdbbefb03)
- Bug 1168292 - Part 2: notify listener with correct tabId. r=dimi (4fb26c705e)
- Bug 1168292 - Part 3: add tabId in notifyUserAccepted. r=dimi (1b36a15b65)
- Bug 1170053 - [Aries] Not able to share a picture using NFC. r=yoshi (0b80df2866)
- Bug 1172159 - [NFC] Introduce getFocusTabId function. r=allstars.chh (b917d0dac7)
- Bug 1168704 - Rename setFocusApp in nslNfcContentHelper. r=yoshi (9918707442)
- more missing of Bug 1209479 (6480ccc315)
- Bug 1166210 - Remove ServicesSetting changes listener in NfcContentHelper. r=yoshi (6e8f388637)
- Bug 1121900 - [flatfish] build break by "error: undefined reference to 'mozilla::dom::MozIsoDepTech::mTechnology'". r=yoshi (b5aaaada08)
- Bug 976457 - B2G NFC: API for supporting NFC_A tags. r=yoshi r=smaug (b908a5d60a)
- Bug 1156208 - this.nfc.getErrorMessage is not a function. r=dimi (42e33fcfc9)
- Bug 1225701 - Add comments in TouchCaret and SelectionCarets for their obsolescence. r=mtseng (ba85234c0e)
- Bug 1210341 - (v3) Reduce accessiblecaret size. r=TYLin Bug 1221459 - Remove TouchCaret and SelectionCarets. r=mtseng, r=roc (ad6282ff96)
- Bug 1216986 - Fix usage of nsIURI.host in password manager and prompt code to support IPv6. r=dolske,liuche,kanru (cec90a6251)
- Bug 962249 part 1 - Add Element.scroll{Top,Left}Min (chrome-only) and convert most of scrollLeftMax uses and part of scrollTopMax uses to combinations with the new properties. r=roc,bz (13ed8ee58f)
- Bug 962249 part 3 - Add Window.scrollMin{X,Y} (chrome-only) and conve… (b9b6e6aa8f)
- Bug 918771 - Part 1: Move Fragment Serialization logic into nsContentUtils::SerializeNodeToMarkup, r=bz (865ea1d334)
- Bug 918771 - Part 2: Send text/html as MIME type for XHR send() of HTML document, r=bz (129b8d4115)
- Bug 918771 - Part 3: Update Web Platform tests to check for correct behavior, r=bz (ed0536f5b8)
- Bug 1085284 - Implement URLSearchParams iterable<>, r=bz (db1edd6d74)
- Bug 1224643. Fix handling of U+0000 in URLSearchParams serialization. r=baku (55856e5353)
- Bug 1224580. Copy of URLSearchParams should not copy its observer pointer. r=baku (1a5d859de5)
- Bug 1153145 - Ensure trusted focus events from web content focus plugins. r=smaug (d79fb35d4b)
- Bug 1183901, properly support WrapperCache on DistributedContentList, r=wchen (f15879c8ca)
- Bug 1188887: P1. Allow seeking when readyState is HAVE_NOTHING. r=jwwang (aa33721a44)
- Bug 1188887: P2. Add mochitest testing new seek behavior. r=jwwang (0520088343)
- Bug 1232223 - Part 6: Remove function prototype for undefined function IsAudioAPIEnabled(). r=jya (5126eb83ed)
- Bug 1232223 - Part 5: Run wave tests unconditionally. r=jya (668e6b9b77)
- Bug 1232223 - Part 1: Remove MOZ_WEBM #ifdefs because WebM is always supported, though not necessarily enabled. r=jya r=glandium (b3ec44f5bc)
- Bug 1232223 - Part 2: Remove MOZ_VPX #ifdefs because VPx is always supported, though not necessarily enabled. r=jya (c8853a16c4)
- Bug 1232223 - Part 3: Remove MOZ_WAVE #ifdefs because Wave is always supported. r=jya (e5c18f912c)
- Bug 1232223 - Part 4: Run webm tests unconditionally. r=jya (a81ab83b00)
- fix (9d9acad7f8)
- bug 1169640 - use nsIPrompt instead of nsIPromptService for insecure form submission r=mrbkap (1091d0d839)
- Bug 1231919 - crash in mozilla::a11y::DocAccessible::ValidateARIAOwned, r=davidb (3466d77e14)
- bug 1172538 - make sure a document has an IPC actor before shutting it down r=davidb, lsocks (823a447f7f)
- Bug 1147646 - Crash in mozilla::a11y::DocAccessible::ProcessContentInserted while stability testing, r=davidb (c9e4e7ef2f)
- Bug 1230118 - added asserts on aOwner and aOwner->Elm() r=surkov (94295ffd61)
- Bug 1213281 - crash in mozilla::a11y::DocAccessible::UpdateTreeOnInsertion, r=davidb (6442f927cd)
- bug 1196460 - add method to get wrapper of proxy for document containing this proxied accessible r=surkov (0cc7ff8758)
- Bug 1222174 - add override declarations in AccessibleWrap.h; r=tbsaunde (a8c1d0d3f3)
- bug 1187055 - check the proxy being destroyed has a wrapper before cleaning it up r=davidb (b73966446c)
- bug 1196460 - remove proxy's ids when they are destroyed r=surkov (3cbc9a8536)
- bug 1196372 - make GetHWNDFor() work with proxied accessibles r=surkov (28a6057913)
- Bug 1196460 - teach GetChildIDFor() to deal with proxied accessibles r=surkov (4746bfa128)
- bug 1199735 - remove event logging from the windows AccessibleWrap::HandleAccEvent r=davidb (869e18d13f)
- bug 1199735 - factor win event dispatch logic into its own function r=davidb (0a9704aa9d)
- bug 1196460 - create different proxy wrappers depending on the type of the proxy r=surkov (cd2d18ebdc)
- bug 1213327 - store interfaces a proxy implements on the Accessible wrapping it (406c9a3715)
- bug 1213606 - work around proxy's that don't have a wrapper for their document r=davidb (252a3ff2e7)
- bug 1192330 - update text change data for proxied text change events r=surkov (4deb371062)
- bug 1199735 - fire windows events on proxies r=davidb (6400387e71)
- bug 1186536 - Bail out of ProxyTextChangeEvent() if the proxy doesn't have a wrapper r=davidb (b40f69c77b)
- bug 1207862 - make WrapperFor() take a const ProxyAccessible * r=davidb (8e79dc6d75)
- bug 1213402 - fixup silly array , on windows r=me landing on a CLOSED TREE (33368af3af)
- bug 606080 - add class to generate unique 32 bit ids r=froydnj (ebf0888dcc)
- Bug 1231148 - Avoid requiring IDL files installed in $DIST/idl to run AccEventGen.py. r=ted (22078343ce)
- Bug 1229838 - rel=apple-touch-icon-precomposed should fire mozbrowsericonchange. r=fabrice (2a3e93da96)
- Bug 1231040 - Check for premulting better. - r=jrmuizel (811609bbb3)
- Bug 1224475 - Start agent after seeking. r=baku (a009b58f2c)
- Bug 1231557 - Use the URL Classifier in Media elements. r=cpearce,r=gcp (9f5503581d)
- bug 1228484 throw instead of capturing a second stream to a different graph r=roc (e762799424)
- Bug 1198435 - MediaElementTableCount now checks that the element is not present for URIs other than the expected one. r=rillian (838b1b886b)
- Bug 1224991 - Log format string mismatch the number of parameters. r=cpearce. (9cd2770bd2)
- Bug 1144409 - Encrypted event should be fired once per initData. r=cpearce (e50b380feb)
- Bug 1213589 part.1 Make ContentEventHandler::GetTextLength() and GetNativeTextLength() called only with a text node r=smaug (10955440a9)
- Bug 1213589 part.2 Clean up GenerateFlatTextContent(), GelerateFlatFontRanges() and GetFlatTextOffsetOfRange() of ContentEventHandler r=smaug (3f6bf987ff)
- Bug 1213589 part.3 ContentEventHandler::GetFlatTextLengthInRange() should handle specially when it's called by nsIMutationObserver::ContentRemoved() r=smaug (ed03499803)
- Bug 1213859 part.4 ContentEventHandler should use NS_NewPreContentIterator rathr than NS_NewContentIterator at converting between DOM tree and flattened text r=smaug (fe6e3b1114)
- Bug 1213589 part.5 Redesign the rules to create range in ContentEventHandler::SetRangeFromFlatTextOffset() r=smaug (edaa7baf87)
- Bug 1213589 part.6 ContentEventHandler should insert line breaks at open tag of elements except non-replaced inline elements r=smaug (c2e9cc11dc)
- Bug 1213589 part.7 Add new testcases to runSetSelectionEventTest() and runQueryTextContentEventTest() for checking the behavior with open tag r=smaug (bcea465e5e)
- Bug 1213589 part.8 When there are no nodes causing text, ContentEventHandler should set start of the editor root to start of the range r=smaug (a6d6b4d8d5)
- Bug 1213589 part.9 ContentEventHandler::ShouldBreakLineBefore() should return false if the content is unknown HTML element r=smaug (960936477f)
- Bug 1214164 - Don't honor all <option> descendants of <select>; r=bz (188bfe2b89)
- Bug 1228876. Correctly ignore nested optgroups even if an optgroup is inserted into another, existing, optgroup. r=mats (f2058c8b68)
- Bug 1193637 - Use IsMap() in HTMLImageElement::PreHandleEvent; r=mrbkap (6f2ef9da61)
- Bug 1160819 - ResponsiveImageSelector - improve some over-aggressive assertions (b5d40119ac)
- Bug 1233259, only in-document images should respond to viewport changes, r=mystor (eb448e258c)
- Bug 773429 - Remove dom.disable_image_src_set. r=khuey (fa06677726)
- Bug 1230110 - HTMLImageElement should call its superclass's DestroyContent(). r=smaug (bac1d12c65)
- Bug 1231245 - Fix logging of Matrix4x4. r=botond (f5c93b67cb)
- Bug 1208661 - Remove some no-longer-used debugging code. r=BenWa (2c86e3efd2)
- Bug 1228602 - Ensure that we pick up the presShell resolution from the layer if there isn't a metrics. r=botond (3c1835a022)
- Annotate FrameMetrics that are created for ScrollInfoLayers. (bug 1192919 part 1, r=kats) (5be0234333)
- Minimize the displayport for frames that must animate synchronously. (bug 1192919 part 2, r=kats) (5abc63e72d)
- Force a paint to be scheduled for scroll info layers. (bug 1192919 part 3, r=kats) (4ed61248c7)
- Bug 1141127 - When inside a slop area around the first touchstart, prevent touchmove events from going to content. r=botond (18fa91d74e)
- Bug 1174532 - Even if the APZC is not pannable, have a small slop area to consume touchmove events. r=botond (ff8e6f46fe)
- Bug 1208636 - Adjust displayport size based on available system memory. r=kats (d1ff9798b2)
- Bug 1232094 - Make some APZ prefs live. r=botond (d45207bc5b)
- Bug 1231168 - Disable memory-based displayport size adjustment by default. r=mchang (c17b445171)
- Turn apz.use_paint_duration off. (bug 1192919 part 4, r=kats) (dc094c9617)
- Bug 1170325 - Undo an unnecessary signature change to AsyncPanZoomAnimation::TakeDeferredTasks(). r=Waldo (62eafb38ef)
- Bug 1216355 - Ensure that if a repaint request is ignored due to a stale generation number the next repaint request doesn't get dropped. r=botond (c070172b48)
- Bug 1228407 - Rearrange some code by extracting a helper function. r=botond (ae53af7449)
- Bug 1228407 - Update existing smooth scroll animations with new destinations when possible, rather than canceling and restarting them. r=kip (c15022d920)
- Dispatch all APZ repaint requests immediately, and rely on content vsync. (bug 1192919 part 5, r=kats) (53ea2674c8)
- Bug 1230552 - Update some out-of-date comments and remove an old #undef. r=kats (16ba5a82e1)
- Bug 1230552 - Introduce a structure to group fling handoff state. r=kats (4f98893742)
- Bug 1230552 - Extend the immediate scroll handoff pref to apply to flings. r=kats (6b3d4fb0e5)
- Bug 1231228 - Correctly check for whether an APZC along the handoff chain is panned into overscroll. r=kats (bbaeb881af)
- Bug 1226920 - For instances of ForEachNode where we want to traverse the entire tree, use an action that returns void. r=botond (e42de54aea)
- Bug 1230552 - Make immediate scroll handoff for panning prefable. r=kats (89ddb8853a)
- Bug 1230552 - Fuzz a floating-point comparison to avoid an intermittent test failure. r=bustage (839bee076d)
- Bug 1231228 - Gtest. r=kats (e141ee97ef)
- Bug 1208973 - Move function bodies into .cpp file, and remove an unused one. r=botond (ef89b08738)
- Bug 1208973 - Refactor and add logging to TaskThrottler. r=botond (0a88091e20)
- Bug 1208973 - Ensure all code in TaskThrottler is threadsafe since it gets called from various threads. r=botond (9735621fd1)
- Bug 1213273 - Use a chromium Task instead of an nsITimer for the timeout in TaskThrottler. r=mstange (6cc9a9369e)
- Bug 1217251 - In TaskThrottler, dispatch the timeout task correctly from the Java UI thread on Android. r=kats (ee64d9458e)
- Bug 1230552 - Const-correctness improvements. r=kats (cc56f3404a)
- Bug 1230552 - Introduce a helper AsyncPanZoomController::CurrentInputBlock(). r=kats (4cef12a744)
- Remove the TaskThrottler. (bug 1192919 part 6, r=kats) (534312c146)
- Bug 1169695 - Advance the stored time when sampling the transform for a future frame. r=botod (3b08c07827)
- Bug 1169695 - Remove as many manual calls to spin the task queue as possible. r=botond (80c390ec7a)
- Bug 1218618 - Assert that the hit-testing tree's root node doesn't have siblings. r=kats (95ec1d07a7)
- Bug 1225178 - Don't start APZ drag if we don't have a displayport to handle it. r=kats (c29d4f962d)
- bit of End APZ wheel transactions when the mouse moves out of frame. (bug 1142866 part 2, r=kats,botond) (5a0c79d052)
- Bug 1226320 - Fix a refactoring in APZCTreeManager that didn't preserve semantics correctly. r=kats (35b92bd4a1)
679 lines
21 KiB
JavaScript
679 lines
21 KiB
JavaScript
/* -*- tab-width: 2; indent-tabs-mode: nil; js-indent-level: 2 -*- */
|
|
/* vim: set ts=2 sw=2 sts=2 et: */
|
|
|
|
/* 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/. */
|
|
|
|
"use strict";
|
|
dump("############################### browserElementPanningAPZDisabled.js loaded\n");
|
|
|
|
var { classes: Cc, interfaces: Ci, results: Cr, utils: Cu } = Components;
|
|
Cu.import("resource://gre/modules/Services.jsm");
|
|
Cu.import("resource://gre/modules/Geometry.jsm");
|
|
|
|
var global = this;
|
|
|
|
const ContentPanningAPZDisabled = {
|
|
// Are we listening to touch or mouse events?
|
|
watchedEventsType: '',
|
|
|
|
// Are mouse events being delivered to this content along with touch
|
|
// events, in violation of spec?
|
|
hybridEvents: false,
|
|
|
|
init: function cp_init() {
|
|
this._setupListenersForPanning();
|
|
},
|
|
|
|
_setupListenersForPanning: function cp_setupListenersForPanning() {
|
|
let events;
|
|
|
|
if (content.TouchEvent) {
|
|
events = ['touchstart', 'touchend', 'touchmove'];
|
|
this.watchedEventsType = 'touch';
|
|
#ifdef MOZ_WIDGET_GONK
|
|
// The gonk widget backend does not deliver mouse events per
|
|
// spec. Third-party content isn't exposed to this behavior,
|
|
// but that behavior creates some extra work for us here.
|
|
let appInfo = Cc["@mozilla.org/xre/app-info;1"];
|
|
let isParentProcess =
|
|
!appInfo || appInfo.getService(Ci.nsIXULRuntime)
|
|
.processType == Ci.nsIXULRuntime.PROCESS_TYPE_DEFAULT;
|
|
this.hybridEvents = isParentProcess;
|
|
#endif
|
|
} else {
|
|
// Touch events aren't supported, so fall back on mouse.
|
|
events = ['mousedown', 'mouseup', 'mousemove'];
|
|
this.watchedEventsType = 'mouse';
|
|
}
|
|
|
|
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));
|
|
},
|
|
|
|
handleEvent: function cp_handleEvent(evt) {
|
|
// Ignore events targeting an oop <iframe mozbrowser> since those will be
|
|
// handle by the BrowserElementPanning.js instance in the child process.
|
|
if (evt.target instanceof Ci.nsIMozBrowserFrame) {
|
|
return;
|
|
}
|
|
|
|
// For in-process <iframe mozbrowser> the events are not targetting
|
|
// directly the container iframe element, but some node of the document.
|
|
// So, the BrowserElementPanning instance of the system app will receive
|
|
// the sequence of touch events, as well as the BrowserElementPanning
|
|
// instance in the targetted app.
|
|
// As a result, multiple mozbrowser iframes will try to interpret the
|
|
// sequence of touch events, which may results into multiple clicks.
|
|
let targetWindow = evt.target.ownerDocument.defaultView;
|
|
let frameElement = targetWindow.frameElement;
|
|
while (frameElement) {
|
|
targetWindow = frameElement.ownerDocument.defaultView;
|
|
frameElement = targetWindow.frameElement;
|
|
}
|
|
|
|
if (content !== targetWindow) {
|
|
return;
|
|
}
|
|
|
|
if (evt.defaultPrevented || evt.multipleActionsPrevented) {
|
|
// clean up panning state even if touchend/mouseup has been preventDefault.
|
|
if(evt.type === 'touchend' || evt.type === 'mouseup') {
|
|
if (this.dragging &&
|
|
(this.watchedEventsType === 'mouse' ||
|
|
this.findPrimaryPointer(evt.changedTouches))) {
|
|
this._finishPanning();
|
|
}
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (evt.type) {
|
|
case 'mousedown':
|
|
case 'touchstart':
|
|
this.onTouchStart(evt);
|
|
break;
|
|
case 'mousemove':
|
|
case 'touchmove':
|
|
this.onTouchMove(evt);
|
|
break;
|
|
case 'mouseup':
|
|
case 'touchend':
|
|
this.onTouchEnd(evt);
|
|
break;
|
|
case 'click':
|
|
evt.stopPropagation();
|
|
evt.preventDefault();
|
|
|
|
let target = evt.target;
|
|
let view = target.ownerDocument ? target.ownerDocument.defaultView
|
|
: target;
|
|
view.removeEventListener('click', this, true, true);
|
|
break;
|
|
}
|
|
},
|
|
|
|
position: new Point(0 , 0),
|
|
|
|
findPrimaryPointer: function cp_findPrimaryPointer(touches) {
|
|
if (!('primaryPointerId' in this))
|
|
return null;
|
|
|
|
for (let i = 0; i < touches.length; i++) {
|
|
if (touches[i].identifier === this.primaryPointerId) {
|
|
return touches[i];
|
|
}
|
|
}
|
|
return null;
|
|
},
|
|
|
|
onTouchStart: function cp_onTouchStart(evt) {
|
|
let screenX, screenY;
|
|
if (this.watchedEventsType == 'touch') {
|
|
if ('primaryPointerId' in this || evt.touches.length >= 2) {
|
|
this._resetActive();
|
|
return;
|
|
}
|
|
|
|
let firstTouch = evt.changedTouches[0];
|
|
this.primaryPointerId = firstTouch.identifier;
|
|
this.pointerDownTarget = firstTouch.target;
|
|
screenX = firstTouch.screenX;
|
|
screenY = firstTouch.screenY;
|
|
} else {
|
|
this.pointerDownTarget = evt.target;
|
|
screenX = evt.screenX;
|
|
screenY = evt.screenY;
|
|
}
|
|
this.dragging = true;
|
|
this.panning = false;
|
|
|
|
let oldTarget = this.target;
|
|
[this.target, this.scrollCallback] = this.getPannable(this.pointerDownTarget);
|
|
|
|
// If we have a pointer down target, we may need to fill in for EventStateManager
|
|
// in setting the active state on the target element. Set a timer to
|
|
// ensure the pointer-down target is active. (If it's already
|
|
// active, the timer is a no-op.)
|
|
if (this.pointerDownTarget !== null) {
|
|
// If there's no possibility this is a drag/pan, activate now.
|
|
// Otherwise wait a little bit to see if the gesture isn't a
|
|
// tap.
|
|
if (this.target === null) {
|
|
this.notify(this._activationTimer);
|
|
} else {
|
|
this._activationTimer.initWithCallback(this,
|
|
this._activationDelayMs,
|
|
Ci.nsITimer.TYPE_ONE_SHOT);
|
|
}
|
|
}
|
|
|
|
// If there is a pan animation running (from a previous pan gesture) and
|
|
// the user touch back the screen, stop this animation immediatly and
|
|
// prevent the possible click action if the touch happens on the same
|
|
// target.
|
|
this.preventNextClick = false;
|
|
if (KineticPanning.active) {
|
|
KineticPanning.stop();
|
|
|
|
if (oldTarget && oldTarget == this.target)
|
|
this.preventNextClick = true;
|
|
}
|
|
|
|
this.position.set(screenX, screenY);
|
|
KineticPanning.reset();
|
|
KineticPanning.record(new Point(0, 0), evt.timeStamp);
|
|
|
|
// We prevent start events to avoid sending a focus event at the end of this
|
|
// touch series. See bug 889717.
|
|
if ((this.panning || this.preventNextClick)) {
|
|
evt.preventDefault();
|
|
}
|
|
},
|
|
|
|
onTouchEnd: function cp_onTouchEnd(evt) {
|
|
let touch = null;
|
|
if (!this.dragging ||
|
|
(this.watchedEventsType == 'touch' &&
|
|
!(touch = this.findPrimaryPointer(evt.changedTouches)))) {
|
|
return;
|
|
}
|
|
|
|
// !isPan() and evt.detail should always give the same answer here
|
|
// since they use the same heuristics, but use the native gecko
|
|
// computation when possible.
|
|
//
|
|
// NB: when we're using touch events, then !KineticPanning.isPan()
|
|
// => this.panning, so we'll never attempt to block the click
|
|
// event. That's OK however, because we won't fire a synthetic
|
|
// click when we're using touch events and this touch series
|
|
// wasn't a "tap" gesture.
|
|
let click = (this.watchedEventsType == 'mouse') ?
|
|
evt.detail : !KineticPanning.isPan();
|
|
// Additionally, if we're seeing non-compliant hybrid events, a
|
|
// "real" click will be generated if we started and ended on the
|
|
// same element.
|
|
if (this.hybridEvents) {
|
|
let target =
|
|
content.document.elementFromPoint(touch.clientX, touch.clientY);
|
|
click |= (target === this.pointerDownTarget);
|
|
}
|
|
|
|
if (this.target && click && (this.panning || this.preventNextClick)) {
|
|
if (this.hybridEvents) {
|
|
let target = this.target;
|
|
let view = target.ownerDocument ? target.ownerDocument.defaultView
|
|
: target;
|
|
view.addEventListener('click', this, true, true);
|
|
} else {
|
|
// We prevent end events to avoid sending a focus event. See bug 889717.
|
|
evt.preventDefault();
|
|
}
|
|
} else if (this.target && click && !this.panning) {
|
|
this.notify(this._activationTimer);
|
|
}
|
|
|
|
this._finishPanning();
|
|
|
|
// Now that we're done, avoid entraining the thing we just panned.
|
|
this.pointerDownTarget = null;
|
|
},
|
|
|
|
onTouchMove: function cp_onTouchMove(evt) {
|
|
if (!this.dragging)
|
|
return;
|
|
|
|
let screenX, screenY;
|
|
if (this.watchedEventsType == 'touch') {
|
|
let primaryTouch = this.findPrimaryPointer(evt.changedTouches);
|
|
if (evt.touches.length > 1 || !primaryTouch)
|
|
return;
|
|
screenX = primaryTouch.screenX;
|
|
screenY = primaryTouch.screenY;
|
|
} else {
|
|
screenX = evt.screenX;
|
|
screenY = evt.screenY;
|
|
}
|
|
|
|
let current = this.position;
|
|
let delta = new Point(screenX - current.x, screenY - current.y);
|
|
current.set(screenX, screenY);
|
|
|
|
KineticPanning.record(delta, evt.timeStamp);
|
|
|
|
let isPan = KineticPanning.isPan();
|
|
|
|
// If we've detected a pan gesture, cancel the active state of the
|
|
// current target.
|
|
if (!this.panning && isPan) {
|
|
this._resetActive();
|
|
}
|
|
|
|
// There's no possibility of us panning anything.
|
|
if (!this.scrollCallback) {
|
|
return;
|
|
}
|
|
|
|
// Scroll manually.
|
|
this.scrollCallback(delta.scale(-1));
|
|
|
|
if (!this.panning && isPan) {
|
|
this.panning = true;
|
|
this._activationTimer.cancel();
|
|
}
|
|
|
|
if (this.panning) {
|
|
// Only do this when we're actually executing a pan gesture.
|
|
// Otherwise synthetic mouse events will be canceled.
|
|
evt.stopPropagation();
|
|
evt.preventDefault();
|
|
}
|
|
},
|
|
|
|
// nsITimerCallback
|
|
notify: function cp_notify(timer) {
|
|
this._setActive(this.pointerDownTarget);
|
|
},
|
|
|
|
onKineticBegin: function cp_onKineticBegin(evt) {
|
|
},
|
|
|
|
onKineticPan: function cp_onKineticPan(delta) {
|
|
return !this.scrollCallback(delta);
|
|
},
|
|
|
|
onKineticEnd: function cp_onKineticEnd() {
|
|
if (!this.dragging)
|
|
this.scrollCallback = null;
|
|
},
|
|
|
|
getPannable: function cp_getPannable(node) {
|
|
let pannableNode = this._findPannable(node);
|
|
if (pannableNode) {
|
|
return [pannableNode, this._generateCallback(pannableNode)];
|
|
}
|
|
|
|
return [null, null];
|
|
},
|
|
|
|
_findPannable: function cp_findPannable(node) {
|
|
if (!(node instanceof Ci.nsIDOMHTMLElement) || node.tagName == 'HTML') {
|
|
return null;
|
|
}
|
|
|
|
let nodeContent = node.ownerDocument.defaultView;
|
|
while (!(node instanceof Ci.nsIDOMHTMLBodyElement)) {
|
|
let style = nodeContent.getComputedStyle(node, null);
|
|
|
|
let overflow = [style.getPropertyValue('overflow'),
|
|
style.getPropertyValue('overflow-x'),
|
|
style.getPropertyValue('overflow-y')];
|
|
|
|
let rect = node.getBoundingClientRect();
|
|
let isAuto = (overflow.indexOf('auto') != -1 &&
|
|
(rect.height < node.scrollHeight ||
|
|
rect.width < node.scrollWidth));
|
|
|
|
let isScroll = (overflow.indexOf('scroll') != -1);
|
|
|
|
let isScrollableTextarea = (node.tagName == 'TEXTAREA' &&
|
|
(node.scrollHeight > node.clientHeight ||
|
|
node.scrollWidth > node.clientWidth ||
|
|
('scrollLeftMin' in node && 'scrollLeftMax' in node &&
|
|
node.scrollLeftMin != node.scrollLeftMax) ||
|
|
('scrollTopMin' in node && 'scrollTopMax' in node &&
|
|
node.scrollTopMin != node.scrollTopMax)));
|
|
if (isScroll || isAuto || isScrollableTextarea) {
|
|
return node;
|
|
}
|
|
|
|
node = node.parentNode;
|
|
}
|
|
|
|
if (nodeContent.scrollMaxX != nodeContent.scrollMinX ||
|
|
nodeContent.scrollMaxY != nodeContent.scrollMinY) {
|
|
return nodeContent;
|
|
}
|
|
|
|
if (nodeContent.frameElement) {
|
|
return this._findPannable(nodeContent.frameElement);
|
|
}
|
|
|
|
return null;
|
|
},
|
|
|
|
_generateCallback: function cp_generateCallback(root) {
|
|
let firstScroll = true;
|
|
let target;
|
|
let current;
|
|
let win, doc, htmlNode, bodyNode;
|
|
|
|
function doScroll(node, delta) {
|
|
if (node instanceof Ci.nsIDOMHTMLElement) {
|
|
return node.scrollByNoFlush(delta.x, delta.y);
|
|
} else if (node instanceof Ci.nsIDOMWindow) {
|
|
win = node;
|
|
doc = win.document;
|
|
|
|
// "overflow:hidden" on either the <html> or the <body> node should
|
|
// prevent the user from scrolling the root viewport.
|
|
if (doc instanceof Ci.nsIDOMHTMLDocument) {
|
|
htmlNode = doc.documentElement;
|
|
bodyNode = doc.body;
|
|
if (win.getComputedStyle(htmlNode, null).overflowX == "hidden" ||
|
|
win.getComputedStyle(bodyNode, null).overflowX == "hidden") {
|
|
delta.x = 0;
|
|
}
|
|
if (win.getComputedStyle(htmlNode, null).overflowY == "hidden" ||
|
|
win.getComputedStyle(bodyNode, null).overflowY == "hidden") {
|
|
delta.y = 0;
|
|
}
|
|
}
|
|
let oldX = node.scrollX;
|
|
let oldY = node.scrollY;
|
|
node.scrollBy(delta.x, delta.y);
|
|
return (node.scrollX != oldX || node.scrollY != oldY);
|
|
}
|
|
// If we get here, |node| isn't an HTML element and it's not a window,
|
|
// but findPannable apparently thought it was scrollable... What is it?
|
|
return false;
|
|
}
|
|
|
|
function targetParent(node) {
|
|
return node.parentNode || node.frameElement || null;
|
|
}
|
|
|
|
function scroll(delta) {
|
|
current = root;
|
|
firstScroll = true;
|
|
while (current) {
|
|
if (doScroll(current, delta)) {
|
|
firstScroll = false;
|
|
return true;
|
|
}
|
|
|
|
// TODO The current code looks for possible scrolling regions only if
|
|
// this is the first scroll action but this should be more dynamic.
|
|
if (!firstScroll) {
|
|
return false;
|
|
}
|
|
|
|
current = ContentPanningAPZDisabled._findPannable(targetParent(current));
|
|
}
|
|
|
|
// There is nothing scrollable here.
|
|
return false;
|
|
}
|
|
return scroll;
|
|
},
|
|
|
|
get _activationTimer() {
|
|
delete this._activationTimer;
|
|
return this._activationTimer = Cc["@mozilla.org/timer;1"]
|
|
.createInstance(Ci.nsITimer);
|
|
},
|
|
|
|
get _activationDelayMs() {
|
|
let delay = Services.prefs.getIntPref('ui.touch_activation.delay_ms');
|
|
delete this._activationDelayMs;
|
|
return this._activationDelayMs = delay;
|
|
},
|
|
|
|
get _domUtils() {
|
|
delete this._domUtils;
|
|
return this._domUtils = Cc['@mozilla.org/inspector/dom-utils;1']
|
|
.getService(Ci.inIDOMUtils);
|
|
},
|
|
|
|
_resetActive: function cp_resetActive() {
|
|
let elt = this.pointerDownTarget || this.target;
|
|
let root = elt.ownerDocument || elt.document;
|
|
this._setActive(root.documentElement);
|
|
},
|
|
|
|
_setActive: function cp_setActive(elt) {
|
|
const kStateActive = 0x00000001;
|
|
this._domUtils.setContentState(elt, kStateActive);
|
|
},
|
|
|
|
_finishPanning: function() {
|
|
this.dragging = false;
|
|
delete this.primaryPointerId;
|
|
this._activationTimer.cancel();
|
|
|
|
// If there is a scroll action, let's do a manual kinetic panning action.
|
|
if (this.panning) {
|
|
KineticPanning.start(this);
|
|
}
|
|
},
|
|
};
|
|
|
|
// Min/max velocity of kinetic panning. This is in pixels/millisecond.
|
|
const kMinVelocity = 0.2;
|
|
const kMaxVelocity = 6;
|
|
|
|
// Constants that affect the "friction" of the scroll pane.
|
|
const kExponentialC = 1000;
|
|
const kPolynomialC = 100 / 1000000;
|
|
|
|
// How often do we change the position of the scroll pane?
|
|
// Too often and panning may jerk near the end.
|
|
// Too little and panning will be choppy. In milliseconds.
|
|
const kUpdateInterval = 16;
|
|
|
|
// The numbers of momentums to use for calculating the velocity of the pan.
|
|
// Those are taken from the end of the action
|
|
const kSamples = 5;
|
|
|
|
const KineticPanning = {
|
|
_position: new Point(0, 0),
|
|
_velocity: new Point(0, 0),
|
|
_acceleration: new Point(0, 0),
|
|
|
|
get active() {
|
|
return this.target !== null;
|
|
},
|
|
|
|
target: null,
|
|
start: function kp_start(target) {
|
|
this.target = target;
|
|
|
|
// Calculate the initial velocity of the movement based on user input
|
|
let momentums = this.momentums;
|
|
let flick = momentums[momentums.length - 1].time - momentums[0].time < 300;
|
|
|
|
let distance = new Point(0, 0);
|
|
momentums.forEach(function(momentum) {
|
|
distance.add(momentum.dx, momentum.dy);
|
|
});
|
|
|
|
function clampFromZero(x, min, max) {
|
|
if (x >= 0)
|
|
return Math.max(min, Math.min(max, x));
|
|
return Math.min(-min, Math.max(-max, x));
|
|
}
|
|
|
|
let elapsed = momentums[momentums.length - 1].time - momentums[0].time;
|
|
let velocityX = clampFromZero(distance.x / elapsed, 0, kMaxVelocity);
|
|
let velocityY = clampFromZero(distance.y / elapsed, 0, kMaxVelocity);
|
|
|
|
let velocity = this._velocity;
|
|
if (flick) {
|
|
// Very fast pan action that does not generate a click are very often pan
|
|
// action. If this is a small gesture then it will not move the view a lot
|
|
// and so it will be above the minimun threshold and not generate any
|
|
// kinetic panning. This does not look on a device since this is often
|
|
// a real gesture, so let's lower the velocity threshold for such moves.
|
|
velocity.set(velocityX, velocityY);
|
|
} else {
|
|
velocity.set(Math.abs(velocityX) < kMinVelocity ? 0 : velocityX,
|
|
Math.abs(velocityY) < kMinVelocity ? 0 : velocityY);
|
|
}
|
|
this.momentums = [];
|
|
|
|
// Set acceleration vector to opposite signs of velocity
|
|
function sign(x) {
|
|
return x ? (x > 0 ? 1 : -1) : 0;
|
|
}
|
|
|
|
this._acceleration.set(velocity.clone().map(sign).scale(-kPolynomialC));
|
|
|
|
// Reset the position
|
|
this._position.set(0, 0);
|
|
|
|
this._startAnimation();
|
|
|
|
this.target.onKineticBegin();
|
|
},
|
|
|
|
stop: function kp_stop() {
|
|
this.reset();
|
|
|
|
if (!this.target)
|
|
return;
|
|
|
|
this.target.onKineticEnd();
|
|
this.target = null;
|
|
},
|
|
|
|
reset: function kp_reset() {
|
|
this.momentums = [];
|
|
this.distance.set(0, 0);
|
|
},
|
|
|
|
momentums: [],
|
|
record: function kp_record(delta, timestamp) {
|
|
this.momentums.push({ 'time': this._getTime(timestamp),
|
|
'dx' : delta.x, 'dy' : delta.y });
|
|
|
|
// We only need to keep kSamples in this.momentums.
|
|
if (this.momentums.length > kSamples) {
|
|
this.momentums.shift();
|
|
}
|
|
|
|
this.distance.add(delta.x, delta.y);
|
|
},
|
|
|
|
_getTime: function kp_getTime(time) {
|
|
// Touch events generated by the platform or hand-made are defined in
|
|
// microseconds instead of milliseconds. Bug 77992 will fix this at the
|
|
// platform level.
|
|
if (time > Date.now()) {
|
|
return Math.floor(time / 1000);
|
|
} else {
|
|
return time;
|
|
}
|
|
},
|
|
|
|
get threshold() {
|
|
let dpi = content.QueryInterface(Ci.nsIInterfaceRequestor)
|
|
.getInterface(Ci.nsIDOMWindowUtils)
|
|
.displayDPI;
|
|
|
|
let threshold = Services.prefs.getIntPref('ui.dragThresholdX') / 240 * dpi;
|
|
|
|
delete this.threshold;
|
|
return this.threshold = threshold;
|
|
},
|
|
|
|
distance: new Point(0, 0),
|
|
isPan: function cp_isPan() {
|
|
return (Math.abs(this.distance.x) > this.threshold ||
|
|
Math.abs(this.distance.y) > this.threshold);
|
|
},
|
|
|
|
_startAnimation: function kp_startAnimation() {
|
|
let c = kExponentialC;
|
|
function getNextPosition(position, v, a, t) {
|
|
// Important traits for this function:
|
|
// p(t=0) is 0
|
|
// p'(t=0) is v0
|
|
//
|
|
// We use exponential to get a smoother stop, but by itself exponential
|
|
// is too smooth at the end. Adding a polynomial with the appropriate
|
|
// weight helps to balance
|
|
position.set(v.x * Math.exp(-t / c) * -c + a.x * t * t + v.x * c,
|
|
v.y * Math.exp(-t / c) * -c + a.y * t * t + v.y * c);
|
|
}
|
|
|
|
let startTime = content.performance.now();
|
|
let elapsedTime = 0, targetedTime = 0, averageTime = 0;
|
|
|
|
let velocity = this._velocity;
|
|
let acceleration = this._acceleration;
|
|
|
|
let position = this._position;
|
|
let nextPosition = new Point(0, 0);
|
|
let delta = new Point(0, 0);
|
|
|
|
let callback = (function(timestamp) {
|
|
if (!this.target)
|
|
return;
|
|
|
|
// To make animation end fast enough but to keep smoothness, average the
|
|
// ideal time frame (smooth animation) with the actual time lapse
|
|
// (end fast enough).
|
|
// Animation will never take longer than 2 times the ideal length of time.
|
|
elapsedTime = timestamp - startTime;
|
|
targetedTime += kUpdateInterval;
|
|
averageTime = (targetedTime + elapsedTime) / 2;
|
|
|
|
// Calculate new position.
|
|
getNextPosition(nextPosition, velocity, acceleration, averageTime);
|
|
delta.set(Math.round(nextPosition.x - position.x),
|
|
Math.round(nextPosition.y - position.y));
|
|
|
|
// Test to see if movement is finished for each component.
|
|
if (delta.x * acceleration.x > 0)
|
|
delta.x = position.x = velocity.x = acceleration.x = 0;
|
|
|
|
if (delta.y * acceleration.y > 0)
|
|
delta.y = position.y = velocity.y = acceleration.y = 0;
|
|
|
|
if (velocity.equals(0, 0) || delta.equals(0, 0)) {
|
|
this.stop();
|
|
return;
|
|
}
|
|
|
|
position.add(delta);
|
|
if (this.target.onKineticPan(delta.scale(-1))) {
|
|
this.stop();
|
|
return;
|
|
}
|
|
|
|
content.requestAnimationFrame(callback);
|
|
}).bind(this);
|
|
|
|
content.requestAnimationFrame(callback);
|
|
}
|
|
};
|