Merge fx-team to m-c on a CLOSED TREE.

This commit is contained in:
Ryan VanderMeulen 2013-10-30 22:34:54 -04:00
commit 41852ac68c
12 changed files with 283 additions and 183 deletions

View File

@ -306,7 +306,20 @@ TabTarget.prototype = {
this._client.connect((aType, aTraits) => {
this._client.listTabs(aResponse => {
this._root = aResponse;
this._form = aResponse.tabs[aResponse.selected];
let windowUtils = this.window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let outerWindow = windowUtils.outerWindowID;
aResponse.tabs.some((tab) => {
if (tab.outerWindowID === outerWindow) {
this._form = tab;
return true;
}
});
if (!this._form) {
this._form = aResponse.tabs[aResponse.selected];
}
attachTab();
});
});

View File

@ -643,8 +643,7 @@ static void RecordFrameMetrics(nsIFrame* aForFrame,
nsRect* aDisplayPort,
nsRect* aCriticalDisplayPort,
ViewID aScrollId,
const nsDisplayItem::ContainerParameters& aContainerParameters,
bool aMayHaveTouchListeners) {
const nsDisplayItem::ContainerParameters& aContainerParameters) {
nsPresContext* presContext = aForFrame->PresContext();
int32_t auPerDevPixel = presContext->AppUnitsPerDevPixel();
LayoutDeviceToLayerScale resolution(aContainerParameters.mXScale, aContainerParameters.mYScale);
@ -719,7 +718,16 @@ static void RecordFrameMetrics(nsIFrame* aForFrame,
metrics.mZoom = metrics.mCumulativeResolution * metrics.mDevPixelsPerCSSPixel
* layerToScreenScale;
metrics.mMayHaveTouchListeners = aMayHaveTouchListeners;
if (presShell) {
nsIDocument* document = nullptr;
document = presShell->GetDocument();
if (document) {
nsCOMPtr<nsPIDOMWindow> innerWin(document->GetInnerWindow());
if (innerWin) {
metrics.mMayHaveTouchListeners = innerWin->HasTouchEventListeners();
}
}
}
// Calculate the composition bounds as the size of the scroll frame and
// its origin relative to the reference frame.
@ -1248,14 +1256,6 @@ void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
}
}
bool mayHaveTouchListeners = false;
if (document) {
nsCOMPtr<nsPIDOMWindow> innerWin(document->GetInnerWindow());
if (innerWin) {
mayHaveTouchListeners = innerWin->HasTouchEventListeners();
}
}
nsRect viewport(aBuilder->ToReferenceFrame(aForFrame), aForFrame->GetSize());
RecordFrameMetrics(aForFrame, rootScrollFrame,
@ -1263,7 +1263,7 @@ void nsDisplayList::PaintForFrame(nsDisplayListBuilder* aBuilder,
root, mVisibleRect, viewport,
(usingDisplayport ? &displayport : nullptr),
(usingCriticalDisplayport ? &criticalDisplayport : nullptr),
id, containerParameters, mayHaveTouchListeners);
id, containerParameters);
if (usingDisplayport &&
!(root->GetContentFlags() & Layer::CONTENT_OPAQUE)) {
// See bug 693938, attachment 567017
@ -3576,7 +3576,7 @@ nsDisplayScrollLayer::BuildLayer(nsDisplayListBuilder* aBuilder,
mVisibleRect, viewport,
(usingDisplayport ? &displayport : nullptr),
(usingCriticalDisplayport ? &criticalDisplayport : nullptr),
scrollId, aContainerParameters, false);
scrollId, aContainerParameters);
return layer.forget();
}

View File

@ -168,6 +168,15 @@ abstract public class BrowserApp extends GeckoApp
private BrowserHealthReporter mBrowserHealthReporter;
// The tab to be selected on editing mode exit.
private Integer mTargetTabForEditingMode = null;
// The animator used to toggle HomePager visibility has a race where if the HomePager is shown
// (starting the animation), the HomePager is hidden, and the HomePager animation completes,
// both the web content and the HomePager will be hidden. This flag is used to prevent the
// race by determining if the web content should be hidden at the animation's end.
private boolean mHideWebContentOnAnimationEnd = false;
private SiteIdentityPopup mSiteIdentityPopup;
public SiteIdentityPopup getSiteIdentityPopup() {
@ -482,7 +491,11 @@ abstract public class BrowserApp extends GeckoApp
mBrowserToolbar.setOnStopEditingListener(new BrowserToolbar.OnStopEditingListener() {
public void onStopEditing() {
// Re-enable doorhanger notifications.
selectTargetTabForEditingMode();
hideHomePager();
hideBrowserSearch();
// Re-enable doorhanger notifications. They may trigger on the selected tab above.
mDoorHangerPopup.enable();
}
});
@ -1302,32 +1315,29 @@ abstract public class BrowserApp extends GeckoApp
return false;
}
// If this tab is already selected, just hide the home pager.
if (tabs.isSelectedTabId(tabId)) {
hideHomePager();
} else {
tabs.selectTab(tabId);
}
// Set the target tab to null so it does not get selected (on editing
// mode exit) in lieu of the tab we are about to select.
mTargetTabForEditingMode = null;
Tabs.getInstance().selectTab(tabId);
hideBrowserSearch();
mBrowserToolbar.cancelEdit();
return true;
}
private void openUrl(String url) {
openUrl(url, null, false);
private void openUrlAndStopEditing(String url) {
openUrlAndStopEditing(url, null, false);
}
private void openUrl(String url, boolean newTab) {
openUrl(url, null, newTab);
private void openUrlAndStopEditing(String url, boolean newTab) {
openUrlAndStopEditing(url, null, newTab);
}
private void openUrl(String url, String searchEngine) {
openUrl(url, searchEngine, false);
private void openUrlAndStopEditing(String url, String searchEngine) {
openUrlAndStopEditing(url, searchEngine, false);
}
private void openUrl(String url, String searchEngine, boolean newTab) {
private void openUrlAndStopEditing(String url, String searchEngine, boolean newTab) {
mBrowserToolbar.setProgressVisibility(true);
int flags = Tabs.LOADURL_NONE;
@ -1337,7 +1347,6 @@ abstract public class BrowserApp extends GeckoApp
Tabs.getInstance().loadUrl(url, searchEngine, -1, flags);
hideBrowserSearch();
mBrowserToolbar.cancelEdit();
}
@ -1420,6 +1429,9 @@ abstract public class BrowserApp extends GeckoApp
throw new IllegalArgumentException("Cannot handle null URLs in enterEditingMode");
}
final Tab selectedTab = Tabs.getInstance().getSelectedTab();
mTargetTabForEditingMode = (selectedTab != null ? selectedTab.getId() : null);
final PropertyAnimator animator = new PropertyAnimator(250);
animator.setUseHardwareLayer(false);
@ -1435,8 +1447,6 @@ abstract public class BrowserApp extends GeckoApp
}
final String url = mBrowserToolbar.commitEdit();
hideHomePager();
hideBrowserSearch();
// Don't do anything if the user entered an empty URL.
if (TextUtils.isEmpty(url)) {
@ -1513,13 +1523,13 @@ abstract public class BrowserApp extends GeckoApp
return false;
}
mBrowserToolbar.cancelEdit();
// Resetting the visibility of HomePager, which might have been hidden
// by the filterEditingMode().
// cancelEdit will call hideHomePager. If we're on web content, this is fine. If we're on
// about:home, the HomePager needs to be visible in the end (note that hideHomePager will
// not hide the HomePager on about:home). However, filterEditingMode may have hidden the
// HomePager so we set it visible here.
mHomePager.setVisibility(View.VISIBLE);
hideHomePager();
hideBrowserSearch();
mBrowserToolbar.cancelEdit();
return true;
}
@ -1535,6 +1545,22 @@ abstract public class BrowserApp extends GeckoApp
}
}
/**
* Selects the target tab for editing mode. This is expected to be the tab selected on editing
* mode entry, unless it is subsequently overridden.
*
* A background tab may be selected while editing mode is active (e.g. popups), causing the
* new url to load in the newly selected tab. Call this method on editing mode exit to
* mitigate this.
*/
private void selectTargetTabForEditingMode() {
if (mTargetTabForEditingMode != null) {
Tabs.getInstance().selectTab(mTargetTabForEditingMode);
}
mTargetTabForEditingMode = null;
}
/**
* Shows or hides the home pager for the given tab.
*/
@ -1578,7 +1604,38 @@ abstract public class BrowserApp extends GeckoApp
final ViewStub homePagerStub = (ViewStub) findViewById(R.id.home_pager_stub);
mHomePager = (HomePager) homePagerStub.inflate();
}
mHomePager.show(getSupportFragmentManager(), page, animator);
// Hide the web content so it cannot be focused by screen readers.
hideWebContentOnPropertyAnimationEnd(animator);
}
private void hideWebContentOnPropertyAnimationEnd(final PropertyAnimator animator) {
if (animator == null) {
hideWebContent();
return;
}
animator.addPropertyAnimationListener(new PropertyAnimator.PropertyAnimationListener() {
@Override
public void onPropertyAnimationStart() {
mHideWebContentOnAnimationEnd = true;
}
@Override
public void onPropertyAnimationEnd() {
if (mHideWebContentOnAnimationEnd) {
hideWebContent();
}
}
});
}
private void hideWebContent() {
// The view is set to INVISIBLE, rather than GONE, to avoid
// the additional requestLayout() call.
mLayerView.setVisibility(View.INVISIBLE);
}
private void hideHomePager() {
@ -1591,6 +1648,12 @@ abstract public class BrowserApp extends GeckoApp
return;
}
// Prevent race in hiding web content - see declaration for more info.
mHideWebContentOnAnimationEnd = false;
// Display the previously hidden web content (which prevented screen reader access).
mLayerView.setVisibility(View.VISIBLE);
if (mHomePager != null) {
mHomePager.hide();
}
@ -2297,7 +2360,7 @@ abstract public class BrowserApp extends GeckoApp
for (String url : urls) {
if (!maybeSwitchToTab(url, flags)) {
openUrl(url, true);
openUrlAndStopEditing(url, true);
}
}
}
@ -2306,7 +2369,7 @@ abstract public class BrowserApp extends GeckoApp
@Override
public void onUrlOpen(String url, EnumSet<OnUrlOpenListener.Flags> flags) {
if (!maybeSwitchToTab(url, flags)) {
openUrl(url);
openUrlAndStopEditing(url);
}
}
@ -2314,7 +2377,7 @@ abstract public class BrowserApp extends GeckoApp
@Override
public void onSearch(SearchEngine engine, String text) {
recordSearch(engine, "barsuggest");
openUrl(text, engine.name);
openUrlAndStopEditing(text, engine.name);
}
// BrowserSearch.OnEditSuggestionListener

View File

@ -410,6 +410,7 @@ public class BrowserToolbar extends GeckoRelativeLayout
}
if (keyCode == KeyEvent.KEYCODE_BACK) {
// Drop the virtual keyboard.
clearFocus();
return true;
}

View File

@ -61,6 +61,11 @@ public final class BitmapUtils {
return GeckoJarReader.getBitmapDrawable(context.getResources(), data);
}
// Don't attempt to validate the JAR signature when loading an add-on icon
if (data.startsWith("jar:file")) {
return GeckoJarReader.getBitmapDrawable(context.getResources(), Uri.decode(data));
}
URL url = new URL(data);
InputStream is = (InputStream) url.getContent();
try {

View File

@ -874,13 +874,13 @@ public class BrowserSearch extends HomeFragment
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
public boolean onTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
// Dismiss the soft keyboard.
requestFocus();
}
return super.onInterceptTouchEvent(event);
return super.onTouchEvent(event);
}
}
}

View File

@ -96,6 +96,13 @@ public class HomePager extends ViewPager {
// This is to keep all 4 pages in memory after they are
// selected in the pager.
setOffscreenPageLimit(3);
// We can call HomePager.requestFocus to steal focus from the URL bar and drop the soft
// keyboard. However, if there are no focusable views (e.g. an empty reading list), the
// URL bar will be refocused. Therefore, we make the HomePager container focusable to
// ensure there is always a focusable view. This would ordinarily be done via an XML
// attribute, but it is not working properly.
setFocusableInTouchMode(true);
}
@Override
@ -324,10 +331,7 @@ public class HomePager extends ViewPager {
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
// XXX: Drop the soft keyboard by stealing focus. Note that the HomePager (via XML
// attr) is focusable after its descendants allowing requestFocus to succeed and drop
// the soft keyboard even if there are no other focusable views on the screen (e.g.
// the Reading List is empty).
// Drop the soft keyboard by stealing focus from the URL bar.
requestFocus();
}

View File

@ -55,20 +55,25 @@
android:layout_alignParentBottom="true">
</RelativeLayout>
<org.mozilla.gecko.BrowserToolbar android:id="@+id/browser_toolbar"
<FrameLayout android:id="@+id/search_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@+id/browser_toolbar"
android:background="@android:color/white"
android:visibility="invisible"/>
<!-- When focus is cleared from from BrowserToolbar's EditText to
lower the virtual keyboard, focus will be returned to the root
view. To make sure the EditText is not the first focusable view in
the root view, BrowserToolbar should be specified as low in the
view hierarchy as possible. -->
<org.mozilla.gecko.BrowserToolbar android:id="@id/browser_toolbar"
style="@style/BrowserToolbar"
android:layout_width="fill_parent"
android:layout_height="@dimen/browser_toolbar_height"
android:clickable="true"
android:focusable="true"/>
<FrameLayout android:id="@+id/search_container"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@id/browser_toolbar"
android:background="@android:color/white"
android:visibility="invisible"/>
</view>
<LinearLayout android:id="@+id/toast"

View File

@ -583,10 +583,15 @@ BrowserTabActor.prototype = {
dbg_assert(this.actorID,
"tab should have an actorID.");
let windowUtils = this.window
.QueryInterface(Ci.nsIInterfaceRequestor)
.getInterface(Ci.nsIDOMWindowUtils);
let response = {
actor: this.actorID,
title: this.title,
url: this.url
url: this.url,
outerWindowID: windowUtils.outerWindowID
};
// Walk over tab actors added by extensions and add them to a new ActorPool.

View File

@ -30,7 +30,15 @@ function run_test() {
}
do_check_eq(gUpdateManager.activeUpdate.state, STATE_DOWNLOADING);
do_test_finished();
// Pause the download and reload the Update Manager with an empty update so
// the Application Update Service doesn't write the update xml files during
// xpcom-shutdown which will leave behind the test directory.
gAUS.pauseDownload();
writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), true);
writeUpdatesToXMLFile(getLocalUpdatesXMLString(""), false);
reloadUpdateManagerData();
do_timeout(TEST_CHECK_TIMEOUT, do_test_finished);
}
function end_test() {

View File

@ -440,6 +440,16 @@ MetroInput::InitTouchEventTouchList(WidgetTouchEvent* aEvent)
static_cast<void*>(&aEvent->touches));
}
bool
MetroInput::ShouldDeliverInputToRecognizer()
{
// If the event is destined for chrome deliver all events to the recognizer.
if (mChromeHitTestCacheForTouch) {
return true;
}
return mRecognizerWantsEvents;
}
// This event is raised when the user pushes the left mouse button, presses a
// pen to the surface, or presses a touch screen.
HRESULT
@ -483,44 +493,26 @@ MetroInput::OnPointerPressed(UI::Core::ICoreWindow* aSender,
// If this is the first touchstart of a touch session reset some
// tracking flags and dispatch the event with a custom callback
// so we can check preventDefault result.
mTouchStartDefaultPrevented = false;
mTouchMoveDefaultPrevented = false;
mContentConsumingTouch = false;
mRecognizerWantsEvents = true;
mIsFirstTouchMove = true;
mCancelable = true;
mTouchCancelSent = false;
InitTouchEventTouchList(touchEvent);
DispatchAsyncTouchEventWithCallback(touchEvent, &MetroInput::OnPointerPressedCallback);
} else {
InitTouchEventTouchList(touchEvent);
DispatchAsyncTouchEventIgnoreStatus(touchEvent);
mCanceledIds.Clear();
}
if (!mTouchStartDefaultPrevented) {
InitTouchEventTouchList(touchEvent);
DispatchAsyncTouchEvent(touchEvent);
if (ShouldDeliverInputToRecognizer()) {
mGestureRecognizer->ProcessDownEvent(currentPoint.Get());
}
return S_OK;
}
void
MetroInput::OnPointerPressedCallback()
{
nsEventStatus status = DeliverNextQueuedTouchEvent();
mTouchStartDefaultPrevented = (nsEventStatus_eConsumeNoDefault == status);
if (mTouchStartDefaultPrevented) {
// If content canceled the first touchstart don't generate any gesture based
// input - clear the recognizer state without sending any events.
mGestureRecognizer->CompleteGesture();
// Let the apz know content wants to consume touch events.
mWidget->ApzContentConsumingTouch();
}
}
void
MetroInput::AddPointerMoveDataToRecognizer(UI::Core::IPointerEventArgs* aArgs)
{
// Only feed move input to the recognizer if the first touchstart and
// subsequent touchmove return results were not eConsumeNoDefault.
if (!mTouchStartDefaultPrevented && !mTouchMoveDefaultPrevented) {
if (ShouldDeliverInputToRecognizer()) {
WRL::ComPtr<Foundation::Collections::IVector<UI::Input::PointerPoint*>>
pointerPoints;
aArgs->GetIntermediatePoints(pointerPoints.GetAddressOf());
@ -586,7 +578,7 @@ MetroInput::OnPointerMoved(UI::Core::ICoreWindow* aSender,
WidgetTouchEvent* touchEvent =
new WidgetTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get());
InitTouchEventTouchList(touchEvent);
DispatchAsyncTouchEventIgnoreStatus(touchEvent);
DispatchAsyncTouchEvent(touchEvent);
}
touch = CreateDOMTouch(currentPoint.Get());
@ -597,13 +589,10 @@ MetroInput::OnPointerMoved(UI::Core::ICoreWindow* aSender,
WidgetTouchEvent* touchEvent =
new WidgetTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get());
// If this is the first touch move of our session, we should check the result.
// Note we may lose some touch move data here for the recognizer since we want
// to wait until we have the result of the first touchmove dispatch. For gesture
// based events this shouldn't break anything.
// If this is the first touch move of our session, dispatch it now.
if (mIsFirstTouchMove) {
InitTouchEventTouchList(touchEvent);
DispatchAsyncTouchEventWithCallback(touchEvent, &MetroInput::OnFirstPointerMoveCallback);
DispatchAsyncTouchEvent(touchEvent);
mIsFirstTouchMove = false;
}
@ -612,22 +601,6 @@ MetroInput::OnPointerMoved(UI::Core::ICoreWindow* aSender,
return S_OK;
}
void
MetroInput::OnFirstPointerMoveCallback()
{
nsEventStatus status = DeliverNextQueuedTouchEvent();
mCancelable = false;
mTouchMoveDefaultPrevented = (nsEventStatus_eConsumeNoDefault == status);
// Let the apz know whether content wants to consume touch events
if (mTouchMoveDefaultPrevented) {
mWidget->ApzContentConsumingTouch();
// reset the recognizer
mGestureRecognizer->CompleteGesture();
} else if (!mTouchMoveDefaultPrevented && !mTouchStartDefaultPrevented) {
mWidget->ApzContentIgnoringTouch();
}
}
// This event is raised when the user lifts the left mouse button, lifts a
// pen from the surface, or lifts her/his finger from a touch screen.
HRESULT
@ -667,7 +640,7 @@ MetroInput::OnPointerReleased(UI::Core::ICoreWindow* aSender,
WidgetTouchEvent* touchEvent =
new WidgetTouchEvent(true, NS_TOUCH_MOVE, mWidget.Get());
InitTouchEventTouchList(touchEvent);
DispatchAsyncTouchEventIgnoreStatus(touchEvent);
DispatchAsyncTouchEvent(touchEvent);
}
// Remove this touch point from our map. Eventually all touch points are
@ -679,11 +652,9 @@ MetroInput::OnPointerReleased(UI::Core::ICoreWindow* aSender,
WidgetTouchEvent* touchEvent =
new WidgetTouchEvent(true, NS_TOUCH_END, mWidget.Get());
touchEvent->touches.AppendElement(CreateDOMTouch(currentPoint.Get()));
DispatchAsyncTouchEventIgnoreStatus(touchEvent);
DispatchAsyncTouchEvent(touchEvent);
// If content didn't cancel the first touchstart feed touchend data to the
// recognizer.
if (!mTouchStartDefaultPrevented && !mTouchMoveDefaultPrevented) {
if (ShouldDeliverInputToRecognizer()) {
mGestureRecognizer->ProcessUpEvent(currentPoint.Get());
}
@ -1179,7 +1150,7 @@ MetroInput::DeliverNextQueuedEventIgnoreStatus()
}
void
MetroInput::DispatchAsyncTouchEventIgnoreStatus(WidgetTouchEvent* aEvent)
MetroInput::DispatchAsyncTouchEvent(WidgetTouchEvent* aEvent)
{
aEvent->time = ::GetMessageTime();
mModifierKeyState.Update();
@ -1190,7 +1161,7 @@ MetroInput::DispatchAsyncTouchEventIgnoreStatus(WidgetTouchEvent* aEvent)
NS_DispatchToCurrentThread(runnable);
}
nsEventStatus
void
MetroInput::DeliverNextQueuedTouchEvent()
{
nsEventStatus status;
@ -1203,88 +1174,118 @@ MetroInput::DeliverNextQueuedTouchEvent()
/*
* We go through states here and make different decisions in each:
*
* 1) delivering first touchpoint touchstart or its first touchmove
* Our callers (OnFirstPointerMoveCallback, OnPointerPressedCallback) will
* check our result and set mTouchStartDefaultPrevented or
* mTouchMoveDefaultPrevented appropriately. Deliver touch events to the apz
* (ignoring return result) and to content and return the content event
* status result to our caller.
* 2) mTouchStartDefaultPrevented or mTouchMoveDefaultPrevented are true
* Deliver touch to content after transforming through the apz. Our callers
* handle calling cancel for the touch sequence on the apz.
* 3) mTouchStartDefaultPrevented and mTouchMoveDefaultPrevented are false
* Deliver events to the apz. If the apz returns eConsumeNoDefault dispatch
* a touchcancel to content and do not deliver any additional events there.
* (If the apz is doing something with the events we can save ourselves
* the overhead of delivering dom events.)
* 1) Hit test chrome on first touchstart
* If chrome is the target simplify event delivery from that point
* on by directing all input to chrome, bypassing the apz.
* 2) Process first touchpoint touchstart and touchmove
* Check the result and set mContentConsumingTouch appropriately. Deliver
* touch events to the apz (ignoring return result) and to content.
* 3) If mContentConsumingTouch is true: deliver touch to content after
* transforming through the apz. Also let the apz know content is
* consuming touch.
* 4) If mContentConsumingTouch is false: send a touchcancel to content
* and deliver all events to the apz. If the apz is doing something with
* the events we can save ourselves the overhead of delivering dom events.
*
* Notes:
* - never rely on the contents of mTouches here, since this is a delayed
* callback. mTouches will likely have been modified.
*/
// Test for chrome vs. content target. To do this we only use the first touch
// point since that will be the input batch target. Cache this for touch events
// since HitTestChrome has to send a dom event.
if (event->message == NS_TOUCH_START) {
if (mCancelable && event->message == NS_TOUCH_START) {
nsRefPtr<Touch> touch = event->touches[0];
LayoutDeviceIntPoint pt = LayoutDeviceIntPoint::FromUntyped(touch->mRefPoint);
bool apzIntersect = mWidget->HitTestAPZC(mozilla::ScreenPoint(pt.x, pt.y));
mChromeHitTestCacheForTouch = (apzIntersect && HitTestChrome(pt));
}
// Check if content called preventDefault on touchstart or first touchmove. If so
// and the event is destined for chrome, send the event. If destined for content,
// translate coordinates through the apz then send.
if (mTouchStartDefaultPrevented || mTouchMoveDefaultPrevented) {
if (!mChromeHitTestCacheForTouch) {
// ContentReceivedTouch has already been called so this shouldn't cause
// the apz to react. We still need to transform our coordinates though.
mWidget->ApzReceiveInputEvent(event);
}
// If this event is destined for chrome, deliver it directly there bypassing
// the apz.
if (!mCancelable && mChromeHitTestCacheForTouch) {
mWidget->DispatchEvent(event, status);
return status;
return;
}
// Forward event data to apz. If the apz consumes the event, don't forward to
// content if this is not a cancelable event.
WidgetTouchEvent transformedEvent(*event);
status = mWidget->ApzReceiveInputEvent(event, &transformedEvent);
if (!mCancelable && status == nsEventStatus_eConsumeNoDefault) {
if (!mTouchCancelSent) {
mTouchCancelSent = true;
DispatchTouchCancel();
// If we have yet to deliver the first touch start and touch move, deliver the
// event to both content and the apz. Ignore the apz's return result since we
// give content the option of saying it wants to consume touch for both events.
if (mCancelable) {
WidgetTouchEvent transformedEvent(*event);
mWidget->ApzReceiveInputEvent(event, &transformedEvent);
mWidget->DispatchEvent(mChromeHitTestCacheForTouch ? event : &transformedEvent, status);
if (event->message == NS_TOUCH_START) {
mContentConsumingTouch = (nsEventStatus_eConsumeNoDefault == status);
// Disable gesture based events (taps, swipes, rotation) if
// preventDefault is called on touchstart.
mRecognizerWantsEvents = !(nsEventStatus_eConsumeNoDefault == status);
} else if (event->message == NS_TOUCH_MOVE) {
mCancelable = false;
// Add this result to to our content comsuming flag
if (!mContentConsumingTouch) {
mContentConsumingTouch = (nsEventStatus_eConsumeNoDefault == status);
}
// Let the apz know if content wants to consume touch events, or cancel
// the touch block for content.
if (mContentConsumingTouch) {
mWidget->ApzContentConsumingTouch();
} else {
mWidget->ApzContentIgnoringTouch();
DispatchTouchCancel(&transformedEvent);
}
}
return status;
// If content is consuming touch don't generate any gesture based
// input - clear the recognizer state without sending any events.
if (!ShouldDeliverInputToRecognizer()) {
mGestureRecognizer->CompleteGesture();
}
return;
}
// Deliver event. If this is destined for chrome, use the untransformed event
// data, if it's destined for content, use the transformed event.
mWidget->DispatchEvent(!mChromeHitTestCacheForTouch ? &transformedEvent : event, status);
return status;
// If content called preventDefault on touchstart or first touchmove send
// the event to content.
if (mContentConsumingTouch) {
// ContentReceivedTouch has already been called in the mCancelable block
// above so this shouldn't cause the apz to react. We still need to
// transform our coordinates though.
mWidget->ApzReceiveInputEvent(event);
mWidget->DispatchEvent(event, status);
return;
}
// Forward event data to apz.
mWidget->ApzReceiveInputEvent(event);
}
void
MetroInput::DispatchTouchCancel()
MetroInput::DispatchTouchCancel(WidgetTouchEvent* aEvent)
{
LogFunction();
// From the spec: The touch point or points that were removed must be
// included in the changedTouches attribute of the TouchEvent, and must
// not be included in the touches and targetTouches attributes.
// (We are 'removing' all touch points that have been sent to content
// thus far.)
MOZ_ASSERT(aEvent);
// Send a touchcancel for each pointer id we have a corresponding start
// for. Note we can't rely on mTouches here since touchends remove points
// from it. The only time we end up in here is if the apz is consuming
// events, so this array shouldn't be very large.
WidgetTouchEvent touchEvent(true, NS_TOUCH_CANCEL, mWidget.Get());
InitTouchEventTouchList(&touchEvent);
mWidget->DispatchEvent(&touchEvent, sThrowawayStatus);
}
nsTArray< nsRefPtr<dom::Touch> >& touches = aEvent->touches;
for (uint32_t i = 0; i < touches.Length(); ++i) {
dom::Touch* touch = touches[i];
if (!touch) {
continue;
}
int32_t id = touch->Identifier();
if (mCanceledIds.Contains(id)) {
continue;
}
mCanceledIds.AppendElement(id);
touchEvent.touches.AppendElement(touch);
}
if (!touchEvent.touches.Length()) {
return;
}
void
MetroInput::DispatchAsyncTouchEventWithCallback(WidgetTouchEvent* aEvent,
void (MetroInput::*Callback)())
{
aEvent->time = ::GetMessageTime();
mModifierKeyState.Update();
mModifierKeyState.InitInputEvent(*aEvent);
mInputEventQueue.Push(aEvent);
nsCOMPtr<nsIRunnable> runnable =
NS_NewRunnableMethod(this, Callback);
NS_DispatchToCurrentThread(runnable);
mWidget->DispatchEvent(&touchEvent, sThrowawayStatus);
}
void

View File

@ -183,6 +183,7 @@ private:
uint32_t aMagEventType,
uint32_t aRotEventType);
uint16_t ProcessInputTypeForGesture(IEdgeGestureEventArgs* aArgs);
bool ShouldDeliverInputToRecognizer();
// The W3C spec states that "whether preventDefault has been called" should
// be tracked on a per-touchpoint basis, but it also states that touchstart
@ -207,11 +208,11 @@ private:
// events will be generated based on the touchstart and touchend events.
// For example, a set of mousemove, mousedown, and mouseup events might
// be sent if a tap is detected.
bool mTouchStartDefaultPrevented;
bool mTouchMoveDefaultPrevented;
bool mContentConsumingTouch;
bool mIsFirstTouchMove;
bool mCancelable;
bool mTouchCancelSent;
bool mRecognizerWantsEvents;
nsTArray<uint32_t> mCanceledIds;
// In the old Win32 way of doing things, we would receive a WM_TOUCH event
// that told us the state of every touchpoint on the touch surface. If
@ -273,21 +274,15 @@ private:
// Async event dispatching
void DispatchAsyncEventIgnoreStatus(WidgetInputEvent* aEvent);
void DispatchAsyncTouchEventIgnoreStatus(WidgetTouchEvent* aEvent);
void DispatchAsyncTouchEventWithCallback(WidgetTouchEvent* aEvent,
void (MetroInput::*Callback)());
void DispatchAsyncTouchEvent(WidgetTouchEvent* aEvent);
// Async event callbacks
void DeliverNextQueuedEventIgnoreStatus();
nsEventStatus DeliverNextQueuedTouchEvent();
// Misc. specialty async callbacks
void OnPointerPressedCallback();
void OnFirstPointerMoveCallback();
void DeliverNextQueuedTouchEvent();
// Sync event dispatching
void DispatchEventIgnoreStatus(WidgetGUIEvent* aEvent);
void DispatchTouchCancel();
void DispatchTouchCancel(WidgetTouchEvent* aEvent);
nsDeque mInputEventQueue;
static nsEventStatus sThrowawayStatus;