Inspect **existing** websockets in Chrome - google-chrome

I know you can inspect new websockets in Chrome via Network, but what about existing sockets? Let's say you open the devtools after the websocket was created. Some data has been sent and received already (don't care), and now I want to inspect the rest. Can I? Who browses with devtools always open? I want to inspect the current page's Websockets, without reloading of course.
I made this, but it requires at least 1 socket.send, and not all websockets do that:
(function() {
var s = WebSocket.prototype.send;
WebSocket.prototype.send = function(json) {
if (!this._oh) {
this._oh = 1;
this.addEventListener('message', function(e) {
console.log('receive', e.data);
});
}
console.log('send', json);
return s.apply(this, arguments);
};
})();
Using a bookmarklet or remote script is fine. I know an extension could do it (by overriding all WebSocket on every single page), but I rather not.

Related

Programmatic injection on nested iframes in extension page

Summary: I need to find a way to accomplish with programmatic injection the same exact behaviour as using content_scripts > matches with "all_frames": true on a manifest. Why? because it is the only way I've found of injecting iframe's content in an extension page without having Cross-Origin errors.
I'm moving to optional_permissions on a Chrome extension and I'm on a dead end.
What I want:
Move this behaviour to optional_permissions in order to be able to add more hosts in the future. With the current code, by adding one new host on content_scripts > matches the extension is disabled by Chrome.
For the move, I removed content_scripts in the manifest and I added "optional_permissions": ["*://*/"],. Then, I successfully implemented a dialog asking new permissions to the user with chrome.permissions.request.
As I said before, the problem is how to inject the iframe's content in an extension page.
What I've tried:
chrome.declarativeContent.RequestContentScript (mentioned here) with allFrames: true. I can only see the script running if I enter the URL directly, nothing happens when that URL is set in an iframe.
chrome.tabs.onUpdated: url is undefined for an extension page. Also, the iframe url is not detected.
Call chrome.tabs.executeScript with allFrames: true as soon as I load the first iframe. By doing this I get an exception Cannot access contents of the page. Extension manifest must request permission to access the respective host. and the "respective host" is chrome-extension://, which is not a valid host if you want to add it to the permissions.
I'm lost. I couldn't find a way to simulate the same behaviour as content_scripts > matches with programmatic injection.
Note: using webNavigation API is not an option since the extension is live and it has thousands of users. Because of this, I can not use the frameId property for executeScript. Thus, my only option with executeScript was to inject all frames but the chrome-extension host issue do not let me continue.
Update: I was able to accomplish what I wanted but only on an HTTP host. I used chrome.tabs.executeScript (option 3).
The question remains on how to make this work on an extension page.
You cannot run content scripts in any extension page, including your own.
If you want to run code in a subframe of your extension page, then you have to use frameId. There are two ways to do this, with and without webNavigation.
I've put all code snippets in this answer together (with some buttons to invoke the individual code snippets) and shared it at https://robwu.nl/s/optional_permissions-script-subframe.zip
To try it out, download and extract the zip file, load the extension at chrome://extensions and click on the extension button to open the test page.
Request optional permissions
Since the goal is to programmatically run scripts with optional permissions, you need to request the permission. My example will use example.com.
If you want to use the webNavigation API too, include its permission in the permission request too.
chrome.permissions.request({
// permissions: ['webNavigation'], // uncomment if you want this.
origins: ['*://*.example.com/*'],
}, function(granted) {
alert('Permission was ' + (granted ? '' : 'not ') + 'granted!');
});
Inject script in subframe
Once you have a tab ID and frameId, injecting scripts in a specific frame is easy. Because of the tabId requirement, this method can only work for frames in tabs, not for frames in your browserAction/pageAction popup or background page!
To demonstrate that code execution succeeds, my examples below will call the next injectInFrame function once the tabId and frameId is known.
function injectInFrame(tabId, frameId) {
chrome.tabs.executeScript(tabId, {
frameId,
code: 'document.body.textContent = "The document content replaced with content at " + new Date().toLocaleString();',
});
}
If you want to run code not just in the specific frame, but all sub frames of that frame, just add allFrames: true to the chrome.tabs.executeScript call.
Option 1: Use webNavigation to find frameId
Use chrome.tabs.getCurrent to find the ID of the tab where the script runs (or chrome.tabs.query with {active:true,currentWindow:true} if you want to know the current tabId from another script (e.g. background script).
After that, use chrome.webNavigation.getAllFrames to query all frames in the tab. The primary way of identifying a frame is by the URL of the page, so you have a problem if the framed page redirects elsewhere, or if there are multiple frames with the same URL. Here is an example:
// Assuming that you already have a frame in your document,
// i.e. <iframe src="https://example.com"></iframe>
chrome.tabs.getCurrent(function(tab) {
chrome.webNavigation.getAllFrames({
tabId: tab.id,
}, function(frames) {
for (var frame of frames) {
if (frame.url === 'https://example.com/') {
injectInFrame(tab.id, frame.frameId);
break;
}
}
});
});
Option 2: Use helper page in the frame to find frameId
The option with webNavigation looks simple but has two main disadvantages:
It requires the webNavigation permission (causing the "Read your browsing history" permission warning)
The identification of the frame can fail if there are multiple frames with the same URL.
An alternative is to first open an extension page that sends an extension message, and find the frameId (and tab ID) in the metadata that is made available in the second parameter of the chrome.runtime.onMessage listener. This code is more complicated than the other option, but it is more reliable and does not require any additional permissions.
framehelper.html
<script src="framehelper.js"></script>
framehelper.js
var parentOrigin = location.ancestorOrigins[location.ancestorOrigins.length - 1];
if (parentOrigin === location.origin) {
// Only send a message if the frame was opened by ourselves.
chrome.runtime.sendMessage(location.hash.slice(1));
}
Code to be run in your extension page:
chrome.runtime.onMessage.addListener(frameMessageListener);
var randomMessage = 'Random message: ' + Math.random();
var f = document.createElement('iframe');
f.src = chrome.runtime.getURL('framehelper.html') + '#' + randomMessage;
document.body.appendChild(f);
function frameMessageListener(msg, sender) {
if (msg !== randomMessage) return;
var tabId = sender.tab.id;
var frameId = sender.frameId;
chrome.runtime.onMessage.removeListener(frameMessageListener);
// Note: This will cause the script to be run on the first load.
// If the frame redirects elsewhere, then the injection can seemingly fail.
f.addEventListener('load', function onload() {
f.removeEventListener('load', onload);
injectInFrame(tabId, frameId);
});
f.src = 'https://example.com';
}

Google Chrome frameless (like kiosk)

I want know if is it possible to make Google Chrome behave like a kiosk (without a frame or controls) but not in full screen, like the next mock picture:
My solution in Electron:
var app = require('app'); // Module to control application life.
var BrowserWindow = require('browser-window'); // Module to create native browser window.
// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is GCed.
var mainWindow = null;
// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
app.on('ready', function() {
// Create the browser window.
mainWindow = new BrowserWindow({width: 800, height: 600, frame:false});
// and load the index.html of the app.
mainWindow.loadURL('http://www.google.com/');
// Emitted when the window is closed.
mainWindow.on('closed', function() {
// Dereference the window object, usually you would store windows
// in an array if your app supports multi windows, this is the time
// when you should delete the corresponding element.
mainWindow = null;
});
});
I do not think that it's possible to make Chrome itself behave that way, but a Chrome App (not an extension) can do this.
This is an option in window creation:
chrome.app.window.create("app.html", {
frame: "none"
});
Note that you will have to provide your own controls to close/move the window.
To make it behave like a browser, you'll need to embed a <webview> element. See also a browser app example.
However, note that Chrome Apps are being deprecated.
You should consider using a similar platform, like Electron or NW.js, to build your own "mini-browser" for your purpose.

How to make screen blink when there is an unread email

I am looking for any way to make the entire screen blink red, or whatever color, when there is an unread email. It could be for any email client. I have done a lot of googling and can't find anything. There is an add-on to thunderbird that creates a little blinking notification, but it only appears very small in the lower right hand corner of the screen.
I was thinking of maybe some add-on to Firefox or Chrome that would allow me to write custom css and javascript that would run on Gmail and make the blinking happen.
Any ideas are greatly appreciated.
I know this is not you regular SO question, but y'all are great and I don't know where else to turn. If there is a better forum out there for this type of question, you could also inform me of it.
Thanks!
I have found this program while searching, haven't tried it. But it says it can execute an external program when email arrives. So seems like you can write a little C# application that can perform the task you want and execute when new email arrives.
http://www.jsonline.nl/Content/Poppy/Poppy.htm
Instead of making a Chrome plugin, I would either make the window title blink or use HTML5 Notifications. Create a simple page which polls your IMAP Gmail for new messages, and include gmail in a large iFrame. If a new message is found, your outer window can issue the notification.
HTML5 Notifications: http://www.html5rocks.com/en/tutorials/notifications/quick/
Blinking Title (adopted from this):
var newMailBlinker = (function () {
var oldTitle = document.title,
msg = 'New Mail!',
timeoutId,
blink = function() {
document.title = document.title == msg ? ' ' : msg;
},
clear = function() {
clearInterval(timeoutId);
document.title = oldTitle;
window.onmousemove = null;
timeoutId = null;
};
return function () {
if (!timeoutId) {
timeoutId = setInterval(blink, 1000);
window.onmousemove = clear;
}
};
}());
PHP Poll Gmail IMAP (adopted from this):
$t1=time();//mark time in
$tt=$t1+(60*1);//total time = t1 + n seconds
do{
if(isset($t2)) unset($t2);//clean it at every loop cicle
$t2=time();//mark time
if(imap_num_msg($imap)!=0){//if there is any message (in the inbox)
$mc=imap_check($imap);//messages check
//var_dump($mc); die;//vardump it to see all the data it is possible to get with imap_check() and them customize it for yourself
echo 'New messages available';
}else echo 'No new messagens';
sleep(rand(7,13));//Give Google server a breack
if(!#imap_ping($imap)){//if the connection is not up
//start the imap connection the normal way like you did at first
}
}while($tt>$t2);//if the total time was not achivied yet, get back to the beginning of the loop
jQuery AJAX Polling to your IMAP script (adopted from this):
// make the AJAX request
function ajax_request() {
$.ajax({
url: '/path/to/gmail/imap/checkMessages.php',
dataType: 'json',
error: function(xhr_data) {
// terminate the script
},
success: function(xhr_data) {
console.log(xhr_data);
if (xhr_data.status == 'No new messages') {
setTimeout(function() { ajax_request(); }, 15000); // wait 15 seconds than call ajax request again
} else {
newMailBlinker(); // blink the title here for new messages
}
}
contentType: 'application/json'
});
}
Obviously you wouldn't use jQuery AND PHP to poll. Pick one to do the polling. I'd recommend have the client do the polling and have PHP check IMAP once per connection. That being said, these snippets should get you started :)
Grab the current google mail checker extension sample (https://developer.chrome.com/extensions/samples.html). Convert it to a packaged app (grab the pieces you want), open a very large window and close it quickly. That should do the trick. Sadly Fullscreen doesnt seem to be possible. But i dont know if thats a problem.
Assuming your dad has the client page always opened, you could just write an extension and manipulate the screen with JS.
You could for example:
Use the gmail client in chrome
Write a chrome extension that checks for new e-mails that come in. You could achieve this by identifying new e-mails. I believe gmail uses a specific css class for new e-mails. So your JS just needs to check for that class.
Have the extension change the page from white to red and back to white a few times (or till the e-mail is read).
You could also possibly have the chrome extension play a sound when a new email arrives?
I found chrome extensions a lot easier to use than FF, especially if you're just going to use JS.

Modify url location in chrome extensions & stop the initial request

I've made an extension who's purpose is to redirect urls.
I.e: www.google.com becomes: www.mysite.com/?url=www.google.com
I came across this post:
How to modify current url location in chrome via extensions
The problem I'm having is that the url's are both processed. The tab initially loads up google.com and only after it's finished my request is shown ( www.mysite.com/?url=www.google.com).
Is there any way to stop the initial request from being processed?
Something like:
chrome.tabs.onUpdated.addListener(function(tabId,obj,tab){
update.stop() // ??????????? Here I'm missing...
chrome.tabs.update(tabId,{url:....}, function callback); // My update stuff..
});
Thoughts?
thank you all.
You're looking for the webNavigation API.
You can register listeners to handle user navigation by modifying or blocking the request on the fly.
In the example below, when a user navigate to www.google.com, before the page even start loading onBeforeNavigate is fired and you can redirect the user to the CSS validation page for that URL:
chrome.webNavigation.onBeforeNavigate.addListener((details) => {
if(details.url.indexOf("www.google.com") !== -1)) {
chrome.tabs.update(details.tabId, {
url: "https://jigsaw.w3.org/css-validator/validator?uri=" + details.url
});
}
});
Remember to add the "webNavigation" permission to your extension manifest to get this functionality enabled.
chrome.tabs.onUpdated is fired two times per tab load - once a tab starts loading, and another time when it finishes loading. If you attach your update to the tab start loading event then it should work relatively quickly. You will still see original url being loaded for a brief moment, but it won't wait until it finishes, as you are describing.
chrome.tabs.onUpdated.addListener(function(tabId,obj,tab){
if(obj.status == "loading") {
chrome.tabs.update(tabId,{url:....}, function callback);
}
});
I don't think there is a more efficient solution at the moment.

How do I access the popup page DOM from bg page in Chrome extension?

In Google Chrome's extension developer section, it says
The HTML pages inside an extension
have complete access to each other's
DOMs, and they can invoke functions on
each other. ... The popup's contents
are a web page defined by an HTML file
(popup.html). The popup doesn't need
to duplicate code that's in the
background page (background.html)
because the popup can invoke functions
on the background page
I've loaded and tested jQuery, and can access DOM elements in background.html with jQuery, but I cannot figure out how to get access to DOM elements in popup.html from background.html.
can you discuss why you would want to do that? A background page is a page that lives forever for the life time of your extension. While the popup page only lives when you click on the popup.
In my opinion, it should be refactored the other way around, your popup should request something from the background page. You just do this in the popup to access the background page:
chrome.extension.getBackgroundPage()
But if you insist, you can use simple communication with extension pages with sendRequest() and onRequest. Perhaps you can use chrome.extension.getViews
I understand why you want to do this as I have run into the problem myself.
The easiest thing I could think of was using Google's method of a callback - the sendRequest and onRequest methods work as well, but I find them to be clunky and less straightforward.
Popup.js
chrome.extension.getBackgroundPage().doMethod(function(params)
{
// Work with modified params
// Use local variables
});
Background.html
function doMethod(callback)
{
if(callback)
{
// Create/modify params if needed
var params;
// Invoke the callback
callback(params);
}
}
As other answers mention, you can call background.js functions from popup.js like so:
var _background = chrome.extension.getBackgroundPage();
_background.backgroundJsFunction();
But to access popup.js or popup.html from background.js, you're supposed to use the messages architecture like so:
// in background.js
chrome.runtime.sendMessage( { property: value } );
// in popup.js
chrome.runtime.onMessage.addListener(handleBackgroundMessages);
function handleBackgroundMessages(message)
{
if (message.property === value)
// do stuff
}
However, it seems that you can synchronously access popup.js from background.js, just like you can synchronously access the other way around. chrome.extension.getViews can get you the popup window object, and you can use that to call functions, access variables, and access the DOM.
var _popup = chrome.extension.getViews( { type: 'popup' } )[0];
_popup.popupJsFunction();
_popup.document.getElementById('element');
_popup.document.title = 'poop'
Note that getViews() will return [] if the popup is not open, so you have to handle that.
I'm not sure why no one else mentioned this. Perhaps there's some pitfalls or bad practices to this that I've overlooked? But in my limited testing in my own extension, it seems to work.