From 475a417dfc29caeb644eb721cbcabd5bb8980636 Mon Sep 17 00:00:00 2001 From: Yury Semikhatsky Date: Wed, 19 May 2021 01:30:20 +0000 Subject: [PATCH] fix: compute payload mime type on server (#6647) --- src/client/elementHandle.ts | 1 - src/protocol/channels.ts | 4 ++-- src/protocol/protocol.yml | 4 ++-- src/protocol/validator.ts | 4 ++-- src/server/dom.ts | 14 +++++++++++--- src/server/frames.ts | 3 ++- 6 files changed, 19 insertions(+), 11 deletions(-) diff --git a/src/client/elementHandle.ts b/src/client/elementHandle.ts index d1b6778a32..45b382a2f1 100644 --- a/src/client/elementHandle.ts +++ b/src/client/elementHandle.ts @@ -302,7 +302,6 @@ export async function convertInputFiles(files: string | FilePayload | string[] | if (typeof item === 'string') { return { name: path.basename(item), - mimeType: mime.getType(item) || 'application/octet-stream', buffer: (await util.promisify(fs.readFile)(item)).toString('base64') }; } else { diff --git a/src/protocol/channels.ts b/src/protocol/channels.ts index 7ea19290f5..cd639ef6d6 100644 --- a/src/protocol/channels.ts +++ b/src/protocol/channels.ts @@ -1676,7 +1676,7 @@ export type FrameSetInputFilesParams = { selector: string, files: { name: string, - mimeType: string, + mimeType?: string, buffer: Binary, }[], timeout?: number, @@ -2181,7 +2181,7 @@ export type ElementHandleSelectTextResult = void; export type ElementHandleSetInputFilesParams = { files: { name: string, - mimeType: string, + mimeType?: string, buffer: Binary, }[], timeout?: number, diff --git a/src/protocol/protocol.yml b/src/protocol/protocol.yml index ca19786919..883514f009 100644 --- a/src/protocol/protocol.yml +++ b/src/protocol/protocol.yml @@ -1328,7 +1328,7 @@ Frame: type: object properties: name: string - mimeType: string + mimeType: string? buffer: binary timeout: number? noWaitAfter: boolean? @@ -1747,7 +1747,7 @@ ElementHandle: type: object properties: name: string - mimeType: string + mimeType: string? buffer: binary timeout: number? noWaitAfter: boolean? diff --git a/src/protocol/validator.ts b/src/protocol/validator.ts index c2f69b8de3..a4df27db37 100644 --- a/src/protocol/validator.ts +++ b/src/protocol/validator.ts @@ -687,7 +687,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { selector: tString, files: tArray(tObject({ name: tString, - mimeType: tString, + mimeType: tOptional(tString), buffer: tBinary, })), timeout: tOptional(tNumber), @@ -874,7 +874,7 @@ export function createScheme(tChannel: (name: string) => Validator): Scheme { scheme.ElementHandleSetInputFilesParams = tObject({ files: tArray(tObject({ name: tString, - mimeType: tString, + mimeType: tOptional(tString), buffer: tBinary, })), timeout: tOptional(tNumber), diff --git a/src/server/dom.ts b/src/server/dom.ts index db23ab2552..17566f0d4a 100644 --- a/src/server/dom.ts +++ b/src/server/dom.ts @@ -14,11 +14,13 @@ * limitations under the License. */ +import * as channels from '../protocol/channels'; import * as frames from './frames'; import { assert } from '../utils/utils'; import type { ElementStateWithoutStable, InjectedScript, InjectedScriptPoll } from './injected/injectedScript'; import * as injectedScriptSource from '../generated/injectedScriptSource'; import * as js from './javascript'; +import * as mime from 'mime'; import { Page } from './page'; import { SelectorInfo } from './selectors'; import * as types from './types'; @@ -26,6 +28,8 @@ import { Progress, ProgressController } from './progress'; import { FatalDOMError, RetargetableDOMError } from './common/domErrors'; import { CallMetadata } from './instrumentation'; +type SetInputFilesFiles = channels.ElementHandleSetInputFilesParams['files']; + export class FrameExecutionContext extends js.ExecutionContext { readonly frame: frames.Frame; private _injectedScriptPromise?: Promise; @@ -528,7 +532,7 @@ export class ElementHandle extends js.JSHandle { }, this._page._timeoutSettings.timeout(options)); } - async setInputFiles(metadata: CallMetadata, files: types.FilePayload[], options: types.NavigatingActionWaitOptions) { + async setInputFiles(metadata: CallMetadata, files: SetInputFilesFiles, options: types.NavigatingActionWaitOptions) { const controller = new ProgressController(metadata, this); return controller.run(async progress => { const result = await this._setInputFiles(progress, files, options); @@ -536,7 +540,11 @@ export class ElementHandle extends js.JSHandle { }, this._page._timeoutSettings.timeout(options)); } - async _setInputFiles(progress: Progress, files: types.FilePayload[], options: types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> { + async _setInputFiles(progress: Progress, files: SetInputFilesFiles, options: types.NavigatingActionWaitOptions): Promise<'error:notconnected' | 'done'> { + for (const payload of files) { + if (!payload.mimeType) + payload.mimeType = mime.getType(payload.name) || 'application/octet-stream'; + } const multiple = throwFatalDOMError(await this.evaluateInUtility(([injected, node]): 'error:notinput' | 'error:notconnected' | boolean => { if (node.nodeType !== Node.ELEMENT_NODE || (node as Node as Element).tagName !== 'INPUT') return 'error:notinput'; @@ -549,7 +557,7 @@ export class ElementHandle extends js.JSHandle { await progress.beforeInputAction(this); await this._page._frameManager.waitForSignalsCreatedBy(progress, options.noWaitAfter, async () => { progress.throwIfAborted(); // Avoid action that has side-effects. - await this._page._delegate.setInputFiles(this as any as ElementHandle, files); + await this._page._delegate.setInputFiles(this as any as ElementHandle, files as types.FilePayload[]); }); await this._page._doSlowMo(); return 'done'; diff --git a/src/server/frames.ts b/src/server/frames.ts index e16ee9899e..6df495b3b3 100644 --- a/src/server/frames.ts +++ b/src/server/frames.ts @@ -15,6 +15,7 @@ * limitations under the License. */ +import * as channels from '../protocol/channels'; import { ConsoleMessage } from './console'; import * as dom from './dom'; import { helper, RegisteredListener } from './helper'; @@ -1060,7 +1061,7 @@ export class Frame extends SdkObject { }, this._page._timeoutSettings.timeout(options)); } - async setInputFiles(metadata: CallMetadata, selector: string, files: types.FilePayload[], options: types.NavigatingActionWaitOptions = {}): Promise { + async setInputFiles(metadata: CallMetadata, selector: string, files: channels.ElementHandleSetInputFilesParams['files'], options: types.NavigatingActionWaitOptions = {}): Promise { const controller = new ProgressController(metadata, this); return controller.run(async progress => { return dom.assertDone(await this._retryWithProgressIfNotConnected(progress, selector, handle => handle._setInputFiles(progress, files, options)));