Wie ich tausend Tabs losgeworden bin ...

... und war 3 Jahre zu spät. Im Idealfall sollte es so aussehen: Der Benutzer startet den Browser und der Browser zeigt an, was der Benutzer benötigt. Obwohl dies nicht implementiert ist, müssen Sie Suchmaschinen verwenden. Im Idealfall sollte es so aussehen: Der Benutzer öffnet eine Suchmaschine, gibt eine Suchabfrage ein und zeigt an, was der Benutzer benötigt. Obwohl die Schaltfläche "Ich fühle mich glücklich" nicht so gut funktioniert (obwohl sich in letzter Zeit eine Bewegung in diese Richtung bemerkbar gemacht hat), müssen Sie manchmal auf der Suchergebnisseite zu mehreren Adressen gehen.

Das Szenario der Verwendung von Suchmaschinen wurde anscheinend historisch behoben (als das Internet langsam war): Als ich zur Suchergebnisseite kam, öffnete ich mehrere Registerkarten im Hintergrund, und während der Rest geladen wurde, war es bereits möglich, die erste Registerkarte zu lesen. Für den Fall, dass ich die erforderlichen Informationen auf einer der Registerkarten fand, musste der Rest manuell geschlossen werden. Wenn es nicht sofort geschlossen wurde, blieben die Registerkarten hängen und erhöhten die Anzahl der geöffneten Registerkarten im Browser, die danach in der Regel selten geschlossen wurden.

Wenn Sie auf die Seite mit den Links klicken, die in einem neuen Fenster geöffnet werden, werden außerdem mehrere (logisch) Registerkarten mit Registerkarten erstellt. Wenn Sie die benötigten Informationen finden, können Sie sich nicht immer daran erinnern, welche Registerkarten verbunden sind. Sie können nicht alles schließen, was auch dazu führt, dass die Anzahl der geöffneten Registerkarten erhöht wird.

Ich brauchte immer den "Gefunden" -Button , der die Konsequenzen der Suche nach mir bereinigt (nennen wir es "Ich hatte Glück" ). Nachdem ich in die Welt der Browser-Erweiterungen eingetaucht war, dachte ich, dass dies in diesem Fall hilfreich sein könnte. So schwach tauchte der Wunsch auf, eine Erweiterung zu schreiben, die meine Probleme lösen würde.

Ich werde Ihnen meine Geschichte erzählen, ich werde die Geschichte in chronologischer Reihenfolge führen, die Schlussfolgerungen können unerwartet sein.

Erster Schritt in Richtung


Als erstes habe ich die Infrastruktur eingerichtet: Webpack + Babel . Und sofort mochte ich diesen babel- duplizierten Code für seine Helfer in jedem Modul nicht. Es war möglich, es für die Verwendung des Objekts zu konfigurieren babelHelper, aber dann musste babelHelperdie Codedatei in der Webpack- Konfiguration verbunden werden . Es entrywar hässlich, eine solche Datei im Projekt zu behalten und darauf zu zeigen. Ich habe ein Plug-In für das Webpack erstellt, das dies automatisch für mich erledigt hat. Nachdem ich mich im ersten Schritt viel Mühe gegeben und mehr Code für die Erweiterung selbst geschrieben hatte, wurde ich etwas langsamer.

Plugin

Stiftung


Die Zeit verging und es war nur ein Plug-In für das Webpack verfügbar, das meine Probleme in keiner Weise löste. Und jedes Mal, wenn ich nach etwas suchte und die Registerkarten nicht schloss, kam mir der Gedanke: „Es wäre schön, diese Erweiterung zu vervollständigen ...“ Der Wunsch wuchs und wuchs, und jetzt, eines schönen Tages, wuchs die Quantität zu Qualität.

Es ist Zeit zu sagen, was die Hauptidee war: Der
Benutzer gelangt zur Suchergebnisseite - SICKLE , wir analysieren die Ergebnisse, speichern die Linkadressen für uns selbst, nachdem der Benutzer auf eine der Adressen geklickt hat, zeigt ihm eine Benachrichtigung mit den restlichen Adressen und der Schaltfläche "Gefunden" Tabs schließen.

Wenn Sie zur Seite gehen, gibt es möglicherweise verschiedene Optionen. Das einfachste: eine Anfrage - eine Antwort vom Server ( 200) Das Schwierigste: eine Anfrage - mehrere Serverumleitungen ( 3xx ), nach der Clientumleitung (mit <meta/>oder Javascript) steht auch die Verlaufs-API ganz oben . Und die Kombinationen zwischen ihnen fallen in der Regel in diese Kategorie.

Einfacher Übergangsfall:

Der Fall eines einfachen Übergangs (Antwort 200)

Komplexer Übergangsfall:

Komplexer Übergangsfall (3xx + Client-Weiterleitungen)

Das heißt, es reicht nicht immer aus, die Seitenadresse zu speichern und nur zu überprüfen. Daher müssen Sie einen logischen Übergang erstellen, in dem Sie alle auf dem Pfad gefundenen Adressen notieren und dann überprüfen, ob der logische Übergang die gespeicherte Adresse enthält. Die Aufgabe ist klar, aber nicht alles ist so einfach in der Ausführung.

In Chrome gibt es zwei APIs für die Navigation: webNavigation und webRequest - jede mit ihren eigenen Ereignissen. Die erste - verbindet die Übergänge und die Benutzeroberfläche des Browsers, die zweite - die zugrunde liegenden Netzwerkanforderungen. Daher, wenn die Adressänderung auf der Seite aufgrund der Verlaufs-API erfolgt istFür letztere gibt es keine Ereignisse, und wenn während einer Netzwerkanforderung Weiterleitungen auftreten, meldet diese erstere überhaupt nicht. Daher ist es erforderlich, beide APIs zu verwenden und eine Prise von jedem Ereignis jeder API zu sammeln, um einen logischen Übergang zu bilden.

Ein paar Details
, webNavigation (wN) :

onBeforeNavigate -> onCommitted -> onDOMContentLoaded -> onCompleted

webRequest (wR):

onBeforeRequest -> [onBeforeRedirect -> onBeforeRequest]* -> onCompleted | onErrorOccurred

wR wN ( ), .. - wN.onBeforeNavigate wR.onBeforeRequest, - . .

, , .

Entwicklung


... Kehren wir zu dem Moment zurück, in dem die Quantität zur Qualität gewachsen ist. Vom Beginn der Entwicklung bis zu diesem Punkt ist viel Zeit vergangen: Browser unterstützen es6-Module , Shadow-DOM und andere moderne Funktionen. Um das Projekt zu erstellen, bin ich zu Rollup gewechselt , diesmal musste ich kein Plugin schreiben. Nach dem Erstellen der Grundlage - der Möglichkeit, Informationen zu jedem Übergang in einer beliebigen Registerkarte abzurufen - muss die Logik des Parsens unterstützter SICKLES und des Anzeigens von Benachrichtigungen auf verwandten Seiten implementiert werden.

Die erste Aufgabe ist ziemlich primitiv: Wir kennen die Adresse des SICKLE, klettern mit dem Inhaltsskript in den Seiteninhalt, holen die Daten ab, die uns interessieren, speichern sie und warten, bis der Benutzer zu einer der Seiten geht, um ihm eine Benachrichtigung mit den anderen Seiten anzuzeigen.

Für die zweite Aufgabe müssen Sie die Benachrichtigung selbst implementieren, was dem Benutzer auf der Seite angezeigt werden soll. Und auch hier können Inhaltsskripte nicht.

Anfangs gab es nur einen Handler (auch bekannt als Controller), der für die Logik während der Benutzerinteraktion mit Suchmaschinen verantwortlich war. Dann kam die Idee auf, warum nicht Benachrichtigungen auf verwandten Registerkarten anzeigen, wenn der Benutzer einfach auf die Links klickt, die in neuen Registerkarten geöffnet werden. Ich musste die Logik wiederholen, um sie universeller zu machen. Ähnlich wie bei Middleware React / Reduxkönnen Sie mehrere Transition-Handler verbinden, wodurch Sie in Zukunft die Möglichkeit implementieren können, verschiedene Handler in den Erweiterungseinstellungen zu deaktivieren / aktivieren.

Privatsphäre


Da es sich bei der Benachrichtigung um ein Bedienfeld am unteren Bildschirmrand handelt, das dem Seitenlayout hinzugefügt wird, kann das Skript auf der Seite auf dasselbe Element wie jedes andere Element auf dieser Seite zugreifen. Das heißt, theoretisch könnte die Seite herausfinden, welche Suchanfrage Sie verwendet haben, in welcher Suchmaschine und welche anderen Seiten Ihnen angeboten wurden, was nicht sehr gut ist.

Eine Technologie namens Shadow DOM hilft . Es wird nicht empfohlen, es closed modebeim Erstellen im Web zu verwenden shadowRoot, da es wenig sinnvoll ist (Sie müssen den Link zum Element noch shadowRootirgendwo speichern, wenn Sie programmgesteuert darauf zugreifen möchten). Sie können die attachShadowzu erstellende Funktion auch neu definierenshadowRootim geöffneten Modus, und dann verwenden die nach der Neudefinition geladenen Skripte bereits die neue Version der Funktion.

Bei der Erweiterung ist dies nicht der Fall. Inhaltsskripte und Seitenskripte leben in parallelen Welten. Skripte von der Seite haben keinen Zugriff auf die in den Inhaltsskripten definierten Objekte, während Inhaltsskripte mit der nativen Implementierung der DOM-Funktionen von Objekten arbeiten (eine überschriebene Funktion eines Skripts von der Seite hat keine Auswirkungen auf die Funktion, mit der das Inhaltsskript arbeitet). Wenn wir diese beiden Bedingungen kombinieren, sehen wir, dass es möglich ist, ein Element mit einem privaten zu erstellen, shadowRootindem der Link dazu in einer Variablen gespeichert wird.

In diesem Fall kann das Skript von der Seite nur auf das Wrapper-Element zugreifen, das für dieses Element leer ist. Er kann den Text der Anfrage oder die vorgeschlagenen Seiten nicht erhalten. Es muss darauf geachtet werden, dass in den generierten Ereignissen kein Link zu einem Element in der Benachrichtigung oder im Klartext angezeigt wird. Daher wird in der Erweiterung die generierte ID in Ereignissen verwendet, und das Hintergrundskript versteht bereits, was diese ID von ihr verlangt. Für die Seite ist diese ID ziemlich bedeutungslos.

Übersetzungsschwierigkeiten


Ursprünglich wurde die Erweiterung nur für Google Chrome entwickelt , aber seit der WebExtensions-API konnte irgendwo in meinem Kopf die Portierung auf andere Browser beibehalten werden. Und das Vorhandensein von Webextension-Polyfill weckte Vertrauen. Aber egal wie. Das Polyphil für diese Erweiterung brachte nur die Möglichkeit, die Chrome-API mit Versprechungen zu verwenden.

Firefox ist zu einer Enttäuschung des Jahres geworden. Die Nichtübereinstimmung der Chrome-API in Firefox ( Bug 1543647 , Bug 1595621 ) erwies sich als kritisch für das Funktionieren der Erweiterung. Wir können sagen, dass sie in diesem Browser (wie erwartet) nicht funktioniert.

Vivaldi war am nächsten, aber auch nicht ohne Kosten. VeranstaltungwN.onCreatedNavigationTargetEs tritt nicht auf, wenn der Benutzer den Link mit der mittleren Maustaste oder über die Shift|Ctrllinke Maustaste + anstelle des Ereignisses öffnet wN.onCommitted transitionType == 'start_page', das nicht in der Chrome-API enthalten ist. Aus diesem Grund funktioniert die Erweiterung nicht in allen Fällen ordnungsgemäß. Auch in Vivaldi funktionieren keine Hotkeys für Erweiterungen. Was in diesem Fall in Chrome eine Killerfunktion ist, ermöglicht es Ihnen, schnell durch die Registerkarten zu navigieren und sie zu schließen, ohne dafür eine Maus verwenden zu müssen.

Fazit


Während des Schreibens des Codes wurde die Logik zum Anzeigen von Benachrichtigungen mehrmals geändert und jedes Mal vereinfacht. Als Ergebnis stellte sich heraus, dass es möglich war, den Garten nicht mit logischen Übergängen zu umzäunen, sondern die „verwandten Übergänge“ des Benutzers zu erfassen (falls wN.onCommittedes ein Flag transitionTypegibt, das angibt, wofür der Übergang gedacht war, handelt es sich in vielen Fällen um „Link“, was bedeutet, dass der Benutzer gewechselt hat durch Bezugnahme), was den Code erheblich vereinfachen und in vielen Fällen funktionieren würde, aber nicht in allen.

Da ich nicht zum Thema gehörte, erwartete ich auch mehr Kompatibilität in Bezug auf die webExtensions-API. Wie immer ist es gut, in einer Welt moderner Browser zu leben, wenn Sie keine Unterstützung für ältere Versionen benötigen. CSS-Animationen sind eine wunderbare Sache: Was Sie früher für die js-Bibliothek verwendet haben, wird jetzt in wenigen Zeilen auf CSS ausgeführt. Benutzerdefinierte Elemente funktionieren nicht in Erweiterungen, aber das Schatten-DOM funktioniert, sodass Sie alle Funktionen nutzen können.

Erweiterung
chrome web store: Handy Search

Source: https://habr.com/ru/post/undefined/


All Articles