Skip to content

web: Resolve URLs without a protocol in the extension player and improve error messages #16913

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

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 10 additions & 4 deletions web/packages/core/src/install.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { PublicAPI } from "./public-api";
import { internalSourceApi } from "./source-api";
import { getSourceApiImplementation } from "./source-api";
import { OriginAPI } from "./origin-api";

/**
* Options to use with this specific installation of Ruffle.
Expand Down Expand Up @@ -27,10 +28,12 @@ export interface InstallationOptions {
* "extension" for browser extensions, and something else for other use cases.
* Names are unique, and last-installed will replace earlier installations with the same name,
* regardless of what those installations are or which version they represent.
* @param originAPI The OriginAPI of this Ruffle version.
* @param options Any options used to configure this specific installation of Ruffle.
*/
export function installRuffle(
sourceName: string,
originAPI: OriginAPI,
options: InstallationOptions = {},
): void {
let publicAPI: PublicAPI;
Expand All @@ -41,8 +44,11 @@ export function installRuffle(
window.RufflePlayer = publicAPI;
}

publicAPI.sources[sourceName] = internalSourceApi;
internalSourceApi.options = options;
const sourceApiImplementation = getSourceApiImplementation(
originAPI,
options,
);
publicAPI.sources[sourceName] = sourceApiImplementation;

// Install the faux plugin detection immediately.
// This is necessary because scripts such as SWFObject check for the
Expand All @@ -51,6 +57,6 @@ export function installRuffle(
const polyfills =
"polyfills" in publicAPI.config ? publicAPI.config.polyfills : true;
if (polyfills !== false) {
internalSourceApi.pluginPolyfill();
sourceApiImplementation.pluginPolyfill();
}
}
1 change: 1 addition & 0 deletions web/packages/core/src/internal/constants.tsx
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
export const RUFFLE_ORIGIN = "https://ruffle.rs";
export const SUPPORTED_PROTOCOLS = ["https:", "http:"];
35 changes: 30 additions & 5 deletions web/packages/core/src/internal/player/inner.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { text, textAsParagraphs } from "../i18n";
import { swfFileName } from "../../swf-utils";
import { isExtension } from "../../current-script";
import { buildInfo } from "../../build-info";
import { RUFFLE_ORIGIN } from "../constants";
import { RUFFLE_ORIGIN, SUPPORTED_PROTOCOLS } from "../constants";
import {
InvalidOptionsError,
InvalidSwfError,
Expand All @@ -26,6 +26,7 @@ import { createRuffleBuilder } from "../../load-ruffle";
import { lookupElement } from "../register-element";
import { configureBuilder } from "../builder";
import { DEFAULT_CONFIG } from "../../config";
import { OriginAPI } from "../../origin-api";

const DIMENSION_REGEX = /^\s*(\d+(\.\d+)?(%)?)/;

Expand Down Expand Up @@ -183,17 +184,22 @@ export class InnerPlayer {
private pointerMoveMaxDistance = 0;

private volumeSettings: VolumeControls;

private readonly originAPI: OriginAPI;

private readonly debugPlayerInfo: () => string;
protected readonly onCallbackAvailable: (name: string) => void;

public constructor(
element: HTMLElement,
debugPlayerInfo: () => string,
onCallbackAvailable: (name: string) => void,
originAPI: OriginAPI
) {
this.element = element;
this.debugPlayerInfo = debugPlayerInfo;
this.onCallbackAvailable = onCallbackAvailable;
this.originAPI = originAPI;

this.shadow = this.element.attachShadow({ mode: "open" });
this.shadow.appendChild(ruffleShadowTemplate.content.cloneNode(true));
Expand Down Expand Up @@ -786,6 +792,7 @@ export class InnerPlayer {
*
* The options will be defaulted by the [[config]] field, which itself
* is defaulted by a global `window.RufflePlayer.config`.
* @param inExtensionPlayer Whether the load command originates from the extension player.
*/
async load(
options: string | URLLoadOptions | DataLoadOptions,
Expand Down Expand Up @@ -836,6 +843,10 @@ export class InnerPlayer {
if ("url" in options) {
console.log(`Loading SWF file ${options.url}`);
this.swfUrl = new URL(options.url, document.baseURI);
if (!SUPPORTED_PROTOCOLS.includes(this.swfUrl.protocol)) {
this.displayRootMovieUnsupportedUrlMessage(this.swfUrl.toString());
return;
}

this.instance!.stream_from(
this.swfUrl.href,
Expand Down Expand Up @@ -880,6 +891,14 @@ export class InnerPlayer {
return false;
}

/**
* Returns the OriginAPI of this Ruffle player.
* @returns The OriginAPI of this Ruffle player.
*/
getOriginAPI(): OriginAPI {
return this.originAPI;
}

/**
* Returns the master volume of the player.
*
Expand Down Expand Up @@ -1879,7 +1898,7 @@ export class InnerPlayer {
errorArray.push(this.getPanicData());

// Clears out any existing content (ie play button or canvas) and replaces it with the error screen
showPanicScreen(this.container, error, errorArray, this.swfUrl);
showPanicScreen(this.container, error, errorArray, this.swfUrl, this.originAPI);

// Do this last, just in case it causes any cascading issues.
this.destroy();
Expand Down Expand Up @@ -1917,12 +1936,18 @@ export class InnerPlayer {
this.container.prepend(div);
}

displayRootMovieUnsupportedUrlMessage(unsupportedUrl: string) {
this.swfUrl = new URL(unsupportedUrl, document.baseURI);
this.displayRootMovieDownloadFailedMessage(false);
}

protected displayRootMovieDownloadFailedMessage(invalidSwf: boolean): void {
const openInNewTab = this.loadedConfig?.openInNewTab;
if (
openInNewTab &&
this.swfUrl &&
window.location.origin !== this.swfUrl.origin
window.location.origin !== this.swfUrl.origin &&
SUPPORTED_PROTOCOLS.includes(this.swfUrl.protocol)
) {
this.addOpenInNewTabMessage(openInNewTab, this.swfUrl);
} else {
Expand Down Expand Up @@ -1994,12 +2019,12 @@ export class InnerPlayer {
}
}

private hideSplashScreen(): void {
hideSplashScreen(): void {
this.splashScreen.classList.add("hidden");
this.container.classList.remove("hidden");
}

private showSplashScreen(): void {
showSplashScreen(): void {
this.splashScreen.classList.remove("hidden");
this.container.classList.add("hidden");
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from "./inner";
import { registerElement } from "../register-element";
import { isSwf } from "../../swf-utils";
import { OriginAPI } from "../../origin-api";

/**
* A polyfill html element.
Expand Down Expand Up @@ -134,16 +135,22 @@ export class RuffleEmbedElement extends RufflePlayerElement {
* Creates a RuffleEmbed that will polyfill and replace the given element.
*
* @param elem Element to replace.
* @param originAPI The OriginAPI that should be used for the Ruffle version
* polyfilling the given Flash element.
* @returns Created RuffleEmbed.
*/
static fromNativeEmbedElement(elem: Element): RuffleEmbedElement {
static fromNativeEmbedElement(
elem: Element,
originAPI: OriginAPI,
): RuffleEmbedElement {
const externalName = registerElement(
"ruffle-embed",
RuffleEmbedElement,
);
const ruffleObj = document.createElement(
externalName,
) as RuffleEmbedElement;
ruffleObj.initialize(originAPI);
copyElement(elem, ruffleObj);

return ruffleObj;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { FLASH_ACTIVEX_CLASSID } from "../../flash-identifiers";
import { registerElement } from "../register-element";
import { RuffleEmbedElement } from "./ruffle-embed-element";
import { isSwf } from "../../swf-utils";
import { OriginAPI } from "../../origin-api";

/**
* Find and return the first value in obj with the given key.
Expand Down Expand Up @@ -250,16 +251,22 @@ export class RuffleObjectElement extends RufflePlayerElement {
* Creates a RuffleObject that will polyfill and replace the given element.
*
* @param elem Element to replace.
* @param originAPI The OriginAPI that should be used for the Ruffle version
* polyfilling the given Flash element.
* @returns Created RuffleObject.
*/
static fromNativeObjectElement(elem: Element): RuffleObjectElement {
static fromNativeObjectElement(
elem: Element,
originAPI: OriginAPI,
): RuffleObjectElement {
const externalName = registerElement(
"ruffle-object",
RuffleObjectElement,
);
const ruffleObj: RuffleObjectElement = document.createElement(
externalName,
) as RuffleObjectElement;
ruffleObj.initialize(originAPI);

// Avoid copying embeds-inside-objects to avoid double polyfilling.
for (const embedElem of Array.from(
Expand Down
Loading