Skip to content
This repository has been archived by the owner on Dec 11, 2019. It is now read-only.

Commit

Permalink
Create a Buffer Window, which is used consistently to open new window…
Browse files Browse the repository at this point in the history
…s more instantly

A buffer window is created at startup, and any time a previous buffer window is detached in order to be utilized as a real window.
This type of window is not shown and has no tabs (pinned or non-pinned) until it is shown.
State is not persisted for Buffer Windows until they are utilized as real windows.

A new command-line flag is introduced: '--debug-window-events' which shows logging around Buffer Window creation and utilization, as well as all other window events. No additional logging should be present without this flag.

Fix #12437
  • Loading branch information
petemill authored and bsclifton committed Feb 15, 2018
1 parent 67642a0 commit 2d8b111
Show file tree
Hide file tree
Showing 9 changed files with 352 additions and 55 deletions.
23 changes: 1 addition & 22 deletions app/browser/reducers/windowsReducer.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,9 @@ const {makeImmutable, isImmutable} = require('../../common/state/immutableUtil')
const electron = require('electron')
const BrowserWindow = electron.BrowserWindow
const firstDefinedValue = require('../../../js/lib/functional').firstDefinedValue
const appConfig = require('../../../js/constants/appConfig')
const settings = require('../../../js/constants/settings')
const getSetting = require('../../../js/settings').getSetting

const platformUtil = require('../../common/lib/platformUtil')

const isDarwin = platformUtil.isDarwin()
const isWindows = platformUtil.isWindows()

// TODO cleanup all this createWindow crap
function isModal (browserOpts) {
// this needs some better checks
Expand Down Expand Up @@ -138,12 +132,7 @@ function windowDefaults (state) {
minHeight: 300,
minModalHeight: 100,
minModalWidth: 100,
windowOffset: 20,
webPreferences: {
// XXX: Do not edit without security review
sharedWorker: true,
partition: 'default'
}
windowOffset: 20
}
}

Expand Down Expand Up @@ -246,20 +235,10 @@ const handleCreateWindowAction = (state, action) => {
browserOpts.width = browserOpts.width < minWidth ? minWidth : browserOpts.width
browserOpts.height = browserOpts.height < minHeight ? minHeight : browserOpts.height

const autoHideMenuBarSetting = isDarwin || getSetting(settings.AUTO_HIDE_MENU)

const windowProps = {
// smaller min size for "modal" windows
minWidth,
minHeight,
// Neither a frame nor a titlebar
// frame: false,
// A frame but no title bar and windows buttons in titlebar 10.10 OSX and up only?
titleBarStyle: 'hidden-inset',
autoHideMenuBar: autoHideMenuBarSetting,
title: appConfig.name,
webPreferences: defaults.webPreferences,
frame: !isWindows,
disposition: frameOpts.disposition
}

Expand Down
220 changes: 213 additions & 7 deletions app/browser/windows.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,22 +17,29 @@ const {makeImmutable} = require('../common/state/immutableUtil')
const {getPinnedTabsByWindowId} = require('../common/state/tabState')
const messages = require('../../js/constants/messages')
const settings = require('../../js/constants/settings')
const appConfig = require('../../js/constants/appConfig')
const config = require('../../js/constants/config')
const appDispatcher = require('../../js/dispatcher/appDispatcher')
const platformUtil = require('../common/lib/platformUtil')
const browserWindowUtil = require('../common/lib/browserWindowUtil')
const windowState = require('../common/state/windowState')
const pinnedSitesState = require('../common/state/pinnedSitesState')
const {zoomLevel} = require('../common/constants/toolbarUserInterfaceScale')
const { shouldDebugWindowEvents } = require('../cmdLine')
const activeTabHistory = require('./activeTabHistory')

const isDarwin = platformUtil.isDarwin()
const isWindows = platformUtil.isWindows()

const {app, BrowserWindow, ipcMain} = electron

// TODO(bridiver) - set window uuid
let currentWindows = {}
const windowPinnedTabStateMemoize = new WeakMap()
const publicEvents = new EventEmitter()
let lastCreatedWindowIsRendererWindow = false
const renderedWindows = new WeakSet()
let bufferWindowId

const getWindowState = (win) => {
if (win.isFullScreen()) {
Expand Down Expand Up @@ -100,8 +107,14 @@ const updatePinnedTabs = (win, appState) => {
const statePinnedSites = pinnedSitesState.getSites(appState)
// no need to continue if we've already processed this state for this window
if (windowPinnedTabStateMemoize.get(win) === statePinnedSites) {
if (shouldDebugWindowEvents) {
console.log(`not running updatePinnedTabs for win ${win.id} since nothing changed since last time`)
}
return
}
if (shouldDebugWindowEvents) {
console.log(`performing updatePinnedTabs for win ${win.id} since state did change since last time`)
}
// cache that this state has been updated for this window,
// so we do not repeat the operation until
// this specific part of the state has changed
Expand Down Expand Up @@ -135,6 +148,9 @@ const updatePinnedTabs = (win, appState) => {
}

function showDeferredShowWindow (win) {
if (shouldDebugWindowEvents) {
console.log(`Window [${win.id}] showDeferredShowWindow`)
}
win.show()
if (win.__shouldFullscreen) {
// this timeout helps with an issue that
Expand All @@ -154,6 +170,38 @@ function showDeferredShowWindow (win) {
win.__shouldMaximize = undefined
}

function openFramesInWindow (win, frames, activeFrameKey) {
if (frames && frames.length) {
let frameIndex = -1
for (const frame of frames) {
frameIndex++
if (frame.guestInstanceId) {
appActions.newWebContentsAdded(win.id, frame)
} else {
appActions.createTabRequested({
windowId: win.id,
url: frame.location || frame.src || frame.provisionalLocation || frame.url,
partitionNumber: frame.partitionNumber,
isPrivate: frame.isPrivate,
active: activeFrameKey ? frame.key === activeFrameKey : true,
discarded: frame.unloaded,
title: frame.title,
faviconUrl: frame.icon,
index: frameIndex
}, false, true)
}
}
}
}

function markWindowCreationTime (windowId) {
console.time(`windowRender:${windowId}`)
}

function markWindowRenderTime (windowId) {
console.timeEnd(`windowRender:${windowId}`)
}

const api = {
init: (state, action) => {
app.on('browser-window-created', function (event, win) {
Expand All @@ -164,6 +212,16 @@ const api = {
}
lastCreatedWindowIsRendererWindow = false
let windowId = -1
if (shouldDebugWindowEvents) {
console.log(`Window created`)
// output console log for each event the tab receives
const oldEmit = win.emit
win.emit = function () {
const eventWindowId = win && !win.isDestroyed() ? win.id : `probably ${windowId}`
console.log(`Window [${eventWindowId}] event '${arguments[0]}'`)
oldEmit.apply(win, arguments)
}
}
const updateWindowMove = debounce(updateWindow, 100)
const updateWindowDebounce = debounce(updateWindow, 5)
const onWindowResizeDebounce = debounce(onWindowResize, 5)
Expand Down Expand Up @@ -286,6 +344,8 @@ const api = {
updateWindowDebounce(windowId)
})
})
// create a buffer window
api.getOrCreateBufferWindow()
// TODO(bridiver) - handle restoring windows
// windowState.getWindows(state).forEach((win) => {
// console.log('restore', win.toJS())
Expand All @@ -298,7 +358,7 @@ const api = {
setImmediate(() => {
const state = appStore.getState()
for (let windowId in currentWindows) {
if (currentWindows[windowId].__ready) {
if (currentWindows[windowId].__ready && currentWindows[windowId] !== api.getBufferWindow()) {
updatePinnedTabs(currentWindows[windowId], state)
}
}
Expand Down Expand Up @@ -363,19 +423,26 @@ const api = {
setImmediate(() => {
const win = currentWindows[windowId]
if (win && !win.isDestroyed()) {
const state = appStore.getState()
updatePinnedTabs(win, state)
if (win !== api.getBufferWindow()) {
const state = appStore.getState()
updatePinnedTabs(win, state)
}
win.__ready = true
win.emit(messages.WINDOW_RENDERER_READY)
}
})
},

windowRendered: (windowIdOrWin) => {
setImmediate(() => {
const win = windowIdOrWin instanceof electron.BrowserWindow
const win = windowIdOrWin instanceof electron.BrowserWindow
? windowIdOrWin
: currentWindows[windowIdOrWin]
if (shouldDebugWindowEvents) {
markWindowRenderTime(win.id)
console.log(`Window [${win.id}] rendered`)
}
renderedWindows.add(win)
setImmediate(() => {
if (win && win.__showWhenRendered && !win.isDestroyed() && !win.isVisible()) {
// window is hidden by default until we receive 'ready' message,
// so show it now
Expand All @@ -389,19 +456,107 @@ const api = {
try {
setImmediate(() => {
if (win && !win.isDestroyed()) {
win.close()
// do not allow the Buffer Window to be automatically closed
// e.g. when it has no tabs open
// In order to fully close the Buffer Window, first it will
// have to be detached from being the Buffer Window
if (win.id !== bufferWindowId) {
win.close()
}
}
})
} catch (e) {
// ignore
}
},

/** Specialist function for providing an existing window for
* Buffer Window. Normally this should not be used as one
* will automatically be created with `getOrCreateBufferWindow`
*/
setWindowIsBufferWindow: (dragBufferWindowId) => {
// close existing buffer window if it exists
const existingBufferWindow = api.getBufferWindow()
if (existingBufferWindow) {
api.closeBufferWindow()
}
bufferWindowId = dragBufferWindowId
},

clearBufferWindow: (createPinnedTabs = true) => {
const bufferWindow = api.getBufferWindow()
bufferWindowId = null
// Pinned tabs are not created for buffer windows.
// Now that this window is no longer a buffer window,
// create the pinned tabs unless explicitly told not to.
if (createPinnedTabs) {
const state = appStore.getState()
updatePinnedTabs(bufferWindow, state)
}
},

closeBufferWindow: () => {
const win = api.getBufferWindow()
if (win) {
if (shouldDebugWindowEvents) {
console.log(`Buffer Window [${win.id}] requested to be closed`)
}
win.close()
cleanupWindow(bufferWindowId)
bufferWindowId = null
} else {
if (shouldDebugWindowEvents) {
console.log('closeBufferWindow: nothing to close')
}
}
},

getBufferWindow: () => {
const win = currentWindows[bufferWindowId]
if (win && !win.isDestroyed()) {
return win
} else {
bufferWindowId = null
}
},

getOrCreateBufferWindow: function (options = { }) {
// only if we don't have one already
let win = api.getBufferWindow()
if (!win) {
options = Object.assign({ fullscreen: false, show: false }, options)
win = api.createWindow(options, null, false, null)
bufferWindowId = win.id
if (shouldDebugWindowEvents) {
console.log(`getOrCreateBufferWindow: created buffer window: ${win.id}`)
}
} else {
if (shouldDebugWindowEvents) {
console.log(`getOrCreateBufferWindow: already had buffer window ${win.id}`)
}
}
return win
},

createWindow: function (windowOptionsIn, parentWindow, maximized, frames, immutableState = Immutable.Map(), hideUntilRendered = true, cb = null) {
const defaultOptions = {
// hide the window until the window reports that it is rendered
show: true,
fullscreenable: true
fullscreenable: true,
// Neither a frame nor a titlebar
// frame: false,
// A frame but no title bar and windows buttons in titlebar 10.10 OSX and up only?
titleBarStyle: 'hidden-inset',
autoHideMenuBar: isDarwin || getSetting(settings.AUTO_HIDE_MENU),
title: appConfig.name,
frame: !isWindows,
minWidth: 480,
minHeight: 300,
webPreferences: {
// XXX: Do not edit without security review
sharedWorker: true,
partition: 'default'
}
}
const windowOptions = Object.assign(
defaultOptions,
Expand All @@ -423,6 +578,53 @@ const api = {
if (showWhenRendered && isDarwin && parentWindow && parentWindow.isFullScreen()) {
windowOptions.fullscreen = true
}
// use a buffer window if scenario is compatible
// determining when to use a buffer window and when to create a brand new window
const bufferWindow = api.getBufferWindow()
let canUseBufferWindow = !!bufferWindow
if (!canUseBufferWindow && shouldDebugWindowEvents) {
console.log('createWindow: not using buffer window because one did not exist')
}
// look for incompatible options, that we do not set for buffer windows
// but would be set for direct new windows
if (canUseBufferWindow && !renderedWindows.has(bufferWindow)) {
canUseBufferWindow = false
if (shouldDebugWindowEvents) {
console.log('createWindow: not using buffer window because it has not completed render yet')
}
}
if (canUseBufferWindow && !browserWindowUtil.canSetAllPropertiesOnExistingWindow(windowOptionsIn)) {
canUseBufferWindow = false
if (shouldDebugWindowEvents) {
console.log('createWindow: not using buffer window due to unsupported window creation options', windowOptionsIn)
}
}
if (canUseBufferWindow) {
if (shouldDebugWindowEvents) {
console.log('createWindow: using buffer window for new window, and setting properties', windowOptionsIn)
}
// detach buffer window (pinned tabs will be created)
api.clearBufferWindow()
// make a new buffer window to replace this one
setImmediate(() => {
if (shouldDebugWindowEvents) {
console.log('creating replacement buffer window...')
}
api.getOrCreateBufferWindow()
})
// set desired properties
browserWindowUtil.setPropertiesOnExistingWindow(bufferWindow, windowOptionsIn)
// create frames for 'new' window
openFramesInWindow(bufferWindow, frames, immutableState.get('activeFrameKey'))
// make fullscreen if applicable, as above
if (windowOptions.fullscreen) {
bufferWindow.setFullScreen(true)
}
if (maximized) {
bufferWindow.maximize()
}
return
}
// if delaying window show, remember if the window should be opened fullscreen
// and remove the fullscreen property for now
// (otherwise the window will be shown immediately by macOS / muon)
Expand All @@ -438,8 +640,12 @@ const api = {
// tabbed renderer window
lastCreatedWindowIsRendererWindow = true
const win = new electron.BrowserWindow(windowOptions)

win.loadURL(appUrlUtil.getBraveExtIndexHTML())

if (shouldDebugWindowEvents) {
markWindowCreationTime(win.id)
}
// TODO: pass UUID
publicEvents.emit('new-window-state', win.id, immutableState)
// let the windowReady handler know to show the window
Expand Down
Loading

0 comments on commit 2d8b111

Please sign in to comment.