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
126 changes: 120 additions & 6 deletions background.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,126 @@ chrome.runtime.onInstalled.addListener(() => {
});
});

// Function to inject content script if needed (same logic as popup)
function ensureContentScriptLoaded(tabId, tabUrl, callback) {
// Check if the URL is a restricted page
const restrictedUrls = [
'chrome://', 'chrome-extension://', 'edge://', 'moz-extension://',
'about:', 'file://', 'data:', 'javascript:'
];

const isRestrictedUrl = restrictedUrls.some(prefix => tabUrl.startsWith(prefix));

if (isRestrictedUrl) {
callback(false, `Cannot run on ${tabUrl.split('://')[0]}:// pages.`);
return;
}

// First attempt: Check if content script is already loaded
chrome.tabs.sendMessage(tabId, {action: 'ping'}, function(response) {
if (chrome.runtime.lastError) {
console.log("background.js: Content script not detected, attempting injection...");

// Second attempt: Try to inject content script programmatically
chrome.scripting.executeScript({
target: { tabId: tabId },
files: ['content.js']
}, function(results) {
if (chrome.runtime.lastError) {
console.log("background.js: File injection failed, trying inline...");

// Third attempt: Inline injection
chrome.scripting.executeScript({
target: { tabId: tabId },
func: function() {
if (!window.pasteHumanInjected) {
window.pasteHumanInjected = true;
let currentTypingSession = null;

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.action === "ping") {
sendResponse({success: true, message: "Inline content script is loaded"});
return true;
} else if (request.action === "emulateTyping") {
navigator.clipboard.readText().then((clipText) => {
const activeElement = document.activeElement;
if (activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA' || activeElement.contentEditable === 'true')) {
// Simple typing simulation
let i = 0;
function typeChar() {
if (i < clipText.length) {
const char = clipText[i];
if (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA') {
activeElement.value += char;
} else {
activeElement.textContent += char;
}
activeElement.dispatchEvent(new Event('input', { bubbles: true }));
i++;
setTimeout(typeChar, Math.random() * 100 + 50);
}
}
typeChar();
}
sendResponse({success: true});
}).catch(err => {
sendResponse({success: false, error: err.message});
});
return true;
} else if (request.action === "stopTyping") {
currentTypingSession = null;
sendResponse({success: true});
return true;
}
});
}
}
}, function(inlineResults) {
if (chrome.runtime.lastError) {
callback(false, `All injection methods failed: ${chrome.runtime.lastError.message}`);
} else {
setTimeout(() => callback(true, 'Inline injection successful'), 200);
}
});
} else {
setTimeout(() => callback(true, 'File injection successful'), 200);
}
});
} else {
callback(true, 'Content script already loaded');
}
});
}

chrome.contextMenus.onClicked.addListener((info, tab) => {
console.log('background.js: Context menu clicked:', info.menuItemId); // Log context menu item clicked
console.log('background.js: Context menu clicked:', info.menuItemId);

if (info.menuItemId === "pasteHuman") {
chrome.tabs.sendMessage(tab.id, { action: "emulateTyping" });
} else if (info.menuItemId === "stopPasteHuman") {
chrome.tabs.sendMessage(tab.id, { action: "stopTyping" });
}
// Ensure content script is loaded before sending messages
ensureContentScriptLoaded(tab.id, tab.url, function(success, message) {
if (!success) {
console.error('background.js: Cannot inject content script:', message);
return;
}

console.log('background.js:', message);

// Now send the actual message
if (info.menuItemId === "pasteHuman") {
chrome.tabs.sendMessage(tab.id, { action: "emulateTyping" }, (response) => {
if (chrome.runtime.lastError) {
console.error('background.js: Error sending emulateTyping message:', chrome.runtime.lastError.message);
} else {
console.log('background.js: emulateTyping message sent successfully');
}
});
} else if (info.menuItemId === "stopPasteHuman") {
chrome.tabs.sendMessage(tab.id, { action: "stopTyping" }, (response) => {
if (chrome.runtime.lastError) {
console.error('background.js: Error sending stopTyping message:', chrome.runtime.lastError.message);
} else {
console.log('background.js: stopTyping message sent successfully');
}
});
}
});
});
25 changes: 23 additions & 2 deletions content.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,38 @@
let currentTypingSession = null;
// Log when content script is loaded
console.log('content.js: Content script loaded and ready on:', window.location.href);

// Prevent multiple injections
if (window.pasteHumanContentScript) {
console.log('content.js: Already loaded, skipping...');
} else {
window.pasteHumanContentScript = true;

let currentTypingSession = null;

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
console.log('content.js: Message received:', request.action); // Log message received

if (request.action === "emulateTyping") {
if (request.action === "ping") {
sendResponse({success: true, message: "Content script is loaded"});
return true;
} else if (request.action === "emulateTyping") {
console.log("content.js: Action received: emulateTyping");
currentTypingSession = Math.random().toString(); // Create a new unique typing session identifier
navigator.clipboard
.readText()
.then((clipText) => {
console.log("content.js: Clipboard text read:", clipText); // Log clipboard text
emulateTyping(clipText, currentTypingSession, request.delayedStart);
sendResponse({success: true});
})
.catch((error) => {
console.error("content.js: Error reading clipboard:", error);
sendResponse({success: false, error: error.message});
});
return true; // Indicates that the response is sent asynchronously
} else if (request.action === "stopTyping") {
currentTypingSession = null; // Invalidate the current typing session
sendResponse({success: true});
}
});

Expand Down Expand Up @@ -69,3 +88,5 @@ function emulateTyping(text, session, delayedStart) {
startTyping();
}
}

} // End of conditional block for preventing multiple injections
9 changes: 6 additions & 3 deletions manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
"name": "PasteHuman",
"description": "Transforming plain text into human-like typing. This extension emulates typing in textboxes.",
"version": "1.0",
"permissions": ["contextMenus", "activeTab", "clipboardRead"],
"permissions": ["contextMenus", "activeTab", "clipboardRead", "scripting"],
"host_permissions": ["http://*/*", "https://*/*"],
"background": {
"service_worker": "background.js"
},
Expand All @@ -22,8 +23,10 @@
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content.js"]
"matches": ["http://*/*", "https://*/*"],
"js": ["content.js"],
"run_at": "document_start",
"all_frames": true
}
]
}
150 changes: 138 additions & 12 deletions popup.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,143 @@
document.getElementById('startTyping').addEventListener('click', function() {
console.log("popup.js: Start typing button clicked");

// Using a timeout function to delay the message being sent by 5 seconds
setTimeout(function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
var currentTab = tabs[0]; // There should be only one in this array
console.log(`popup.js: Sending message to tab ${currentTab.id}`);
chrome.tabs.sendMessage(currentTab.id, {
action: 'emulateTyping',
delayedStart: true,
}, function(response) {
console.log("popup.js: Message sent: emulateTyping");
});
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
if (tabs.length === 0) {
console.error("popup.js: No active tab found");
return;
}

var currentTab = tabs[0];
console.log("popup.js: Current tab URL:", currentTab.url);

// First check if content script is loaded
checkContentScriptLoaded(currentTab.id, currentTab.url, function(isLoaded, message) {
if (!isLoaded) {
console.error("popup.js:", message);
// Show user-friendly error message
alert(`PasteHuman Error: ${message}`);
return;
}

console.log("popup.js: Content script verified, starting delayed typing");

// Using a timeout function to delay the message being sent by 5 seconds
setTimeout(function() {
console.log(`popup.js: Sending message to tab ${currentTab.id}`);
chrome.tabs.sendMessage(currentTab.id, {
action: 'emulateTyping',
delayedStart: true,
}, function(response) {
if (chrome.runtime.lastError) {
console.error("popup.js: Error sending message:", chrome.runtime.lastError.message);
} else {
console.log("popup.js: Message sent successfully:", response);
}
});
}, 5000); // 5000 milliseconds (5 seconds) delay
});
}, 5000); // 5000 milliseconds (5 seconds) delay
});
});

// Function to check if content script is loaded
function checkContentScriptLoaded(tabId, tabUrl, callback) {
// Check if the URL is a restricted page where content scripts can't run
const restrictedUrls = [
'chrome://',
'chrome-extension://',
'edge://',
'moz-extension://',
'about:',
'file://',
'data:',
'javascript:'
];

const isRestrictedUrl = restrictedUrls.some(prefix => tabUrl.startsWith(prefix));

if (isRestrictedUrl) {
callback(false, `Cannot run on ${tabUrl.split('://')[0]}:// pages. Please navigate to a regular website.`);
return;
}

console.log("popup.js: Checking content script on:", tabUrl);

// First attempt: Check if content script is already loaded
chrome.tabs.sendMessage(tabId, {action: 'ping'}, function(response) {
if (chrome.runtime.lastError) {
console.log("popup.js: Content script not detected, attempting injection...");

// Second attempt: Try to inject content script programmatically
chrome.scripting.executeScript({
target: { tabId: tabId },
files: ['content.js']
}, function(results) {
if (chrome.runtime.lastError) {
console.error("popup.js: Script injection failed:", chrome.runtime.lastError.message);

// Third attempt: Try inline code injection as fallback
chrome.scripting.executeScript({
target: { tabId: tabId },
func: function() {
// Minimal inline content script
if (!window.pasteHumanInjected) {
window.pasteHumanInjected = true;
console.log('Inline content script injected');

let currentTypingSession = null;

chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
if (request.action === "ping") {
sendResponse({success: true, message: "Inline content script is loaded"});
return true;
} else if (request.action === "emulateTyping") {
navigator.clipboard.readText().then((clipText) => {
// Simple typing simulation
const activeElement = document.activeElement;
if (activeElement && (activeElement.tagName === 'INPUT' || activeElement.tagName === 'TEXTAREA' || activeElement.contentEditable === 'true')) {
activeElement.value = clipText;
activeElement.dispatchEvent(new Event('input', { bubbles: true }));
}
sendResponse({success: true});
}).catch(err => {
sendResponse({success: false, error: err.message});
});
return true;
}
});
}
}
}, function(inlineResults) {
if (chrome.runtime.lastError) {
callback(false, `All injection methods failed: ${chrome.runtime.lastError.message}. The website may have strict security policies.`);
} else {
// Wait and test the inline injection
setTimeout(() => {
chrome.tabs.sendMessage(tabId, {action: 'ping'}, function(response) {
if (chrome.runtime.lastError) {
callback(false, `Inline injection verification failed: ${chrome.runtime.lastError.message}`);
} else {
callback(true, 'Content script injected via inline method');
}
});
}, 200);
}
});
} else {
// File injection succeeded, wait and verify
setTimeout(() => {
chrome.tabs.sendMessage(tabId, {action: 'ping'}, function(response) {
if (chrome.runtime.lastError) {
callback(false, `File injection verification failed: ${chrome.runtime.lastError.message}`);
} else {
callback(true, 'Content script injected via file method');
}
});
}, 200);
}
});
} else {
callback(true, 'Content script already loaded');
}
});
}
Loading