From 7fa1a98bf2fff51f6bb87593bb173b5f87badf68 Mon Sep 17 00:00:00 2001 From: Vikram Pasupathy Date: Tue, 11 Apr 2023 10:10:37 -0700 Subject: [PATCH] feat: Add a toggle between hex and base64 (#62) A lot less rewrite -> same feature of toggle. Also makes use of session storage instead of chrome storage so we don't request extra permissions from the users, and is more lightweight and easier to access. --- log-window.html | 28 +++++++++++++++++ log-window.js | 67 ++++++++++++++++++++++++++++++---------- spec/log-window-tests.js | 61 ++++++++++++++++++++++++++++++++++-- 3 files changed, 137 insertions(+), 19 deletions(-) diff --git a/log-window.html b/log-window.html index 843818f..fa2bd3a 100644 --- a/log-window.html +++ b/log-window.html @@ -30,6 +30,30 @@ right: 10px; } +#base64-radio-input { + position: fixed; + top: 70px; + right: 10px; +} + +#base64-radio-input-label { + position: fixed; + top: 70px; + right: 40px; +} + +#hex-radio-input { + position: fixed; + top: 100px; + right: 10px; +} + +#hex-radio-input-label { + position: fixed; + top: 100px; + right: 40px; +} + h3 { padding-top: 1em; } @@ -58,6 +82,10 @@

EME Method Call and Event Log

Some personally identifiable information may be in the log. Be careful about posting the log on bug reports.

+ + + + diff --git a/log-window.js b/log-window.js index 5e3a327..9267ce2 100644 --- a/log-window.js +++ b/log-window.js @@ -37,14 +37,24 @@ class EmeLoggerWindow { this.downloadButton_ = document.querySelector('#download-button'); this.downloadButton_.addEventListener('click', () => { // Format the logs into a Blob. - const blob = new Blob([this.textLogs_], { type: 'text/plain' }); + const blob = new Blob([this.textLogs_], {type: 'text/plain'}); // Trigger a download. - chrome.downloads.download({ - url: URL.createObjectURL(blob), - filename: 'EMELogFile.txt' - }); + chrome.downloads.download( + {url: URL.createObjectURL(blob), filename: 'EMELogFile.txt'}); }); + + // Default to hex first as that is what is selected. + sessionStorage.setItem('toggle', 'hex'); + let contact = document.querySelectorAll('input[name="radio-toggle-group"]'); + + /** @private {!HTMLInputElement} */ + for (let i = 0; i < contact.length; i++) { + contact[i].addEventListener('change', function() { + let val = this.value; + sessionStorage.setItem('toggle', this.value); + }); + } } /** @@ -143,21 +153,34 @@ class EmeLoggerWindow { } // TODO: Keep an array of blobs instead? More efficient for large logs? - this.textLogs_ += - formattedTimestamp + '\n\n' + - instanceId.textContent + '\n' + - data.textContent + '\n\n\n\n'; + this.textLogs_ += formattedTimestamp + '\n\n' + instanceId.textContent + + '\n' + data.textContent + '\n\n\n\n'; } /** * @param {number} byte * @return {string} - * @private */ - byteToHex_(byte) { + byteToHex(byte) { return '0x' + byte.toString(16).padStart(2, '0'); } + /** + * @param {number} byte + * @return {string} + */ + bytesToBase64(bytes) { + return btoa(String.fromCharCode.apply(null, new Uint8Array(bytes))); + } + + /** + * @return {string} + * @private + */ + toggleStoredInSessionStorage_() { + return sessionStorage.getItem('toggle'); + } + /** * @param {*} obj * @param {string} indentation @@ -185,12 +208,22 @@ class EmeLoggerWindow { if (data.length == 0) { format += '[]'; } else { - format += ' ' + '[\n'; - while (data.length) { - const row = data.splice(0, 16); - format += indentation + ' '; - format += row.map(this.byteToHex_).join(', '); - format += ',\n'; + format += ' ' + + '[\n'; + if (this.toggleStoredInSessionStorage_() == 'hex') { + while (data.length) { + const row = data.splice(0, 16); + format += indentation + ' '; + format += row.map(this.byteToHex).join(', '); + format += ',\n'; + } + } else { + const base64data = + this.bytesToBase64(data).split(/(.{97})/).filter(O => O); + base64data.forEach(base64chunk => { + format += base64chunk; + format += '\n'; + }) } format += indentation + ']'; } diff --git a/spec/log-window-tests.js b/spec/log-window-tests.js index 39a0f08..fad5548 100644 --- a/spec/log-window-tests.js +++ b/spec/log-window-tests.js @@ -21,6 +21,8 @@ describe('Log window', () => { let mockLogElement; let mockClearButton; let mockDownloadButton; + let mockToggleToHexRadioButton; + let mockToggleToBase64RadioButton; beforeAll(() => { mockDocument = document.createElement('div'); @@ -51,6 +53,22 @@ describe('Log window', () => { mockDownloadButton.style.display = 'none'; mockDocument.appendChild(mockDownloadButton); + // Add mock radio + mockToggleToHexRadioButton = document.createElement('input'); + mockToggleToHexRadioButton.checked = 'true'; + mockToggleToHexRadioButton.type = 'radio'; + mockToggleToHexRadioButton.id = 'hex-radio-input'; + mockToggleToHexRadioButton.name = 'radio-toggle-group'; + mockToggleToHexRadioButton.value = 'hex'; + mockDocument.appendChild(mockToggleToHexRadioButton); + + mockToggleToBase64RadioButton = document.createElement('input'); + mockToggleToBase64RadioButton.type = 'radio'; + mockToggleToBase64RadioButton.id = 'base64-radio-input'; + mockToggleToBase64RadioButton.name = 'radio-toggle-group'; + mockToggleToBase64RadioButton.value = 'base64'; + mockDocument.appendChild(mockToggleToBase64RadioButton); + // Reset the singleton we're testing. EmeLoggerWindow.instance = new EmeLoggerWindow(); }); @@ -142,7 +160,7 @@ describe('Log window', () => { type: TraceAnything.LogTypes.Event, className: 'SomeClass', eventName: 'someevent', - event: fakeObjectWithType('Event', { type: 'someevent' }), + event: fakeObjectWithType('Event', {type: 'someevent'}), }); expect(mockLogElement.querySelector('.title').textContent) .toContain('SomeClass someevent Event'); @@ -156,7 +174,7 @@ describe('Log window', () => { type: TraceAnything.LogTypes.Event, className: 'SomeClass', eventName: 'someevent', - event: fakeObjectWithType('Event', { type: 'someevent' }), + event: fakeObjectWithType('Event', {type: 'someevent'}), value: 0, }); expect(mockLogElement.querySelector('.data').textContent) @@ -289,6 +307,45 @@ describe('Log window', () => { const objectText = text.split('=> MediaKeySession instance ')[1]; expect(JSON5.parse(objectText)).toEqual(fields); }); + + it('builds a formatted string with Hex', () => { + const fakeLicenseResponse = 23; + const fakeLicenseResponseInHex = + EmeLoggerWindow.instance.byteToHex(fakeLicenseResponse); + + const fieldsHex = { + type: TraceAnything.LogTypes.Method, + className: 'MediaKeySession', + methodName: 'update', + args: [fakeLicenseResponseInHex], + }; + const objectHex = fakeObjectWithType('MediaKeySession', fieldsHex); + logResult(objectHex); + + const textWithHex = mockLogElement.querySelector('.data').textContent; + + // 23 in decimal -> hex is 0x17. + expect(textWithHex).toContain('0x17'); + }); + + it('builds a formatted string with Base64', () => { + const fakeLicenseResponse = new Uint8Array([23]); + const fakeLicenseResponseInBase64 = + EmeLoggerWindow.instance.bytesToBase64(fakeLicenseResponse); + const fieldsBase64 = { + type: TraceAnything.LogTypes.Method, + className: 'MediaKeySession', + methodName: 'update', + args: [fakeLicenseResponseInBase64], + }; + const objectBase64 = fakeObjectWithType('MediaKeySession', fieldsBase64); + logResult(objectBase64); + + const textWithBase64 = mockLogElement.querySelector('.data').textContent; + + // 23 in decimal -> base64 is 'Fw=='. + expect(textWithBase64).toContain('Fw=='); + }); }); // This matches the format used in function emeLogger() in