Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/bright-cities-taste.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@rrweb/rrweb-plugin-console-record": patch
---

Fix wrapped console methods being called with the wrong `this`, which could throw "Illegal invocation" in strict contexts such as extension content scripts
10 changes: 8 additions & 2 deletions packages/plugins/rrweb-plugin-console-record/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,12 @@ function initLogObserver(
level,
(original: (...args: Array<unknown>) => void) => {
return (...args: Array<unknown>) => {
original.apply(this, args);
// `this` here is not the logger (this arrow fn's lexical `this` is
// whatever `replace` was called with, not the console instance).
// Native console methods can throw "Illegal invocation" when
// called with the wrong receiver (observed in extension content
// scripts), so bind explicitly to `_logger`.
original.apply(_logger, args);

if (level === 'assert' && !!args[0]) {
// assert does not log if the first argument evaluates to true
Expand Down Expand Up @@ -230,7 +235,8 @@ function initLogObserver(
});
}
} catch (error) {
original('rrweb logger error:', error, ...args);
// same `this`-binding requirement as above
original.apply(_logger, ['rrweb logger error:', error, ...args]);
} finally {
inStack = false;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* @vitest-environment jsdom
*/
import { describe, it, expect } from 'vitest';
import { getRecordConsolePlugin } from '../src';
import type { IWindow } from '@rrweb/types';

describe('rrweb-plugin-console-record this-binding', () => {
it('invokes the original console method with the logger as `this`', () => {
// Native console implementations in some contexts (observed in Chrome
// extension content scripts) throw "Illegal invocation" if the method
// is applied with an incorrect receiver. Simulate that here: if the
// wrapper ever calls through with the wrong `this`, this logger throws.
let capturedThis: unknown;
const fakeLogger = {
log(...args: unknown[]) {
capturedThis = this;
},
};

const plugin = getRecordConsolePlugin({
level: ['log'],
logger: fakeLogger,
});

const stop = plugin.observer(
() => {
//
},
window as unknown as IWindow,
plugin.options,
);

fakeLogger.log('hello');

expect(capturedThis).toBe(fakeLogger);
stop();
});
});