Skip to content

Commit

Permalink
Multi window support for web views
Browse files Browse the repository at this point in the history
Support for moving webview-based views into a secondary window or tab.
For webview-based views a new button becomes available in the toolbar of the view to move the view to a secondary window.
This is only supported for webview-based views and only one view can be moved into the secondary window.
There can be multiple secondary windows though.

Primary code changes:
- Add concept of extractable widgets. Only widgets implementing the interface can be extracted.
- Add SecondaryWindowHandler that encapsulates logic to move widgets to new windows.
- Only webviews can be extracted
- Configure opened secondary windows in electron
  - Hide electron menu
  - Always use the native window frame for secondary windows to get window controls
  - Do not show secondary window icon if main window uses a custom title bar
- Contribute widget extraction button in a separate new extension `widget-extraction-ui`
- Extend application shell areas with a `secondaryWindow` area that contains all extracted widgets
- Extend frontend and webpack generators to generate the base html for secondary windows and copy it to the lib folder
- Bridge plugin communication securely via secure messaging between external webview and Theia:
  Webviews only accept messages from its direct parent window. This is necessary to avoid Cross Site Scripting (XSS).
  To make the messaging work in secondary windows, messages are sent to the external widget and then delegated to the webview's iframe.
  Thereby, the secondary window only accepts messages from its opener which is the theia main window.
  To achieve this, a webview knows the secondary window it is in (if any). It then sends messages to this window instead of directly to the webview iframe.
- Patch phosphor library during install via a postinstall hook. Remove check that widget attachment target must be in same DOM.

Contributed on behalf of ST Microelectronics and Ericsson and by ARM and EclipseSource.

Co-authored-by: Stefan Dirix <[email protected]>
Co-authored-by: robmor01 <[email protected]>
Signed-off-by: Lucas Koehler <[email protected]>
  • Loading branch information
3 people committed May 16, 2022
1 parent 26b7891 commit dd58c2e
Show file tree
Hide file tree
Showing 40 changed files with 770 additions and 41 deletions.
9 changes: 8 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,16 @@

- [Previous Changelogs](https://github.com/eclipse-theia/theia/tree/master/doc/changelogs/)

## v.1.26.0
## v1.26.0 - 5/26/2022

[1.26.0 Milestone](https://github.com/eclipse-theia/theia/milestone/34)

- [plugin] Introduce `DebugSession#workspaceFolder` [#11090](https://github.com/eclipse-theia/theia/pull/11090) - Contributed on behalf of STMicroelectronics
- [core] Added support for moving webview-based views into a secondary window/tab. Added new extension `secondary-window-ui` that contributes the UI integration to use this. [#11048](https://github.com/eclipse-theia/theia/pull/11048) - Contributed on behalf of ST Microelectronics and Ericsson and by ARM and EclipseSource

<a name="breaking_changes_1.26.0">[Breaking Changes:](#breaking_changes_1.26.0)</a>

- [core] Added constructor injection to `ApplicationShell`: `SecondaryWindowHandler`. [#11048](https://github.com/eclipse-theia/theia/pull/11048) - Contributed on behalf of ST Microelectronics and Ericsson and by ARM and EclipseSource

## v1.25.0 - 4/28/2022

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export class FrontendGenerator extends AbstractGenerator {
const frontendModules = this.pck.targetFrontendModules;
await this.write(this.pck.frontend('index.html'), this.compileIndexHtml(frontendModules));
await this.write(this.pck.frontend('index.js'), this.compileIndexJs(frontendModules));
await this.write(this.pck.frontend('secondary-window.html'), this.compileSecondaryWindowHtml());
if (this.pck.isElectron()) {
const electronMainModules = this.pck.targetElectronMainModules;
await this.write(this.pck.frontend('electron-main.js'), this.compileElectronMain(electronMainModules));
Expand Down Expand Up @@ -197,4 +198,48 @@ module.exports = Promise.resolve()${this.compileElectronMainModuleImports(electr
`;
}

/** HTML for secondary windows that contain an extracted widget. */
protected compileSecondaryWindowHtml(): string {
return `<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Theia — Secondary Window</title>
<style>
html, body {
overflow: hidden;
-ms-overflow-style: none;
}
body {
margin: 0;
}
html,
head,
body,
#widget-host,
.p-Widget {
width: 100% !important;
height: 100% !important;
}
</style>
<script>
window.addEventListener('message', e => {
// Only process messages from Theia main window
if (e.source === window.opener) {
// Delegate message to iframe
document.getElementsByTagName('iframe').item(0).contentWindow.postMessage({ ...e.data }, '*');
}
});
</script>
</head>
<body>
<div id="widget-host"></div>
</body>
</html>`;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ export class WebpackGenerator extends AbstractGenerator {
const path = require('path');
const webpack = require('webpack');
const yargs = require('yargs');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const CircularDependencyPlugin = require('circular-dependency-plugin');
const CompressionPlugin = require('compression-webpack-plugin')
Expand All @@ -72,6 +73,12 @@ const { mode, staticCompression } = yargs.option('mode', {
const development = mode === 'development';
const plugins = [
new CopyWebpackPlugin({
patterns: [{
// copy secondary window html file to lib folder
from: path.resolve(__dirname, 'src-gen/frontend/secondary-window.html')
}]
}),
new webpack.ProvidePlugin({
// the Buffer class doesn't exist in the browser but some dependencies rely on it
Buffer: ['buffer', 'Buffer']
Expand Down
1 change: 1 addition & 0 deletions examples/browser/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"@theia/scm": "1.25.0",
"@theia/scm-extra": "1.25.0",
"@theia/search-in-workspace": "1.25.0",
"@theia/secondary-window-ui": "1.25.0",
"@theia/task": "1.25.0",
"@theia/terminal": "1.25.0",
"@theia/timeline": "1.25.0",
Expand Down
3 changes: 3 additions & 0 deletions examples/browser/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,9 @@
{
"path": "../../packages/search-in-workspace"
},
{
"path": "../../packages/secondary-window-ui"
},
{
"path": "../../packages/task"
},
Expand Down
1 change: 1 addition & 0 deletions examples/electron/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"@theia/scm": "1.25.0",
"@theia/scm-extra": "1.25.0",
"@theia/search-in-workspace": "1.25.0",
"@theia/secondary-window-ui": "1.25.0",
"@theia/task": "1.25.0",
"@theia/terminal": "1.25.0",
"@theia/timeline": "1.25.0",
Expand Down
3 changes: 3 additions & 0 deletions examples/electron/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,9 @@
{
"path": "../../packages/search-in-workspace"
},
{
"path": "../../packages/secondary-window-ui"
},
{
"path": "../../packages/task"
},
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"lint:clean": "rimraf .eslintcache",
"lint:oneshot": "node --max-old-space-size=4096 node_modules/eslint/bin/eslint.js --cache=true \"{dev-packages,packages,examples}/**/*.{ts,tsx}\"",
"preinstall": "node-gyp install",
"postinstall": "node scripts/patch-libraries.js",
"prepare": "yarn -s compile:references && lerna run prepare && yarn -s compile",
"publish:latest": "lerna publish --exact --yes --no-push && yarn -s publish:check",
"publish:next": "lerna publish preminor --exact --canary --preid next --dist-tag next --no-git-reset --no-git-tag-version --no-push --yes && yarn -s publish:check",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/i18n/nls.cs.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@
"resultSubset": "Jedná se pouze o podmnožinu všech výsledků. Pro zúžení seznamu výsledků použijte konkrétnější vyhledávací výraz.",
"searchOnEditorModification": "Prohledat aktivní editor při úpravě."
},
"secondary-window-ui": {
"extract-widget": "Přesunutí zobrazení do sekundárního okna"
},
"task": {
"attachTask": "Připojte úkol...",
"clearHistory": "Vymazat historii",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/i18n/nls.de.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@
"resultSubset": "Dies ist nur eine Teilmenge aller Ergebnisse. Verwenden Sie einen spezifischeren Suchbegriff, um die Ergebnisliste einzugrenzen.",
"searchOnEditorModification": "Durchsucht den aktiven Editor nach Änderungen."
},
"secondary-window-ui": {
"extract-widget": "Ansicht in sekundäres Fenster verschieben"
},
"task": {
"attachTask": "Aufgabe anhängen...",
"clearHistory": "Geschichte löschen",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/i18n/nls.es.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@
"resultSubset": "Esto es sólo un subconjunto de todos los resultados. Utilice un término de búsqueda más específico para reducir la lista de resultados.",
"searchOnEditorModification": "Busca en el editor activo cuando se modifica."
},
"secondary-window-ui": {
"extract-widget": "Mover la vista a la ventana secundaria"
},
"task": {
"attachTask": "Adjuntar tarea...",
"clearHistory": "Historia clara",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/i18n/nls.fr.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@
"resultSubset": "Il ne s'agit que d'un sous-ensemble de tous les résultats. Utilisez un terme de recherche plus spécifique pour réduire la liste des résultats.",
"searchOnEditorModification": "Rechercher l'éditeur actif lorsqu'il est modifié."
},
"secondary-window-ui": {
"extract-widget": "Déplacer la vue vers une fenêtre secondaire"
},
"task": {
"attachTask": "Attacher la tâche...",
"clearHistory": "Histoire claire",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/i18n/nls.hu.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@
"resultSubset": "Ez csak egy részhalmaza az összes eredménynek. A találati lista szűkítéséhez használjon konkrétabb keresési kifejezést.",
"searchOnEditorModification": "Keresés az aktív szerkesztőben, amikor módosítják."
},
"secondary-window-ui": {
"extract-widget": "Nézet áthelyezése másodlagos ablakba"
},
"task": {
"attachTask": "Feladat csatolása...",
"clearHistory": "Történelem törlése",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/i18n/nls.it.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@
"resultSubset": "Questo è solo un sottoinsieme di tutti i risultati. Usa un termine di ricerca più specifico per restringere la lista dei risultati.",
"searchOnEditorModification": "Cerca l'editor attivo quando viene modificato."
},
"secondary-window-ui": {
"extract-widget": "Sposta la vista nella finestra secondaria"
},
"task": {
"attachTask": "Allegare il compito...",
"clearHistory": "Storia chiara",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/i18n/nls.ja.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@
"resultSubset": "これは、すべての結果の一部に過ぎません。より具体的な検索用語を使って、結果リストを絞り込んでください。",
"searchOnEditorModification": "修正されたときにアクティブなエディタを検索します。"
},
"secondary-window-ui": {
"extract-widget": "セカンダリーウィンドウへの表示移動"
},
"task": {
"attachTask": "タスクの添付...",
"clearHistory": "明確な歴史",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/i18n/nls.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@
"resultSubset": "This is only a subset of all results. Use a more specific search term to narrow down the result list.",
"searchOnEditorModification": "Search the active editor when modified."
},
"secondary-window-ui": {
"extract-widget": "Move view to secondary window"
},
"task": {
"attachTask": "Attach Task...",
"clearHistory": "Clear History",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/i18n/nls.pl.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@
"resultSubset": "To jest tylko podzbiór wszystkich wyników. Użyj bardziej szczegółowego terminu wyszukiwania, aby zawęzić listę wyników.",
"searchOnEditorModification": "Przeszukiwanie aktywnego edytora po modyfikacji."
},
"secondary-window-ui": {
"extract-widget": "Przenieś widok do okna podrzędnego"
},
"task": {
"attachTask": "Dołącz zadanie...",
"clearHistory": "Czysta historia",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/i18n/nls.pt-br.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@
"resultSubset": "Este é apenas um subconjunto de todos os resultados. Use um termo de busca mais específico para restringir a lista de resultados.",
"searchOnEditorModification": "Pesquise o editor ativo quando modificado."
},
"secondary-window-ui": {
"extract-widget": "Mover vista para a janela secundária"
},
"task": {
"attachTask": "Anexar Tarefa...",
"clearHistory": "Histórico claro",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/i18n/nls.pt-pt.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@
"resultSubset": "Este é apenas um subconjunto de todos os resultados. Use um termo de pesquisa mais específico para restringir a lista de resultados.",
"searchOnEditorModification": "Pesquisar o editor activo quando modificado."
},
"secondary-window-ui": {
"extract-widget": "Mover vista para a janela secundária"
},
"task": {
"attachTask": "Anexar Tarefa...",
"clearHistory": "História clara",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/i18n/nls.ru.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@
"resultSubset": "Это только часть всех результатов. Используйте более конкретный поисковый запрос, чтобы сузить список результатов.",
"searchOnEditorModification": "Поиск активного редактора при изменении."
},
"secondary-window-ui": {
"extract-widget": "Переместить вид в дополнительное окно"
},
"task": {
"attachTask": "Прикрепите задание...",
"clearHistory": "Чистая история",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/i18n/nls.zh-cn.json
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,9 @@
"resultSubset": "这只是所有结果的一个子集。使用一个更具体的搜索词来缩小结果列表。",
"searchOnEditorModification": "修改时搜索活动的编辑器。"
},
"secondary-window-ui": {
"extract-widget": "将视图移至第二窗口"
},
"task": {
"attachTask": "附加任务...",
"clearHistory": "清除历史",
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/browser/frontend-application-module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,7 @@ import { RendererHost } from './widgets';
import { TooltipService, TooltipServiceImpl } from './tooltip-service';
import { bindFrontendStopwatch, bindBackendStopwatch } from './performance';
import { SaveResourceService } from './save-resource-service';
import { SecondaryWindowHandler } from './secondary-window-handler';
import { UserWorkingDirectoryProvider } from './user-working-directory-provider';

export { bindResourceProvider, bindMessageService, bindPreferenceService };
Expand Down Expand Up @@ -400,4 +401,6 @@ export const frontendApplicationModule = new ContainerModule((bind, unbind, isBo

bind(SaveResourceService).toSelf().inSingletonScope();
bind(UserWorkingDirectoryProvider).toSelf().inSingletonScope();

bind(SecondaryWindowHandler).toSelf().inSingletonScope();
});
Loading

0 comments on commit dd58c2e

Please sign in to comment.