Skip to content

Commit

Permalink
first cut/experiment of icon/resouce loading, microsoft#75061
Browse files Browse the repository at this point in the history
  • Loading branch information
jrieken committed Jun 7, 2019
1 parent d941578 commit 2024296
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 1 deletion.
6 changes: 5 additions & 1 deletion src/vs/workbench/browser/web.main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { IWorkspaceInitializationPayload } from 'vs/platform/workspaces/common/w
import { WorkspaceService } from 'vs/workbench/services/configuration/browser/configurationService';
import { ConfigurationCache } from 'vs/workbench/services/configuration/browser/configurationCache';
import { ConfigurationFileService } from 'vs/workbench/services/configuration/common/configuration';
import { WebResources } from 'vs/workbench/browser/web.resources';

interface IWindowConfiguration {
settingsUri: URI;
Expand Down Expand Up @@ -61,6 +62,9 @@ class CodeRendererMain extends Disposable {
// Layout
this._register(addDisposableListener(window, EventType.RESIZE, () => this.workbench.layout()));

// Resource Loading
this._register(new WebResources(<IFileService>services.serviceCollection.get(IFileService)));

// Workbench Lifecycle
this._register(this.workbench.onShutdown(() => this.dispose()));

Expand Down Expand Up @@ -188,4 +192,4 @@ function toResource(path: string): URI {
authority: document.location.host,
path
});
}
}
98 changes: 98 additions & 0 deletions src/vs/workbench/browser/web.resources.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { IFileService } from 'vs/platform/files/common/files';
import { URI } from 'vs/base/common/uri';

export class WebResources {

private readonly _regexp = /url\(('|")?(vscode-remote:\/\/.*?)\1\)/g;
private readonly _cache = new Map<string, string>();
private readonly _observer: MutationObserver;

constructor(@IFileService private readonly _fileService: IFileService) {
this._observer = new MutationObserver(r => this._handleMutation(r));

// todo@joh add observer to more than head-element
// todo@joh explore alternative approach
this._observer.observe(document.head, { subtree: true, childList: true });
}

dispose(): void {
this._observer.disconnect();
this._cache.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);
}
});
}
}
}

private _handleStyleNode(target: Node): void {

if (!target.textContent) {
return;
}

const positions: number[] = [];
const promises: Promise<any>[] = [];

let match: RegExpMatchArray | null = null;
while (match = this._regexp.exec(target.textContent)) {

const remoteUrl = match[2];
positions.push(match.index! + 'url('.length + match[1].length);
positions.push(remoteUrl.length);

if (this._cache.has(remoteUrl)) {
promises.push(Promise.resolve());

} else {
promises.push(this._fileService.readFile(URI.parse(remoteUrl, true)).then(file => {
// todo@joh hack
const type = /\.woff$/.test(remoteUrl) ? 'application/font-woff' : 'image/svg+xml';
this._cache.set(remoteUrl, URL.createObjectURL(new Blob([file.value.buffer], { type })));
}));
}
}

if (promises.length === 0) {
return;
}

let content = target.textContent;

Promise.all(promises).then(() => {

if (target.textContent !== content) {
return;
}

for (let i = positions.length - 1; i >= 0; i -= 2) {
const start = positions[i - 1];
const len = positions[i];
const url = this._cache.get(content.substr(start, len));
content = content.substring(0, start) + url + content.substring(start + len);
}

target.textContent = content;

}).catch(e => {
console.error(e);
});
}
}

0 comments on commit 2024296

Please sign in to comment.