Skip to content

Commit

Permalink
Issue #122 - introduced RPC connection and types
Browse files Browse the repository at this point in the history
  • Loading branch information
Manvel committed Dec 28, 2023
1 parent 1628bb8 commit 5488819
Show file tree
Hide file tree
Showing 8 changed files with 198 additions and 52 deletions.
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@
},
"homepage": "https://github.com/Manvel/cba#readme",
"dependencies": {
"cba-components": "github:browser-automation/cba-components#master",
"cba-components": "^1.1.1",
"copy-webpack-plugin": "^11.0.0",
"csso": "^5.0.5",
"puppeteer": "^21.6.1",
Expand Down
56 changes: 23 additions & 33 deletions src/js/background/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,49 +17,39 @@
* <http://www.gnu.org/licenses/>.
*/

/**
* @typedef {import("../db/projects").ActionType} ActionType
*/

/**
* Projects containing actions.
* @typedef {object} RecordedEventMsg
* @property {"RecordedEvent"} msgType - Message ID.
* @property {ActionType} type - One of [injectable action types](https://chrome-automation.com/actions).
* @property {string[]} inputs - action's arguments/inputs.
*/
// Expose browser to global scope.
globalThis.browser = require("webextension-polyfill");

/**
* @typedef {RecordedEventMsg} RpcMessages
*/

const browser = require("webextension-polyfill");
require("../analytics");
const {CBA} = require("./CBA");
const {playProject} = require("./actions");
const projectsDb = require("../db/projects");
const customActionsDb = require("../db/customActions");
const {addRpcListener} = require("../rpc/host");

/** @global */
globalThis.cba = new CBA();
//TODO: Use message passing to run the functions
globalThis.cba.playButtonClick = playButtonClick;
globalThis.cba.recordButtonClick = recordButtonClick;
globalThis.cba.stopButtonClick = stopButtonClick;
globalThis.browser = browser;

/*
* Function for listening to connection port and get data from content script
*/
browser.runtime.onConnect.addListener((port) => {
port.onMessage.addListener(async(/** @type {RpcMessages} */ msg) => {
if (msg.msgType == "RecordedEvent") {
addRpcListener(async(msg) => {
switch (msg.msgType) {
case "RecordedEvent": {
if(cba.allowRec) {
storeRecord(msg);
/** @type {import("../db/projects").Action} */
const action = {inputs: msg.inputs, type: msg.type, id: msg.id};
await storeRecord(action);
}
break;
}
});
});
case "PlayProject": {
const {actions, repeatTimes, id} = msg;
return playButtonClick(actions, repeatTimes, id);
}
}
})

/*
* check whether background page is loading for first time.
Expand Down Expand Up @@ -100,17 +90,17 @@ isFirstLoad();

/**
* Function for storing records in Local Storage
* @param {import("../types/messagePassing").RecordedEventMsg} msg
* @param {import("../db/projects").Action} action
*/
function storeRecord(msg) {
if(msg.type == "redirect") {
return projectsDb.addAction(cba.recordingGroupId, cba.recordingProjectId, msg);
function storeRecord(action) {
if(action.type == "redirect") {
return projectsDb.addAction(cba.recordingGroupId, cba.recordingProjectId, action);
}
if((cba.lastEvType == "update") && (msg.type == "update")) {
if((cba.lastEvType == "update") && (action.type == "update")) {
return false;
}
cba.lastEvType = msg.type;
return projectsDb.addAction(cba.recordingGroupId, cba.recordingProjectId, msg);
cba.lastEvType = action.type;
return projectsDb.addAction(cba.recordingGroupId, cba.recordingProjectId, action);
}

async function storeCurrentUrl() {
Expand Down
18 changes: 3 additions & 15 deletions src/js/cs/record.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@
* <http://www.gnu.org/licenses/>.
*/

const {sendRpcMessage} = require("../rpc/client");

/**
* @typedef {import("../db/projects").ActionType} ActionType
*/

const port = browser.runtime.connect({name: "recordPort"});

function findClosest(query)
{
return this.closest(query);
Expand All @@ -41,7 +41,7 @@ function recordAction(target, actionsData)
const closestTarget = closestTargets[0];
const inputs = [getActionDataValue(input1, closestTarget),
getActionDataValue(input2, closestTarget)];
return sendmsg(type, inputs);
return sendRpcMessage({msgType: "RecordedEvent", action: {inputs, type}});
}
}
}
Expand Down Expand Up @@ -135,18 +135,6 @@ function getPath(element) {
return path.length ? path.join(" > ") : false;
}

/**
* Function for sending event to background page
* Data: the path to the object (selector) or redirectionURL
* evType: Type of the event (click, change, redirect)
* newValue: newValue as example for changed value
* @param {ActionType} type
* @param {string[]} inputs
*/
function sendmsg(type, inputs){
port.postMessage({msgType: "RecordedEvent", type, inputs});
}

document.addEventListener("click", actionRecorder, true);
document.addEventListener("change", actionRecorder, true);
document.addEventListener("blur", actionRecorder, true);
2 changes: 1 addition & 1 deletion src/js/db/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* Injectable actions as seen in [Actions table](https://chrome-automation.com/actions-grid).
* Learn more about [Various actions](https://chrome-automation.com/actions).
* @typedef {object} Action
* @property {string} id - Unique Identifier.
* @property {string} [id] - Unique Identifier.
* @property {ActionType} type - One of [injectable action types](https://chrome-automation.com/actions).
* @property {string[]} inputs - action's arguments/inputs:
* 1. Query, code, timer wait time or redirection link.
Expand Down
38 changes: 38 additions & 0 deletions src/js/rpc/client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* This file is part of Chromium Browser Automation.
* Copyright (C) 2020-present Manvel Saroyan
*
* Chromium Browser Automation is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chromium Browser Automation is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chromium Browser Automation. If not, see
* <http://www.gnu.org/licenses/>.
*/

/**
* @typedef {import("./types").RpcMessages} RpcMessages
*/

// TODO: rename port.
const port = browser.runtime.connect({name: "rpcPort"});

/**
* Function for sending event to background page
* Data: the path to the object (selector) or redirectionURL
* evType: Type of the event (click, change, redirect)
* newValue: newValue as example for changed value
* @param {RpcMessages} message
*/
function sendRpcMessage(message){
port.postMessage(message);
}

module.exports = {sendRpcMessage};
62 changes: 62 additions & 0 deletions src/js/rpc/host.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* This file is part of Chromium Browser Automation.
* Copyright (C) 2020-present Manvel Saroyan
*
* Chromium Browser Automation is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chromium Browser Automation is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chromium Browser Automation. If not, see
* <http://www.gnu.org/licenses/>.
*/

/**
* @typedef {import("./types").RpcMessages} RpcMessages
*/

/**
* @callback RpcHandler
* @param {RpcMessages} msg
*/

/**
* @type {RpcHandler[]}
*/
const listeners = [];

browser.runtime.onConnect.addListener((port) => {
if (port.name !== "rpcPort") throw new Error("Unknown port.");
port.onMessage.addListener(async(/** @type {RpcMessages} */ msg) => {
for (const listener of listeners) {
listener(msg);
}
});
});

/**
* Adds rpc listener.
* @param {RpcHandler} handler - rpc listener handler.
*/
function addRpcListener(handler) {
listeners.push(handler);
}

/**
* Removes rpc listener.
* @param {RpcHandler} handler - rpc listener handler.
*/
function removeRpcListener(handler) {
const index = listeners.indexOf(handler);
if (index !== -1) {
listeners.splice(index, 1);
}
}

module.exports = {addRpcListener, removeRpcListener};
44 changes: 44 additions & 0 deletions src/js/rpc/types.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/*
* This file is part of Chromium Browser Automation.
* Copyright (C) 2020-present Manvel Saroyan
*
* Chromium Browser Automation is free software: you can redistribute it and/or
* modify it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Chromium Browser Automation is distributed in the hope that it will be
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Chromium Browser Automation. If not, see
* <http://www.gnu.org/licenses/>.
*/

/**
* @typedef {import("../db/projects").ActionType} ActionType
*/

/**
* Record triggered event.
* @typedef {object} RpcRecordedEvent
* @property {"RecordedEvent"} msgType - Message ID.
* @property {import("../db/projects").Action} action - action to record.
*/

/**
* Projects containing actions.
* @typedef {object} RpcPlayProject
* @property {"PlayProject"} msgType - Message ID.
* @property {import("../db/projects").Action[]} actions - Injectable actions.
* @property {string} repeatTimes - Repeat times.
* @property {string} id - Unique Identifier.
*/

/**
* @typedef {RpcRecordedEvent|RpcPlayProject} RpcMessages
*/

module.exports = {}; // Unless specified types are not being imported.
28 changes: 26 additions & 2 deletions src/js/ui/projects.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,29 @@ const {NO_PROJ_SELECTED, NO_PROJ_GROUP_SELECTED, NO_ACTION_SELECTED,
const projectsDb = require("../db/projects");
const prefs = require("../db/prefs");
const ActionInputs = require("./ActionInputs");
const {sendRpcMessage} = require("../rpc/client");

/**
* @typedef {object} PayloadDef
* @property {import("../db/projects").Action[]} actions - Actions to be executed.
* @property {"project|group"} type - type of the list item item.
*/

/**
* @typedef {(import("cba-components/src/cba-list/cba-list").ListItem|import("cba-components/src/cba-list/cba-list").ListSubItem) & PayloadDef } ItemPayloadDef
*/

/**
* @type {import("cba-components/src/cba-list/cba-list").List}
*/
const projectsComp = document.querySelector("#projects cba-list");
/**
* @type {import("cba-components/src/cba-list/cba-list").List}
*/
const functions = document.querySelector("#functions");
/**
* @type {import("cba-components/src/cba-table/cba-table").Table}
*/
const actionsComp = document.querySelector("#actions");

const playButtonTooltip = document.querySelector("#playButtonTooltip");
Expand All @@ -54,6 +74,7 @@ async function loadProjects()
if (bg.lastSelectedProjectId)
projectsComp.selectRow(bg.lastSelectedProjectId);

/** @type {import("../db/projects").Action} actions */
const {type, actions} = projectsComp.getSelectedItem();
if (type === "project")
populateActions(actions);
Expand Down Expand Up @@ -378,14 +399,17 @@ async function onAction(action)
break;
}
case "play": {
/** @type {ItemPayloadDef} */
const selectedProject = projectsComp.getSelectedItem();
if (!selectedProject)
return notification.error(NO_PROJ_SELECTED);

const {type, actions, id} = selectedProject;
if (type === "project" && actions) {
const repeateValue = document.querySelector("#repeat").value;
bg.playButtonClick(actions, repeateValue, id);
/** @type {HTMLInputElement} */
const inputElem = document.querySelector("#repeat");
const repeatTimes = inputElem.value;
sendRpcMessage({msgType: "PlayProject", actions, repeatTimes, id});
keepHighlightingPlayingAction();
}
else {
Expand Down

0 comments on commit 5488819

Please sign in to comment.