diff --git a/atom/browser/api/atom_api_web_contents.cc b/atom/browser/api/atom_api_web_contents.cc index 3fc22438ab7..89e7a7d65b7 100644 --- a/atom/browser/api/atom_api_web_contents.cc +++ b/atom/browser/api/atom_api_web_contents.cc @@ -1672,9 +1672,9 @@ void WebContents::BuildPrototype(v8::Isolate* isolate, .SetMethod("getFrameRate", &WebContents::GetFrameRate) .SetMethod("invalidate", &WebContents::Invalidate) .SetMethod("setZoomLevel", &WebContents::SetZoomLevel) - .SetMethod("getZoomLevel", &WebContents::GetZoomLevel) + .SetMethod("_getZoomLevel", &WebContents::GetZoomLevel) .SetMethod("setZoomFactor", &WebContents::SetZoomFactor) - .SetMethod("getZoomFactor", &WebContents::GetZoomFactor) + .SetMethod("_getZoomFactor", &WebContents::GetZoomFactor) .SetMethod("getType", &WebContents::GetType) .SetMethod("getWebPreferences", &WebContents::GetWebPreferences) .SetMethod("getOwnerBrowserWindow", &WebContents::GetOwnerBrowserWindow) diff --git a/atom/browser/web_contents_zoom_controller.cc b/atom/browser/web_contents_zoom_controller.cc index cb293705c99..1bd4f5a9e4f 100644 --- a/atom/browser/web_contents_zoom_controller.cc +++ b/atom/browser/web_contents_zoom_controller.cc @@ -112,8 +112,7 @@ void WebContentsZoomController::DidFinishNavigation( return; } - if (!navigation_handle->IsSamePage()) - SetZoomFactorOnNavigationIfNeeded(navigation_handle->GetURL()); + SetZoomFactorOnNavigationIfNeeded(navigation_handle->GetURL()); } void WebContentsZoomController::WebContentsDestroyed() { diff --git a/lib/browser/api/web-contents.js b/lib/browser/api/web-contents.js index c94bd768c0f..c43a2882391 100644 --- a/lib/browser/api/web-contents.js +++ b/lib/browser/api/web-contents.js @@ -206,6 +206,26 @@ WebContents.prototype.printToPDF = function (options, callback) { this._printToPDF(printingSetting, callback) } +WebContents.prototype.getZoomLevel = function (callback) { + if (typeof callback !== 'function') { + throw new Error('Must pass function as an argument') + } + process.nextTick(() => { + const zoomLevel = this._getZoomLevel() + callback(zoomLevel) + }) +} + +WebContents.prototype.getZoomFactor = function (callback) { + if (typeof callback !== 'function') { + throw new Error('Must pass function as an argument') + } + process.nextTick(() => { + const zoomFactor = this._getZoomFactor() + callback(zoomFactor) + }) +} + // Add JavaScript wrappers for WebContents class. WebContents.prototype._init = function () { // The navigation controller. diff --git a/lib/browser/guest-view-manager.js b/lib/browser/guest-view-manager.js index e61b2574ad3..721da5acc44 100644 --- a/lib/browser/guest-view-manager.js +++ b/lib/browser/guest-view-manager.js @@ -184,7 +184,7 @@ const attachGuest = function (event, elementInstanceId, guestInstanceId, params) guestInstanceId: guestInstanceId, nodeIntegration: params.nodeintegration != null ? params.nodeintegration : false, plugins: params.plugins, - zoomFactor: embedder.getZoomFactor(), + zoomFactor: embedder._getZoomFactor(), webSecurity: !params.disablewebsecurity, blinkFeatures: params.blinkfeatures, disableBlinkFeatures: params.disableblinkfeatures diff --git a/lib/renderer/web-view/web-view.js b/lib/renderer/web-view/web-view.js index c98b02d2744..5e5a10cb004 100644 --- a/lib/renderer/web-view/web-view.js +++ b/lib/renderer/web-view/web-view.js @@ -374,7 +374,11 @@ var registerWebViewElement = function () { 'print', 'printToPDF', 'showDefinitionForSelection', - 'capturePage' + 'capturePage', + 'setZoomFactor', + 'setZoomLevel', + 'getZoomLevel', + 'getZoomFactor' ] const nonblockMethods = [ 'insertCSS', @@ -383,8 +387,6 @@ var registerWebViewElement = function () { 'sendInputEvent', 'setLayoutZoomLevelLimits', 'setVisualZoomLevelLimits', - 'setZoomFactor', - 'setZoomLevel', // TODO(kevinsawicki): Remove in 2.0, deprecate before then with warnings 'setZoomLevelLimits' ] diff --git a/spec/api-web-contents-spec.js b/spec/api-web-contents-spec.js index 0faaafbcda7..448d2556a1b 100644 --- a/spec/api-web-contents-spec.js +++ b/spec/api-web-contents-spec.js @@ -1,11 +1,12 @@ 'use strict' const assert = require('assert') +const http = require('http') const path = require('path') const {closeWindow} = require('./window-helpers') const {ipcRenderer, remote} = require('electron') -const {BrowserWindow, webContents, ipcMain} = remote +const {BrowserWindow, webContents, ipcMain, session} = remote const isCi = remote.getGlobal('isCi') @@ -322,4 +323,208 @@ describe('webContents module', function () { }) }) }) + + describe('zoom api', () => { + const zoomScheme = remote.getGlobal('zoomScheme') + const hostZoomMap = { + host1: 0.3, + host2: 0.7, + host3: 0.2 + } + + before((done) => { + const protocol = session.defaultSession.protocol + protocol.registerStringProtocol(zoomScheme, (request, callback) => { + const response = `` + callback({data: response, mimeType: 'text/html'}) + }, (error) => done(error)) + }) + + after((done) => { + const protocol = session.defaultSession.protocol + protocol.unregisterProtocol(zoomScheme, (error) => done(error)) + }) + + it('can set the correct zoom level', (done) => { + w.loadURL('about:blank') + w.webContents.on('did-finish-load', () => { + w.webContents.getZoomLevel((zoomLevel) => { + assert.equal(zoomLevel, 0.0) + w.webContents.setZoomLevel(0.5) + w.webContents.getZoomLevel((zoomLevel) => { + assert.equal(zoomLevel, 0.5) + w.webContents.setZoomLevel(0) + done() + }) + }) + }) + }) + + it('can persist zoom level across navigation', (done) => { + let finalNavigation = false + ipcMain.on('set-zoom', (e, host) => { + const zoomLevel = hostZoomMap[host] + if (!finalNavigation) { + w.webContents.setZoomLevel(zoomLevel) + } + e.sender.send(`${host}-zoom-set`) + }) + ipcMain.on('host1-zoom-level', (e, zoomLevel) => { + const expectedZoomLevel = hostZoomMap.host1 + assert.equal(zoomLevel, expectedZoomLevel) + if (finalNavigation) { + done() + } else { + w.loadURL(`${zoomScheme}://host2`) + } + }) + ipcMain.once('host2-zoom-level', (e, zoomLevel) => { + const expectedZoomLevel = hostZoomMap.host2 + assert.equal(zoomLevel, expectedZoomLevel) + finalNavigation = true + w.webContents.goBack() + }) + w.loadURL(`${zoomScheme}://host1`) + }) + + it('can propagate zoom level across same session', (done) => { + const w2 = new BrowserWindow({ + show: false + }) + w2.webContents.on('did-finish-load', () => { + w.webContents.getZoomLevel((zoomLevel1) => { + assert.equal(zoomLevel1, hostZoomMap.host3) + w2.webContents.getZoomLevel((zoomLevel2) => { + assert.equal(zoomLevel1, zoomLevel2) + w2.setClosable(true) + w2.close() + done() + }) + }) + }) + w.webContents.on('did-finish-load', () => { + w.webContents.setZoomLevel(hostZoomMap.host3) + w2.loadURL(`${zoomScheme}://host3`) + }) + w.loadURL(`${zoomScheme}://host3`) + }) + + it('cannot propagate zoom level across different session', (done) => { + const w2 = new BrowserWindow({ + show: false, + webPreferences: { + partition: 'temp' + } + }) + const protocol = w2.webContents.session.protocol + protocol.registerStringProtocol(zoomScheme, (request, callback) => { + callback('hello') + }, (error) => { + if (error) return done(error) + w2.webContents.on('did-finish-load', () => { + w.webContents.getZoomLevel((zoomLevel1) => { + assert.equal(zoomLevel1, hostZoomMap.host3) + w2.webContents.getZoomLevel((zoomLevel2) => { + assert.equal(zoomLevel2, 0) + assert.notEqual(zoomLevel1, zoomLevel2) + protocol.unregisterProtocol(zoomScheme, (error) => { + if (error) return done(error) + w2.setClosable(true) + w2.close() + done() + }) + }) + }) + }) + w.webContents.on('did-finish-load', () => { + w.webContents.setZoomLevel(hostZoomMap.host3) + w2.loadURL(`${zoomScheme}://host3`) + }) + w.loadURL(`${zoomScheme}://host3`) + }) + }) + + it('can persist when it contains iframe', (done) => { + const server = http.createServer(function (req, res) { + setTimeout(() => { + res.end() + }, 200) + }) + server.listen(0, '127.0.0.1', function () { + const url = 'http://127.0.0.1:' + server.address().port + const content = `` + w.webContents.on('did-frame-finish-load', (e, isMainFrame) => { + if (!isMainFrame) { + w.webContents.getZoomLevel((zoomLevel) => { + assert.equal(zoomLevel, 2.0) + w.webContents.setZoomLevel(0) + server.close() + done() + }) + } + }) + w.webContents.on('dom-ready', () => { + w.webContents.setZoomLevel(2.0) + }) + w.loadURL(`data:text/html,${content}`) + }) + }) + + it('cannot propagate when used with webframe', (done) => { + let finalZoomLevel = 0 + const w2 = new BrowserWindow({ + show: false + }) + w2.webContents.on('did-finish-load', () => { + w.webContents.getZoomLevel((zoomLevel1) => { + assert.equal(zoomLevel1, finalZoomLevel) + w2.webContents.getZoomLevel((zoomLevel2) => { + assert.equal(zoomLevel2, 0) + assert.notEqual(zoomLevel1, zoomLevel2) + w2.setClosable(true) + w2.close() + done() + }) + }) + }) + ipcMain.once('temporary-zoom-set', (e, zoomLevel) => { + w2.loadURL(`file://${fixtures}/pages/c.html`) + finalZoomLevel = zoomLevel + }) + w.loadURL(`file://${fixtures}/pages/webframe-zoom.html`) + }) + + it('cannot persist zoom level after navigation with webFrame', (done) => { + let initialNavigation = true + const source = ` + const {ipcRenderer, webFrame} = require('electron') + webFrame.setZoomLevel(0.6) + ipcRenderer.send('zoom-level-set', webFrame.getZoomLevel()) + ` + w.webContents.on('did-finish-load', () => { + if (initialNavigation) { + w.webContents.executeJavaScript(source, () => {}) + } else { + w.webContents.getZoomLevel((zoomLevel) => { + assert.equal(zoomLevel, 0) + done() + }) + } + }) + ipcMain.once('zoom-level-set', (e, zoomLevel) => { + assert.equal(zoomLevel, 0.6) + w.loadURL(`file://${fixtures}/pages/d.html`) + initialNavigation = false + }) + w.loadURL(`file://${fixtures}/pages/c.html`) + }) + }) }) diff --git a/spec/fixtures/pages/webframe-zoom.html b/spec/fixtures/pages/webframe-zoom.html new file mode 100644 index 00000000000..81006aad64a --- /dev/null +++ b/spec/fixtures/pages/webframe-zoom.html @@ -0,0 +1,9 @@ + + + + + diff --git a/spec/fixtures/pages/webview-custom-zoom-level.html b/spec/fixtures/pages/webview-custom-zoom-level.html new file mode 100644 index 00000000000..24f246fdd29 --- /dev/null +++ b/spec/fixtures/pages/webview-custom-zoom-level.html @@ -0,0 +1,26 @@ + + + + + + diff --git a/spec/fixtures/pages/webview-in-page-navigate.html b/spec/fixtures/pages/webview-in-page-navigate.html new file mode 100644 index 00000000000..5b061e387b9 --- /dev/null +++ b/spec/fixtures/pages/webview-in-page-navigate.html @@ -0,0 +1,32 @@ + + + + + + diff --git a/spec/fixtures/pages/zoom-factor.html b/spec/fixtures/pages/zoom-factor.html index b9f8f988cae..c27b5ea495a 100644 --- a/spec/fixtures/pages/zoom-factor.html +++ b/spec/fixtures/pages/zoom-factor.html @@ -2,7 +2,7 @@ diff --git a/spec/static/main.js b/spec/static/main.js index 1eafd78503d..c36e86aeaec 100644 --- a/spec/static/main.js +++ b/spec/static/main.js @@ -93,7 +93,8 @@ if (global.isCi) { // Register app as standard scheme. global.standardScheme = 'app' -protocol.registerStandardSchemes([global.standardScheme], { secure: true }) +global.zoomScheme = 'zoom' +protocol.registerStandardSchemes([global.standardScheme, global.zoomScheme], { secure: true }) app.on('window-all-closed', function () { app.quit() diff --git a/spec/webview-spec.js b/spec/webview-spec.js index c6b69310a8b..027a3a95200 100644 --- a/spec/webview-spec.js +++ b/spec/webview-spec.js @@ -1067,21 +1067,6 @@ describe(' tag', function () { }) }) - it('inherits the zoomFactor of the parent window', function (done) { - w = new BrowserWindow({ - show: false, - webPreferences: { - zoomFactor: 1.2 - } - }) - ipcMain.once('pong', function (event, zoomFactor, zoomLevel) { - assert.equal(zoomFactor, 1.2) - assert.equal(zoomLevel, 1) - done() - }) - w.loadURL('file://' + fixtures + '/pages/webview-zoom-factor.html') - }) - it('inherits the parent window visibility state and receives visibilitychange events', function (done) { w = new BrowserWindow({ show: false @@ -1540,4 +1525,71 @@ describe(' tag', function () { }) }) }) + + describe('zoom behavior', () => { + const zoomScheme = remote.getGlobal('zoomScheme') + const webviewSession = session.fromPartition('webview-temp') + + before((done) => { + const protocol = webviewSession.protocol + protocol.registerStringProtocol(zoomScheme, (request, callback) => { + callback('hello') + }, (error) => done(error)) + }) + + after((done) => { + const protocol = webviewSession.protocol + protocol.unregisterProtocol(zoomScheme, (error) => done(error)) + }) + + it('inherits the zoomFactor of the parent window', (done) => { + w = new BrowserWindow({ + show: false, + webPreferences: { + zoomFactor: 1.2 + } + }) + ipcMain.once('webview-parent-zoom-level', (event, zoomFactor, zoomLevel) => { + assert.equal(zoomFactor, 1.2) + assert.equal(zoomLevel, 1) + done() + }) + w.loadURL(`file://${fixtures}/pages/webview-zoom-factor.html`) + }) + + it('maintains zoom level on navigation', (done) => { + w = new BrowserWindow({ + show: false, + webPreferences: { + zoomFactor: 1.2 + } + }) + ipcMain.on('webview-zoom-level', (event, zoomLevel, zoomFactor, newHost, final) => { + if (!newHost) { + assert.equal(zoomFactor, 1.44) + assert.equal(zoomLevel, 2.0) + } else { + assert.equal(zoomFactor, 1.2) + assert.equal(zoomLevel, 1) + } + if (final) done() + }) + w.loadURL(`file://${fixtures}/pages/webview-custom-zoom-level.html`) + }) + + it('maintains zoom level when navigating within same page', (done) => { + w = new BrowserWindow({ + show: false, + webPreferences: { + zoomFactor: 1.2 + } + }) + ipcMain.on('webview-zoom-in-page', (event, zoomLevel, zoomFactor, final) => { + assert.equal(zoomFactor, 1.44) + assert.equal(zoomLevel, 2.0) + if (final) done() + }) + w.loadURL(`file://${fixtures}/pages/webview-in-page-navigate.html`) + }) + }) })