Browse Source

fix: allow iframe-initiated HTML fullscreen to exit while in macOS fullscreen (7-1-x) (#21021)

* fix: explicitly resize the contents when exiting html fullscreen while in OS fullscreen

* test: ensure HTML fullscreen toggles while in OS fullscreen
loc 5 years ago
parent
commit
c5abb17569

+ 7 - 0
shell/browser/common_web_contents_delegate.cc

@@ -344,6 +344,13 @@ void CommonWebContentsDelegate::ExitFullscreenModeForTab(
     return;
   SetHtmlApiFullscreen(false);
   owner_window_->NotifyWindowLeaveHtmlFullScreen();
+
+  if (native_fullscreen_) {
+    // Explicitly trigger a view resize, as the size is not actually changing if
+    // the browser is fullscreened, too. Chrome does this indirectly from
+    // `chrome/browser/ui/exclusive_access/fullscreen_controller.cc`.
+    source->GetRenderViewHost()->GetWidget()->SynchronizeVisualProperties();
+  }
 }
 
 bool CommonWebContentsDelegate::IsFullscreenForTabOrPending(

+ 86 - 7
spec-main/chromium-spec.ts

@@ -1,13 +1,14 @@
 import * as chai from 'chai'
 import * as chaiAsPromised from 'chai-as-promised'
 import { BrowserWindow, WebContents, session, ipcMain } from 'electron'
-import { emittedOnce } from './events-helpers';
-import { closeAllWindows } from './window-helpers';
-import * as https from 'https';
-import * as http from 'http';
-import * as path from 'path';
-import * as fs from 'fs';
-import { EventEmitter } from 'events';
+import { emittedOnce } from './events-helpers'
+import { closeAllWindows } from './window-helpers'
+import * as https from 'https'
+import * as http from 'http'
+import * as path from 'path'
+import * as fs from 'fs'
+import { EventEmitter } from 'events'
+import { promisify } from 'util'
 
 const { expect } = chai
 
@@ -242,3 +243,81 @@ describe('web security', () => {
     await p
   })
 })
+
+describe('iframe using HTML fullscreen API while window is OS-fullscreened', () => {
+  const fullscreenChildHtml = promisify(fs.readFile)(
+    path.join(fixturesPath, 'pages', 'fullscreen-oopif.html')
+  )
+  let w: BrowserWindow, server: http.Server
+
+  before(() => {
+    server = http.createServer(async (_req, res) => {
+      res.writeHead(200, { 'Content-Type': 'text/html' })
+      res.write(await fullscreenChildHtml)
+      res.end()
+    })
+
+    server.listen(8989, '127.0.0.1')
+  })
+
+  beforeEach(() => {
+    w = new BrowserWindow({
+      show: true,
+      fullscreen: true,
+      webPreferences: {
+        nodeIntegration: true,
+        nodeIntegrationInSubFrames: true
+      }
+    })
+  })
+
+  afterEach(async () => {
+    await closeAllWindows()
+    ;(w as any) = null
+    server.close()
+  })
+
+  it('can fullscreen from out-of-process iframes (OOPIFs)', done => {
+    ipcMain.once('fullscreenChange', async () => {
+      const fullscreenWidth = await w.webContents.executeJavaScript(
+        "document.querySelector('iframe').offsetWidth"
+      )
+      expect(fullscreenWidth > 0).to.be.true
+
+      await w.webContents.executeJavaScript(
+        "document.querySelector('iframe').contentWindow.postMessage('exitFullscreen', '*')"
+      )
+
+      await new Promise(resolve => setTimeout(resolve, 500))
+
+      const width = await w.webContents.executeJavaScript(
+        "document.querySelector('iframe').offsetWidth"
+      )
+      expect(width).to.equal(0)
+
+      done()
+    })
+
+    const html =
+      '<iframe style="width: 0" frameborder=0 src="http://localhost:8989" allowfullscreen></iframe>'
+    w.loadURL(`data:text/html,${html}`)
+  })
+
+  it('can fullscreen from in-process iframes', done => {
+    ipcMain.once('fullscreenChange', async () => {
+      const fullscreenWidth = await w.webContents.executeJavaScript(
+        "document.querySelector('iframe').offsetWidth"
+      )
+      expect(fullscreenWidth > 0).to.true
+
+      await w.webContents.executeJavaScript('document.exitFullscreen()')
+      const width = await w.webContents.executeJavaScript(
+        "document.querySelector('iframe').offsetWidth"
+      )
+      expect(width).to.equal(0)
+      done()
+    })
+
+    w.loadFile(path.join(fixturesPath, 'pages', 'fullscreen-ipif.html'))
+  })
+})

+ 15 - 0
spec/fixtures/pages/fullscreen-ipif.html

@@ -0,0 +1,15 @@
+<body>
+    <iframe style="width: 0" frameborder=0 src="fullscreen.html" allowfullscreen></iframe>
+    <script>
+        const { webFrame, ipcRenderer } = require('electron')
+        const iframe = document.querySelector('iframe')
+
+        document.addEventListener('fullscreenchange', () => {
+            ipcRenderer.send('fullscreenChange')
+        })
+
+        iframe.addEventListener('load', () => {
+            webFrame.executeJavaScript("document.querySelector('iframe').contentDocument.querySelector('video').requestFullscreen()", true)
+        })
+    </script>
+</body>

+ 19 - 0
spec/fixtures/pages/fullscreen-oopif.html

@@ -0,0 +1,19 @@
+<script>
+    const { webFrame, ipcRenderer } = require('electron')
+
+    document.addEventListener('fullscreenchange', () => {
+        ipcRenderer.send('fullscreenChange', webFrame.routingId)
+    });
+
+    window.addEventListener('message', ({ data }) => {
+        if (data === 'exitFullscreen') {
+            document.exitFullscreen()
+        }
+    })
+
+    window.addEventListener('load', () => {
+        window.focus()
+        webFrame.executeJavaScript('document.querySelector("video").requestFullscreen()', true)
+    });
+</script>
+<video></video>