Skip to content

Commit 4de9878

Browse files
authored
ref(browser): Streamline browser init() checks (#16340)
Extracted this out of #15307 This PR: 1. Moves things around in the `sdk.ts` file a bit, moving types & consts to the top of the file 2. Removes an "unecessary" log around fetch not being supported: This log has two problems. First, it is not actually logged, because we do `logger.log()` before running `init()`, which would actually set the logger up. So the log would never show up. Second, the log is superfluous, if the (default) fetch transport fails to send because fetch is not available, you get a warning anyhow for this. 3. Streamlines the browser extension check code slightly. Closes #16284
1 parent 90cb4d1 commit 4de9878

File tree

6 files changed

+76
-68
lines changed

6 files changed

+76
-68
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 & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,7 @@ import {
77
getLocationHref,
88
inboundFiltersIntegration,
99
initAndBind,
10-
logger,
1110
stackParserFromStackParserOptions,
12-
supportsFetch,
1311
} from '@sentry/core';
1412
import type { BrowserClientOptions, BrowserOptions } from './client';
1513
import { BrowserClient } from './client';
@@ -24,6 +22,22 @@ import { linkedErrorsIntegration } from './integrations/linkederrors';
2422
import { defaultStackParser } from './stack-parsers';
2523
import { makeFetchTransport } from './transports/fetch';
2624

25+
type ExtensionProperties = {
26+
chrome?: Runtime;
27+
browser?: Runtime;
28+
nw?: unknown;
29+
};
30+
type Runtime = {
31+
runtime?: {
32+
id?: string;
33+
};
34+
};
35+
36+
/**
37+
* A magic string that build tooling can leverage in order to inject a release value into the SDK.
38+
*/
39+
declare const __SENTRY_RELEASE__: string | undefined;
40+
2741
/** Get the default integrations for the browser SDK. */
2842
export function getDefaultIntegrations(_options: Options): Integration[] {
2943
/**
@@ -79,49 +93,6 @@ function dropTopLevelUndefinedKeys<T extends object>(obj: T): Partial<T> {
7993
return mutatetedObj;
8094
}
8195

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

186-
if (DEBUG_BUILD && !supportsFetch()) {
187-
logger.warn(
188-
'No Fetch API detected. The Sentry SDK requires a Fetch API compatible environment to send events. Please add a Fetch API polyfill.',
189-
);
190-
}
147+
const options = applyDefaultOptions(browserOptions);
191148
const clientOptions: BrowserClientOptions = {
192149
...options,
193150
stackParser: stackParserFromStackParserOptions(options.stackParser || defaultStackParser),
@@ -213,3 +170,48 @@ export function forceLoad(): void {
213170
export function onLoad(callback: () => void): void {
214171
callback();
215172
}
173+
174+
function _isEmbeddedBrowserExtension(): boolean {
175+
if (typeof WINDOW.window === 'undefined') {
176+
// No need to show the error if we're not in a browser window environment (e.g. service workers)
177+
return false;
178+
}
179+
180+
const _window = WINDOW as typeof WINDOW & ExtensionProperties;
181+
182+
// Running the SDK in NW.js, which appears like a browser extension but isn't, is also fine
183+
// see: https://github.com/getsentry/sentry-javascript/issues/12668
184+
if (_window.nw) {
185+
return false;
186+
}
187+
188+
const extensionObject = _window['chrome'] || _window['browser'];
189+
190+
if (!extensionObject?.runtime?.id) {
191+
return false;
192+
}
193+
194+
const href = getLocationHref();
195+
const extensionProtocols = ['chrome-extension', 'moz-extension', 'ms-browser-extension', 'safari-web-extension'];
196+
197+
// Running the SDK in a dedicated extension page and calling Sentry.init is fine; no risk of data leakage
198+
const isDedicatedExtensionPage =
199+
WINDOW === WINDOW.top && extensionProtocols.some(protocol => href.startsWith(`${protocol}://`));
200+
201+
return !isDedicatedExtensionPage;
202+
}
203+
204+
function _checkForBrowserExtension(): true | void {
205+
if (_isEmbeddedBrowserExtension()) {
206+
if (DEBUG_BUILD) {
207+
consoleSandbox(() => {
208+
// eslint-disable-next-line no-console
209+
console.error(
210+
'[Sentry] You cannot use Sentry.init() in a browser extension, see: https://docs.sentry.io/platforms/javascript/best-practices/browser-extensions/',
211+
);
212+
});
213+
}
214+
215+
return true;
216+
}
217+
}

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)