Detect Youtube video change with injected javascript - javascript

I am building an extension (some injected js) that adds some buttons to a youtube page, which works fine. My issue is that when a user clicks on another video (say in the right hand side list), the next video is loaded without an actual page reload. Is there any way to detect this change so that I can rerun my code?
I have already tried things like binding to the hashchange event, to no avail.

The idea is simple:
Use background.js to listen for url changes to a specific youtube tab using chrome.tabs.onUpdated
Once tab change is detected, send the new URL to the content-script running in that tab
The background page listens for URL changes to other tabs also but I'm pretty sure you can figure out how to fix that.
manifest.json
{
"manifest_version": 2,
"name": "Tab url change detection",
"version": "1.0",
"background": {
"persistent":true,
"page":"bg.html"
},
"content_scripts": [{
"matches": ["http://www.youtube.com/*"],
"js": ["app.js"]
}
],
"permissions": [
"tabs",
"http://www.youtube.com/*"
]
}
background.html
<script src="background.js"></script>
background.js
//Listen for when a Tab changes state
chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab){
if(changeInfo && changeInfo.status == "complete"){
console.log("Tab updated: " + tab.url);
chrome.tabs.sendMessage(tabId, {data: tab}, function(response) {
console.log(response);
});
}
});
app.js
chrome.runtime.onMessage.addListener(function(request, sender, sendResponse) {
//here we get the new
console.log("URL CHANGED: " + request.data.url);
});

I know it's an old thread but I hope someone else will be able to use this information.
I had a similar issue building a chrome extension for youtube. To be more specific I needed to detect the navigation within youtube.
TL;DR;
After going over most of the StackOverflow, I've ended with the following in the context.js file.
function run(){
// your youtube extention logic
console.log('change');
}
window.onload = run;
window.addEventListener('yt-navigate-start', run, true);
Explanation
In case you wish to see all of the window events you can run getEventListeners(window).
youtube has a costume event when changing pages, the event is yt-navigate-start.
For the first time youtube loads, I used the window.onload and after that, add the event listener window.addEventListener('yt-navigate-start', run, true);
Good luck, I hope this could help.

I was recently struggling with this and I figured out what to do. I'm not sure if this is the best way to do it, but it doesn't rely on a background.js file.
var oldURL= ""
//window.setInterval takes a function and executes it after
//a given time (defined by the second parmeter)in miliseconds
var urlChangeHandler = window.setInterval(checkUrlChange, 500)
function checkURLChange(){
newURL = document.URL;
if(newURL !== oldURL){
doSomething();
oldURL = newURL;
}
}

I hope this will be helpful for somebody.
var old_url = '';
var mutationObserver = new MutationObserver(function (mutations) {
mutations.forEach(function (mutation) {
if (location.href != old_url) {
old_url = location.href;
console.log('URL was changed');
}
});
});
mutationObserver.observe(document.documentElement, {childList: true, subtree: true});

Related

How to check if iframe has completed loading

Before you answer, I know about the load event.
I need to check if an iframe has loaded or not. With images I can do it easy with the HTMLImageElement.complete property, but the iframe doesn't seem to have that... so... does it have something else one can use?
How can you check if an <iframe> has completed loading?
Reason: I'm currently looping through images in a setInterval loop to check if they all have loaded, and when they have, I fade them in one by one. Looks very nice. And I'd like to add iframes to that fanciness, but can't figure out how to check if the iframes have loaded... 😕
If all your use cases are for youtube players than you can use youtube's iframe player api to get onReady events that let you know when the player is ready to be used.
After making sure the iframe api has loaded, onYoutubePlayerAPIReady() is called when it is, you can call YT.Player() to create or operate on an iframe that links to a youtube video
function onPlayerReady(){
//do work
}
player = new YT.Player('container', {
height: '390',
width: '640',
videoId: 'Video's Youtube Id',
events: {
'onReady': onPlayerReady
}
});
The onReady property will let the api know which callback to use.
Now you do not have to load/use the iframe api to get these to work. You just have to implement the correct postMessage calls and message event callbacks yourself.
To do this you first have to make sure the url that is being used has enablejsapi=1 as a parameter. It tells youtube to load the necessary api libraries within the iframe. for instance:
https://www.youtube.com/embed/VgC4b9K-gYU
would become
https://www.youtube.com/embed/VgC4b9K-gYU?enablejsapi=1
You then have to send a listening and an addEventListener postMessage to the iframe's window. This tells the api that there is something wanting to listen to events and which event you are wanting to listen for.
var frame = document.querySelector('iframe');
var listenEvent = {"event":"listening","id":1,"channel":"test"};
frame.contentWindow.postMessage(JSON.stringify(listenEvent),'*');
var listenerEvent = {
"event":"command",
"func":"addEventListener",
"args":["onReady"],
"id":1,
"channel":"test"
};
frame.contentWindow.postMessage(JSON.stringify(listenerEvent),'*');
Then you just need to add an message event handler to handle any incoming messages.
window.addEventListener('message',function(data,origin){
data = JSON.parse(data);
if(data.event == 'onReady'){
//do work
}
});
Now if all your cases do not involve youtube players then you may be out of luck, as cross-origin rules do not let you access the dom/events of the iframe content. The source would have to implement something similar to the postMessage / message system like youtube does above.
Demo
window.addEventListener('message', function(event, origin) {
var data = JSON.parse(event.data);
if (data.event == 'onReady') {
onReady();
}
});
function onReady() {
console.log("Player ready");
}
var frame = null;
window.addEventListener('load', function() {
frame = document.querySelector('iframe');
var command = {
"event": "listening",
"id": 1,
"channel": "test"
};
frame.contentWindow.postMessage(JSON.stringify(command), '*');
command = {
"event": "command",
"func": "addEventListener",
"args": ["onReady"],
"id": 1,
"channel": "test"
};
frame.contentWindow.postMessage(JSON.stringify(command), '*');
});
<iframe src="https://www.youtube.com/embed/VgC4b9K-gYU?enablejsapi=1"></iframe>
Please try below:
<iframe id="my-iframe"></iframe>
<script>
var iframe_document = document.querySelector('#my-iframe').contentDocument;
if (iframe_document.readyState !== 'loading') onLoadingCompleted();
else iframe_document.addEventListener('DOMContentLoaded', onLoadingCompleted);
function onLoadingCompleted(){
console.log('iFrame loaded!');
}
</script>

Firefox Extension location.href not redirecting

I'm using something rather generic here, but essentially I want to be able to load a new tab at my desired URL when selecting my extension, then when at that tab, redirect to a new URL. (The add on should run some code at the first page before redirecting, but that's for another day).
The code I have at the moment is
var buttons = require('sdk/ui/button/action');
var tabs = require("sdk/tabs");
var button = buttons.ActionButton({
id: "redirect",
label: "redirect",
icon: {
"16": "./icon-16.png"
},
onClick: handleClick
});
function handleClick(state) {
tabs.open({
url: "http://www.google.com",
onReady: loadRedirect
});
function loadRedirect(tab) {
tab.attach({
contentScript: "location.href = 'www.youtube.com;'"
});
}
}
When running this however, the 2nd URL appends to the first, rather than replaces, and then gets stuck in an infinite load/refresh loop until I close the browser.
I assume I'm missing something absolutely obvious, but I wasn't able to find anything while searching around.
You are trying to change the location.href to a string that has no scheme. It is assumed that it is a URL within the current domain. Because it also does not start with a / it is assumed to be relative to the current page. Thus, it is appended to the current google.com URL. The page becomes ready; fires the ready event; and your onReady handler is called. Your handler then changes the URL, causing the page to be reloaded, and, again, fire the ready event, which starts the process over again. This is your infinite loop.
To actually get to www.youtube.com, you could change your code to:
function loadRedirect(tab) {
tab.attach({
contentScript: "location.href = 'https://www.youtube.com';"
});
}
Which could have been done without the need to inject a content script by assigning to tab.url:
function loadRedirect(tab) {
tab.url = 'https://www.youtube.com';
}
However, that will not prevent the infinite loop. There are many ways to do so. The easiest is to just remove the ready listener:
function loadRedirect(tab) {
tab.off('ready',loadRedirect);
tab.url = 'https://www.youtube.com';
}

Chrome Extension: pushing information gathered before a new tab is created?

So I have a popup.html which loads a script (popup.js) as its header for the browser action.
This then makes calls to a site to get information necessary.
I got all the information necessary using XMLHTTPRequests and saved it into variables in popup.js.
Now I try to create a new tab, load a webpage and then use the information I saved to fill out a form.
chrome.tabs.create({ 'url': "https://site.html" });
This line is in popup.js
This takes my to the site desired quickly but seems to remove all the information I had previously retrieved. It does not even alert me the information I want even though I ask for it before making the tab. How would I go about creating a tab with a specific url and then using the information I gathered previously in popup.js to fill a form on it all while contained inside popup.js? Is my logic flawed? Thank you so much for any advice.
UPDATE: Ill edit this to make more sense.
this is my popup.js
function do(){
var xhr = new XMLHttpRequest();
xhr.open("Get", Url);
xhr.send();
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
var dogName = this.responseText;
alert(dogName)
}
do();
chrome.tabs.create({ 'url': "site.html" });
inside site.html there is a form. I was to do this to that form.
document.getElementById('form').getElementsByTagName('input')[0].value = dogName;
document.getElementById("btnOK").click()
But it wont do anything if the line
chrome.tabs.create({ 'url': "https://site.html" });
exists, it will only take me to that page and not run do() to alert anything.
By removing the create line, the extension alerts me of the information I want.
You will need to use an event page instead of a popup. The event page is a long-running script that will allow you to retrieve your data on-demand. The tab that is opened will also need to run a content script. This content script will send messages to the event page to retrieve the fetch XHR data and then apply the data to the form fields. Content scripts can be injected using either the manifest.json file or directly on the chrome.tabs.create function passed in as the second argument.
Note: The below code is a close example of how the files and structure
should be. It has not been tested and it should be used to get a
general idea of the solution.
Manifest.json
"content_scripts": [{
"matches": ["*://eample.com/formpage.html"],
"js": ["content.js"],
"run_at": "document_idle"
}],
OR
popup.js
chrome.tabs.create({ 'url': "https://example.com/formpage.html" }, function(tab1) {
chrome.tabs.executeScript(tab1.id, {file: 'content.js', run_at});
});
content.js
chrome.runtime.sendMessage({data: "dogName"}, function(response) {
var dogName = response.data;
document.getElementById('form').getElementsByTagName('input')[0].value = dogName;
});
event.js
function getDogName(callback){
var xhr = new XMLHttpRequest();
xhr.open("Get", Url);
xhr.send();
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
var = dogName = this.responseText;
callback({data: dogName});
}
});
}
chrome.runtime.onMessage.addListener(
function(request, sender, sendResponse) {
if (request.data === "dogName") {
getDogName(sendResponse);
return true;
}
});
You can find more details about extension communications here: Message Passing.

Hide all elements before loading chrome extension

So, I am trying to do something like parental control. When I start my extension for the first time it works fine, but when I use it again elements don't hide, whole page loads and then it redirects, I want to hide all elements on page and then redirect, I am not using onBeforeRequest I want to use it with google search too and I don't know if I can put regex inside urls option. My manifest is ok, I start content script at "document_start".
background.js
var activeTabUrl;
var regex = /http:\/\/www.youporn.com\//
chrome.tabs.onUpdated.addListener(function (tabId, changeInfo, tab) {
chrome.tabs.query({
active: true,
currentWindow: true
}, function (arrayOfTabs) {
activeTabUrl = arrayOfTabs[0].url;
});
if (activeTabUrl.match(regex)) {
chrome.tabs.update({
url: "http://google.com/"
})
} else {
chrome.tabs.sendMessage(tabId, {
action: "show_my_page"
}, function (response) {});
}
});
myscript.js (content_script)
_ini();
function _ini() {
document.getElementsByTagName("html")[0].style.display = "none";
chrome.extension.onMessage.addListener(function (msg, sender, sendResponse) {
if (msg.action == 'show_my_page') {
document.getElementsByTagName("html")[0].style.display = "block";
}
});
}
That's a wrong approach, but first why it fails.
If you're executing at document_start, then the only node existing in the dom would be the document node. Not even html yet.
Yes, you could potentially wait for html node to appear, but this is very roundabout. Your goal is to prevent navigation in the first place - and for blocker-style functionality there's the webrequest API (which, IIRC, was specifically implemented for AdBlock).
Here's a minimal sample from the docs themselves:
chrome.webRequest.onBeforeRequest.addListener(
function(details) { return {cancel: true}; },
{urls: ["*://www.evil.com/*"]},
["blocking"]);
You can also redirect that with a redirectUrl instead of cancel in the blocking response. See also CatBlock for a complete sample.

Stop a Google Chrome Extensions with a On and Off Button. How?

I have by default an External JS called alerton that will run on anywebppage when the extension is enabled.
I've also set up a Popup/Menu for when you click the Chrome Extension Icon at the top right.
I want to when the user presses the button "off" to Turn off/Remove an external javascript file called "alerton"
After many many hours, I'm at a loss as to what I need to do to get this to work!
I've looked at chrome.contentSettings.javascript However it doesn't seem like I can disable just one particular Javascript file.
I'm hoping someone has an answer...
One way you could achieve this is by reading and modifying a boolean variable in a Background Page and use Message Passing to communicate to and from your content-script and popup page. You can define a Background Page in your Manifest as such:
....
"background": {
"scripts": ["background.js"]
},
....
The background.js would look something like this:
var isExtensionOn = true;
chrome.extension.onMessage.addListener(
function (request, sender, sendResponse) {
if (request.cmd == "setOnOffState") {
isExtensionOn = request.data.value;
}
if (request.cmd == "getOnOffState") {
sendResponse(isExtensionOn);
}
});
From your PopUp.html and your content-script you could then call the background.js to read and set the isExtensionOn variable.
//SET VARIABLE
var isExtensionOn = false;
chrome.extension.sendMessage({ cmd: "setOnOffState", data: { value: isExtensionOn } });
//GET VARIABLE
chrome.extension.sendMessage({ cmd: "isAutoFeedMode" }, function (response) {
if (response == true) {
//Run the rest of your content-script in here..
}
});

Categories

Resources