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

feat(env): support iframe with relative window/document #8897

Merged
merged 14 commits into from
May 8, 2023
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## [next]

- feat(env): relative window/document, support iframe [#8897](https://github.com/fabricjs/fabric.js/pull/8897)
- docs(): add repo repro link to `bug_report.yml` [#8900](https://github.com/fabricjs/fabric.js/pull/8900)
- refactor(fabric.Line): Line position is calculated from the center between the 2 points now [#8877](https://github.com/fabricjs/fabric.js/pull/8877)
- chore(Path, Polyline): Clean up old SVG import code [#8857](https://github.com/fabricjs/fabric.js/pull/8857)

Expand Down
2 changes: 1 addition & 1 deletion fabric.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export { getEnv, getDocument, getWindow, setEnv } from './src/env';
export { getEnv, getFabricDocument, getFabricWindow, setEnv } from './src/env';
export { cache } from './src/cache';
export { VERSION as version, iMatrix } from './src/constants';
export { config } from './src/config';
Expand Down
14 changes: 7 additions & 7 deletions src/canvas/Canvas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import type { IText } from '../shapes/IText/IText';
import type { FabricObject } from '../shapes/Object/FabricObject';
import { AssertKeys } from '../typedefs';
import { isTouchEvent, stopEvent } from '../util/dom_event';
import { getElementDocument, getElementWindow } from '../util/dom_misc';
import { getDocumentFromElement, getWindowFromElement } from '../util/dom_misc';
import { sendPointToPlane } from '../util/misc/planeChange';
import {
isFabricObjectWithDragSupport,
Expand Down Expand Up @@ -165,7 +165,7 @@ export class Canvas extends SelectableCanvas {
addOrRemove(functor: any, eventjsFunctor: 'add' | 'remove') {
const canvasElement = this.upperCanvasEl,
eventTypePrefix = this._getEventPrefix();
functor(getElementWindow(canvasElement), 'resize', this._onResize);
functor(getWindowFromElement(canvasElement), 'resize', this._onResize);
functor(canvasElement, eventTypePrefix + 'down', this._onMouseDown);
functor(
canvasElement,
Expand Down Expand Up @@ -207,7 +207,7 @@ export class Canvas extends SelectableCanvas {
this.addOrRemove(removeListener, 'remove');
// if you dispose on a mouseDown, before mouse up, you need to clean document to...
const eventTypePrefix = this._getEventPrefix();
const doc = getElementDocument(this.upperCanvasEl);
const doc = getDocumentFromElement(this.upperCanvasEl);
removeListener(
doc,
`${eventTypePrefix}up`,
Expand Down Expand Up @@ -615,7 +615,7 @@ export class Canvas extends SelectableCanvas {
this._resetTransformEventData();
const canvasElement = this.upperCanvasEl,
eventTypePrefix = this._getEventPrefix();
const doc = getElementDocument(canvasElement);
const doc = getDocumentFromElement(canvasElement);
addListener(
doc,
'touchend',
Expand Down Expand Up @@ -651,7 +651,7 @@ export class Canvas extends SelectableCanvas {
this._onMouseMove as EventListener,
addEventOptions
);
const doc = getElementDocument(canvasElement);
const doc = getDocumentFromElement(canvasElement);
addListener(doc, `${eventTypePrefix}up`, this._onMouseUp as EventListener);
addListener(
doc,
Expand All @@ -674,7 +674,7 @@ export class Canvas extends SelectableCanvas {
this._resetTransformEventData();
this.mainTouchId = null;
const eventTypePrefix = this._getEventPrefix();
const doc = getElementDocument(this.upperCanvasEl);
const doc = getDocumentFromElement(this.upperCanvasEl);
removeListener(
doc,
'touchend',
Expand Down Expand Up @@ -712,7 +712,7 @@ export class Canvas extends SelectableCanvas {
const canvasElement = this.upperCanvasEl,
eventTypePrefix = this._getEventPrefix();
if (this._isMainEvent(e)) {
const doc = getElementDocument(this.upperCanvasEl);
const doc = getDocumentFromElement(this.upperCanvasEl);
removeListener(
doc,
`${eventTypePrefix}up`,
Expand Down
4 changes: 2 additions & 2 deletions src/canvas/SelectableCanvas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getDocument, getEnv } from '../env';
import { getFabricDocument, getEnv } from '../env';
import { dragHandler } from '../controls/drag';
import { getActionFromCorner } from '../controls/util';
import { Point } from '../Point';
Expand Down Expand Up @@ -1219,7 +1219,7 @@ export class SelectableCanvas<
}

protected _initWrapperElement() {
const container = getDocument().createElement('div');
const container = getFabricDocument().createElement('div');
container.classList.add(this.containerClass);
this.wrapperEl = wrapElement(this.lowerCanvasEl, container);
this.wrapperEl.setAttribute('data-fabric', 'wrapper');
Expand Down
6 changes: 2 additions & 4 deletions src/canvas/StaticCanvas.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getDocument, getEnv } from '../env';
import { getFabricDocument, getEnv } from '../env';
import { config } from '../config';
import { iMatrix, VERSION } from '../constants';
import type { CanvasEvents, StaticCanvasEvents } from '../EventTypeDefs';
Expand Down Expand Up @@ -56,8 +56,6 @@ export type TDestroyedCanvas<T extends StaticCanvas> = TDestroyed<
| '_activeSelection'
>;

const CANVAS_INIT_ERROR = 'Could not initialize `canvas` element';

export type TCanvasSizeOptions = {
backstoreOnly?: boolean;
cssOnly?: boolean;
Expand Down Expand Up @@ -428,7 +426,7 @@ export class StaticCanvas<
this.lowerCanvasEl = canvasEl;
} else {
this.lowerCanvasEl =
(getDocument().getElementById(canvasEl) as HTMLCanvasElement) ||
(getFabricDocument().getElementById(canvasEl) as HTMLCanvasElement) ||
createCanvasElement();
}
if (this.lowerCanvasEl.hasAttribute('data-fabric')) {
Expand Down
4 changes: 2 additions & 2 deletions src/env/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ export const setEnv = (value: TFabricEnv) => {

export const getEnv = () => env || getBrowserEnv();

export const getDocument = (): Document => getEnv().document;
export const getFabricDocument = (): Document => getEnv().document;

export const getWindow = (): (Window & typeof globalThis) | DOMWindow =>
export const getFabricWindow = (): (Window & typeof globalThis) | DOMWindow =>
getEnv().window;
10 changes: 5 additions & 5 deletions src/filters/WebGLFilterBackend.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getWindow } from '../env';
import { getFabricWindow } from '../env';
import { config } from '../config';
import { createCanvasElement } from '../util/misc/dom';
import {
Expand Down Expand Up @@ -97,13 +97,13 @@ export class WebGLFilterBackend {
targetCanvas.width = width;
targetCanvas.height = height;

startTime = getWindow().performance.now();
startTime = getFabricWindow().performance.now();
this.copyGLTo2D.call(testContext, this.gl, testPipelineState);
const drawImageTime = getWindow().performance.now() - startTime;
const drawImageTime = getFabricWindow().performance.now() - startTime;

startTime = getWindow().performance.now();
startTime = getFabricWindow().performance.now();
copyGLTo2DPutImageData.call(testContext, this.gl, testPipelineState);
const putImageDataTime = getWindow().performance.now() - startTime;
const putImageDataTime = getFabricWindow().performance.now() - startTime;

if (drawImageTime > putImageDataTime) {
this.imageBuffer = imageBuffer;
Expand Down
4 changes: 2 additions & 2 deletions src/parser/loadSVGFromString.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { getWindow } from '../env';
import { getFabricWindow } from '../env';
import { LoadImageOptions } from '../util/misc/objectEnlive';
import { parseSVGDocument } from './parseSVGDocument';
import type { SVGParsingOutput, TSvgReviverCallback } from './typedefs';
Expand All @@ -22,7 +22,7 @@ export function loadSVGFromString(
reviver?: TSvgReviverCallback,
options?: LoadImageOptions
): Promise<SVGParsingOutput> {
const parser = new (getWindow().DOMParser)(),
const parser = new (getFabricWindow().DOMParser)(),
// should we use `image/svg+xml` here?
doc = parser.parseFromString(string.trim(), 'text/xml');
return parseSVGDocument(doc.documentElement, reviver, options);
Expand Down
4 changes: 2 additions & 2 deletions src/shapes/IText/DraggableTextDelegate.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type { IText } from './IText';
import { setStyle } from '../../util/dom_style';
import { cloneDeep } from '../../util/internals/cloneDeep';
import { TextStyleDeclaration } from '../Text/StyledText';
import { getElementDocument } from '../../util/dom_misc';
import { getDocumentFromElement } from '../../util/dom_misc';

/**
* #### Dragging IText/Textbox Lifecycle
Expand Down Expand Up @@ -159,7 +159,7 @@ export class DraggableTextDelegate {
this.__dragImageDisposer = () => {
dragImage.remove();
};
getElementDocument(
getDocumentFromElement(
(e.target || this.target.hiddenTextarea)! as HTMLElement
).body.appendChild(dragImage);
e.dataTransfer?.setDragImage(dragImage, offset.x, offset.y);
Expand Down
4 changes: 2 additions & 2 deletions src/shapes/IText/ITextBehavior.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type { ValueAnimation } from '../../util/animation/ValueAnimation';
import type { TextStyleDeclaration } from '../Text/StyledText';
import type { SerializedTextProps, TextProps } from '../Text/Text';
import { TProps } from '../Object/types';
import { getElementDocument } from '../../util/dom_misc';
import { getDocumentFromElement } from '../../util/dom_misc';

/**
* extend this regex to support non english languages
Expand Down Expand Up @@ -405,7 +405,7 @@ export abstract class ITextBehavior<
updateSelectionOnMouseMove(e: TPointerEvent) {
const el = this.hiddenTextarea!;
// regain focus
getElementDocument(el).activeElement !== el && el.focus();
getDocumentFromElement(el).activeElement !== el && el.focus();

const newSelectionStart = this.getSelectionStartFromPointer(e),
currentStart = this.selectionStart,
Expand Down
8 changes: 4 additions & 4 deletions src/shapes/IText/ITextKeyBehavior.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
//@ts-nocheck

import { config } from '../../config';
import { getDocument, getEnv } from '../../env';
import { getFabricDocument, getEnv } from '../../env';
import { TPointerEvent } from '../../EventTypeDefs';
import { capValue } from '../../util/misc/capValue';
import { ITextBehavior, ITextEvents } from './ITextBehavior';
import type { TKeyMapIText } from './constants';
import { TProps } from '../Object/types';
import { TextProps, SerializedTextProps } from '../Text/Text';
import { getElementDocument } from '../../util/dom_misc';
import { getDocumentFromElement } from '../../util/dom_misc';

export abstract class ITextKeyBehavior<
Props extends TProps<TextProps> = Partial<TextProps>,
Expand Down Expand Up @@ -61,8 +61,8 @@ export abstract class ITextKeyBehavior<
*/
initHiddenTextarea() {
const doc =
(this.canvas && getElementDocument(this.canvas.getElement())) ||
getDocument();
(this.canvas && getDocumentFromElement(this.canvas.getElement())) ||
getFabricDocument();
this.hiddenTextarea = doc.createElement('textarea');
this.hiddenTextarea.setAttribute('autocapitalize', 'off');
this.hiddenTextarea.setAttribute('autocorrect', 'off');
Expand Down
4 changes: 2 additions & 2 deletions src/shapes/Image.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @ts-nocheck
import { getDocument, getEnv } from '../env';
import { getFabricDocument, getEnv } from '../env';
import type { BaseFilter } from '../filters/BaseFilter';
import { getFilterBackend } from '../filters/FilterBackend';
import { SHARED_ATTRIBUTES } from '../parser/attributes';
Expand Down Expand Up @@ -196,7 +196,7 @@ export class Image<
typeof arg0 === 'string'
? ((
(this.canvas && getElementDocument(this.canvas.getElement())) ||
getDocument()
getFabricDocument()
).getElementById(arg0) as ImageSource)
: arg0,
options
Expand Down
6 changes: 3 additions & 3 deletions src/util/animation/AnimationFrameProvider.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import { getWindow } from '../../env';
import { getFabricWindow } from '../../env';

export function requestAnimFrame(callback: FrameRequestCallback): number {
return getWindow().requestAnimationFrame(callback);
return getFabricWindow().requestAnimationFrame(callback);
}

export function cancelAnimFrame(handle: number): void {
return getWindow().cancelAnimationFrame(handle);
return getFabricWindow().cancelAnimationFrame(handle);
}
11 changes: 6 additions & 5 deletions src/util/dom_misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export function wrapElement(element: HTMLElement, wrapper: HTMLDivElement) {
export function getScrollLeftTop(element: HTMLElement) {
let left = 0,
top = 0;
const doc = getElementDocument(element);
const doc = getDocumentFromElement(element);
const docElement = doc.documentElement,
body = doc.body || {
scrollLeft: 0,
Expand Down Expand Up @@ -59,7 +59,7 @@ export function getScrollLeftTop(element: HTMLElement) {
*/
export function getElementOffset(element: HTMLElement) {
let box = { left: 0, top: 0 };
const doc = element && getElementDocument(element),
const doc = element && getDocumentFromElement(element),
offset = { left: 0, top: 0 },
offsetAttributes = {
borderLeftWidth: 'left',
Expand All @@ -72,7 +72,7 @@ export function getElementOffset(element: HTMLElement) {
return offset;
}
const elemStyle =
getElementWindow(element)?.getComputedStyle(element, null) || {};
getWindowFromElement(element)?.getComputedStyle(element, null) || {};
for (const attr in offsetAttributes) {
// @ts-expect-error TS learn to iterate!
offset[offsetAttributes[attr]] += parseInt(elemStyle[attr], 10) || 0;
Expand Down Expand Up @@ -118,7 +118,8 @@ export function makeElementSelectable(element: HTMLElement) {
return element;
}

export const getElementDocument = (el: HTMLElement) => el.ownerDocument || null;
export const getDocumentFromElement = (el: HTMLElement) =>
el.ownerDocument || null;

export const getElementWindow = (el: HTMLElement) =>
export const getWindowFromElement = (el: HTMLElement) =>
el.ownerDocument?.defaultView || null;
4 changes: 2 additions & 2 deletions src/util/dom_request.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
// @ts-nocheck
import { getWindow } from '../env';
import { getFabricWindow } from '../env';
import { noop } from '../constants';

/**
Expand All @@ -17,7 +17,7 @@ import { noop } from '../constants';
export function request(url, options = {}) {
const method = options.method ? options.method.toUpperCase() : 'GET',
onComplete = options.onComplete || noop,
xhr = new (getWindow().XMLHttpRequest)(),
xhr = new (getFabricWindow().XMLHttpRequest)(),
body = options.body || options.parameters,
signal = options.signal,
abort = function () {
Expand Down
13 changes: 9 additions & 4 deletions src/util/misc/dom.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
import { getDocument } from '../../env';
import { getFabricDocument } from '../../env';
import { ImageFormat } from '../../typedefs';
/**
* Creates canvas element
* @return {CanvasElement} initialized canvas element
*/
export const createCanvasElement = (): HTMLCanvasElement =>
getDocument().createElement('canvas');
export const createCanvasElement = (): HTMLCanvasElement => {
const element = getFabricDocument().createElement('canvas');
if (!element || typeof element.getContext === 'undefined') {
throw new Error('Failed to create `canvas` element');
}
return element;
};

/**
* Creates image element (works on client and node)
* @return {HTMLImageElement} HTML image element
*/
export const createImage = (): HTMLImageElement =>
getDocument().createElement('img');
getFabricDocument().createElement('img');

/**
* Creates a canvas element that is a copy of another and is also painted
Expand Down
14 changes: 7 additions & 7 deletions test/lib/event.simulate.js
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I am pretty sure this is not correct

Copy link
Contributor Author

Choose a reason for hiding this comment

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

but it is outdated anyways.
What is it?

Copy link
Member

Choose a reason for hiding this comment

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

i don't remember.
Is probably some primitive way to fire events before JSDOM or maybe we still use it.

Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
oEvent,
eventType;

element = typeof element === 'string' ? fabric.getDocument().getElementById(element) : element;
element = typeof element === 'string' ? fabric.getFabricDocument().getElementById(element) : element;

for (var name in eventMatchers) {
if (eventMatchers[name].test(eventName)) {
Expand All @@ -49,14 +49,14 @@
throw new SyntaxError('This event is not supported');
}

if (fabric.getDocument().createEvent) {
if (fabric.getFabricDocument().createEvent) {
try {
// Opera doesn't support event types like "KeyboardEvent",
// but allows to create event of type "HTMLEvents", then fire key event on it
oEvent = fabric.getDocument().createEvent(eventType);
oEvent = fabric.getFabricDocument().createEvent(eventType);
}
catch (err) {
oEvent = fabric.getDocument().createEvent('HTMLEvents');
oEvent = fabric.getFabricDocument().createEvent('HTMLEvents');
}

if (eventType === 'HTMLEvents') {
Expand All @@ -65,7 +65,7 @@
else if (eventType === 'KeyboardEvent') {
// TODO (kangax): this needs to be tested
if (oEvent.initKeyEvent) {
oEvent.initKeyEvent(eventName, options.bubbles, options.cancelable, fabric.getDocument().defaultView,
oEvent.initKeyEvent(eventName, options.bubbles, options.cancelable, fabric.getFabricDocument().defaultView,
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.keyCode,
options.charCode);
}
Expand All @@ -74,7 +74,7 @@
}
}
else {
oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, fabric.getDocument().defaultView,
oEvent.initMouseEvent(eventName, options.bubbles, options.cancelable, fabric.getFabricDocument().defaultView,
options.button, options.pointerX, options.pointerY, options.pointerX, options.pointerY,
options.ctrlKey, options.altKey, options.shiftKey, options.metaKey, options.button, element);
}
Expand All @@ -83,7 +83,7 @@
else {
options.clientX = options.pointerX;
options.clientY = options.pointerY;
oEvent = extendObject(fabric.getDocument().createEventObject(), options);
oEvent = extendObject(fabric.getFabricDocument().createEventObject(), options);
element.fireEvent('on' + eventName, oEvent);
}
return element;
Expand Down
Loading