diff --git a/docs/api/structures/upload-file.md b/docs/api/structures/upload-file.md index ae231bdaf895..b7cdd98aab8a 100644 --- a/docs/api/structures/upload-file.md +++ b/docs/api/structures/upload-file.md @@ -1,6 +1,6 @@ # UploadFile Object -* `type` String - `file`. +* `type` 'file' - `file`. * `filePath` String - Path of file to be uploaded. * `offset` Integer - Defaults to `0`. * `length` Integer - Number of bytes to read from `offset`. diff --git a/docs/api/structures/upload-raw-data.md b/docs/api/structures/upload-raw-data.md index 4fe162311fa1..e80eaa907583 100644 --- a/docs/api/structures/upload-raw-data.md +++ b/docs/api/structures/upload-raw-data.md @@ -1,4 +1,4 @@ # UploadRawData Object -* `type` String - `rawData`. +* `type` 'rawData' - `rawData`. * `bytes` Buffer - Data to be uploaded. diff --git a/lib/browser/guest-window-manager.ts b/lib/browser/guest-window-manager.ts index c97c8edd025f..7b72727e5572 100644 --- a/lib/browser/guest-window-manager.ts +++ b/lib/browser/guest-window-manager.ts @@ -56,6 +56,7 @@ export function openGuestWindow ({ event, embedder, guest, referrer, disposition windowOpenArgs, additionalFeatures, disposition, + postData, referrer }); if (didCancelEvent) return; @@ -148,7 +149,7 @@ function emitDeprecatedNewWindowEvent ({ event, embedder, guest, windowOpenArgs, const isWebViewWithPopupsDisabled = embedder.getType() === 'webview' && embedder.getLastWebPreferences().disablePopups; const postBody = postData ? { data: postData, - headers: formatPostDataHeaders(postData as Electron.UploadRawData[]) + ...parseContentTypeFormat(postData) } : null; embedder.emit( @@ -277,22 +278,40 @@ function getDeprecatedInheritedOptions (embedder: WebContents) { return inheritableOptions; } -function formatPostDataHeaders (postData: Electron.UploadRawData[]) { +function formatPostDataHeaders (postData: PostData) { if (!postData) return; - let extraHeaders = 'content-type: application/x-www-form-urlencoded'; + const { contentType, boundary } = parseContentTypeFormat(postData); + if (boundary != null) { return `content-type: ${contentType}; boundary=${boundary}`; } - if (postData.length > 0) { - const postDataFront = postData[0].bytes.toString(); - const boundary = /^--.*[^-\r\n]/.exec( - postDataFront - ); - if (boundary != null) { - extraHeaders = `content-type: multipart/form-data; boundary=${boundary[0].substr( - 2 - )}`; + return `content-type: ${contentType}`; +} + +const MULTIPART_CONTENT_TYPE = 'multipart/form-data'; +const URL_ENCODED_CONTENT_TYPE = 'application/x-www-form-urlencoded'; + +// Figure out appropriate headers for post data. +const parseContentTypeFormat = function (postData: Exclude) { + if (postData.length) { + if (postData[0].type === 'rawData') { + // For multipart forms, the first element will start with the boundary + // notice, which looks something like `------WebKitFormBoundary12345678` + // Note, this regex would fail when submitting a urlencoded form with an + // input attribute of name="--theKey", but, uhh, don't do that? + const postDataFront = postData[0].bytes.toString(); + const boundary = /^--.*[^-\r\n]/.exec(postDataFront); + if (boundary) { + return { + boundary: boundary[0].substr(2), + contentType: MULTIPART_CONTENT_TYPE + }; + } } } - - return extraHeaders; -} + // Either the form submission didn't contain any inputs (the postData array + // was empty), or we couldn't find the boundary and thus we can assume this is + // a key=value style form. + return { + contentType: URL_ENCODED_CONTENT_TYPE + }; +}; diff --git a/lib/common/parse-features-string.ts b/lib/common/parse-features-string.ts index 21731be2a7d8..c9883ae8b3ed 100644 --- a/lib/common/parse-features-string.ts +++ b/lib/common/parse-features-string.ts @@ -62,7 +62,7 @@ export function parseCommaSeparatedKeyValue (source: string, useSoonToBeDeprecat for (const keyValuePair of source.split(',')) { const [key, value] = keyValuePair.split('=').map(str => str.trim()); if (useSoonToBeDeprecatedBehaviorForBareKeys && value === undefined) { - bareKeys.push(key); + if (key) { bareKeys.push(key); } continue; } parsed[key] = coerce(key, value); diff --git a/spec-main/api-browser-window-spec.ts b/spec-main/api-browser-window-spec.ts index 81e57f32fcdc..d480c2ab522c 100644 --- a/spec-main/api-browser-window-spec.ts +++ b/spec-main/api-browser-window-spec.ts @@ -6,7 +6,7 @@ import * as os from 'os'; import * as qs from 'querystring'; import * as http from 'http'; import { AddressInfo } from 'net'; -import { app, BrowserWindow, BrowserView, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents } from 'electron/main'; +import { app, BrowserWindow, BrowserView, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents, BrowserWindowConstructorOptions } from 'electron/main'; import { emittedOnce, emittedUntil } from './events-helpers'; import { ifit, ifdescribe, defer, delay } from './spec-helpers'; @@ -3047,6 +3047,55 @@ describe('BrowserWindow module', () => { }); w.loadFile(path.join(fixtures, 'pages', 'target-name.html')); }); + + it('includes all properties', async () => { + const w = new BrowserWindow({ show: false }); + + const p = new Promise<{ + url: string, + frameName: string, + disposition: string, + options: BrowserWindowConstructorOptions, + additionalFeatures: string[], + referrer: Electron.Referrer, + postBody: Electron.PostBody + }>((resolve) => { + w.webContents.once('new-window', (e, url, frameName, disposition, options, additionalFeatures, referrer, postBody) => { + e.preventDefault(); + resolve({ url, frameName, disposition, options, additionalFeatures, referrer, postBody }); + }); + }); + w.loadURL(`data:text/html,${encodeURIComponent(` +
+ +
+ + `)}`); + const { url, frameName, disposition, options, additionalFeatures, referrer, postBody } = await p; + expect(url).to.equal('http://example.com/test'); + expect(frameName).to.equal(''); + expect(disposition).to.equal('foreground-tab'); + expect(options).to.deep.equal({ + show: true, + width: 800, + height: 600, + webPreferences: { + contextIsolation: true, + nodeIntegration: false, + webviewTag: false, + nodeIntegrationInSubFrames: false, + openerId: options.webPreferences!.openerId + }, + webContents: undefined + }); + expect(referrer.policy).to.equal('strict-origin-when-cross-origin'); + expect(referrer.url).to.equal(''); + expect(additionalFeatures).to.deep.equal([]); + expect(postBody.data).to.have.length(1); + expect(postBody.data[0].type).to.equal('rawData'); + expect(postBody.data[0].bytes).to.deep.equal(Buffer.from('post-test-key=post-test-value')); + expect(postBody.contentType).to.equal('application/x-www-form-urlencoded'); + }); }); ifdescribe(process.platform !== 'linux')('max/minimize events', () => {