Skip to content
This repository has been archived by the owner on Sep 26, 2024. It is now read-only.

Commit

Permalink
Get wrapper working. FIXME: button is not swapping wrapper modes
Browse files Browse the repository at this point in the history
  • Loading branch information
elg0nz committed May 15, 2024
1 parent f7e2bb6 commit 21f5f90
Show file tree
Hide file tree
Showing 6 changed files with 149 additions and 120 deletions.
18 changes: 18 additions & 0 deletions apps/owlserver-chrome/src/components/record_button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,24 @@ const RecordButton: React.FC<RecordButtonProps> = ({ onRecordChange }) => {
if (newRecordingState) {
localStorage.setItem(MOCKSI_CURRENT_STEP, "1");
}
const type = newRecordingState
? "startRecordingAction"
: "stopRecordingAction";
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
if (tabs[0].id) {
chrome.scripting.executeScript(
{
target: { tabId: tabs[0].id },
files: ["main.js"],
},
() => {
chrome.tabs.sendMessage(tabs[0].id, { type: type }, (response) => {
console.log("Response from content script:", response);
});
},
);
}
});
};

const label = isRecording ? "stop" : "start";
Expand Down
37 changes: 28 additions & 9 deletions apps/owlserver-chrome/src/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,25 @@ import PopUp from "src/components/Popup";
const s = document.createElement("script");
s.src = chrome.runtime.getURL("wrappers.js");
(document.head || document.documentElement).appendChild(s);
let wrapperInstance = null;

s.onload = () => {
wrapperInstance = window.wrapper;
s.remove();
};

const root = document.getElementById("popup-container");
const rootDiv = ReactDOM.createRoot(root || document.createElement("div"));
rootDiv.render(
<React.StrictMode>
<ChakraProvider>
<PopUp />
</ChakraProvider>
</React.StrictMode>,
);
const rootDiv = document.getElementById("popup-container");
if (rootDiv) {
rootDiv.id = "popup-container";
const reactRoot = ReactDOM.createRoot(rootDiv);
reactRoot.render(
<React.StrictMode>
<ChakraProvider>
<PopUp />
</ChakraProvider>
</React.StrictMode>,
);
}

document.addEventListener("wrapperToBackground", (e): void => {
const jsonHolder = document.getElementById("jsonHolder");
Expand All @@ -33,3 +39,16 @@ document.addEventListener("wrapperToBackground", (e): void => {
},
);
});

// FIXME: this is not working
chrome.runtime.onMessage.addListener((message, sender, sendResponse) => {
if (message.message === "startRecordingAction") {
wrapperInstance?.record();
}
if (message.message === "stopRecordingAction") {
wrapperInstance?.remove();
}
console.log("window.wrapper mode", wrapperInstance?.mode);
const status = wrapperInstance ? "success" : "error";
sendResponse({ status: status });
});
9 changes: 8 additions & 1 deletion apps/owlserver-chrome/src/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@
"background": {
"service_worker": "background.js"
},
"permissions": ["activeTab", "webRequest", "downloads", "storage"],
"permissions": [
"activeTab",
"webRequest",
"downloads",
"storage",
"tabs",
"scripting"
],
"host_permissions": ["<all_urls>"]
}
159 changes: 68 additions & 91 deletions apps/owlserver-chrome/src/services/api_recorder.ts
Original file line number Diff line number Diff line change
@@ -1,103 +1,80 @@
import {
ActionType,
sendToBackground,
} from "../services/background_interactor";
import { ActionType, sendToBackground } from "./background_interactor";

class ApiRecorder {
private XHR: typeof XMLHttpRequest;
private originalXHR: typeof XMLHttpRequest;
// TODO: do these need to be protected or private?
protected requestHeaders: Record<string, string>;
protected _method: string;
protected _url: string;
class APIRecorder extends XMLHttpRequest {
private _method: string;
private _url: string;
private _requestHeaders: Record<string, string> = {};
// biome-ignore lint/suspicious/noExplicitAny: it's fine
private _postData: any;
private _startTime: string;

constructor() {
this.XHR = XMLHttpRequest;
this.originalXHR = XMLHttpRequest;
this.requestHeaders = {};
this.setupXHR();
open(
method: string,
url: string | URL,
async = true,
username?: string,
password?: string,
): void {
this._method = method;
this._url = url.toString();
this._requestHeaders = {};
this._startTime = new Date().toISOString();
this._postData = {};
super.open(method, url.toString(), async, username, password);
}

setupXHR() {
const open = this.originalXHR.prototype.open;
const send = this.originalXHR.prototype.send;
const setRequestHeader = this.originalXHR.prototype.setRequestHeader;

this.XHR.prototype.open = function (
method: string,
url: string | URL,
...args
) {
this._method = method;
this._url = url.toString();
return open.call(this, method, url, ...args);
};

this.XHR.prototype.setRequestHeader = function (
header: string,
value: string,
...args
) {
this.requestHeaders = this.requestHeaders || {};
this._requestHeaders[header] = value;
return setRequestHeader.call(this, header, value, ...args);
};
setRequestHeader(header: string, value: string): void {
this._requestHeaders[header] = value;
super.setRequestHeader(header, value);
}

this.XHR.prototype.send = function (postData?: Document | BodyInit | null) {
this._postData = postData;
this.addEventListener("load", () => {
sendToBackground(this._method, ActionType.RequestMethod);
sendToBackground(this._url.toLowerCase(), ActionType.RequestUrl);
sendToBackground(
JSON.stringify(this._postData),
ActionType.RequestBody,
);
sendToBackground(
JSON.stringify(this._requestHeaders),
ActionType.RequestHeaders,
);
send(postData?: Document | BodyInit | null): void {
this._postData = postData;
this.addEventListener("load", () => {
sendToBackground(this._method, ActionType.RequestMethod);
sendToBackground(this._url.toLowerCase(), ActionType.RequestUrl);
sendToBackground(JSON.stringify(this._postData), ActionType.RequestBody);
sendToBackground(
JSON.stringify(this._requestHeaders),
ActionType.RequestHeaders,
);

const responseHeaders = this.getAllResponseHeaders();
sendToBackground(
JSON.stringify(responseHeaders),
ActionType.ResponseHeaders,
);
const responseHeaders = this.getAllResponseHeaders();
sendToBackground(
JSON.stringify(responseHeaders),
ActionType.ResponseHeaders,
);

let responseBody = "";
switch (this.responseType) {
case "":
case "text":
responseBody = this.responseText;
break;
case "arraybuffer": {
const buffer = new Uint8Array(this.response);
const decoder = new TextDecoder();
responseBody = decoder.decode(buffer);
break;
}
case "blob": {
responseBody = "BLOB";
break;
}
case "document": {
const serializer = new XMLSerializer();
responseBody = serializer.serializeToString(this.response);
break;
}
case "json":
responseBody = JSON.stringify(this.response);
break;
let responseBody = "";
switch (this.responseType) {
case "":
case "text":
responseBody = this.responseText;
break;
case "arraybuffer": {
const buffer = new Uint8Array(this.response);
const decoder = new TextDecoder();
responseBody = decoder.decode(buffer);
break;
}
if (this.responseType !== "blob") {
// Ensure we do not send blob data synchronously
sendToBackground(responseBody, ActionType.ResponseBody);
case "blob":
responseBody = "BLOB";
break;
case "document": {
const serializer = new XMLSerializer();
responseBody = serializer.serializeToString(this.response);
break;
}
});

// biome-ignore lint/style/noArguments: dynamic
return send.apply(this, arguments);
};
case "json":
responseBody = JSON.stringify(this.response);
break;
}
if (this.responseType !== "blob") {
sendToBackground(responseBody, ActionType.ResponseBody);
}
});
super.send(postData as XMLHttpRequestBodyInit);
}
}

export default ApiRecorder;
export default APIRecorder;
44 changes: 26 additions & 18 deletions apps/owlserver-chrome/src/wrappers.ts
Original file line number Diff line number Diff line change
@@ -1,43 +1,51 @@
import ApiMock, { type MockResponse } from "./services/api_mock";
// Import necessary classes and types
import APIRecorder from "./services/api_recorder";

// Define the mode types
enum ModeType {
Record = "record",
Mock = "mock",
Original = "original",
}

// biome-ignore lint/complexity/noStaticOnlyClass: wrapper got to wrap.
// Declare new property on the window object
declare global {
interface Window {
wrapper: typeof XHRWrapper;
}
}

// Define the XHRWrapper class
// biome-ignore lint/complexity/noStaticOnlyClass: <explanation>
class XHRWrapper extends XMLHttpRequest {
public static originalXHR = XMLHttpRequest;
// biome-ignore lint/suspicious/noExplicitAny: it's ok:
public static injectedXHR: any = XMLHttpRequest;
public static mockData: Record<string, MockResponse>;
public static originalXHR: typeof XMLHttpRequest = XMLHttpRequest;
public static injectedXHR: typeof XMLHttpRequest = XMLHttpRequest;
public static mode: ModeType = ModeType.Original;
static XHRWrapper: typeof XHRWrapper;

// Set the mode to record
static record() {
XHRWrapper.injectedXHR = APIRecorder; // Assuming APIRecorder extends XMLHttpRequest
XHRWrapper.wrap(ModeType.Record);
}

static mock(mockData: Record<string, MockResponse>) {
XHRWrapper.injectedXHR = ApiMock; // Assuming ApiMock is compatible with XMLHttpRequest interface
XHRWrapper.mockData = mockData;
XHRWrapper.wrap(ModeType.Mock);
}

// Revert to the original XMLHttpRequest
static remove() {
XHRWrapper.injectedXHR = XMLHttpRequest;
XHRWrapper.injectedXHR = XHRWrapper.originalXHR;
XHRWrapper.wrap(ModeType.Original);
}

// Wrap the XMLHttpRequest with the specified mode
static wrap(mode: ModeType) {
XHRWrapper.mode = mode;
const xhrFactory = () => new XHRWrapper.injectedXHR();
// Copy over all static properties and methods from XMLHttpRequest to xhrFactory
Object.assign(xhrFactory, XMLHttpRequest);
window.XMLHttpRequest = xhrFactory as unknown as typeof XMLHttpRequest;
((xhr) => XHRWrapper.injectedXHR)(XMLHttpRequest);
window.XMLHttpRequest = XMLHttpRequest;
}
}

if (typeof window !== "undefined") {
XHRWrapper.record(); // Supply necessary mock data
window.wrapper = XHRWrapper; // Make the wrapper available globally on the window object
}

// Export the XHRWrapper class as a default export
export default XHRWrapper;
2 changes: 1 addition & 1 deletion packages/typescript-config/base.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"esModuleInterop": true,
"incremental": false,
"isolatedModules": true,
"lib": ["es2022", "DOM", "DOM.Iterable"],
"lib": ["es2022", "DOM", "DOM.Iterable", "esnext"],
"module": "NodeNext",
"moduleDetection": "force",
"moduleResolution": "NodeNext",
Expand Down

0 comments on commit 21f5f90

Please sign in to comment.