ПисањС Π±Π΅Π·Π±Π΅Π΄Π½ΠΎΠ³ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°

ПисањС Π±Π΅Π·Π±Π΅Π΄Π½ΠΎΠ³ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°

Π—Π° Ρ€Π°Π·Π»ΠΈΠΊΡƒ ΠΎΠ΄ ΡƒΠΎΠ±ΠΈΡ‡Π°Ρ˜Π΅Π½Π΅ Π°Ρ€Ρ…ΠΈΡ‚Π΅ΠΊΡ‚ΡƒΡ€Π΅ β€žΠΊΠ»ΠΈΡ˜Π΅Π½Ρ‚-ΡΠ΅Ρ€Π²Π΅Ρ€β€œ, Π΄Π΅Ρ†Π΅Π½Ρ‚Ρ€Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½Π΅ Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π΅ ΠΊΠ°Ρ€Π°ΠΊΡ‚Π΅Ρ€ΠΈΡˆΠ΅:

  • НСма ΠΏΠΎΡ‚Ρ€Π΅Π±Π΅ Π·Π° ΡΠΊΠ»Π°Π΄ΠΈΡˆΡ‚Π΅ΡšΠ΅ΠΌ Π±Π°Π·Π΅ ΠΏΠΎΠ΄Π°Ρ‚Π°ΠΊΠ° са корисничким ΠΏΡ€ΠΈΡ˜Π°Π²Π°ΠΌΠ° ΠΈ Π»ΠΎΠ·ΠΈΠ½ΠΊΠ°ΠΌΠ°. ΠŸΡ€ΠΈΡΡ‚ΡƒΠΏΠ½Π΅ ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡ˜Π΅ Ρ‡ΡƒΠ²Π°Ρ˜Ρƒ искључиво сами корисници, Π° ΠΏΠΎΡ‚Π²Ρ€Π΄Π° ΡšΠΈΡ…ΠΎΠ²Π΅ аутСнтичности сС дСшава Π½Π° Π½ΠΈΠ²ΠΎΡƒ ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ»Π°.
  • НСма ΠΏΠΎΡ‚Ρ€Π΅Π±Π΅ Π΄Π° користитС сСрвСр. Π›ΠΎΠ³ΠΈΠΊΠ° Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π΅ сС ΠΌΠΎΠΆΠ΅ ΠΈΠ·Π²Ρ€ΡˆΠΈΡ‚ΠΈ Π½Π° Π±Π»ΠΎΠΊΡ‡Π΅Ρ˜Π½ ΠΌΡ€Π΅ΠΆΠΈ, Π³Π΄Π΅ јС ΠΌΠΎΠ³ΡƒΡ›Π΅ ΡƒΡΠΊΠ»Π°Π΄ΠΈΡˆΡ‚ΠΈΡ‚ΠΈ ΠΏΠΎΡ‚Ρ€Π΅Π±Π½Ρƒ ΠΊΠΎΠ»ΠΈΡ‡ΠΈΠ½Ρƒ ΠΏΠΎΠ΄Π°Ρ‚Π°ΠΊΠ°.

ΠŸΠΎΡΡ‚ΠΎΡ˜Π΅ 2 Ρ€Π΅Π»Π°Ρ‚ΠΈΠ²Π½ΠΎ Π±Π΅Π·Π±Π΅Π΄Π½Π° ΡΠΊΠ»Π°Π΄ΠΈΡˆΡ‚Π° Π·Π° корисничкС ΠΊΡ™ΡƒΡ‡Π΅Π²Π΅ - хардвСрски Π½ΠΎΠ²Ρ‡Π°Π½ΠΈΡ†ΠΈ ΠΈ Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Π΅ ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°. Π₯ардвСрски Π½ΠΎΠ²Ρ‡Π°Π½ΠΈΡ†ΠΈ су ΡƒΠ³Π»Π°Π²Π½ΠΎΠΌ ΠΈΠ·ΡƒΠ·Π΅Ρ‚Π½ΠΎ Π±Π΅Π·Π±Π΅Π΄Π½ΠΈ, Π°Π»ΠΈ Ρ‚Π΅ΡˆΠΊΠΈ Π·Π° ΠΊΠΎΡ€ΠΈΡˆΡ›Π΅ΡšΠ΅ ΠΈ Π΄Π°Π»Π΅ΠΊΠΎ ΠΎΠ΄ бСсплатних, Π°Π»ΠΈ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π° су ΡΠ°Π²Ρ€ΡˆΠ΅Π½Π° ΠΊΠΎΠΌΠ±ΠΈΠ½Π°Ρ†ΠΈΡ˜Π° бСзбСдности ΠΈ Π»Π°ΠΊΠΎΡ›Π΅ ΠΊΠΎΡ€ΠΈΡˆΡ›Π΅ΡšΠ°, Π° ΠΌΠΎΠ³Ρƒ Π±ΠΈΡ‚ΠΈ ΠΈ ΠΏΠΎΡ‚ΠΏΡƒΠ½ΠΎ бСсплатна Π·Π° ΠΊΡ€Π°Ρ˜ΡšΠ΅ корисникС.

Π£Π·ΠΈΠΌΠ°Ρ˜ΡƒΡ›ΠΈ свС ΠΎΠ²ΠΎ Ρƒ ΠΎΠ±Π·ΠΈΡ€, ΠΆΠ΅Π»Π΅Π»ΠΈ смо Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌΠΎ најбСзбСднијС ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ΅ којС ΠΏΠΎΡ˜Π΅Π΄Π½ΠΎΡΡ‚Π°Π²Ρ™ΡƒΡ˜Π΅ Ρ€Π°Π·Π²ΠΎΡ˜ Π΄Π΅Ρ†Π΅Π½Ρ‚Ρ€Π°Π»ΠΈΠ·ΠΎΠ²Π°Π½ΠΈΡ… Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π° ΠΏΡ€ΡƒΠΆΠ°ΡšΠ΅ΠΌ Ρ˜Π΅Π΄Π½ΠΎΡΡ‚Π°Π²Π½ΠΎΠ³ АПИ-ја Π·Π° Ρ€Π°Π΄ са Ρ‚Ρ€Π°Π½ΡΠ°ΠΊΡ†ΠΈΡ˜Π°ΠΌΠ° ΠΈ потписима.
О ΠΎΠ²ΠΎΠΌ искуству Ρ›Π΅ΠΌΠΎ Π²Π°ΠΌ Ρ€Π΅Ρ›ΠΈ Ρƒ наставку.

Π§Π»Π°Π½Π°ΠΊ Ρ›Π΅ садрТати упутства ΠΊΠΎΡ€Π°ΠΊ ΠΏΠΎ ΠΊΠΎΡ€Π°ΠΊ ΠΎ Ρ‚ΠΎΠΌΠ΅ ΠΊΠ°ΠΊΠΎ написати ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ΅ ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°, са ΠΏΡ€ΠΈΠΌΠ΅Ρ€ΠΈΠΌΠ° ΠΊΠΎΠ΄Π° ΠΈ снимцима Π΅ΠΊΡ€Π°Π½Π°. ΠœΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΡ€ΠΎΠ½Π°Ρ›ΠΈ сав ΠΊΠΎΠ΄ Ρƒ ΡΠΏΡ€Π΅ΠΌΠΈΡˆΡ‚Π°. Π‘Π²Π°ΠΊΠΎ ΡƒΡ€Π΅Π·ΠΈΠ²Π°ΡšΠ΅ Π»ΠΎΠ³ΠΈΡ‡Π½ΠΎ ΠΎΠ΄Π³ΠΎΠ²Π°Ρ€Π° Π΄Π΅Π»Ρƒ ΠΎΠ²ΠΎΠ³ Ρ‡Π»Π°Π½ΠΊΠ°.

ΠšΡ€Π°Ρ‚ΠΊΠ° ΠΈΡΡ‚ΠΎΡ€ΠΈΡ˜Π° ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°

Π•ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Π΅ Π·Π° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π΅ ΠΏΠΎΡΡ‚ΠΎΡ˜Π΅ Π²Π΅Ρ› Π΄ΡƒΠΆΠ΅ Π²Ρ€Π΅ΠΌΠ΅. Појавили су сС Ρƒ Π˜Π½Ρ‚Π΅Ρ€Π½Π΅Ρ‚ Π•ΠΊΠΏΠ»ΠΎΡ€Π΅Ρ€-Ρƒ још 1999. Π³ΠΎΠ΄ΠΈΠ½Π΅, Ρƒ Π€ΠΈΡ€Π΅Ρ„ΠΎΠΊ-Ρƒ 2004. Π³ΠΎΠ΄ΠΈΠ½Π΅. ΠœΠ΅Ρ’ΡƒΡ‚ΠΈΠΌ, Π²Π΅ΠΎΠΌΠ° Π΄ΡƒΠ³ΠΎ нијС ΠΏΠΎΡΡ‚ΠΎΡ˜Π°ΠΎ Ρ˜Π΅Π΄ΠΈΠ½ΡΡ‚Π²Π΅Π½ΠΈ стандард Π·Π° ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ°.

МоТСмо Ρ€Π΅Ρ›ΠΈ Π΄Π° сС појавио зајСдно са Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Π°ΠΌΠ° Ρƒ Ρ‡Π΅Ρ‚Π²Ρ€Ρ‚ΠΎΡ˜ Π²Π΅Ρ€Π·ΠΈΡ˜ΠΈ Π“ΠΎΠΎΠ³Π»Π΅ Π¦Ρ…Ρ€ΠΎΠΌΠ΅-Π°. Наравно, Ρ‚Π°Π΄Π° нијС Π±ΠΈΠ»ΠΎ ΡΠΏΠ΅Ρ†ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π΅, Π°Π»ΠΈ јС Π¦Ρ…Ρ€ΠΎΠΌΠ΅ АПИ постао њСгова основа: освојивши Π²Π΅Ρ›ΠΈΠ½Ρƒ Ρ‚Ρ€ΠΆΠΈΡˆΡ‚Π° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π° ΠΈ ΠΈΠΌΠ°Ρ˜ΡƒΡ›ΠΈ ΡƒΠ³Ρ€Π°Ρ’Π΅Π½Ρƒ ΠΏΡ€ΠΎΠ΄Π°Π²Π½ΠΈΡ†Ρƒ Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π°, Π¦Ρ…Ρ€ΠΎΠΌΠ΅ јС Π·Π°ΠΏΡ€Π°Π²ΠΎ поставио стандард Π·Π° ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°.

Мозилла јС ΠΈΠΌΠ°Π»Π° свој стандард, Π°Π»ΠΈ видСвши популарност Π¦Ρ…Ρ€ΠΎΠΌΠ΅ Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Π°, компанија јС ΠΎΠ΄Π»ΡƒΡ‡ΠΈΠ»Π° Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈ ΠΊΠΎΠΌΠΏΠ°Ρ‚ΠΈΠ±ΠΈΠ»Π°Π½ АПИ. Π£ 2015. Π³ΠΎΠ΄ΠΈΠ½ΠΈ, Π½Π° ΠΈΠ½ΠΈΡ†ΠΈΡ˜Π°Ρ‚ΠΈΠ²Ρƒ МозилС, створСна јС посСбна Π³Ρ€ΡƒΠΏΠ° Ρƒ ΠΎΠΊΠ²ΠΈΡ€Ρƒ Π’ΠΎΡ€Π»Π΄ Π’ΠΈΠ΄Π΅ Π’Π΅Π± Цонсортиум (Π’3Π¦) Π·Π° Ρ€Π°Π΄ Π½Π° ΡΠΏΠ΅Ρ†ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π°ΠΌΠ° ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° Π·Π° вишС ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°.

ΠŸΠΎΡΡ‚ΠΎΡ˜Π΅Ρ›Π° АПИ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° Π·Π° Π¦Ρ…Ρ€ΠΎΠΌΠ΅ су ΡƒΠ·Π΅Ρ‚Π° ΠΊΠ°ΠΎ основа. Π Π°Π΄ јС ΠΎΠ±Π°Π²Ρ™Π΅Π½ ΡƒΠ· ΠΏΠΎΠ΄Ρ€ΡˆΠΊΡƒ ΠœΠΈΡ†Ρ€ΠΎΡΠΎΡ„Ρ‚Π° (Π“ΡƒΠ³Π» јС ΠΎΠ΄Π±ΠΈΠΎ Π΄Π° ΡƒΡ‡Π΅ΡΡ‚Π²ΡƒΡ˜Π΅ Ρƒ Ρ€Π°Π·Π²ΠΎΡ˜Ρƒ стандарда), Π° ΠΊΠ°ΠΎ Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚ Ρ‚ΠΎΠ³Π° појавио сС Π½Π°Ρ†Ρ€Ρ‚ ΡΠΏΠ΅Ρ†ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π΅.

Π€ΠΎΡ€ΠΌΠ°Π»Π½ΠΎ, ΡΠΏΠ΅Ρ†ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΡ˜Ρƒ ΠΏΠΎΠ΄Ρ€ΠΆΠ°Π²Π°Ρ˜Ρƒ Π•Π΄Π³Π΅, Π€ΠΈΡ€Π΅Ρ„ΠΎΠΊ ΠΈ ΠžΠΏΠ΅Ρ€Π° (ΠΈΠΌΠ°Ρ˜Ρ‚Π΅ Π½Π° ΡƒΠΌΡƒ Π΄Π° Π¦Ρ…Ρ€ΠΎΠΌΠ΅ нијС Π½Π° овој листи). Али Ρƒ ствари, стандард јС Ρƒ вСликој ΠΌΠ΅Ρ€ΠΈ ΠΊΠΎΠΌΠΏΠ°Ρ‚ΠΈΠ±ΠΈΠ»Π°Π½ са Π¦Ρ…Ρ€ΠΎΠΌΠ΅-ΠΎΠΌ, ΠΏΠΎΡˆΡ‚ΠΎ јС Π·Π°ΠΏΡ€Π°Π²ΠΎ написан Π½Π° основу ΡšΠ΅Π³ΠΎΠ²ΠΈΡ… Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Π°. ΠœΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Ρ‚ΠΈ вишС ΠΎ ВСбЕктСнсионс АПИ-Ρ˜Ρƒ ΠΎΠ²Π΄Π΅.

Π‘Ρ‚Ρ€ΡƒΠΊΡ‚ΡƒΡ€Π° ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ°

ЈСдина Π΄Π°Ρ‚ΠΎΡ‚Π΅ΠΊΠ° која јС ΠΏΠΎΡ‚Ρ€Π΅Π±Π½Π° Π·Π° Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Ρƒ јС манифСст (манифСст.јсон). Π’ΠΎ јС Ρ‚Π°ΠΊΠΎΡ’Π΅ β€žΡƒΠ»Π°Π·Π½Π° Ρ‚Π°Ρ‡ΠΊΠ°β€œ Ρƒ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ΅.

ΠœΠ°Π½ΠΈΡ„Π΅ΡΡ‚

ΠŸΡ€Π΅ΠΌΠ° ΡΠΏΠ΅Ρ†ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΡ˜ΠΈ, Π΄Π°Ρ‚ΠΎΡ‚Π΅ΠΊΠ° манифСста јС Π²Π°ΠΆΠ΅Ρ›Π° ЈБОН Π΄Π°Ρ‚ΠΎΡ‚Π΅ΠΊΠ°. ΠŸΠΎΡ‚ΠΏΡƒΠ½ΠΈ опис ΠΊΡ™ΡƒΡ‡Π΅Π²Π° манифСста са ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡ˜Π°ΠΌΠ° ΠΎ Ρ‚ΠΎΠΌΠ΅ који ΠΊΡ™ΡƒΡ‡Π΅Π²ΠΈ су ΠΏΠΎΠ΄Ρ€ΠΆΠ°Π½ΠΈ Ρƒ ΠΊΠΎΠΌ ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Ρƒ сС ΠΌΠΎΠΆΠ΅ Π²ΠΈΠ΄Π΅Ρ‚ΠΈ ΠΎΠ²Π΄Π΅.

ΠšΡ™ΡƒΡ‡Π΅Π²ΠΈ који нису Ρƒ ΡΠΏΠ΅Ρ†ΠΈΡ„ΠΈΠΊΠ°Ρ†ΠΈΡ˜ΠΈ β€žΠΌΠΎΠ³Ρƒβ€œ Π±ΠΈΡ‚ΠΈ Π·Π°Π½Π΅ΠΌΠ°Ρ€Π΅Π½ΠΈ (ΠΈ Π¦Ρ…Ρ€ΠΎΠΌΠ΅ ΠΈ Π€ΠΈΡ€Π΅Ρ„ΠΎΠΊ ΠΏΡ€ΠΈΡ˜Π°Π²Ρ™ΡƒΡ˜Ρƒ Π³Ρ€Π΅ΡˆΠΊΠ΅, Π°Π»ΠΈ Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Π΅ Π½Π°ΡΡ‚Π°Π²Ρ™Π°Ρ˜Ρƒ Π΄Π° Ρ€Π°Π΄Π΅).

И ΠΆΠ΅Π»Π΅ΠΎ Π±ΠΈΡ… Π΄Π° скрСнСм ΠΏΠ°ΠΆΡšΡƒ Π½Π° Π½Π΅ΠΊΠ΅ Ρ‚Π°Ρ‡ΠΊΠ΅.

  1. ΠΏΠΎΠ·Π°Π΄ΠΈΠ½Π° β€” ΠΎΠ±Ρ˜Π΅ΠΊΠ°Ρ‚ који ΡƒΠΊΡ™ΡƒΡ‡ΡƒΡ˜Π΅ слСдСћа ΠΏΠΎΡ™Π°:
    1. скриптС β€” Π½ΠΈΠ· скрипти којС Ρ›Π΅ сС ΠΈΠ·Π²Ρ€ΡˆΠ°Π²Π°Ρ‚ΠΈ Ρƒ позадинском контСксту (ΠΎ Ρ‚ΠΎΠΌΠ΅ Ρ›Π΅ΠΌΠΎ Π³ΠΎΠ²ΠΎΡ€ΠΈΡ‚ΠΈ ΠΌΠ°Π»ΠΎ каснијС);
    2. страна - умСсто скрипти којС Ρ›Π΅ сС ΠΈΠ·Π²Ρ€ΡˆΠ°Π²Π°Ρ‚ΠΈ Π½Π° ΠΏΡ€Π°Π·Π½ΠΎΡ˜ страници, ΠΌΠΎΠΆΠ΅Ρ‚Π΅ навСсти Ρ…Ρ‚ΠΌΠ» са ΡΠ°Π΄Ρ€ΠΆΠ°Ρ˜Π΅ΠΌ. Π£ ΠΎΠ²ΠΎΠΌ ΡΠ»ΡƒΡ‡Π°Ρ˜Ρƒ, ΠΏΠΎΡ™Π΅ скриптС Ρ›Π΅ Π±ΠΈΡ‚ΠΈ Π·Π°Π½Π΅ΠΌΠ°Ρ€Π΅Π½ΠΎ, Π° скриптС Ρ›Π΅ ΠΌΠΎΡ€Π°Ρ‚ΠΈ Π΄Π° сС ΡƒΠ±Π°Ρ†Π΅ Ρƒ страницу са ΡΠ°Π΄Ρ€ΠΆΠ°Ρ˜Π΅ΠΌ;
    3. ΡƒΠΏΠΎΡ€Π°Π½ β€” Π±ΠΈΠ½Π°Ρ€Π½Π° заставица, Π°ΠΊΠΎ нијС Π½Π°Π²Π΅Π΄Π΅Π½Π°, ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡ Ρ›Π΅ β€žΡƒΠ±ΠΈΡ‚ΠΈβ€œ позадински процСс ΠΊΠ°Π΄Π° сматра Π΄Π° Π½Π΅ Ρ€Π°Π΄ΠΈ Π½ΠΈΡˆΡ‚Π° ΠΈ ΠΏΠΎΠ½ΠΎΠ²ΠΎ Π³Π° ΠΏΠΎΠΊΡ€Π΅Π½ΡƒΡ‚ΠΈ Π°ΠΊΠΎ јС ΠΏΠΎΡ‚Ρ€Π΅Π±Π½ΠΎ. Π£ супротном, страница Ρ›Π΅ Π±ΠΈΡ‚ΠΈ ΡƒΡ‡ΠΈΡ‚Π°Π½Π° само ΠΊΠ°Π΄Π° јС ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡ Π·Π°Ρ‚Π²ΠΎΡ€Π΅Π½. НијС ΠΏΠΎΠ΄Ρ€ΠΆΠ°Π½ΠΎ Ρƒ Π€ΠΈΡ€Π΅Ρ„ΠΎΠΊ-Ρƒ.
  2. Ρ†ΠΎΠ½Ρ‚Π΅Π½Ρ‚_сцриптс β€” Π½ΠΈΠ· ΠΎΠ±Ρ˜Π΅ΠΊΠ°Ρ‚Π° који Π²Π°ΠΌ ΠΎΠΌΠΎΠ³ΡƒΡ›Π°Π²Π°Ρ˜Ρƒ Π΄Π° ΡƒΡ‡ΠΈΡ‚Π°Ρ‚Π΅ Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΡ‚Π΅ скриптС Π½Π° Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΡ‚Π΅ Π²Π΅Π± страницС. Π‘Π²Π°ΠΊΠΈ ΠΎΠ±Ρ˜Π΅ΠΊΠ°Ρ‚ садрТи слСдСћа Π²Π°ΠΆΠ½Π° ΠΏΠΎΡ™Π°:
    1. ΡˆΠΈΠ±ΠΈΡ†Π΅ - ΠΎΠ±Ρ€Π°Π·Π°Ρ† ΡƒΡ€Π», који ΠΎΠ΄Ρ€Π΅Ρ’ΡƒΡ˜Π΅ Π΄Π° Π»ΠΈ Ρ›Π΅ ΠΎΠ΄Ρ€Π΅Ρ’Π΅Π½Π° скрипта ΡΠ°Π΄Ρ€ΠΆΠ°Ρ˜Π° Π±ΠΈΡ‚ΠΈ ΡƒΠΊΡ™ΡƒΡ‡Π΅Π½Π° ΠΈΠ»ΠΈ Π½Π΅.
    2. js β€” списак скрипти којС Ρ›Π΅ Π±ΠΈΡ‚ΠΈ ΡƒΡ‡ΠΈΡ‚Π°Π½Π΅ Ρƒ овај ΠΌΠ΅Ρ‡;
    3. Π΅ΠΊΡ†Π»ΡƒΠ΄Π΅_матцхСс - ΠΈΡΠΊΡ™ΡƒΡ‡ΡƒΡ˜Π΅ са Ρ‚Π΅Ρ€Π΅Π½Π° match Π£Π Π› адрСсС којС ΠΎΠ΄Π³ΠΎΠ²Π°Ρ€Π°Ρ˜Ρƒ ΠΎΠ²ΠΎΠΌ ΠΏΠΎΡ™Ρƒ.
  3. ΠΏΠ°Π³Π΅_Π°Ρ†Ρ‚ΠΈΠΎΠ½ - јС Π·Π°ΠΏΡ€Π°Π²ΠΎ ΠΎΠ±Ρ˜Π΅ΠΊΠ°Ρ‚ који јС ΠΎΠ΄Π³ΠΎΠ²ΠΎΡ€Π°Π½ Π·Π° ΠΈΠΊΠΎΠ½Ρƒ која сС ΠΏΡ€ΠΈΠΊΠ°Π·ΡƒΡ˜Π΅ ΠΏΠΎΡ€Π΅Π΄ адрСснС Ρ‚Ρ€Π°ΠΊΠ΅ Ρƒ ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Ρƒ ΠΈ ΠΈΠ½Ρ‚Π΅Ρ€Π°ΠΊΡ†ΠΈΡ˜Ρƒ са њом. Π’Π°ΠΊΠΎΡ’Π΅ Π²Π°ΠΌ ΠΎΠΌΠΎΠ³ΡƒΡ›Π°Π²Π° Π΄Π° ΠΏΡ€ΠΈΠΊΠ°ΠΆΠ΅Ρ‚Π΅ искачући ΠΏΡ€ΠΎΠ·ΠΎΡ€, који јС дСфинисан ΠΊΠΎΡ€ΠΈΡˆΡ›Π΅ΡšΠ΅ΠΌ вашСг сопствСног Π₯Π’ΠœΠ›-Π°, Π¦Π‘Π‘-Π° ΠΈ ЈБ-Π°.
    1. Π΄Π΅Ρ„Π°ΡƒΠ»Ρ‚_ΠΏΠΎΠΏΡƒΠΏ β€” ΠΏΡƒΡ‚Π°ΡšΠ° Π΄ΠΎ Π₯Π’ΠœΠ› Π΄Π°Ρ‚ΠΎΡ‚Π΅ΠΊΠ΅ са искачућим ΠΈΠ½Ρ‚Π΅Ρ€Ρ„Π΅Ρ˜ΡΠΎΠΌ, ΠΌΠΎΠΆΠ΅ Π΄Π° садрТи Π¦Π‘Π‘ ΠΈ ЈБ.
  4. Π”ΠΎΠ·Π²ΠΎΠ»Π΅ β€” Π½ΠΈΠ· Π·Π° ΡƒΠΏΡ€Π°Π²Ρ™Π°ΡšΠ΅ ΠΏΡ€Π°Π²ΠΈΠΌΠ° ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ°. ΠŸΠΎΡΡ‚ΠΎΡ˜Π΅ 3 врстС ΠΏΡ€Π°Π²Π°, која су Π΄Π΅Ρ‚Π°Ρ™Π½ΠΎ описана ΠΎΠ²Π΄Π΅
  5. Π²Π΅Π±_аццСссиблС_рСсоурцСс β€” рСсурси ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° којС Π²Π΅Π± страница ΠΌΠΎΠΆΠ΅ Π΄Π° Π·Π°Ρ…Ρ‚Π΅Π²Π°, Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€, сликС, ЈБ, Π¦Π‘Π‘, Π₯Π’ΠœΠ› Π΄Π°Ρ‚ΠΎΡ‚Π΅ΠΊΠ΅.
  6. Π΅ΠΊΡ‚Π΅Ρ€Π½Π°Π»Π»ΠΈ_Ρ†ΠΎΠ½Π½Π΅Ρ†Ρ‚Π°Π±Π»Π΅ β€” ΠΎΠ²Π΄Π΅ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ Сксплицитно навСсти Π˜Π”-ΠΎΠ²Π΅ Π΄Ρ€ΡƒΠ³ΠΈΡ… Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Π° ΠΈ Π΄ΠΎΠΌΠ΅Π½Π° Π²Π΅Π± страница са ΠΊΠΎΡ˜ΠΈΡ… сС ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΠΎΠ²Π΅Π·Π°Ρ‚ΠΈ. Π”ΠΎΠΌΠ΅Π½ ΠΌΠΎΠΆΠ΅ Π±ΠΈΡ‚ΠΈ Π΄Ρ€ΡƒΠ³ΠΎΠ³ Π½ΠΈΠ²ΠΎΠ° ΠΈΠ»ΠΈ вишСг. НС Ρ€Π°Π΄ΠΈ Ρƒ Π€ΠΈΡ€Π΅Ρ„ΠΎΠΊ-Ρƒ.

ΠšΠΎΠ½Ρ‚Π΅ΠΊΡΡ‚ ΠΈΠ·Π²Ρ€ΡˆΠ΅ΡšΠ°

Π•ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Π° ΠΈΠΌΠ° Ρ‚Ρ€ΠΈ контСкста ΠΈΠ·Π²Ρ€ΡˆΠ°Π²Π°ΡšΠ° ΠΊΠΎΠ΄Π°, односно Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π° сС ΡΠ°ΡΡ‚ΠΎΡ˜ΠΈ ΠΎΠ΄ Ρ‚Ρ€ΠΈ Π΄Π΅Π»Π° са Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΡ‚ΠΈΠΌ Π½ΠΈΠ²ΠΎΠΈΠΌΠ° приступа АПИ-Ρ˜Ρƒ ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°.

ΠšΠΎΠ½Ρ‚Π΅ΠΊΡΡ‚ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ°

Π’Π΅Ρ›ΠΈΠ½Π° АПИ-ја јС доступна ΠΎΠ²Π΄Π΅. Π£ ΠΎΠ²ΠΎΠΌ контСксту ΠΎΠ½ΠΈ "ΠΆΠΈΠ²Π΅":

  1. Позадинска страница β€” β€žΠ±Π°Ρ†ΠΊΠ΅Π½Π΄β€œ Π΄Π΅ΠΎ Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Π΅. Π”Π°Ρ‚ΠΎΡ‚Π΅ΠΊΠ° јС Π½Π°Π²Π΅Π΄Π΅Π½Π° Ρƒ манифСсту ΠΏΠΎΠΌΠΎΡ›Ρƒ ΠΊΡ™ΡƒΡ‡Π° β€žΠ±Π°Ρ†ΠΊΠ³Ρ€ΠΎΡƒΠ½Π΄β€œ.
  2. Π˜ΡΠΊΠ°Ρ‡ΡƒΡ›Π° страница β€” искачућа страница која сС ΠΏΠΎΡ˜Π°Π²Ρ™ΡƒΡ˜Π΅ ΠΊΠ°Π΄Π° ΠΊΠ»ΠΈΠΊΠ½Π΅Ρ‚Π΅ Π½Π° ΠΈΠΊΠΎΠ½Ρƒ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ°. Π£ манифСсту browser_action -> default_popup.
  3. ΠŸΡ€ΠΈΠ»Π°Π³ΠΎΡ’Π΅Π½Π° страница β€” страница ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ°, β€žΠΆΠΈΠ²ΠΈβ€œ Ρƒ посСбној ΠΊΠ°Ρ€Ρ‚ΠΈΡ†ΠΈ ΠΏΡ€ΠΈΠΊΠ°Π·Π° chrome-extension://<id_Ρ€Π°ΡΡˆΠΈΡ€Π΅Π½ΠΈΡ>/customPage.html.

Овај контСкст ΠΏΠΎΡΡ‚ΠΎΡ˜ΠΈ нСзависно ΠΎΠ΄ ΠΏΡ€ΠΎΠ·ΠΎΡ€Π° ΠΈ ΠΊΠ°Ρ€Ρ‚ΠΈΡ†Π° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°. Позадинска страница ΠΏΠΎΡΡ‚ΠΎΡ˜ΠΈ Ρƒ јСдној копији ΠΈ ΡƒΠ²Π΅ΠΊ Ρ€Π°Π΄ΠΈ (ΠΈΠ·ΡƒΠ·Π΅Ρ‚Π°ΠΊ јС страница Π΄ΠΎΠ³Π°Ρ’Π°Ρ˜Π°, ΠΊΠ°Π΄Π° јС позадинска скрипта ΠΏΠΎΠΊΡ€Π΅Π½ΡƒΡ‚Π° Π΄ΠΎΠ³Π°Ρ’Π°Ρ˜Π΅ΠΌ ΠΈ β€žΡƒΠΌΡ€Π΅β€œ Π½Π°ΠΊΠΎΠ½ њСговог ΠΈΠ·Π²Ρ€ΡˆΠ΅ΡšΠ°). Π˜ΡΠΊΠ°Ρ‡ΡƒΡ›Π° страница ΠΏΠΎΡΡ‚ΠΎΡ˜ΠΈ ΠΊΠ°Π΄Π° јС искачући ΠΏΡ€ΠΎΠ·ΠΎΡ€ ΠΎΡ‚Π²ΠΎΡ€Π΅Π½, ΠΈ ΠŸΡ€ΠΈΠ»Π°Π³ΠΎΡ’Π΅Π½Π° страница β€” Π΄ΠΎΠΊ јС ΠΊΠ°Ρ€Ρ‚ΠΈΡ†Π° са њом ΠΎΡ‚Π²ΠΎΡ€Π΅Π½Π°. НСма приступа Π΄Ρ€ΡƒΠ³ΠΈΠΌ ΠΊΠ°Ρ€Ρ‚ΠΈΡ†Π°ΠΌΠ° ΠΈ ΡšΠΈΡ…ΠΎΠ²ΠΎΠΌ ΡΠ°Π΄Ρ€ΠΆΠ°Ρ˜Ρƒ ΠΈΠ· ΠΎΠ²ΠΎΠ³ контСкста.

ΠšΠΎΠ½Ρ‚Π΅ΠΊΡΡ‚ скриптС ΡΠ°Π΄Ρ€ΠΆΠ°Ρ˜Π°

Π”Π°Ρ‚ΠΎΡ‚Π΅ΠΊΠ° скриптС ΡΠ°Π΄Ρ€ΠΆΠ°Ρ˜Π° сС ΠΏΠΎΠΊΡ€Π΅Ρ›Π΅ зајСдно са сваком ΠΊΠ°Ρ€Ρ‚ΠΈΡ†ΠΎΠΌ ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°. Има приступ Π΄Π΅Π»Ρƒ АПИ-ја Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Π΅ ΠΈ Π”ΠžΠœ стаблу Π²Π΅Π± страницС. Π—Π° ΠΈΠ½Ρ‚Π΅Ρ€Π°ΠΊΡ†ΠΈΡ˜Ρƒ са страницом ΠΎΠ΄Π³ΠΎΠ²ΠΎΡ€Π½Π΅ су скриптС ΡΠ°Π΄Ρ€ΠΆΠ°Ρ˜Π°. Π•ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Π΅ којС ΠΌΠ°Π½ΠΈΠΏΡƒΠ»ΠΈΡˆΡƒ Π”ΠžΠœ стаблом Ρ‚ΠΎ Ρ€Π°Π΄Π΅ Ρƒ скриптама ΡΠ°Π΄Ρ€ΠΆΠ°Ρ˜Π° – Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Π±Π»ΠΎΠΊΠ°Ρ‚ΠΎΡ€ΠΈΠΌΠ° огласа ΠΈΠ»ΠΈ ΠΏΡ€Π΅Π²ΠΎΠ΄ΠΈΠΎΡ†ΠΈΠΌΠ°. Π’Π°ΠΊΠΎΡ’Π΅, скрипта ΡΠ°Π΄Ρ€ΠΆΠ°Ρ˜Π° ΠΌΠΎΠΆΠ΅ Π΄Π° ΠΊΠΎΠΌΡƒΠ½ΠΈΡ†ΠΈΡ€Π° са страницом ΠΏΡƒΡ‚Π΅ΠΌ стандарда postMessage.

ΠšΠΎΠ½Ρ‚Π΅ΠΊΡΡ‚ Π²Π΅Π± страницС

Ово јС сама Π²Π΅Π± страница. НСма Π½ΠΈΠΊΠ°ΠΊΠ²Π΅ Π²Π΅Π·Π΅ са Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜ΠΎΠΌ ΠΈ Π½Π΅ΠΌΠ° приступ Ρ‚Π°ΠΌΠΎ, осим Ρƒ ΡΠ»ΡƒΡ‡Π°Ρ˜Π΅Π²ΠΈΠΌΠ° ΠΊΠ°Π΄Π° Π΄ΠΎΠΌΠ΅Π½ ΠΎΠ²Π΅ страницС нијС Сксплицитно Π½Π°Π²Π΅Π΄Π΅Π½ Ρƒ манифСсту (вишС ΠΎ Ρ‚ΠΎΠΌΠ΅ Ρƒ наставку).

Π Π°Π·ΠΌΠ΅Π½Π° ΠΏΠΎΡ€ΡƒΠΊΠ°

Π Π°Π·Π»ΠΈΡ‡ΠΈΡ‚ΠΈ Π΄Π΅Π»ΠΎΠ²ΠΈ Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π΅ ΠΌΠΎΡ€Π°Ρ˜Ρƒ мСђусобно Π΄Π° Ρ€Π°Π·ΠΌΠ΅ΡšΡƒΡ˜Ρƒ ΠΏΠΎΡ€ΡƒΠΊΠ΅. Π—Π° ΠΎΠ²ΠΎ ΠΏΠΎΡΡ‚ΠΎΡ˜ΠΈ АПИ runtime.sendMessage Π΄Π° ΠΏΠΎΡˆΠ°Ρ™Π΅Ρ‚Π΅ ΠΏΠΎΡ€ΡƒΠΊΡƒ background ΠΈ tabs.sendMessage Π΄Π° ΠΏΠΎΡˆΠ°Ρ™Π΅Ρ‚Π΅ ΠΏΠΎΡ€ΡƒΠΊΡƒ страници (скрипта ΡΠ°Π΄Ρ€ΠΆΠ°Ρ˜Π°, искачући ΠΏΡ€ΠΎΠ·ΠΎΡ€ ΠΈΠ»ΠΈ Π²Π΅Π± страница Π°ΠΊΠΎ јС доступна externally_connectable). Испод јС ΠΏΡ€ΠΈΠΌΠ΅Ρ€ ΠΊΠ°Π΄Π° приступатС Π¦Ρ…Ρ€ΠΎΠΌΠ΅ АПИ-Ρ˜Ρƒ.

// Π‘ΠΎΠΎΠ±Ρ‰Π΅Π½ΠΈΠ΅ΠΌ ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ любой 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.

Π”ΠΈΡ˜Π°Π³Ρ€Π°ΠΌ ΠΏΡ€ΠΈΠΌΠ΅Π½Π΅

Π₯ајдС Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌΠΎ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ΅ ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π° којС Ρ‡ΡƒΠ²Π° ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½Π΅ ΠΊΡ™ΡƒΡ‡Π΅Π²Π΅, ΠΎΠΌΠΎΠ³ΡƒΡ›Π°Π²Π° приступ јавним ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡ˜Π°ΠΌΠ° (адрСса, јавни ΠΊΡ™ΡƒΡ‡ ΠΊΠΎΠΌΡƒΠ½ΠΈΡ†ΠΈΡ€Π° са страницом ΠΈ ΠΎΠΌΠΎΠ³ΡƒΡ›Π°Π²Π° Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π°ΠΌΠ° Ρ‚Ρ€Π΅Ρ›ΠΈΡ… страна Π΄Π° Π·Π°Ρ…Ρ‚Π΅Π²Π°Ρ˜Ρƒ потпис Π·Π° Ρ‚Ρ€Π°Π½ΡΠ°ΠΊΡ†ΠΈΡ˜Π΅.

Развој Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π°

Наша Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π° ΠΌΠΎΡ€Π° ΠΈ Π΄Π° ΠΊΠΎΠΌΡƒΠ½ΠΈΡ†ΠΈΡ€Π° са корисником ΠΈ Π΄Π° страници ΠΎΠ±Π΅Π·Π±Π΅Π΄ΠΈ АПИ Π·Π° позивањС ΠΌΠ΅Ρ‚ΠΎΠ΄Π° (Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€, Π·Π° ΠΏΠΎΡ‚ΠΏΠΈΡΠΈΠ²Π°ΡšΠ΅ Ρ‚Ρ€Π°Π½ΡΠ°ΠΊΡ†ΠΈΡ˜Π°). Π—Π°Π΄ΠΎΠ²ΠΎΡ™ΠΈΡ‚Π΅ сС само јСдним contentscript Π½Π΅Ρ›Π΅ Ρ€Π°Π΄ΠΈΡ‚ΠΈ, ΠΏΠΎΡˆΡ‚ΠΎ ΠΈΠΌΠ° приступ само Π”ΠžΠœ-Ρƒ, Π°Π»ΠΈ Π½Π΅ ΠΈ ЈБ-Ρƒ страницС. ΠŸΠΎΠ²Π΅ΠΆΠΈΡ‚Π΅ сС ΠΏΡ€Π΅ΠΊΠΎ runtime.connect Π½Π΅ ΠΌΠΎΠΆΠ΅ΠΌΠΎ, Ρ˜Π΅Ρ€ јС АПИ ΠΏΠΎΡ‚Ρ€Π΅Π±Π°Π½ Π½Π° свим Π΄ΠΎΠΌΠ΅Π½ΠΈΠΌΠ°, Π° само ΠΎΠ΄Ρ€Π΅Ρ’Π΅Π½ΠΈ ΠΌΠΎΠ³Ρƒ Π±ΠΈΡ‚ΠΈ Π½Π°Π²Π΅Π΄Π΅Π½ΠΈ Ρƒ манифСсту. Као Ρ€Π΅Π·ΡƒΠ»Ρ‚Π°Ρ‚, Π΄ΠΈΡ˜Π°Π³Ρ€Π°ΠΌ Ρ›Π΅ ΠΈΠ·Π³Π»Π΅Π΄Π°Ρ‚ΠΈ ΠΎΠ²Π°ΠΊΠΎ:

ПисањС Π±Π΅Π·Π±Π΅Π΄Π½ΠΎΠ³ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°

Π‘ΠΈΡ›Π΅ још јСдан сцСнарио - inpage, који Ρ›Π΅ΠΌΠΎ ΡƒΠ±Π°Ρ†ΠΈΡ‚ΠΈ Π½Π° страницу. ΠŸΠΎΠΊΡ€Π΅Ρ›Π΅ сС Ρƒ свом контСксту ΠΈ ΠΎΠ±Π΅Π·Π±Π΅Π΄ΠΈΡ›Π΅ АПИ Π·Π° Ρ€Π°Π΄ са Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜ΠΎΠΌ.

ΠΏΠΎΡ‡Π΅Ρ‚Π°ΠΊ

Π‘Π°Π² ΠΊΠΎΠ΄ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π° доступан јС Π½Π° Π“ΠΈΡ‚Π₯ΡƒΠ±. Π’ΠΎΠΊΠΎΠΌ описа Ρ›Π΅ Π±ΠΈΡ‚ΠΈ Π²Π΅Π·Π΅ Π΄ΠΎ ΡƒΡ€Π΅Π·ΠΈΠ²Π°ΡšΠ°.

ΠŸΠΎΡ‡Π½ΠΈΠΌΠΎ са манифСстом:

{
  // Имя ΠΈ описаниС, вСрсия. ВсС это Π±ΡƒΠ΄Π΅Ρ‚ Π²ΠΈΠ΄Π½ΠΎ Π² Π±Ρ€Π°ΡƒΠ·Π΅Ρ€Π΅ Π² 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"]
}

НаправитС ΠΏΡ€Π°Π·Π°Π½ Π±Π°Ρ†ΠΊΠ³Ρ€ΠΎΡƒΠ½Π΄.јс, ΠΏΠΎΠΏΡƒΠΏ.јс, ΠΈΠ½ΠΏΠ°Π³Π΅.јс ΠΈ цонтСнтсцрипт.јс. Π”ΠΎΠ΄Π°ΠΌΠΎ ΠΏΠΎΠΏΡƒΠΏ.Ρ…Ρ‚ΠΌΠ» - ΠΈ наша Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π° сС Π²Π΅Ρ› ΠΌΠΎΠΆΠ΅ ΡƒΡ‡ΠΈΡ‚Π°Ρ‚ΠΈ Ρƒ Π“ΠΎΠΎΠ³Π»Π΅ Π¦Ρ…Ρ€ΠΎΠΌΠ΅ ΠΈ ΡƒΠ²Π΅Ρ€ΠΈΡ‚ΠΈ сС Π΄Π° Ρ€Π°Π΄ΠΈ.

Π”Π° бистС Ρ‚ΠΎ ΠΏΠΎΡ‚Π²Ρ€Π΄ΠΈΠ»ΠΈ, ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΡƒΠ·Π΅Ρ‚ΠΈ ΠΊΠΎΠ΄ стога. ΠŸΠΎΡ€Π΅Π΄ ΠΎΠ½ΠΎΠ³Π° ΡˆΡ‚ΠΎ смо ΡƒΡ€Π°Π΄ΠΈΠ»ΠΈ, Π»ΠΈΠ½ΠΊ јС конфигурисао ΠΌΠΎΠ½Ρ‚Π°ΠΆΡƒ ΠΏΡ€ΠΎΡ˜Π΅ΠΊΡ‚Π° ΠΏΠΎΠΌΠΎΡ›Ρƒ Π²Π΅Π± ΠΏΠ°ΠΊΠ΅Ρ‚Π°. Π”Π° бистС Π΄ΠΎΠ΄Π°Π»ΠΈ Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Ρƒ Ρƒ ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡, Ρƒ Ρ†Ρ…Ρ€ΠΎΠΌΠ΅://СктСнсионс ΠΌΠΎΡ€Π°Ρ‚Π΅ Π΄Π° ΠΈΠ·Π°Π±Π΅Ρ€Π΅Ρ‚Π΅ Π»ΠΎΠ°Π΄ ΡƒΠ½ΠΏΠ°Ρ†ΠΊΠ΅Π΄ ΠΈ Ρ„ΠΎΠ»Π΄Π΅Ρ€ са ΠΎΠ΄Π³ΠΎΠ²Π°Ρ€Π°Ρ˜ΡƒΡ›ΠΎΠΌ Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜ΠΎΠΌ - Ρƒ нашСм ΡΠ»ΡƒΡ‡Π°Ρ˜Ρƒ дист.

ПисањС Π±Π΅Π·Π±Π΅Π΄Π½ΠΎΠ³ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°

Π‘Π°Π΄Π° јС наша Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Π° инсталирана ΠΈ Ρ€Π°Π΄ΠΈ. ΠœΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΠΎΠΊΡ€Π΅Π½ΡƒΡ‚ΠΈ Π°Π»Π°Ρ‚ΠΊΠ΅ Π·Π° ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠ΅Ρ€Π΅ Π·Π° Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΡ‚Π΅ контСкстС Π½Π° слСдСћи Π½Π°Ρ‡ΠΈΠ½:

ΠΏΠΎΠΏΡƒΠΏ ->

ПисањС Π±Π΅Π·Π±Π΅Π΄Π½ΠΎΠ³ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°

ΠŸΡ€ΠΈΡΡ‚ΡƒΠΏ ΠΊΠΎΠ½Π·ΠΎΠ»ΠΈ скриптС ΡΠ°Π΄Ρ€ΠΆΠ°Ρ˜Π° Π²Ρ€ΡˆΠΈ сС ΠΏΡ€Π΅ΠΊΠΎ ΠΊΠΎΠ½Π·ΠΎΠ»Π΅ самС страницС Π½Π° којој јС ΠΏΠΎΠΊΡ€Π΅Π½ΡƒΡ‚Π°.ПисањС Π±Π΅Π·Π±Π΅Π΄Π½ΠΎΠ³ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°

Π Π°Π·ΠΌΠ΅Π½Π° ΠΏΠΎΡ€ΡƒΠΊΠ°

Π”Π°ΠΊΠ»Π΅, ΠΌΠΎΡ€Π°ΠΌΠΎ успоставити Π΄Π²Π° ΠΊΠ°Π½Π°Π»Π° ΠΊΠΎΠΌΡƒΠ½ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π΅: ΠΏΠΎΠ·Π°Π΄ΠΈΠ½Ρƒ ΠΈΠ½ΠΏΠ°Π³Π΅ <-> ΠΈ ΠΏΠΎΠ·Π°Π΄ΠΈΠ½Ρƒ искачућСг ΠΏΡ€ΠΎΠ·ΠΎΡ€Π°. ΠœΠΎΠΆΠ΅Ρ‚Π΅, Π½Π°Ρ€Π°Π²Π½ΠΎ, само слати ΠΏΠΎΡ€ΡƒΠΊΠ΅ Π½Π° ΠΏΠΎΡ€Ρ‚ ΠΈ измислити сопствСни ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ», Π°Π»ΠΈ ја вишС Π²ΠΎΠ»ΠΈΠΌ приступ који сам Π²ΠΈΠ΄Π΅ΠΎ Ρƒ мСтамаск ΠΏΡ€ΠΎΡ˜Π΅ΠΊΡ‚Ρƒ ΠΎΡ‚Π²ΠΎΡ€Π΅Π½ΠΎΠ³ ΠΊΠΎΠ΄Π°.

Ово јС ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ΅ ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π° Π·Π° Ρ€Π°Π΄ са Π•Ρ‚Ρ…Π΅Ρ€Π΅ΡƒΠΌ ΠΌΡ€Π΅ΠΆΠΎΠΌ. Π£ ΡšΠ΅ΠΌΡƒ Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΡ‚ΠΈ Π΄Π΅Π»ΠΎΠ²ΠΈ Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π΅ ΠΊΠΎΠΌΡƒΠ½ΠΈΡ†ΠΈΡ€Π°Ρ˜Ρƒ ΠΏΡ€Π΅ΠΊΠΎ РПЦ-Π° користСћи Π΄Π½ΠΎΠ΄Π΅ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΡƒ. ΠžΠΌΠΎΠ³ΡƒΡ›Π°Π²Π° Π²Π°ΠΌ Π΄Π° ΠΎΡ€Π³Π°Π½ΠΈΠ·ΡƒΡ˜Π΅Ρ‚Π΅ Ρ€Π°Π·ΠΌΠ΅Π½Ρƒ ΠΏΡ€ΠΈΠ»ΠΈΡ‡Π½ΠΎ Π±Ρ€Π·ΠΎ ΠΈ Π·Π³ΠΎΠ΄Π½ΠΎ Π°ΠΊΠΎ јој ΠΎΠ±Π΅Π·Π±Π΅Π΄ΠΈΡ‚Π΅ нодСјс стрСам ΠΊΠ°ΠΎ транспорт (ΡˆΡ‚ΠΎ Π·Π½Π°Ρ‡ΠΈ ΠΎΠ±Ρ˜Π΅ΠΊΠ°Ρ‚ који ΠΈΠΌΠΏΠ»Π΅ΠΌΠ΅Π½Ρ‚ΠΈΡ€Π° исти ΠΈΠ½Ρ‚Π΅Ρ€Ρ„Π΅Ρ˜Ρ):

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)))
})

Π‘Π°Π΄Π° Ρ›Π΅ΠΌΠΎ ΠΊΡ€Π΅ΠΈΡ€Π°Ρ‚ΠΈ класу Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π΅. Он Ρ›Π΅ ΠΊΡ€Π΅ΠΈΡ€Π°Ρ‚ΠΈ АПИ ΠΎΠ±Ρ˜Π΅ΠΊΡ‚Π΅ Π·Π° искачућу ΠΈ Π²Π΅Π± страницу ΠΈ ΠΊΡ€Π΅ΠΈΡ€Π°Ρ‚ΠΈ Π΄Π½ΠΎΠ΄Π΅ Π·Π° ΡšΠΈΡ…:

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)
        })
    }
}

ОвдС ΠΈ испод, умСсто Π³Π»ΠΎΠ±Π°Π»Π½ΠΎΠ³ Π¦Ρ…Ρ€ΠΎΠΌΠ΅ ΠΎΠ±Ρ˜Π΅ΠΊΡ‚Π°, користимо Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Ρƒ Апи, која приступа Π¦Ρ…Ρ€ΠΎΠΌΠ΅-Ρƒ Ρƒ Π“ΠΎΠΎΠ³Π»Π΅-ΠΎΠ²ΠΎΠΌ ΠΏΡ€Π΅Π³Π»Π΅Π΄Π°Ρ‡Ρƒ ΠΈ ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Ρƒ Ρƒ Π΄Ρ€ΡƒΠ³ΠΈΠΌ. Ово јС ΡƒΡ€Π°Ρ’Π΅Π½ΠΎ Ρ€Π°Π΄ΠΈ компатибилности са Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΡ‚ΠΈΠΌ ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡ΠΈΠΌΠ°, Π°Π»ΠΈ Π·Π° ΠΏΠΎΡ‚Ρ€Π΅Π±Π΅ ΠΎΠ²ΠΎΠ³ Ρ‡Π»Π°Π½ΠΊΠ° ΠΌΠΎΠΆΠ΅ сС Ρ˜Π΅Π΄Π½ΠΎΡΡ‚Π°Π²Π½ΠΎ користити 'Ρ†Ρ…Ρ€ΠΎΠΌΠ΅.Ρ€ΡƒΠ½Ρ‚ΠΈΠΌΠ΅.Ρ†ΠΎΠ½Π½Π΅Ρ†Ρ‚'.

Π₯ајдС Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌΠΎ инстанцу Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π΅ Ρƒ скрипти Ρƒ ΠΏΠΎΠ·Π°Π΄ΠΈΠ½ΠΈ:

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)
    }
}

ΠŸΠΎΡˆΡ‚ΠΎ Π΄Π½ΠΎΠ΄Π΅ Ρ€Π°Π΄ΠΈ са стрСамовима, Π° ΠΌΠΈ добијамо ΠΏΠΎΡ€Ρ‚, ΠΏΠΎΡ‚Ρ€Π΅Π±Π½Π° јС класа Π°Π΄Π°ΠΏΡ‚Π΅Ρ€Π°. НаправљСн јС ΠΏΠΎΠΌΠΎΡ›Ρƒ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ΅ Ρ€Π΅Π°Π΄Π°Π±Π»Π΅-стрСам, која ΠΈΠΌΠΏΠ»Π΅ΠΌΠ΅Π½Ρ‚ΠΈΡ€Π° нодСјс стрСамовС Ρƒ ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Ρƒ:

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()
    }
}

Π‘Π°Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΠΌΠΎ Π²Π΅Π·Ρƒ Ρƒ корисничком ΠΈΠ½Ρ‚Π΅Ρ€Ρ„Π΅Ρ˜ΡΡƒ:

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);
    }
}

ΠŸΠΎΡˆΡ‚ΠΎ Π½Π°ΠΌ АПИ нијС ΠΏΠΎΡ‚Ρ€Π΅Π±Π°Π½ Ρƒ скрипти ΡΠ°Π΄Ρ€ΠΆΠ°Ρ˜Π°, Π²Π΅Ρ› Π΄ΠΈΡ€Π΅ΠΊΡ‚Π½ΠΎ Π½Π° страници, Ρ€Π°Π΄ΠΈΠΌΠΎ Π΄Π²Π΅ ствари:

  1. ΠŸΡ€Π°Π²ΠΈΠΌΠΎ Π΄Π²Π° Ρ‚ΠΎΠΊΠ°. ЈСдан - ΠΏΡ€Π΅ΠΌΠ° страници, Π½Π° Π²Ρ€Ρ…Ρƒ ΠΏΠΎΡ€ΡƒΠΊΠ΅. Π—Π° ΠΎΠ²ΠΎ користимо ΠΎΠ²ΠΎ овај ΠΏΠ°ΠΊΠ΅Ρ‚ ΠΎΠ΄ ΠΊΡ€Π΅Π°Ρ‚ΠΎΡ€Π° мСтамаскС. Π”Ρ€ΡƒΠ³ΠΈ Ρ‚ΠΎΠΊ јС Ρƒ ΠΏΠΎΠ·Π°Π΄ΠΈΠ½ΠΈ ΠΏΡ€Π΅ΠΊΠΎ ΠΏΡ€ΠΈΠΌΡ™Π΅Π½ΠΎΠ³ ΠΏΠΎΡ€Ρ‚Π° runtime.connect. Π₯ајдС Π΄Π° ΠΈΡ… ΠΊΡƒΠΏΠΈΠΌΠΎ. Π‘Π°Π΄Π° Ρ›Π΅ страница ΠΈΠΌΠ°Ρ‚ΠΈ Ρ‚ΠΎΠΊ Ρƒ ΠΏΠΎΠ·Π°Π΄ΠΈΠ½ΠΈ.
  2. Π£Π±Π°Ρ†ΠΈΡ‚Π΅ скрипту Ρƒ Π”ΠžΠœ. ΠŸΡ€Π΅ΡƒΠ·ΠΌΠΈΡ‚Π΅ скрипту (приступ јој јС Π±ΠΈΠΎ Π΄ΠΎΠ·Π²ΠΎΡ™Π΅Π½ Ρƒ манифСсту) ΠΈ ΠΊΡ€Π΅ΠΈΡ€Π°Ρ˜Ρ‚Π΅ ΠΎΠ·Π½Π°ΠΊΡƒ 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);
    }
}

Π‘Π°Π΄Π° ΠΊΡ€Π΅ΠΈΡ€Π°ΠΌΠΎ Π°ΠΏΠΈ ΠΎΠ±Ρ˜Π΅ΠΊΠ°Ρ‚ Ρƒ ΠΈΠ½ΠΏΠ°Π³Π΅ ΠΈ постављамо Π³Π° Π½Π° Π³Π»ΠΎΠ±Π°Π»Π½ΠΎ:

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;
}

Ми смо спрСмни Позив ΡƒΠ΄Π°Ρ™Π΅Π½Π΅ ΠΏΡ€ΠΎΡ†Π΅Π΄ΡƒΡ€Π΅ (РПЦ) са засСбним АПИ-јСм Π·Π° страницу ΠΈ кориснички ΠΈΠ½Ρ‚Π΅Ρ€Ρ„Π΅Ρ˜Ρ. Када ΠΏΠΎΠ²Π΅ΠΆΠ΅Ρ‚Π΅ Π½ΠΎΠ²Ρƒ страницу са ΠΏΠΎΠ·Π°Π΄ΠΈΠ½ΠΎΠΌ, ΠΌΠΎΠΆΠ΅ΠΌΠΎ Π²ΠΈΠ΄Π΅Ρ‚ΠΈ ΠΎΠ²ΠΎ:

ПисањС Π±Π΅Π·Π±Π΅Π΄Π½ΠΎΠ³ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°

ΠŸΡ€Π°Π·Π°Π½ АПИ ΠΈ ΠΏΠΎΡ€Π΅ΠΊΠ»ΠΎ. На страни страницС, Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡ˜Ρƒ Ρ…Π΅Π»Π»ΠΎ ΠΌΠΎΠΆΠ΅ΠΌΠΎ ΠΏΠΎΠ·Π²Π°Ρ‚ΠΈ ΠΎΠ²Π°ΠΊΠΎ:

ПисањС Π±Π΅Π·Π±Π΅Π΄Π½ΠΎΠ³ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°

Π Π°Π΄ са Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡ˜Π°ΠΌΠ° ΠΏΠΎΠ²Ρ€Π°Ρ‚Π½ΠΎΠ³ ΠΏΠΎΠ·ΠΈΠ²Π° Ρƒ ΠΌΠΎΠ΄Π΅Ρ€Π½ΠΎΠΌ ЈБ-Ρƒ јС лош ΠΌΠ°Π½ΠΈΡ€ΠΈ, ΠΏΠ° Ρ…Π°Ρ˜Π΄Π΅ Π΄Π° напишСмо ΠΌΠ°Π»ΠΈ ΠΏΠΎΠΌΠΎΡ›Π½ΠΈΠΊ Π·Π° ΠΊΡ€Π΅ΠΈΡ€Π°ΡšΠ΅ Π΄Π½ΠΎΠ΄Π΅ који Π²Π°ΠΌ ΠΎΠΌΠΎΠ³ΡƒΡ›Π°Π²Π° Π΄Π° прослСдитС АПИ ΠΎΠ±Ρ˜Π΅ΠΊΠ°Ρ‚ ΡƒΡ‚ΠΈΠ»ΠΈΠΌΠ°.

АПИ ΠΎΠ±Ρ˜Π΅ΠΊΡ‚ΠΈ Ρ›Π΅ сада ΠΈΠ·Π³Π»Π΅Π΄Π°Ρ‚ΠΈ ΠΎΠ²Π°ΠΊΠΎ:

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))
    })
});

А позивањС Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡ˜Π° Π²Ρ€Π°Ρ›Π° ΠΎΠ±Π΅Ρ›Π°ΡšΠ΅:

ПисањС Π±Π΅Π·Π±Π΅Π΄Π½ΠΎΠ³ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°

Доступна Π²Π΅Ρ€Π·ΠΈΡ˜Π° са асинхроним Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡ˜Π°ΠΌΠ° ΠΎΠ²Π΄Π΅.

Π‘Π²Π΅ Ρƒ свСму, приступ РПЦ-Π° ΠΈ стрима ΠΈΠ·Π³Π»Π΅Π΄Π° ΠΏΡ€ΠΈΠ»ΠΈΡ‡Π½ΠΎ флСксибилан: ΠΌΠΎΠΆΠ΅ΠΌΠΎ користити ΠΏΠ°Ρ€Π½ΠΎ ΠΌΡƒΠ»Ρ‚ΠΈΠΏΠ»Π΅ΠΊΡΠΈΡ€Π°ΡšΠ΅ ΠΈ ΠΊΡ€Π΅ΠΈΡ€Π°Ρ‚ΠΈ Π½Π΅ΠΊΠΎΠ»ΠΈΠΊΠΎ Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΡ‚ΠΈΡ… АПИ-ја Π·Π° Ρ€Π°Π·Π»ΠΈΡ‡ΠΈΡ‚Π΅ Π·Π°Π΄Π°Ρ‚ΠΊΠ΅. Π£ ΠΏΡ€ΠΈΠ½Ρ†ΠΈΠΏΡƒ, Π΄Π½ΠΎΠ΄Π΅ сС ΠΌΠΎΠΆΠ΅ користити Π±ΠΈΠ»ΠΎ Π³Π΄Π΅, Π³Π»Π°Π²Π½Π° ствар јС Π΄Π° сС транспорт ΠΎΠΌΠΎΡ‚Π° Ρƒ ΠΎΠ±Π»ΠΈΠΊΡƒ нодСјс Ρ‚ΠΎΠΊΠ°.

АлтСрнатива јС ЈБОН Ρ„ΠΎΡ€ΠΌΠ°Ρ‚, који ΠΈΠΌΠΏΠ»Π΅ΠΌΠ΅Π½Ρ‚ΠΈΡ€Π° ΠΏΡ€ΠΎΡ‚ΠΎΠΊΠΎΠ» ЈБОН РПЦ 2. ΠœΠ΅Ρ’ΡƒΡ‚ΠΈΠΌ, Ρ€Π°Π΄ΠΈ са спСцифичним транспортима (ВЦП ΠΈ Π₯ВВП(Π‘)), ΡˆΡ‚ΠΎ Ρƒ нашСм ΡΠ»ΡƒΡ‡Π°Ρ˜Ρƒ нијС ΠΏΡ€ΠΈΠΌΠ΅Π½Ρ™ΠΈΠ²ΠΎ.

Π˜Π½Ρ‚Π΅Ρ€Π½ΠΎ ΡΡ‚Π°ΡšΠ΅ ΠΈ Π»ΠΎΠΊΠ°Π»Π½ΠΎ ΡΠΊΠ»Π°Π΄ΠΈΡˆΡ‚Π΅

ΠœΠΎΡ€Π°Ρ›Π΅ΠΌΠΎ Π΄Π° сачувамо ΠΈΠ½Ρ‚Π΅Ρ€Π½ΠΎ ΡΡ‚Π°ΡšΠ΅ Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π΅ - Π±Π°Ρ€Π΅ΠΌ ΠΊΡ™ΡƒΡ‡Π΅Π²Π΅ Π·Π° ΠΏΠΎΡ‚ΠΏΠΈΡΠΈΠ²Π°ΡšΠ΅. МоТСмо ΠΏΡ€ΠΈΠ»ΠΈΡ‡Π½ΠΎ Π»Π°ΠΊΠΎ Π΄ΠΎΠ΄Π°Ρ‚ΠΈ ΡΡ‚Π°ΡšΠ΅ Ρƒ Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Ρƒ ΠΈ ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅ Π·Π° ΡšΠ΅Π³ΠΎΠ²Ρƒ ΠΏΡ€ΠΎΠΌΠ΅Π½Ρƒ Ρƒ искачућСм АПИ-Ρ˜Ρƒ:

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)
        }
    }
}

Π₯ајдС Π΄Π° Π΄ΠΎΠ΄Π°ΠΌΠΎ Π½Π΅ΠΊΠΎΠ»ΠΈΠΊΠΎ ΠΊΡ™ΡƒΡ‡Π΅Π²Π° са УИ ΠΊΠΎΠ½Π·ΠΎΠ»Π΅ ΠΈ Π²ΠΈΠ΄ΠΈΠΌΠΎ ΡˆΡ‚Π° сС дСшава са ΡΡ‚Π°ΡšΠ΅ΠΌ:

ПисањС Π±Π΅Π·Π±Π΅Π΄Π½ΠΎΠ³ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°

Π‘Ρ‚Π°ΡšΠ΅ Ρ‚Ρ€Π΅Π±Π° ΡƒΡ‡ΠΈΠ½ΠΈΡ‚ΠΈ ΠΏΠΎΡΡ‚ΠΎΡ˜Π°Π½ΠΈΠΌ ΠΊΠ°ΠΊΠΎ сС ΠΊΡ™ΡƒΡ‡Π΅Π²ΠΈ Π½Π΅ Π±ΠΈ ΠΈΠ·Π³ΡƒΠ±ΠΈΠ»ΠΈ ΠΏΡ€ΠΈΠ»ΠΈΠΊΠΎΠΌ ΠΏΠΎΠ½ΠΎΠ²Π½ΠΎΠ³ ΠΏΠΎΠΊΡ€Π΅Ρ‚Π°ΡšΠ°.

Π‘Π°Ρ‡ΡƒΠ²Π°Ρ›Π΅ΠΌΠΎ Π³Π° Ρƒ Π»ΠΎΡ†Π°Π»Π‘Ρ‚ΠΎΡ€Π°Π³Π΅, Π·Π°ΠΌΠ΅ΡšΡƒΡ˜ΡƒΡ›ΠΈ Π³Π° са сваком ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΎΠΌ. Након Ρ‚ΠΎΠ³Π°, приступ Ρ›Π΅ ΠΌΡƒ Ρ‚Π°ΠΊΠΎΡ’Π΅ Π±ΠΈΡ‚ΠΈ Π½Π΅ΠΎΠΏΡ…ΠΎΠ΄Π°Π½ Π·Π° кориснички ΠΈΠ½Ρ‚Π΅Ρ€Ρ„Π΅Ρ˜Ρ, Π° Ρ‚Π°ΠΊΠΎΡ’Π΅ Π±ΠΈΡ… ΠΆΠ΅Π»Π΅ΠΎ Π΄Π° сС ΠΏΡ€Π΅Ρ‚ΠΏΠ»Π°Ρ‚ΠΈΠΌ Π½Π° ΠΏΡ€ΠΎΠΌΠ΅Π½Π΅. На основу ΠΎΠ²ΠΎΠ³Π°, Π±ΠΈΡ›Π΅ Π·Π³ΠΎΠ΄Π½ΠΎ ΠΊΡ€Π΅ΠΈΡ€Π°Ρ‚ΠΈ Π²ΠΈΠ΄Ρ™ΠΈΠ²ΠΎ ΡΠΊΠ»Π°Π΄ΠΈΡˆΡ‚Π΅ ΠΈ ΠΏΡ€Π΅Ρ‚ΠΏΠ»Π°Ρ‚ΠΈΡ‚ΠΈ сС Π½Π° њСговС ΠΏΡ€ΠΎΠΌΠ΅Π½Π΅.

ΠšΠΎΡ€ΠΈΡΡ‚ΠΈΡ›Π΅ΠΌΠΎ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΡƒ ΠΌΠΎΠ±ΠΊ (https://github.com/mobxjs/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)
    }

    ...

}

β€žΠ˜ΡΠΏΠΎΠ΄ Ρ…Π°ΡƒΠ±Π΅β€œ, ΠΌΠΎΠ±ΠΊ јС Π·Π°ΠΌΠ΅Π½ΠΈΠΎ сва ΠΏΠΎΡ™Π° ΠΏΡ€ΠΎΠ΄Π°Π²Π½ΠΈΡ†Π΅ ΠΏΡ€ΠΎΠΊΡΠΈΡ˜Π΅ΠΌ ΠΈ прСсрСћС свС ΠΏΠΎΠ·ΠΈΠ²Π΅ ΠΊΠ° њима. Π‘ΠΈΡ›Π΅ ΠΌΠΎΠ³ΡƒΡ›Π΅ ΠΏΡ€Π΅Ρ‚ΠΏΠ»Π°Ρ‚ΠΈΡ‚ΠΈ сС Π½Π° ΠΎΠ²Π΅ ΠΏΠΎΡ€ΡƒΠΊΠ΅.

Π£ наставку Ρ›Ρƒ чСсто користити ΠΈΠ·Ρ€Π°Π· β€žΠΏΡ€ΠΈ ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈβ€œ, ΠΈΠ°ΠΊΠΎ Ρ‚ΠΎ нијС сасвим Ρ‚Π°Ρ‡Π½ΠΎ. Мобк ΠΏΡ€Π°Ρ‚ΠΈ приступ ΠΏΠΎΡ™ΠΈΠΌΠ°. ΠšΠΎΡ€ΠΈΡΡ‚Π΅ сС Π³Π΅Ρ‚Ρ‚Π΅Ρ€ΠΈ ΠΈ сСттСри ΠΏΡ€ΠΎΠΊΠΈ ΠΎΠ±Ρ˜Π΅ΠΊΠ°Ρ‚Π° којС ΠΊΡ€Π΅ΠΈΡ€Π° Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΠ°.

Акциони Π΄Π΅ΠΊΠΎΡ€Π°Ρ‚Π΅Ρ€ΠΈ ΠΈΠΌΠ°Ρ˜Ρƒ Π΄Π²Π΅ сврхС:

  1. Π£ строгом Ρ€Π΅ΠΆΠΈΠΌΡƒ са заставицом СнфорцСАцтионс, ΠΌΠΎΠ±ΠΊ Π·Π°Π±Ρ€Π°ΡšΡƒΡ˜Π΅ Π΄ΠΈΡ€Π΅ΠΊΡ‚Π½Ρƒ ΠΏΡ€ΠΎΠΌΠ΅Π½Ρƒ ΡΡ‚Π°ΡšΠ°. Π‘ΠΌΠ°Ρ‚Ρ€Π° сС Π΄Π° јС Π΄ΠΎΠ±Ρ€Π° пракса Ρ€Π°Π΄ΠΈΡ‚ΠΈ ΠΏΠΎΠ΄ строгим условима.
  2. Π§Π°ΠΊ ΠΈ Π°ΠΊΠΎ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡ˜Π° ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈ ΡΡ‚Π°ΡšΠ΅ Π½Π΅ΠΊΠΎΠ»ΠΈΠΊΠΎ ΠΏΡƒΡ‚Π° – Π½Π° ΠΏΡ€ΠΈΠΌΠ΅Ρ€, ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈΠΌΠΎ Π½Π΅ΠΊΠΎΠ»ΠΈΠΊΠΎ ΠΏΠΎΡ™Π° Ρƒ Π½Π΅ΠΊΠΎΠ»ΠΈΠΊΠΎ Ρ€Π΅Π΄ΠΎΠ²Π° ΠΊΠΎΠ΄Π° – посматрачи Π΄ΠΎΠ±ΠΈΡ˜Π°Ρ˜Ρƒ ΠΎΠ±Π°Π²Π΅ΡˆΡ‚Π΅ΡšΠ΅ Ρ‚Π΅ΠΊ ΠΊΠ°Π΄Π° сС Π·Π°Π²Ρ€ΡˆΠΈ. Ово јС посСбно Π²Π°ΠΆΠ½ΠΎ Π·Π° Ρ„Ρ€ΠΎΠ½Ρ‚Π΅Π½Π΄, Π³Π΄Π΅ Π½Π΅ΠΏΠΎΡ‚Ρ€Π΅Π±Π½Π° Π°ΠΆΡƒΡ€ΠΈΡ€Π°ΡšΠ° ΡΡ‚Π°ΡšΠ° Π΄ΠΎΠ²ΠΎΠ΄Π΅ Π΄ΠΎ Π½Π΅ΠΏΠΎΡ‚Ρ€Π΅Π±Π½ΠΎΠ³ ΠΏΡ€ΠΈΠΊΠ°Π·ΠΈΠ²Π°ΡšΠ° Π΅Π»Π΅ΠΌΠ΅Π½Π°Ρ‚Π°. Π£ нашСм ΡΠ»ΡƒΡ‡Π°Ρ˜Ρƒ, Π½ΠΈ ΠΏΡ€Π²ΠΎ Π½ΠΈ Π΄Ρ€ΡƒΠ³ΠΎ нису посСбно Ρ€Π΅Π»Π΅Π²Π°Π½Ρ‚Π½ΠΈ, Π°Π»ΠΈ Ρ›Π΅ΠΌΠΎ слСдити Π½Π°Ρ˜Π±ΠΎΡ™Ρƒ праксу. Π£ΠΎΠ±ΠΈΡ‡Π°Ρ˜Π΅Π½ΠΎ јС Π΄Π° сС свим Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡ˜Π°ΠΌΠ° којС ΠΌΠ΅ΡšΠ°Ρ˜Ρƒ ΡΡ‚Π°ΡšΠ΅ посматраних ΠΏΠΎΡ™Π° Π΄ΠΎΠ΄Π°Ρ˜Ρƒ Π΄Π΅ΠΊΠΎΡ€Π°Ρ‚ΠΎΡ€ΠΈ.

Π£ ΠΏΠΎΠ·Π°Π΄ΠΈΠ½ΠΈ Ρ›Π΅ΠΌΠΎ Π΄ΠΎΠ΄Π°Ρ‚ΠΈ ΠΈΠ½ΠΈΡ†ΠΈΡ˜Π°Π»ΠΈΠ·Π°Ρ†ΠΈΡ˜Ρƒ ΠΈ Ρ‡ΡƒΠ²Π°ΡšΠ΅ ΡΡ‚Π°ΡšΠ° Ρƒ Π»ΠΎΡ†Π°Π»Π‘Ρ‚ΠΎΡ€Π°Π³Π΅:

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)
        }
    }
}

ОвдС јС Π·Π°Π½ΠΈΠΌΡ™ΠΈΠ²Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡ˜Π° Ρ€Π΅Π°ΠΊΡ†ΠΈΡ˜Π΅. Има Π΄Π²Π° Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚Π°:

  1. Π‘ΠΈΡ€Π°Ρ‡ ΠΏΠΎΠ΄Π°Ρ‚Π°ΠΊΠ°.
  2. Π ΡƒΠΊΠΎΠ²Π°Π»Π°Ρ† који Ρ›Π΅ Π±ΠΈΡ‚ΠΈ ΠΏΠΎΠ·Π²Π°Π½ са ΠΎΠ²ΠΈΠΌ ΠΏΠΎΠ΄Π°Ρ†ΠΈΠΌΠ° сваки ΠΏΡƒΡ‚ ΠΊΠ°Π΄Π° сС ΠΏΡ€ΠΎΠΌΠ΅Π½Π΅.

Π—Π° Ρ€Π°Π·Π»ΠΈΠΊΡƒ ΠΎΠ΄ Ρ€Π΅Π΄ΡƒΠΊ-Π°, Π³Π΄Π΅ ΠΌΠΈ Сксплицитно ΠΏΡ€ΠΈΠΌΠ°ΠΌΠΎ ΡΡ‚Π°ΡšΠ΅ ΠΊΠ°ΠΎ Π°Ρ€Π³ΡƒΠΌΠ΅Π½Ρ‚, ΠΌΠΎΠ±ΠΊ ΠΏΠ°ΠΌΡ‚ΠΈ којим посматрачима приступамо ΡƒΠ½ΡƒΡ‚Π°Ρ€ сСлСктора ΠΈ ΠΏΠΎΠ·ΠΈΠ²Π° Ρ€ΡƒΠΊΠΎΠ²Π°Π»Π°Ρ† само ΠΊΠ°Π΄Π° сС ΠΏΡ€ΠΎΠΌΠ΅Π½Π΅.

Π’Π°ΠΆΠ½ΠΎ јС Ρ‚Π°Ρ‡Π½ΠΎ Ρ€Π°Π·ΡƒΠΌΠ΅Ρ‚ΠΈ ΠΊΠ°ΠΊΠΎ ΠΌΠΎΠ±ΠΊ ΠΎΠ΄Π»ΡƒΡ‡ΡƒΡ˜Π΅ Π½Π° којС посматрачС сС ΠΏΡ€Π΅Ρ‚ΠΏΠ»Π°Ρ‚ΠΈΠΌΠΎ. Π”Π° сам написао сСлСктор Ρƒ ΠΎΠ²Π°ΠΊΠ²ΠΎΠΌ ΠΊΠΎΠ΄Ρƒ() => app.store, Ρ‚Π°Π΄Π° Ρ€Π΅Π°ΠΊΡ†ΠΈΡ˜Π° Π½ΠΈΠΊΠ°Π΄Π° Π½Π΅Ρ›Π΅ Π±ΠΈΡ‚ΠΈ ΠΏΠΎΠ·Π²Π°Π½Π°, ΠΏΠΎΡˆΡ‚ΠΎ само ΡΠΊΠ»Π°Π΄ΠΈΡˆΡ‚Π΅ нијС Π²ΠΈΠ΄Ρ™ΠΈΠ²ΠΎ, Π²Π΅Ρ› само њСгова ΠΏΠΎΡ™Π°.

Π”Π° сам ΠΎΠ²Π°ΠΊΠΎ написао () => app.store.keys, ΠΎΠ½Π΄Π° сС ΠΎΠΏΠ΅Ρ‚ Π½ΠΈΡˆΡ‚Π° Π½Π΅ Π±ΠΈ дСсило, ΠΏΠΎΡˆΡ‚ΠΎ сС ΠΏΡ€ΠΈΠ»ΠΈΠΊΠΎΠΌ додавања/ΡƒΠΊΠ»Π°ΡšΠ°ΡšΠ° Π΅Π»Π΅ΠΌΠ΅Π½Π°Ρ‚Π° Π½ΠΈΠ·Π° Ρ€Π΅Ρ„Π΅Ρ€Π΅Π½Ρ†Π° Π½Π° њСга Π½Π΅Ρ›Π΅ ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈΡ‚ΠΈ.

Мобк ΠΏΠΎ ΠΏΡ€Π²ΠΈ ΠΏΡƒΡ‚ Π΄Π΅Π»ΡƒΡ˜Π΅ ΠΊΠ°ΠΎ сСлСктор ΠΈ ΠΏΡ€Π°Ρ‚ΠΈ само посматранС врСдности којима смо приступили. Ово сС Ρ€Π°Π΄ΠΈ ΠΏΡ€Π΅ΠΊΠΎ ΠΏΡ€ΠΎΠΊΠΈ Π³Π΅Ρ‚Ρ‚Π΅Ρ€Π°. Π‘Ρ‚ΠΎΠ³Π° сС ΠΎΠ²Π΄Π΅ користи ΡƒΠ³Ρ€Π°Ρ’Π΅Π½Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡ˜Π° toJS. Π’Ρ€Π°Ρ›Π° Π½ΠΎΠ²ΠΈ ΠΎΠ±Ρ˜Π΅ΠΊΠ°Ρ‚ са свим ΠΏΡ€ΠΎΠΊΡΠΈΡ˜ΠΈΠΌΠ° замСњСним ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π»Π½ΠΈΠΌ ΠΏΠΎΡ™ΠΈΠΌΠ°. Π’ΠΎΠΊΠΎΠΌ ΠΈΠ·Π²Ρ€ΡˆΠ°Π²Π°ΡšΠ°, Ρ‡ΠΈΡ‚Π° сва ΠΏΠΎΡ™Π° ΠΎΠ±Ρ˜Π΅ΠΊΡ‚Π° - стога сС ΠΏΠΎΠΊΡ€Π΅Ρ›Ρƒ Π³Π΅Ρ‚Ρ‚Π΅Ρ€ΠΈ.

Π£ ΠΈΡΠΊΠ°Ρ‡ΡƒΡ›ΠΎΡ˜ ΠΊΠΎΠ½Π·ΠΎΠ»ΠΈ Ρ›Π΅ΠΌΠΎ ΠΏΠΎΠ½ΠΎΠ²ΠΎ Π΄ΠΎΠ΄Π°Ρ‚ΠΈ Π½Π΅ΠΊΠΎΠ»ΠΈΠΊΠΎ ΠΊΡ™ΡƒΡ‡Π΅Π²Π°. Овај ΠΏΡƒΡ‚ су Ρ‚Π°ΠΊΠΎΡ’Π΅ Π·Π°Π²Ρ€ΡˆΠΈΠ»ΠΈ Ρƒ Π»ΠΎΡ†Π°Π»Π‘Ρ‚ΠΎΡ€Π°Π³Π΅:

ПисањС Π±Π΅Π·Π±Π΅Π΄Π½ΠΎΠ³ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°

Када сС позадинска страница ΠΏΠΎΠ½ΠΎΠ²ΠΎ ΡƒΡ‡ΠΈΡ‚Π°, ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡ˜Π΅ ΠΎΡΡ‚Π°Ρ˜Ρƒ Π½Π° мСсту.

Π‘Π²ΠΈ ΠΊΠΎΠ΄ΠΎΠ²ΠΈ Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π΅ Π΄ΠΎ ΠΎΠ²Π΅ Ρ‚Π°Ρ‡ΠΊΠ΅ сС ΠΌΠΎΠ³Ρƒ Π²ΠΈΠ΄Π΅Ρ‚ΠΈ ΠΎΠ²Π΄Π΅.

Π‘ΠΈΠ³ΡƒΡ€Π½ΠΎ ΡΠΊΠ»Π°Π΄ΠΈΡˆΡ‚Π΅ΡšΠ΅ ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½ΠΈΡ… ΠΊΡ™ΡƒΡ‡Π΅Π²Π°

Π§ΡƒΠ²Π°ΡšΠ΅ ΠΏΡ€ΠΈΠ²Π°Ρ‚Π½ΠΈΡ… ΠΊΡ™ΡƒΡ‡Π΅Π²Π° Ρƒ чистом тСксту нијС Π±Π΅Π·Π±Π΅Π΄Π½ΠΎ: ΡƒΠ²Π΅ΠΊ ΠΏΠΎΡΡ‚ΠΎΡ˜ΠΈ шанса Π΄Π° Π±ΡƒΠ΄Π΅Ρ‚Π΅ Ρ…Π°ΠΊΠΎΠ²Π°Π½ΠΈ, Π΄Π° Π΄ΠΎΠ±ΠΈΡ˜Π΅Ρ‚Π΅ приступ свом Ρ€Π°Ρ‡ΡƒΠ½Π°Ρ€Ρƒ ΠΈ Ρ‚Π°ΠΊΠΎ Π΄Π°Ρ™Π΅. Π‘Ρ‚ΠΎΠ³Π° Ρ›Π΅ΠΌΠΎ Ρƒ Π»ΠΎΡ†Π°Π»Π‘Ρ‚ΠΎΡ€Π°Π³Π΅ Ρ‡ΡƒΠ²Π°Ρ‚ΠΈ ΠΊΡ™ΡƒΡ‡Π΅Π²Π΅ Ρƒ ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½ΠΎΠΌ ΠΎΠ±Π»ΠΈΠΊΡƒ.

Π—Π° Π²Π΅Ρ›Ρƒ сигурност, Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜ΠΈ Ρ›Π΅ΠΌΠΎ Π΄ΠΎΠ΄Π°Ρ‚ΠΈ Π·Π°ΠΊΡ™ΡƒΡ‡Π°Π½ΠΎ ΡΡ‚Π°ΡšΠ΅ Ρƒ којСм ΡƒΠΎΠΏΡˆΡ‚Π΅ Π½Π΅Ρ›Π΅ Π±ΠΈΡ‚ΠΈ приступа ΠΊΡ™ΡƒΡ‡Π΅Π²ΠΈΠΌΠ°. Аутоматски Ρ›Π΅ΠΌΠΎ ΠΏΡ€Π΅Π±Π°Ρ†ΠΈΡ‚ΠΈ Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Ρƒ Ρƒ Π·Π°ΠΊΡ™ΡƒΡ‡Π°Π½ΠΎ ΡΡ‚Π°ΡšΠ΅ Π·Π±ΠΎΠ³ истСка Π²Ρ€Π΅ΠΌΠ΅Π½Π°.

Мобк Π²Π°ΠΌ ΠΎΠΌΠΎΠ³ΡƒΡ›Π°Π²Π° Π΄Π° сачуватС само ΠΌΠΈΠ½ΠΈΠΌΠ°Π»Π½ΠΈ скуп ΠΏΠΎΠ΄Π°Ρ‚Π°ΠΊΠ°, Π° остатак сС аутоматски ΠΈΠ·Ρ€Π°Ρ‡ΡƒΠ½Π°Π²Π° Π½Π° основу њСга. Π’ΠΎ су Ρ‚Π°ΠΊΠΎΠ·Π²Π°Π½Π° ΠΈΠ·Ρ€Π°Ρ‡ΡƒΠ½Π°Ρ‚Π° ΡΠ²ΠΎΡ˜ΡΡ‚Π²Π°. Они сС ΠΌΠΎΠ³Ρƒ ΡƒΠΏΠΎΡ€Π΅Π΄ΠΈΡ‚ΠΈ са ΠΏΡ€ΠΈΠΊΠ°Π·ΠΈΠΌΠ° Ρƒ Π±Π°Π·Π°ΠΌΠ° ΠΏΠΎΠ΄Π°Ρ‚Π°ΠΊΠ°:

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')
        }
    }
}

Π‘Π°Π΄Π° Ρ‡ΡƒΠ²Π°ΠΌΠΎ само ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°Π½Π΅ ΠΊΡ™ΡƒΡ‡Π΅Π²Π΅ ΠΈ Π»ΠΎΠ·ΠΈΠ½ΠΊΡƒ. Π‘Π²Π΅ остало јС ΠΏΡ€ΠΎΡ€Π°Ρ‡ΡƒΠ½Π°Ρ‚ΠΎ. Π’Ρ€ΡˆΠΈΠΌΠΎ прСнос Ρƒ Π·Π°ΠΊΡ™ΡƒΡ‡Π°Π½ΠΎ ΡΡ‚Π°ΡšΠ΅ ΡƒΠΊΠ»Π°ΡšΠ°ΡšΠ΅ΠΌ Π»ΠΎΠ·ΠΈΠ½ΠΊΠ΅ ΠΈΠ· ΡΡ‚Π°ΡšΠ°. Јавни АПИ сада ΠΈΠΌΠ° ΠΌΠ΅Ρ‚ΠΎΠ΄ Π·Π° ΠΈΠ½ΠΈΡ†ΠΈΡ˜Π°Π»ΠΈΠ·Π°Ρ†ΠΈΡ˜Ρƒ ΡΠΊΠ»Π°Π΄ΠΈΡˆΡ‚Π°.

Написано Π·Π° ΡˆΠΈΡ„Ρ€ΠΎΠ²Π°ΡšΠ΅ услуТни ΠΏΡ€ΠΎΠ³Ρ€Π°ΠΌΠΈ који користС Ρ†Ρ€ΠΈΠΏΡ‚ΠΎ-јс:

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)
}

ΠŸΡ€Π΅Π³Π»Π΅Π΄Π°Ρ‡ ΠΈΠΌΠ° Π½Π΅Π°ΠΊΡ‚ΠΈΠ²Π½ΠΈ АПИ ΠΏΡ€Π΅ΠΊΠΎ којСг сС ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΡ€Π΅Ρ‚ΠΏΠ»Π°Ρ‚ΠΈΡ‚ΠΈ Π½Π° Π΄ΠΎΠ³Π°Ρ’Π°Ρ˜ - ΠΏΡ€ΠΎΠΌΠ΅Π½Π΅ ΡΡ‚Π°ΡšΠ°. Π”Ρ€ΠΆΠ°Π²Π°, сходно Ρ‚ΠΎΠΌΠ΅, ΠΌΠΎΠΆΠ΅ Π±ΠΈΡ‚ΠΈ idle, active ΠΈ locked. Π—Π° ΠΌΠΈΡ€ΠΎΠ²Π°ΡšΠ΅ ΠΌΠΎΠΆΠ΅Ρ‚Π΅ подСсити врСмСнско ΠΎΠ³Ρ€Π°Π½ΠΈΡ‡Π΅ΡšΠ΅, Π° Π·Π°ΠΊΡ™ΡƒΡ‡Π°Π½ΠΎ сС поставља ΠΊΠ°Π΄Π° јС сам ОБ Π±Π»ΠΎΠΊΠΈΡ€Π°Π½. Π’Π°ΠΊΠΎΡ’Π΅ Ρ›Π΅ΠΌΠΎ ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈΡ‚ΠΈ сСлСктор Π·Π° Ρ‡ΡƒΠ²Π°ΡšΠ΅ Ρƒ Π»ΠΎΡ†Π°Π»Π‘Ρ‚ΠΎΡ€Π°Π³Π΅:

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)
        }
    }
}

Код ΠΏΡ€Π΅ ΠΎΠ²ΠΎΠ³ ΠΊΠΎΡ€Π°ΠΊΠ° јС ΠΎΠ²Π΄Π΅.

Π’Ρ€Π°Π½ΡΠ°ΠΊΡ†ΠΈΡ˜Π°

Π”Π°ΠΊΠ»Π΅, Π΄ΠΎΠ»Π°Π·ΠΈΠΌΠΎ Π΄ΠΎ најваТнијС ствари: ΠΊΡ€Π΅ΠΈΡ€Π°ΡšΠ° ΠΈ ΠΏΠΎΡ‚ΠΏΠΈΡΠΈΠ²Π°ΡšΠ° Ρ‚Ρ€Π°Π½ΡΠ°ΠΊΡ†ΠΈΡ˜Π° Π½Π° Π±Π»ΠΎΠΊΡ‡Π΅Ρ˜Π½Ρƒ. ΠšΠΎΡ€ΠΈΡΡ‚ΠΈΡ›Π΅ΠΌΠΎ ВАВЕБ Π±Π»ΠΎΡ†ΠΊΡ†Ρ…Π°ΠΈΠ½ ΠΈ Π±ΠΈΠ±Π»ΠΈΠΎΡ‚Π΅ΠΊΡƒ таласи-Ρ‚Ρ€Π°Π½ΡΠ°ΠΊΡ†ΠΈΡ˜Π΅.

ΠŸΡ€Π²ΠΎ, додајмо ΡΡ‚Π°ΡšΡƒ Π½ΠΈΠ· ΠΏΠΎΡ€ΡƒΠΊΠ° којС Ρ‚Ρ€Π΅Π±Π° потписати, Π° Π·Π°Ρ‚ΠΈΠΌ Π΄ΠΎΠ΄Π°ΠΌΠΎ ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅ Π·Π° додавањС Π½ΠΎΠ²Π΅ ΠΏΠΎΡ€ΡƒΠΊΠ΅, ΠΏΠΎΡ‚Π²Ρ€Ρ’ΠΈΠ²Π°ΡšΠ΅ потписа ΠΈ одбијањС:

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 Ρ€ΡƒΡ‡Π½ΠΎ, ΠΎΠ½Π΄Π° Ρ›Π΅ ΠΌΠΎΠ±ΠΊ Ρ‚ΠΎ ΡƒΡ€Π°Π΄ΠΈΡ‚ΠΈ сам ΠΊΠ°Π΄Π° додајС ΠΏΠΎΡ€ΡƒΠΊΠ΅ Ρƒ Π½ΠΈΠ·. ΠœΠ΅Ρ’ΡƒΡ‚ΠΈΠΌ, ΠΊΡ€Π΅ΠΈΡ€Π°Ρ›Π΅ Π½ΠΎΠ²ΠΈ ΠΎΠ±Ρ˜Π΅ΠΊΠ°Ρ‚ Π½Π° који Π½Π΅Ρ›Π΅ΠΌΠΎ ΠΈΠΌΠ°Ρ‚ΠΈ Ρ€Π΅Ρ„Π΅Ρ€Π΅Π½Ρ†Ρƒ, Π°Π»ΠΈ Ρ›Π΅ Π½Π°ΠΌ Ρ‚Ρ€Π΅Π±Π°Ρ‚ΠΈ Π·Π° слСдСћи ΠΊΠΎΡ€Π°ΠΊ.

Π—Π°Ρ‚ΠΈΠΌ Π²Ρ€Π°Ρ›Π°ΠΌΠΎ ΠΎΠ±Π΅Ρ›Π°ΡšΠ΅ којС сС Ρ€Π΅ΡˆΠ°Π²Π° ΠΊΠ°Π΄Π° сС статус ΠΏΠΎΡ€ΡƒΠΊΠ΅ ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈ. Бтатус сС ΠΏΡ€Π°Ρ‚ΠΈ Ρ€Π΅Π°ΠΊΡ†ΠΈΡ˜ΠΎΠΌ, која Ρ›Π΅ сС β€žΡƒΠ±ΠΈΡ‚ΠΈβ€œ ΠΊΠ°Π΄Π° сС статус ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈ.

Π¨ΠΈΡ„Ρ€Π° ΠΌΠ΅Ρ‚ΠΎΠ΄Π΅ approve ΠΈ reject Π²Ρ€Π»ΠΎ Ρ˜Π΅Π΄Π½ΠΎΡΡ‚Π°Π²Π½ΠΎ: Ρ˜Π΅Π΄Π½ΠΎΡΡ‚Π°Π²Π½ΠΎ мСњамо статус ΠΏΠΎΡ€ΡƒΠΊΠ΅, Π½Π°ΠΊΠΎΠ½ ΡˆΡ‚ΠΎ јС ΠΏΠΎΡ‚ΠΏΠΈΡˆΠ΅ΠΌΠΎ Π°ΠΊΠΎ јС ΠΏΠΎΡ‚Ρ€Π΅Π±Π½ΠΎ.

Π‘Ρ‚Π°Π²Ρ™Π°ΠΌΠΎ АппровС ΠΈ Π Π΅Ρ˜Π΅Ρ†Ρ‚ Ρƒ УИ АПИ, нСвМСссагС Ρƒ АПИ страницС:

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

Π˜Π½Ρ‚Π΅Ρ€Ρ„Π΅Ρ˜ΡΡƒ јС ΠΏΠΎΡ‚Ρ€Π΅Π±Π°Π½ приступ ΡΡ‚Π°ΡšΡƒ Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π΅. На страни корисничког ΠΈΠ½Ρ‚Π΅Ρ€Ρ„Π΅Ρ˜ΡΠ° Ρ›Π΅ΠΌΠΎ ΡƒΡ€Π°Π΄ΠΈΡ‚ΠΈ observable ΡΡ‚Π°ΡšΠ΅ ΠΈ Π΄ΠΎΠ΄Π°Ρ˜Ρ‚Π΅ Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡ˜Ρƒ Ρƒ АПИ која Ρ›Π΅ ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈΡ‚ΠΈ ΠΎΠ²ΠΎ ΡΡ‚Π°ΡšΠ΅. Π”Π° Π΄ΠΎΠ΄Π°ΠΌΠΎ observable Π½Π° АПИ ΠΎΠ±Ρ˜Π΅ΠΊΠ°Ρ‚ ΠΏΡ€ΠΈΠΌΡ™Π΅Π½ ΠΈΠ· ΠΏΠΎΠ·Π°Π΄ΠΈΠ½Π΅:

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')
    );
}

Π‘Π° ΠΌΠΎΠ±ΠΊ-ΠΎΠΌ јС Π²Π΅ΠΎΠΌΠ° Π»Π°ΠΊΠΎ Π·Π°ΠΏΠΎΡ‡Π΅Ρ‚ΠΈ Ρ€Π΅Π½Π΄Π΅Ρ€ΠΎΠ²Π°ΡšΠ΅ ΠΊΠ°Π΄Π° сС ΠΏΠΎΠ΄Π°Ρ†ΠΈ ΠΏΡ€ΠΎΠΌΠ΅Π½Π΅. ΠˆΠ΅Π΄Π½ΠΎΡΡ‚Π°Π²Π½ΠΎ ΠΎΠΊΠ°Ρ‡ΠΈΠΌΠΎ посматрача Π΄Π΅ΠΊΠΎΡ€Π°Ρ‚Π΅Ρ€Π° са ΠΏΠ°ΠΊΠ΅Ρ‚Π° ΠΌΠΎΠ±ΠΊ-Ρ€Π΅Π°Ρ†Ρ‚ Π½Π° ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚ΠΈ, Π° Ρ€Π΅Π½Π΄Π΅Ρ€ Ρ›Π΅ Π±ΠΈΡ‚ΠΈ аутоматски ΠΏΠΎΠ·Π²Π°Π½ ΠΊΠ°Π΄Π° сС ΠΏΡ€ΠΎΠΌΠ΅Π½Π΅ Π±ΠΈΠ»ΠΎ који Π²ΠΈΠ΄Ρ™ΠΈΠ²ΠΈ Π΅Π»Π΅ΠΌΠ΅Π½Ρ‚ΠΈ Π½Π° којС ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π° ΡƒΠΏΡƒΡ›ΡƒΡ˜Π΅. НС Ρ‚Ρ€Π΅Π±Π° Π²Π°ΠΌ Π½ΠΈΠΊΠ°ΠΊΠ°Π² ΠΌΠ°ΠΏΠ‘Ρ‚Π°Ρ‚Π΅Π’ΠΎΠŸΡ€ΠΎΠΏΡ ΠΈΠ»ΠΈ повСзивањС ΠΊΠ°ΠΎ Ρƒ рСдуксу. Π‘Π²Π΅ Ρ€Π°Π΄ΠΈ ΠΎΠ΄ΠΌΠ°Ρ… ΠΈΠ· ΠΊΡƒΡ‚ΠΈΡ˜Π΅:

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>
    }
}

ΠŸΡ€Π΅ΠΎΡΡ‚Π°Π»Π΅ ΠΊΠΎΠΌΠΏΠΎΠ½Π΅Π½Ρ‚Π΅ сС ΠΌΠΎΠ³Ρƒ Π²ΠΈΠ΄Π΅Ρ‚ΠΈ Ρƒ ΠΊΠΎΠ΄Ρƒ Ρƒ фасцикли УИ.

Π‘Π°Π΄Π° Ρƒ класи Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π΅ Ρ‚Ρ€Π΅Π±Π° Π΄Π° Π½Π°ΠΏΡ€Π°Π²ΠΈΡ‚Π΅ сСлСктор ΡΡ‚Π°ΡšΠ° Π·Π° кориснички ΠΈΠ½Ρ‚Π΅Ρ€Ρ„Π΅Ρ˜Ρ ΠΈ обавСститС УИ ΠΊΠ°Π΄Π° сС ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈ. Π”Π° бисмо Ρ‚ΠΎ ΡƒΡ€Π°Π΄ΠΈΠ»ΠΈ, додајмо ΠΌΠ΅Ρ‚ΠΎΠ΄ 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 Π΄Π° ΠΏΡ€ΠΎΠΌΠ΅Π½ΠΈΡ‚Π΅ ΡΡ‚Π°ΡšΠ΅ којС ΠΏΠΎΠ·ΠΈΠ²Π° Ρ„ΡƒΠ½ΠΊΡ†ΠΈΡ˜Ρƒ Π½Π° страни корисничког ΠΈΠ½Ρ‚Π΅Ρ€Ρ„Π΅Ρ˜ΡΠ°.

ПослСдњи Π΄ΠΎΠ΄ΠΈΡ€ јС додавањС ΠΏΡ€ΠΈΠΊΠ°Π·Π° Π½ΠΎΠ²ΠΈΡ… ΠΏΠΎΡ€ΡƒΠΊΠ° Π½Π° ΠΈΠΊΠΎΠ½Ρƒ Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Π΅:

function setupApp() {
...

    // Reaction Π½Π° выставлСниС тСкста Π±Π΅Π΄ΠΆΠ°.
    reaction(
        () => app.store.newMessages.length > 0 ? app.store.newMessages.length.toString() : '',
        text => extensionApi.browserAction.setBadgeText({text}),
        {fireImmediately: true}
    );

...
}

Π”Π°ΠΊΠ»Π΅, Π°ΠΏΠ»ΠΈΠΊΠ°Ρ†ΠΈΡ˜Π° јС спрСмна. Π’Π΅Π± страницС ΠΌΠΎΠ³Ρƒ Π·Π°Ρ…Ρ‚Π΅Π²Π°Ρ‚ΠΈ потпис Π·Π° Ρ‚Ρ€Π°Π½ΡΠ°ΠΊΡ†ΠΈΡ˜Π΅:

ПисањС Π±Π΅Π·Π±Π΅Π΄Π½ΠΎΠ³ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°

ПисањС Π±Π΅Π·Π±Π΅Π΄Π½ΠΎΠ³ ΠΏΡ€ΠΎΡˆΠΈΡ€Π΅ΡšΠ° ΠΏΡ€Π΅Ρ‚Ρ€Π°ΠΆΠΈΠ²Π°Ρ‡Π°

Код јС доступан ΠΎΠ²Π΄Π΅ Π²Π΅Π·Π°.

Π—Π°ΠΊΡ™ΡƒΡ‡Π°ΠΊ

Ако стС ΠΏΡ€ΠΎΡ‡ΠΈΡ‚Π°Π»ΠΈ Ρ‡Π»Π°Π½Π°ΠΊ Π΄ΠΎ ΠΊΡ€Π°Ρ˜Π°, Π°Π»ΠΈ ΠΈ Π΄Π°Ρ™Π΅ ΠΈΠΌΠ°Ρ‚Π΅ ΠΏΠΈΡ‚Π°ΡšΠ°, ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΈΡ… поставити Π½Π° ΡΠΏΡ€Π΅ΠΌΠΈΡˆΡ‚Π° са Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜ΠΎΠΌ. Π’Π°ΠΌΠΎ Ρ›Π΅Ρ‚Π΅ Ρ‚Π°ΠΊΠΎΡ’Π΅ ΠΏΡ€ΠΎΠ½Π°Ρ›ΠΈ ΡƒΡ€Π΅Π·ΠΈΠ²Π°ΡšΠ΅ Π·Π° сваки Π½Π°Π·Π½Π°Ρ‡Π΅Π½ΠΈ ΠΊΠΎΡ€Π°ΠΊ.

А Π°ΠΊΠΎ стС заинтСрСсовани Π΄Π° ΠΏΠΎΠ³Π»Π΅Π΄Π°Ρ‚Π΅ ΠΊΠΎΠ΄ Π·Π° стварну Π΅ΠΊΡΡ‚Π΅Π½Π·ΠΈΡ˜Ρƒ, ΠΌΠΎΠΆΠ΅Ρ‚Π΅ ΠΏΡ€ΠΎΠ½Π°Ρ›ΠΈ ΠΎΠ²ΠΎ ΠΎΠ²Π΄Π΅.

Π¨ΠΈΡ„Ρ€Π°, ΡΠΏΡ€Π΅ΠΌΠΈΡˆΡ‚Π΅ ΠΈ опис посла ΠΎΠ΄ сиСмарСлл

Π˜Π·Π²ΠΎΡ€: Π²Π²Π².Ρ…Π°Π±Ρ€.Ρ†ΠΎΠΌ

Π”ΠΎΠ΄Π°Ρ˜ ΠΊΠΎΠΌΠ΅Π½Ρ‚Π°Ρ€