แกแแแ แแ "แแแแแแข-แกแแ แแแ แแก" แแ แฅแแขแแฅแขแฃแ แแกแแแ แแแแกแฎแแแแแแแ, แแแชแแแขแ แแแแแแแฃแแ แแแแแแแชแแแแ แฎแแกแแแแแแแ:
- แแ แแ แแก แกแแญแแ แ แแแแแชแแแแ แแแแแก แจแแแแฎแแ แแแแฎแแแ แแแแแก แจแแกแแแแแแแ แแ แแแ แแแแแแ. แฌแแแแแแก แแแคแแ แแแชแแ แแแแฎแแแ แแฅแกแแแฃแแแฃแ แแ แแแแแ แแแแฎแแแ แแแแแแแก แแแแ แแ แแแแ แแแแแแขแฃแ แแแแก แแแแแกแขแฃแ แแแ แฎแแแแ แแ แแขแแแแแแก แแแแแแ.
- แแ แแ แแก แกแแญแแ แ แกแแ แแแ แแก แแแแแงแแแแแ. แแแแแแแชแแแก แแแแแแ แจแแแซแแแแ แจแแกแ แฃแแแแก แแแแแฉแแแ แฅแกแแแจแ, แกแแแแช แจแแกแแซแแแแแแแ แกแแญแแ แ แ แแแแแแแแแก แแแแแชแแแแแแก แจแแแแฎแแ.
แแแแฎแแแ แแแแแก แแแกแแฆแแแแแแกแแแแก แแ แแก 2 แจแแแแ แแแแ แฃแกแแคแ แแฎแ แกแแชแแแ - แขแแฅแแแแแก แกแแคแฃแแแแแ แแ แแ แแฃแแแ แแก แแแคแแ แแแแแแแ. แขแแฅแแแแแก แกแแคแฃแแแแแ แซแแ แแแแแแ แฃแแแแฃแ แแกแแ แฃแกแแคแ แแฎแแ, แแแแ แแ แ แแฃแแ แแแแแกแแงแแแแแแแ แแ แจแแ แก แแ แแก แฃแคแแกแ, แแแแ แแ แแ แแฃแแแ แแก แแแคแแ แแแแแแแ แฃแกแแคแ แแฎแแแแแกแ แแ แแแแแงแแแแแแก แกแแแแ แขแแแแก แจแแกแแแแจแแแแ แแแแแแแแชแแแ แแ แแกแแแ แจแแแซแแแแ แแงแแก แกแ แฃแแแแ แฃแคแแกแ แกแแแแแแ แแแแฎแแแ แแแแแแแกแแแแก.
แแ แงแแแแแคแ แแก แแแแแแแแกแฌแแแแแแ, แฉแแแ แแแแแแแแ แจแแแแแฅแแแ แงแแแแแแ แฃแกแแคแ แแฎแ แแแคแแ แแแแแ, แ แแแแแแช แแแแ แขแแแแแก แแแชแแแขแ แแแแแแแฃแแ แแแแแแแชแแแแแก แแแแแแแแ แแแแก แขแ แแแแแฅแชแแแแแแ แแ แฎแแแแแฌแแ แแแแแ แแฃแจแแแแแก แแแ แขแแแ API-แก แแแฌแแแแแแ.
แแ แแแแแชแแแแแแแก แจแแกแแฎแแ แฅแแแแแ แแแแแงแแแแแ.
แกแขแแขแแ แจแแแชแแแก แแแแแฏ-แแแแแฏ แแแกแขแ แฃแฅแชแแแแก, แแฃ แ แแแแ แฃแแแ แแแฌแแ แแ แแ แแฃแแแ แแก แแแคแแ แแแแแ, แแแแแก แแแแแแแแแแแ แแ แแแ แแแแก แแแแแแญแแแแแ. แแฅแแแ แจแแแแซแแแแ แแแแแแ แงแแแแ แแแแ
แแ แแฃแแแ แแก แแแคแแ แแแแแแแแก แแแแแ แแกแขแแ แแ
แแ แแฃแแแ แแก แแแคแแ แแแแแแแ แแแแ แฎแแแแ แแ แกแแแแแก. แแกแแแ แแแแแฉแแแแแ Internet Explorer-แจแ 1999 แฌแแแก, Firefox-แจแ 2004 แฌแแแก. แแฃแแชแ, แซแแแแแ แแแแ แฎแแแก แแแแแแแแแแแจแ แแ แแ แกแแแแแแ แแ แแ แกแขแแแแแ แขแ แแแคแแ แแแแแแกแแแแก.
แจแแแซแแแแ แแแฅแแแก, แ แแ แแก แแแคแแ แแแแแแแแแ แแ แแแ แแแแแฉแแแ Google Chrome-แแก แแแแแฎแ แแแ แกแแแจแ. แ แ แแฅแแ แฃแแแ, แแแจแแ แแ แแ แกแแแแแแ แกแแแชแแคแแแแชแแ, แแแแ แแ แแก แแงแ Chrome API, แ แแแแแแช แแแฎแแ แแแกแ แกแแคแฃแซแแแแ: แแ แแฃแแแ แแก แแแแ แแก แฃแแแขแแกแ แแแฌแแแ แแแแแงแ แ แแ แฉแแจแแแแแฃแแ แแแแแแแชแแแแแก แแแฆแแแแ แฐแฅแแแแ, Chrome-แแ แ แแแแฃแ แแ แแแแฌแแกแ แกแขแแแแแ แขแ แแ แแฃแแแ แแก แแแคแแ แแแแแแแแกแแแแก.
Mozilla-แก แฐแฅแแแแ แกแแแฃแแแ แ แกแขแแแแแ แขแ, แแแแ แแ Chrome-แแก แแแคแแ แแแแแแแแก แแแแฃแแแ แแแแก แแแแแแแแกแฌแแแแแแ, แแแแแแแแแ แแแแแฌแงแแแขแ แจแแแฅแแแ แแแแกแแแแแ API. 2015 แฌแแแก, Mozilla-แก แแแแชแแแขแแแแ, แแกแแคแแแ แฅแกแแแแก แแแแกแแ แชแแฃแแแก (W3C) แคแแ แแแแแจแ แจแแแฅแแแ แกแแแชแแแแฃแ แ แฏแแฃแคแ, แ แแแแแแช แแฃแจแแแแแ แแ แแฃแแแ แแก แแแคแแ แแแแแแก แกแแแชแแคแแแแชแแแแแ.
แกแแคแฃแซแแแแ แแฅแแ แแแฆแแแฃแแ Chrome-แแกแแแแก แแ แกแแแฃแแ API แแแคแแ แแแแแแแ. แแฃแจแแแแ แแแแฎแแ แชแแแแแ Microsoft-แแก แแฎแแ แแแญแแ แแ (แแฃแแแแ แฃแแ แ แแฅแแ แกแขแแแแแ แขแแก แจแแแฃแจแแแแแแจแ แแแแแฌแแแแแแแแ), แ แแก แจแแแแแแแแช แแแแแฉแแแ แแ แแแฅแขแ
แคแแ แแแแฃแ แแ, แกแแแชแแคแแแแชแแแก แแฎแแ แก แฃแญแแ แก Edge, Firefox แแ Opera (แแแแแแแแแกแฌแแแแ, แ แแ Chrome แแ แแ แแก แแ แกแแแจแ). แกแแแแแแแแแแจแ, แกแขแแแแแ แขแ แแแแฌแแแแ แแแแกแแแแแแ Chrome-แแแ, แ แแแแแ แแก แ แแแแฃแ แแ แแฌแแ แแแ แแแกแ แแแคแแ แแแแแแแแก แกแแคแฃแซแแแแแ. แจแแแแซแแแแ แแแขแ แฌแแแแแแฎแแ WebExtensions API-แแก แจแแกแแฎแแ
แแแคแแ แแแแแแก แกแขแ แฃแฅแขแฃแ แ
แแ แแแแแ แแ แคแแแแ, แ แแแแแแช แกแแญแแ แแ แแแคแแ แแแแแแกแแแแก, แแ แแก manifest (manifest.json). แแก แแกแแแ แแ แแก แแแคแแ แแแแแแก "แจแแกแแแแก แฌแแ แขแแแ".
แแแแแคแแกแขแ
แกแแแชแแคแแแแชแแแก แแแฎแแแแแ, manifest แคแแแแ แแ แแก แแแฅแแแแ JSON แคแแแแ. แแแแแคแแกแขแแก แแแแแแจแแแแก แกแ แฃแแ แแฆแฌแแ แ แแแคแแ แแแชแแ แแแแก แจแแกแแฎแแ, แแฃ แ แแแแแ แแแแแแจแแแแ แแฎแแ แแแญแแ แแแ, แ แแแแ แแ แแฃแแแ แจแ แจแแแซแแแแ แแแฎแแ
แแแกแแฆแแแแแ, แ แแแแแแแช แแ แแ แแก แกแแแชแแคแแแแชแแแจแ โแจแแแซแแแแโ แแแแแ แแ แแแฃแแ แแงแแก (แ แแแแ แช Chrome, แแกแแแ Firefox แแชแแแแแแแ แจแแชแแแแแแก, แแแแ แแ แแแคแแ แแแแแแแ แแแ แซแแแแแแ แแฃแจแแแแแก).
แแ แแแแแ แแแแแแแฎแแแแ แงแฃแ แแแฆแแแ แ แแแแแแแแ แแฃแแฅแขแแ.
- แคแแแแ - แแแแแฅแขแ, แ แแแแแแช แแแแชแแแก แจแแแแแ แแแแแแก:
- แกแแ แแแขแแแ โ แกแแ แแแขแแแแก แแแกแแแ, แ แแแแแแช แจแแกแ แฃแแแแแ แคแแแฃแ แแแแขแแฅแกแขแจแ (แแแแแ แชแแขแ แแแแแแแแแแแ แแแกแแฃแแ แแแ);
- แแแแ แแแ โ แกแแ แแแขแแแแก แแแชแแแแ, แ แแแแแแแช แจแแกแ แฃแแแแแ แชแแ แแแ แแแแ แแแ, แจแแแแซแแแแ แแแฃแแแแแ html แจแแแแแ แกแแ. แแ แจแแแแฎแแแแแจแ แกแแ แแแขแแก แแแแ แแแแแ แแ แแแฃแแ แแฅแแแแ แแ แกแแญแแ แ แแฅแแแแ แกแแ แแแขแแแแก แฉแแกแแ แจแแแแแ แกแแก แแแแ แแแ;
- แแ แกแแแแแก โ แแแแแ แฃแแ แแ แแจแ, แแฃ แแ แแ แแก แแแแแแแแฃแแ, แแ แแฃแแแ แ โแแแแแแแกโ แคแแแฃแ แแ แแชแแกแก, แ แแชแ แฉแแแแแแก, แ แแ แแก แแ แแคแแ แก แแแแแแแก แแ แกแแญแแ แแแแแก แจแแแแฎแแแแแจแ แแแแแขแแแ แแแแก แแแก. แฌแแแแแฆแแแแ แจแแแแฎแแแแแจแ, แแแแ แแ แแแแแขแแแ แแแแ แแฎแแแแ แแ แแฃแแแ แแก แแแฎแฃแ แแแกแแก. แแ แแ แแก แแฎแแ แแแญแแ แแแ Firefox-แจแ.
- แจแแแแแ แกแ_แกแแ แแแขแแแ - แแแแแฅแขแแแแก แแแกแแแ, แ แแแแแแช แกแแจแฃแแแแแแก แแแซแแแแ แฉแแขแแแ แแแ แกแฎแแแแแกแฎแแ แกแแ แแแขแแแ แกแฎแแแแแกแฎแแ แแแ แแแแ แแแแแ. แแแแแแฃแแ แแแแแฅแขแ แจแแแชแแแก แจแแแแแ แแแแจแแแแแแแแ แแแแแแก:
- แแแขแฉแแแ -
แแแแฃแจแแก url , แ แแแแแแช แแแแกแแแฆแแ แแแก, แจแแแแก แแฃ แแ แ แแแแแ แแขแฃแแ แจแแแแแ แกแแก แกแแ แแแขแ. - js โ แกแแ แแแขแแแแก แกแแ, แ แแแแแแแช แฉแแแขแแแ แแแแ แแ แแแขแฉแจแ;
- exclude_matches - แแแแแ แแชแฎแแแก แแแแแแแแแ
match
URL-แแแ, แ แแแแแแแช แแแแฎแแแแ แแ แแแแก.
- แแแขแฉแแแ -
- แแแแ แแ_แแแฅแแแแแแ - แ แแแแฃแ แแ แแ แแก แแแแแฅแขแ, แ แแแแแแช แแแกแฃแฎแแกแแแแแแแแ แแ แแฃแแแ แจแ แแแกแแแแ แแแแแก แแแแแก แแแแ แแแ แแแแแกแแฎแฃแ แฎแแขแแ แแ แแแกแแแ แฃแ แแแแ แแฅแแแแแแแแ. แแก แแกแแแ แกแแจแฃแแแแแแก แแแซแแแแ แแฉแแแแแ แแแแแฎแขแแ แ แคแแแฏแแ แ, แ แแแแแแช แแแแแกแแแฆแแ แแแ แแฅแแแแ แกแแแฃแแแ แ HTML, CSS แแ JS แแแแแงแแแแแแ.
- default_popup โ HTML แคแแแแแก แแแ แแแแแฎแขแแ แ แแแขแแ แคแแแกแแ, แจแแแซแแแแ แจแแแชแแแแแก CSS แแ JS.
- แฃแคแแแแแแ โ แแแกแแแ แแแคแแ แแแแแแก แฃแคแแแแแแแก แแแ แแแแกแแแแก. แแ แกแแแแแก 3 แกแแฎแแก แฃแคแแแแ, แ แแแแแแแช แแแขแแแฃแ แแ แแ แแก แแฆแฌแแ แแแ
แแฅ - แแแ_แฎแแแแแกแแฌแแแแแ_แ แแกแฃแ แกแแแ โ แแแคแแ แแแแแแก แ แแกแฃแ แกแแแ, แ แแแแแแแช แจแแแซแแแแ แแแแแฎแแแแก แแแ แแแแ แแแ, แแแแแแแแแ, แกแฃแ แแแแแ, JS, CSS, HTML แคแแแแแแ.
- แแแ แ_แแแแแแจแแ แแแแแ โ แแฅ แจแแแแซแแแแ แแแ แแแแแ แแแฃแแแแแ แแแ แแแแ แแแแแก แกแฎแแ แแแคแแ แแแแแแแแก แแ แแแแแแแแแก ID, แ แแแแแแแแแแแช แจแแแแซแแแแ แแแแแแจแแ แแแ. แแแแแแ แจแแแซแแแแ แแงแแก แแแแ แ แแแแแก แแ แฃแคแ แ แแแฆแแแ. Firefox-แจแ แแ แแฃแจแแแแก.
แจแแกแ แฃแแแแแก แแแแขแแฅแกแขแ
แแแคแแ แแแแแแก แแฅแแก แกแแแ แแแแแก แจแแกแ แฃแแแแแก แแแแขแแฅแกแขแ, แแแฃ แแแแแแแชแแ แจแแแแแแ แกแแแ แแแฌแแแแกแแแ, แแ แแฃแแแ แแก API-แแ แฌแแแแแแก แกแฎแแแแแกแฎแแ แแแแแ.
แแแคแแ แแแแแแก แแแแขแแฅแกแขแ
API-แแก แฃแแแขแแกแแแ แแฅ แแ แแก แฎแแแแแกแแฌแแแแแ. แแ แแแแขแแฅแกแขแจแ แแกแแแ "แชแฎแแแ แแแแ":
- แคแแแแก แแแแ แแ โ แแแคแแ แแแแแแก โbackendโ แแแฌแแแ. แคแแแแ แแแแแแแแฃแแแ แแแแแคแแกแขแจแ แฆแแแแแแก โแคแแแแกโ แแแแแงแแแแแแ.
- แแแแแฎแขแแ แ แแแแ แแ โ แแแแแฎแขแแ แ แแแแ แแ, แ แแแแแแช แฉแแแแแ แแแคแแ แแแแแแก แฎแแขแฃแแแแ แแแฌแแแแฃแแแแแกแแก. แแแแแคแแกแขแจแ
browser_action
->default_popup
. - แแแ แแแแฃแแ แแแแ แแ โ แแแคแแ แแแแแแก แแแแ แแ, โแชแฎแแแ แแแโ แฎแแแแก แชแแแแ แฉแแแแ แแจแ
chrome-extension://<id_ัะฐััะธัะตะฝะธั>/customPage.html
.
แแก แแแแขแแฅแกแขแ แแ แกแแแแแก แแ แแฃแแแ แแก แคแแแฏแ แแแแกแ แแ แฉแแแแ แแแแแกแแแ แแแแแฃแแแแแแแแ. แคแแแแก แแแแ แแ แแ แกแแแแแก แแ แ แแแแแแแแแ แแ แแ แงแแแแแแแแก แแฃแจแแแแก (แแแแแแแแแแกแแ แแแแแแแแก แแแแ แแ, แ แแแแกแแช แคแแแแก แกแแ แแแขแ แแแจแแแแฃแแแ แแแแแแแแก แแแแ แแ "แแแแแแ" แแแกแ แจแแกแ แฃแแแแแก แจแแแแแ). แแแแแฎแขแแ แ แแแแ แแ แแ แกแแแแแก, แ แแแแกแแช แแแแแฎแขแแ แ แคแแแฏแแ แ แฆแแแ แแ แแแ แแแแฃแแ แแแแ แแ โ แกแแแแ แแแกแแแ แฉแแแแ แแ แฆแแแ. แแ แแแแขแแฅแกแขแแแแ แกแฎแแ แฉแแแแ แแแแแ แแ แแแ แจแแแแแ แกแแ แฌแแแแแ แแ แแ แแก.
แแแแขแแแขแแก แกแแ แแแขแแก แแแแขแแฅแกแขแ
แจแแแแแ แกแแก แกแแ แแแขแแก แคแแแแ แแฎแกแแแแ แแ แแฃแแแ แแก แแแแแแฃแ แฉแแแแ แแแแ แแ แแแ. แแแก แแฅแแก แฌแแแแแ แแแคแแ แแแแแแก API-แแก แแแฌแแแแ แแ แแแ แแแแ แแแก DOM แฎแแแ. แแก แแ แแก แจแแแแแ แกแแก แกแแ แแแขแแแ, แ แแแแแแแช แแแกแฃแฎแแกแแแแแแแแ แแ แแแ แแแแ แแแแ แฃแ แแแแ แแฅแแแแแแแกแแแแก. แแแคแแ แแแแแแแ, แ แแแแแแแช แแแแแแฃแแแ แแแแ DOM แฎแแแ, แแแแก แแแแแแแแ แจแแแแแ แกแแก แกแแ แแแขแแแจแ - แแแแแแแแแ, แ แแแแแแแก แแแแแแ แแแจแ แแ แแแแ แแแแแแแแจแ. แแกแแแ, แแแแขแแแขแแก แกแแ แแแขแก แจแแฃแซแแแ แแแแ แแแแ แแแแฃแแแแแชแแ แกแขแแแแแ แขแแก แกแแจแฃแแแแแแ postMessage
.
แแแ แแแแ แแแก แแแแขแแฅแกแขแ
แแก แแ แแก แแแแแ แแแ แแแแ แแ. แแแก แแ แแคแแ แ แแฅแแก แกแแแ แแ แแแคแแ แแแแแแกแแแ แแ แแ แแฅแแก แฌแแแแแ แแฅ, แแแ แแ แแ แจแแแแฎแแแแแแแกแ, แ แแแแกแแช แแ แแแแ แแแก แแแแแแ แแจแแแ แแ แแ แแ แแก แแแแแแแแฃแแ แแแแแคแแกแขแจแ (แแแฌแแ แแแแแแ แแแแก แจแแกแแฎแแ แฅแแแแแ).
แจแแขแงแแแแแแแ
แแแแแแแชแแแก แกแฎแแแแแกแฎแแ แแแฌแแแแ แฃแแแ แแแชแแแแแก แจแแขแงแแแแแแแแแ แแ แแแแแแแแแ. แแแแกแแแแก แแ แแก API runtime.sendMessage
แจแแขแงแแแแแแแแก แแแกแแแแแแแแ background
ะธ tabs.sendMessage
แจแแขแงแแแแแแแแก แแแแแแแแ แแแแ แแแ (แแแแขแแแขแแก แกแแ แแแขแ, แแแแแฎแขแแ แ แคแแแฏแแ แ แแ แแแ แแแแ แแ, แแฃ แแก แจแแกแแซแแแแแแแ externally_connectable
). แฅแแแแแ แแแชแแแฃแแแ แแแแแแแแ Chrome API-แแ แฌแแแแแแกแแก.
// ะกะพะพะฑัะตะฝะธะตะผ ะผะพะถะตั ะฑััั ะปัะฑะพะน JSON ัะตัะธะฐะปะธะทัะตะผัะน ะพะฑัะตะบั
const msg = {a: 'foo', b: 'bar'};
// extensionId ะผะพะถะฝะพ ะฝะต ัะบะฐะทัะฒะฐัั, ะตัะปะธ ะผั ั
ะพัะธะผ ะฟะพัะปะฐัั ัะพะพะฑัะตะฝะธะต 'ัะฒะพะตะผั' ัะฐััะธัะตะฝะธั (ะธะท ui ะธะปะธ ะบะพะฝัะตะฝั ัะบัะธะฟัะฐ)
chrome.runtime.sendMessage(extensionId, msg);
// ะขะฐะบ ะฒัะณะปัะดะธั ะพะฑัะฐะฑะพััะธะบ
chrome.runtime.onMessage.addListener((msg) => console.log(msg))
// ะะพะถะฝะพ ัะปะฐัั ัะพะพะฑัะตะฝะธั ะฒะบะปะฐะดะบะฐะผ ะทะฝะฐั ะธั
id
chrome.tabs.sendMessage(tabId, msg)
// ะะพะปััะธัั ะบ ะฒะบะปะฐะดะบะฐะผ ะธ ะธั
id ะผะพะถะฝะพ, ะฝะฐะฟัะธะผะตั, ะฒะพั ัะฐะบ
chrome.tabs.query(
{currentWindow: true, active : true},
function(tabArray){
tabArray.forEach(tab => console.log(tab.id))
}
)
แกแ แฃแแ แแแแฃแแแแแชแแแกแแแแก, แจแแแแซแแแแ แจแแฅแแแแ แแแแจแแ แแแ runtime.connect
. แแแกแฃแฎแแ แแแแแฆแแแ runtime.Port
, แ แแแแแกแแช, แกแแแแ แแก แฆแแแ, แจแแแแซแแแแ แแแแแแแแแ แแแแแกแแแแ แ แ แแแแแแแแแก แจแแขแงแแแแแแแ. แแแแแแขแแก แแฎแ แแแแ, แแแแแแแแแ, contentscript
, แแกแ แแแแแแงแฃแ แแแ:
// ะะฟััั ะถะต extensionId ะผะพะถะฝะพ ะฝะต ัะบะฐะทัะฒะฐัั ะฟัะธ ะบะพะผะผัะฝะธะบะฐัะธะธ ะฒะฝัััะธ ะพะดะฝะพะณะพ ัะฐััะธัะตะฝะธั. ะะพะดะบะปััะตะฝะธะต ะผะพะถะฝะพ ะธะผะตะฝะพะฒะฐัั
const port = chrome.runtime.connect({name: "knockknock"});
port.postMessage({joke: "Knock knock"});
port.onMessage.addListener(function(msg) {
if (msg.question === "Who's there?")
port.postMessage({answer: "Madame"});
else if (msg.question === "Madame who?")
port.postMessage({answer: "Madame... Bovary"});
แกแแ แแแ แ แแ แคแแแ:
// ะะฑัะฐะฑะพััะธะบ ะดะปั ะฟะพะดะบะปััะตะฝะธั 'ัะฒะพะธั
' ะฒะบะปะฐะดะพะบ. ะะพะฝัะตะฝั ัะบัะธะฟัะพะฒ, popup ะธะปะธ ัััะฐะฝะธั ัะฐััะธัะตะฝะธั
chrome.runtime.onConnect.addListener(function(port) {
console.assert(port.name === "knockknock");
port.onMessage.addListener(function(msg) {
if (msg.joke === "Knock knock")
port.postMessage({question: "Who's there?"});
else if (msg.answer === "Madame")
port.postMessage({question: "Madame who?"});
else if (msg.answer === "Madame... Bovary")
port.postMessage({question: "I don't get it."});
});
});
// ะะฑัะฐะฑะพััะธะบ ะดะปั ะฟะพะดะบะปััะตะฝะธั ะฒะฝะตัะฝะธั
ะฒะบะปะฐะดะพะบ. ะััะณะธั
ัะฐััะธัะตะฝะธะน ะธะปะธ ะฒะตะฑ ัััะฐะฝะธั, ะบะพัะพััะผ ัะฐะทัะตัะตะฝ ะดะพัััะฟ ะฒ ะผะฐะฝะธัะตััะต
chrome.runtime.onConnectExternal.addListener(function(port) {
...
});
แแกแแแ แแ แแก แฆแแแแกแซแแแแ onDisconnect
แแ แแแแแแ disconnect
.
แแแแแชแฎแแแแก แแแแแ แแแ
แแแแแ แจแแแฅแแแแ แแ แแฃแแแ แแก แแแคแแ แแแแแ, แ แแแแแแช แแแแฎแแแก แแแ แแ แแแกแแฆแแแแแก, แฃแแ แฃแแแแแงแแคแก แกแแฏแแ แ แแแคแแ แแแชแแแแ แฌแแแแแแก (แแแกแแแแ แแ, แกแแฏแแ แ แแแกแแฆแแแ แแแฃแแแแจแแ แแแแ แแแแ แแแ แแ แกแแจแฃแแแแแแก แแซแแแแก แแแกแแแ แแฎแแ แแก แแแแแแแชแแแแก แแแแแฎแแแแ แฎแแแแแฌแแ แ แขแ แแแแแฅแชแแแแแ.
แแแแแแแชแแแก แจแแแฃแจแแแแแ
แฉแแแแแ แแแแแแแชแแแ แฃแแแ แแแแฅแแแแแก แแแแฎแแแ แแแแแแแ แแ แแแแฌแแแแก แแแแ แแก API แแแแแแแแแก แแแแแกแแซแแฎแแแแแ (แแแแแแแแแ, แขแ แแแแแฅแชแแแแแก แแแกแแคแแ แแแแแแ). แจแแแกแ แฃแแแ แแฎแแแแ แแ แแ contentscript
แแ แแแฃแจแแแแแก, แ แแแแแ แแแก แแฅแแก แฌแแแแแ แแฎแแแแ DOM-แแ, แแแแ แแ แแ แ แแแแ แแแก JS-แแ. แแแแแแจแแ แแแ แแแจแแแแแแ runtime.connect
แฉแแแ แแ แจแแแแแซแแแ, แ แแแแแ API แกแแญแแ แแ แงแแแแ แแแแแแแ แแ แแฎแแแแ แแแแแ แแขแฃแแ แจแแแซแแแแ แแงแแก แแแแแแแแฃแแ แแแแแคแแกแขแจแ. แจแแแแแแ, แแแแแ แแแ แแกแ แแแแแแงแฃแ แแแ:
แแฅแแแแ แกแฎแแ แกแชแแแแ แ - inpage
, แ แแแแแกแแช แแแแ แแแ แจแแแแขแแแ. แแก แแแฃแจแแแแแก แแแแแก แแแแขแแฅแกแขแจแ แแ แฃแแ แฃแแแแแงแแคแก API-แก แแแคแแ แแแแแแกแแแ แแฃแจแแแแแกแแแแก.
แแแฌแงแแแ
แแ แแฃแแแ แแก แแแคแแ แแแแแแก แงแแแแ แแแแ แฎแแแแแกแแฌแแแแแแ แแแกแแแแ แแแ
แแแแแฌแงแแ แแแแแคแแกแขแแ:
{
// ะะผั ะธ ะพะฟะธัะฐะฝะธะต, ะฒะตััะธั. ะัะต ััะพ ะฑัะดะตั ะฒะธะดะฝะพ ะฒ ะฑัะฐัะทะตัะต ะฒ chrome://extensions/?id=<id ัะฐััะธัะตะฝะธั>
"name": "Signer",
"description": "Extension demo",
"version": "0.0.1",
"manifest_version": 2,
// ะกะบัะธะฟัั, ะบะพัะพััะต ะฑัะดัั ะธัะฟะพะปะฝัััั ะฒ background, ะธั
ะผะพะถะตั ะฑััั ะฝะตัะบะพะปัะบะพ
"background": {
"scripts": ["background.js"]
},
// ะะฐะบะพะน html ะธัะฟะพะปัะทะพะฒะฐัั ะดะปั popup
"browser_action": {
"default_title": "My Extension",
"default_popup": "popup.html"
},
// ะะพะฝัะตะฝั ัะบัะธะฟัั.
// ะฃ ะฝะฐั ะพะดะธะฝ ะพะฑัะตะบั: ะดะปั ะฒัะตั
url ะฝะฐัะธะฝะฐััะธั
ัั ั http ะธะปะธ https ะผั ะทะฐะฟััะบะฐะตะผ
// contenscript context ัะพ ัะบัะธะฟัะพะผ contentscript.js. ะะฐะฟััะบะฐัั ััะฐะทั ะฟะพ ะฟะพะปััะตะฝะธะธ ะดะพะบัะผะตะฝัะฐ ะดะปั ะฒัะตั
ััะตะนะผะพะฒ
"content_scripts": [
{
"matches": [
"http://*/*",
"https://*/*"
],
"js": [
"contentscript.js"
],
"run_at": "document_start",
"all_frames": true
}
],
// ะ ะฐะทัะตัะตะฝ ะดะพัััะฟ ะบ localStorage ะธ idle api
"permissions": [
"storage",
// "unlimitedStorage",
//"clipboardWrite",
"idle"
//"activeTab",
//"webRequest",
//"notifications",
//"tabs"
],
// ะะดะตัั ัะบะฐะทัะฒะฐัััั ัะตััััั, ะบ ะบะพัะพััะผ ะฑัะดะตั ะธะผะตัั ะดะพัััะฟ ะฒะตะฑ ัััะฐะฝะธัะฐ. ะขะพะตััั ะธั
ะผะพะถะฝะพ ะฑัะดะตั ะทะฐะฟัะฐัะธะฒะฐัั fetche'ะผ ะธะปะธ ะฟัะพััะพ xhr
"web_accessible_resources": ["inpage.js"]
}
แจแแฅแแแแแ แชแแ แแแแ background.js, popup.js, inpage.js แแ contentscript.js. แฉแแแ แแแแแแแขแแแ popup.html - แแ แฉแแแแ แแแแแแแชแแ แฃแแแ แจแแแซแแแแ แฉแแแขแแแ แแแก Google Chrome-แจแ แแ แแแแ แฌแแฃแแแแ, แ แแ แแก แแฃแจแแแแก.
แแแแก แแแกแแแแกแขแฃแ แแแแแ, แจแแแแซแแแแ แแแฆแแ แแแแ
แแฎแแ แฉแแแแ แแแคแแ แแแแแ แแแแแกแขแแแแ แแแฃแแแ แแ แแฃแจแแแแก. แแฅแแแ แจแแแแซแแแแ แแฌแแ แแแแ แแแแแแแแแ แแก แแแกแขแ แฃแแแแขแแแ แกแฎแแแแแกแฎแแ แแแแขแแฅแกแขแจแ แจแแแแแแแแแ แแ:
แแแแแฎแขแแ แ แคแแแฏแแ แ ->
แแแแขแแแขแแก แกแแ แแแขแแก แแแแกแแแแ แฌแแแแแ แฎแแ แชแแแแแแแ แแแแแ แแแแ แแแก แแแแกแแแแก แแแจแแแแแแ, แ แแแแแแแช แแก แแแจแแแแฃแแแ.
แจแแขแงแแแแแแแ
แแกแ แ แแ, แฉแแแ แฃแแแ แแแแแแงแแ แแ แแ แ แกแแแแแฃแแแแแชแแ แแ แฎแ: inpage <-> background แแ popup <-> background. แแฅแแแ, แ แ แแฅแแ แฃแแแ, แจแแแแซแแแแ แฃแแ แแแแ แแแแแแแแแ แจแแขแงแแแแแแแแแ แแแ แขแจแ แแ แแแแแแแแแแ แแฅแแแแ แกแแแฃแแแ แ แแ แแขแแแแแ, แแแแ แแ แแ แแแ แฉแแแแแ แแแแแแแ, แ แแแแแแช แแแแฎแ แแแขแแแแกแแแก แฆแแ แแแแแก แแ แแแฅแขแจแ.
แแก แแ แแก แแ แแฃแแแ แแก แแแคแแ แแแแแ Ethereum แฅแกแแแแแ แแฃแจแแแแแกแแแแก. แแแกแจแ แแแแแแแชแแแก แกแฎแแแแแกแฎแแ แแแฌแแแ แฃแ แแแแ แแแแก RPC-แแ dnode แแแแแแแแแแแก แแแแแงแแแแแแ. แแก แกแแจแฃแแแแแแก แแแซแแแแ แแแแฌแงแแ แแแชแแแ แกแแแแแแ แกแฌแ แแคแแ แแ แแแฎแแ แฎแแแฃแแแ, แแฃ แแแก แแแแฌแแแแ nodejs แแแแแแก, แ แแแแ แช แขแ แแแกแแแ แขแ (แแแฃแแแกแฎแแแแ แแแแแฅแขแ, แ แแแแแแช แแฎแแ แชแแแแแแก แแแแแ แแแขแแ แคแแแกแก):
import Dnode from "dnode/browser";
// ะ ััะพะผ ะฟัะธะผะตัะต ััะปะพะฒะธะผัั ััะพ ะบะปะธะตะฝั ัะดะฐะปะตะฝะฝะพ ะฒัะทัะฒะฐะตั ััะฝะบัะธะธ ะฝะฐ ัะตัะฒะตัะต, ั
ะพัั ะฝะธัะตะณะพ ะฝะฐะผ ะฝะต ะผะตัะฐะตั ัะดะตะปะฐัั ััะพ ะดะฒัะฝะฐะฟัะฐะฒะปะตะฝะฝัะผ
// Cะตัะฒะตั
// API, ะบะพัะพัะพะต ะผั ั
ะพัะธะผ ะฟัะตะดะพััะฐะฒะธัั
const dnode = Dnode({
hello: (cb) => cb(null, "world")
})
// ะขัะฐะฝัะฟะพัั, ะฟะพะฒะตัั
ะบะพัะพัะพะณะพ ะฑัะดะตั ัะฐะฑะพัะฐัั dnode. ะัะฑะพะน nodejs ัััะธะผ. ะ ะฑัะฐัะทะตัะต ะตััั ะฑะธะฑะธะปะธะพัะตะบะฐ 'readable-stream'
connectionStream.pipe(dnode).pipe(connectionStream)
// ะะปะธะตะฝั
const dnodeClient = Dnode() // ะัะทะพะฒ ะฑะตะท ะฐะณััะผะตะฝัะฐ ะทะฝะฐัะธั ััะพ ะผั ะฝะต ะฟัะตะดะพััะฐะฒะปัะตะผ API ะฝะฐ ะดััะณะพะน ััะพัะพะฝะต
// ะัะฒะตะดะตั ะฒ ะบะพะฝัะพะปั world
dnodeClient.once('remote', remote => {
remote.hello(((err, value) => console.log(value)))
})
แแฎแแ แฉแแแ แจแแแฅแแแแ แแแแแแแชแแแก แแแแกแก. แแก แจแแฅแแแแก API แแแแแฅแขแแแก แแแแแฎแขแแ แ แแ แแแ แแแแ แแแกแแแแก แแ แจแแฅแแแแก dnode-แก แแแแแแแก:
import Dnode from 'dnode/browser';
export class SignerApp {
// ะะพะทะฒัะฐัะฐะตั ะพะฑัะตะบั API ะดะปั ui
popupApi(){
return {
hello: cb => cb(null, 'world')
}
}
// ะะพะทะฒัะฐัะฐะตั ะพะฑัะตั API ะดะปั ัััะฐะฝะธัั
pageApi(){
return {
hello: cb => cb(null, 'world')
}
}
// ะะพะดะบะปััะฐะตั popup ui
connectPopup(connectionStream){
const api = this.popupApi();
const dnode = Dnode(api);
connectionStream.pipe(dnode).pipe(connectionStream);
dnode.on('remote', (remote) => {
console.log(remote)
})
}
// ะะพะดะบะปััะฐะตั ัััะฐะฝะธัั
connectPage(connectionStream, origin){
const api = this.popupApi();
const dnode = Dnode(api);
connectionStream.pipe(dnode).pipe(connectionStream);
dnode.on('remote', (remote) => {
console.log(origin);
console.log(remote)
})
}
}
แแฅ แแ แฅแแแแแ, แแแแแแแฃแ แ Chrome แแแแแฅแขแแก แแแชแแแแ, แแแงแแแแแ extensionApi-แก, แ แแแแแแช แฌแแแแแ Chrome-แก Google-แแก แแ แแฃแแแ แจแ แแ แแ แแฃแแแ แจแ แกแฎแแแแจแ. แแก แแแแแแแ แแ แแฃแแแ แแก แฏแแแ แแแแแ แแแแกแแแแแแแแกแแแแก, แแแแ แแ แแ แกแขแแขแแแก แแแแแแแแกแแแแก แจแแแซแแแแ แฃแแ แแแแ แแแแแแงแแแแ 'chrome.runtime.connect'.
แแแแแ แจแแแฅแแแแ แแแแแแแชแแแก แแแแแแแแ แคแแแแก แกแแ แแแขแจแ:
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {SignerApp} from "./SignerApp";
const app = new SignerApp();
// onConnect ััะฐะฑะฐััะฒะฐะตั ะฟัะธ ะฟะพะดะบะปััะตะฝะธะธ 'ะฟัะพัะตััะพะฒ' (contentscript, popup, ะธะปะธ ัััะฐะฝะธัะฐ ัะฐััะธัะตะฝะธั)
extensionApi.runtime.onConnect.addListener(connectRemote);
function connectRemote(remotePort) {
const processName = remotePort.name;
const portStream = new PortStream(remotePort);
// ะัะธ ัััะฐะฝะพะฒะบะต ัะพะตะดะธะฝะตะฝะธั ะผะพะถะฝะพ ัะบะฐะทัะฒะฐัั ะธะผั, ะฟะพ ััะพะผั ะธะผะตะฝะธ ะผั ะธ ะพะฟะฟัะตะดะตะปัะตะผ ะบัะพ ะบ ะฝะฐะผ ะฟะพะดะปััะธะปัั, ะบะพะฝัะตะฝััะบัะธะฟั ะธะปะธ ui
if (processName === 'contentscript'){
const origin = remotePort.sender.url
app.connectPage(portStream, origin)
}else{
app.connectPopup(portStream)
}
}
แแแแแแแแ dnode แแฃแจแแแแก แแแแแแแแแแ แแ แฉแแแ แแแฆแแแ แแแ แขแก, แกแแญแแ แแ แแแแแขแแ แแก แแแแกแ. แแแ แแแแแแแแ แฌแแแแแฎแฃแแ แแแแแแแก แแแแแแแแแแแก แแแแแงแแแแแแ, แ แแแแแแช แแฎแแ แชแแแแแแก nodejs แแแแแแแแก แแ แแฃแแแ แจแ:
import {Duplex} from 'readable-stream';
export class PortStream extends Duplex{
constructor(port){
super({objectMode: true});
this._port = port;
port.onMessage.addListener(this._onMessage.bind(this));
port.onDisconnect.addListener(this._onDisconnect.bind(this))
}
_onMessage(msg) {
if (Buffer.isBuffer(msg)) {
delete msg._isBuffer;
const data = new Buffer(msg);
this.push(data)
} else {
this.push(msg)
}
}
_onDisconnect() {
this.destroy()
}
_read(){}
_write(msg, encoding, cb) {
try {
if (Buffer.isBuffer(msg)) {
const data = msg.toJSON();
data._isBuffer = true;
this._port.postMessage(data)
} else {
this._port.postMessage(msg)
}
} catch (err) {
return cb(new Error('PortStream - disconnected'))
}
cb()
}
}
แแฎแแ แแแแแ แจแแแฅแแแแ แแแแจแแ แ UI-แจแ:
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import Dnode from 'dnode/browser';
const DEV_MODE = process.env.NODE_ENV !== 'production';
setupUi().catch(console.error);
async function setupUi(){
// ะขะฐะบะถะต, ะบะฐะบ ะธ ะฒ ะบะปะฐััะต ะฟัะธะปะพะถะตะฝะธั ัะพะทะดะฐะตะผ ะฟะพัั, ะพะฑะพัะฐัะธะฒะฐะตะผ ะฒ stream, ะดะตะปะฐะตะผ dnode
const backgroundPort = extensionApi.runtime.connect({name: 'popup'});
const connectionStream = new PortStream(backgroundPort);
const dnode = Dnode();
connectionStream.pipe(dnode).pipe(connectionStream);
const background = await new Promise(resolve => {
dnode.once('remote', api => {
resolve(api)
})
});
// ะะตะปะฐะตะผ ะพะฑัะตะบั API ะดะพัััะฟะฝัะผ ะธะท ะบะพะฝัะพะปะธ
if (DEV_MODE){
global.background = background;
}
}
แจแแแแแ แฉแแแ แแฅแแแแ แแแแจแแ แก แจแแแแแ แกแแก แกแแ แแแขแจแ:
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import PostMessageStream from 'post-message-stream';
setupConnection();
injectScript();
function setupConnection(){
const backgroundPort = extensionApi.runtime.connect({name: 'contentscript'});
const backgroundStream = new PortStream(backgroundPort);
const pageStream = new PostMessageStream({
name: 'content',
target: 'page',
});
pageStream.pipe(backgroundStream).pipe(pageStream);
}
function injectScript(){
try {
// inject in-page script
let script = document.createElement('script');
script.src = extensionApi.extension.getURL('inpage.js');
const container = document.head || document.documentElement;
container.insertBefore(script, container.children[0]);
script.onload = () => script.remove();
} catch (e) {
console.error('Injection failed.', e);
}
}
แแแแแแแแ แฉแแแ แแแญแแ แแแแ API แแ แ แแแแขแแแขแแก แกแแ แแแขแจแ, แแ แแแแ แแแ แแแแแ แแแแ แแแ, แฉแแแ แแแแแแแแ แแ แ แแแแก:
- แฉแแแ แแฅแแแแ แแ แแแแแแก. แแ แแ - แแแแ แแแกแแแ, แแแกแขแแแกแแฏแแก แแแแแ. แแแแกแแแแก แแแงแแแแแ แแแแก
แแก แแแแแขแ แแแขแแแแกแแแก แจแแแฅแแแแแแแแกแแแ. แแแแ แ แแแแแแ แแ แแก แคแแแแ แแแฆแแแฃแแ แแแ แขแruntime.connect
. แแแแแ แแแงแแแแ แแกแแแ. แแฎแแ แแแแ แแก แแฅแแแแ แแแแแแ แคแแแแ. - แจแแแขแแแแ แกแแ แแแขแ DOM-แจแ. แฉแแแแขแแแ แแแ แกแแ แแแขแ (แแแกแแ แฌแแแแแ แแแแแแแ แแฃแแ แแงแ manifest-แจแ) แแ แจแแฅแแแแแ แขแแแ
script
แจแแแแแ แแแกแ แจแแแแแ แกแแ:
import PostMessageStream from 'post-message-stream';
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
setupConnection();
injectScript();
function setupConnection(){
// ะกััะธะผ ะบ ะฑะตะบะณัะฐัะฝะดั
const backgroundPort = extensionApi.runtime.connect({name: 'contentscript'});
const backgroundStream = new PortStream(backgroundPort);
// ะกััะธะผ ะบ ัััะฐะฝะธัะต
const pageStream = new PostMessageStream({
name: 'content',
target: 'page',
});
pageStream.pipe(backgroundStream).pipe(pageStream);
}
function injectScript(){
try {
// inject in-page script
let script = document.createElement('script');
script.src = extensionApi.extension.getURL('inpage.js');
const container = document.head || document.documentElement;
container.insertBefore(script, container.children[0]);
script.onload = () => script.remove();
} catch (e) {
console.error('Injection failed.', e);
}
}
แแฎแแ แฉแแแ แแฅแแแแ api แแแแแฅแขแก inpage-แจแ แแ แแแงแแแแแ แแแก แแแแแแแฃแ แแ:
import PostMessageStream from 'post-message-stream';
import Dnode from 'dnode/browser';
setupInpageApi().catch(console.error);
async function setupInpageApi() {
// ะกััะธะผ ะบ ะบะพะฝัะตะฝััะบัะธะฟัั
const connectionStream = new PostMessageStream({
name: 'page',
target: 'content',
});
const dnode = Dnode();
connectionStream.pipe(dnode).pipe(connectionStream);
// ะะพะปััะฐะตะผ ะพะฑัะตะบั API
const pageApi = await new Promise(resolve => {
dnode.once('remote', api => {
resolve(api)
})
});
// ะะพัััะฟ ัะตัะตะท window
global.SignerApp = pageApi;
}
แฒฉแแแ แแแแ แแแ แ
แชแแ แแแแ API แแ แฌแแ แแแจแแแ. แแแแ แแแก แแฎแแ แแก, แฉแแแ แจแแแแแซแแแ แแฃแฌแแแแ hello แคแฃแแฅแชแแ แแกแ:
แแแแแแแแ แแแ JS-แจแ แแแแแซแแฎแแแแก แคแฃแแฅแชแแแแแแ แแฃแจแแแแ แชแฃแแ แแแแแ แแ, แแแแขแแ แแแแแ แแแแฌแแ แแ แแแขแแ แ แแแแฎแแแ แ dnode-แแก แจแแกแแฅแแแแแแ, แ แแแแแแช แกแแจแฃแแแแแแก แแแแชแแแ แแแแแกแชแแ API แแแแแฅแขแ utils-แก.
API แแแแแฅแขแแแ แแฎแแ แแกแ แแแแแแงแฃแ แแแ:
export class SignerApp {
popupApi() {
return {
hello: async () => "world"
}
}
...
}
แแแกแขแแแชแแฃแ แแแแ แแกแแแ แแแแแฅแขแแก แแแฆแแแ:
import {cbToPromise, transformMethods} from "../../src/utils/setupDnode";
const pageApi = await new Promise(resolve => {
dnode.once('remote', remoteApi => {
// ะก ะฟะพะผะพััั ััะธะปะธั ะผะตะฝัะตะผ ะฒัะต callback ะฝะฐ promise
resolve(transformMethods(cbToPromise, remoteApi))
})
});
แแ แคแฃแแฅแชแแแแแก แแแแแซแแฎแแแ แแแ แฃแแแแก แแแแแ แแแแก:
แฎแแแแแกแแฌแแแแแแ แแแ แกแแ แแกแแแฅแ แแแฃแแ แคแฃแแฅแชแแแแแ
แกแแแ แแ แฏแแแจแ, RPC แแ แแแแแแแก แแแแแแแ แกแแแแแแ แแแฅแแแแ แฉแแแก: แฉแแแ แจแแแแแซแแแ แแแแแแแงแแแแ แแ แแฅแแแก แแฃแแขแแแแแฅแกแแ แแแ แแ แจแแแฅแแแแ แ แแแแแแแแ แแแแกแฎแแแแแแฃแแ API แกแฎแแแแแกแฎแแ แแแแชแแแแแแกแแแแก. แแ แแแชแแแจแ, dnode แจแแแซแแแแ แแแแแงแแแแแฃแ แแฅแแแก แแแแแกแแแแ แแแแแแแก, แแแแแแ แแ แขแ แแแกแแแ แขแแก แแแแแขแแแ nodejs แแแแแแแก แกแแฎแแ.
แแแขแแ แแแขแแแ แแ แแก JSON แคแแ แแแขแ, แ แแแแแแช แแฎแแ แชแแแแแแก JSON RPC 2 แแ แแขแแแแแก. แแฃแแชแ, แแก แแฃแจแแแแก แกแแแชแแคแแแฃแ แขแ แแแกแแแ แขแแแแแ (TCP แแ HTTP(S)), แ แแช แฉแแแแก แจแแแแฎแแแแแจแ แแ แแแแแแงแแแแแ.
แจแแแ แแแแแแแ แแแแ แแ แแแแแแแแ แแแ แจแแแแฎแแ
แฉแแแ แแแแแญแแ แแแแ แแแแแแแชแแแก แจแแแ แแแแแแแ แแแแแก แจแแแแฎแแ - แแแแแแฃแ แฎแแแแแฌแแ แแก แแแกแแฆแแแแแ. แฉแแแ แกแแแแแแ แแแ แขแแแแ แจแแแแแซแแแ แแแแแแแขแแ แแแแแแแ แแแแ แแแแแแแชแแแก แแ แแแกแ แจแแชแแแแก แแแแแแแแ แแแแแฎแขแแ API-แจแ:
import {setupDnode} from "./utils/setupDnode";
export class SignerApp {
constructor(){
this.store = {
keys: [],
};
}
addKey(key){
this.store.keys.push(key)
}
removeKey(index){
this.store.keys.splice(index,1)
}
popupApi(){
return {
addKey: async (key) => this.addKey(key),
removeKey: async (index) => this.removeKey(index)
}
}
...
}
แคแแแแ, แฉแแแ แงแแแแแคแแ แก แแแแแฎแแแแ แคแฃแแฅแชแแแก แแ แฉแแแฌแแ แ แแแแแแแชแแแก แแแแแฅแขแก แคแแแฏแแ แแจแ, แ แแแ แจแแแซแแแ แแแกแแแ แแฃแจแแแแ แแแแกแแแแแแ:
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {SignerApp} from "./SignerApp";
const DEV_MODE = process.env.NODE_ENV !== 'production';
setupApp();
function setupApp() {
const app = new SignerApp();
if (DEV_MODE) {
global.app = app;
}
extensionApi.runtime.onConnect.addListener(connectRemote);
function connectRemote(remotePort) {
const processName = remotePort.name;
const portStream = new PortStream(remotePort);
if (processName === 'contentscript') {
const origin = remotePort.sender.url;
app.connectPage(portStream, origin)
} else {
app.connectPopup(portStream)
}
}
}
แแแแแ แแแแแแแขแแ แ แแแแแแแแ แแแกแแฆแแแ UI แแแแกแแแแแแ แแ แแแแฎแแ, แ แ แแแฎแแ แกแแฎแแแแฌแแคแแกแแแ:
แแแแแแแ แแแแ แฃแแแ แแงแแก แแแแ แแแ, แ แแแ แแแแแแจแแแ แแ แแแแแแ แแแก แแแแแขแแแ แแแแกแแก.
แฉแแแ แแแแแฎแแแ แแแก localStorage-แจแ, แแแแแแแฌแแ แ แแแก แงแแแแแ แชแแแแแแแแกแแก. แจแแแแแแแจแ แแแกแแ แฌแแแแแ แแกแแแ แกแแญแแ แ แแฅแแแแ UI-แกแแแแก แแ แแกแแแ แแแแแ แแแแแแฌแแ แ แชแแแแแแแแแ. แแแแก แกแแคแฃแซแแแแแ แแแกแแฎแแ แฎแแแแแ แแฅแแแแ แแแแแแ แแแแแแ แกแแชแแแแก แจแแฅแแแ แแ แแแกแ แชแแแแแแแแแแก แแแแแฌแแ แ.
แฉแแแ แแแแแแแงแแแแแ mobx แแแแแแแแแแแก (
แแแแแ แแแแแแแขแแ แกแแฌแงแแกแ แแแแแแแ แแแแแก แแแแชแแแแแแแชแแ แแ แแแฆแแแแ แแแแแแ แแแแแแ แแแแฎแแแแ:
import {observable, action} from 'mobx';
import {setupDnode} from "./utils/setupDnode";
export class SignerApp {
constructor(initState = {}) {
// ะะฝะตัะฝะต store ัะฐะบ ะธ ะพััะฐะฝะตััั ัะตะผ ะถะต ะพะฑัะตะบัะพะผ, ัะพะปัะบะพ ัะตะฟะตัั ะฒัะต ะตะณะพ ะฟะพะปั ััะฐะปะธ proxy, ะบะพัะพััะต ะพััะปะตะถะธะฒะฐัั ะดะพัััะฟ ะบ ะฝะธะผ
this.store = observable.object({
keys: initState.keys || [],
});
}
// ะะตัะพะดั, ะบะพัะพััะต ะผะตะฝััั observable ะฟัะธะฝััะพ ะพะฑะพัะฐัะธะฒะฐัั ะดะตะบะพัะฐัะพัะพะผ
@action
addKey(key) {
this.store.keys.push(key)
}
@action
removeKey(index) {
this.store.keys.splice(index, 1)
}
...
}
โแฅแฃแแแก แฅแแแจโ, mobx-แแ แจแแชแแแแ แแแฆแแแแแก แงแแแแ แแแแ แแ แแฅแกแแ แแ แฌแงแแแขแก แแแแแแ แงแแแแ แแแ แก. แแ แจแแขแงแแแแแแแแแแก แแแแแฌแแ แ แจแแกแแซแแแแแแ แแฅแแแแ.
แฅแแแแแ แฎแจแแ แแ แแแแแแแงแแแแ แขแแ แแแแก โแชแแแแกแแกโ, แแฃแแชแ แแก แแแแแ แกแฌแแ แ แแ แแ แแก. Mobx แแแแแขแ แแแแแก แแแแแแแ แฌแแแแแแก. แแแแแแงแแแแแ แแแแแแแแแแแก แแแแ แจแแฅแแแแแ แแ แแฅแกแ แแแแแฅแขแแแแก แจแแแแ แแแแแแแแ แแ แกแแขแแ แแแ.
แกแแแแฅแแแแ แแแแแ แแขแแ แแแ แแแกแแฎแฃแ แแแ แแ แแแแแแก:
- แแแแชแ แ แแแแแจแ enforceActions แแ แแจแแ, mobx แแ แซแแแแแก แแแแแแแ แแแแแก แแแ แแแแแ แจแแชแแแแก. แแแแชแ แแแ แแแแแจแ แแฃแจแแแแ แแแ แ แแ แแฅแขแแแแ แแแแแแแ.
- แแแจแแแแช แแ, แแฃ แคแฃแแฅแชแแ แ แแแแแแฏแแ แแ แชแแแแก แแแแแแแ แแแแแก - แแแแแแแแแ, แฉแแแ แแชแแแแ แ แแแแแแแแ แแแแก แแแแแก แ แแแแแแแแ แกแขแ แแฅแแแจแ - แแแแแแแ แแแแแแแก แแชแแแแแแแ แแฎแแแแ แแแกแ แแแกแ แฃแแแแแก แจแแแแแ. แแก แแแแกแแแฃแแ แแแแ แแแแจแแแแแแแแแแ แคแ แแแขแแแขแแกแแแแก, แกแแแแช แแแแแแแ แแแแแก แแ แแกแแญแแ แ แแแแแฎแแแแแแ แแฌแแแแก แแแแแแแขแแแแก แแ แแกแแญแแ แ แ แแแแแ แแ แแแแก. แฉแแแแก แจแแแแฎแแแแแจแ แแ แช แแแ แแแแ แแ แแ แช แแแแ แ แแ แแ แแก แแแแกแแแฃแแ แแแแ แแฅแขแฃแแแฃแ แ, แแแแ แแ แฉแแแ แแแแงแแแแแ แกแแฃแแแแแกแ แแ แแฅแขแแแแก. แฉแแแฃแแแแ แแแแ แแแแแ แแขแแ แแแแก แแแแแแ แแแ แงแแแแ แคแฃแแฅแชแแแแ, แ แแแแแแช แชแแแแก แแแแแแ แแแแฃแแ แแแแแแแก แแแแแแแ แแแแแก.
แคแแแแ แแแแแแแขแแแ แแแแชแแแแแแแชแแแก แแ แแแแแแแ แแแแแก แจแแแแฎแแแก localStorage-แจแ:
import {reaction, toJS} from 'mobx';
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {SignerApp} from "./SignerApp";
// ะัะฟะพะผะพะณะฐัะตะปัะฝัะต ะผะตัะพะดั. ะะฐะฟะธััะฒะฐัั/ัะธัะฐัั ะพะฑัะตะบั ะฒ/ะธะท localStorage ะฒะธะดะต JSON ัััะพะบะธ ะฟะพ ะบะปััั 'store'
import {loadState, saveState} from "./utils/localStorage";
const DEV_MODE = process.env.NODE_ENV !== 'production';
setupApp();
function setupApp() {
const initState = loadState();
const app = new SignerApp(initState);
if (DEV_MODE) {
global.app = app;
}
// Setup state persistence
// ะ ะตะทัะปััะฐั reaction ะฟัะธัะฒะฐะธะฒะฐะตััั ะฟะตัะตะผะตะฝะฝะพะน, ััะพะฑั ะฟะพะดะฟะธัะบั ะผะพะถะฝะพ ะฑัะปะพ ะพัะผะตะฝะธัั. ะะฐะผ ััะพ ะฝะต ะฝัะถะฝะพ, ะพััะฐะฒะปะตะฝะพ ะดะปั ะฟัะธะผะตัะฐ
const localStorageReaction = reaction(
() => toJS(app.store), // ะคัะฝะบัะธั-ัะตะปะตะบัะพั ะดะฐะฝะฝัั
saveState // ะคัะฝะบัะธั, ะบะพัะพัะฐั ะฑัะดะตั ะฒัะทะฒะฐะฝะฐ ะฟัะธ ะธะทะผะตะฝะตะฝะธะธ ะดะฐะฝะฝัั
, ะบะพัะพััะต ะฒะพะทะฒัะฐัะฐะตั ัะตะปะตะบัะพั
);
extensionApi.runtime.onConnect.addListener(connectRemote);
function connectRemote(remotePort) {
const processName = remotePort.name;
const portStream = new PortStream(remotePort);
if (processName === 'contentscript') {
const origin = remotePort.sender.url
app.connectPage(portStream, origin)
} else {
app.connectPopup(portStream)
}
}
}
แแฅ แกแแแแขแแ แแกแแ แ แแแฅแชแแแก แคแฃแแฅแชแแ. แแแก แแฅแแก แแ แ แแ แแฃแแแแขแ:
- แแแแแชแแแแ แแแแแ แฉแแแ.
- แแแแแฃแจแแแแแแแ, แ แแแแแแช แแแแแแซแแฎแแแ แแ แแแแแชแแแแแแ แงแแแแแ แชแแแแแแแแกแแก.
แแแแกแฎแแแแแแแ redux-แแกแแแ, แกแแแแช แฉแแแ แแจแแแ แแ แแแฆแแแ แแแแแแแ แแแแแก แแ แแฃแแแแขแแ, mobx แแฎแกแแแก, แ แแแแ แแแแแแ แแแแแ แฌแแแแแแก แแแซแแแแ แกแแแแฅแขแแ แแก แจแแแแแ แแ แแฎแแแแ แแแจแแ แแซแแฎแแแก แแแแแฃแจแแแแแแแก, แ แแแแกแแช แแกแแแ แจแแแชแแแแแแแ.
แแแแจแแแแแแแแแแ แแฃแกแขแแ แแแแกแแแแแก, แแฃ แ แแแแ แฌแงแแแขแก mobx, แแฃ แ แแแแ แแแแแแ แแแแแ แแแแแแแฌแแ แแ. แกแแแแฅแขแแ แ แ แแ แแแแฌแแ แ แแแแจแ แแกแ() => app.store
, แแแจแแ แ แแแฅแชแแ แแ แแกแแแแก แแฅแแแแ แแแซแแฎแแแฃแแ, แ แแแแแ แกแแชแแแ แแแแแกแแแแแ แแ แแ แแก แแแแแแ แแแแแแ, แแฎแแแแ แแแกแ แแแแแแแ.
แแฃ แแแแฌแแ แ แแกแ () => app.store.keys
, แแแจแแ แแกแแ แแ แแคแแ แ แแแฎแแแแแแ, แแแแแแแแ แแแกแแแแก แแแแแแแขแแแแก แแแแแขแแแแก/แแแฎแกแแแกแแก แแแกแแ แแแแแแแแ แแ แจแแแชแแแแแ.
Mobx แแแฅแแแแแแก แ แแแแ แช แกแแแแฅแขแแ แ แแแ แแแแแ แแ แแแแแขแ แแแแแก แแฎแแแแ แแแแแแ แแแแแ แแแแแฅแขแแแก, แ แแแแแแแแช แฉแแแ แแแแแฆแแ แฌแแแแแ. แแก แแแแแแแ แแ แแฅแกแ แแแแฆแแแแแแก แแแจแแแแแแ. แแฅแแแแ แแแแแแแแแแ แ, แแฅ แแแแแแงแแแแแ แฉแแจแแแแแฃแแ แคแฃแแฅแชแแ toJS
. แแก แแแ แฃแแแแก แแฎแแ แแแแแฅแขแก แงแแแแ แแ แแฅแกแ แฉแแแแชแแแแแฃแแ แแ แแแแแแแฃแ แ แแแแแแแ. แจแแกแ แฃแแแแแก แแ แแก แแก แแแแฎแฃแแแแก แแแแแฅแขแแก แงแแแแ แแแแก - แจแแกแแแแแแกแแ, แแแแฆแแแแแ แแแแฅแแแแแแแ.
แแแแแฎแขแแ แแแแกแแแจแ แแแแแ แแแแแแแขแแแ แ แแแแแแแแ แแแแแแจแก. แแแฏแแ แแ แแกแแแ แแกแแแ แแฆแแแฉแแแแแ localStorage-แจแ:
แ แแแแกแแช แคแแแแก แแแแ แแ แฎแแแแฎแแ แแขแแแ แแแแ, แแแคแแ แแแชแแ แแแแแแแ แ แฉแแแ.
แแ แแแแแแขแแแแ แแแแแแแชแแแก แงแแแแ แแแแแก แแแฎแแ แจแแกแแซแแแแแแแ
แแแ แแแ แแแกแแฆแแแแแแก แฃแกแแคแ แแฎแ แจแแแแฎแแ
แแแ แแแ แแแกแแฆแแแแแแก แแแแคแแ แขแแฅแกแขแจแ แจแแแแฎแแ แฃแกแแคแ แแฎแแ: แงแแแแแแแแก แแ แแก แจแแแกแ, แ แแ แแแแแขแแฎแแ, แแแแแแแแ แฌแแแแแ แแฅแแแแก แแแแแแฃแขแแ แแ แแ แ.แจ. แแแแขแแ, localStorage-แจแ แฉแแแ แแแแแฎแแแ แแแกแแฆแแแแแก แแแ แแแแ แแแจแแคแ แฃแแ แคแแ แแแ.
แแแขแ แฃแกแแคแ แแฎแแแแแกแแแแก แแแแแแแชแแแก แแแแแแแขแแแ แฉแแแแขแแ แแแแแแแ แแแแแก, แ แแแแแจแแช แกแแแ แแแ แแ แแฅแแแแ แฌแแแแแ แแแกแแฆแแแแแแ. แฉแแแ แแแขแแแแขแฃแ แแ แแแแแแแขแแแ แแแคแแ แแแแแแก แฉแแแแขแแ แแแแแแแ แแแแแจแ แแ แแแก แแแแฌแฃแ แแแก แแแแ.
Mobx แกแแจแฃแแแแแแก แแแซแแแแ แจแแแแแฎแแ แแแแแชแแแแแแก แแฎแแแแ แแแแแแแแฃแ แ แแแแ แแแ, แแแแแ แฉแแแ แแ แแแขแแแแขแฃแ แแ แแแแแแแแแแแ แแแกแแ แแแงแ แแแแแแ. แแก แแ แแก แแแ แแ แฌแแแแแฃแแ แแแแแแแแแแ แแแแกแแแแแ. แแแแ แจแแแแ แแแ แจแแกแแซแแแแแแแ แแแแแชแแแแ แแแแแแแก แฎแแแแแแแ:
import {observable, action} from 'mobx';
import {setupDnode} from "./utils/setupDnode";
// ะฃัะธะปะธัั ะดะปั ะฑะตะทะพะฟะฐัะฝะพะณะพ ัะธััะพะฒะฐะฝะธั ัััะพะบ. ะัะฟะพะปัะทััั crypto-js
import {encrypt, decrypt} from "./utils/cryptoUtils";
export class SignerApp {
constructor(initState = {}) {
this.store = observable.object({
// ะฅัะฐะฝะธะผ ะฟะฐัะพะปั ะธ ะทะฐัะธััะพะฒะฐะฝะฝัะต ะบะปััะธ. ะัะปะธ ะฟะฐัะพะปั null - ะฟัะธะปะพะถะตะฝะธะต locked
password: null,
vault: initState.vault,
// ะะตััะตัั ะดะปั ะฒััะธัะปะธะผัั
ะฟะพะปะตะน. ะะพะถะฝะพ ะฟัะพะฒะตััะธ ะฐะฝะฐะปะพะณะธั ั view ะฒ ะฑะด.
get locked(){
return this.password == null
},
get keys(){
return this.locked ?
undefined :
SignerApp._decryptVault(this.vault, this.password)
},
get initialized(){
return this.vault !== undefined
}
})
}
// ะะฝะธัะธะฐะปะธะทะฐัะธั ะฟัััะพะณะพ ั
ัะฐะฝะธะปะธัะฐ ะฝะพะฒัะผ ะฟะฐัะพะปะตะผ
@action
initVault(password){
this.store.vault = SignerApp._encryptVault([], password)
}
@action
lock() {
this.store.password = null
}
@action
unlock(password) {
this._checkPassword(password);
this.store.password = password
}
@action
addKey(key) {
this._checkLocked();
this.store.vault = SignerApp._encryptVault(this.store.keys.concat(key), this.store.password)
}
@action
removeKey(index) {
this._checkLocked();
this.store.vault = SignerApp._encryptVault([
...this.store.keys.slice(0, index),
...this.store.keys.slice(index + 1)
],
this.store.password
)
}
... // ะบะพะด ะฟะพะดะบะปััะตะฝะธั ะธ api
// private
_checkPassword(password) {
SignerApp._decryptVault(this.store.vault, password);
}
_checkLocked() {
if (this.store.locked){
throw new Error('App is locked')
}
}
// ะะตัะพะดั ะดะปั ัะธััะพะฒะบะธ/ะดะตัะธััะพะฒะบะธ ั
ัะฐะฝะธะปะธัะฐ
static _encryptVault(obj, pass){
const jsonString = JSON.stringify(obj)
return encrypt(jsonString, pass)
}
static _decryptVault(str, pass){
if (str === undefined){
throw new Error('Vault not initialized')
}
try {
const jsonString = decrypt(str, pass)
return JSON.parse(jsonString)
}catch (e) {
throw new Error('Wrong password')
}
}
}
แแฎแแ แฉแแแ แแแแแฎแแแ แแฎแแแแ แแแจแแคแ แฃแ แแแกแแฆแแแแแก แแ แแแ แแแก. แแแแแ แฉแแแ แงแแแแแคแแ แ แแแแแแแแแ. แฉแแแ แแแแแแแแ แแแแแชแแแแก แฉแแแแขแแ แแแแแแแ แแแแแจแ แแแ แแแแก แแแแฆแแแแ. แกแแฏแแ แ API-แก แแฎแแ แแฅแแก แแแฎแกแแแ แแแแก แแแแชแแแแแแแชแแแก แแแแแแ.
แแแฌแแ แแแแ แแแจแแคแแ แแกแแแแก
import CryptoJS from 'crypto-js'
// ะัะฟะพะปัะทัะตััั ะดะปั ะพัะปะพะถะฝะตะฝะธั ะฟะพะดะฑะพัะฐ ะฟะฐัะพะปั ะฟะตัะตะฑะพัะพะผ. ะะฐ ะบะฐะถะดัะน ะฒะฐัะธะฐะฝั ะฟะฐัะพะปั ะทะปะพัะผััะปะตะฝะฝะธะบั ะฟัะธะดะตััั ัะดะตะปะฐัั 5000 ั
ะตัะตะน
function strengthenPassword(pass, rounds = 5000) {
while (rounds-- > 0){
pass = CryptoJS.SHA256(pass).toString()
}
return pass
}
export function encrypt(str, pass){
const strongPass = strengthenPassword(pass);
return CryptoJS.AES.encrypt(str, strongPass).toString()
}
export function decrypt(str, pass){
const strongPass = strengthenPassword(pass)
const decrypted = CryptoJS.AES.decrypt(str, strongPass);
return decrypted.toString(CryptoJS.enc.Utf8)
}
แแ แแฃแแแ แก แแฅแแก แฃแแแฅแแแแ API, แ แแแแแก แแแจแแแแแแแแช แจแแแแซแแแแ แแแแแแฌแแ แแ แฆแแแแกแซแแแแ - แชแแแแแแแแแ แแแแแแแ แแแแก. แกแแฎแแแแฌแแคแ, แจแแกแแแแแแกแแ, แจแแแซแแแแ แแงแแก idle
, active
ะธ locked
. แฃแแแฅแแแแแแแกแแแแก แจแแแแซแแแแ แแแแงแแแแ แแแแแก แแแแฌแฃแ แแ, แฎแแแ แฉแแแแขแแแ แแแงแแแแแฃแแแ, แ แแแแกแแช แแแแแ OS แแแแแแแแแแ. แฉแแแ แแกแแแ แจแแแชแแแแ แแแแแ แฉแแแแแก localStorage-แจแ แจแแกแแแแฎแแ:
import {reaction, toJS} from 'mobx';
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {SignerApp} from "./SignerApp";
import {loadState, saveState} from "./utils/localStorage";
const DEV_MODE = process.env.NODE_ENV !== 'production';
const IDLE_INTERVAL = 30;
setupApp();
function setupApp() {
const initState = loadState();
const app = new SignerApp(initState);
if (DEV_MODE) {
global.app = app;
}
// ะขะตะฟะตัั ะผั ัะฒะฝะพ ัะทัะฒะฐะตะผ ะฟะพะปะต, ะบะพัะพัะพะผั ะฑัะดะตั ะฟัะพะธัั
ะพะดะธัั ะดะพัััะฟ, reaction ะพััะฐะฑะพัะฐะตั ะฝะพัะผะฐะปัะฝะพ
reaction(
() => ({
vault: app.store.vault
}),
saveState
);
// ะขะฐะนะผะฐัั ะฑะตะทะดะตะนััะฒะธั, ะบะพะณะดะฐ ััะฐะฑะพัะฐะตั ัะพะฑััะธะต
extensionApi.idle.setDetectionInterval(IDLE_INTERVAL);
// ะัะปะธ ะฟะพะปัะทะพะฒะฐัะตะปั ะทะฐะปะพัะธะป ัะบัะฐะฝ ะธะปะธ ะฑะตะทะดะตะนััะฒะพะฒะฐะป ะฒ ัะตัะตะฝะธะต ัะบะฐะทะฐะฝะฝะพะณะพ ะธะฝัะตัะฒะฐะปะฐ ะปะพัะธะผ ะฟัะธะปะพะถะตะฝะธะต
extensionApi.idle.onStateChanged.addListener(state => {
if (['locked', 'idle'].indexOf(state) > -1) {
app.lock()
}
});
// Connect to other contexts
extensionApi.runtime.onConnect.addListener(connectRemote);
function connectRemote(remotePort) {
const processName = remotePort.name;
const portStream = new PortStream(remotePort);
if (processName === 'contentscript') {
const origin = remotePort.sender.url
app.connectPage(portStream, origin)
} else {
app.connectPopup(portStream)
}
}
}
แแแแ แแ แแแแแฏแแก แฌแแ แแ แแก
แแแ แแแแแแแ
แแกแ แ แแ, แฉแแแ แแแแแแแแ แ แงแแแแแแ แแแแจแแแแแแแแ แกแแแแแฎแแแแ: แขแ แแแแแฅแชแแแแแก แจแแฅแแแแกแ แแ แฎแแแแแฌแแ แแแ แแแแแฉแแแแแ. แฉแแแ แแแแแแแงแแแแแ WAVES แแแแแฉแแแแก แแ แแแแแแแแแแแก
แฏแแ , แแแแแ, แกแแฎแแแแฌแแคแแก แแแแแแแขแแ แจแแขแงแแแแแแแแแแก แแแกแแแ, แ แแแแแแแช แฎแแแแแฌแแ แแแแ, แจแแแแแ แแแแแแแขแแ แแฎแแแ แจแแขแงแแแแแแแแก แแแแแขแแแแก, แฎแแแแแฌแแ แแก แแแแแกแขแฃแ แแแแกแ แแ แฃแแ แแก แแฅแแแก แแแแแแแแ:
import {action, observable, reaction} from 'mobx';
import uuid from 'uuid/v4';
import {signTx} from '@waves/waves-transactions'
import {setupDnode} from "./utils/setupDnode";
import {decrypt, encrypt} from "./utils/cryptoUtils";
export class SignerApp {
...
@action
newMessage(data, origin) {
// ะะปั ะบะฐะถะดะพะณะพ ัะพะพะฑัะตะฝะธั ัะพะทะดะฐะตะผ ะผะตัะฐะดะฐะฝะฝัะต ั id, ััะฐัััะพะผ, ะฒััะตะผะตะฝะตะผ ัะพะทะดะฐะฝะธั ะธ ัะด.
const message = observable.object({
id: uuid(), // ะะดะตะฝัะธัะธะบะฐัะพั, ะธัะฟะพะปัะทััั uuid
origin, // Origin ะฑัะดะตะผ ะฒะฟะพัะปะตะดััะฒะธะธ ะฟะพะบะฐะทัะฒะฐัั ะฒ ะธะฝัะตััะตะนัะต
data, //
status: 'new', // ะกัะฐัััะพะฒ ะฑัะดะตั ัะตัััะต: new, signed, rejected ะธ failed
timestamp: Date.now()
});
console.log(`new message: ${JSON.stringify(message, null, 2)}`);
this.store.messages.push(message);
// ะะพะทะฒัะฐัะฐะตะผ ะฟัะพะผะธั ะฒะฝัััะธ ะบะพัะพัะพะณะพ mobx ะผะพะฝะธัะพัะธั ะธะทะผะตะฝะตะฝะธั ัะพะพะฑัะตะฝะธั. ะะฐะบ ัะพะปัะบะพ ััะฐััั ะฟะพะผะตะฝัะตััั ะผั ะทะฐัะตะทะพะปะฒะธะผ ะตะณะพ
return new Promise((resolve, reject) => {
reaction(
() => message.status, //ะัะดะตะผ ะพะฑัะตัะฒะธัั ััะฐััั ัะพะพะฑัะตะฝั
(status, reaction) => { // ะฒัะพัะพะน ะฐัะณัะผะตะฝั ััะพ ัััะปะบะฐ ะฝะฐ ัะฐะผ reaction, ััะพะฑั ะตะณะพ ะผะพะถะฝะพ ะฑัะปะพ ัะฝะธััะพะถัั ะฒะฝัััะธ ะฒัะทะพะฒะฐ
switch (status) {
case 'signed':
resolve(message.data);
break;
case 'rejected':
reject(new Error('User rejected message'));
break;
case 'failed':
reject(new Error(message.err.message));
break;
default:
return
}
reaction.dispose()
}
)
})
}
@action
approve(id, keyIndex = 0) {
const message = this.store.messages.find(msg => msg.id === id);
if (message == null) throw new Error(`No msg with id:${id}`);
try {
message.data = signTx(message.data, this.store.keys[keyIndex]);
message.status = 'signed'
} catch (e) {
message.err = {
stack: e.stack,
message: e.message
};
message.status = 'failed'
throw e
}
}
@action
reject(id) {
const message = this.store.messages.find(msg => msg.id === id);
if (message == null) throw new Error(`No msg with id:${id}`);
message.status = 'rejected'
}
...
}
แ แแแแกแแช แแแฆแแแ แแฎแแ แจแแขแงแแแแแแแแก, แฉแแแ แแแแแขแแแ แแแก แแแขแแแแแแชแแแแแก, แแแแแแแแ observable
แแ แแแแแแขแแ store.messages
.
แแฃ แแ แ observable
แฎแแแแ, แแแจแแ mobx แแแแก แแแแแ แแแแแแแแแก แแแกแแแจแ แจแแขแงแแแแแแแแแแก แแแแแขแแแแกแแก. แแฃแแชแ, แแก แจแแฅแแแแก แแฎแแ แแแแแฅแขแก, แ แแแแแแแช แแ แแแแฅแแแแ แแแแแแแแ, แแแแ แแ แแแแแญแแ แแแแ แจแแแแแแ แแแแแฏแแกแแแแก.
แจแแแแแแ, แฉแแแ แแแแ แฃแแแแ แแแแแ แแแแก, แ แแแแแแช แฌแงแแแแ แจแแขแงแแแแแแแแก แกแขแแขแฃแกแแก แจแแชแแแแกแแก. แกแขแแขแฃแกแก แแแแแขแ แแแแแก แ แแแฅแชแแ, แ แแแแแแช แกแขแแขแฃแกแแก แจแแชแแแแกแแก โแแแแแแก แแแแกโ.
แแแแแแแก แแแแ approve
ะธ reject
แซแแแแแ แแแ แขแแแแ: แฉแแแ แฃแแ แแแแ แแชแแแแ แจแแขแงแแแแแแแแก แกแขแแขแฃแกแก, แกแแญแแ แแแแแก แจแแแแฎแแแแแจแ, แฎแแแแแฌแแ แแก แจแแแแแ.
แฉแแแ แแแแแแกแแแ Approve-แก แแ แฃแแ แงแแคแแก UI API-แจแ, newMessage-แก แแแแ แแแ API-แจแ:
export class SignerApp {
...
popupApi() {
return {
addKey: async (key) => this.addKey(key),
removeKey: async (index) => this.removeKey(index),
lock: async () => this.lock(),
unlock: async (password) => this.unlock(password),
initVault: async (password) => this.initVault(password),
approve: async (id, keyIndex) => this.approve(id, keyIndex),
reject: async (id) => this.reject(id)
}
}
pageApi(origin) {
return {
signTransaction: async (txParams) => this.newMessage(txParams, origin)
}
}
...
}
แแฎแแ แแชแแแแ แฎแแแ แแแแแฌแแ แแ แขแ แแแแแฅแชแแแก แแแคแแ แแแแแแ:
แแแแแแแ, แงแแแแแคแแ แ แแแแ แแ แแก, แ แฉแแแ แแฎแแแแ
UI
แแแขแแ แคแแแกแก แกแญแแ แแแแ แฌแแแแแ แแแแแชแฎแแแแก แแแแแแแ แแแแแแ. UI แแฎแแ แแก แฉแแแ แแแแแแแแแแ observable
แแแแแแแ แแแแ แแ แแแแแแขแแ แคแฃแแฅแชแแ API-แจแ, แ แแแแแแช แจแแชแแแแก แแ แแแแแแแ แแแแแก. แแแแแแแขแแ observable
แคแแแแแแ แแแฆแแแฃแ API แแแแแฅแขแก:
import {observable} from 'mobx'
import {extensionApi} from "./utils/extensionApi";
import {PortStream} from "./utils/PortStream";
import {cbToPromise, setupDnode, transformMethods} from "./utils/setupDnode";
import {initApp} from "./ui/index";
const DEV_MODE = process.env.NODE_ENV !== 'production';
setupUi().catch(console.error);
async function setupUi() {
// ะะพะดะบะปััะฐะตะผัั ะบ ะฟะพััั, ัะพะทะดะฐะตะผ ะธะท ะฝะตะณะพ ัััะธะผ
const backgroundPort = extensionApi.runtime.connect({name: 'popup'});
const connectionStream = new PortStream(backgroundPort);
// ะกะพะทะดะฐะตะผ ะฟัััะพะน observable ะดะปั ัะพััะพัะฝะธั background'a
let backgroundState = observable.object({});
const api = {
//ะัะดะฐะตะผ ะฑะตะบะณัะฐัะฝะดั ััะฝะบัะธั, ะบะพัะพัะฐั ะฑัะดะตั ะพะฑะฝะพะฒะปััั observable
updateState: async state => {
Object.assign(backgroundState, state)
}
};
// ะะตะปะฐะตะผ RPC ะพะฑัะตะบั
const dnode = setupDnode(connectionStream, api);
const background = await new Promise(resolve => {
dnode.once('remote', remoteApi => {
resolve(transformMethods(cbToPromise, remoteApi))
})
});
// ะะพะฑะฐะฒะปัะตะผ ะฒ background observable ัะพ ััะตะนัะพะผ
background.state = backgroundState;
if (DEV_MODE) {
global.background = background;
}
// ะะฐะฟััะบ ะธะฝัะตััะตะนัะฐ
await initApp(background)
}
แแแกแแกแ แฃแแก แแแฌแงแแแ แแแแแแแชแแแก แแแขแแ แคแแแกแแก แ แแแแแ แแ แแแแก. แแก แแ แแก แ แแแฅแขแแฃแแ แแแแแแแชแแ. แคแแแแก แแแแแฅแขแ แฃแแ แแแแ แแแแแแชแแแ แ แแแแแแแขแแแแก แแแแแงแแแแแแ. แกแฌแแ แ แแฅแแแแแแ, แ แ แแฅแแ แฃแแแ, แแแแแแแแแกแแแแก แชแแแแ แกแแ แแแกแแก แแแแแแแแ แแ แกแแฎแแแแฌแแคแแกแแแแก แแแฆแแแแ, แแแแ แแ แแ แกแขแแขแแแก แแแแแแแแกแแแแก แแก แกแแแแแ แแกแแ:
import {render} from 'react-dom'
import App from './App'
import React from "react";
// ะะฝะธัะธะฐะปะธะทะธััะตะผ ะฟัะธะปะพะถะตะฝะธะต ั background ะพะฑัะตะบัะพะผ ะฒ ะบะฐัะตัั ะฒะต props
export async function initApp(background){
render(
<App background={background}/>,
document.getElementById('app-content')
);
}
mobx-แแ แซแแแแแ แแแแแแแ แ แแแแแ แแก แแแฌแงแแแ, แ แแแแกแแช แแแแแชแแแแแ แแชแแแแแ. แฉแแแ แฃแแ แแแแ แแแแแแแ แแแแแแแ แแแแแแก แแแแแ แแขแแ แก แจแแคแฃแแแแแแ
import React, {Component, Fragment} from 'react'
import {observer} from "mobx-react";
import Init from './components/Initialize'
import Keys from './components/Keys'
import Sign from './components/Sign'
import Unlock from './components/Unlock'
@observer // ะฃ ะะพะผะฟะพะฝะตัะฐ ั ััะธะผ ะดะตะบะพัะฐัะพะผ ะฑัะดะตั ะฐะฒัะพะผะฐัะธัะตัะบะธ ะฒัะทะฒะฐะฝ ะผะตัะพะด render, ะตัะปะธ ะฑัะดัั ะธะทะผะตะฝะตะฝั observable ะฝะฐ ะบะพัะพััะต ะพะฝ ัััะปะฐะตััั
export default class App extends Component {
// ะัะฐะฒะธะปัะฝะพ ะบะพะฝะตัะฝะพ ะฒัะฝะตััะธ ะปะพะณะธะบั ัะตะฝะดะตัะฐ ัััะฐะฝะธั ะฒ ัะพััะธะฝะณ ะธ ะฝะต ะธัะฟะพะปัะทะพะฒะฐัั ะฒะปะพะถะตะฝะฝัะต ัะตัะฝะฐัะฝัะต ะพะฟะตัะฐัะพัั,
// ะธ ะฟัะธะฒัะทัะฒะฐัั observable ะธ ะผะตัะพะดั background ะฝะตะฟะพััะตะดััะฒะตะฝะฝะพ ะบ ัะตะผ ะบะพะผะฟะพะฝะตะฝัะฐะผ, ะบะพัะพััะต ะธั
ะธัะฟะพะปัะทััั
render() {
const {keys, messages, initialized, locked} = this.props.background.state;
const {lock, unlock, addKey, removeKey, initVault, deleteVault, approve, reject} = this.props.background;
return <Fragment>
{!initialized
?
<Init onInit={initVault}/>
:
locked
?
<Unlock onUnlock={unlock}/>
:
messages.length > 0
?
<Sign keys={keys} message={messages[messages.length - 1]} onApprove={approve} onReject={reject}/>
:
<Keys keys={keys} onAdd={addKey} onRemove={removeKey}/>
}
<div>
{!locked && <button onClick={() => lock()}>Lock App</button>}
{initialized && <button onClick={() => deleteVault()}>Delete all keys and init</button>}
</div>
</Fragment>
}
}
แแแ แฉแแแแแ แแแแแแแแแขแแแแก แแแฎแแ แจแแกแแซแแแแแแแ แแแแจแ
แแฎแแ แแแแแแแชแแแก แแแแกแจแ แแฅแแแ แฃแแแ แแแแแแแแ UI-แก แแแแแแแ แแแแแก แกแแแแฅแขแแ แ แแ แจแแแขแงแแแแแแ UI-แก, แ แแแแกแแช แแก แจแแแชแแแแแ. แแแแกแแแแแก แแแแแแแขแแ แแแแแแ getState
ะธ reaction
แ แแแแแก remote.updateState
:
import {action, observable, reaction} from 'mobx';
import uuid from 'uuid/v4';
import {signTx} from '@waves/waves-transactions'
import {setupDnode} from "./utils/setupDnode";
import {decrypt, encrypt} from "./utils/cryptoUtils";
export class SignerApp {
...
// public
getState() {
return {
keys: this.store.keys,
messages: this.store.newMessages,
initialized: this.store.initialized,
locked: this.store.locked
}
}
...
//
connectPopup(connectionStream) {
const api = this.popupApi();
const dnode = setupDnode(connectionStream, api);
dnode.once('remote', (remote) => {
// ะกะพะทะดะฐะตะผ reaction ะฝะฐ ะธะทะผะตะฝะตะฝะธั ััะตะนัะฐ, ะบะพัะพััะน ัะดะตะปะฐะตั ะฒัะทะพะฒะตั ัะดะฐะปะตะฝะฝั ะฟัะพัะตะดััั ะธ ะพะฑะฝะพะฒะธั ััะตะนั ะฒ ui ะฟัะพัะตััะต
const updateStateReaction = reaction(
() => this.getState(),
(state) => remote.updateState(state),
// ะขัะตััะธะผ ะฐัะณัะผะตะฝัะพะผ ะผะพะถะฝะพ ะฟะตัะตะดะฐะฒะฐัั ะฟะฐัะฐะผะตััั. fireImmediatly ะทะฝะฐัะธั ััะพ reaction ะฒัะฟะพะปะฝะธัััั ะฟะตัะฒัะน ัะฐะท ััะฐะทั.
// ะญัะพ ะฝะตะพะฑั
ะพะดะธะผะพ, ััะพะฑั ะฟะพะปััะธัั ะฝะฐัะฐะปัะฝะพะต ัะพััะพัะฝะธะต. Delay ะฟะพะทะฒะพะปัะตั ัััะฐะฝะพะฒะธัั debounce
{fireImmediately: true, delay: 500}
);
// ะฃะดะฐะปะธะผ ะฟะพะดะฟะธัะบั ะฟัะธ ะพัะบะปััะตะฝะธะธ ะบะปะธะตะฝัะฐ
dnode.once('end', () => updateStateReaction.dispose())
})
}
...
}
แแแแแฅแขแแก แแแฆแแแแกแแก remote
แจแแแฅแแแ reaction
แจแแชแแแแแก แแแแแแแ แแแแ, แ แแแแแแช แแซแแฎแแแก แคแฃแแฅแชแแแก UI แแฎแแ แแก.
แกแแแแแแ แจแแฎแแแ แแ แแก แแฎแแแ แจแแขแงแแแแแแแแแแก แฉแแแแแแแก แแแแแขแแแ แแแคแแ แแแแแแก แฎแแขแฃแแแแ:
function setupApp() {
...
// Reaction ะฝะฐ ะฒัััะฐะฒะปะตะฝะธะต ัะตะบััะฐ ะฑะตะดะถะฐ.
reaction(
() => app.store.newMessages.length > 0 ? app.store.newMessages.length.toString() : '',
text => extensionApi.browserAction.setBadgeText({text}),
{fireImmediately: true}
);
...
}
แแกแ แ แแ, แแแแแชแฎแแแ แแแแ แแ แแก. แแแแแแแ แแแแแ แจแแแซแแแแ แแแแแฎแแแแ แฎแแแแแฌแแ แ แขแ แแแแแฅแชแแแแแ:
แแแแ แฎแแแแแกแแฌแแแแแแ แแฅ
แแแกแแแแ
แแฃ แแฅแแแ แฌแแแแแแฎแแ แกแขแแขแแ แแแแแแแ, แแแแ แแ แแแแแช แแแฅแแ แจแแแแแฎแแแแ, แจแแแแซแแแแ แแแฃแกแแแ แแกแแแ แแแกแแแแ แแแ
แแ แแฃ แแแแแขแแ แแกแแแ แคแแฅแขแแแ แแแ แแแคแแ แแแแแแก แแแแแก แแแฎแแ, แจแแแแซแแแแ แแแแแแ แแก
แแแแ, แกแแชแแแ แแ แกแแแฃแจแแแก แแฆแฌแแ แ แแแ
แฌแงแแ แ: www.habr.com