forked from mirrors/gecko-dev
And hook them up to the iOS build. Differential Revision: https://phabricator.services.mozilla.com/D204711
167 lines
5.5 KiB
JavaScript
167 lines
5.5 KiB
JavaScript
/* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
|
|
|
import { GeckoViewActorChild } from "resource://gre/modules/GeckoViewActorChild.sys.mjs";
|
|
|
|
const lazy = {};
|
|
|
|
ChromeUtils.defineESModuleGetters(lazy, {
|
|
ManifestObtainer: "resource://gre/modules/ManifestObtainer.sys.mjs",
|
|
});
|
|
|
|
export class ContentDelegateChild extends GeckoViewActorChild {
|
|
notifyParentOfViewportFit() {
|
|
if (this.triggerViewportFitChange) {
|
|
this.contentWindow.cancelIdleCallback(this.triggerViewportFitChange);
|
|
}
|
|
this.triggerViewportFitChange = this.contentWindow.requestIdleCallback(
|
|
() => {
|
|
this.triggerViewportFitChange = null;
|
|
const viewportFit = this.contentWindow.windowUtils.getViewportFitInfo();
|
|
if (this.lastViewportFit === viewportFit) {
|
|
return;
|
|
}
|
|
this.lastViewportFit = viewportFit;
|
|
this.eventDispatcher.sendRequest({
|
|
type: "GeckoView:DOMMetaViewportFit",
|
|
viewportfit: viewportFit,
|
|
});
|
|
}
|
|
);
|
|
}
|
|
|
|
// eslint-disable-next-line complexity
|
|
handleEvent(aEvent) {
|
|
debug`handleEvent: ${aEvent.type}`;
|
|
|
|
switch (aEvent.type) {
|
|
case "contextmenu": {
|
|
if (aEvent.defaultPrevented) {
|
|
return;
|
|
}
|
|
|
|
function nearestParentAttribute(aNode, aAttribute) {
|
|
while (
|
|
aNode &&
|
|
aNode.hasAttribute &&
|
|
!aNode.hasAttribute(aAttribute)
|
|
) {
|
|
aNode = aNode.parentNode;
|
|
}
|
|
return aNode && aNode.getAttribute && aNode.getAttribute(aAttribute);
|
|
}
|
|
|
|
function createAbsoluteUri(aBaseUri, aUri) {
|
|
if (!aUri || !aBaseUri || !aBaseUri.displaySpec) {
|
|
return null;
|
|
}
|
|
return Services.io.newURI(aUri, null, aBaseUri).displaySpec;
|
|
}
|
|
|
|
const node = aEvent.composedTarget;
|
|
const baseUri = node.ownerDocument.baseURIObject;
|
|
const uri = createAbsoluteUri(
|
|
baseUri,
|
|
nearestParentAttribute(node, "href")
|
|
);
|
|
const title = nearestParentAttribute(node, "title");
|
|
const alt = nearestParentAttribute(node, "alt");
|
|
const elementType = ChromeUtils.getClassName(node);
|
|
const isImage = elementType === "HTMLImageElement";
|
|
const isMedia =
|
|
elementType === "HTMLVideoElement" ||
|
|
elementType === "HTMLAudioElement";
|
|
let elementSrc = (isImage || isMedia) && (node.currentSrc || node.src);
|
|
if (elementSrc) {
|
|
const isBlob = elementSrc.startsWith("blob:");
|
|
if (isBlob && !URL.isValidObjectURL(elementSrc)) {
|
|
elementSrc = null;
|
|
}
|
|
}
|
|
|
|
if (uri || isImage || isMedia) {
|
|
const msg = {
|
|
type: "GeckoView:ContextMenu",
|
|
// We don't have full zoom on Android, so using CSS coordinates
|
|
// here is fine, since the CSS coordinate spaces match between the
|
|
// child and parent processes.
|
|
screenX: aEvent.screenX,
|
|
screenY: aEvent.screenY,
|
|
baseUri: (baseUri && baseUri.displaySpec) || null,
|
|
uri,
|
|
title,
|
|
alt,
|
|
elementType,
|
|
elementSrc: elementSrc || null,
|
|
textContent: node.textContent || null,
|
|
};
|
|
|
|
this.eventDispatcher.sendRequest(msg);
|
|
aEvent.preventDefault();
|
|
}
|
|
break;
|
|
}
|
|
case "MozDOMFullscreen:Request": {
|
|
this.sendAsyncMessage("GeckoView:DOMFullscreenRequest", {});
|
|
break;
|
|
}
|
|
case "MozDOMFullscreen:Entered":
|
|
case "MozDOMFullscreen:Exited":
|
|
// Content may change fullscreen state by itself, and we should ensure
|
|
// that the parent always exits fullscreen when content has left
|
|
// full screen mode.
|
|
if (this.contentWindow?.document.fullscreenElement) {
|
|
break;
|
|
}
|
|
// fall-through
|
|
case "MozDOMFullscreen:Exit":
|
|
this.sendAsyncMessage("GeckoView:DOMFullscreenExit", {});
|
|
break;
|
|
case "DOMMetaViewportFitChanged":
|
|
if (aEvent.originalTarget.ownerGlobal == this.contentWindow) {
|
|
this.notifyParentOfViewportFit();
|
|
}
|
|
break;
|
|
case "DOMContentLoaded": {
|
|
if (aEvent.originalTarget.ownerGlobal == this.contentWindow) {
|
|
// If loaded content doesn't have viewport-fit, parent still
|
|
// uses old value of previous content.
|
|
this.notifyParentOfViewportFit();
|
|
}
|
|
if (this.contentWindow !== this.contentWindow?.top) {
|
|
// Only check WebApp manifest on the top level window.
|
|
return;
|
|
}
|
|
this.contentWindow.requestIdleCallback(async () => {
|
|
const manifest = await lazy.ManifestObtainer.contentObtainManifest(
|
|
this.contentWindow
|
|
);
|
|
if (manifest) {
|
|
this.eventDispatcher.sendRequest({
|
|
type: "GeckoView:WebAppManifest",
|
|
manifest,
|
|
});
|
|
}
|
|
});
|
|
break;
|
|
}
|
|
case "MozFirstContentfulPaint": {
|
|
this.eventDispatcher.sendRequest({
|
|
type: "GeckoView:FirstContentfulPaint",
|
|
});
|
|
break;
|
|
}
|
|
case "MozPaintStatusReset": {
|
|
this.eventDispatcher.sendRequest({
|
|
type: "GeckoView:PaintStatusReset",
|
|
});
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const { debug, warn } = ContentDelegateChild.initLogging(
|
|
"ContentDelegateChild"
|
|
);
|