@theia/mini-browser 1.53.0-next.4 → 1.53.0-next.55

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. package/README.md +45 -45
  2. package/lib/browser/mini-browser-frontend-security-warnings.js +2 -2
  3. package/lib/node/mini-browser-backend-security-warnings.js +6 -6
  4. package/package.json +5 -5
  5. package/src/browser/environment/mini-browser-environment-module.ts +24 -24
  6. package/src/browser/environment/mini-browser-environment.ts +87 -87
  7. package/src/browser/location-mapper-service.ts +150 -150
  8. package/src/browser/mini-browser-content-style.ts +32 -32
  9. package/src/browser/mini-browser-content.ts +630 -630
  10. package/src/browser/mini-browser-frontend-module.ts +86 -86
  11. package/src/browser/mini-browser-frontend-security-warnings.ts +59 -59
  12. package/src/browser/mini-browser-open-handler.ts +312 -312
  13. package/src/browser/mini-browser.ts +110 -110
  14. package/src/browser/pdfobject.d.ts +99 -99
  15. package/src/browser/style/index.css +157 -157
  16. package/src/browser/style/mini-browser.svg +17 -17
  17. package/src/common/mini-browser-endpoint.ts +28 -28
  18. package/src/common/mini-browser-service.ts +29 -29
  19. package/src/electron-browser/environment/electron-mini-browser-environment-module.ts +25 -25
  20. package/src/electron-browser/environment/electron-mini-browser-environment.ts +53 -53
  21. package/src/electron-main/mini-browser-electron-main-contribution.ts +42 -42
  22. package/src/node/mini-browser-backend-module.ts +41 -41
  23. package/src/node/mini-browser-backend-security-warnings.ts +45 -45
  24. package/src/node/mini-browser-endpoint.ts +315 -315
  25. package/src/node/mini-browser-ws-validator.ts +56 -56
  26. package/src/package.spec.ts +21 -21
@@ -1,312 +1,312 @@
1
- // *****************************************************************************
2
- // Copyright (C) 2018 TypeFox and others.
3
- //
4
- // This program and the accompanying materials are made available under the
5
- // terms of the Eclipse Public License v. 2.0 which is available at
6
- // http://www.eclipse.org/legal/epl-2.0.
7
- //
8
- // This Source Code may also be made available under the following Secondary
9
- // Licenses when the conditions for such availability set forth in the Eclipse
10
- // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
- // with the GNU Classpath Exception which is available at
12
- // https://www.gnu.org/software/classpath/license.html.
13
- //
14
- // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
- // *****************************************************************************
16
-
17
- import { Widget } from '@theia/core/shared/@phosphor/widgets';
18
- import { injectable, inject, optional } from '@theia/core/shared/inversify';
19
- import URI from '@theia/core/lib/common/uri';
20
- import { MaybePromise } from '@theia/core/lib/common/types';
21
- import { codicon, QuickInputService } from '@theia/core/lib/browser';
22
- import { ApplicationShell } from '@theia/core/lib/browser/shell';
23
- import { Command, CommandContribution, CommandRegistry } from '@theia/core/lib/common/command';
24
- import { MenuContribution, MenuModelRegistry } from '@theia/core/lib/common/menu';
25
- import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
26
- import { NavigatableWidget, NavigatableWidgetOpenHandler } from '@theia/core/lib/browser/navigatable';
27
- import { open, OpenerService } from '@theia/core/lib/browser/opener-service';
28
- import { LabelProvider } from '@theia/core/lib/browser/label-provider';
29
- import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
30
- import { WidgetOpenerOptions } from '@theia/core/lib/browser/widget-open-handler';
31
- import { MiniBrowserService } from '../common/mini-browser-service';
32
- import { MiniBrowser, MiniBrowserProps } from './mini-browser';
33
- import { LocationMapperService } from './location-mapper-service';
34
- import { nls } from '@theia/core/lib/common/nls';
35
-
36
- export namespace MiniBrowserCommands {
37
-
38
- export const PREVIEW_CATEGORY = 'Preview';
39
- export const PREVIEW_CATEGORY_KEY = nls.getDefaultKey(PREVIEW_CATEGORY);
40
-
41
- export const PREVIEW = Command.toLocalizedCommand({
42
- id: 'mini-browser.preview',
43
- label: 'Open Preview',
44
- iconClass: codicon('open-preview')
45
- }, 'vscode.markdown-language-features/package/markdown.preview.title');
46
- export const OPEN_SOURCE: Command = {
47
- id: 'mini-browser.open.source',
48
- iconClass: codicon('go-to-file')
49
- };
50
- export const OPEN_URL = Command.toDefaultLocalizedCommand({
51
- id: 'mini-browser.openUrl',
52
- category: PREVIEW_CATEGORY,
53
- label: 'Open URL'
54
- });
55
- }
56
-
57
- /**
58
- * Further options for opening a new `Mini Browser` widget.
59
- */
60
- export interface MiniBrowserOpenerOptions extends WidgetOpenerOptions, MiniBrowserProps {
61
- /**
62
- * Controls how the mini-browser widget should be opened.
63
- * - `source`: editable source.
64
- * - `preview`: rendered content of the source.
65
- */
66
- openFor?: 'source' | 'preview';
67
- }
68
-
69
- @injectable()
70
- export class MiniBrowserOpenHandler extends NavigatableWidgetOpenHandler<MiniBrowser>
71
- implements FrontendApplicationContribution, CommandContribution, MenuContribution, TabBarToolbarContribution {
72
-
73
- static PREVIEW_URI = new URI().withScheme('__minibrowser__preview__');
74
-
75
- /**
76
- * Instead of going to the backend with each file URI to ask whether it can handle the current file or not,
77
- * we have this map of extension and priority pairs that we populate at application startup.
78
- * The real advantage of this approach is the following: [Phosphor cannot run async code when invoking `isEnabled`/`isVisible`
79
- * for the command handlers](https://github.com/eclipse-theia/theia/issues/1958#issuecomment-392829371)
80
- * so the menu item would be always visible for the user even if the file type cannot be handled eventually.
81
- * Hopefully, we could get rid of this hack once we have migrated the existing Phosphor code to [React](https://github.com/eclipse-theia/theia/issues/1915).
82
- */
83
- protected readonly supportedExtensions: Map<string, number> = new Map();
84
-
85
- readonly id = MiniBrowser.ID;
86
- readonly label = nls.localize(MiniBrowserCommands.PREVIEW_CATEGORY_KEY, MiniBrowserCommands.PREVIEW_CATEGORY);
87
-
88
- @inject(OpenerService)
89
- protected readonly openerService: OpenerService;
90
-
91
- @inject(LabelProvider)
92
- protected readonly labelProvider: LabelProvider;
93
-
94
- @inject(QuickInputService) @optional()
95
- protected readonly quickInputService: QuickInputService;
96
-
97
- @inject(MiniBrowserService)
98
- protected readonly miniBrowserService: MiniBrowserService;
99
-
100
- @inject(LocationMapperService)
101
- protected readonly locationMapperService: LocationMapperService;
102
-
103
- onStart(): void {
104
- this.miniBrowserService.supportedFileExtensions().then(entries => {
105
- entries.forEach(entry => {
106
- const { extension, priority } = entry;
107
- this.supportedExtensions.set(extension, priority);
108
- });
109
- });
110
- }
111
-
112
- canHandle(uri: URI, options?: MiniBrowserOpenerOptions): number {
113
- // It does not guard against directories. For instance, a folder with this name: `Hahahah.html`.
114
- // We could check with the FS, but then, this method would become async again.
115
- const extension = uri.toString().split('.').pop();
116
- if (!extension) {
117
- return 0;
118
- }
119
- if (options?.openFor === 'source') {
120
- return -100;
121
- } else if (options?.openFor === 'preview') {
122
- return 200; // higher than that of the editor.
123
- } else {
124
- return this.supportedExtensions.get(extension.toLocaleLowerCase()) || 0;
125
- }
126
- }
127
-
128
- override async open(uri: URI, options?: MiniBrowserOpenerOptions): Promise<MiniBrowser> {
129
- const widget = await super.open(uri, options);
130
- const area = this.shell.getAreaFor(widget);
131
- if (area === 'right' || area === 'left') {
132
- const panelLayout = area === 'right' ? this.shell.getLayoutData().rightPanel : this.shell.getLayoutData().leftPanel;
133
- const minSize = this.shell.mainPanel.node.offsetWidth / 2;
134
- if (panelLayout && panelLayout.size && panelLayout.size <= minSize) {
135
- requestAnimationFrame(() => this.shell.resize(minSize, area));
136
- }
137
- }
138
- return widget;
139
- }
140
-
141
- protected override async getOrCreateWidget(uri: URI, options?: MiniBrowserOpenerOptions): Promise<MiniBrowser> {
142
- const props = await this.options(uri, options);
143
- const widget = await super.getOrCreateWidget(uri, props);
144
- widget.setProps(props);
145
- return widget;
146
- }
147
-
148
- protected async options(uri?: URI, options?: MiniBrowserOpenerOptions): Promise<MiniBrowserOpenerOptions & { widgetOptions: ApplicationShell.WidgetOptions }> {
149
- // Get the default options.
150
- let result = await this.defaultOptions();
151
- if (uri) {
152
- // Decorate it with a few properties inferred from the URI.
153
- const startPage = uri.toString(true);
154
- const name = this.labelProvider.getName(uri);
155
- const iconClass = `${this.labelProvider.getIcon(uri)} file-icon`;
156
- // The background has to be reset to white only for "real" web-pages but not for images, for instance.
157
- const resetBackground = await this.resetBackground(uri);
158
- result = {
159
- ...result,
160
- startPage,
161
- name,
162
- iconClass,
163
- // Make sure the toolbar is not visible. We have the `iframe.src` anyway.
164
- toolbar: 'hide',
165
- resetBackground
166
- };
167
- }
168
- if (options) {
169
- // Explicit options overrule everything.
170
- result = {
171
- ...result,
172
- ...options
173
- };
174
- }
175
- return result;
176
- }
177
-
178
- protected resetBackground(uri: URI): MaybePromise<boolean> {
179
- const { scheme } = uri;
180
- const uriStr = uri.toString();
181
- return scheme === 'http'
182
- || scheme === 'https'
183
- || (scheme === 'file'
184
- && (uriStr.endsWith('html') || uriStr.endsWith('.htm'))
185
- );
186
- }
187
-
188
- protected async defaultOptions(): Promise<MiniBrowserOpenerOptions & { widgetOptions: ApplicationShell.WidgetOptions }> {
189
- return {
190
- mode: 'activate',
191
- widgetOptions: { area: 'main' },
192
- sandbox: MiniBrowserProps.SandboxOptions.DEFAULT,
193
- toolbar: 'show'
194
- };
195
- }
196
-
197
- registerCommands(commands: CommandRegistry): void {
198
- commands.registerCommand(MiniBrowserCommands.PREVIEW, {
199
- execute: widget => this.preview(widget),
200
- isEnabled: widget => this.canPreviewWidget(widget),
201
- isVisible: widget => this.canPreviewWidget(widget)
202
- });
203
- commands.registerCommand(MiniBrowserCommands.OPEN_SOURCE, {
204
- execute: widget => this.openSource(widget),
205
- isEnabled: widget => !!this.getSourceUri(widget),
206
- isVisible: widget => !!this.getSourceUri(widget)
207
- });
208
- commands.registerCommand(MiniBrowserCommands.OPEN_URL, {
209
- execute: (arg?: string) => this.openUrl(arg)
210
- });
211
- }
212
-
213
- registerMenus(menus: MenuModelRegistry): void {
214
- menus.registerMenuAction(['editor_context_menu', 'navigation'], {
215
- commandId: MiniBrowserCommands.PREVIEW.id
216
- });
217
- }
218
-
219
- registerToolbarItems(toolbar: TabBarToolbarRegistry): void {
220
- toolbar.registerItem({
221
- id: MiniBrowserCommands.PREVIEW.id,
222
- command: MiniBrowserCommands.PREVIEW.id,
223
- tooltip: nls.localize('vscode.markdown-language-features/package/markdown.previewSide.title', 'Open Preview to the Side')
224
- });
225
- toolbar.registerItem({
226
- id: MiniBrowserCommands.OPEN_SOURCE.id,
227
- command: MiniBrowserCommands.OPEN_SOURCE.id,
228
- tooltip: nls.localize('vscode.markdown-language-features/package/markdown.showSource.title', 'Open Source')
229
- });
230
- }
231
-
232
- protected canPreviewWidget(widget?: Widget): boolean {
233
- const uri = this.getUriToPreview(widget);
234
- return !!uri && !!this.canHandle(uri);
235
- }
236
-
237
- protected getUriToPreview(widget?: Widget): URI | undefined {
238
- const current = this.getWidgetToPreview(widget);
239
- return current && current.getResourceUri();
240
- }
241
-
242
- protected getWidgetToPreview(widget?: Widget): NavigatableWidget | undefined {
243
- const current = widget ? widget : this.shell.currentWidget;
244
- // MiniBrowser is NavigatableWidget and should be excluded from widgets to preview
245
- return !(current instanceof MiniBrowser) && NavigatableWidget.is(current) && current || undefined;
246
- }
247
-
248
- protected async preview(widget?: Widget): Promise<void> {
249
- const ref = this.getWidgetToPreview(widget);
250
- if (!ref) {
251
- return;
252
- }
253
- const uri = ref.getResourceUri();
254
- if (!uri) {
255
- return;
256
- }
257
- await this.open(uri, {
258
- mode: 'reveal',
259
- widgetOptions: { ref, mode: 'open-to-right' },
260
- openFor: 'preview'
261
- });
262
- }
263
-
264
- protected async openSource(ref?: Widget): Promise<void> {
265
- const uri = this.getSourceUri(ref);
266
- if (uri) {
267
- await open(this.openerService, uri, {
268
- widgetOptions: { ref, mode: 'tab-after' },
269
- openFor: 'source'
270
- });
271
- }
272
- }
273
-
274
- protected getSourceUri(ref?: Widget): URI | undefined {
275
- const uri = ref instanceof MiniBrowser && ref.getResourceUri() || undefined;
276
- if (!uri || uri.scheme === 'http' || uri.scheme === 'https' || uri.isEqual(MiniBrowserOpenHandler.PREVIEW_URI)) {
277
- return undefined;
278
- }
279
- return uri;
280
- }
281
-
282
- protected async openUrl(arg?: string): Promise<void> {
283
- const url = arg ? arg : await this.quickInputService?.input({
284
- prompt: nls.localizeByDefault('URL to open'),
285
- placeHolder: nls.localize('theia/mini-browser/typeUrl', 'Type a URL')
286
- });
287
- if (url) {
288
- await this.openPreview(url);
289
- }
290
- }
291
-
292
- async openPreview(startPage: string): Promise<MiniBrowser> {
293
- const props = await this.getOpenPreviewProps(await this.locationMapperService.map(startPage));
294
- return this.open(MiniBrowserOpenHandler.PREVIEW_URI, props);
295
- }
296
-
297
- protected async getOpenPreviewProps(startPage: string): Promise<MiniBrowserOpenerOptions> {
298
- const resetBackground = await this.resetBackground(new URI(startPage));
299
- return {
300
- name: nls.localize(MiniBrowserCommands.PREVIEW_CATEGORY_KEY, MiniBrowserCommands.PREVIEW_CATEGORY),
301
- startPage,
302
- toolbar: 'read-only',
303
- widgetOptions: {
304
- area: 'right'
305
- },
306
- resetBackground,
307
- iconClass: codicon('preview'),
308
- openFor: 'preview'
309
- };
310
- }
311
-
312
- }
1
+ // *****************************************************************************
2
+ // Copyright (C) 2018 TypeFox and others.
3
+ //
4
+ // This program and the accompanying materials are made available under the
5
+ // terms of the Eclipse Public License v. 2.0 which is available at
6
+ // http://www.eclipse.org/legal/epl-2.0.
7
+ //
8
+ // This Source Code may also be made available under the following Secondary
9
+ // Licenses when the conditions for such availability set forth in the Eclipse
10
+ // Public License v. 2.0 are satisfied: GNU General Public License, version 2
11
+ // with the GNU Classpath Exception which is available at
12
+ // https://www.gnu.org/software/classpath/license.html.
13
+ //
14
+ // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
15
+ // *****************************************************************************
16
+
17
+ import { Widget } from '@theia/core/shared/@phosphor/widgets';
18
+ import { injectable, inject, optional } from '@theia/core/shared/inversify';
19
+ import URI from '@theia/core/lib/common/uri';
20
+ import { MaybePromise } from '@theia/core/lib/common/types';
21
+ import { codicon, QuickInputService } from '@theia/core/lib/browser';
22
+ import { ApplicationShell } from '@theia/core/lib/browser/shell';
23
+ import { Command, CommandContribution, CommandRegistry } from '@theia/core/lib/common/command';
24
+ import { MenuContribution, MenuModelRegistry } from '@theia/core/lib/common/menu';
25
+ import { TabBarToolbarContribution, TabBarToolbarRegistry } from '@theia/core/lib/browser/shell/tab-bar-toolbar';
26
+ import { NavigatableWidget, NavigatableWidgetOpenHandler } from '@theia/core/lib/browser/navigatable';
27
+ import { open, OpenerService } from '@theia/core/lib/browser/opener-service';
28
+ import { LabelProvider } from '@theia/core/lib/browser/label-provider';
29
+ import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution';
30
+ import { WidgetOpenerOptions } from '@theia/core/lib/browser/widget-open-handler';
31
+ import { MiniBrowserService } from '../common/mini-browser-service';
32
+ import { MiniBrowser, MiniBrowserProps } from './mini-browser';
33
+ import { LocationMapperService } from './location-mapper-service';
34
+ import { nls } from '@theia/core/lib/common/nls';
35
+
36
+ export namespace MiniBrowserCommands {
37
+
38
+ export const PREVIEW_CATEGORY = 'Preview';
39
+ export const PREVIEW_CATEGORY_KEY = nls.getDefaultKey(PREVIEW_CATEGORY);
40
+
41
+ export const PREVIEW = Command.toLocalizedCommand({
42
+ id: 'mini-browser.preview',
43
+ label: 'Open Preview',
44
+ iconClass: codicon('open-preview')
45
+ }, 'vscode.markdown-language-features/package/markdown.preview.title');
46
+ export const OPEN_SOURCE: Command = {
47
+ id: 'mini-browser.open.source',
48
+ iconClass: codicon('go-to-file')
49
+ };
50
+ export const OPEN_URL = Command.toDefaultLocalizedCommand({
51
+ id: 'mini-browser.openUrl',
52
+ category: PREVIEW_CATEGORY,
53
+ label: 'Open URL'
54
+ });
55
+ }
56
+
57
+ /**
58
+ * Further options for opening a new `Mini Browser` widget.
59
+ */
60
+ export interface MiniBrowserOpenerOptions extends WidgetOpenerOptions, MiniBrowserProps {
61
+ /**
62
+ * Controls how the mini-browser widget should be opened.
63
+ * - `source`: editable source.
64
+ * - `preview`: rendered content of the source.
65
+ */
66
+ openFor?: 'source' | 'preview';
67
+ }
68
+
69
+ @injectable()
70
+ export class MiniBrowserOpenHandler extends NavigatableWidgetOpenHandler<MiniBrowser>
71
+ implements FrontendApplicationContribution, CommandContribution, MenuContribution, TabBarToolbarContribution {
72
+
73
+ static PREVIEW_URI = new URI().withScheme('__minibrowser__preview__');
74
+
75
+ /**
76
+ * Instead of going to the backend with each file URI to ask whether it can handle the current file or not,
77
+ * we have this map of extension and priority pairs that we populate at application startup.
78
+ * The real advantage of this approach is the following: [Phosphor cannot run async code when invoking `isEnabled`/`isVisible`
79
+ * for the command handlers](https://github.com/eclipse-theia/theia/issues/1958#issuecomment-392829371)
80
+ * so the menu item would be always visible for the user even if the file type cannot be handled eventually.
81
+ * Hopefully, we could get rid of this hack once we have migrated the existing Phosphor code to [React](https://github.com/eclipse-theia/theia/issues/1915).
82
+ */
83
+ protected readonly supportedExtensions: Map<string, number> = new Map();
84
+
85
+ readonly id = MiniBrowser.ID;
86
+ readonly label = nls.localize(MiniBrowserCommands.PREVIEW_CATEGORY_KEY, MiniBrowserCommands.PREVIEW_CATEGORY);
87
+
88
+ @inject(OpenerService)
89
+ protected readonly openerService: OpenerService;
90
+
91
+ @inject(LabelProvider)
92
+ protected readonly labelProvider: LabelProvider;
93
+
94
+ @inject(QuickInputService) @optional()
95
+ protected readonly quickInputService: QuickInputService;
96
+
97
+ @inject(MiniBrowserService)
98
+ protected readonly miniBrowserService: MiniBrowserService;
99
+
100
+ @inject(LocationMapperService)
101
+ protected readonly locationMapperService: LocationMapperService;
102
+
103
+ onStart(): void {
104
+ this.miniBrowserService.supportedFileExtensions().then(entries => {
105
+ entries.forEach(entry => {
106
+ const { extension, priority } = entry;
107
+ this.supportedExtensions.set(extension, priority);
108
+ });
109
+ });
110
+ }
111
+
112
+ canHandle(uri: URI, options?: MiniBrowserOpenerOptions): number {
113
+ // It does not guard against directories. For instance, a folder with this name: `Hahahah.html`.
114
+ // We could check with the FS, but then, this method would become async again.
115
+ const extension = uri.toString().split('.').pop();
116
+ if (!extension) {
117
+ return 0;
118
+ }
119
+ if (options?.openFor === 'source') {
120
+ return -100;
121
+ } else if (options?.openFor === 'preview') {
122
+ return 200; // higher than that of the editor.
123
+ } else {
124
+ return this.supportedExtensions.get(extension.toLocaleLowerCase()) || 0;
125
+ }
126
+ }
127
+
128
+ override async open(uri: URI, options?: MiniBrowserOpenerOptions): Promise<MiniBrowser> {
129
+ const widget = await super.open(uri, options);
130
+ const area = this.shell.getAreaFor(widget);
131
+ if (area === 'right' || area === 'left') {
132
+ const panelLayout = area === 'right' ? this.shell.getLayoutData().rightPanel : this.shell.getLayoutData().leftPanel;
133
+ const minSize = this.shell.mainPanel.node.offsetWidth / 2;
134
+ if (panelLayout && panelLayout.size && panelLayout.size <= minSize) {
135
+ requestAnimationFrame(() => this.shell.resize(minSize, area));
136
+ }
137
+ }
138
+ return widget;
139
+ }
140
+
141
+ protected override async getOrCreateWidget(uri: URI, options?: MiniBrowserOpenerOptions): Promise<MiniBrowser> {
142
+ const props = await this.options(uri, options);
143
+ const widget = await super.getOrCreateWidget(uri, props);
144
+ widget.setProps(props);
145
+ return widget;
146
+ }
147
+
148
+ protected async options(uri?: URI, options?: MiniBrowserOpenerOptions): Promise<MiniBrowserOpenerOptions & { widgetOptions: ApplicationShell.WidgetOptions }> {
149
+ // Get the default options.
150
+ let result = await this.defaultOptions();
151
+ if (uri) {
152
+ // Decorate it with a few properties inferred from the URI.
153
+ const startPage = uri.toString(true);
154
+ const name = this.labelProvider.getName(uri);
155
+ const iconClass = `${this.labelProvider.getIcon(uri)} file-icon`;
156
+ // The background has to be reset to white only for "real" web-pages but not for images, for instance.
157
+ const resetBackground = await this.resetBackground(uri);
158
+ result = {
159
+ ...result,
160
+ startPage,
161
+ name,
162
+ iconClass,
163
+ // Make sure the toolbar is not visible. We have the `iframe.src` anyway.
164
+ toolbar: 'hide',
165
+ resetBackground
166
+ };
167
+ }
168
+ if (options) {
169
+ // Explicit options overrule everything.
170
+ result = {
171
+ ...result,
172
+ ...options
173
+ };
174
+ }
175
+ return result;
176
+ }
177
+
178
+ protected resetBackground(uri: URI): MaybePromise<boolean> {
179
+ const { scheme } = uri;
180
+ const uriStr = uri.toString();
181
+ return scheme === 'http'
182
+ || scheme === 'https'
183
+ || (scheme === 'file'
184
+ && (uriStr.endsWith('html') || uriStr.endsWith('.htm'))
185
+ );
186
+ }
187
+
188
+ protected async defaultOptions(): Promise<MiniBrowserOpenerOptions & { widgetOptions: ApplicationShell.WidgetOptions }> {
189
+ return {
190
+ mode: 'activate',
191
+ widgetOptions: { area: 'main' },
192
+ sandbox: MiniBrowserProps.SandboxOptions.DEFAULT,
193
+ toolbar: 'show'
194
+ };
195
+ }
196
+
197
+ registerCommands(commands: CommandRegistry): void {
198
+ commands.registerCommand(MiniBrowserCommands.PREVIEW, {
199
+ execute: widget => this.preview(widget),
200
+ isEnabled: widget => this.canPreviewWidget(widget),
201
+ isVisible: widget => this.canPreviewWidget(widget)
202
+ });
203
+ commands.registerCommand(MiniBrowserCommands.OPEN_SOURCE, {
204
+ execute: widget => this.openSource(widget),
205
+ isEnabled: widget => !!this.getSourceUri(widget),
206
+ isVisible: widget => !!this.getSourceUri(widget)
207
+ });
208
+ commands.registerCommand(MiniBrowserCommands.OPEN_URL, {
209
+ execute: (arg?: string) => this.openUrl(arg)
210
+ });
211
+ }
212
+
213
+ registerMenus(menus: MenuModelRegistry): void {
214
+ menus.registerMenuAction(['editor_context_menu', 'navigation'], {
215
+ commandId: MiniBrowserCommands.PREVIEW.id
216
+ });
217
+ }
218
+
219
+ registerToolbarItems(toolbar: TabBarToolbarRegistry): void {
220
+ toolbar.registerItem({
221
+ id: MiniBrowserCommands.PREVIEW.id,
222
+ command: MiniBrowserCommands.PREVIEW.id,
223
+ tooltip: nls.localize('vscode.markdown-language-features/package/markdown.previewSide.title', 'Open Preview to the Side')
224
+ });
225
+ toolbar.registerItem({
226
+ id: MiniBrowserCommands.OPEN_SOURCE.id,
227
+ command: MiniBrowserCommands.OPEN_SOURCE.id,
228
+ tooltip: nls.localize('vscode.markdown-language-features/package/markdown.showSource.title', 'Open Source')
229
+ });
230
+ }
231
+
232
+ protected canPreviewWidget(widget?: Widget): boolean {
233
+ const uri = this.getUriToPreview(widget);
234
+ return !!uri && !!this.canHandle(uri);
235
+ }
236
+
237
+ protected getUriToPreview(widget?: Widget): URI | undefined {
238
+ const current = this.getWidgetToPreview(widget);
239
+ return current && current.getResourceUri();
240
+ }
241
+
242
+ protected getWidgetToPreview(widget?: Widget): NavigatableWidget | undefined {
243
+ const current = widget ? widget : this.shell.currentWidget;
244
+ // MiniBrowser is NavigatableWidget and should be excluded from widgets to preview
245
+ return !(current instanceof MiniBrowser) && NavigatableWidget.is(current) && current || undefined;
246
+ }
247
+
248
+ protected async preview(widget?: Widget): Promise<void> {
249
+ const ref = this.getWidgetToPreview(widget);
250
+ if (!ref) {
251
+ return;
252
+ }
253
+ const uri = ref.getResourceUri();
254
+ if (!uri) {
255
+ return;
256
+ }
257
+ await this.open(uri, {
258
+ mode: 'reveal',
259
+ widgetOptions: { ref, mode: 'open-to-right' },
260
+ openFor: 'preview'
261
+ });
262
+ }
263
+
264
+ protected async openSource(ref?: Widget): Promise<void> {
265
+ const uri = this.getSourceUri(ref);
266
+ if (uri) {
267
+ await open(this.openerService, uri, {
268
+ widgetOptions: { ref, mode: 'tab-after' },
269
+ openFor: 'source'
270
+ });
271
+ }
272
+ }
273
+
274
+ protected getSourceUri(ref?: Widget): URI | undefined {
275
+ const uri = ref instanceof MiniBrowser && ref.getResourceUri() || undefined;
276
+ if (!uri || uri.scheme === 'http' || uri.scheme === 'https' || uri.isEqual(MiniBrowserOpenHandler.PREVIEW_URI)) {
277
+ return undefined;
278
+ }
279
+ return uri;
280
+ }
281
+
282
+ protected async openUrl(arg?: string): Promise<void> {
283
+ const url = arg ? arg : await this.quickInputService?.input({
284
+ prompt: nls.localizeByDefault('URL to open'),
285
+ placeHolder: nls.localize('theia/mini-browser/typeUrl', 'Type a URL')
286
+ });
287
+ if (url) {
288
+ await this.openPreview(url);
289
+ }
290
+ }
291
+
292
+ async openPreview(startPage: string): Promise<MiniBrowser> {
293
+ const props = await this.getOpenPreviewProps(await this.locationMapperService.map(startPage));
294
+ return this.open(MiniBrowserOpenHandler.PREVIEW_URI, props);
295
+ }
296
+
297
+ protected async getOpenPreviewProps(startPage: string): Promise<MiniBrowserOpenerOptions> {
298
+ const resetBackground = await this.resetBackground(new URI(startPage));
299
+ return {
300
+ name: nls.localize(MiniBrowserCommands.PREVIEW_CATEGORY_KEY, MiniBrowserCommands.PREVIEW_CATEGORY),
301
+ startPage,
302
+ toolbar: 'read-only',
303
+ widgetOptions: {
304
+ area: 'right'
305
+ },
306
+ resetBackground,
307
+ iconClass: codicon('preview'),
308
+ openFor: 'preview'
309
+ };
310
+ }
311
+
312
+ }