replace mutation observer with dom#asDomUri, #75061
This commit is contained in:
parent
7c88e07cc6
commit
7980cd2697
|
@ -14,6 +14,8 @@ import { Emitter, Event } from 'vs/base/common/event';
|
|||
import { Disposable, IDisposable, toDisposable } from 'vs/base/common/lifecycle';
|
||||
import * as platform from 'vs/base/common/platform';
|
||||
import { coalesce } from 'vs/base/common/arrays';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Schemas } from 'vs/base/common/network';
|
||||
|
||||
export function clearNode(node: HTMLElement): void {
|
||||
while (node.firstChild) {
|
||||
|
@ -1181,3 +1183,23 @@ export function animate(fn: () => void): IDisposable {
|
|||
let stepDisposable = scheduleAtNextAnimationFrame(step);
|
||||
return toDisposable(() => stepDisposable.dispose());
|
||||
}
|
||||
|
||||
|
||||
|
||||
const _location = URI.parse(window.location.href);
|
||||
|
||||
export function asDomUri(uri: URI): URI {
|
||||
if (!uri) {
|
||||
return uri;
|
||||
}
|
||||
if (!platform.isWeb) {
|
||||
//todo@joh remove this once we have sw in electron going
|
||||
return uri;
|
||||
}
|
||||
if (Schemas.vscodeRemote === uri.scheme) {
|
||||
// rewrite vscode-remote-uris to uris of the window location
|
||||
// so that they can be intercepted by the service worker
|
||||
return _location.with({ path: '/vscode-resources/fetch', query: uri.toString() });
|
||||
}
|
||||
return uri;
|
||||
}
|
||||
|
|
|
@ -75,12 +75,15 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions
|
|||
return encodeURIComponent(JSON.stringify(data));
|
||||
};
|
||||
|
||||
const _href = function (href: string): string {
|
||||
const _href = function (href: string, isDomUri: boolean): string {
|
||||
const data = markdown.uris && markdown.uris[href];
|
||||
if (!data) {
|
||||
return href;
|
||||
}
|
||||
let uri = URI.revive(data);
|
||||
if (isDomUri) {
|
||||
uri = DOM.asDomUri(uri);
|
||||
}
|
||||
if (uri.query) {
|
||||
uri = uri.with({ query: _uriMassage(uri.query) });
|
||||
}
|
||||
|
@ -97,7 +100,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions
|
|||
|
||||
const renderer = new marked.Renderer();
|
||||
renderer.image = (href: string, title: string, text: string) => {
|
||||
href = _href(href);
|
||||
href = _href(href, true);
|
||||
let dimensions: string[] = [];
|
||||
if (href) {
|
||||
const splitted = href.split('|').map(s => s.trim());
|
||||
|
@ -138,7 +141,7 @@ export function renderMarkdown(markdown: IMarkdownString, options: RenderOptions
|
|||
if (href === text) { // raw link case
|
||||
text = removeMarkdownEscapes(text);
|
||||
}
|
||||
href = _href(href);
|
||||
href = _href(href, false);
|
||||
title = removeMarkdownEscapes(title);
|
||||
href = removeMarkdownEscapes(href);
|
||||
if (
|
||||
|
|
|
@ -399,7 +399,7 @@ class DecorationCSSRules {
|
|||
if (typeof opts !== 'undefined') {
|
||||
this.collectBorderSettingsCSSText(opts, cssTextArr);
|
||||
if (typeof opts.contentIconPath !== 'undefined') {
|
||||
cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, URI.revive(opts.contentIconPath).toString(true).replace(/'/g, '%27')));
|
||||
cssTextArr.push(strings.format(_CSS_MAP.contentIconPath, dom.asDomUri(URI.revive(opts.contentIconPath)).toString(true).replace(/'/g, '%27')));
|
||||
}
|
||||
if (typeof opts.contentText === 'string') {
|
||||
const truncated = opts.contentText.match(/^.*$/m)![0]; // only take first line
|
||||
|
@ -426,7 +426,7 @@ class DecorationCSSRules {
|
|||
const cssTextArr: string[] = [];
|
||||
|
||||
if (typeof opts.gutterIconPath !== 'undefined') {
|
||||
cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, URI.revive(opts.gutterIconPath).toString(true).replace(/'/g, '%27')));
|
||||
cssTextArr.push(strings.format(_CSS_MAP.gutterIconPath, dom.asDomUri(URI.revive(opts.gutterIconPath)).toString(true).replace(/'/g, '%27')));
|
||||
if (typeof opts.gutterIconSize !== 'undefined') {
|
||||
cssTextArr.push(strings.format(_CSS_MAP.gutterIconSize, opts.gutterIconSize));
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
* Licensed under the MIT License. See License.txt in the project root for license information.
|
||||
*--------------------------------------------------------------------------------------------*/
|
||||
|
||||
import { addClasses, createCSSRule, removeClasses } from 'vs/base/browser/dom';
|
||||
import { addClasses, createCSSRule, removeClasses, asDomUri } from 'vs/base/browser/dom';
|
||||
import { domEvent } from 'vs/base/browser/event';
|
||||
import { ActionViewItem, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
|
||||
import { IAction } from 'vs/base/common/actions';
|
||||
|
@ -244,8 +244,8 @@ export class MenuEntryActionViewItem extends ActionViewItem {
|
|||
iconClass = MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.get(iconPathMapKey)!;
|
||||
} else {
|
||||
iconClass = ids.nextId();
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: url("${(item.iconLocation.light || item.iconLocation.dark).toString()}")`);
|
||||
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${item.iconLocation.dark.toString()}")`);
|
||||
createCSSRule(`.icon.${iconClass}`, `background-image: url("${asDomUri(item.iconLocation.light || item.iconLocation.dark).toString()}")`);
|
||||
createCSSRule(`.vs-dark .icon.${iconClass}, .hc-black .icon.${iconClass}`, `background-image: url("${asDomUri(item.iconLocation.dark).toString()}")`);
|
||||
MenuEntryActionViewItem.ICON_PATH_TO_CSS_RULES.set(iconPathMapKey, iconClass);
|
||||
}
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet';
|
|||
import { IEditorGroupsService } from 'vs/workbench/services/editor/common/editorGroupsService';
|
||||
import { IWorkbenchActionRegistry, Extensions as ActionExtensions } from 'vs/workbench/common/actions';
|
||||
import { SyncActionDescriptor } from 'vs/platform/actions/common/actions';
|
||||
import { createCSSRule } from 'vs/base/browser/dom';
|
||||
import { createCSSRule, asDomUri } from 'vs/base/browser/dom';
|
||||
|
||||
export interface IUserFriendlyViewsContainerDescriptor {
|
||||
id: string;
|
||||
|
@ -327,7 +327,7 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
|||
|
||||
// Generate CSS to show the icon in the activity bar
|
||||
const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${cssClass}`;
|
||||
createCSSRule(iconClass, `-webkit-mask: url('${icon}') no-repeat 50% 50%; -webkit-mask-size: 24px;`);
|
||||
createCSSRule(iconClass, `-webkit-mask: url('${asDomUri(icon)}') no-repeat 50% 50%; -webkit-mask-size: 24px;`);
|
||||
}
|
||||
|
||||
return viewContainer;
|
||||
|
@ -456,4 +456,4 @@ class ViewsExtensionHandler implements IWorkbenchContribution {
|
|||
}
|
||||
|
||||
const workbenchRegistry = Registry.as<IWorkbenchContributionsRegistry>(WorkbenchExtensions.Workbench);
|
||||
workbenchRegistry.registerWorkbenchContribution(ViewsExtensionHandler, LifecyclePhase.Starting);
|
||||
workbenchRegistry.registerWorkbenchContribution(ViewsExtensionHandler, LifecyclePhase.Starting);
|
||||
|
|
|
@ -173,7 +173,7 @@ export class PlaceHolderViewletActivityAction extends ViewletActivityAction {
|
|||
super({ id, name: id, cssClass: `extensionViewlet-placeholder-${id.replace(/\./g, '-')}` }, viewletService, layoutService, telemetryService);
|
||||
|
||||
const iconClass = `.monaco-workbench .activitybar .monaco-action-bar .action-label.${this.class}`; // Generate Placeholder CSS to show the icon in the activity bar
|
||||
DOM.createCSSRule(iconClass, `-webkit-mask: url('${iconUrl || ''}') no-repeat 50% 50%; -webkit-mask-size: 24px;`);
|
||||
DOM.createCSSRule(iconClass, `-webkit-mask: url('${DOM.asDomUri(iconUrl) || ''}') no-repeat 50% 50%; -webkit-mask-size: 24px;`);
|
||||
}
|
||||
|
||||
setActivity(activity: IActivity): void {
|
||||
|
|
|
@ -22,8 +22,8 @@ export function getIconClass(iconPath: { dark: URI; light?: URI; } | undefined):
|
|||
iconClass = iconPathToClass[key];
|
||||
} else {
|
||||
iconClass = iconClassGenerator.nextId();
|
||||
dom.createCSSRule(`.${iconClass}`, `background-image: url("${(iconPath.light || iconPath.dark).toString()}")`);
|
||||
dom.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: url("${iconPath.dark.toString()}")`);
|
||||
dom.createCSSRule(`.${iconClass}`, `background-image: url("${dom.asDomUri(iconPath.light || iconPath.dark).toString()}")`);
|
||||
dom.createCSSRule(`.vs-dark .${iconClass}, .hc-black .${iconClass}`, `background-image: url("${dom.asDomUri(iconPath.dark).toString()}")`);
|
||||
iconPathToClass[key] = iconClass;
|
||||
}
|
||||
|
||||
|
|
|
@ -674,7 +674,7 @@ class TreeRenderer implements IRenderer {
|
|||
templateData.resourceLabel.setResource({ name: label, description }, { title, hideIcon: true, extraClasses: ['custom-view-tree-node-item-resourceLabel'], matches });
|
||||
}
|
||||
|
||||
templateData.icon.style.backgroundImage = iconUrl ? `url('${iconUrl.toString(true)}')` : '';
|
||||
templateData.icon.style.backgroundImage = iconUrl ? `url('${DOM.asDomUri(iconUrl).toString(true)}')` : '';
|
||||
DOM.toggleClass(templateData.icon, 'custom-view-tree-node-item-icon', !!iconUrl);
|
||||
templateData.actionBar.context = (<TreeViewItemHandleArg>{ $treeViewId: this.treeViewId, $treeItemHandle: node.handle });
|
||||
templateData.actionBar.push(this.menus.getResourceActions(node), { icon: true, label: false });
|
||||
|
|
|
@ -10,88 +10,6 @@ import { Registry } from 'vs/platform/registry/common/platform';
|
|||
import { IWorkbenchContributionsRegistry, Extensions } from 'vs/workbench/common/contributions';
|
||||
import { LifecyclePhase } from 'vs/platform/lifecycle/common/lifecycle';
|
||||
|
||||
// todo@joh explore alternative, explicit approach
|
||||
class ResourcesMutationObserver {
|
||||
|
||||
private readonly _urlCache = new Map<string, string>();
|
||||
private readonly _observer: MutationObserver;
|
||||
|
||||
private readonly _regexp = /url\(('|")?(vscode-remote:\/\/(.*?))\1\)/ig;
|
||||
|
||||
constructor() {
|
||||
this._observer = new MutationObserver(r => this._handleMutation(r));
|
||||
this._observer.observe(document, {
|
||||
subtree: true,
|
||||
childList: true,
|
||||
attributes: true,
|
||||
attributeFilter: ['style']
|
||||
});
|
||||
this.scan();
|
||||
}
|
||||
|
||||
scan(): void {
|
||||
document.querySelectorAll('style').forEach(value => this._handleStyleNode(value));
|
||||
// todo@joh more!
|
||||
}
|
||||
|
||||
dispose(): void {
|
||||
this._observer.disconnect();
|
||||
this._urlCache.forEach(value => URL.revokeObjectURL(value));
|
||||
}
|
||||
|
||||
private _handleMutation(records: MutationRecord[]): void {
|
||||
for (const record of records) {
|
||||
if (record.target.nodeName === 'STYLE') {
|
||||
// style-element directly modified
|
||||
this._handleStyleNode(record.target);
|
||||
|
||||
} else if (record.target.nodeName === 'HEAD' && record.type === 'childList') {
|
||||
// style-element added to head
|
||||
record.addedNodes.forEach(node => {
|
||||
if (node.nodeName === 'STYLE') {
|
||||
this._handleStyleNode(node);
|
||||
}
|
||||
});
|
||||
} else if (record.type === 'attributes') {
|
||||
// style-attribute
|
||||
this._handleAttrMutation(record.target);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private _handleStyleNode(target: Node): void {
|
||||
if (target.textContent && target.textContent.indexOf('vscode-remote://') >= 0) {
|
||||
const content = target.textContent;
|
||||
this._rewriteUrls(content).then(value => {
|
||||
if (content === target.textContent) {
|
||||
target.textContent = value;
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private _handleAttrMutation(target: Node): void {
|
||||
const styleValue = (<HTMLElement>target).getAttribute('style');
|
||||
if (styleValue && styleValue.indexOf('vscode-remote://') >= 0) {
|
||||
this._rewriteUrls(styleValue).then(value => {
|
||||
if (value !== styleValue) {
|
||||
(<HTMLElement>target).setAttribute('style', value);
|
||||
}
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async _rewriteUrls(textContent: string): Promise<string> {
|
||||
return textContent.replace(this._regexp, function (_m, quote = '', url) {
|
||||
return `url(${quote}${location.href}vscode-resources/fetch?${encodeURIComponent(url)}${quote})`;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class ResourceServiceWorker {
|
||||
|
||||
private readonly _disposables = new DisposableStore();
|
||||
|
@ -114,7 +32,6 @@ class ResourceServiceWorker {
|
|||
return navigator.serviceWorker.ready;
|
||||
}).then(() => {
|
||||
// console.log('ready');
|
||||
this._disposables.add(new ResourcesMutationObserver());
|
||||
}).catch(err => {
|
||||
console.error(err);
|
||||
});
|
||||
|
|
|
@ -39,10 +39,10 @@ export class WebviewEditorInput extends EditorInput {
|
|||
this._icons.forEach((value, key) => {
|
||||
const webviewSelector = `.show-file-icons .webview-${key}-name-file-icon::before`;
|
||||
if (URI.isUri(value)) {
|
||||
cssRules.push(`${webviewSelector} { content: ""; background-image: url(${value.toString()}); }`);
|
||||
cssRules.push(`${webviewSelector} { content: ""; background-image: url(${dom.asDomUri(value).toString()}); }`);
|
||||
} else {
|
||||
cssRules.push(`.vs ${webviewSelector} { content: ""; background-image: url(${value.light.toString()}); }`);
|
||||
cssRules.push(`.vs-dark ${webviewSelector} { content: ""; background-image: url(${value.dark.toString()}); }`);
|
||||
cssRules.push(`.vs ${webviewSelector} { content: ""; background-image: url(${dom.asDomUri(value.light).toString()}); }`);
|
||||
cssRules.push(`.vs-dark ${webviewSelector} { content: ""; background-image: url(${dom.asDomUri(value.dark).toString()}); }`);
|
||||
}
|
||||
});
|
||||
this._styleElement.innerHTML = cssRules.join('\n');
|
||||
|
|
|
@ -11,6 +11,7 @@ import * as Json from 'vs/base/common/json';
|
|||
import { ExtensionData, IThemeExtensionPoint, IFileIconTheme } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IFileService } from 'vs/platform/files/common/files';
|
||||
import { getParseErrorMessage } from 'vs/base/common/jsonErrorMessages';
|
||||
import { asDomUri } from 'vs/base/browser/dom';
|
||||
|
||||
export class FileIconThemeData implements IFileIconTheme {
|
||||
id: string;
|
||||
|
@ -331,7 +332,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i
|
|||
let fonts = iconThemeDocument.fonts;
|
||||
if (Array.isArray(fonts)) {
|
||||
fonts.forEach(font => {
|
||||
let src = font.src.map(l => `url('${resolvePath(l.path)}') format('${l.format}')`).join(', ');
|
||||
let src = font.src.map(l => `url('${asDomUri(resolvePath(l.path))}') format('${l.format}')`).join(', ');
|
||||
cssRules.push(`@font-face { src: ${src}; font-family: '${font.id}'; font-weight: ${font.weight}; font-style: ${font.style}; }`);
|
||||
});
|
||||
cssRules.push(`.show-file-icons .file-icon::before, .show-file-icons .folder-icon::before, .show-file-icons .rootfolder-icon::before { font-family: '${fonts[0].id}'; font-size: ${fonts[0].size || '150%'}}`);
|
||||
|
@ -342,7 +343,7 @@ function _processIconThemeDocument(id: string, iconThemeDocumentLocation: URI, i
|
|||
let definition = iconThemeDocument.iconDefinitions[defId];
|
||||
if (definition) {
|
||||
if (definition.iconPath) {
|
||||
cssRules.push(`${selectors.join(', ')} { content: ' '; background-image: url("${resolvePath(definition.iconPath)}"); }`);
|
||||
cssRules.push(`${selectors.join(', ')} { content: ' '; background-image: url("${asDomUri(resolvePath(definition.iconPath))}"); }`);
|
||||
}
|
||||
if (definition.fontCharacter || definition.fontColor) {
|
||||
let body = '';
|
|
@ -11,7 +11,7 @@ import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/workbench/serv
|
|||
import { ExtensionData, IThemeExtensionPoint } from 'vs/workbench/services/themes/common/workbenchThemeService';
|
||||
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
|
||||
import { Event, Emitter } from 'vs/base/common/event';
|
||||
import { FileIconThemeData } from 'vs/workbench/services/themes/common/fileIconThemeData';
|
||||
import { FileIconThemeData } from 'vs/workbench/services/themes/browser/fileIconThemeData';
|
||||
import { URI } from 'vs/base/common/uri';
|
||||
import { Disposable } from 'vs/base/common/lifecycle';
|
||||
|
|
@ -19,8 +19,8 @@ import { Event, Emitter } from 'vs/base/common/event';
|
|||
import { registerFileIconThemeSchemas } from 'vs/workbench/services/themes/common/fileIconThemeSchema';
|
||||
import { IDisposable, dispose } from 'vs/base/common/lifecycle';
|
||||
import { ColorThemeStore } from 'vs/workbench/services/themes/common/colorThemeStore';
|
||||
import { FileIconThemeStore } from 'vs/workbench/services/themes/common/fileIconThemeStore';
|
||||
import { FileIconThemeData } from 'vs/workbench/services/themes/common/fileIconThemeData';
|
||||
import { FileIconThemeStore } from 'vs/workbench/services/themes/browser/fileIconThemeStore';
|
||||
import { FileIconThemeData } from 'vs/workbench/services/themes/browser/fileIconThemeData';
|
||||
import { removeClasses, addClasses } from 'vs/base/browser/dom';
|
||||
import { IWorkbenchEnvironmentService } from 'vs/workbench/services/environment/common/environmentService';
|
||||
import { IFileService, FileChangeType } from 'vs/platform/files/common/files';
|
||||
|
@ -696,4 +696,4 @@ const tokenColorCustomizationConfiguration: IConfigurationNode = {
|
|||
};
|
||||
configurationRegistry.registerConfiguration(tokenColorCustomizationConfiguration);
|
||||
|
||||
registerSingleton(IWorkbenchThemeService, WorkbenchThemeService);
|
||||
registerSingleton(IWorkbenchThemeService, WorkbenchThemeService);
|
||||
|
|
Loading…
Reference in a new issue