fix and simplify .user.css URL installer (#856)

* fix and simplify .user.css URL installer

* Refactor: pull out tab-manager and icon-manager

* fixes/cosmetics

* usercss installer url check
* extract downloaders
* simplify tabManager
* rework/split openInstallerPage
* use a simple object instead of map
* trivial bugfixes
* cosmetics

* fixup! updateIconBadge in styleViaAPI

Co-authored-by: eight <eight04@gmail.com>
This commit is contained in:
tophf 2020-02-23 18:43:26 +03:00 committed by GitHub
parent c3b7657433
commit 4bbce7cb9f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
32 changed files with 401 additions and 741 deletions

View File

@ -230,10 +230,6 @@
"message": "Провери за обновления", "message": "Провери за обновления",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "За да разрешите проверка за обновления, пуснете файла върху лентата с табове, или в метаданните на стила укажете @updateURL.",
"description": ""
},
"license": { "license": {
"message": "Лиценз", "message": "Лиценз",
"description": "Label for the license" "description": "Label for the license"
@ -309,18 +305,10 @@
"message": "Получи се грешка докато наблюдавахме файла", "message": "Получи се грешка докато наблюдавахме файла",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "Преглед на живо е разрешен, така че инсталирания стил ще бъде обновен автоматично при външни промени докато двата прозореца с кода и оригинала са отворени.",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Преглед на живо", "message": "Преглед на живо",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "За да разрешите презареждане в реално време, пуснете файла върху лентата с табове (областта, където са показани заглавията на табовете).",
"description": ""
},
"manageFilters": { "manageFilters": {
"message": "Филтри", "message": "Филтри",
"description": "Label for filters container" "description": "Label for filters container"

View File

@ -597,10 +597,6 @@
"message": "Při sledování souboru došlo k chybě", "message": "Při sledování souboru došlo k chybě",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "Živá aktualizace je povolena, takže nainstalovaný styl bude automaticky aktualizován při externích změnách, dokud budou tento list a list zdrojového souboru otevřeny.",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Živá aktualizace", "message": "Živá aktualizace",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"

View File

@ -526,10 +526,6 @@
"message": "Nach Updates suchen", "message": "Nach Updates suchen",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "Ziehe die Datei auf die Tableiste oder definiere @updateURL in den Metadaten des Styles, um automatisch nach Updates zu suchen.",
"description": ""
},
"license": { "license": {
"message": "Lizenz", "message": "Lizenz",
"description": "Label for the license" "description": "Label for the license"
@ -605,18 +601,10 @@
"message": "Bei der Echtzeitaktualisierung der Datei ist ein Fehler aufgetreten", "message": "Bei der Echtzeitaktualisierung der Datei ist ein Fehler aufgetreten",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "Echtzeitaktualisierung ist aktiviert, sodass die Darstellung des jeweiligen Styles automatisch aktualisiert wird, wenn externe Änderungen erfolgen.",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Echtzeitaktualisierung", "message": "Echtzeitaktualisierung",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "Ziehe die Datei auf die Tableiste, um die Echtzeitaktualisierung nutzen zu können.",
"description": ""
},
"manageFavicons": { "manageFavicons": {
"message": "Favicons in der \"Gilt für\" Spalte anzeigen", "message": "Favicons in der \"Gilt für\" Spalte anzeigen",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -290,6 +290,10 @@
"message": "Drop your backup file anywhere on this page to import.", "message": "Drop your backup file anywhere on this page to import.",
"description": "Drag'n'drop message" "description": "Drag'n'drop message"
}, },
"dragDropUsercssTabstrip": {
"message": "To install the file, drop it on the tab strip (the area where the tab titles are shown).",
"description": "Message popup shown when erroneously dropping a usercss file into the manager page"
},
"editDeleteText": { "editDeleteText": {
"message": "Delete", "message": "Delete",
"description": "Label for the context menu item in the editor to delete selected text" "description": "Label for the context menu item in the editor to delete selected text"
@ -542,9 +546,6 @@
"message": "Check for updates", "message": "Check for updates",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "To enable check for updates, drop the file on the tab strip or specify @updateURL in the style metadata."
},
"license": { "license": {
"message": "License", "message": "License",
"description": "Label for the license" "description": "Label for the license"
@ -625,16 +626,17 @@
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": { "liveReloadInstallHint": {
"message": "Live reload is enabled so the installed style will be auto-updated on external changes while both this tab and the source file tab are open.", "message": "Keep this tab open to auto-update the style on external changes.",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadInstallHintFF": {
"message": "Keep both this tab and the original tab open to auto-update the style on external changes.",
"description": "The extra hint of live-reload feature shown only for file:// URLs in Firefox"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Live reload", "message": "Live reload",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "To enable live reload, drop the file on the tab strip (the area where the tab titles are shown)."
},
"manageFavicons": { "manageFavicons": {
"message": "Favicons in applies-to column", "message": "Favicons in applies-to column",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -35,10 +35,6 @@
"message": "Edit style", "message": "Edit style",
"description": "Title of the page for editing styles" "description": "Title of the page for editing styles"
}, },
"installUpdateUnavailable": {
"message": "To enable checking for updates, drop the file on the tab strip or specify @updateURL in the style metadata.",
"description": ""
},
"license": { "license": {
"message": "Licence", "message": "Licence",
"description": "Label for the license" "description": "Label for the license"

View File

@ -518,10 +518,6 @@
"message": "Buscar actualizaciones", "message": "Buscar actualizaciones",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "Para habilitar la búsqueda de actualizaciones, suelte el archivo en la pestaña o especifique @updateURL en los metadatos del estilo.",
"description": ""
},
"license": { "license": {
"message": "Licencia", "message": "Licencia",
"description": "Label for the license" "description": "Label for the license"
@ -597,18 +593,10 @@
"message": "Se ha producido un error al visualizar el archivo", "message": "Se ha producido un error al visualizar el archivo",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "La recarga en tiempo real está habilitada, por lo que el estilo instalado se actualizará automáticamente con los cambios externos mientras estén abiertas esta pestaña y la pestaña del archivo de origen.",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Recarga en tiempo real", "message": "Recarga en tiempo real",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "Para habilitar la recarga en tiempo real, suelte el archivo en la pestaña (el área donde se muestran los títulos de las pestañas).",
"description": ""
},
"manageFavicons": { "manageFavicons": {
"message": "Favicons en la columna 'Se aplica a'", "message": "Favicons en la columna 'Se aplica a'",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -538,10 +538,6 @@
"message": "Kontrolli uuendusi", "message": "Kontrolli uuendusi",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "Uuenduste kontrollimise lubamiseks lohista failid kaartide ribale või määratle stiili metaandmetes @updateURL.",
"description": ""
},
"license": { "license": {
"message": "Litsents", "message": "Litsents",
"description": "Label for the license" "description": "Label for the license"
@ -621,18 +617,10 @@
"message": "Faili vaatamisel esines viga", "message": "Faili vaatamisel esines viga",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "Reaalajas uuestilaadimine on lubatud, seega paigaldatud stiili uuendatakse väliste muudatuste korral automaatselt, kuniks see kaart ja lähtefaili kaart mõlemad lahti on.",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Reaalajas uuestilaadimine", "message": "Reaalajas uuestilaadimine",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "Reaalajas uuestilaadimise lubamiseks lohista fail kaartide ribale (ala, kus näidatakse kaartide pealkirju).",
"description": ""
},
"manageFavicons": { "manageFavicons": {
"message": "Lehe ikoonid rakendub-tulbas", "message": "Lehe ikoonid rakendub-tulbas",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -530,10 +530,6 @@
"message": "Rechercher les mises à jour", "message": "Rechercher les mises à jour",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "Pour activer la vérification des mises à jour, glissez le fichier sur la barre des onglets ou spécifiez @updateURL dans les métadonnées du style.",
"description": ""
},
"license": { "license": {
"message": "Licence", "message": "Licence",
"description": "Label for the license" "description": "Label for the license"
@ -609,18 +605,10 @@
"message": "Une erreur est survenue durant la surveillance du fichier", "message": "Une erreur est survenue durant la surveillance du fichier",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "Le rechargement automatique est activé, donc le style installé sera mis à jour automatiquement après une modification externe quand à la fois cet onglet et longlet du fichier source sont ouverts.",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Rechargement immédiat", "message": "Rechargement immédiat",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "Pour activer le rechargement automatique, glisser le fichier sur la barre des onglets (la zone où les titres des onglets sont affichés).",
"description": ""
},
"manageFavicons": { "manageFavicons": {
"message": "Favicons dans la colonne « sapplique à »", "message": "Favicons dans la colonne « sapplique à »",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -449,10 +449,6 @@
"message": "בדוק עדכונים", "message": "בדוק עדכונים",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "על־מנת לאפשר בדיקת עדכונים, אנא שחרר את הקובץ על רצועת הכרטיסיות או ציין @updateURL ב־metadata של העיצוב.",
"description": ""
},
"license": { "license": {
"message": "רישיון", "message": "רישיון",
"description": "Label for the license" "description": "Label for the license"
@ -519,10 +515,6 @@
"message": "רענון לייב (live)", "message": "רענון לייב (live)",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "על־מנת לאפשר רענון לייב (live), אנא שחרר את הקובץ על רצועת הכרטיסיות (האזור בו כותרות הכרטיסיות מוצגות).",
"description": ""
},
"manageFavicons": { "manageFavicons": {
"message": "הצגת אייקונים בעמודת 'חל על'", "message": "הצגת אייקונים בעמודת 'חל על'",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -609,10 +609,6 @@
"message": "Hiba történt a fájl figyelése közben", "message": "Hiba történt a fájl figyelése közben",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "A valós idejű újratöltés engedélyezve van, így a telepített stílus automatikusan frissül külső változások során, amíg ez a fül és a forrásfájlt tartalmazó fül nyitva van.",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Valós idejű újratöltés", "message": "Valós idejű újratöltés",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"

View File

@ -538,10 +538,6 @@
"message": "更新をチェック", "message": "更新をチェック",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "更新のチェックを有効にするには、ファイルをタブストリップにドロップするか、スタイルのメタデータで @updateURL を指定してください。",
"description": ""
},
"license": { "license": {
"message": "ライセンス", "message": "ライセンス",
"description": "Label for the license" "description": "Label for the license"
@ -621,18 +617,10 @@
"message": "ファイルの監視中にエラーが発生しました", "message": "ファイルの監視中にエラーが発生しました",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "自動リロードが有効になっているため、このタブとソースファイルのタブの両方が開いている間に、外部変更によってインストール済みスタイルが自動更新されることがあります。",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "自動リロード", "message": "自動リロード",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "自動リロードを有効にするには、ファイルをタブストリップ(タブのタイトルが表示されている領域)にドロップしてください。",
"description": ""
},
"manageFavicons": { "manageFavicons": {
"message": "適用先欄のファビコン", "message": "適用先欄のファビコン",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -534,10 +534,6 @@
"message": "Controleren op updates", "message": "Controleren op updates",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "Als u controle op updates wilt inschakelen, sleep dan het bestand naar de tabbladenstrook of geef een @updateURL op in de metagegevens van de stijl.",
"description": ""
},
"license": { "license": {
"message": "Licentie", "message": "Licentie",
"description": "Label for the license" "description": "Label for the license"
@ -613,18 +609,10 @@
"message": "Er is een fout opgetreden tijdens het bekijken van het bestand", "message": "Er is een fout opgetreden tijdens het bekijken van het bestand",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "Live herladen is ingeschakeld. De geïnstalleerde stijl zal bij externe wijzigingen automatisch worden bijgewerkt als zowel dit tabblad als het tabblad van het bronbestand zijn geopend.",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Live herladen", "message": "Live herladen",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "Als u live herladen wilt inschakelen, sleep dan het bestand naar de tabbladenstrook (het gebied waar de tabbladtitels worden getoond).",
"description": ""
},
"manageFavicons": { "manageFavicons": {
"message": "Favicons in kolom Van toepassing op", "message": "Favicons in kolom Van toepassing op",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -546,10 +546,6 @@
"message": "Sprawdź aktualizacje", "message": "Sprawdź aktualizacje",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "Aby włączyć sprawdzanie aktualizacji, upuść plik na pasku kart lub określ @updateURL w metadanych stylu.",
"description": ""
},
"license": { "license": {
"message": "Licencja", "message": "Licencja",
"description": "Label for the license" "description": "Label for the license"
@ -625,18 +621,10 @@
"message": "Wystąpił błąd podczas oglądania pliku", "message": "Wystąpił błąd podczas oglądania pliku",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "Przeładowanie na żywo jest włączone, więc zainstalowany styl zostanie automatycznie zaktualizowany w przypadku zmian zewnętrznych, gdy ta karta i karta pliku źródłowego są otwarte.",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Przeładuj na żywo", "message": "Przeładuj na żywo",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "Aby włączyć przeładowanie na żywo, upuść plik na pasku kart (obszar, w którym wyświetlane są tytuły kart).",
"description": ""
},
"manageFavicons": { "manageFavicons": {
"message": "Ikony ulubionych w kolumnie dotyczących", "message": "Ikony ulubionych w kolumnie dotyczących",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -506,10 +506,6 @@
"message": "Procurar atualizações", "message": "Procurar atualizações",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "Para ativar a verificação de atualizações, solte o ficheiro na faixa de separadores ou especifique @updateURL nos metadados de estilo.",
"description": ""
},
"license": { "license": {
"message": "Licença", "message": "Licença",
"description": "Label for the license" "description": "Label for the license"
@ -585,18 +581,10 @@
"message": "Ocorreu um erro ao vigiar o arquivo", "message": "Ocorreu um erro ao vigiar o arquivo",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "O recarregamento dinâmico está ativado para que o estilo instalado seja atualizado automaticamente em alterações externas enquanto esse separador e o separador do arquivo de origem estiverem abertos.",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Recarregamento dinâmico", "message": "Recarregamento dinâmico",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "Para ativar recarregamento dinâmico, solte o ficheiro na faixa de separadores (a área onde os títulos dos separadores são mostrados).",
"description": ""
},
"manageFavicons": { "manageFavicons": {
"message": "Favicons em colunas de aplica-se a", "message": "Favicons em colunas de aplica-se a",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -462,10 +462,6 @@
"message": "Verificați update-urile", "message": "Verificați update-urile",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "Pentru a activa verificarea de updates. trage fișierul pe taburi (zona cu titluri) sau specifica @updateURL în metadata temei.",
"description": ""
},
"license": { "license": {
"message": "Licență", "message": "Licență",
"description": "Label for the license" "description": "Label for the license"
@ -537,14 +533,6 @@
"message": "A avut loc o eroare în timpul monitorizării acestui fișier", "message": "A avut loc o eroare în timpul monitorizării acestui fișier",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "Reload automat este activat deci tema instalată va fi updatată automat când acest tab si fișierul surca sunt deschise.",
"description": "The label of live-reload feature"
},
"liveReloadUnavailable": {
"message": "Pentru a activa live reload (refresh automat), trage fișierul pe taburi (zona unde titlurile temelor sunt afișate) ",
"description": ""
},
"manageFavicons": { "manageFavicons": {
"message": "Favicons în coloana 'se aplică la'", "message": "Favicons în coloana 'se aplică la'",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -534,10 +534,6 @@
"message": "Проверить обновления", "message": "Проверить обновления",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "Для проверки обновлений перетяните файл на полоску вкладок или впишите @updateURL в мета-данных стиля.",
"description": ""
},
"license": { "license": {
"message": "Лицензия", "message": "Лицензия",
"description": "Label for the license" "description": "Label for the license"
@ -617,18 +613,10 @@
"message": "Ошибка слежения за файлом", "message": "Ошибка слежения за файлом",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "Включена автозагрузка изменений установленный стиль будет обновляться автоматически пока открыта эта вкладка и вкладка исходного файла.",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Автозагрузка изменений", "message": "Автозагрузка изменений",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "Для автозагрузки изменений перетяните файл на полоску вкладок (область, где показываются названия вкладок).",
"description": ""
},
"manageFavicons": { "manageFavicons": {
"message": "Пиктограммы для целевых сайтов", "message": "Пиктограммы для целевых сайтов",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -522,10 +522,6 @@
"message": "Sök efter uppdateringar", "message": "Sök efter uppdateringar",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "För att aktivera sök efter uppdateringar, släpp filen på flikremsan eller ange @updateURL i stilmetadatan.",
"description": ""
},
"license": { "license": {
"message": "Licens", "message": "Licens",
"description": "Label for the license" "description": "Label for the license"
@ -597,18 +593,10 @@
"message": "Ett fel uppstod medan du tittade på filen", "message": "Ett fel uppstod medan du tittade på filen",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "Uppdatering i realtid är aktiverat så att den installerade stilen automatiskt uppdateras vid externa ändringar medan både den här fliken och fliken för källfilen är öppna.",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "Uppdaterar i realtid", "message": "Uppdaterar i realtid",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "För att aktivera uppdatering i realtid, släpp filen på fliken strip (det område där fliktitlar visas).",
"description": ""
},
"manageFavicons": { "manageFavicons": {
"message": "Ikoner i 'Tillämpad för' kolumnen", "message": "Ikoner i 'Tillämpad för' kolumnen",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -534,10 +534,6 @@
"message": "检查更新", "message": "检查更新",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "若想允许检查更新,请将文件拖动到标签栏上,或在样式的元数据中声明 @updateURL。",
"description": ""
},
"license": { "license": {
"message": "许可证", "message": "许可证",
"description": "Label for the license" "description": "Label for the license"
@ -613,18 +609,10 @@
"message": "查看文件时发生错误", "message": "查看文件时发生错误",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "动态刷新被激活后,当被安装的样式被更新时,只要本网页和目标网页都是开启状态,在样式上进行的更新会实时反映到目标网页上。",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "动态刷新", "message": "动态刷新",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "要想激活动态刷新,请将文件拖到标签条(即选项卡的区域)上。",
"description": ""
},
"manageFavicons": { "manageFavicons": {
"message": "显示已应用的图标", "message": "显示已应用的图标",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -546,10 +546,6 @@
"message": "檢查更新", "message": "檢查更新",
"description": "Label for the checkbox to save current URL for update check" "description": "Label for the checkbox to save current URL for update check"
}, },
"installUpdateUnavailable": {
"message": "要啟用檢查更新,將檔案拖放到分頁條上或是在樣式詮釋資料中指定 @updateURL。",
"description": ""
},
"license": { "license": {
"message": "授權條款", "message": "授權條款",
"description": "Label for the license" "description": "Label for the license"
@ -625,18 +621,10 @@
"message": "觀看檔案時發生錯誤", "message": "觀看檔案時發生錯誤",
"description": "The label of live-reload error" "description": "The label of live-reload error"
}, },
"liveReloadInstallHint": {
"message": "即時重新整理已啟用,以便在這個分頁與來源檔案分頁都開啟時自動於有外部變更時自動更新已安裝樣式。",
"description": "The label of live-reload feature"
},
"liveReloadLabel": { "liveReloadLabel": {
"message": "即時重新整理", "message": "即時重新整理",
"description": "The label of live-reload feature" "description": "The label of live-reload feature"
}, },
"liveReloadUnavailable": {
"message": "要啟用即時重新整理,將檔案托放到分頁條上(分頁標題顯示的區域)。",
"description": ""
},
"manageFavicons": { "manageFavicons": {
"message": "Favicons 要套用到的欄位", "message": "Favicons 要套用到的欄位",
"description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page" "description": "Label for the checkbox that toggles applies-to favicons in the new UI on manage page"

View File

@ -1,7 +1,8 @@
/* global download prefs openURL FIREFOX CHROME VIVALDI /* global download prefs openURL FIREFOX CHROME
debounce URLS ignoreChromeError getTab URLS ignoreChromeError usercssHelper
styleManager msg navigatorUtil iconUtil workerUtil contentScripts sync styleManager msg navigatorUtil workerUtil contentScripts sync
findExistingTab createTab activateTab isTabReplaceable getActiveTab */ findExistingTab createTab activateTab isTabReplaceable getActiveTab
iconManager tabManager */
'use strict'; 'use strict';
@ -49,14 +50,7 @@ window.API_METHODS = Object.assign(window.API_METHODS || {}, {
openEditor, openEditor,
updateIconBadge(count) { updateIconBadge(count) {
// TODO: remove once our manifest's minimum_chrome_version is 50+ iconManager.updateIconBadge(this.sender.tab.id, count);
// Chrome 49 doesn't report own extension pages in webNavigation apparently
// so we do a force update which doesn't use the cache.
if (CHROME && CHROME < 2661 && this.sender.tab.url.startsWith(URLS.ownOrigin)) {
updateIconBadgeForce(this.sender.tab.id, count);
} else {
updateIconBadge(this.sender.tab.id, count);
}
return true; return true;
}, },
@ -87,23 +81,23 @@ var browserCommands, contextMenus;
// register all listeners // register all listeners
msg.on(onRuntimeMessage); msg.on(onRuntimeMessage);
// tell apply.js to refresh styles for non-committed navigation
navigatorUtil.onUrlChange(({tabId, frameId}, type) => { navigatorUtil.onUrlChange(({tabId, frameId}, type) => {
if (type === 'committed') { if (type !== 'committed') {
// styles would be updated when content script is injected.
return;
}
msg.sendTab(tabId, {method: 'urlChanged'}, {frameId}) msg.sendTab(tabId, {method: 'urlChanged'}, {frameId})
.catch(msg.ignoreError); .catch(msg.ignoreError);
}
});
tabManager.onUpdate(({tabId, url, oldUrl = ''}) => {
if (usercssHelper.testUrl(url) && !oldUrl.startsWith(URLS.installUsercss)) {
usercssHelper.testContents(tabId, url).then(data => {
if (data.code) usercssHelper.openInstallerPage(tabId, url, data);
});
}
}); });
if (FIREFOX) { if (FIREFOX) {
// FF applies page CSP even to content scripts, https://bugzil.la/1267027
navigatorUtil.onCommitted(webNavUsercssInstallerFF, {
url: [
{pathSuffix: '.user.css'},
{pathSuffix: '.user.styl'},
]
});
// FF misses some about:blank iframes so we inject our content script explicitly // FF misses some about:blank iframes so we inject our content script explicitly
navigatorUtil.onDOMContentLoaded(webNavIframeHelperFF, { navigatorUtil.onDOMContentLoaded(webNavIframeHelperFF, {
url: [ url: [
@ -122,46 +116,6 @@ if (chrome.commands) {
chrome.commands.onCommand.addListener(command => browserCommands[command]()); chrome.commands.onCommand.addListener(command => browserCommands[command]());
} }
const tabIcons = new Map();
chrome.tabs.onRemoved.addListener(tabId => tabIcons.delete(tabId));
chrome.tabs.onReplaced.addListener((added, removed) => tabIcons.delete(removed));
prefs.subscribe([
'disableAll',
'badgeDisabled',
'badgeNormal',
], () => debounce(refreshIconBadgeColor));
prefs.subscribe([
'show-badge'
], () => debounce(refreshAllIconsBadgeText));
prefs.subscribe([
'disableAll',
'iconset',
], () => debounce(refreshAllIcons));
prefs.initializing.then(() => {
refreshIconBadgeColor();
refreshAllIconsBadgeText();
refreshAllIcons();
});
navigatorUtil.onUrlChange(({tabId, frameId, transitionQualifiers}, type) => {
if (type === 'committed' && !frameId) {
// it seems that the tab icon would be reset by navigation. We
// invalidate the cache here so it would be refreshed by `apply.js`.
tabIcons.delete(tabId);
// however, if the tab was swapped in by forward/backward buttons,
// `apply.js` doesn't notify the background to update the icon,
// so we have to refresh it manually.
if (transitionQualifiers.includes('forward_back')) {
msg.sendTab(tabId, {method: 'updateCount'}).catch(msg.ignoreError);
}
}
});
// ************************************************************************* // *************************************************************************
chrome.runtime.onInstalled.addListener(({reason}) => { chrome.runtime.onInstalled.addListener(({reason}) => {
// save install type: "admin", "development", "normal", "sideload" or "other" // save install type: "admin", "development", "normal", "sideload" or "other"
@ -293,21 +247,6 @@ if (FIREFOX && browser.commands && browser.commands.update) {
msg.broadcastTab({method: 'backgroundReady'}); msg.broadcastTab({method: 'backgroundReady'});
function webNavUsercssInstallerFF(data) {
const {tabId} = data;
Promise.all([
msg.sendTab(tabId, {method: 'ping'})
.catch(() => false),
// we need tab index to open the installer next to the original one
// and also to skip the double-invocation in FF which assigns tab url later
getTab(tabId),
]).then(([pong, tab]) => {
if (pong !== true && tab.url !== 'about:blank') {
window.API_METHODS.openUsercssInstallPage({direct: true}, {tab});
}
});
}
function webNavIframeHelperFF({tabId, frameId}) { function webNavIframeHelperFF({tabId, frameId}) {
if (!frameId) return; if (!frameId) return;
msg.sendTab(tabId, {method: 'ping'}, {frameId}) msg.sendTab(tabId, {method: 'ping'}, {frameId})
@ -326,75 +265,6 @@ function webNavIframeHelperFF({tabId, frameId}) {
}); });
} }
function updateIconBadge(tabId, count) {
let tabIcon = tabIcons.get(tabId);
if (!tabIcon) tabIcons.set(tabId, (tabIcon = {}));
if (tabIcon.count === count) {
return;
}
const oldCount = tabIcon.count;
tabIcon.count = count;
refreshIconBadgeText(tabId, tabIcon);
if (Boolean(oldCount) !== Boolean(count)) {
refreshIcon(tabId, tabIcon);
}
}
function updateIconBadgeForce(tabId, count) {
refreshIconBadgeText(tabId, {count});
refreshIcon(tabId, {count});
}
function refreshIconBadgeText(tabId, icon) {
iconUtil.setBadgeText({
text: prefs.get('show-badge') && icon.count ? String(icon.count) : '',
tabId
});
}
function refreshIcon(tabId, icon) {
const disableAll = prefs.get('disableAll');
const iconset = prefs.get('iconset') === 1 ? 'light/' : '';
const postfix = disableAll ? 'x' : !icon.count ? 'w' : '';
const iconType = iconset + postfix;
if (icon.iconType === iconType) {
return;
}
icon.iconType = iconset + postfix;
const sizes = FIREFOX || CHROME >= 2883 && !VIVALDI ? [16, 32] : [19, 38];
iconUtil.setIcon({
path: sizes.reduce(
(obj, size) => {
obj[size] = `/images/icon/${iconset}${size}${postfix}.png`;
return obj;
},
{}
),
tabId
});
}
function refreshIconBadgeColor() {
const color = prefs.get(prefs.get('disableAll') ? 'badgeDisabled' : 'badgeNormal');
iconUtil.setBadgeBackgroundColor({
color
});
}
function refreshAllIcons() {
for (const [tabId, icon] of tabIcons) {
refreshIcon(tabId, icon);
}
refreshIcon(null, {}); // default icon
}
function refreshAllIconsBadgeText() {
for (const [tabId, icon] of tabIcons) {
refreshIconBadgeText(tabId, icon);
}
}
function onRuntimeMessage(msg, sender) { function onRuntimeMessage(msg, sender) {
if (msg.method !== 'invokeAPI') { if (msg.method !== 'invokeAPI') {
return; return;

101
background/icon-manager.js Normal file
View File

@ -0,0 +1,101 @@
/* global prefs debounce iconUtil FIREFOX CHROME VIVALDI tabManager */
/* exported iconManager */
'use strict';
const iconManager = (() => {
const ICON_SIZES = FIREFOX || CHROME >= 2883 && !VIVALDI ? [16, 32] : [19, 38];
prefs.subscribe([
'disableAll',
'badgeDisabled',
'badgeNormal',
], () => debounce(refreshIconBadgeColor));
prefs.subscribe([
'show-badge'
], () => debounce(refreshAllIconsBadgeText));
prefs.subscribe([
'disableAll',
'iconset',
], () => debounce(refreshAllIcons));
prefs.initializing.then(() => {
refreshIconBadgeColor();
refreshAllIconsBadgeText();
refreshAllIcons();
});
return {updateIconBadge};
// FIXME: in some cases, we only have to redraw the badge. is it worth a optimization?
function updateIconBadge(tabId, count, force = true) {
tabManager.set(tabId, 'count', count);
refreshIconBadgeText(tabId);
refreshIcon(tabId, force);
}
function refreshIconBadgeText(tabId) {
const count = tabManager.get(tabId, 'count');
iconUtil.setBadgeText({
text: prefs.get('show-badge') && count ? String(count) : '',
tabId
});
}
function getIconName(count = 0) {
const iconset = prefs.get('iconset') === 1 ? 'light/' : '';
const postfix = prefs.get('disableAll') ? 'x' : !count ? 'w' : '';
return `${iconset}$SIZE$${postfix}`;
}
function refreshIcon(tabId, force = false) {
const oldIcon = tabManager.get(tabId, 'icon');
const newIcon = getIconName(tabManager.get(tabId, 'count'));
if (!force && oldIcon === newIcon) {
return;
}
tabManager.set(tabId, 'icon', newIcon);
iconUtil.setIcon({
path: getIconPath(newIcon),
tabId
});
}
function getIconPath(icon) {
return ICON_SIZES.reduce(
(obj, size) => {
obj[size] = `/images/icon/${icon.replace('$SIZE$', size)}.png`;
return obj;
},
{}
);
}
function refreshGlobalIcon() {
iconUtil.setIcon({
path: getIconPath(getIconName())
});
}
function refreshIconBadgeColor() {
const color = prefs.get(prefs.get('disableAll') ? 'badgeDisabled' : 'badgeNormal');
iconUtil.setBadgeBackgroundColor({
color
});
}
function refreshAllIcons() {
for (const tabId of tabManager.list()) {
refreshIcon(tabId);
}
refreshGlobalIcon();
}
function refreshAllIconsBadgeText() {
for (const tabId of tabManager.list()) {
refreshIconBadgeText(tabId);
}
}
})();

View File

@ -1,4 +1,4 @@
/* global API_METHODS styleManager CHROME prefs updateIconBadge */ /* global API_METHODS styleManager CHROME prefs iconManager */
'use strict'; 'use strict';
API_METHODS.styleViaAPI = !CHROME && (() => { API_METHODS.styleViaAPI = !CHROME && (() => {
@ -36,7 +36,7 @@ API_METHODS.styleViaAPI = !CHROME && (() => {
throw new Error('we do not count styles for frames'); throw new Error('we do not count styles for frames');
} }
const {frameStyles} = getCachedData(tab.id, frameId); const {frameStyles} = getCachedData(tab.id, frameId);
updateIconBadge(tab.id, Object.keys(frameStyles).length); iconManager.updateIconBadge(tab.id, Object.keys(frameStyles).length);
} }
function styleApply({id = null, ignoreUrlCheck = false}, {tab, frameId, url}) { function styleApply({id = null, ignoreUrlCheck = false}, {tab, frameId, url}) {

43
background/tab-manager.js Normal file
View File

@ -0,0 +1,43 @@
/* global navigatorUtil */
/* exported tabManager */
'use strict';
const tabManager = (() => {
const listeners = [];
const cache = new Map();
chrome.tabs.onRemoved.addListener(tabId => cache.delete(tabId));
chrome.tabs.onReplaced.addListener((added, removed) => cache.delete(removed));
navigatorUtil.onUrlChange(({tabId, frameId, url}) => {
if (frameId) return;
const oldUrl = tabManager.get(tabId, 'url');
tabManager.set(tabId, 'url', url);
for (const fn of listeners) {
try {
fn({tabId, url, oldUrl});
} catch (err) {
console.error(err);
}
}
});
return {
onUpdate(fn) {
listeners.push(fn);
},
get(tabId, key) {
const meta = cache.get(tabId);
return meta && meta[key];
},
set(tabId, key, value) {
let meta = cache.get(tabId);
if (!meta) {
meta = {};
cache.set(tabId, meta);
}
meta[key] = value;
},
list() {
return cache.keys();
},
};
})();

View File

@ -1,42 +1,66 @@
/* global API_METHODS usercss chromeLocal styleManager FIREFOX deepCopy openURL /* global API_METHODS usercss styleManager deepCopy openURL download URLS getTab */
download */ /* exports usercssHelper */
'use strict'; 'use strict';
(() => { // eslint-disable-next-line no-unused-vars
const usercssHelper = (() => {
const installCodeCache = {};
const clearInstallCode = url => delete installCodeCache[url];
const isResponseText = r => /^text\/(css|plain)(;.*?)?$/i.test(r.headers.get('content-type'));
// in Firefox we have to use a content script to read file://
const fileLoader = !chrome.app && // not relying on navigator.ua which can be spoofed
(tabId => browser.tabs.executeScript(tabId, {file: '/content/install-hook-usercss.js'}).then(r => r[0]));
API_METHODS.installUsercss = installUsercss; API_METHODS.installUsercss = installUsercss;
API_METHODS.editSaveUsercss = editSaveUsercss; API_METHODS.editSaveUsercss = editSaveUsercss;
API_METHODS.configUsercssVars = configUsercssVars; API_METHODS.configUsercssVars = configUsercssVars;
API_METHODS.buildUsercss = build; API_METHODS.buildUsercss = build;
API_METHODS.openUsercssInstallPage = install;
API_METHODS.findUsercss = find; API_METHODS.findUsercss = find;
const TEMP_CODE_PREFIX = 'tempUsercssCode'; API_METHODS.getUsercssInstallCode = url => {
const TEMP_CODE_CLEANUP_DELAY = 60e3; // when the installer tab is reloaded after the cache is expired, this will throw intentionally
let tempCodeLastWriteDate = 0; const {code, timer} = installCodeCache[url];
if (FIREFOX) { clearInstallCode(url);
// the temp code is created on direct installation of usercss URLs in FF clearTimeout(timer);
// and can be left behind in case the install page didn't open in time before return code;
// the extension was updated/reloaded/disabled or the browser was closed };
setTimeout(function poll() {
if (Date.now() - tempCodeLastWriteDate < TEMP_CODE_CLEANUP_DELAY) { return {
setTimeout(poll, TEMP_CODE_CLEANUP_DELAY);
return; testUrl(url) {
} return url.includes('.user.') &&
chrome.storage.local.get(null, storage => { /^(https?|file|ftps?):/.test(url) &&
const leftovers = []; /\.user\.(css|styl)$/.test(url.split(/[#?]/, 1)[0]);
for (const key in storage) { },
if (key.startsWith(TEMP_CODE_PREFIX)) {
leftovers.push(key); /** @return {Promise<{ code:string, inTab:boolean } | false>} */
} testContents(tabId, url) {
} const isFile = url.startsWith('file:');
if (leftovers.length) { const inTab = isFile && Boolean(fileLoader);
chrome.storage.local.remove(leftovers); return Promise.resolve(isFile || fetch(url, {method: 'HEAD'}).then(isResponseText))
} .then(ok => ok && (inTab ? fileLoader(tabId) : download(url)))
}); .then(code => /==userstyle==/i.test(code) && {code, inTab});
}, TEMP_CODE_CLEANUP_DELAY); },
openInstallerPage(tabId, url, {code, inTab} = {}) {
const newUrl = `${URLS.installUsercss}?updateUrl=${encodeURIComponent(url)}`;
if (inTab) {
getTab(tabId).then(tab =>
openURL({
url: `${newUrl}&tabId=${tabId}`,
active: tab.active,
index: tab.index + 1,
openerTabId: tabId,
currentWindow: null,
}));
} else {
const timer = setTimeout(clearInstallCode, 10e3, url);
installCodeCache[url] = {code, timer};
chrome.tabs.update(tabId, {url: newUrl});
} }
},
};
function buildMeta(style) { function buildMeta(style) {
if (style.usercssData) { if (style.usercssData) {
@ -156,33 +180,4 @@
} }
}); });
} }
function install({url, direct, downloaded, tab}, sender = this.sender) {
tab = tab !== undefined ? tab : sender.tab;
url = url || tab.url;
if (direct && !downloaded) {
prefetchCodeForInstallation(tab.id, url);
}
return openURL({
url: '/install-usercss.html' +
'?updateUrl=' + encodeURIComponent(url) +
'&tabId=' + tab.id +
(direct ? '&direct=yes' : ''),
index: tab.index + 1,
openerTabId: tab.id,
currentWindow: null,
});
}
function prefetchCodeForInstallation(tabId, url) {
const key = TEMP_CODE_PREFIX + tabId;
tempCodeLastWriteDate = Date.now();
Promise.all([
download(url),
chromeLocal.setValue(key, {loading: true}),
]).then(([code]) => {
chromeLocal.setValue(key, code);
setTimeout(() => chromeLocal.remove(key), TEMP_CODE_CLEANUP_DELAY);
});
}
})(); })();

View File

@ -1,123 +1,22 @@
/* global API */
'use strict'; 'use strict';
(() => { // preventing reregistration if reinjected by tabs.executeScript for whatever reason, just in case
// some weird bug in new Chrome: the content script gets injected multiple times if (typeof self.oldCode !== 'string') {
if (typeof window.initUsercssInstall === 'function') return; self.oldCode = (document.querySelector('body > pre') || document.body).textContent;
if (!/text\/(css|plain)/.test(document.contentType) || chrome.runtime.onConnect.addListener(port => {
!/==userstyle==/i.test(document.body.textContent)) { if (port.name !== 'downloadSelf') return;
return; port.onMessage.addListener(({id, timer}) => {
} fetch(location.href, {mode: 'same-origin'})
window.initUsercssInstall = () => {};
orphanCheck();
const DELAY = 500;
const url = location.href;
let sourceCode, port, timer;
chrome.runtime.onConnect.addListener(onConnected);
API.openUsercssInstallPage({url})
.catch(err => alert(err));
function onConnected(newPort) {
port = newPort;
port.onDisconnect.addListener(stop);
port.onMessage.addListener(onMessage);
}
function onMessage(msg, port) {
switch (msg.method) {
case 'getSourceCode':
fetchText(url)
.then(text => {
sourceCode = sourceCode || text;
port.postMessage({
method: msg.method + 'Response',
sourceCode,
});
})
.catch(err => port.postMessage({
method: msg.method + 'Response',
error: err.message || String(err),
}));
break;
case 'liveReloadStart':
start();
break;
case 'liveReloadStop':
stop();
break;
}
}
function fetchText(url) {
// XHR throws in Chrome 49
// FIXME: choose a correct version
// https://github.com/openstyles/stylus/issues/560
if (getChromeVersion() <= 49) {
return fetch(url)
.then(r => r.text()) .then(r => r.text())
.catch(() => fetchTextXHR(url)); .then(code => ({id, code: timer && code === self.oldCode ? null : code}))
} .catch(error => ({id, error: error.message || `${error}`}))
return fetchTextXHR(url); .then(msg => {
} port.postMessage(msg);
if (msg.code != null) self.oldCode = msg.code;
function fetchTextXHR(url) {
return new Promise((resolve, reject) => {
// you can't use fetch in Chrome under 'file:' protocol
const xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.addEventListener('load', () => resolve(xhr.responseText));
xhr.addEventListener('error', () => reject(xhr));
xhr.send();
}); });
}
function getChromeVersion() {
const match = navigator.userAgent.match(/chrome\/(\d+)/i);
return match ? Number(match[1]) : undefined;
}
function start() {
timer = timer || setTimeout(check, DELAY);
}
function stop() {
clearTimeout(timer);
timer = null;
}
function check() {
fetchText(url)
.then(text => {
if (sourceCode === text) return;
sourceCode = text;
port.postMessage({method: 'sourceCodeChanged', sourceCode});
})
.catch(error => {
console.log(chrome.i18n.getMessage('liveReloadError', error));
})
.then(() => {
timer = null;
start();
}); });
} });
}
function orphanCheck() { // passing the result to tabs.executeScript
const eventName = chrome.runtime.id + '-install-hook-usercss'; self.oldCode; // eslint-disable-line no-unused-expressions
const orphanCheckRequest = () => {
if (chrome.i18n && chrome.i18n.getUILanguage()) return true;
// In Chrome content script is orphaned on an extension update/reload
// so we need to detach event listeners
removeEventListener(eventName, orphanCheckRequest, true);
try {
chrome.runtime.onConnect.removeListener(onConnected);
} catch (e) {}
};
dispatchEvent(new Event(eventName));
addEventListener(eventName, orphanCheckRequest, true);
}
})();

View File

@ -58,19 +58,17 @@
<div class="actions"> <div class="actions">
<h2 class="installed" i18n-text="installButtonInstalled"></h2> <h2 class="installed" i18n-text="installButtonInstalled"></h2>
<button class="install" i18n-text="installButton"></button> <button class="install" i18n-text="installButton"></button>
<p id="live-reload-install-hint" i18n-text="liveReloadInstallHint" class="hidden"></p> <p id="live-reload-install-hint" hidden></p>
<label class="set-update-url"> <label class="set-update-url">
<input type="checkbox"> <input type="checkbox">
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg> <svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
<span class="available-message" i18n-text="installUpdateFromLabel"></span> <span i18n-text="installUpdateFromLabel"></span>
<span class="unavailable-message" i18n-text="installUpdateUnavailable"></span>
<p></p> <p></p>
</label> </label>
<label class="live-reload"> <label class="live-reload">
<input type="checkbox"> <input type="checkbox">
<svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg> <svg class="svg-icon checked"><use xlink:href="#svg-icon-checked"/></svg>
<span class="available-message" i18n-text="liveReloadLabel"></span> <span i18n-text="liveReloadLabel"></span>
<span class="unavailable-message" i18n-text="liveReloadUnavailable"></span>
</label> </label>
</div> </div>
<p class="meta-description"></p> <p class="meta-description"></p>

View File

@ -246,18 +246,6 @@ h2.installed.active {
min-width: 0; min-width: 0;
} }
.unavailable-message,
.unavailable .available-message,
.unavailable .svg-icon,
.live-reload.unavailable input,
.set-update-url.unavailable input {
display: none;
}
.unavailable .unavailable-message {
display: block;
}
.set-update-url { .set-update-url {
flex-wrap: wrap; flex-wrap: wrap;
} }
@ -317,7 +305,7 @@ li {
user-select: auto; user-select: auto;
} }
label:not(.unavailable) { label {
padding-left: 16px; padding-left: 16px;
position: relative; position: relative;
} }

View File

@ -1,46 +1,18 @@
/* global CodeMirror semverCompare closeCurrentTab messageBox download /* global CodeMirror semverCompare closeCurrentTab messageBox download
$ $$ $create $createLink t prefs API getTab */ $ $$ $create $createLink t prefs API */
'use strict'; 'use strict';
(() => { (() => {
const DUMMY_URL = 'foo:';
// TODO: remove .replace(/^\?/, '') when minimum_chrome_version >= 52 (https://crbug.com/601425) // TODO: remove .replace(/^\?/, '') when minimum_chrome_version >= 52 (https://crbug.com/601425)
const params = new URLSearchParams(location.search.replace(/^\?/, '')); const params = new URLSearchParams(location.search.replace(/^\?/, ''));
let liveReload = false; const tabId = params.has('tabId') ? Number(params.get('tabId')) : -1;
const initialUrl = params.get('updateUrl');
let installed = null; let installed = null;
let installedDup = null; let installedDup = null;
const tabId = Number(params.get('tabId')); const liveReload = initLiveReload();
let tabUrl; liveReload.ready.then(initSourceCode, error => messageBox.alert(error, 'pre'));
let port;
if (params.has('direct')) {
setUnavailable('.live-reload');
getCodeDirectly();
} else {
port = chrome.tabs.connect(tabId);
port.postMessage({method: 'getSourceCode'});
port.onMessage.addListener(msg => {
switch (msg.method) {
case 'getSourceCodeResponse':
if (msg.error) {
messageBox.alert(msg.error, 'pre');
} else {
initSourceCode(msg.sourceCode);
}
break;
case 'sourceCodeChanged':
if (msg.error) {
messageBox.alert(msg.error, 'pre');
} else {
liveReloadUpdate(msg.sourceCode);
}
break;
}
});
port.onDisconnect.addListener(onPortDisconnected);
}
const theme = prefs.get('editor.theme'); const theme = prefs.get('editor.theme');
const cm = CodeMirror($('.main'), { const cm = CodeMirror($('.main'), {
@ -54,8 +26,13 @@
href: `vendor/codemirror/theme/${theme}.css` href: `vendor/codemirror/theme/${theme}.css`
})); }));
} }
let liveReloadPending = Promise.resolve();
window.addEventListener('resize', adjustCodeHeight); window.addEventListener('resize', adjustCodeHeight);
// "History back" in Firefox (for now) restores the old DOM including the messagebox,
// which stays after installing since we don't want to wait for the fadeout animation before resolving.
document.addEventListener('visibilitychange', () => {
if (messageBox.element) messageBox.element.remove();
if (installed) liveReload.onToggled();
});
setTimeout(() => { setTimeout(() => {
if (!installed) { if (!installed) {
@ -64,35 +41,6 @@
} }
}, 200); }, 200);
getTab(tabId).then(tab => (tabUrl = tab.url));
chrome.tabs.onUpdated.addListener((id, {url}) => {
if (id === tabId && url && url !== tabUrl) {
closeCurrentTab();
}
});
// close the tab in case the port didn't report onDisconnect
chrome.tabs.onRemoved.addListener(id => {
if (id === tabId) {
closeCurrentTab();
}
});
function liveReloadUpdate(sourceCode) {
liveReloadPending = liveReloadPending.then(() => {
const scrollInfo = cm.getScrollInfo();
const cursor = cm.getCursor();
cm.setValue(sourceCode);
cm.setCursor(cursor);
cm.scrollTo(scrollInfo.left, scrollInfo.top);
API.installUsercss({
id: (installed || installedDup).id,
sourceCode
}).then(style => {
updateMeta(style);
}).catch(showError);
});
}
function updateMeta(style, dup = installedDup) { function updateMeta(style, dup = installedDup) {
installedDup = dup; installedDup = dup;
@ -204,7 +152,7 @@
$$.remove('.warning'); $$.remove('.warning');
$('button.install').disabled = true; $('button.install').disabled = true;
$('button.install').classList.add('installed'); $('button.install').classList.add('installed');
$('#live-reload-install-hint').classList.toggle('hidden', !liveReload); $('#live-reload-install-hint').classList.toggle('hidden', !liveReload.enabled);
$('h2.installed').classList.add('active'); $('h2.installed').classList.add('active');
$('.set-update-url input[type=checkbox]').disabled = true; $('.set-update-url input[type=checkbox]').disabled = true;
$('.set-update-url').title = style.updateUrl ? $('.set-update-url').title = style.updateUrl ?
@ -212,16 +160,18 @@
updateMeta(style); updateMeta(style);
if (!liveReload && !prefs.get('openEditInWindow')) { if (!liveReload.enabled && !prefs.get('openEditInWindow')) {
chrome.tabs.update({url: '/edit.html?id=' + style.id}); location.href = '/edit.html?id=' + style.id;
} else { } else {
API.openEditor({id: style.id}); API.openEditor({id: style.id});
if (!liveReload) { if (!liveReload.enabled) {
if (tabId < 0 && history.length > 1) {
history.back();
} else {
closeCurrentTab(); closeCurrentTab();
} }
} }
}
window.dispatchEvent(new CustomEvent('installed'));
} }
function initSourceCode(sourceCode) { function initSourceCode(sourceCode) {
@ -307,17 +257,11 @@
// set updateUrl // set updateUrl
const checker = $('.set-update-url input[type=checkbox]'); const checker = $('.set-update-url input[type=checkbox]');
// only use the installation URL if not specified in usercss const updateUrl = new URL(style.updateUrl || initialUrl);
const installationUrl = (params.get('updateUrl') || '').replace(/^blob.+/, '');
const updateUrl = new URL(style.updateUrl || installationUrl || DUMMY_URL);
if (dup && dup.updateUrl === updateUrl.href) { if (dup && dup.updateUrl === updateUrl.href) {
checker.checked = true; checker.checked = true;
// there is no way to "unset" updateUrl, you can only overwrite it. // there is no way to "unset" updateUrl, you can only overwrite it.
checker.disabled = true; checker.disabled = true;
} else if (updateUrl.href === DUMMY_URL) {
// drag'n'dropped on the manage page and the style doesn't have @updateURL
setUnavailable('.set-update-url');
return;
} else if (updateUrl.protocol !== 'file:') { } else if (updateUrl.protocol !== 'file:') {
checker.checked = true; checker.checked = true;
style.updateUrl = updateUrl.href; style.updateUrl = updateUrl.href;
@ -329,38 +273,11 @@
$('.set-update-url p').textContent = updateUrl.href.length < 300 ? updateUrl.href : $('.set-update-url p').textContent = updateUrl.href.length < 300 ? updateUrl.href :
updateUrl.href.slice(0, 300) + '...'; updateUrl.href.slice(0, 300) + '...';
if (!port) { if (initialUrl.startsWith('file:')) {
return; $('.live-reload input').onchange = liveReload.onToggled;
}
// live reload
const setLiveReload = $('.live-reload input[type=checkbox]');
if (!installationUrl || !installationUrl.startsWith('file:')) {
setLiveReload.parentNode.remove();
} else { } else {
setLiveReload.addEventListener('change', () => { $('.live-reload').remove();
liveReload = setLiveReload.checked;
if (installed || installedDup) {
const method = 'liveReload' + (liveReload ? 'Start' : 'Stop');
port.postMessage({method});
$('.install').disabled = liveReload;
$('#live-reload-install-hint').classList.toggle('hidden', !liveReload);
} }
});
window.addEventListener('installed', () => {
if (liveReload) {
port.postMessage({method: 'liveReloadStart'});
}
});
}
}
function setUnavailable(label) {
const el = $(label);
el.classList.add('unavailable');
const input = $('input', el);
input.disabled = true;
input.checked = false;
} }
function getAppliesTo(style) { function getAppliesTo(style) {
@ -391,47 +308,105 @@
} }
} }
function getCodeDirectly() { function initLiveReload() {
// FF applies page CSP even to content scripts, https://bugzil.la/1267027 const DELAY = 500;
// To circumvent that, the bg process downloads the code directly let isEnabled = false;
const key = 'tempUsercssCode' + tabId; let timer = 0;
chrome.storage.local.get(key, data => { /** @type function(?options):Promise<string|null> */
const code = data && data[key]; let getData = null;
/** @type Promise */
// bg already downloaded the code let sequence = null;
if (typeof code === 'string') { if (tabId < 0) {
initSourceCode(code); getData = DirectDownloader();
chrome.storage.local.remove(key); sequence = API.getUsercssInstallCode(initialUrl).catch(getData);
return; } else {
getData = PortDownloader();
sequence = getData({timer: false});
} }
return {
// bg still downloads the code get enabled() {
if (code && code.loading) { return isEnabled;
const waitForCodeInStorage = (changes, area) => { },
if (area === 'local' && key in changes) { ready: sequence,
initSourceCode(changes[key].newValue); onToggled(e) {
chrome.storage.onChanged.removeListener(waitForCodeInStorage); if (e) isEnabled = e.target.checked;
chrome.storage.local.remove(key); if (installed || installedDup) {
} (isEnabled ? start : stop)();
}; $('.install').disabled = isEnabled;
chrome.storage.onChanged.addListener(waitForCodeInStorage); Object.assign($('#live-reload-install-hint'), {
return; hidden: !isEnabled,
} textContent: t(`liveReloadInstallHint${tabId >= 0 ? 'FF' : ''}`),
// on the off-chance dbExecChromeStorage.getAll ran right after bg download was saved
download(params.get('updateUrl'))
.then(initSourceCode)
.catch(err => messageBox.alert(t('styleInstallFailed', String(err)), 'pre'));
}); });
} }
},
function onPortDisconnected() { };
function check() {
getData()
.then(update, logError)
.then(() => {
timer = 0;
start();
});
}
function logError(error) {
console.warn(t('liveReloadError', error));
}
function start() {
timer = timer || setTimeout(check, DELAY);
}
function stop() {
clearTimeout(timer);
timer = 0;
}
function update(code) {
if (code == null) return;
sequence = sequence.catch(console.error).then(() => {
const {id} = installed || installedDup;
const scrollInfo = cm.getScrollInfo();
const cursor = cm.getCursor();
cm.setValue(code);
cm.setCursor(cursor);
cm.scrollTo(scrollInfo.left, scrollInfo.top);
return API.installUsercss({id, sourceCode: code})
.then(updateMeta)
.catch(showError);
});
}
function DirectDownloader() {
let oldCode = null;
const passChangedCode = code => {
const isSame = code === oldCode;
oldCode = code;
return isSame ? null : code;
};
return () => download(initialUrl).then(passChangedCode);
}
function PortDownloader() {
const resolvers = new Map();
const port = chrome.tabs.connect(tabId, {name: 'downloadSelf'});
port.onMessage.addListener(({id, code, error}) => {
const r = resolvers.get(id);
resolvers.delete(id);
if (error) {
r.reject(error);
} else {
r.resolve(code);
}
});
port.onDisconnect.addListener(() => {
chrome.tabs.get(tabId, tab => { chrome.tabs.get(tabId, tab => {
if (chrome.runtime.lastError) { if (chrome.runtime.lastError) {
closeCurrentTab(); closeCurrentTab();
} else if (tab.url === tabUrl) { } else if (tab.url === initialUrl) {
location.reload(); location.reload();
} }
}); });
});
return ({timer = true} = {}) => new Promise((resolve, reject) => {
const id = performance.now();
resolvers.set(id, {resolve, reject});
port.postMessage({id, timer});
});
}
} }
})(); })();

View File

@ -39,6 +39,8 @@ const URLS = {
OPERA ? 'opera://settings/configureCommands' OPERA ? 'opera://settings/configureCommands'
: 'chrome://extensions/configureCommands', : 'chrome://extensions/configureCommands',
installUsercss: chrome.runtime.getURL('install-usercss.html'),
// CWS cannot be scripted in chromium, see ChromeExtensionsClient::IsScriptableURL // CWS cannot be scripted in chromium, see ChromeExtensionsClient::IsScriptableURL
// https://cs.chromium.org/chromium/src/chrome/common/extensions/chrome_extensions_client.cc // https://cs.chromium.org/chromium/src/chrome/common/extensions/chrome_extensions_client.cc
browserWebStore: browserWebStore:

View File

@ -1,4 +1,4 @@
/* global messageBox styleSectionsEqual getOwnTab API onDOMready /* global messageBox styleSectionsEqual API onDOMready
tryJSONparse scrollElementIntoView $ $$ API $create t animateElement tryJSONparse scrollElementIntoView $ $$ API $create t animateElement
styleJSONseemsValid */ styleJSONseemsValid */
'use strict'; 'use strict';
@ -87,14 +87,11 @@ function importFromFile({fileTypeFilter, file} = {}) {
const text = event.target.result; const text = event.target.result;
const maybeUsercss = !/^[\s\r\n]*\[/.test(text) && const maybeUsercss = !/^[\s\r\n]*\[/.test(text) &&
(text.includes('==UserStyle==') || /==UserStyle==/i.test(text)); (text.includes('==UserStyle==') || /==UserStyle==/i.test(text));
(!maybeUsercss ? if (maybeUsercss) {
importFromString(text) : messageBox.alert(t('dragDropUsercssTabstrip'));
getOwnTab().then(tab => { return;
tab.url = URL.createObjectURL(new Blob([text], {type: 'text/css'})); }
return API.openUsercssInstallPage({direct: true, tab}) importFromString(text).then(numStyles => {
.then(() => URL.revokeObjectURL(tab.url));
})
).then(numStyles => {
document.body.style.cursor = ''; document.body.style.cursor = '';
resolve(numStyles); resolve(numStyles);
}); });

View File

@ -46,6 +46,8 @@
"background/style-manager.js", "background/style-manager.js",
"background/navigator-util.js", "background/navigator-util.js",
"background/icon-util.js", "background/icon-util.js",
"background/tab-manager.js",
"background/icon-manager.js",
"background/background.js", "background/background.js",
"background/usercss-helper.js", "background/usercss-helper.js",
"background/style-via-api.js", "background/style-via-api.js",
@ -92,30 +94,6 @@
"run_at": "document_start", "run_at": "document_start",
"all_frames": false, "all_frames": false,
"js": ["content/install-hook-openusercss.js"] "js": ["content/install-hook-openusercss.js"]
},
{
"matches": [
"*://*/*.user.css",
"*://*/*.user.styl",
"file://*/*.user.css",
"file://*/*.user.styl",
"ftp://*/*.user.css",
"ftp://*/*.user.styl",
"*://*/*.user.css?*",
"*://*/*.user.styl?*",
"file://*/*.user.css?*",
"file://*/*.user.styl?*",
"ftp://*/*.user.css?*",
"ftp://*/*.user.styl?*"
],
"run_at": "document_idle",
"all_frames": false,
"js": ["content/install-hook-usercss.js"]
} }
], ],
"browser_action": { "browser_action": {

View File

@ -34,8 +34,8 @@
"update-transifex": "tx push -s", "update-transifex": "tx push -s",
"build-vendor": "shx rm -rf vendor/* && node tools/build-vendor", "build-vendor": "shx rm -rf vendor/* && node tools/build-vendor",
"zip": "node tools/zip.js", "zip": "node tools/zip.js",
"start": "web-ext run --bc", "start": "web-ext run",
"start-chrome": "web-ext run -t chromium --bc", "start-chrome": "web-ext run -t chromium",
"preversion": "npm test", "preversion": "npm test",
"version": "sync-version manifest.json && git add .", "version": "sync-version manifest.json && git add .",
"postversion": "npm run zip && git push --follow-tags" "postversion": "npm run zip && git push --follow-tags"