Refer to page

Official chrome plug-in development document VERSION v3

【 Dry goods 】Chrome plug-in (extension) development full guide V2 version

Principles of Automatic Scripts

1. Collect mouse and keyboard events

Gathering events also requires a time dimension, so each event object requires a delay from the last event to this event. Window.starttime = new Date().getTime()

1.1 Collecting Mouse click events

function onclick (ev) { const x = ev.clientX; const y = ev.clientY; Let delay = new Date().gettime () -window.startTime If (delay > 5 &&! Window. The window. Running) {+ startTime = delay / / this is the API plug-in development behind talk about principle, here is to record a click event. Chrome runtime. SendMessage ({type: 'add-event', event: { x, y, type: 'click', tagName: ev.target.tagName, time: delay } }) } }Copy the code

Bind click events

// 给整个document加上点击事件
// 不用 jquery on方法, useCapture设置为true在捕获时就触发, 是为了避免stopPropagation的情况
document.addEventListener('click', onclick, true)
Copy the code

1.2 Collecting Keyboard input events

Keyboard input events are more complex, it is relatively simple to input in English, but it is very troublesome to input with input method, online to find a lot of bloggers to write to find the basic can be used, as follows

Function onKeyUp (ev) {let flag = ev.target.isNeedPrevent if (flag) return sendInputMessage(ev) ev.target.keyEvent = false } function onkeydown (ev) { ev.target.keyEvent = true } function input (ev) { if(! ev.target.keyEvent){ sendInputMessage(ev) } } function compositionstart (ev){ ev.target.isNeedPrevent = true } function Compositionend (ev){ev.target.isNeedPrevent = false} function sendInputMessage (ev){let delay = new Date().getTime() - window.startTime if (delay > 5 && ! window.running) { window.startTime += delay chrome.runtime.sendMessage({ type: 'add-event', event: { type: 'set-input-value', value: ev.target.value, time: delay}})}} // Keyboard input event logic endCopy the code

Type: ‘set-input-value’, value: ev.target.value. For example, when a user enters a string “I love China”, he may type it one word at a time or four words at a time. The first one triggers sendInputMessage four times, and the second one only triggers sendInputMessage once.

Value: ‘I’ > value: ‘I love’ > value: ‘I love China’ > value: ‘I love China’

The next logic is value: ‘I love China’

The advantage of this is that you can optimize it later, but we’ll talk about that later. The next step is to bind these events

$(document). On ('keyup', 'input', onKeyUp, $(document). ) $(document).on('keydown', 'input', onkeydown) $(document).on('input', 'input', input) $(document).on('compositionstart', 'input', compositionstart) $(document).on('compositionend', 'input', compositionend)Copy the code

1.3 Collecting mouse scroll events

Page scrolling is also cumbersome, and it can have multiple scrollable areas, or it can be nested. The following method is also found in nuggets, but can not find the source address, so can not be posted.

// let mouseX = 0; let mouseY = 0; let scrollStartEl = null; Let scrollElementSet = new Set(); let scrollElementSet = new Set() function setScrollWatcher (ev) { mouseX = ev && ev.clientX || mouseX; mouseY = ev && ev.clientY || mouseY; scrollStartEl = document.elementFromPoint(mouseX, mouseY); // Let el = scrollStartEl; while (el) { if (scrollElementSet.has(el)) { el = null; } else { el.onscroll = throttle(recordScrollInfo); scrollElementSet.add(el); el = el.parentNode; } } } function recordScrollInfo (ev) { let el = scrollStartEl; SetScrollWatcher (); setScrollWatcher(); let scrollRecordInfo = { mouseX: mouseX, mouseY: mouseY, scrollList: []} while (el) {/ / record each roll of child node to the top of the HTML distance scrollRecordInfo. ScrollList. Push ({top: el. ScrollTop, left: el scrollLeft}); el = el.parentNode; } let delay = new Date().getTime() - window.startTime if (delay > 5 && ! window.running) { window.startTime += delay let message = { type: 'add-event', event: { ... ScrollRecordInfo, type: 'insensitive' time: delay}} chrome. Runtime. The sendMessage (message)}} / / page scroll event logical endCopy the code

Bind mouse scroll events

Const mousemove = throttle (setScrollWatcher) rolling / / / / throttling binding document. The mouse events addEventListener (' mousemove 'mousemove, true)Copy the code

2. Run these events

Each event object has the tpYE and time fields to run the corresponding operation according to type, mainly by simulating the event.

Dom let focusTarget = null dom let focusTarget = null dom let focusTarget = null i) { let target = null switch (item.type) { case 'click': let click = new MouseEvent('click', { clientX: item.x, clientY: item.y, bubbles: true, cancelable: true }) target = document.elementFromPoint(item.x, If (item.tagName === 'INPUT') {target.focus && target.focus() focusTarget = target } else { focusTarget = null } break case 'set-input-value': // Changing the value of the input directly does not trigger vue's bidirectional binding logic. If (focusTarget) {// look at this https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-onchange-event-in-react-js let nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, "value").set; nativeInputValueSetter.call(focusTarget, item.value); let inputEvent = new InputEvent('input', {bubbles: true}) focusTarget.dispatchEvent(inputEvent) } break case 'scroll': target = document.elementFromPoint(item.mouseX, item.mouseY) let el = target for (let i =0; i< item.scrollList.length; i++) { if (typeof item.scrollList[i].top ! == 'undefined') { $(el).scrollTop(item.scrollList[i].top) $(el).scrollLeft(item.scrollList[i].left) } el = el.parentNode } break} console.log(' Automation -${I}-${item.type} wait ${item.time} milliseconds', target)}Copy the code

Complete the above logic to run the automated script. You can send the entire eventList array to run, or you can send a single event to run later.

async function startEventList (vm) {
  for (let i = 0; i < vm.eventList.length; i++) {
    let item = vm.eventList[i]
    startEvent(item, i)
    await sleep(item.time)
  }
}
function sleep (time) {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve()
    }, time)
  })
}
Copy the code

Chrome Plugin development

Global communication logic

Configuration file manifest.json

Manifest_version: 3 uses Manifest_version 3, and some of its apis are different from That of Manifest_version 2.

Background specifies the background JS

Content_scripts specifies the JS loaded for each browser TAB page

Devtools_page Development tools panel generates logic

{" name ":" automation scripts ", "version" : "1.0", "manifest_version" : 3, "action" : {}, "permissions" : [ "scripting", "activeTab", "contextMenus", "storage", "tabs" ], "host_permissions": [ "http://*/", "https://*/" ], "background": { "service_worker": "background.js" }, "content_scripts": [ { "matches": ] [" < all_urls > ", "js" : [" js/jquery - 1.8.3. Js ", "js/bind - unbind. Js]}]," devtools_page ":" devtools. HTML "}Copy the code

This project only uses these 3 modules at present, and other modules can be optimized later.

Resident background

This JS is the heart of the plug-in, and messages flow through it like blood to each module.

Devtools_page for displaying content and initiating scripts

Use vUE development, need to use the CSP environment development, because the V3 version has this limitation, how to use vUE CSP environment development see here cn.vuejs.org/v2/guide/in…

Bind the event, unbind the event, run the content_scripts of the event

Js /bind-unbind. Js /bind-unbind. Js /bind-unbind. Js /bind-unbind.

Demonstration effect

To ensure the accuracy of x and Y coordinates, use developer tools in separate mode as much as possible, and the browser window size should be the same as when recording.

Demo recording

Chrome plugin – Automatic script recording using demo

Run the demo

Chrome plugin – Automated script running demo

The code address

Github.com/yjj5855/chr…