提交 481485b3 编写于 作者: J Johannes Rieken

use string instead of URL, help with IE11 and with unwanted URL magic

上级 65159d00
......@@ -361,25 +361,3 @@ export function toLocalResource(resource: URI, authority: string | undefined): U
return resource.with({ scheme: Schemas.file });
}
export function matchesScheme(target: URI | URL, scheme: string) {
if (URI.isUri(target)) {
return equalsIgnoreCase(target.scheme, scheme);
} else {
return equalsIgnoreCase(target.protocol, scheme + ':');
}
}
/**
* `true` when urls can be constructed via `new URL("string")`, should only be `false` in IE:
* https://developer.mozilla.org/en-US/docs/Web/API/URL/URL#Browser_compatibility
*/
export const hasURLCtor = (function () {
try {
// tslint:disable-next-line: no-unused-expression
new URL('some://thing');
return true;
} catch {
return false;
}
})();
......@@ -8,11 +8,11 @@ import { IDisposable } from 'vs/base/common/lifecycle';
import { LinkedList } from 'vs/base/common/linkedList';
import { parse } from 'vs/base/common/marshalling';
import { Schemas } from 'vs/base/common/network';
import { matchesScheme, normalizePath } from 'vs/base/common/resources';
import { normalizePath } from 'vs/base/common/resources';
import { URI } from 'vs/base/common/uri';
import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService';
import { CommandsRegistry, ICommandService } from 'vs/platform/commands/common/commands';
import { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, ResolveExternalUriOptions, IResolvedExternalUri, IExternalOpener } from 'vs/platform/opener/common/opener';
import { IOpener, IOpenerService, IValidator, IExternalUriResolver, OpenOptions, ResolveExternalUriOptions, IResolvedExternalUri, IExternalOpener, matchesScheme } from 'vs/platform/opener/common/opener';
import { EditorOpenContext } from 'vs/platform/editor/common/editor';
......@@ -43,7 +43,7 @@ export class OpenerService implements IOpenerService {
// Default opener: maito, http(s), command, and catch-all-editors
this._openerAsExternal = {
open: async (target: URI | URL, options?: OpenOptions) => {
open: async (target: URI | string, options?: OpenOptions) => {
if (options?.openExternal || matchesScheme(target, Schemas.mailto) || matchesScheme(target, Schemas.http) || matchesScheme(target, Schemas.https)) {
// open externally
await this._doOpenExternal(target, options);
......@@ -59,8 +59,8 @@ export class OpenerService implements IOpenerService {
return false;
}
// run command or bail out if command isn't known
if (!URI.isUri(target)) {
target = URI.from(target);
if (typeof target === 'string') {
target = URI.parse(target);
}
if (!CommandsRegistry.getCommand(target.path)) {
throw new Error(`command '${target.path}' NOT known`);
......@@ -82,8 +82,8 @@ export class OpenerService implements IOpenerService {
this._openerAsEditor = {
open: async (target, options: OpenOptions) => {
if (!URI.isUri(target)) {
target = URI.from(target);
if (typeof target === 'string') {
target = URI.parse(target);
}
let selection: { startLineNumber: number; startColumn: number; } | undefined = undefined;
const match = /^L?(\d+)(?:,(\d+))?/.exec(target.fragment);
......@@ -132,7 +132,7 @@ export class OpenerService implements IOpenerService {
this._externalOpener = externalOpener;
}
async open(target: URI | URL, options?: OpenOptions): Promise<boolean> {
async open(target: URI | string, options?: OpenOptions): Promise<boolean> {
// check with contributed validators
for (const validator of this._validators.toArray()) {
......@@ -169,7 +169,7 @@ export class OpenerService implements IOpenerService {
return { resolved: resource, dispose: () => { } };
}
private async _doOpenExternal(resource: URI | URL, options: OpenOptions | undefined): Promise<boolean> {
private async _doOpenExternal(resource: URI | string, options: OpenOptions | undefined): Promise<boolean> {
if (URI.isUri(resource)) {
const { resolved } = await this.resolveExternalUri(resource, options);
// TODO@Jo neither encodeURI nor toString(true) should be needed
......@@ -177,7 +177,7 @@ export class OpenerService implements IOpenerService {
return this._externalOpener.openExternal(encodeURI(resolved.toString(true)));
} else {
//todo@joh what about resolveExternalUri?
return this._externalOpener.openExternal(resource.href);
return this._externalOpener.openExternal(resource);
}
}
......
......@@ -13,7 +13,6 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { isDisposable, Disposable } from 'vs/base/common/lifecycle';
import { coalesce } from 'vs/base/common/arrays';
import { hasURLCtor } from 'vs/base/common/resources';
export class Link implements ILink {
......@@ -45,19 +44,9 @@ export class Link implements ILink {
return this._link.tooltip;
}
async resolve(token: CancellationToken): Promise<URI | URL> {
async resolve(token: CancellationToken): Promise<URI | string> {
if (this._link.url) {
try {
if (URI.isUri(this._link.url)) {
return this._link.url;
} else if (!hasURLCtor) {
return URI.parse(this._link.url);
} else {
return new URL(this._link.url);
}
} catch (e) {
return Promise.reject(new Error('invalid'));
}
return this._link.url;
}
if (typeof this._provider.resolveLink === 'function') {
......
......@@ -8,6 +8,7 @@ import { URI } from 'vs/base/common/uri';
import { OpenerService } from 'vs/editor/browser/services/openerService';
import { TestCodeEditorService } from 'vs/editor/test/browser/editorTestServices';
import { CommandsRegistry, ICommandService, NullCommandService } from 'vs/platform/commands/common/commands';
import { matchesScheme } from 'vs/platform/opener/common/opener';
suite('OpenerService', function () {
const editorService = new TestCodeEditorService();
......@@ -199,4 +200,18 @@ suite('OpenerService', function () {
assert.equal(v1, 2);
assert.equal(v2, 0);
});
test('matchesScheme', function () {
assert.ok(matchesScheme('https://microsoft.com', 'https'));
assert.ok(matchesScheme('http://microsoft.com', 'http'));
assert.ok(matchesScheme('hTTPs://microsoft.com', 'https'));
assert.ok(matchesScheme('httP://microsoft.com', 'http'));
assert.ok(matchesScheme(URI.parse('https://microsoft.com'), 'https'));
assert.ok(matchesScheme(URI.parse('http://microsoft.com'), 'http'));
assert.ok(matchesScheme(URI.parse('hTTPs://microsoft.com'), 'https'));
assert.ok(matchesScheme(URI.parse('httP://microsoft.com'), 'http'));
assert.ok(!matchesScheme(URI.parse('https://microsoft.com'), 'http'));
assert.ok(!matchesScheme(URI.parse('htt://microsoft.com'), 'http'));
assert.ok(!matchesScheme(URI.parse('z://microsoft.com'), 'http'));
});
});
......@@ -6,6 +6,7 @@
import { URI } from 'vs/base/common/uri';
import { createDecorator } from 'vs/platform/instantiation/common/instantiation';
import { IDisposable, Disposable } from 'vs/base/common/lifecycle';
import { equalsIgnoreCase, startsWithIgnoreCase } from 'vs/base/common/strings';
export const IOpenerService = createDecorator<IOpenerService>('openerService');
......@@ -35,7 +36,7 @@ export interface IResolvedExternalUri extends IDisposable {
}
export interface IOpener {
open(resource: URI | URL, options?: OpenInternalOptions | OpenExternalOptions): Promise<boolean>;
open(resource: URI | string, options?: OpenInternalOptions | OpenExternalOptions): Promise<boolean>;
}
export interface IExternalOpener {
......@@ -43,7 +44,7 @@ export interface IExternalOpener {
}
export interface IValidator {
shouldOpen(resource: URI | URL): Promise<boolean>;
shouldOpen(resource: URI | string): Promise<boolean>;
}
export interface IExternalUriResolver {
......@@ -82,7 +83,7 @@ export interface IOpenerService {
* @param resource A resource
* @return A promise that resolves when the opening is done.
*/
open(resource: URI | URL, options?: OpenInternalOptions | OpenExternalOptions): Promise<boolean>;
open(resource: URI | string, options?: OpenInternalOptions | OpenExternalOptions): Promise<boolean>;
/**
* Resolve a resource to its external form.
......@@ -99,3 +100,11 @@ export const NullOpenerService: IOpenerService = Object.freeze({
async open() { return false; },
async resolveExternalUri(uri: URI) { return { resolved: uri, dispose() { } }; },
});
export function matchesScheme(target: URI | string, scheme: string) {
if (URI.isUri(target)) {
return equalsIgnoreCase(target.scheme, scheme);
} else {
return startsWithIgnoreCase(target, scheme + ':');
}
}
......@@ -266,8 +266,7 @@ export class TerminalLinkHandler {
}
private _handleHypertextLink(url: string): void {
const urlObj = new URL(url);
this._openerService.open(urlObj, { allowTunneling: !!(this._processManager && this._processManager.remoteAuthority) });
this._openerService.open(url, { allowTunneling: !!(this._processManager && this._processManager.remoteAuthority) });
}
private _isLinkActivationModifierDown(event: MouseEvent): boolean {
......
......@@ -8,7 +8,7 @@ import Severity from 'vs/base/common/severity';
import { URI } from 'vs/base/common/uri';
import { localize } from 'vs/nls';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IOpenerService } from 'vs/platform/opener/common/opener';
import { IOpenerService, matchesScheme } from 'vs/platform/opener/common/opener';
import { IProductService } from 'vs/platform/product/common/productService';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { IStorageService } from 'vs/platform/storage/common/storage';
......@@ -19,7 +19,7 @@ import {
} from 'vs/workbench/contrib/url/common/trustedDomains';
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
import { matchesScheme } from 'vs/base/common/resources';
export class OpenerValidatorContributions implements IWorkbenchContribution {
constructor(
......@@ -34,13 +34,13 @@ export class OpenerValidatorContributions implements IWorkbenchContribution {
this._openerService.registerValidator({ shouldOpen: r => this.validateLink(r) });
}
async validateLink(resource: URI | URL): Promise<boolean> {
async validateLink(resource: URI | string): Promise<boolean> {
if (!matchesScheme(resource, Schemas.http) && !matchesScheme(resource, Schemas.https)) {
return true;
}
if (!URI.isUri(resource)) {
resource = URI.from(resource);
if (typeof resource === 'string') {
resource = URI.parse(resource);
}
const { scheme, authority, path, query, fragment } = resource;
......
......@@ -8,7 +8,7 @@ import { URI, UriComponents } from 'vs/base/common/uri';
import { IMainProcessService } from 'vs/platform/ipc/electron-browser/mainProcessService';
import { URLHandlerChannel } from 'vs/platform/url/common/urlIpc';
import { URLService } from 'vs/platform/url/node/urlService';
import { IOpenerService, IOpener } from 'vs/platform/opener/common/opener';
import { IOpenerService, IOpener, matchesScheme } from 'vs/platform/opener/common/opener';
import product from 'vs/platform/product/common/product';
import { registerSingleton } from 'vs/platform/instantiation/common/extensions';
import { IElectronEnvironmentService } from 'vs/workbench/services/electron/electron-browser/electronEnvironmentService';
......@@ -51,16 +51,15 @@ export class RelayURLService extends URLService implements IURLHandler, IOpener
return uri.with({ query });
}
async open(resource: URI | URL, options?: IRelayOpenURLOptions): Promise<boolean> {
async open(resource: URI | string, options?: IRelayOpenURLOptions): Promise<boolean> {
if (resource instanceof URL) {
resource = URI.from(resource);
}
if (resource.scheme !== product.urlProtocol) {
if (!matchesScheme(resource, product.urlProtocol)) {
return false;
}
if (typeof resource === 'string') {
resource = URI.parse(resource);
}
return await this.urlService.open(resource, options);
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册