Skip to content

Commit

Permalink
fix(ripple): Re-flow logic to avoid crashing Edge (#2542)
Browse files Browse the repository at this point in the history
  • Loading branch information
kfranqueiro authored Apr 10, 2018
1 parent f6c1002 commit 4ca8925
Show file tree
Hide file tree
Showing 2 changed files with 57 additions and 14 deletions.
42 changes: 30 additions & 12 deletions packages/mdc-ripple/foundation.js
Original file line number Diff line number Diff line change
Expand Up @@ -330,25 +330,43 @@ class MDCRippleFoundation extends MDCFoundation {
this.registerDeactivationHandlers_(e);
}

activationState.wasElementMadeActive = this.checkElementMadeActive_(e);
if (activationState.wasElementMadeActive) {
this.animateActivation_();
}

requestAnimationFrame(() => {
// This needs to be wrapped in an rAF call b/c web browsers
// report active states inconsistently when they're called within
// event handling code:
// - https://bugs.chromium.org/p/chromium/issues/detail?id=635971
// - https://bugzilla.mozilla.org/show_bug.cgi?id=1293741
activationState.wasElementMadeActive = (e && e.type === 'keydown') ? this.adapter_.isSurfaceActive() : true;
if (activationState.wasElementMadeActive) {
this.animateActivation_();
} else {
// Reset array on next frame after the current event has had a chance to bubble to prevent ancestor ripples
activatedTargets = [];

if (!activationState.wasElementMadeActive && (e.key === ' ' || e.keyCode === 32)) {
// If space was pressed, try again within an rAF call to detect :active, because different UAs report
// active states inconsistently when they're called within event handling code:
// - https://bugs.chromium.org/p/chromium/issues/detail?id=635971
// - https://bugzilla.mozilla.org/show_bug.cgi?id=1293741
// We try first outside rAF to support Edge, which does not exhibit this problem, but will crash if a CSS
// variable is set within a rAF callback for a submit button interaction (#2241).
activationState.wasElementMadeActive = this.checkElementMadeActive_(e);
if (activationState.wasElementMadeActive) {
this.animateActivation_();
}
}

if (!activationState.wasElementMadeActive) {
// Reset activation state immediately if element was not made active.
this.activationState_ = this.defaultActivationState_();
}

// Reset array on next frame after the current event has had a chance to bubble to prevent ancestor ripples
activatedTargets = [];
});
}

/**
* @param {?Event} e
* @private
*/
checkElementMadeActive_(e) {
return (e && e.type === 'keydown') ? this.adapter_.isSurfaceActive() : true;
}

/**
* @param {?Event=} event Optional event containing position information.
*/
Expand Down
29 changes: 27 additions & 2 deletions test/unit/mdc-ripple/foundation-activation.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -176,17 +176,42 @@ testFoundation('sets FG position from the coords to the center within surface on
));
});

testFoundation('adds activation classes on keydown when surface is made active',
testFoundation('adds activation classes on keydown when surface is made active on same frame',
({foundation, adapter, mockRaf}) => {
const handlers = captureHandlers(adapter, 'registerInteractionHandler');
td.when(adapter.isSurfaceActive()).thenReturn(true);
foundation.init();
mockRaf.flush();

handlers.keydown();
td.verify(adapter.addClass(cssClasses.FG_ACTIVATION));
});

testFoundation('adds activation classes on keydown when surface only reflects :active on next frame for space keydown',
({foundation, adapter, mockRaf}) => {
const handlers = captureHandlers(adapter, 'registerInteractionHandler');
td.when(adapter.isSurfaceActive()).thenReturn(false, true);
foundation.init();
mockRaf.flush();

td.verify(adapter.addClass(cssClasses.FG_ACTIVATION));
handlers.keydown({key: ' '});
td.verify(adapter.addClass(cssClasses.FG_ACTIVATION), {times: 0});

mockRaf.flush();
td.verify(adapter.addClass(cssClasses.FG_ACTIVATION), {times: 1});
});

testFoundation('does not add activation classes on keydown when surface is not made active',
({foundation, adapter, mockRaf}) => {
const handlers = captureHandlers(adapter, 'registerInteractionHandler');
td.when(adapter.isSurfaceActive()).thenReturn(false, false);
foundation.init();
mockRaf.flush();

handlers.keydown({key: ' '});
mockRaf.flush();

td.verify(adapter.addClass(cssClasses.FG_ACTIVATION), {times: 0});
});

testFoundation('sets FG position to center on non-pointer activation', ({foundation, adapter, mockRaf}) => {
Expand Down

0 comments on commit 4ca8925

Please sign in to comment.