Skip to content

Commit

Permalink
🐛 Guard against getBoundingClientRect to prevent unspecified errors i…
Browse files Browse the repository at this point in the history
…n IE. (ampproject#20731)

* Guard against `Element.getBoundingClientRect`.

* Refactor solution.

* Check if element exists in the DOM.

* Fix solution using `Node.isConnected`.

* Correct the default value of the desired DOMRect

* Patch getBoundingClientRect for unsupported browsers. Alternative solution

* Refactor patch.

* Fix - use assigned variable rect, previously unused.

* Fix imports.

* Minor fixes.

* Make the return type of getBoundingClientRect be consistent with usages

* File overview comment and a minor fix.
  • Loading branch information
delima02 authored and William Chou committed Feb 14, 2019
1 parent e9acbd2 commit a36ee5d
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 0 deletions.
72 changes: 72 additions & 0 deletions src/get-bounding-client-rect.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/**
* Copyright 2016 The AMP HTML Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/**
* @fileoverview
* IE 10 throws "Unspecified error" when calling getBoundingClientRect() on a
* disconnected node.
* @see https://developer.microsoft.com/en-us/microsoft-edge/platform/issues/106812/
*/

import {
LayoutRectDef,
layoutRectLtwh,
} from './layout-rect';
import {isConnectedNode} from './dom';

const nativeClientRect = Element.prototype.getBoundingClientRect;

/**
* Polyfill for Node.getBoundingClientRect API.
* @param {!Element} el
* @return {!ClientRect|!LayoutRectDef}
*/
function getBoundingClientRect(el) {
if (isConnectedNode(el)) {
return nativeClientRect.call(el);
}

return layoutRectLtwh(0, 0, 0, 0);
}

/**
* Determines if this polyfill should be installed.
* @param {!Window} win
* @return {boolean}
*/
function shouldInstall(win) {
try {
const div = win.document.createElement('div');
const rect = div./*OK*/getBoundingClientRect();
return rect.top !== 0;
} catch (e) {
// IE 10 or less
return true;
}
}

/**
* Sets the getBoundingClientRect polyfill if using IE 10 or an
* earlier version.
* @param {!Window} win
*/
export function install(win) {
if (shouldInstall(win)) {
win.Object.defineProperty(win.Element.prototype, 'getBoundingClientRect', {
value: getBoundingClientRect,
});
}
}
4 changes: 4 additions & 0 deletions src/polyfills.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ import {
} from './polyfills/domtokenlist-toggle';
import {install as installDocContains} from './polyfills/document-contains';
import {install as installFetch} from './polyfills/fetch';
import {
install as installGetBoundingClientRect,
} from './get-bounding-client-rect';
import {install as installMathSign} from './polyfills/math-sign';
import {install as installObjectAssign} from './polyfills/object-assign';
import {install as installObjectValues} from './polyfills/object-values';
Expand All @@ -38,6 +41,7 @@ installObjectValues(self);
installPromise(self);
installDocContains(self);
installArrayIncludes(self);
installGetBoundingClientRect(self);
// isExperimentOn() must be called after Object.assign polyfill is installed.
// TODO(jridgewell): Ship custom-elements-v1. For now, we use this hack so it
// is DCE'd from production builds.
Expand Down

0 comments on commit a36ee5d

Please sign in to comment.