Skip to content
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

Popover: Fix issue with undefined getBoundingClientRect #27445

Merged

Conversation

sirreal
Copy link
Member

@sirreal sirreal commented Dec 2, 2020

Description

Fix an issue where the Popover component throws a runtime TypeError: Cannot read property 'getBoundingClientRect' of undefined. on this line:

const topRect = top.getBoundingClientRect();

This may happen in WordPress if a Popover is rendered in an iframe. It's been observed by rendering a BlockList component containing a RichText component in an iframe 😵

This is the problematic section:

if ( anchorRef !== false ) {
if (
! anchorRef ||
! window.Range ||
! window.Element ||
! window.DOMRect
) {
return;
}
if ( anchorRef instanceof window.Range ) {
return getRectangleFromRange( anchorRef );
}
if ( anchorRef instanceof window.Element ) {
const rect = anchorRef.getBoundingClientRect();
if ( shouldAnchorIncludePadding ) {
return rect;
}
return withoutPadding( rect, anchorRef );
}

In the issue I've observed, anchorRef is an Element on line 78, but it's not an instance of window.Element. It's an instance of anchorRef.ownerDocument.defaultView.Element 😮

Both of the conditional blocks are skipped and we end up calling anchorRef.top.getBoundingClientRect() on an Element. This triggers the error:

const { top, bottom } = anchorRef;
const topRect = top.getBoundingClientRect();
const bottomRect = bottom.getBoundingClientRect();

You can verify this is problematic by running the following in a recent browser at about:blank:

var iframe = document.createElement('iframe');
iframe.srcdoc = '<div></div>';
document.body.appendChild(iframe);

// Don't just paste this in a single command, we need to let the append complete…

var div = iframe.contentDocument.querySelector('div');

console.log( "Instance of window's Element? %o", div instanceof window.Element ); // False!!! 💥
console.log( "Instance of its iframe's Element? %o", div instanceof div?.ownerDocument.defaultView.Element ); // True 😎👍

The issue would apparently begin to manifest after #25332. The iframed issue described above worked with Gutenberg 9.3 but began to throw errors in 9.4.

How has this been tested?

Appears to fix the issue where observed.

Popovers should continue to work in normal usage.

Types of changes

Bug fix

Checklist:

  • My code is tested.
  • My code follows the WordPress code style.
  • My code follows the accessibility standards.
  • My code has proper inline documentation.
  • I've included developer documentation if appropriate.
  • I've updated all React Native files affected by any refactorings/renamings in this PR.

@sirreal sirreal added [Package] Components /packages/components [Type] Bug An existing feature does not function as intended [Type] Regression Related to a regression in the latest release labels Dec 2, 2020
@github-actions
Copy link

github-actions bot commented Dec 2, 2020

Size Change: +7 B (0%)

Total Size: 1.19 MB

Filename Size Change
build/components/index.js 172 kB +7 B (0%)
ℹ️ View Unchanged
Filename Size Change
build/a11y/index.js 1.14 kB 0 B
build/annotations/index.js 3.8 kB 0 B
build/api-fetch/index.js 3.42 kB 0 B
build/autop/index.js 2.84 kB 0 B
build/blob/index.js 665 B 0 B
build/block-directory/index.js 8.72 kB 0 B
build/block-directory/style-rtl.css 943 B 0 B
build/block-directory/style.css 942 B 0 B
build/block-editor/index.js 128 kB 0 B
build/block-editor/style-rtl.css 11.2 kB 0 B
build/block-editor/style.css 11.2 kB 0 B
build/block-library/editor-rtl.css 8.88 kB 0 B
build/block-library/editor.css 8.88 kB 0 B
build/block-library/index.js 148 kB 0 B
build/block-library/style-rtl.css 8.34 kB 0 B
build/block-library/style.css 8.34 kB 0 B
build/block-library/theme-rtl.css 789 B 0 B
build/block-library/theme.css 790 B 0 B
build/block-serialization-default-parser/index.js 1.88 kB 0 B
build/block-serialization-spec-parser/index.js 3.06 kB 0 B
build/blocks/index.js 48.1 kB 0 B
build/components/style-rtl.css 15.3 kB 0 B
build/components/style.css 15.3 kB 0 B
build/compose/index.js 9.95 kB 0 B
build/core-data/index.js 14.8 kB 0 B
build/data-controls/index.js 827 B 0 B
build/data/index.js 8.98 kB 0 B
build/date/index.js 11.2 kB 0 B
build/deprecated/index.js 768 B 0 B
build/dom-ready/index.js 571 B 0 B
build/dom/index.js 4.95 kB 0 B
build/edit-navigation/index.js 11.1 kB 0 B
build/edit-navigation/style-rtl.css 881 B 0 B
build/edit-navigation/style.css 885 B 0 B
build/edit-post/index.js 306 kB 0 B
build/edit-post/style-rtl.css 6.42 kB 0 B
build/edit-post/style.css 6.4 kB 0 B
build/edit-site/index.js 24.1 kB 0 B
build/edit-site/style-rtl.css 4.06 kB 0 B
build/edit-site/style.css 4.06 kB 0 B
build/edit-widgets/index.js 26.3 kB 0 B
build/edit-widgets/style-rtl.css 3.13 kB 0 B
build/edit-widgets/style.css 3.13 kB 0 B
build/editor/editor-styles-rtl.css 476 B 0 B
build/editor/editor-styles.css 478 B 0 B
build/editor/index.js 43.2 kB 0 B
build/editor/style-rtl.css 3.85 kB 0 B
build/editor/style.css 3.85 kB 0 B
build/element/index.js 4.63 kB 0 B
build/escape-html/index.js 735 B 0 B
build/format-library/index.js 6.86 kB 0 B
build/format-library/style-rtl.css 547 B 0 B
build/format-library/style.css 548 B 0 B
build/hooks/index.js 2.27 kB 0 B
build/html-entities/index.js 623 B 0 B
build/i18n/index.js 3.56 kB 0 B
build/is-shallow-equal/index.js 698 B 0 B
build/keyboard-shortcuts/index.js 2.54 kB 0 B
build/keycodes/index.js 1.94 kB 0 B
build/list-reusable-blocks/index.js 3.1 kB 0 B
build/list-reusable-blocks/style-rtl.css 476 B 0 B
build/list-reusable-blocks/style.css 476 B 0 B
build/media-utils/index.js 5.32 kB 0 B
build/notices/index.js 1.82 kB 0 B
build/nux/index.js 3.42 kB 0 B
build/nux/style-rtl.css 671 B 0 B
build/nux/style.css 668 B 0 B
build/plugins/index.js 2.56 kB 0 B
build/primitives/index.js 1.43 kB 0 B
build/priority-queue/index.js 791 B 0 B
build/redux-routine/index.js 2.84 kB 0 B
build/reusable-blocks/index.js 2.92 kB 0 B
build/rich-text/index.js 13.4 kB 0 B
build/server-side-render/index.js 2.77 kB 0 B
build/shortcode/index.js 1.69 kB 0 B
build/token-list/index.js 1.27 kB 0 B
build/url/index.js 2.84 kB 0 B
build/viewport/index.js 1.86 kB 0 B
build/warning/index.js 1.14 kB 0 B
build/wordcount/index.js 1.22 kB 0 B

compressed-size-action

@sirreal sirreal self-assigned this Dec 2, 2020
@sirreal sirreal marked this pull request as ready for review December 2, 2020 16:56
@noahtallen
Copy link
Member

Oddly enough, the e2e failures do seem to be related 🤔

@sirreal sirreal marked this pull request as draft December 2, 2020 19:00
@sirreal sirreal added the [Status] In Progress Tracking issues with work in progress label Dec 2, 2020
@sirreal sirreal force-pushed the fix/popover-anchor-undefined-get-bounding-client-rect branch from fb14e14 to 92f0164 Compare December 2, 2020 19:11
@sirreal
Copy link
Member Author

sirreal commented Dec 2, 2020

Oddly enough, the e2e failures do seem to be related 🤔

Whoops! The same fix doesn't work for Range although it could be susceptible to the same issue. Fixed!

@sirreal sirreal marked this pull request as ready for review December 2, 2020 19:12
@sirreal sirreal removed the [Status] In Progress Tracking issues with work in progress label Dec 2, 2020
@ockham
Copy link
Contributor

ockham commented Dec 2, 2020

Can we add a unit test for the scenario you're describing in the PR desc to packages/components/src/popover/test/index.js?

@ellatrix
Copy link
Member

ellatrix commented Dec 2, 2020

When is it throwing this error? We haven't iframed the content yet?

@ockham
Copy link
Contributor

ockham commented Dec 2, 2020

When is it throwing this error? We haven't iframed the content yet?

This seems to be happening in an environment where we iframe the editor, and use a popover for page template selection (a custom feature on our platform).

if ( anchorRef instanceof window.Element ) {
if (
anchorRef?.ownerDocument &&
anchorRef instanceof anchorRef.ownerDocument.defaultView.Element
Copy link
Member

Choose a reason for hiding this comment

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

Maybe we can just check for .getBoundingClientRect?

Copy link
Member Author

Choose a reason for hiding this comment

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

That would probably be more direct 🙂

@ockham
Copy link
Contributor

ockham commented Dec 2, 2020

If we decide that this fixes the issue, and if there will be a 9.5.1 to fix the 'Schedule...' button label issue -- can we include this PR also in that patch release? cc/ @ntsekouras @jorgefilipecosta

@@ -75,7 +75,10 @@ function computeAnchorRect(
return getRectangleFromRange( anchorRef );
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't the above be changed too? Could potentially check for .endContainer.

Copy link
Member Author

Choose a reason for hiding this comment

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

Yes, I was looking at that and I think the range check is susceptible as well. I'll push the change in a moment.

@sirreal sirreal force-pushed the fix/popover-anchor-undefined-get-bounding-client-rect branch from 0724a15 to 08c4ff3 Compare December 2, 2020 21:24
@sirreal
Copy link
Member Author

sirreal commented Dec 2, 2020

I added a check for Range that should work across the iframe boundary as well and used duck-typing (check for methods) to discover Range and Element.

@sirreal sirreal requested a review from ellatrix December 2, 2020 21:36
Copy link
Member

@noahtallen noahtallen left a comment

Choose a reason for hiding this comment

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

Code looks code to me 👍

@sirreal sirreal merged commit bff5eac into master Dec 3, 2020
@sirreal sirreal deleted the fix/popover-anchor-undefined-get-bounding-client-rect branch December 3, 2020 13:38
@github-actions github-actions bot added this to the Gutenberg 9.6 milestone Dec 3, 2020
ellatrix pushed a commit that referenced this pull request Dec 7, 2020
Fix an issue where the `Popover` component throws a runtime `TypeError: Cannot read property 'getBoundingClientRect' of undefined.`

This may happen in WordPress if a Popover is rendered in an iframe. It's been observed by rendering a `BlockList` component containing `RichText` in an iframe 😵

The issue is that `instanceof` checks fail across iframe boundaries, where `anchorRef instanceof window.Element` fails because `anchorRef` when it _is_ an instance of Element in the frame but not `window.Element`.

Instead of `instanceof` checks that fail across iframe boundaries, check for expected methods as the predicate.
WunderBart pushed a commit that referenced this pull request Dec 8, 2020
Fix an issue where the `Popover` component throws a runtime `TypeError: Cannot read property 'getBoundingClientRect' of undefined.`

This may happen in WordPress if a Popover is rendered in an iframe. It's been observed by rendering a `BlockList` component containing `RichText` in an iframe 😵

The issue is that `instanceof` checks fail across iframe boundaries, where `anchorRef instanceof window.Element` fails because `anchorRef` when it _is_ an instance of Element in the frame but not `window.Element`.

Instead of `instanceof` checks that fail across iframe boundaries, check for expected methods as the predicate.
@WunderBart WunderBart mentioned this pull request Dec 8, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
[Package] Components /packages/components [Type] Bug An existing feature does not function as intended [Type] Regression Related to a regression in the latest release
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants