Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[performance:] construct our combined mutation observer individually when settings are changed? #2276

Open
ImprovedTube opened this issue May 16, 2024 · 2 comments
Assignees

Comments

@ImprovedTube
Copy link
Member

"Features dont need their own mutation observer each" (like this:)

if (ImprovedTube.storage.channel_default_tab && ImprovedTube.storage.channel_default_tab !== '/' ) {
new MutationObserver(function (mutationList) {
for (var i = 0, l = mutationList.length; i < l; i++) {
var mutation = mutationList[i];
if (mutation.type === 'attributes') {
ImprovedTube.channelDefaultTab(mutation.target);
}
}

instead we have one observer for all (here:)

ImprovedTube.observer = new MutationObserver(function (mutationList) {
for (var i = 0, l = mutationList.length; i < l; i++) {
var mutation = mutationList[i];
if (mutation.type === 'childList') {
for (var j = 0, k = mutation.addedNodes.length; j < k; j++) {
ImprovedTube.childHandler(mutation.addedNodes[j]);
}
for (const node of mutation.removedNodes){
if(node.nodeName === 'BUTTON' && node.id === 'it-popup-playlist-button') ImprovedTube.playlistPopupUpdate();
}
}
if (mutation.target && mutation.target.id === 'owner-sub-count') {
if (name === 'A') {
if (node.href) {
this.channelDefaultTab(node);
if (this.storage.blocklist_activate && node.classList.contains('ytd-thumbnail')) {

VS: We also don't need to check if the features are active at every mutation again.

  • Hence the combined observer could be constructed individually, every time settings are changed....?



More: the commented out code functions.js 20-33 & init.js 30-54 is a less of an optimization to be reviewed/tested

@raszpl
Copy link
Contributor

raszpl commented May 17, 2024

new MutationObserver(function (mutationList) {

this never executes because its being called before storage loads. This is when you broke it c7695ff#r142127013

This mutation observer listens (well, listened until you broke it :P) to a very specific thing attributeFilter: ['href'],, Im not sure what its supposed to be doing, but its very cheap to run.

Combining all mutation observers into one colossus targeting document.documentElement is a bad idea :( it triggers on everything.
This is why YT feels little sluggish with Extension loaded, this

ImprovedTube.childHandler = function (node) {
is the worst thing ever :)
Currently on channel page ytElementsHandler runs ~2000 times before first thumbnail is even inserted into the page, and keeps running for another ~20000 times before last thumbnail is added.

Just to drive point home adding ImprovedTube.counter = 0; on top of /js%26css/web-accessible/functions.js and

	} else {
						console.log(++ImprovedTube.counter);
	}

here


gives us ~24000 times ytElementsHandler was run without any reason, + another 12000 it matched
} else if (document.documentElement.dataset.pageType === 'video') {

... then again all this pointless calling of ytElementsHandler combined steals "only" ~30ms for average Video page, thats two frames of screen refresh, not the end of the world in html land.

In fact Instead of combining its the other way around. Optimally there should be multiple individual mutation observers listening only on specific target elements. I think this might be what you meant by "construct". So instead of

if (node.href) {
this.channelDefaultTab(node);
if (this.storage.blocklist_activate && node.classList.contains('ytd-thumbnail')) {
this.blocklist('video', node);

testing every link for 'ytd-thumbnail' class to run blocklist there would be dedicated mutation observer added only after '#items #contents' suggestion sidebar was added to the page and listening to only changes inside '#items #contents' target.
Problem with this solution is fragility, every time YT does the slightest change (order it injects content) things could/would break.

The lightest way possible is waiting until YT loads everything and then scanning once for things we are interested in, this should handle 90% of what ImprovedTube.ytElementsHandler is scanning for on every element on every mutation. Only after this one big scan would ytElementsHandler mutation observer be started.

@ImprovedTube
Copy link
Member Author

hi! @raszpl!
we could avoid to check features that aren't enabled. (Thats what i meant)

waiting until YT loads

-and avoid depth of recursion at first, unless it will be of interest somewhere later/cheaper

Yes, to be (much) future-proof would be worth some ms 😊 (/not hard to have the confidence to add 0.5% to the total time YouTube loads anyways.), yet we aren't using the Observer much like that and these edits making guesses should be aware how often these lines might run: https://github.com/code-charity/youtube/blob/96879c6e2ce0321d68b1a27f36251c9a92beba44/js%26css/web-accessible/functions.js#L171-220

else if (name === 'YTD-TOGGLE-BUTTON-RENDERER' || name === 'YTD-PLAYLIST-LOOP-BUTTON-RENDERER') {
//can be precise previously node.parentComponent & node.parentComponent.parentComponent
if (node.closest("YTD-MENU-RENDERER")
&& node.closest("YTD-PLAYLIST-PANEL-RENDERER")) {
var index = Array.prototype.indexOf.call(node.parentNode.children, node);
if (index === 0) {
if (this.storage.playlist_reverse === true) {
//can be precise:
try{this.elements.playlist.actions = node.parentNode.parentNode.parentNode.parentNode;}
catch{try{this.elements.playlist.actions = node.parentNode.parentNode.parentNode;}
catch{try{this.elements.playlist.actions = node.parentNode.parentNode;}
catch{try{this.elements.playlist.actions = node.parentNode;}
catch{try{this.elements.playlist.actions = node;}catch{}}
}
}
}
}
this.playlistReverse();
} else if (index === 1) {
this.elements.playlist.shuffle_button = node;
this.playlistShuffle();
if (this.storage.playlist_reverse === true) {
//can be precise:
try{this.elements.playlist.actions = node.parentNode.parentNode.parentNode.parentNode;}
catch{try{this.elements.playlist.actions = node.parentNode.parentNode.parentNode;}
catch{try{this.elements.playlist.actions = node.parentNode.parentNode;}
catch{try{this.elements.playlist.actions = node.parentNode;}
catch{try{this.elements.playlist.actions = node;}catch{}}
}
}
}
}
this.playlistReverse();

And it doesn't watch attributes & characterData. And one could suppress many elements instantly to clean, ( thats after adjusting JS before DOM is ready or after intercepting http request with firefox w3c/webextensions#506) )


never executes

channel_default_tab() is working 😅


after '#items #contents' suggestion sidebar was added

blockable items are also called #dismissible. In #secondary #related, ytd-search, ytd-two-column-browse-results-renderer, and the related videos grid


( repo-history 2020 https://github.com/code-charity/youtube/blob/2fc9341cb2066240a3b6dfc2da763feb06dec5e1/src/youtube/js/mutations.js 2021 https://raw.githubusercontent.com/code-charity/youtube/2bba2fe772dc8a7abdc06cfe8475bc88b6c5ed56/youtube-scripts.js https://raw.githubusercontent.com/code-charity/youtube/2bba2fe772dc8a7abdc06cfe8475bc88b6c5ed56/content-scripts.js )

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants