mirror of
https://github.com/Microsoft/vscode
synced 2024-08-27 04:49:35 +00:00
Add simple browser extension (#109276)
* Add support for TS's Hierarchical refactorings API https://github.com/microsoft/TypeScript/pull/41975 * Add simple browser extension This change adds a new 'simple browser' extension. This extension uses a webview to render webpages directly in VS Code. We plan on using it for optionally previewing local servers in both desktop and codespaces The browser itself has a number of limitations due to the security around iframes: - It traps keyboard focus - We can't detect if a page fails to load - We can't track the current url of the iframe * Add experimental alert when the iframe is focused * Disable events on focus warning * Hooking up simple browser to opener
This commit is contained in:
parent
69dfa670ef
commit
3ed300eb9d
|
@ -59,6 +59,7 @@ const compilations = [
|
|||
'php-language-features/tsconfig.json',
|
||||
'python/tsconfig.json',
|
||||
'search-result/tsconfig.json',
|
||||
'simple-browser/tsconfig.json',
|
||||
'typescript-language-features/test-workspace/tsconfig.json',
|
||||
'typescript-language-features/tsconfig.json',
|
||||
'vscode-api-tests/tsconfig.json',
|
||||
|
|
|
@ -93,6 +93,7 @@ const indentationFilter = [
|
|||
'!**/*.Dockerfile',
|
||||
'!**/*.dockerfile',
|
||||
'!extensions/markdown-language-features/media/*.js',
|
||||
'!extensions/simple-browser/media/*.js',
|
||||
];
|
||||
|
||||
const copyrightFilter = [
|
||||
|
|
|
@ -34,6 +34,7 @@ exports.dirs = [
|
|||
'extensions/npm',
|
||||
'extensions/php-language-features',
|
||||
'extensions/search-result',
|
||||
'extensions/simple-browser',
|
||||
'extensions/typescript-language-features',
|
||||
'extensions/vscode-api-tests',
|
||||
'extensions/vscode-colorize-tests',
|
||||
|
|
12
extensions/simple-browser/.vscodeignore
Normal file
12
extensions/simple-browser/.vscodeignore
Normal file
|
@ -0,0 +1,12 @@
|
|||
test/**
|
||||
test-workspace/**
|
||||
src/**
|
||||
tsconfig.json
|
||||
out/test/**
|
||||
out/**
|
||||
extension.webpack.config.js
|
||||
extension-browser.webpack.config.js
|
||||
cgmanifest.json
|
||||
yarn.lock
|
||||
preview-src/**
|
||||
webpack.config.js
|
3
extensions/simple-browser/README.md
Normal file
3
extensions/simple-browser/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
# Simple Browser files
|
||||
|
||||
**Notice:** This extension is bundled with Visual Studio Code. It can be disabled but not uninstalled.
|
|
@ -0,0 +1,17 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
//@ts-check
|
||||
|
||||
'use strict';
|
||||
|
||||
const withBrowserDefaults = require('../shared.webpack.config').browser;
|
||||
|
||||
module.exports = withBrowserDefaults({
|
||||
context: __dirname,
|
||||
entry: {
|
||||
extension: './src/extension.ts'
|
||||
}
|
||||
});
|
20
extensions/simple-browser/extension.webpack.config.js
Normal file
20
extensions/simple-browser/extension.webpack.config.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
//@ts-check
|
||||
|
||||
'use strict';
|
||||
|
||||
const withDefaults = require('../shared.webpack.config');
|
||||
|
||||
module.exports = withDefaults({
|
||||
context: __dirname,
|
||||
resolve: {
|
||||
mainFields: ['module', 'main']
|
||||
},
|
||||
entry: {
|
||||
extension: './src/extension.ts',
|
||||
}
|
||||
});
|
201
extensions/simple-browser/media/index.js
Normal file
201
extensions/simple-browser/media/index.js
Normal file
|
@ -0,0 +1,201 @@
|
|||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // define __esModule on exports
|
||||
/******/ __webpack_require__.r = function(exports) {
|
||||
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
||||
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||||
/******/ }
|
||||
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // create a fake namespace object
|
||||
/******/ // mode & 1: value is a module id, require it
|
||||
/******/ // mode & 2: merge all properties of value into the ns
|
||||
/******/ // mode & 4: return value when already ns object
|
||||
/******/ // mode & 8|1: behave like require
|
||||
/******/ __webpack_require__.t = function(value, mode) {
|
||||
/******/ if(mode & 1) value = __webpack_require__(value);
|
||||
/******/ if(mode & 8) return value;
|
||||
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
|
||||
/******/ var ns = Object.create(null);
|
||||
/******/ __webpack_require__.r(ns);
|
||||
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
|
||||
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
|
||||
/******/ return ns;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "";
|
||||
/******/
|
||||
/******/
|
||||
/******/ // Load entry module and return exports
|
||||
/******/ return __webpack_require__(__webpack_require__.s = "./preview-src/index.ts");
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ({
|
||||
|
||||
/***/ "./preview-src/events.ts":
|
||||
/*!*******************************!*\
|
||||
!*** ./preview-src/events.ts ***!
|
||||
\*******************************/
|
||||
/*! no static exports found */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.onceDocumentLoaded = void 0;
|
||||
function onceDocumentLoaded(f) {
|
||||
if (document.readyState === 'loading' || document.readyState === 'uninitialized') {
|
||||
document.addEventListener('DOMContentLoaded', f);
|
||||
}
|
||||
else {
|
||||
f();
|
||||
}
|
||||
}
|
||||
exports.onceDocumentLoaded = onceDocumentLoaded;
|
||||
|
||||
|
||||
/***/ }),
|
||||
|
||||
/***/ "./preview-src/index.ts":
|
||||
/*!******************************!*\
|
||||
!*** ./preview-src/index.ts ***!
|
||||
\******************************/
|
||||
/*! no static exports found */
|
||||
/***/ (function(module, exports, __webpack_require__) {
|
||||
|
||||
"use strict";
|
||||
|
||||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
const events_1 = __webpack_require__(/*! ./events */ "./preview-src/events.ts");
|
||||
const vscode = acquireVsCodeApi();
|
||||
function getSettings() {
|
||||
const element = document.getElementById('simple-browser-settings');
|
||||
if (element) {
|
||||
const data = element.getAttribute('data-settings');
|
||||
if (data) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
}
|
||||
throw new Error(`Could not load settings`);
|
||||
}
|
||||
const settings = getSettings();
|
||||
const iframe = document.querySelector('iframe');
|
||||
const header = document.querySelector('.header');
|
||||
const input = header.querySelector('.url-input');
|
||||
const forwardButton = header.querySelector('.forward-button');
|
||||
const backButton = header.querySelector('.back-button');
|
||||
const reloadButton = header.querySelector('.reload-button');
|
||||
const openExternalButton = header.querySelector('.open-external-button');
|
||||
window.addEventListener('message', e => {
|
||||
switch (e.data.type) {
|
||||
case 'focus':
|
||||
{
|
||||
iframe.focus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
events_1.onceDocumentLoaded(() => {
|
||||
setInterval(() => {
|
||||
var _a;
|
||||
const iframeFocused = ((_a = document.activeElement) === null || _a === void 0 ? void 0 : _a.tagName) === 'IFRAME';
|
||||
document.body.classList.toggle('iframe-focused', iframeFocused);
|
||||
}, 50);
|
||||
iframe.addEventListener('load', () => {
|
||||
// Noop
|
||||
});
|
||||
input.addEventListener('change', e => {
|
||||
const url = e.target.value;
|
||||
navigateTo(url);
|
||||
});
|
||||
forwardButton.addEventListener('click', () => {
|
||||
history.forward();
|
||||
});
|
||||
backButton.addEventListener('click', () => {
|
||||
history.back();
|
||||
});
|
||||
openExternalButton.addEventListener('click', () => {
|
||||
vscode.postMessage({
|
||||
type: 'openExternal',
|
||||
url: input.value
|
||||
});
|
||||
});
|
||||
reloadButton.addEventListener('click', () => {
|
||||
// This does not seem to trigger what we want
|
||||
// history.go(0);
|
||||
// This incorrectly adds entries to the history but does reload
|
||||
iframe.src = input.value;
|
||||
});
|
||||
navigateTo(settings.url);
|
||||
function navigateTo(url) {
|
||||
iframe.src = url;
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
/***/ })
|
||||
|
||||
/******/ });
|
||||
//# sourceMappingURL=index.js.map
|
1
extensions/simple-browser/media/index.js.map
Normal file
1
extensions/simple-browser/media/index.js.map
Normal file
File diff suppressed because one or more lines are too long
115
extensions/simple-browser/media/main.css
Normal file
115
extensions/simple-browser/media/main.css
Normal file
|
@ -0,0 +1,115 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
:root {
|
||||
--container-paddding: 20px;
|
||||
--input-padding-vertical: 2px;
|
||||
--input-padding-horizontal: 4px;
|
||||
--input-margin-vertical: 4px;
|
||||
--input-margin-horizontal: 0;
|
||||
}
|
||||
|
||||
html, body {
|
||||
height: 100%;
|
||||
min-height: 100%;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
body {
|
||||
display: grid;
|
||||
grid-template-rows: auto 1fr;
|
||||
}
|
||||
|
||||
input:not([type='checkbox']),
|
||||
textarea {
|
||||
display: block;
|
||||
width: 100%;
|
||||
border: none;
|
||||
font-family: var(--vscode-font-family);
|
||||
padding: var(--input-padding-vertical) var(--input-padding-horizontal);
|
||||
color: var(--vscode-input-foreground);
|
||||
outline-color: var(--vscode-input-border);
|
||||
background-color: var(--vscode-input-background);
|
||||
}
|
||||
|
||||
input::placeholder,
|
||||
textarea::placeholder {
|
||||
color: var(--vscode-input-placeholderForeground);
|
||||
}
|
||||
|
||||
button {
|
||||
border: none;
|
||||
padding: var(--input-padding-vertical) var(--input-padding-horizontal);
|
||||
text-align: center;
|
||||
outline: 1px solid transparent;
|
||||
outline-offset: 2px !important;
|
||||
color: var(--vscode-button-foreground);
|
||||
background: var(--vscode-button-background);
|
||||
}
|
||||
|
||||
button:hover:not(:disabled) {
|
||||
cursor: pointer;
|
||||
background: var(--vscode-button-hoverBackground);
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
opacity: 0.5;
|
||||
background: var(--vscode-button-background);
|
||||
}
|
||||
|
||||
button:focus {
|
||||
outline-color: var(--vscode-focusBorder);
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
margin: 0.4em 1em;
|
||||
}
|
||||
|
||||
.url-input {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.controls {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.controls button {
|
||||
display: flex;
|
||||
margin-right: 0.3em;
|
||||
}
|
||||
|
||||
.content {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.iframe-focused-alert {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 1em;
|
||||
background: var(--vscode-editorWidget-background);
|
||||
color: var(--vscode-editorWidget-foreground);
|
||||
padding: 0.2em 0.2em;
|
||||
border-radius: 4px;
|
||||
|
||||
font-size: 8px;
|
||||
font-family: monospace;
|
||||
user-select: none;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.iframe-focused .iframe-focused-alert {
|
||||
display: block;
|
||||
}
|
3
extensions/simple-browser/media/preview-dark.svg
Normal file
3
extensions/simple-browser/media/preview-dark.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 2L1 3V13L2 14H14L15 13V3L14 2H2ZM2 13V3H14V13H2ZM12 5H4V6H12V5ZM3 4V7H13V4H3ZM7 9H3V8H7V9ZM3 12H7V11H3V12ZM12 9H10V11H12V9ZM9 8V12H13V8H9Z" fill="#C5C5C5"/>
|
||||
</svg>
|
After Width: | Height: | Size: 312 B |
3
extensions/simple-browser/media/preview-light.svg
Normal file
3
extensions/simple-browser/media/preview-light.svg
Normal file
|
@ -0,0 +1,3 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill-rule="evenodd" clip-rule="evenodd" d="M2 2L1 3V13L2 14H14L15 13V3L14 2H2ZM2 13V3H14V13H2ZM12 5H4V6H12V5ZM3 4V7H13V4H3ZM7 9H3V8H7V9ZM3 12H7V11H3V12ZM12 9H10V11H12V9ZM9 8V12H13V8H9Z" fill="#424242"/>
|
||||
</svg>
|
After Width: | Height: | Size: 312 B |
68
extensions/simple-browser/package.json
Normal file
68
extensions/simple-browser/package.json
Normal file
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"name": "simple-browser",
|
||||
"displayName": "%displayName%",
|
||||
"description": "%description%",
|
||||
"enableProposedApi": true,
|
||||
"version": "1.0.0",
|
||||
"icon": "icon.png",
|
||||
"publisher": "vscode",
|
||||
"license": "MIT",
|
||||
"aiKey": "AIF-d9b70cd4-b9f9-4d70-929b-a071c400b217",
|
||||
"engines": {
|
||||
"vscode": "^1.50.0"
|
||||
},
|
||||
"main": "./out/extension",
|
||||
"browser": "./dist/browser/extension",
|
||||
"categories": [
|
||||
"Programming Languages"
|
||||
],
|
||||
"activationEvents": [
|
||||
"onCommand:simpleBrowser.show",
|
||||
"onUriOpen:http",
|
||||
"onUriOpen:https"
|
||||
],
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "simpleBrowser.show",
|
||||
"title": "Show",
|
||||
"category": "Simple Browser"
|
||||
}
|
||||
],
|
||||
"configuration": [
|
||||
{
|
||||
"title": "Simple Browser",
|
||||
"properties": {
|
||||
"simpleBrowser.opener.enabled": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"title": "Opener Enabled",
|
||||
"description": "(Experimental) Enables opening http and https urls using the built-in seper browser"
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
"scripts": {
|
||||
"compile": "gulp compile-extension:markdown-language-features && npm run build-preview",
|
||||
"watch": "npm run build-preview && gulp watch-extension:markdown-language-features",
|
||||
"vscode:prepublish": "npm run build-ext && npm run build-preview",
|
||||
"build-ext": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:markdown-language-features ./tsconfig.json",
|
||||
"build-preview": "webpack --mode development",
|
||||
"build-preview-production": "webpack --mode production",
|
||||
"compile-web": "npx webpack-cli --config extension-browser.webpack.config --mode none",
|
||||
"watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose"
|
||||
},
|
||||
"dependencies": {
|
||||
"vscode-codicons": "^0.0.12",
|
||||
"vscode-extension-telemetry": "0.1.1",
|
||||
"vscode-nls": "^4.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^12.11.7",
|
||||
"ts-loader": "^6.2.1",
|
||||
"typescript": "^3.7.3",
|
||||
"webpack": "^4.41.2",
|
||||
"webpack-cli": "^3.3.0"
|
||||
}
|
||||
}
|
28
extensions/simple-browser/package.nls.json
Normal file
28
extensions/simple-browser/package.nls.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"displayName": "Markdown Language Features",
|
||||
"description": "Provides rich language support for Markdown.",
|
||||
"markdown.preview.breaks.desc": "Sets how line-breaks are rendered in the markdown preview. Setting it to 'true' creates a <br> for newlines inside paragraphs.",
|
||||
"markdown.preview.linkify": "Enable or disable conversion of URL-like text to links in the markdown preview.",
|
||||
"markdown.preview.doubleClickToSwitchToEditor.desc": "Double click in the markdown preview to switch to the editor.",
|
||||
"markdown.preview.fontFamily.desc": "Controls the font family used in the markdown preview.",
|
||||
"markdown.preview.fontSize.desc": "Controls the font size in pixels used in the markdown preview.",
|
||||
"markdown.preview.lineHeight.desc": "Controls the line height used in the markdown preview. This number is relative to the font size.",
|
||||
"markdown.preview.markEditorSelection.desc": "Mark the current editor selection in the markdown preview.",
|
||||
"markdown.preview.scrollEditorWithPreview.desc": "When a markdown preview is scrolled, update the view of the editor.",
|
||||
"markdown.preview.scrollPreviewWithEditor.desc": "When a markdown editor is scrolled, update the view of the preview.",
|
||||
"markdown.preview.title": "Open Preview",
|
||||
"markdown.previewSide.title": "Open Preview to the Side",
|
||||
"markdown.showLockedPreviewToSide.title": "Open Locked Preview to the Side",
|
||||
"markdown.showSource.title": "Show Source",
|
||||
"markdown.styles.dec": "A list of URLs or local paths to CSS style sheets to use from the markdown preview. Relative paths are interpreted relative to the folder open in the explorer. If there is no open folder, they are interpreted relative to the location of the markdown file. All '\\' need to be written as '\\\\'.",
|
||||
"markdown.showPreviewSecuritySelector.title": "Change Preview Security Settings",
|
||||
"markdown.trace.desc": "Enable debug logging for the markdown extension.",
|
||||
"markdown.preview.refresh.title": "Refresh Preview",
|
||||
"markdown.preview.toggleLock.title": "Toggle Preview Locking",
|
||||
"configuration.markdown.preview.openMarkdownLinks.description": "Controls how links to other markdown files in the markdown preview should be opened.",
|
||||
"configuration.markdown.preview.openMarkdownLinks.inEditor": "Try to open links in the editor",
|
||||
"configuration.markdown.preview.openMarkdownLinks.inPreview": "Try to open links in the markdown preview",
|
||||
"configuration.markdown.links.openLocation.description": "Controls where links in markdown files should be opened.",
|
||||
"configuration.markdown.links.openLocation.currentGroup": "Open links in the active editor group.",
|
||||
"configuration.markdown.links.openLocation.beside": "Open links beside the active editor."
|
||||
}
|
12
extensions/simple-browser/preview-src/events.ts
Normal file
12
extensions/simple-browser/preview-src/events.ts
Normal file
|
@ -0,0 +1,12 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
export function onceDocumentLoaded(f: () => void) {
|
||||
if (document.readyState === 'loading' || document.readyState as string === 'uninitialized') {
|
||||
document.addEventListener('DOMContentLoaded', f);
|
||||
} else {
|
||||
f();
|
||||
}
|
||||
}
|
86
extensions/simple-browser/preview-src/index.ts
Normal file
86
extensions/simple-browser/preview-src/index.ts
Normal file
|
@ -0,0 +1,86 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { onceDocumentLoaded } from './events';
|
||||
|
||||
declare let acquireVsCodeApi: any;
|
||||
const vscode = acquireVsCodeApi();
|
||||
|
||||
function getSettings() {
|
||||
const element = document.getElementById('simple-browser-settings');
|
||||
if (element) {
|
||||
const data = element.getAttribute('data-settings');
|
||||
if (data) {
|
||||
return JSON.parse(data);
|
||||
}
|
||||
}
|
||||
|
||||
throw new Error(`Could not load settings`);
|
||||
}
|
||||
|
||||
const settings = getSettings();
|
||||
|
||||
const iframe = document.querySelector('iframe')!;
|
||||
const header = document.querySelector('.header')!;
|
||||
const input = header.querySelector<HTMLInputElement>('.url-input')!;
|
||||
const forwardButton = header.querySelector<HTMLButtonElement>('.forward-button')!;
|
||||
const backButton = header.querySelector<HTMLButtonElement>('.back-button')!;
|
||||
const reloadButton = header.querySelector<HTMLButtonElement>('.reload-button')!;
|
||||
const openExternalButton = header.querySelector<HTMLButtonElement>('.open-external-button')!;
|
||||
|
||||
window.addEventListener('message', e => {
|
||||
switch (e.data.type) {
|
||||
case 'focus':
|
||||
{
|
||||
iframe.focus();
|
||||
break;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
onceDocumentLoaded(() => {
|
||||
setInterval(() => {
|
||||
const iframeFocused = document.activeElement?.tagName === 'IFRAME';
|
||||
document.body.classList.toggle('iframe-focused', iframeFocused);
|
||||
}, 50);
|
||||
|
||||
iframe.addEventListener('load', () => {
|
||||
// Noop
|
||||
});
|
||||
|
||||
input.addEventListener('change', e => {
|
||||
const url = (e.target as HTMLInputElement).value;
|
||||
navigateTo(url);
|
||||
});
|
||||
|
||||
forwardButton.addEventListener('click', () => {
|
||||
history.forward();
|
||||
});
|
||||
|
||||
backButton.addEventListener('click', () => {
|
||||
history.back();
|
||||
});
|
||||
|
||||
openExternalButton.addEventListener('click', () => {
|
||||
vscode.postMessage({
|
||||
type: 'openExternal',
|
||||
url: input.value
|
||||
});
|
||||
});
|
||||
|
||||
reloadButton.addEventListener('click', () => {
|
||||
// This does not seem to trigger what we want
|
||||
// history.go(0);
|
||||
|
||||
// This incorrectly adds entries to the history but does reload
|
||||
iframe.src = input.value;
|
||||
});
|
||||
|
||||
navigateTo(settings.url);
|
||||
|
||||
function navigateTo(url: string): void {
|
||||
iframe.src = url;
|
||||
}
|
||||
});
|
12
extensions/simple-browser/preview-src/tsconfig.json
Normal file
12
extensions/simple-browser/preview-src/tsconfig.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"extends": "../../shared.tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./dist/",
|
||||
"jsx": "react",
|
||||
"lib": [
|
||||
"es2018",
|
||||
"DOM",
|
||||
"DOM.Iterable"
|
||||
]
|
||||
}
|
||||
}
|
51
extensions/simple-browser/src/extension.ts
Normal file
51
extensions/simple-browser/src/extension.ts
Normal file
|
@ -0,0 +1,51 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { SimpleBrowserManager } from './simpleBrowserManager';
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
const openCommand = 'simpleBrowser.open';
|
||||
const showCommand = 'simpleBrowser.show';
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
|
||||
const manager = new SimpleBrowserManager(context.extensionUri);
|
||||
context.subscriptions.push(manager);
|
||||
|
||||
context.subscriptions.push(vscode.commands.registerCommand(showCommand, async (url?: string) => {
|
||||
if (!url) {
|
||||
url = await vscode.window.showInputBox({
|
||||
placeHolder: localize('simpleBrowser.show.placeholder', "https://example.com"),
|
||||
prompt: localize('simpleBrowser.show.prompt', "Enter url to visit")
|
||||
});
|
||||
}
|
||||
|
||||
if (url) {
|
||||
manager.show(url);
|
||||
}
|
||||
}));
|
||||
|
||||
context.subscriptions.push(vscode.commands.registerCommand(openCommand, (url: vscode.Uri) => {
|
||||
manager.show(url.toString());
|
||||
}));
|
||||
|
||||
context.subscriptions.push(vscode.window.registerExternalUriOpener(['http', 'https'], {
|
||||
openExternalUri(uri: vscode.Uri): vscode.Command | undefined {
|
||||
const configuration = vscode.workspace.getConfiguration('simpleBrowser');
|
||||
if (!configuration.get('opener.enabled', false)) {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
title: localize('openTitle', "Open in simple browser"),
|
||||
command: openCommand,
|
||||
arguments: [uri]
|
||||
};
|
||||
}
|
||||
}));
|
||||
}
|
38
extensions/simple-browser/src/simpleBrowserManager.ts
Normal file
38
extensions/simple-browser/src/simpleBrowserManager.ts
Normal file
|
@ -0,0 +1,38 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import { SimpleBrowserView } from './simpleBrowserView';
|
||||
|
||||
export class SimpleBrowserManager {
|
||||
|
||||
private _activeView?: SimpleBrowserView;
|
||||
|
||||
constructor(
|
||||
private readonly extensionUri: vscode.Uri,
|
||||
) { }
|
||||
|
||||
dispose() {
|
||||
this._activeView?.dispose();
|
||||
this._activeView = undefined;
|
||||
}
|
||||
|
||||
public show(url: string): void {
|
||||
if (this._activeView) {
|
||||
this._activeView.show(url);
|
||||
} else {
|
||||
const view = new SimpleBrowserView(this.extensionUri, url);
|
||||
view.onDispose(() => {
|
||||
if (this._activeView === view) {
|
||||
this._activeView = undefined;
|
||||
}
|
||||
});
|
||||
|
||||
this._activeView = view;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
131
extensions/simple-browser/src/simpleBrowserView.ts
Normal file
131
extensions/simple-browser/src/simpleBrowserView.ts
Normal file
|
@ -0,0 +1,131 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import * as vscode from 'vscode';
|
||||
import * as nls from 'vscode-nls';
|
||||
|
||||
const localize = nls.loadMessageBundle();
|
||||
|
||||
export class SimpleBrowserView {
|
||||
|
||||
public static readonly viewType = 'simpleBrowser.view';
|
||||
private static readonly title = localize('view.title', "Simple Browser");
|
||||
|
||||
private readonly _webviewPanel: vscode.WebviewPanel;
|
||||
|
||||
private readonly _onDidDispose = new vscode.EventEmitter<void>();
|
||||
public readonly onDispose = this._onDidDispose.event;
|
||||
|
||||
constructor(
|
||||
private readonly extensionUri: vscode.Uri,
|
||||
url: string,
|
||||
) {
|
||||
this._webviewPanel = vscode.window.createWebviewPanel(SimpleBrowserView.viewType, SimpleBrowserView.title, {
|
||||
viewColumn: vscode.ViewColumn.Active,
|
||||
}, {
|
||||
enableScripts: true,
|
||||
retainContextWhenHidden: true,
|
||||
});
|
||||
|
||||
this._webviewPanel.webview.onDidReceiveMessage(e => {
|
||||
switch (e.type) {
|
||||
case 'openExternal':
|
||||
try {
|
||||
const url = vscode.Uri.parse(e.url);
|
||||
vscode.env.openExternal(url);
|
||||
} catch {
|
||||
// Noop
|
||||
}
|
||||
break;
|
||||
}
|
||||
});
|
||||
|
||||
this._webviewPanel.onDidDispose(() => {
|
||||
this.dispose();
|
||||
});
|
||||
|
||||
this.show(url);
|
||||
}
|
||||
|
||||
public dispose() {
|
||||
this._onDidDispose.fire();
|
||||
this._webviewPanel.dispose();
|
||||
}
|
||||
|
||||
public show(url: string) {
|
||||
this._webviewPanel.webview.html = this.getHtml(url);
|
||||
this._webviewPanel.reveal();
|
||||
}
|
||||
|
||||
private getHtml(url: string) {
|
||||
const nonce = new Date().getTime() + '' + new Date().getMilliseconds();
|
||||
|
||||
const mainJs = this.extensionResourceUrl('media', 'index.js');
|
||||
const mainCss = this.extensionResourceUrl('media', 'main.css');
|
||||
const codiconsUri = this.extensionResourceUrl('node_modules', 'vscode-codicons', 'dist', 'codicon.css');
|
||||
const codiconsFontUri = this.extensionResourceUrl('node_modules', 'vscode-codicons', 'dist', 'codicon.ttf');
|
||||
|
||||
return /* html */ `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-type" content="text/html;charset=UTF-8">
|
||||
|
||||
<meta http-equiv="Content-Security-Policy" content="
|
||||
default-src 'none';
|
||||
font-src ${codiconsFontUri};
|
||||
style-src ${this._webviewPanel.webview.cspSource};
|
||||
script-src 'nonce-${nonce}';
|
||||
frame-src *;
|
||||
">
|
||||
|
||||
<meta id="simple-browser-settings" data-settings="${escapeAttribute(JSON.stringify({
|
||||
url: url,
|
||||
}))}">
|
||||
|
||||
<link rel="stylesheet" type="text/css" href="${mainCss}">
|
||||
<link rel="stylesheet" type="text/css" href="${codiconsUri}">
|
||||
</head>
|
||||
<body>
|
||||
<header class="header">
|
||||
<nav class="controls">
|
||||
<button
|
||||
title="${localize('control.back.title', "Back")}"
|
||||
class="back-button icon"><i class="codicon codicon-arrow-left"></i></button>
|
||||
|
||||
<button
|
||||
title="${localize('control.forward.title', "Forward")}"
|
||||
class="forward-button icon"><i class="codicon codicon-arrow-right"></i></button>
|
||||
|
||||
<button
|
||||
title="${localize('control.reload.title', "Reload")}"
|
||||
class="reload-button icon"><i class="codicon codicon-refresh"></i></button>
|
||||
</nav>
|
||||
|
||||
<input class="url-input" type="text" value=${url}>
|
||||
|
||||
<nav class="controls">
|
||||
<button
|
||||
title="${localize('control.openExternal.title', "Open in browser")}"
|
||||
class="open-external-button icon"><i class="codicon codicon-link-external"></i></button>
|
||||
</nav>
|
||||
</header>
|
||||
<div class="content">
|
||||
<div class="iframe-focused-alert">${localize('view.iframe-focused', "Focus Lock")}</div>
|
||||
<iframe sandbox="allow-scripts allow-forms allow-same-origin"></iframe>
|
||||
</div>
|
||||
|
||||
<script src="${mainJs}" nonce="${nonce}"></script>
|
||||
</body>
|
||||
</html>`;
|
||||
}
|
||||
|
||||
private extensionResourceUrl(...parts: string[]): vscode.Uri {
|
||||
return this._webviewPanel.webview.asWebviewUri(vscode.Uri.joinPath(this.extensionUri, ...parts));
|
||||
}
|
||||
}
|
||||
|
||||
function escapeAttribute(value: string | vscode.Uri): string {
|
||||
return value.toString().replace(/"/g, '"');
|
||||
}
|
7
extensions/simple-browser/src/typings/ref.d.ts
vendored
Normal file
7
extensions/simple-browser/src/typings/ref.d.ts
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
/// <reference path='../../../../src/vs/vscode.d.ts'/>
|
||||
/// <reference path='../../../../src/vs/vscode.proposed.d.ts'/>
|
10
extensions/simple-browser/tsconfig.json
Normal file
10
extensions/simple-browser/tsconfig.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"extends": "../shared.tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "./out",
|
||||
"experimentalDecorators": true
|
||||
},
|
||||
"include": [
|
||||
"src/**/*"
|
||||
]
|
||||
}
|
28
extensions/simple-browser/webpack.config.js
Normal file
28
extensions/simple-browser/webpack.config.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
/*---------------------------------------------------------------------------------------------
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
const path = require('path');
|
||||
|
||||
module.exports = {
|
||||
entry: {
|
||||
index: './preview-src/index.ts',
|
||||
},
|
||||
devtool: 'source-map',
|
||||
module: {
|
||||
rules: [
|
||||
{
|
||||
test: /\.tsx?$/,
|
||||
use: 'ts-loader',
|
||||
exclude: /node_modules/
|
||||
}
|
||||
]
|
||||
},
|
||||
resolve: {
|
||||
extensions: ['.tsx', '.ts', '.js']
|
||||
},
|
||||
output: {
|
||||
filename: '[name].js',
|
||||
path: path.resolve(__dirname, 'media')
|
||||
}
|
||||
};
|
2732
extensions/simple-browser/yarn.lock
Normal file
2732
extensions/simple-browser/yarn.lock
Normal file
File diff suppressed because it is too large
Load diff
|
@ -301,6 +301,7 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider<TsCodeActi
|
|||
const args: Proto.GetApplicableRefactorsRequestArgs = {
|
||||
...typeConverters.Range.toFileRangeRequestArgs(file, rangeOrSelection),
|
||||
triggerReason: this.toTsTriggerReason(context),
|
||||
kind: context.only?.value
|
||||
};
|
||||
return this.client.execute('getApplicableRefactors', args, token);
|
||||
});
|
||||
|
@ -384,6 +385,9 @@ class TypeScriptRefactorProvider implements vscode.CodeActionProvider<TsCodeActi
|
|||
}
|
||||
|
||||
private static getKind(refactor: Proto.RefactorActionInfo) {
|
||||
if (refactor.kind) {
|
||||
return vscode.CodeActionKind.Empty.append(refactor.kind);
|
||||
}
|
||||
const match = allKnownCodeActionKinds.find(kind => kind.matches(refactor));
|
||||
return match ? match.kind : vscode.CodeActionKind.Refactor;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue