Skip to content

fix(cdk/overlay): ensure error isn't thrown when many overlay items are closed from handler #20377

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Aug 28, 2020

Conversation

andy9775
Copy link
Contributor

When an event handler listening to the outsidePointerEvents observable closes out any open
overlays this removes them from the OverlayOutsideClickDispatcher's array of overlay elements. This
results in an error being thrown since the overlays array in the for-loop is modified (elements are
removed) and the counter not accounting for this. This fix ensures that if the array of overlay
refs is modified we don't get an off by 1 error when fetching an element from the array on
subsequent iterations of the loop.

@googlebot googlebot added the cla: yes PR author has agreed to Google's Contributor License Agreement label Aug 20, 2020
@andy9775 andy9775 requested a review from teflonwaffles August 20, 2020 22:38
const backgroundElement = document.createElement('div');
document.body.appendChild(backgroundElement);

expect(fakeAsync(() => backgroundElement.click())).not.toThrowError();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the fakeAsync supposed to be around the entire test?

// We copy the array because the original may be modified asynchronously if the
// outsidePointerEvents listener decides to detach overlays resulting in index errors inside
// the for loop.
const overlays = this._attachedOverlays.slice();
Copy link
Member

@crisbeto crisbeto Aug 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would prevent the error, but doesn't it have the potential of firing off the event for an overlay that's already closed? It may not be a problem in problem in practice since the Subject gets completed, but it still seems smelly.

Looking through the loop again, shouldn't we be breaking off as soon as we find an overlay to emit to? @jelbourn I was under the impression that we want to emit to the top overlay that has subscribers since that's how the keyboard dispatcher works too, but this will emit to multiple overlays. I think the OverlayKeyboardDispatcher is doing it correctly.

It isn't something changed by this PR, but we may have to address it at some point before people start depending on it.

Copy link
Contributor Author

@andy9775 andy9775 Aug 21, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It may fire off on an already closed overlay. An alternative to this would be to also check overlayRef.hasAttached() and continue to the next one if it has nothing. Or modifying the loop to consider mutations to the list when decrementing the counter and not copying the list - but this didn't seem ideal either.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure about stopping at the first overlay (worth a follow-up discussion), but it does seem that we should check that the overlay is not already closed before emitting.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IMO we should be doing it, because the primary use case for this dispatcher is closing when the user clicks outside of an overlay. If we were to dispatch to all overlays, it would mean that when the user has multiple dialogs open at the same time, clicking on the top one will close everything else.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suspect different use-cases would call for one or the other


expect(spy).toHaveBeenCalled();

backgroundElement.parentNode!.removeChild(backgroundElement);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this go in an afterEach block in a separate describe block, if it needs to be run even if the test fails?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The jasmine tests don't exit on a failed expect so the closing logic will run regardless.

Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM on this iteration; we can discuss other change to the overlay as a follow-up if necessary

@jelbourn jelbourn added action: merge The PR is ready for merge by the caretaker target: patch This PR is targeted for the next patch release labels Aug 26, 2020
@andy9775 andy9775 force-pushed the cdk-overlay-fix-for-loop branch from 7bad7a3 to 80e6254 Compare August 26, 2020 20:01
…re closed from handler

When an event handler listening to the `outsidePointerEvents` observable closes out any open
overlays this removes them from the OverlayOutsideClickDispatcher's array of overlay elements. This
results in an error being thrown since the overlays array in the for-loop is modified (elements are
removed) and the counter not accounting for this. This fix ensures that if the array of overlay
refs is modified we don't get an off by 1 error when fetching an element from the array on
subsequent iterations of the loop.
@andy9775 andy9775 force-pushed the cdk-overlay-fix-for-loop branch from 80e6254 to 6bf0c04 Compare August 26, 2020 20:10
Copy link
Member

@jelbourn jelbourn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@jelbourn jelbourn merged commit 1d0e3c7 into angular:master Aug 28, 2020
jelbourn pushed a commit that referenced this pull request Aug 28, 2020
…re closed from handler (#20377)

When an event handler listening to the `outsidePointerEvents` observable closes out any open
overlays this removes them from the OverlayOutsideClickDispatcher's array of overlay elements. This
results in an error being thrown since the overlays array in the for-loop is modified (elements are
removed) and the counter not accounting for this. This fix ensures that if the array of overlay
refs is modified we don't get an off by 1 error when fetching an element from the array on
subsequent iterations of the loop.

(cherry picked from commit 1d0e3c7)
@andy9775 andy9775 deleted the cdk-overlay-fix-for-loop branch August 28, 2020 15:15
annieyw pushed a commit to annieyw/components that referenced this pull request Aug 31, 2020
…re closed from handler (angular#20377)

When an event handler listening to the `outsidePointerEvents` observable closes out any open
overlays this removes them from the OverlayOutsideClickDispatcher's array of overlay elements. This
results in an error being thrown since the overlays array in the for-loop is modified (elements are
removed) and the counter not accounting for this. This fix ensures that if the array of overlay
refs is modified we don't get an off by 1 error when fetching an element from the array on
subsequent iterations of the loop.
mmalerba pushed a commit that referenced this pull request Sep 2, 2020
…re closed from handler (#20377)

When an event handler listening to the `outsidePointerEvents` observable closes out any open
overlays this removes them from the OverlayOutsideClickDispatcher's array of overlay elements. This
results in an error being thrown since the overlays array in the for-loop is modified (elements are
removed) and the counter not accounting for this. This fix ensures that if the array of overlay
refs is modified we don't get an off by 1 error when fetching an element from the array on
subsequent iterations of the loop.
@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Sep 28, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
action: merge The PR is ready for merge by the caretaker cla: yes PR author has agreed to Google's Contributor License Agreement target: patch This PR is targeted for the next patch release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants