Loading browser/components/extensions/parent/ext-windows.js +71 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,69 @@ ChromeUtils.defineESModuleGetters(this, { var { ExtensionError, promiseObserved } = ExtensionUtils; function sanitizePositionParams(params, window = null, positionOffset = 0) { if (params.left === null && params.top === null) { return; } if (params.left === null) { const baseLeft = window ? window.screenX : 0; params.left = baseLeft + positionOffset; } if (params.top === null) { const baseTop = window ? window.screenY : 0; params.top = baseTop + positionOffset; } // boundary check: don't put window out of visible area const baseWidth = window ? window.outerWidth : 0; const baseHeight = window ? window.outerHeight : 0; // Secure minimum size of an window should be same to the one // defined at nsGlobalWindowOuter::CheckSecurityWidthAndHeight. const minWidth = 100; const minHeight = 100; const width = Math.max( minWidth, params.width !== null ? params.width : baseWidth ); const height = Math.max( minHeight, params.height !== null ? params.height : baseHeight ); const screenManager = Cc["@mozilla.org/gfx/screenmanager;1"].getService( Ci.nsIScreenManager ); const screen = screenManager.screenForRect( params.left, params.top, width, height ); const availDeviceLeft = {}; const availDeviceTop = {}; const availDeviceWidth = {}; const availDeviceHeight = {}; screen.GetAvailRect( availDeviceLeft, availDeviceTop, availDeviceWidth, availDeviceHeight ); const factor = screen.defaultCSSScaleFactor; const availLeft = Math.floor(availDeviceLeft.value / factor); const availTop = Math.floor(availDeviceTop.value / factor); const availWidth = Math.floor(availDeviceWidth.value / factor); const availHeight = Math.floor(availDeviceHeight.value / factor); params.left = Math.min( availLeft + availWidth - width, Math.max(availLeft, params.left) ); params.top = Math.min( availTop + availHeight - height, Math.max(availTop, params.top) ); } this.windows = class extends ExtensionAPIPersistent { windowEventRegistrar(event, listener) { let { extension } = this; Loading Loading @@ -325,10 +388,12 @@ this.windows = class extends ExtensionAPIPersistent { "dialog", "resizable", "minimizable", "centerscreen", "titlebar", "close" ); if (createData.left === null && createData.top === null) { features.push("centerscreen"); } } if (createData.incognito !== null) { Loading @@ -344,6 +409,10 @@ this.windows = class extends ExtensionAPIPersistent { } } const baseWindow = windowTracker.getTopNormalWindow(context); // 10px offset is same to Chromium sanitizePositionParams(createData, baseWindow, 10); let window = Services.ww.openWindow( null, AppConstants.BROWSER_CHROME_URL, Loading Loading @@ -427,6 +496,7 @@ this.windows = class extends ExtensionAPIPersistent { win.window.getAttention(); } sanitizePositionParams(updateInfo, win.window); win.updateGeometry(updateInfo); if (updateInfo.titlePreface !== null) { Loading browser/components/extensions/test/browser/browser_ext_windows_create_params.js +122 −0 Original line number Diff line number Diff line Loading @@ -124,3 +124,125 @@ add_task(async function testWindowCreateFocused() { ExtensionTestUtils.failOnSchemaWarnings(true); }); add_task(async function testPopupTypeWithDimension() { let extension = ExtensionTestUtils.loadExtension({ async background() { await browser.windows.create({ type: "popup", left: 123, top: 123, width: 151, height: 152, }); await browser.windows.create({ type: "popup", left: 123, width: 152, height: 153, }); await browser.windows.create({ type: "popup", top: 123, width: 153, height: 154, }); await browser.windows.create({ type: "popup", left: screen.availWidth * 100, top: screen.availHeight * 100, width: 154, height: 155, }); await browser.windows.create({ type: "popup", left: -screen.availWidth * 100, top: -screen.availHeight * 100, width: 155, height: 156, }); browser.test.sendMessage("windows-created"); }, }); const baseWindow = await BrowserTestUtils.openNewBrowserWindow(); baseWindow.resizeTo(150, 150); baseWindow.moveTo(50, 50); let windows = []; let windowListener = (window, topic) => { if (topic == "domwindowopened") { windows.push(window); } }; Services.ww.registerNotification(windowListener); await extension.startup(); await extension.awaitMessage("windows-created"); await extension.unload(); const regularScreen = getScreenAt(0, 0, 150, 150); const roundedX = roundCssPixcel(123, regularScreen); const roundedY = roundCssPixcel(123, regularScreen); const availRectLarge = getCssAvailRect( getScreenAt(screen.width * 100, screen.height * 100, 150, 150) ); const maxRight = availRectLarge.right; const maxBottom = availRectLarge.bottom; const availRectSmall = getCssAvailRect( getScreenAt(-screen.width * 100, -screen.height * 100, 150, 150150) ); const minLeft = availRectSmall.left; const minTop = availRectSmall.top; const actualCoordinates = windows .slice(0, 3) .map(window => `${window.screenX},${window.screenY}`); const offsetFromBase = 10; const expectedCoordinates = [ `${roundedX},${roundedY}`, // Missing top should be +10 from the last browser window. `${roundedX},${baseWindow.screenY + offsetFromBase}`, // Missing left should be +10 from the last browser window. `${baseWindow.screenX + offsetFromBase},${roundedY}`, ]; is( actualCoordinates.join(" / "), expectedCoordinates.join(" / "), "expected popup type windows are opened at given coordinates" ); const actualSizes = windows .slice(0, 3) .map(window => `${window.outerWidth}x${window.outerHeight}`); const expectedSizes = [`151x152`, `152x153`, `153x154`]; is( actualSizes.join(" / "), expectedSizes.join(" / "), "expected popup type windows are opened with given size" ); const actualRect = { top: windows[4].screenY, bottom: windows[3].screenY + windows[3].outerHeight, left: windows[4].screenX, right: windows[3].screenX + windows[3].outerWidth, }; const maxRect = { top: minTop, bottom: maxBottom, left: minLeft, right: maxRight, }; isRectContained(actualRect, maxRect); for (const window of windows) { window.close(); } Services.ww.unregisterNotification(windowListener); windows = null; await BrowserTestUtils.closeWindow(baseWindow); }); browser/components/extensions/test/browser/browser_ext_windows_size.js +1 −1 Original line number Diff line number Diff line Loading @@ -84,7 +84,7 @@ add_task(async function testWindowCreate() { if (platformInfo.os != "linux") { geom = { left: -50, top: -50, width: 800, height: 600 }; await browser.windows.update(windowId, geom); await checkWindow(geom); await checkWindow({ ...geom, left: 0, top: 0 }); } await browser.windows.remove(windowId); Loading browser/components/extensions/test/browser/browser_ext_windows_update.js +133 −0 Original line number Diff line number Diff line Loading @@ -229,3 +229,136 @@ add_task(async function testWindowUpdateParams() { await extension.awaitFinish("window-update-params"); await extension.unload(); }); add_task(async function testPositionBoundaryCheck() { const extension = ExtensionTestUtils.loadExtension({ async background() { function waitMessage() { return new Promise((resolve, reject) => { const onMessage = message => { if (message == "continue") { browser.test.onMessage.removeListener(onMessage); resolve(); } }; browser.test.onMessage.addListener(onMessage); }); } const win = await browser.windows.create({ type: "popup", left: 50, top: 50, width: 150, height: 150, }); await browser.test.sendMessage("ready"); await waitMessage(); await browser.windows.update(win.id, { left: 123, top: 123, }); await browser.test.sendMessage("regular"); await waitMessage(); await browser.windows.update(win.id, { left: 123, }); await browser.test.sendMessage("only-left"); await waitMessage(); await browser.windows.update(win.id, { top: 123, }); await browser.test.sendMessage("only-top"); await waitMessage(); await browser.windows.update(win.id, { left: screen.availWidth * 100, top: screen.availHeight * 100, }); await browser.test.sendMessage("too-large"); await waitMessage(); await browser.windows.update(win.id, { left: -screen.availWidth * 100, top: -screen.availHeight * 100, }); await browser.test.sendMessage("too-small"); }, }); const promisedWin = new Promise((resolve, reject) => { const windowListener = (window, topic) => { if (topic == "domwindowopened") { Services.ww.unregisterNotification(windowListener); resolve(window); } }; Services.ww.registerNotification(windowListener); }); await extension.startup(); const win = await promisedWin; const regularScreen = getScreenAt(0, 0, 150, 150); const roundedX = roundCssPixcel(123, regularScreen); const roundedY = roundCssPixcel(123, regularScreen); const availRectLarge = getCssAvailRect( getScreenAt(screen.width * 100, screen.height * 100, 150, 150) ); const maxRight = availRectLarge.right; const maxBottom = availRectLarge.bottom; const availRectSmall = getCssAvailRect( getScreenAt(-screen.width * 100, -screen.height * 100, 150, 150) ); const minLeft = availRectSmall.left; const minTop = availRectSmall.top; const expectedCoordinates = [ `${roundedX},${roundedY}`, `${roundedX},${win.screenY}`, `${win.screenX},${roundedY}`, ]; await extension.awaitMessage("ready"); const actualCoordinates = []; extension.sendMessage("continue"); await extension.awaitMessage("regular"); actualCoordinates.push(`${win.screenX},${win.screenY}`); win.moveTo(50, 50); extension.sendMessage("continue"); await extension.awaitMessage("only-left"); actualCoordinates.push(`${win.screenX},${win.screenY}`); win.moveTo(50, 50); extension.sendMessage("continue"); await extension.awaitMessage("only-top"); actualCoordinates.push(`${win.screenX},${win.screenY}`); is( actualCoordinates.join(" / "), expectedCoordinates.join(" / "), "expected window is placed at given coordinates" ); const actualRect = {}; const maxRect = { top: minTop, bottom: maxBottom, left: minLeft, right: maxRight, }; extension.sendMessage("continue"); await extension.awaitMessage("too-large"); actualRect.right = win.screenX + win.outerWidth; actualRect.bottom = win.screenY + win.outerHeight; extension.sendMessage("continue"); await extension.awaitMessage("too-small"); actualRect.top = win.screenY; actualRect.left = win.screenX; isRectContained(actualRect, maxRect); await extension.unload(); await BrowserTestUtils.closeWindow(win); }); browser/components/extensions/test/browser/head.js +51 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ * navigateTab historyPushState promiseWindowRestored * getIncognitoWindow startIncognitoMonitorExtension * loadTestSubscript awaitBrowserLoaded backgroundColorSetOnRoot * getScreenAt roundCssPixcel getCssAvailRect isRectContained */ // There are shutdown issues for which multiple rejections are left uncaught. Loading Loading @@ -1016,3 +1017,53 @@ function backgroundColorSetOnRoot() { } return os.windowsVersion < 10; } function getScreenAt(left, top, width, height) { const screenManager = Cc["@mozilla.org/gfx/screenmanager;1"].getService( Ci.nsIScreenManager ); return screenManager.screenForRect(left, top, width, height); } function roundCssPixcel(pixel, screen) { return Math.floor( Math.floor(pixel * screen.defaultCSSScaleFactor) / screen.defaultCSSScaleFactor ); } function getCssAvailRect(screen) { const availDeviceLeft = {}; const availDeviceTop = {}; const availDeviceWidth = {}; const availDeviceHeight = {}; screen.GetAvailRect( availDeviceLeft, availDeviceTop, availDeviceWidth, availDeviceHeight ); const factor = screen.defaultCSSScaleFactor; const left = Math.floor(availDeviceLeft.value / factor); const top = Math.floor(availDeviceTop.value / factor); const width = Math.floor(availDeviceWidth.value / factor); const height = Math.floor(availDeviceHeight.value / factor); return { left, top, width, height, right: left + width, bottom: top + height, }; } function isRectContained(actualRect, maxRect) { is( `top=${actualRect.top >= maxRect.top},bottom=${actualRect.bottom <= maxRect.bottom},left=${actualRect.left >= maxRect.left},right=${actualRect.right <= maxRect.right}`, "top=true,bottom=true,left=true,right=true", `Dimension must be inside, top:${actualRect.top}>=${maxRect.top}, bottom:${actualRect.bottom}<=${maxRect.bottom}, left:${actualRect.left}>=${maxRect.left}, right:${actualRect.right}<=${maxRect.right}` ); } Loading
browser/components/extensions/parent/ext-windows.js +71 −1 Original line number Diff line number Diff line Loading @@ -17,6 +17,69 @@ ChromeUtils.defineESModuleGetters(this, { var { ExtensionError, promiseObserved } = ExtensionUtils; function sanitizePositionParams(params, window = null, positionOffset = 0) { if (params.left === null && params.top === null) { return; } if (params.left === null) { const baseLeft = window ? window.screenX : 0; params.left = baseLeft + positionOffset; } if (params.top === null) { const baseTop = window ? window.screenY : 0; params.top = baseTop + positionOffset; } // boundary check: don't put window out of visible area const baseWidth = window ? window.outerWidth : 0; const baseHeight = window ? window.outerHeight : 0; // Secure minimum size of an window should be same to the one // defined at nsGlobalWindowOuter::CheckSecurityWidthAndHeight. const minWidth = 100; const minHeight = 100; const width = Math.max( minWidth, params.width !== null ? params.width : baseWidth ); const height = Math.max( minHeight, params.height !== null ? params.height : baseHeight ); const screenManager = Cc["@mozilla.org/gfx/screenmanager;1"].getService( Ci.nsIScreenManager ); const screen = screenManager.screenForRect( params.left, params.top, width, height ); const availDeviceLeft = {}; const availDeviceTop = {}; const availDeviceWidth = {}; const availDeviceHeight = {}; screen.GetAvailRect( availDeviceLeft, availDeviceTop, availDeviceWidth, availDeviceHeight ); const factor = screen.defaultCSSScaleFactor; const availLeft = Math.floor(availDeviceLeft.value / factor); const availTop = Math.floor(availDeviceTop.value / factor); const availWidth = Math.floor(availDeviceWidth.value / factor); const availHeight = Math.floor(availDeviceHeight.value / factor); params.left = Math.min( availLeft + availWidth - width, Math.max(availLeft, params.left) ); params.top = Math.min( availTop + availHeight - height, Math.max(availTop, params.top) ); } this.windows = class extends ExtensionAPIPersistent { windowEventRegistrar(event, listener) { let { extension } = this; Loading Loading @@ -325,10 +388,12 @@ this.windows = class extends ExtensionAPIPersistent { "dialog", "resizable", "minimizable", "centerscreen", "titlebar", "close" ); if (createData.left === null && createData.top === null) { features.push("centerscreen"); } } if (createData.incognito !== null) { Loading @@ -344,6 +409,10 @@ this.windows = class extends ExtensionAPIPersistent { } } const baseWindow = windowTracker.getTopNormalWindow(context); // 10px offset is same to Chromium sanitizePositionParams(createData, baseWindow, 10); let window = Services.ww.openWindow( null, AppConstants.BROWSER_CHROME_URL, Loading Loading @@ -427,6 +496,7 @@ this.windows = class extends ExtensionAPIPersistent { win.window.getAttention(); } sanitizePositionParams(updateInfo, win.window); win.updateGeometry(updateInfo); if (updateInfo.titlePreface !== null) { Loading
browser/components/extensions/test/browser/browser_ext_windows_create_params.js +122 −0 Original line number Diff line number Diff line Loading @@ -124,3 +124,125 @@ add_task(async function testWindowCreateFocused() { ExtensionTestUtils.failOnSchemaWarnings(true); }); add_task(async function testPopupTypeWithDimension() { let extension = ExtensionTestUtils.loadExtension({ async background() { await browser.windows.create({ type: "popup", left: 123, top: 123, width: 151, height: 152, }); await browser.windows.create({ type: "popup", left: 123, width: 152, height: 153, }); await browser.windows.create({ type: "popup", top: 123, width: 153, height: 154, }); await browser.windows.create({ type: "popup", left: screen.availWidth * 100, top: screen.availHeight * 100, width: 154, height: 155, }); await browser.windows.create({ type: "popup", left: -screen.availWidth * 100, top: -screen.availHeight * 100, width: 155, height: 156, }); browser.test.sendMessage("windows-created"); }, }); const baseWindow = await BrowserTestUtils.openNewBrowserWindow(); baseWindow.resizeTo(150, 150); baseWindow.moveTo(50, 50); let windows = []; let windowListener = (window, topic) => { if (topic == "domwindowopened") { windows.push(window); } }; Services.ww.registerNotification(windowListener); await extension.startup(); await extension.awaitMessage("windows-created"); await extension.unload(); const regularScreen = getScreenAt(0, 0, 150, 150); const roundedX = roundCssPixcel(123, regularScreen); const roundedY = roundCssPixcel(123, regularScreen); const availRectLarge = getCssAvailRect( getScreenAt(screen.width * 100, screen.height * 100, 150, 150) ); const maxRight = availRectLarge.right; const maxBottom = availRectLarge.bottom; const availRectSmall = getCssAvailRect( getScreenAt(-screen.width * 100, -screen.height * 100, 150, 150150) ); const minLeft = availRectSmall.left; const minTop = availRectSmall.top; const actualCoordinates = windows .slice(0, 3) .map(window => `${window.screenX},${window.screenY}`); const offsetFromBase = 10; const expectedCoordinates = [ `${roundedX},${roundedY}`, // Missing top should be +10 from the last browser window. `${roundedX},${baseWindow.screenY + offsetFromBase}`, // Missing left should be +10 from the last browser window. `${baseWindow.screenX + offsetFromBase},${roundedY}`, ]; is( actualCoordinates.join(" / "), expectedCoordinates.join(" / "), "expected popup type windows are opened at given coordinates" ); const actualSizes = windows .slice(0, 3) .map(window => `${window.outerWidth}x${window.outerHeight}`); const expectedSizes = [`151x152`, `152x153`, `153x154`]; is( actualSizes.join(" / "), expectedSizes.join(" / "), "expected popup type windows are opened with given size" ); const actualRect = { top: windows[4].screenY, bottom: windows[3].screenY + windows[3].outerHeight, left: windows[4].screenX, right: windows[3].screenX + windows[3].outerWidth, }; const maxRect = { top: minTop, bottom: maxBottom, left: minLeft, right: maxRight, }; isRectContained(actualRect, maxRect); for (const window of windows) { window.close(); } Services.ww.unregisterNotification(windowListener); windows = null; await BrowserTestUtils.closeWindow(baseWindow); });
browser/components/extensions/test/browser/browser_ext_windows_size.js +1 −1 Original line number Diff line number Diff line Loading @@ -84,7 +84,7 @@ add_task(async function testWindowCreate() { if (platformInfo.os != "linux") { geom = { left: -50, top: -50, width: 800, height: 600 }; await browser.windows.update(windowId, geom); await checkWindow(geom); await checkWindow({ ...geom, left: 0, top: 0 }); } await browser.windows.remove(windowId); Loading
browser/components/extensions/test/browser/browser_ext_windows_update.js +133 −0 Original line number Diff line number Diff line Loading @@ -229,3 +229,136 @@ add_task(async function testWindowUpdateParams() { await extension.awaitFinish("window-update-params"); await extension.unload(); }); add_task(async function testPositionBoundaryCheck() { const extension = ExtensionTestUtils.loadExtension({ async background() { function waitMessage() { return new Promise((resolve, reject) => { const onMessage = message => { if (message == "continue") { browser.test.onMessage.removeListener(onMessage); resolve(); } }; browser.test.onMessage.addListener(onMessage); }); } const win = await browser.windows.create({ type: "popup", left: 50, top: 50, width: 150, height: 150, }); await browser.test.sendMessage("ready"); await waitMessage(); await browser.windows.update(win.id, { left: 123, top: 123, }); await browser.test.sendMessage("regular"); await waitMessage(); await browser.windows.update(win.id, { left: 123, }); await browser.test.sendMessage("only-left"); await waitMessage(); await browser.windows.update(win.id, { top: 123, }); await browser.test.sendMessage("only-top"); await waitMessage(); await browser.windows.update(win.id, { left: screen.availWidth * 100, top: screen.availHeight * 100, }); await browser.test.sendMessage("too-large"); await waitMessage(); await browser.windows.update(win.id, { left: -screen.availWidth * 100, top: -screen.availHeight * 100, }); await browser.test.sendMessage("too-small"); }, }); const promisedWin = new Promise((resolve, reject) => { const windowListener = (window, topic) => { if (topic == "domwindowopened") { Services.ww.unregisterNotification(windowListener); resolve(window); } }; Services.ww.registerNotification(windowListener); }); await extension.startup(); const win = await promisedWin; const regularScreen = getScreenAt(0, 0, 150, 150); const roundedX = roundCssPixcel(123, regularScreen); const roundedY = roundCssPixcel(123, regularScreen); const availRectLarge = getCssAvailRect( getScreenAt(screen.width * 100, screen.height * 100, 150, 150) ); const maxRight = availRectLarge.right; const maxBottom = availRectLarge.bottom; const availRectSmall = getCssAvailRect( getScreenAt(-screen.width * 100, -screen.height * 100, 150, 150) ); const minLeft = availRectSmall.left; const minTop = availRectSmall.top; const expectedCoordinates = [ `${roundedX},${roundedY}`, `${roundedX},${win.screenY}`, `${win.screenX},${roundedY}`, ]; await extension.awaitMessage("ready"); const actualCoordinates = []; extension.sendMessage("continue"); await extension.awaitMessage("regular"); actualCoordinates.push(`${win.screenX},${win.screenY}`); win.moveTo(50, 50); extension.sendMessage("continue"); await extension.awaitMessage("only-left"); actualCoordinates.push(`${win.screenX},${win.screenY}`); win.moveTo(50, 50); extension.sendMessage("continue"); await extension.awaitMessage("only-top"); actualCoordinates.push(`${win.screenX},${win.screenY}`); is( actualCoordinates.join(" / "), expectedCoordinates.join(" / "), "expected window is placed at given coordinates" ); const actualRect = {}; const maxRect = { top: minTop, bottom: maxBottom, left: minLeft, right: maxRight, }; extension.sendMessage("continue"); await extension.awaitMessage("too-large"); actualRect.right = win.screenX + win.outerWidth; actualRect.bottom = win.screenY + win.outerHeight; extension.sendMessage("continue"); await extension.awaitMessage("too-small"); actualRect.top = win.screenY; actualRect.left = win.screenX; isRectContained(actualRect, maxRect); await extension.unload(); await BrowserTestUtils.closeWindow(win); });
browser/components/extensions/test/browser/head.js +51 −0 Original line number Diff line number Diff line Loading @@ -29,6 +29,7 @@ * navigateTab historyPushState promiseWindowRestored * getIncognitoWindow startIncognitoMonitorExtension * loadTestSubscript awaitBrowserLoaded backgroundColorSetOnRoot * getScreenAt roundCssPixcel getCssAvailRect isRectContained */ // There are shutdown issues for which multiple rejections are left uncaught. Loading Loading @@ -1016,3 +1017,53 @@ function backgroundColorSetOnRoot() { } return os.windowsVersion < 10; } function getScreenAt(left, top, width, height) { const screenManager = Cc["@mozilla.org/gfx/screenmanager;1"].getService( Ci.nsIScreenManager ); return screenManager.screenForRect(left, top, width, height); } function roundCssPixcel(pixel, screen) { return Math.floor( Math.floor(pixel * screen.defaultCSSScaleFactor) / screen.defaultCSSScaleFactor ); } function getCssAvailRect(screen) { const availDeviceLeft = {}; const availDeviceTop = {}; const availDeviceWidth = {}; const availDeviceHeight = {}; screen.GetAvailRect( availDeviceLeft, availDeviceTop, availDeviceWidth, availDeviceHeight ); const factor = screen.defaultCSSScaleFactor; const left = Math.floor(availDeviceLeft.value / factor); const top = Math.floor(availDeviceTop.value / factor); const width = Math.floor(availDeviceWidth.value / factor); const height = Math.floor(availDeviceHeight.value / factor); return { left, top, width, height, right: left + width, bottom: top + height, }; } function isRectContained(actualRect, maxRect) { is( `top=${actualRect.top >= maxRect.top},bottom=${actualRect.bottom <= maxRect.bottom},left=${actualRect.left >= maxRect.left},right=${actualRect.right <= maxRect.right}`, "top=true,bottom=true,left=true,right=true", `Dimension must be inside, top:${actualRect.top}>=${maxRect.top}, bottom:${actualRect.bottom}<=${maxRect.bottom}, left:${actualRect.left}>=${maxRect.left}, right:${actualRect.right}<=${maxRect.right}` ); }