Skip to content

Commit 6d339f2

Browse files
committed
ref(browser): Streamline browser init() checks
1 parent 7e79a40 commit 6d339f2

File tree

6 files changed

+76
-67
lines changed

6 files changed

+76
-67
lines changed

dev-packages/browser-integration-tests/suites/manual-client/skip-init-browser-extension/test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ sentryTest(
2323
if (hasDebugLogs()) {
2424
expect(errorLogs.length).toEqual(1);
2525
expect(errorLogs[0]).toEqual(
26-
'[Sentry] You cannot run Sentry this way in a browser extension, check: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/',
26+
'[Sentry] You cannot use Sentry.init() in a browser extension, see: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/',
2727
);
2828
} else {
2929
expect(errorLogs.length).toEqual(0);

dev-packages/browser-integration-tests/suites/manual-client/skip-init-chrome-extension/test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ sentryTest('should not initialize when inside a Chrome browser extension', async
2121
if (hasDebugLogs()) {
2222
expect(errorLogs.length).toEqual(1);
2323
expect(errorLogs[0]).toEqual(
24-
'[Sentry] You cannot run Sentry this way in a browser extension, check: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/',
24+
'[Sentry] You cannot use Sentry.init() in a browser extension, see: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/',
2525
);
2626
} else {
2727
expect(errorLogs.length).toEqual(0);

packages/browser/src/sdk.ts

Lines changed: 63 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import {
1212
lastEventId,
1313
logger,
1414
stackParserFromStackParserOptions,
15-
supportsFetch,
1615
} from '@sentry/core';
1716
import type { BrowserClientOptions, BrowserOptions } from './client';
1817
import { BrowserClient } from './client';
@@ -27,6 +26,22 @@ import { linkedErrorsIntegration } from './integrations/linkederrors';
2726
import { defaultStackParser } from './stack-parsers';
2827
import { makeFetchTransport } from './transports/fetch';
2928

29+
type ExtensionProperties = {
30+
chrome?: Runtime;
31+
browser?: Runtime;
32+
nw?: unknown;
33+
};
34+
type Runtime = {
35+
runtime?: {
36+
id?: string;
37+
};
38+
};
39+
40+
/**
41+
* A magic string that build tooling can leverage in order to inject a release value into the SDK.
42+
*/
43+
declare const __SENTRY_RELEASE__: string | undefined;
44+
3045
/** Get the default integrations for the browser SDK. */
3146
export function getDefaultIntegrations(_options: Options): Integration[] {
3247
/**
@@ -82,49 +97,6 @@ function dropTopLevelUndefinedKeys<T extends object>(obj: T): Partial<T> {
8297
return mutatetedObj;
8398
}
8499

85-
type ExtensionProperties = {
86-
chrome?: Runtime;
87-
browser?: Runtime;
88-
nw?: unknown;
89-
};
90-
type Runtime = {
91-
runtime?: {
92-
id?: string;
93-
};
94-
};
95-
96-
function shouldShowBrowserExtensionError(): boolean {
97-
const windowWithMaybeExtension =
98-
typeof WINDOW.window !== 'undefined' && (WINDOW as typeof WINDOW & ExtensionProperties);
99-
if (!windowWithMaybeExtension) {
100-
// No need to show the error if we're not in a browser window environment (e.g. service workers)
101-
return false;
102-
}
103-
104-
const extensionKey = windowWithMaybeExtension.chrome ? 'chrome' : 'browser';
105-
const extensionObject = windowWithMaybeExtension[extensionKey];
106-
107-
const runtimeId = extensionObject?.runtime?.id;
108-
const href = getLocationHref() || '';
109-
110-
const extensionProtocols = ['chrome-extension:', 'moz-extension:', 'ms-browser-extension:', 'safari-web-extension:'];
111-
112-
// Running the SDK in a dedicated extension page and calling Sentry.init is fine; no risk of data leakage
113-
const isDedicatedExtensionPage =
114-
!!runtimeId && WINDOW === WINDOW.top && extensionProtocols.some(protocol => href.startsWith(`${protocol}//`));
115-
116-
// Running the SDK in NW.js, which appears like a browser extension but isn't, is also fine
117-
// see: https://github.com/getsentry/sentry-javascript/issues/12668
118-
const isNWjs = typeof windowWithMaybeExtension.nw !== 'undefined';
119-
120-
return !!runtimeId && !isDedicatedExtensionPage && !isNWjs;
121-
}
122-
123-
/**
124-
* A magic string that build tooling can leverage in order to inject a release value into the SDK.
125-
*/
126-
declare const __SENTRY_RELEASE__: string | undefined;
127-
128100
/**
129101
* The Sentry Browser SDK Client.
130102
*
@@ -172,25 +144,11 @@ declare const __SENTRY_RELEASE__: string | undefined;
172144
* @see {@link BrowserOptions} for documentation on configuration options.
173145
*/
174146
export function init(browserOptions: BrowserOptions = {}): Client | undefined {
175-
const options = applyDefaultOptions(browserOptions);
176-
177-
if (!options.skipBrowserExtensionCheck && shouldShowBrowserExtensionError()) {
178-
if (DEBUG_BUILD) {
179-
consoleSandbox(() => {
180-
// eslint-disable-next-line no-console
181-
console.error(
182-
'[Sentry] You cannot run Sentry this way in a browser extension, check: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/',
183-
);
184-
});
185-
}
147+
if (!browserOptions.skipBrowserExtensionCheck && _checkForBrowserExtension()) {
186148
return;
187149
}
188150

189-
if (DEBUG_BUILD && !supportsFetch()) {
190-
logger.warn(
191-
'No Fetch API detected. The Sentry SDK requires a Fetch API compatible environment to send events. Please add a Fetch API polyfill.',
192-
);
193-
}
151+
const options = applyDefaultOptions(browserOptions);
194152
const clientOptions: BrowserClientOptions = {
195153
...options,
196154
stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),
@@ -282,3 +240,48 @@ export function forceLoad(): void {
282240
export function onLoad(callback: () => void): void {
283241
callback();
284242
}
243+
244+
function _isEmbeddedBrowserExtension(): boolean {
245+
if (typeof WINDOW.window === 'undefined') {
246+
// No need to show the error if we're not in a browser window environment (e.g. service workers)
247+
return false;
248+
}
249+
250+
const _window = WINDOW as typeof WINDOW & ExtensionProperties;
251+
252+
// Running the SDK in NW.js, which appears like a browser extension but isn't, is also fine
253+
// see: https://github.com/getsentry/sentry-javascript/issues/12668
254+
if (_window.nw) {
255+
return false;
256+
}
257+
258+
const extensionObject = _window['chrome'] || _window['browser'];
259+
260+
if (!extensionObject?.runtime?.id) {
261+
return false;
262+
}
263+
264+
const href = getLocationHref();
265+
const extensionProtocols = ['chrome-extension', 'moz-extension', 'ms-browser-extension', 'safari-web-extension'];
266+
267+
// Running the SDK in a dedicated extension page and calling Sentry.init is fine; no risk of data leakage
268+
const isDedicatedExtensionPage =
269+
WINDOW === WINDOW.top && extensionProtocols.some(protocol => href.startsWith(`${protocol}://`));
270+
271+
return !isDedicatedExtensionPage;
272+
}
273+
274+
function _checkForBrowserExtension(): true | void {
275+
if (_isEmbeddedBrowserExtension()) {
276+
if (DEBUG_BUILD) {
277+
consoleSandbox(() => {
278+
// eslint-disable-next-line no-console
279+
console.error(
280+
'[Sentry] You cannot use Sentry.init() in a browser extension, see: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/',
281+
);
282+
});
283+
}
284+
285+
return true;
286+
}
287+
}

packages/browser/test/sdk.test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ describe('init', () => {
149149

150150
expect(consoleErrorSpy).toBeCalledTimes(1);
151151
expect(consoleErrorSpy).toHaveBeenCalledWith(
152-
'[Sentry] You cannot run Sentry this way in a browser extension, check: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/',
152+
'[Sentry] You cannot use Sentry.init() in a browser extension, see: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/',
153153
);
154154

155155
consoleErrorSpy.mockRestore();
@@ -164,7 +164,7 @@ describe('init', () => {
164164

165165
expect(consoleErrorSpy).toBeCalledTimes(1);
166166
expect(consoleErrorSpy).toHaveBeenCalledWith(
167-
'[Sentry] You cannot run Sentry this way in a browser extension, check: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/',
167+
'[Sentry] You cannot use Sentry.init() in a browser extension, see: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/',
168168
);
169169

170170
consoleErrorSpy.mockRestore();

packages/core/src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,9 +203,11 @@ export {
203203
supportsDOMError,
204204
supportsDOMException,
205205
supportsErrorEvent,
206+
// eslint-disable-next-line deprecation/deprecation
206207
supportsFetch,
207208
supportsHistory,
208209
supportsNativeFetch,
210+
// eslint-disable-next-line deprecation/deprecation
209211
supportsReferrerPolicy,
210212
supportsReportingObserver,
211213
} from './utils-hoist/supports';

packages/core/src/utils-hoist/supports.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,8 +69,11 @@ export function supportsHistory(): boolean {
6969
* {@link supportsFetch}.
7070
*
7171
* @returns Answer to the given question.
72+
* @deprecated This is no longer used and will be removed in a future major version.
7273
*/
73-
export function supportsFetch(): boolean {
74+
export const supportsFetch = _isFetchSupported;
75+
76+
function _isFetchSupported(): boolean {
7477
if (!('fetch' in WINDOW)) {
7578
return false;
7679
}
@@ -104,7 +107,7 @@ export function supportsNativeFetch(): boolean {
104107
return true;
105108
}
106109

107-
if (!supportsFetch()) {
110+
if (!_isFetchSupported()) {
108111
return false;
109112
}
110113

@@ -153,14 +156,15 @@ export function supportsReportingObserver(): boolean {
153156
* {@link supportsReferrerPolicy}.
154157
*
155158
* @returns Answer to the given question.
159+
* @deprecated This is no longer used and will be removed in a future major version.
156160
*/
157161
export function supportsReferrerPolicy(): boolean {
158162
// Despite all stars in the sky saying that Edge supports old draft syntax, aka 'never', 'always', 'origin' and 'default'
159163
// (see https://caniuse.com/#feat=referrer-policy),
160164
// it doesn't. And it throws an exception instead of ignoring this parameter...
161165
// REF: https://github.com/getsentry/raven-js/issues/1233
162166

163-
if (!supportsFetch()) {
167+
if (!_isFetchSupported()) {
164168
return false;
165169
}
166170

0 commit comments

Comments
 (0)