I am trying to read json of post request using Chrome Extension. So i have created a extension with following code:-
menifest.json file:-
{
"name": "numera name",
"version": "1.0",
"description": "numera",
"manifest_version": 2,
"content_scripts": [
{
"matches": ["<all_urls>"],
"run_at": "document_start",
"js": ["contentScript.js"]
}
],
"permissions": [
"activeTab",
"tabs"
]
}
contentScript.js
function interceptData() {
var xhrOverrideScript = document.createElement('script');
xhrOverrideScript.type = 'text/javascript';
xhrOverrideScript.innerHTML = '
(function() {
var XHR = XMLHttpRequest.prototype;
var send = XHR.send;
var open = XHR.open;
XHR.open = function(method, url) {
this.url = url; // the request url
return open.apply(this, arguments);
}
XHR.send = function() {
this.addEventListener('load', function() {
if (this.url.includes('WaitForResponsesData')) {
var dataDOMElement = document.createElement('div');
dataDOMElement.id = '__interceptedData';
dataDOMElement.innerText = this.response;
console.log(this.response);
dataDOMElement.style.height = 0;
//dataDOMElement.style.overflow = 'hidden';
document.body.appendChild(dataDOMElement);
}
});
return send.apply(this, arguments);
};
})();'
document.head.prepend(xhrOverrideScript);
}
function checkForDOM() {
if (document.body && document.head) {
interceptData();
} else {
requestIdleCallback(checkForDOM);
}
}
requestIdleCallback(checkForDOM);
function scrapeData() {
var responseContainingEle = document.getElementById('__interceptedData');
if (responseContainingEle) {
var response = JSON.parse(responseContainingEle.innerHTML);
} else {
requestIdleCallback(scrapeData);
}
}
requestIdleCallback(scrapeData);
But I am not sure why this code is not working i have added console log and alert as well not no luck,
Also i have tried to search __interceptedData ID in page HTML but that is also not there.
I followed this URL https://medium.com/better-programming/chrome-extension-intercepting-and-reading-the-body-of-http-requests-dd9ebdf2348b
please help me out from this
Related
For my Chrome Extension,
I'm searching multiple classes/id's to then include their text with some HTML I'm inserting into the website.
When I run the extension,
the HTML widget I'm inserting is inserted multiple times but should only be done once.
I realize MutationObserver is intended to check for every DOM change and seemingly inserts my widget every time that happens I presume. However, I'm not sure how best to approach making sure the widget is only inserted once.
I tried using observer.disconnect(); which helps on initial load to insert the widget once but then it won't re-insert the widget on return to the page – will only on refresh. Couldn't figure out a way to turn observe back on.
Not sure if disconnect and reconnect is even a smart path but it's what I came up with.
In the attached screenshot, you can see it keeps adding the widget with DOM changes.
How should I approach fixing this?
Thank you!
Link to download the extension
manifest.json
{
"manifest_version": 3,
"name": "Test SO Mutation Answer",
"description": "Example for StackOverflow",
"version": "0.0.1",
"host_permissions": ["<all_urls>"],
"permissions": ["storage", "activeTab", "scripting", "tabs", "webNavigation"],
"content_scripts": [
{
"matches": ["https://www.zillow.com/*"],
"js": ["ballpark.js"],
"css": ["main.css"]
}
],
"web_accessible_resources": [
{
"resources": ["/images/*"],
"matches": ["<all_urls>"]
}
]
}
ballpark.js
const observer = new MutationObserver(function() {
if (document.getElementsByClassName('ds-action-bar')[0]) {
if (document.querySelector('[data-testid="price"] span') && document.querySelector('.ds-expandable-card .eUxMDw')) {
console.log("All data found");
startWidget();
if (document.getElementById('ballpark-container')) {
console.log("Do nothing, BP exists");
} else {
console.log("Adding BP...");
insertWidget();
}
} else {
console.log("Cannot find all data");
}
} else {
offerPrice = undefined;
monthlyPITI = undefined;
}
})
const target = document.querySelector('body');
const config = { childList: true };
observer.observe(target, config);
var offerPrice;
var monthlyPITI;
function startWidget() {
getAskingPrice();
getExpenses();
}
// Get Price from Zillow
function getAskingPrice() {
var askingPrice = document.querySelector('[data-testid="price"] span');
if(askingPrice !== null) {
offerPrice = parseFloat(askingPrice.innerText.replace(/\$|,/g, ''));
console.log(offerPrice + " Offer Price");
} else {
console.log("Null Asking Price Div");
}
}
function getExpenses() {
var monthlyPITIZillow = document.querySelector('.ds-expandable-card .eUxMDw');
if (monthlyPITIZillow !== null) {
monthlyPITI = parseFloat(monthlyPITIZillow.innerText.replace(/\$|,/g, ''));
console.log(monthlyPITI + " PITI");
} else {
console.log("Null Monthly PITI Div");
}
}
// Find Zillow widget to insert the extension widget
function insertWidget() {
const select_div_for_bpd = document.querySelector('div.Spacer-c11n-8-65-2__sc-17suqs2-0');
if(select_div_for_bpd !== null) {
const ballpark_container = document.createElement("div");
ballpark_container.setAttribute("id", "ballpark-container");
select_div_for_bpd.appendChild(ballpark_container);
ballpark_container.innerHTML = `
<div class="ballpark-roi-container">
<div><h1>${offerPrice}</h1> Offer Price</div>
<div><h1>${monthlyPITI}</h1> Monthly PITI</div>
</div>
`;
} else {
console.log("Cannot insert your widget");
}
}
can you help me again with this problem? I am getting error Uncaught Error: Extension context invalidated. in chrome://extensions.
I am trying to make a timer that whenever you we're idle let's say 5 seconds an alarm will trigger and you need to move your mouse to stop the alarm and I need to sync it with other opened tabs that's why I am using chrome.storage.onChanged.addListener as per the recommendation of wOxxOm thankyou. It works perfectly fine but I am getting this error.
*manifest.json*
{
"manifest_version": 2,
"name": "Toool",
"description": "Description ....",
"version": "1.0.0",
"icons": { "128": "icon_128.png" },
"permissions": ["activeTab", "storage"],
"content_scripts": [
{
"matches": [
"*://*.google.com/*"
],
"css":[
"bootstrap/css/bootstrap-iso.css",
"css/animate.min.css",
"css/all.css",
"css/style.css"
],
"js": [
"js/jquery.min.js",
"js/contentscript.js"
]
}
],
"web_accessible_resources": [
"index.html",
"audio/*",
"webfonts/*"
]
}
*contentscript.js*
// So here is what I've been working so far..
//Instead of popup, I am using this to inject html directly to the DOM.
$('body').prepend('<div class="idletimer-element bootstrap" style="display: block"></div>');
$.get(chrome.extension.getURL('index.html'), function(data) {
$(data).appendTo('.idletimer-element');
});
var audio_url = chrome.runtime.getURL('audio/bells.mp3');
var audio = new Audio(audio_url);
var timerStartIn = 6;
var timer = -1;
var idle = null;
var timeOut = null;
var MouseMoved = 0;
//Initialize the mouse storage value
chrome.storage.local.set({"mouseStorage": MouseMoved}, function() {
console.log('Mouse Moved: ' + MouseMoved);
});
$(document).on('mousemove', function(){
// I used validation here to just execute it when necessary but the problem didn't fix it at all :(
clearTimeout(timeOut);
if(MouseMoved == 0){
MouseMoved = 1;
chrome.storage.local.set({"mouseStorage": MouseMoved}, function() {
console.log('Mouse Moved: ' + MouseMoved);
});
}
timeOut = setTimeout(function(){
MouseMoved = 0;
chrome.storage.local.set({"mouseStorage": MouseMoved}, function() {
console.log('Mouse Moved: ' + MouseMoved);
});
}, 1000);
});
chrome.storage.onChanged.addListener(function(changes, namespace) {
if(changes['mouseStorage']){
IT();
}
});
function IT(){
clearInterval(idle);
timer = -1;
audio.pause(); // Stop playing
audio.currentTime = 0; // Reset time
timerStartIn = 6;
$('.timer').html('User is active');
$('.timer-content i').removeClass('animated shake infinite');
$('.it-status').html('<span> <strong>Reviewer Status: </strong> <span class="active m-r-10">Active</span></span>');
idle = setInterval(function(){
timer++;
//console.log(timer);
timerStartIn--;
$('.timer').html('User is active. <br> Idle starts in ' + timerStartIn + ' second/s');
if(timer >= 5){
//Idle
//console.log('Idle');
$('.timer').html('You are idle for about <br> ' + secondsToHms(timer - 5) +'.');
//console.log(timer);
audio.play();
$('.timer-content i').addClass('animated shake infinite');
$('.it-status').html('<span> <strong>Reviewer Status: </strong> <span class="idle m-r-10">Idle</span></span>');
}
}, 1000);
}
I created an endpoint in my C# code at /events path. If the event is of type "Microsoft.EventGrid.SubscriptionValidationEvent", i extract the ValidationCode and return 200 with a json object with validationResponse. I tested this using Postman, and everything appears to work, but when using "New-AzEventGridSubscription", it fails. Any ideas?
Here's the command I used, scrubbed of course.
New-AzEventGridSubscription -ResourceGroup MyResourceGroupName -Endpoint https://requestb.in/19qlscd1/events -EventSubscriptionName EventSubscription1 -TopicName Topic1
Sample Post:
[
{
"id": "531d4a96-d4c7-43d6-8b4c-e1ff9351b869",
"topic": "scrubbed",
"subject": "",
"data": {
"validationCode": "7062AAC0-656D-4C4C-BDD6-0FA673676D95",
"validationUrl": "scrubbed"
},
"eventType": "Microsoft.EventGrid.SubscriptionValidationEvent",
"eventTime": "2020-03-30T17:46:15.1827678Z",
"metadataVersion": "1",
"dataVersion": "2"
}
]
Sample Response:
{
"validationResponse": "7062AAC0-656D-4C4C-BDD6-0FA673676D95"
}
Here is my code to handle the Event. I've tested this using Ngrok, and it appears to work.
private void HandleEvent(IApplicationBuilder app)
{
app.Run(async context =>
{
string content;
using (var reader = new StreamReader(context.Request.Body))
{
content = reader.ReadToEnd();
}
var eventGridEvents = JsonConvert.DeserializeObject<EventGridEvent[]>(content);
foreach (var eventGridEvent in eventGridEvents)
{
if (eventGridEvent.EventType == "Microsoft.EventGrid.SubscriptionValidationEvent")
{
var eventDataJson = JsonConvert.SerializeObject(eventGridEvent.Data);
var eventData = JsonConvert.DeserializeObject<SubscriptionValidationEventData>(eventDataJson);
var responseData = new SubscriptionValidationResponse()
{
ValidationResponse = eventData.ValidationCode
};
context.Response.StatusCode = 200;
await context.Response.WriteAsync(JsonConvert.SerializeObject(responseData));
}
else
{
var deserialized = JsonConvert.DeserializeObject((string)eventGridEvent.Data);
if (typeof(IEnumerable).IsAssignableFrom(deserialized.GetType()))
{
eventGridEvent.Data = (IEnumerable<object>)deserialized;
}
else
{
eventGridEvent.Data = new List<object>() { deserialized };
}
await HandleMessageListAsync(eventGridEvents.ToList());
}
}
context.Response.StatusCode = 200;
await context.Response.WriteAsync(string.Empty);
});
}
Thank you!
Turns out the issue was my API was returned as "Chunked". Microsoft applied a patch to support "Chunked" responses, and now it works. :)
I'm creating a Chrome extension that should interact with the user selection in the Microsoft Word Online documents - add new highlighting instead of the natural selection highlighting and then remove it.
The problem is that I'm not able to get the user selection: the response for the window.getSelection() returns the result like the selection is empty.
Here are files from my extension:
manifest.json
{
"manifest_version": 2,
"name": "The extension name",
"version": "1.0",
"description": "This extension description",
"browser_action": {
"default_icon": "icon.png",
"default_popup": "popup.html"
},
"icons": {
"128": "icon.png"
},
"content_scripts": [{
"matches": ["<all_urls>"],
"js": ["content_script.js"],
"run_at": "document_end",
"all_frames": true
}],
"permissions": ["activeTab"]
}
popup.html
<!doctype html>
<html>
<head>
<script src="popup.js"></script>
</head>
<body>
<div id="wrapper">
<form id="settings" name="settings">
<div id="apply" class="form-row">
<input type="submit" name="apply" value="Apply"/>
</div>
</form>
</div>
</body>
</html>
popup.js
document.addEventListener('DOMContentLoaded', function() {
document.getElementById("settings").addEventListener("submit", function (e) {
e.preventDefault();
chrome.tabs.executeScript(null, {file: 'toolbar.js'}, function() {
chrome.tabs.query({active: true, currentWindow: true}, function(tabs) {
chrome.tabs.sendMessage(tabs[0].id, {});
});
);
}, false);
});
toolbar.js
function showToolbar() {
var dom_body = document.getElementsByTagName('body')[0];
var tb_wrapper = document.createElement('div');
tb_wrapper.id = "toolbar_wrapper";
var tb_toolbar_play = document.createElement('button');
tb_toolbar_play.id = "toolbar_play";
tb_toolbar_play.title = "Play";
tb_toolbar_play.value = "Play";
tb_wrapper.appendChild(tb_toolbar_play);
dom_body.appendChild(tb_wrapper);
}
showToolbar();
content_script.js
function playButtonOnClickEventListener(request, sender, sendResponse) {
var toolbar = document.getElementById("toolbar_wrapper");
if (toolbar !== null) {
var toolbar_play_button = document.getElementById("toolbar_play");
toolbar_play_button.addEventListener("click", function (e) {
var selection = window.getSelection();
console.log(selection);
});
}
sendResponse({data: "response", success: true});
}
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
playButtonOnClickEventListener(request, sender, sendResponse);
});
So, what I want to see in the Chrome Developer tools after the console.log(selection) executes:
What I actually get:
P.S. The extension works perfectly with Google Docs.
I'm not sure how the Word Online editor is built or why you can't use window.getSelection(), but one thing I noticed immediately is that when you select text in it, the editor gives that text a Selected class. So maybe you could take advantage of that somehow? Override the style of the relevant elements?
Possibly you could use a MutationObserver and look for this class name being added to elements. You would have to do some experimenting to see if this works for your needs, and obviously if Microsoft change the class name or behaviour, your extension breaks.
Try selecting some text and putting
document.getElementsByClassName("Selected")[0].innerHTML
in the console.
Here's the solution of the issue. Hope it can be useful for somebody once.
Currently I see the whole picture differently, so I changed the number of files and its content.
manifest.json
{
"manifest_version": 2,
"version": "1.1.1",
"name": "Test name",
"description": "Test description",
"browser_action": {
"default_icon": "icon.png"
},
"icons": {
"128": "icon.png"
},
"content_scripts": [
{
"matches": ["<all_urls>"],
"js": ["content_script.js"],
"run_at": "document_end",
"all_frames": true
}
],
"permissions": ["activeTab"]
}
content_script.js
//Word Online pages contain several different iframes, so, in order
//to find the one where the text that we are going to work with
//is placed, we have to use the Chrome Runtime API and its
//"sendMessage" method that works in every frame.
//https://developer.chrome.com/apps/runtime#method-sendMessage
chrome.runtime.sendMessage("", function() {
if (checkFrameLocation()) {
showToolbar();
}
});
function checkFrameLocation() {
//So as I work with documents in OneDrive (onedrive.live.com)
//and in Sharepoint (<USER_NAME>-my.shrepoint.com), I found out
//that I need iframes with these URLs only.
var trusted_urls = [
"https://euc-word-edit.officeapps.live.com",
"https://word-edit.officeapps.live.com",
"nl1-word-edit.officeapps.live.com"
];
var result = false;
for (var i = 0; i < trusted_urls.length; i++) {
if (window.location.href.indexOf(trusted_urls[i]) > -1) {
result = true;
}
}
return result;
}
function showToolbar() {
var dom_body = document.body;
var tb_wrapper = document.createElement('div');
tb_wrapper.id = "toolbar_wrapper";
tb_wrapper.style.position = "absolute";
tb_wrapper.style.top = "200px";
tb_wrapper.style.left = "200px";
tb_wrapper.style.backgroundColor = "#ffffff";
tb_wrapper.style.height = "30px";
tb_wrapper.style.width = "50px";
var tb_play = document.createElement('button');
tb_play.id = "toolbar_play_button";
tb_play.title = "Play";
tb_play.innerHTML = "►";
tb_play.style.height = "100%";
tb_play.style.width = "100%";
tb_play.style.lineHeight = "12px";
tb_play.addEventListener("click", function() {
playButtonOnClickEventListener();
});
tb_wrapper.appendChild(tb_play);
dom_body.appendChild(tb_wrapper);
}
function playButtonOnClickEventListener() {
//Now we can use the window selection object
var window_selection = window.getSelection();
console.log(window_selection);
//Also, Word Online adds special class name to every selected element
//in the document. So, this is another way to get the selected text.
var elements_selection = document.getElementsByClassName("Selected");
console.log(elements_selection);
}
And here's the proof screenshot:
There are results of accessing the selected text in the Word Online document
I'm trying to build a Chrome extension with TypeScript.
The setup is quite simple:
In manifest.json
{
"permissions": [
"webRequest",
"webRequestBlocking",
"tabs",
"storage",
"http://*/",
"https://*/*"
],
"content_scripts": [
{
"matches": [ "http://*/*", "https://*/*" ],
"js": [ "scripts/require.js", "scripts/require-cs.js",
"scripts/main.js", "scripts/contentscript.js" ],
"run_at": "document_end",
"all_frames": true
}],
}
In model.ts:
export class WebPage
{
private id: number;
private processed: boolean;
get Id() { return this.id; }
set Id(value: number) { this.id = value };
get Processed() { return this.processed; }
set Processed(value: boolean) { this.processed = value };
constructor(id: number)
{
this.id = id;
this.processed = false;
}
}
When compiled the resulting JavaScript starts with:
define(["require", "exports"], function (require, exports) {
var WebPage = (function ()
{
//Code omitted to keep the SO question short
}});
In main.ts:
(function ()
{
console.log("Executing main.js");
requirejs.config(
{
baseUrl: "scripts", paths: { "model" : "model" }
});
})();
In contentscript.ts:
import model = require("model");
console.log("Processing page");
var page = new model.WebPage(1);
page.Processed = true;
console.log("Done processing page");
When compiled the resulting JavaScript looks like this:
define(["require", "exports", "model"], function (require, exports, model) {
console.log("Processing page");
var page = new model.WebPage(1);
page.Processed = true;
console.log("Done processing page");
});
And finally in require-cs.js:
console.log("Executing requirejs-cs.js");
require.load = function (context, moduleName, url) {
console.log("require.load called");
var xhr = new XMLHttpRequest();
xhr.open("GET", chrome.extension.getURL(url) + '?r=' + (new Date()).getTime(), true);
xhr.onreadystatechange = function (e) {
if (xhr.readyState === 4 && xhr.status === 200) {
console.log("evaluating" + url)
eval(xhr.responseText);
context.completeLoad(moduleName);
}
};
xhr.send(null);
};
Which is what I found in all the other questions related to my issue.
All of this results in the following output when loading a page:
Uncaught Error: Mismatched anonymous define() module: function (require, exports, model) {
console.log("Processing page");
var page = new model.WebPage(1);
page.Processed = true;
console.log("Done processing page");
}
http://requirejs.org/docs/errors.html#mismatch
I've read those docs, I went through a lot of similar questions on SO,
but I haven't found anything that works for me yet.
Most questions deal with JavaScript specifically,
perhaps there is something missing on the TypeScript side of things?
Note: The TypeScript compiler is configured to use AMD.
The docs for the error message state:
Be sure to load all scripts that call define() via the RequireJS API. Do not manually code script tags in HTML to load scripts that have define() calls in them.
As described in this question, it seems that if you use import in a type script, it will be turned into a module when compiled using AMD. So by including "scripts/contentscript.js" as a content script you are trying to load a module script without using the RequireJS API. You can try removing contentscript.js from the content_scripts entry in the manifest and adding the following to main.js:
requirejs(["contentscript"], function() {});