เจเจฎ "เจเจฒเจพเจเฉฐเจ-เจธเจฐเจตเจฐ" เจเจฐเจเฉเจเฉเจเจเจฐ เจฆเฉ เจเจฒเจ, เจตเจฟเจเฉเจเจฆเจฐเฉเจเฉเจฐเจฟเจค เจเจชเจฒเฉเจเฉเจธเจผเจจเจพเจ เจฆเฉ เจตเจฟเจธเจผเฉเจธเจผเจคเจพ เจนเฉ:
- เจเจชเจญเฉเจเจคเจพ เจฒเฉเจเจฟเจจ เจ เจคเฉ เจชเจพเจธเจตเจฐเจกเจพเจ เจฆเฉ เจจเจพเจฒ เจเฉฑเจ เจกเฉเจเจพเจฌเฉเจธ เจจเฉเฉฐ เจธเจเฉเจฐ เจเจฐเจจ เจฆเฉ เจเฉเจ เจฒเฉเฉ เจจเจนเฉเจ เจนเฉ. เจชเจนเฉเฉฐเจ เจเจพเจฃเจเจพเจฐเฉ เจจเฉเฉฐ เจธเจฟเจฐเจซเจผ เจเจชเจญเฉเจเจคเจพเจตเจพเจ เจฆเฉเจเจฐเจพ เจนเฉ เจธเจเฉเจฐ เจเฉเจคเจพ เจเจพเจเจฆเจพ เจนเฉ, เจ เจคเฉ เจเจนเจจเจพเจ เจฆเฉ เจชเฉเจฐเจฎเจพเจฃเจฟเจเจคเจพ เจฆเฉ เจชเฉเจธเจผเจเฉ เจชเฉเจฐเฉเจเฉเจเฉเจฒ เจชเฉฑเจงเจฐ 'เจคเฉ เจนเฉเฉฐเจฆเฉ เจนเฉเฅค
- เจธเจฐเจตเจฐ เจตเจฐเจคเจฃ เจฆเฉ เจเฉเจ เจฒเฉเฉ เจจเจนเฉเจเฅค เจเจชเจฒเฉเจเฉเจธเจผเจจ เจคเจฐเจ เจจเฉเฉฐ เจเฉฑเจ เจฌเจฒเจพเจเจเฉเจจ เจจเฉเจเจตเจฐเจ เจคเฉ เจเจฒเจพเจเจ เจเจพ เจธเจเจฆเจพ เจนเฉ, เจเจฟเฉฑเจฅเฉ เจฒเฉเฉเฉเจเจฆเฉ เจฎเจพเจคเจฐเจพ เจตเจฟเฉฑเจ เจกเฉเจเจพ เจจเฉเฉฐ เจธเจเฉเจฐ เจเจฐเจจเจพ เจธเฉฐเจญเจต เจนเฉเฅค
เจเจชเจญเฉเจเจคเจพ เจเฉเฉฐเจเฉเจเจ เจฒเจ 2 เจฎเฉเจเจพเจฌเจฒเจคเจจ เจธเฉเจฐเฉฑเจเจฟเจ เจค เจธเจเฉเจฐเฉเจ เจนเจจ - เจนเจพเจฐเจกเจตเฉเจ เจฐ เจตเจพเจฒเจฟเจ เจ เจคเฉ เจฌเฉเจฐเจพเจเจเจผเจฐ เจเจเจธเจเฉเจเจธเจผเจจเฅค เจนเจพเจฐเจกเจตเฉเจ เจฐ เจตเจพเจฒเจฟเจ เจเจผเจฟเจเจฆเจพเจคเจฐ เจฌเจนเฉเจค เจธเฉเจฐเฉฑเจเจฟเจ เจค เจนเฉเฉฐเจฆเฉ เจนเจจ, เจชเจฐ เจตเจฐเจคเจฃ เจตเจฟเฉฑเจ เจฎเฉเจธเจผเจเจฒ เจนเฉเฉฐเจฆเฉ เจนเจจ เจ เจคเฉ เจฎเฉเจซเจค เจคเฉเจ เจฆเฉเจฐ เจนเฉเฉฐเจฆเฉ เจนเจจ, เจชเจฐ เจฌเฉเจฐเจพเจเจเจผเจฐ เจเจเจธเจเฉเจเจธเจผเจจ เจธเฉเจฐเฉฑเจเจฟเจ เจ เจคเฉ เจตเจฐเจคเฉเจ เจตเจฟเฉฑเจ เจเจธเจพเจจเฉ เจฆเจพ เจธเฉฐเจชเฉเจฐเจจ เจธเฉเจฎเฉเจฒ เจนเจจ, เจ เจคเฉ เจ เฉฐเจคเจฎ เจเจชเจญเฉเจเจคเจพเจตเจพเจ เจฒเจ เจชเฉเจฐเฉ เจคเจฐเฉเจนเจพเจ เจฎเฉเจซเจค เจตเฉ เจนเฉ เจธเจเจฆเฉ เจนเจจเฅค
เจเจธ เจธเจญ เจจเฉเฉฐ เจงเจฟเจเจจ เจตเจฟเฉฑเจ เจฐเฉฑเจเจฆเฉ เจนเฉเจ, เจ
เจธเฉเจ เจธเจญ เจคเฉเจ เจธเฉเจฐเฉฑเจเจฟเจ
เจค เจเจเจธเจเฉเจเจธเจผเจจ เจฌเจฃเจพเจเจฃเจพ เจเจพเจนเฉเฉฐเจฆเฉ เจธเฉ เจเฉ เจเฉเจฐเจพเจเจเฉเจเจธเจผเจจเจพเจ เจ
เจคเฉ เจฆเจธเจคเจเจคเจพเจ เจจเจพเจฒ เจเฉฐเจฎ เจเจฐเจจ เจฒเจ เจเฉฑเจ เจธเจงเจพเจฐเจจ API เจชเฉเจฐเจฆเจพเจจ เจเจฐเจเฉ เจตเจฟเจเฉเจเจฆเจฐเฉเจเฉเจฐเจฟเจค เจเจชเจฒเฉเจเฉเจธเจผเจจเจพเจ เจฆเฉ เจตเจฟเจเจพเจธ เจจเฉเฉฐ เจธเจฐเจฒ เจฌเจฃเจพเจเจเจฆเจพ เจนเฉเฅค
เจ
เจธเฉเจ เจคเฉเจนเจพเจจเฉเฉฐ เจนเฉเจ เจพเจ เจเจธ เจ
เจจเฉเจญเจต เจฌเจพเจฐเฉ เจฆเฉฑเจธเจพเจเจเฉเฅค
เจฒเฉเจ เจตเจฟเฉฑเจ เจเฉเจก เจเจฆเจพเจนเจฐเจจเจพเจ เจ
เจคเฉ เจธเจเฉเจฐเฉเจจเจธเจผเฉเจเจธ เจฆเฉ เจจเจพเจฒ เจเฉฑเจ เจฌเฉเจฐเจพเจเจเจผเจฐ เจเจเจธเจเฉเจเจธเจผเจจ เจเจฟเจตเฉเจ เจฒเจฟเจเจฃเจพ เจนเฉ เจเจธ เจฌเจพเจฐเฉ เจเจฆเจฎ-เจฆเจฐ-เจเจฆเจฎ เจจเจฟเจฐเจฆเฉเจธเจผ เจธเจผเจพเจฎเจฒ เจนเฉเจฃเจเฉเฅค เจตเจฟเฉฑเจ เจคเฉเจธเฉเจ เจธเจพเจฐเจพ เจเฉเจก เจฒเฉฑเจญ เจธเจเจฆเฉ เจนเฉ
เจฌเฉเจฐเจพเจเจเจผเจฐ เจเจเจธเจเฉเจเจธเจผเจจเจพเจ เจฆเจพ เจธเฉฐเจเฉเจช เจเจคเจฟเจนเจพเจธ
เจฌเฉเจฐเจพเจเจเจผเจฐ เจเจเจธเจเฉเจเจธเจผเจจ เจฒเฉฐเจฌเฉ เจธเจฎเฉเจ เจคเฉเจ เจเจฒเฉ-เจฆเฉเจเจฒเฉ เจนเจจเฅค เจเจน 1999 เจตเจฟเฉฑเจ เจเฉฐเจเจฐเจจเฉเฉฑเจ เจเจเจธเจชเจฒเฉเจฐเจฐ เจตเจฟเฉฑเจ, 2004 เจตเจฟเฉฑเจ เจซเจพเจเจฐเจซเจพเจเจธ เจตเจฟเฉฑเจ เจชเฉเจฐเจเจ เจนเฉเจเฅค เจนเจพเจฒเจพเจเจเจฟ, เจฌเจนเฉเจค เจฒเฉฐเจฌเฉ เจธเจฎเฉเจ เจฒเจ เจเจเจธเจเฉเจเจธเจผเจจเจพเจ เจฒเจ เจเฉเจ เจเฉฑเจ เจฎเจพเจชเจฆเฉฐเจก เจจเจนเฉเจ เจธเฉ.
เจ เจธเฉเจ เจเจนเจฟ เจธเจเจฆเฉ เจนเจพเจ เจเจฟ เจเจน เจเฉเจเจฒ เจเจฐเฉเจฎ เจฆเฉ เจเฉเจฅเฉ เจธเฉฐเจธเจเจฐเจฃ เจตเจฟเฉฑเจ เจเจเจธเจเฉเจเจธเจผเจจเจพเจ เจฆเฉ เจจเจพเจฒ เจชเฉเจฐเจเจ เจนเฉเจเจ เจนเฉเฅค เจฌเฉเจธเจผเฉฑเจ, เจเจฆเฉเจ เจเฉเจ เจจเจฟเจฐเจงเจพเจฐเจจ เจจเจนเฉเจ เจธเฉ, เจชเจฐ เจเจน เจเฉเจฐเฉเจฎ API เจธเฉ เจเฉ เจเจธเจฆเจพ เจ เจงเจพเจฐ เจฌเจฃ เจเจฟเจ: เจเจผเจฟเจเจฆเจพเจคเจฐ เจฌเฉเจฐเจพเจเจเจผเจฐ เจฎเจพเจฐเจเฉเจ เจจเฉเฉฐ เจเจฟเฉฑเจคเจฃ เจ เจคเฉ เจเฉฑเจ เจฌเจฟเจฒเจ-เจเจจ เจเจชเจฒเฉเจเฉเจธเจผเจจ เจธเจเฉเจฐ เจนเฉเจฃ เจฆเฉ เจฌเจพเจ เจฆ, เจเฉเจฐเฉเจฎ เจจเฉ เจ เจธเจฒ เจตเจฟเฉฑเจ เจฌเฉเจฐเจพเจเจเจผเจฐ เจเจเจธเจเฉเจเจธเจผเจจเจพเจ เจฒเจ เจธเจเฉเจเจกเจฐเจก เจธเฉเฉฑเจ เจเฉเจคเจพเฅค
เจฎเฉเจเจผเฉเจฒเจพ เจฆเจพ เจเจชเจฃเจพ เจธเจเฉเจเจกเจฐเจก เจธเฉ, เจชเจฐ เจเฉเจฐเฉเจฎ เจเจเจธเจเฉเจเจธเจผเจจเจพเจ เจฆเฉ เจชเฉเจฐเจธเจฟเฉฑเจงเฉ เจจเฉเฉฐ เจฆเฉเจเจฆเฉ เจนเฉเจ, เจเฉฐเจชเจจเฉ เจจเฉ เจเฉฑเจ เจ เจจเฉเจเฉเจฒ API เจฌเจฃเจพเจเจฃ เจฆเจพ เจซเฉเจธเจฒเจพ เจเฉเจคเจพเฅค 2015 เจตเจฟเฉฑเจ, เจฎเฉเจเจผเฉเจฒเจพ เจฆเฉ เจชเจนเจฟเจฒเจเจฆเจฎเฉ 'เจคเฉ, เจเจฐเจพเจธ-เจฌเฉเจฐเจพเจเจเจผเจฐ เจเจเจธเจเฉเจเจธเจผเจจ เจตเจฟเจธเจผเฉเจธเจผเจคเจพเจตเจพเจ 'เจคเฉ เจเฉฐเจฎ เจเจฐเจจ เจฒเจ เจตเจฐเจฒเจก เจตเจพเจเจก เจตเฉเฉฑเจฌ เจเฉฐเจธเฉเจฐเจเฉเจ เจฎ (W3C) เจฆเฉ เจ เฉฐเจฆเจฐ เจเฉฑเจ เจตเจฟเจธเจผเฉเจธเจผ เจธเจฎเฉเจน เจฌเจฃเจพเจเจ เจเจฟเจ เจธเฉเฅค
Chrome เจฒเจ เจฎเฉเจเฉเจฆเจพ API เจเจเจธเจเฉเจเจธเจผเจจเจพเจ เจจเฉเฉฐ เจเจงเจพเจฐ เจตเจเฉเจ เจฒเจฟเจ เจเจฟเจ เจธเฉเฅค เจเจน เจเฉฐเจฎ เจฎเจพเจเจเจฐเฉเจธเจพเจซเจ เจฆเฉ เจธเจฎเจฐเจฅเจจ เจจเจพเจฒ เจเฉเจคเจพ เจเจฟเจ เจธเฉ (เจเฉเจเจฒ เจจเฉ เจธเจเฉเจเจกเจฐเจก เจฆเฉ เจตเจฟเจเจพเจธ เจตเจฟเฉฑเจ เจนเจฟเฉฑเจธเจพ เจฒเฉเจฃ เจคเฉเจ เจเจจเจเจพเจฐ เจเจฐ เจฆเจฟเฉฑเจคเจพ), เจ
เจคเฉ เจจเจคเฉเจเฉ เจตเจเฉเจ เจเฉฑเจ เจกเจฐเจพเจซเจ เจชเฉเจฐเจเจ เจนเฉเจเจ
เจฐเจธเจฎเฉ เจคเฉเจฐ 'เจคเฉ, เจธเจชเฉเจธเฉเจซเจฟเจเฉเจธเจผเจจ เจเจ, เจซเจพเจเจฐเจซเจพเจเจธ เจ
เจคเฉ เจเจชเฉเจฐเจพ เจฆเฉเจเจฐเจพ เจธเจฎเจฐเจฅเจฟเจค เจนเฉ (เจจเฉเจ เจเจฐเฉ เจเจฟ เจเฉเจฐเฉเจฎ เจเจธ เจธเฉเจเฉ เจตเจฟเฉฑเจ เจจเจนเฉเจ เจนเฉ)เฅค เจชเจฐ เจตเจพเจธเจคเจต เจตเจฟเฉฑเจ, เจธเจเฉเจเจกเจฐเจก เจตเฉฑเจกเฉ เจชเฉฑเจงเจฐ 'เจคเฉ Chrome เจจเจพเจฒ เจ
เจจเฉเจเฉเจฒ เจนเฉ, เจเจฟเจเจเจเจฟ เจเจน เจ
เจธเจฒ เจตเจฟเฉฑเจ เจเจธเจฆเฉ เจเจเจธเจเฉเจเจธเจผเจจเจพเจ เจฆเฉ เจ
เจงเจพเจฐ เจคเฉ เจฒเจฟเจเจฟเจ เจเจฟเจ เจนเฉเฅค เจคเฉเจธเฉเจ WebExtensions API เจฌเจพเจฐเฉ เจนเฉเจฐ เจชเฉเฉเจน เจธเจเจฆเฉ เจนเฉ
เจเจเจธเจเฉเจเจธเจผเจจ เจฌเจฃเจคเจฐ
เจเจเจธเจเฉเจเจธเจผเจจ เจฒเจ เจฒเฉเฉเฉเจเจฆเฉ เจเฉเจตเจฒ เจซเจพเจเจฒ เจฎเฉเจจเฉเจซเฉเจธเจ (manifest.json) เจนเฉเฅค เจเจน เจตเจฟเจธเจฅเจพเจฐ เจฒเจ "เจเจเจเจฐเฉ เจชเฉเจเจเฉฐเจ" เจตเฉ เจนเฉเฅค
เจชเฉเจฐเจเจ
เจจเจฟเจฐเจงเจพเจฐเจจ เจฆเฉ เจ
เจจเฉเจธเจพเจฐ, เจฎเฉเจจเฉเจซเฉเจธเจ เจซเจพเจเจฒ เจเฉฑเจ เจตเฉเจง JSON เจซเจพเจเจฒ เจนเฉเฅค เจฎเฉเจจเฉเจซเฉเจธเจ เจเฉเฉฐเจเฉเจเจ เจฆเจพ เจชเฉเจฐเจพ เจตเฉเจฐเจตเจพ เจเจธ เจฌเจพเจฐเฉ เจเจพเจฃเจเจพเจฐเฉ เจฆเฉ เจจเจพเจฒ เจเจฟ เจเจฟเจนเฉเฉเจเจ เจเฉเฉฐเจเฉเจเจ เจธเจฎเจฐเจฅเจฟเจค เจนเจจ เจเจฟเจธ เจตเจฟเฉฑเจ เจฌเฉเจฐเจพเจเจเจผเจฐ เจฆเฉเจเจฟเจ เจเจพ เจธเจเจฆเจพ เจนเฉ
เจเฉเฉฐเจเฉเจเจ เจเฉ เจจเจฟเจฐเจงเจพเจฐเจจ เจตเจฟเฉฑเจ เจจเจนเฉเจ เจนเจจ "เจนเฉ เจธเจเจฆเจพ เจนเฉ" เจจเฉเฉฐ เจ เจฃเจกเจฟเฉฑเจ เจเฉเจคเจพ เจเจพ เจธเจเจฆเจพ เจนเฉ (เจฆเฉเจตเฉเจ เจเจฐเฉเจฎ เจ เจคเฉ เจซเจพเจเจฐเจซเจพเจเจธ เจฐเจฟเจชเฉเจฐเจ เจฆเฉเจเจ เจคเจฐเฉเฉฑเจเฉเจเจ, เจชเจฐ เจเจเจธเจเฉเจเจธเจผเจจ เจเฉฐเจฎ เจเจฐเจจเจพ เจเจพเจฐเฉ เจฐเฉฑเจเจฆเฉ เจนเจจ)เฅค
เจ เจคเฉ เจฎเฉเจ เจเฉเจ เจจเฉเจเจคเจฟเจเจ เจตเฉฑเจฒ เจงเจฟเจเจจ เจเจฟเฉฑเจเจฃเจพ เจเจพเจนเจพเจเจเจพเฅค
- เจชเจฟเจเฉเจเฉ โ เจเฉฑเจ เจตเจธเจคเฉ เจเจฟเจธ เจตเจฟเฉฑเจ เจนเฉเจ เจพเจ เจฆเจฟเฉฑเจคเฉ เจเฉเจคเจฐ เจธเจผเจพเจฎเจฒ เจนเจจ:
- เจธเจเฉเจฐเจฟเจชเจเจพเจ โ เจธเจเฉเจฐเจฟเจชเจเจพเจ เจฆเจพ เจเฉฑเจ เจเจฐเฉ เจเฉ เจฌเฉเจเจเฉเจฐเจพเจเจเจก เจธเฉฐเจฆเจฐเจญ เจตเจฟเฉฑเจ เจเจฒเจพเจเจ เจเจพเจตเฉเจเจพ (เจ เจธเฉเจ เจเจธ เจฌเจพเจฐเฉ เจฅเฉเฉเฉเจนเฉ เจฆเฉเจฐ เจฌเจพเจ เจฆ เจเฉฑเจฒ เจเจฐเจพเจเจเฉ);
- เจชเฉฐเจจเจพ - เจธเจเฉเจฐเจฟเจชเจเจพเจ เจฆเฉ เจฌเจเจพเจ เจเฉ เจเฉฑเจ เจเจพเจฒเฉ เจชเฉฐเจจเฉ เจตเจฟเฉฑเจ เจเจฒเจพเจเจเจ เจเจพเจฃเจเฉเจเจ, เจคเฉเจธเฉเจ เจธเจฎเฉฑเจเจฐเฉ เจฆเฉ เจจเจพเจฒ html เจจเจฟเจธเจผเจเจฟเจค เจเจฐ เจธเจเจฆเฉ เจนเฉเฅค เจเจธ เจธเจฅเจฟเจคเฉ เจตเจฟเฉฑเจ, เจธเจเฉเจฐเจฟเจชเจ เจเฉเจคเจฐ เจจเฉเฉฐ เจ เจฃเจกเจฟเฉฑเจ เจเฉเจคเจพ เจเจพเจตเฉเจเจพ, เจ เจคเฉ เจธเจเฉเจฐเจฟเจชเจเจพเจ เจจเฉเฉฐ เจธเจฎเฉฑเจเจฐเฉ เจชเฉฐเจจเฉ เจตเจฟเฉฑเจ เจธเจผเจพเจฎเจฒ เจเจฐเจจ เจฆเฉ เจฒเฉเฉ เจนเฉเจตเฉเจเฉ;
- เจเจพเจฐเฉ - เจเฉฑเจ เจฌเจพเจเจจเจฐเฉ เจซเจฒเฉเจ, เจเฉเจเจฐ เจจเจฟเจฐเจงเจพเจฐเจค เจจเจนเฉเจ เจเฉเจคเจพ เจเจฟเจ เจนเฉ, เจคเจพเจ เจฌเฉเจฐเจพเจเจเจผเจฐ เจฌเฉเจเจเฉเจฐเจพเจเจเจก เจชเฉเจฐเจเจฟเจฐเจฟเจ เจจเฉเฉฐ "เจฎเจพเจฐ" เจฆเฉเจตเฉเจเจพ เจเจฆเฉเจ เจเจน เจธเจฎเจเจฆเจพ เจนเฉ เจเจฟ เจเจน เจเฉเจ เจจเจนเฉเจ เจเจฐ เจฐเจฟเจนเจพ เจนเฉ, เจ เจคเฉ เจเฉ เจฒเฉเฉ เจนเฉเจตเฉ เจคเจพเจ เจเจธเจจเฉเฉฐ เจฎเฉเฉ เจเจพเจฒเฉ เจเจฐ เจฆเฉเจตเฉเจเจพเฅค เจจเจนเฉเจ เจคเจพเจ, เจฌเฉเจฐเจพเจเจเจผเจฐ เจฌเฉฐเจฆ เจนเฉเจฃ 'เจคเฉ เจนเฉ เจชเฉฐเจจเจพ เจ เจจเจฒเฉเจก เจเฉเจคเจพ เจเจพเจตเฉเจเจพเฅค เจซเจพเจเจฐเจซเจพเจเจธ เจตเจฟเฉฑเจ เจธเจฎเจฐเจฅเจฟเจค เจจเจนเฉเจ เจนเฉเฅค
- เจธเจฎเฉฑเจเจฐเฉ_เจธเจเฉเจฐเจฟเจชเจเจพเจ โ เจตเจธเจคเฉเจเจ เจฆเฉ เจเฉฑเจ เจฒเฉเฉ เจเฉ เจคเฉเจนเจพเจจเฉเฉฐ เจตเฉฑเจ-เจตเฉฑเจ เจตเฉเฉฑเจฌ เจชเฉฐเจจเจฟเจเจ 'เจคเฉ เจตเฉฑเจ-เจตเฉฑเจ เจธเจเฉเจฐเจฟเจชเจเจพเจ เจจเฉเฉฐ เจฒเฉเจก เจเจฐเจจ เจฆเฉ เจเจเจพเจเจผเจค เจฆเจฟเฉฐเจฆเฉ เจนเฉเฅค เจนเจฐเฉเจ เจตเจธเจคเฉ เจตเจฟเฉฑเจ เจนเฉเจ เจฒเจฟเจเฉ เจฎเจนเฉฑเจคเจตเจชเฉเจฐเจจ เจเฉเจคเจฐ เจนเฉเฉฐเจฆเฉ เจนเจจ:
- เจฎเฉเจ -
เจชเฉเจเจฐเจจ url , เจเฉ เจเจน เจจเจฟเจฐเจงเจพเจฐเจค เจเจฐเจฆเจพ เจนเฉ เจเจฟ เจเฉ เจเฉฑเจ เจเจพเจธ เจธเจฎเฉฑเจเจฐเฉ เจธเจเฉเจฐเจฟเจชเจ เจธเจผเจพเจฎเจฒ เจเฉเจคเฉ เจเจพเจตเฉเจเฉ เจเจพเจ เจจเจนเฉเจเฅค - js โ เจธเจเฉเจฐเจฟเจชเจเจพเจ เจฆเฉ เจเฉฑเจ เจธเฉเจเฉ เจเฉ เจเจธ เจฎเฉเจ เจตเจฟเฉฑเจ เจฒเฉเจก เจเฉเจคเฉเจเจ เจเจพเจฃเจเฉเจเจ;
- exclude_maches - เจเฉเจคเจฐ เจคเฉเจ เจฌเจพเจนเจฐ เจฐเฉฑเจเจฟเจ เจเจฟเจ เจนเฉ
match
URL เจเฉ เจเจธ เจเฉเจคเจฐ เจจเจพเจฒ เจฎเฉเจฒ เจเจพเจเจฆเฉ เจนเจจเฅค
- เจฎเฉเจ -
- เจชเฉฐเจจเจพ_เจเจพเจฐเจตเจพเจ - เจ
เจธเจฒ เจตเจฟเฉฑเจ เจเฉฑเจ เจตเจธเจคเฉ เจนเฉ เจเฉ เจเจเจเจจ เจฒเจ เจเจผเจฟเฉฐเจฎเฉเจตเจพเจฐ เจนเฉ เจเฉ เจฌเฉเจฐเจพเจเจเจผเจฐ เจตเจฟเฉฑเจ เจเจกเจฐเฉเฉฑเจธ เจฌเจพเจฐ เจฆเฉ เจ
เฉฑเจเฉ เจชเฉเจฐเจฆเจฐเจธเจผเจฟเจค เจนเฉเฉฐเจฆเจพ เจนเฉ เจ
เจคเฉ เจเจธเจฆเฉ เจจเจพเจฒ เจชเจฐเจธเจชเจฐ เจชเฉเจฐเจญเจพเจต เจนเฉเฉฐเจฆเจพ เจนเฉเฅค เจเจน เจคเฉเจนเจพเจจเฉเฉฐ เจเฉฑเจ เจชเฉเจชเจ
เฉฑเจช เจตเจฟเฉฐเจกเฉ เจชเฉเจฐเจฆเจฐเจธเจผเจฟเจค เจเจฐเจจ เจฆเฉ เจตเฉ เจเจเจพเจเจผเจค เจฆเจฟเฉฐเจฆเจพ เจนเฉ, เจเฉ เจคเฉเจนเจพเจกเฉ เจเจชเจฃเฉ HTML, CSS เจ
เจคเฉ JS เจฆเฉ เจตเจฐเจคเฉเจ เจเจฐเจเฉ เจชเจฐเจฟเจญเจพเจธเจผเจฟเจค เจเฉเจคเจพ เจเจฟเจ เจนเฉเฅค
- default_popup - เจชเฉเจชเจ เฉฑเจช เจเฉฐเจเจฐเจซเฉเจธ เจจเจพเจฒ HTML เจซเจพเจเจฒ เจฆเจพ เจฎเจพเจฐเจ, CSS เจ เจคเฉ JS เจนเฉ เจธเจเจฆเจพ เจนเฉเฅค
- เจ
เจงเจฟเจเจพเจฐ โ เจเจเจธเจเฉเจเจธเจผเจจ เจ
เจงเจฟเจเจพเจฐเจพเจ เจฆเฉ เจชเฉเจฐเจฌเฉฐเจงเจจ เจฒเจ เจเฉฑเจ เจเจฐเฉเฅค เจ
เจงเจฟเจเจพเจฐเจพเจ เจฆเฉเจเจ 3 เจเจฟเจธเจฎเจพเจ เจนเจจ, เจเจฟเจจเฉเจนเจพเจ เจฆเจพ เจตเจฟเจธเจฅเจพเจฐ เจตเจฟเฉฑเจ เจตเจฐเจฃเจจ เจเฉเจคเจพ เจเจฟเจ เจนเฉ
เจเฉฑเจฅเฉ - เจตเฉเฉฑเจฌ_เจชเจนเฉเฉฐเจเจฏเฉเจ_เจธเจฐเฉเจค โ เจเจเจธเจเฉเจเจธเจผเจจ เจธเจฐเฉเจค เจเฉ เจเฉฑเจ เจตเฉเฉฑเจฌ เจชเฉฐเจจเจพ เจฌเฉเจจเจคเฉ เจเจฐ เจธเจเจฆเจพ เจนเฉ, เจเจฆเจพเจนเจฐเจจ เจฒเจ, เจเจฟเฉฑเจคเจฐ, JS, CSS, HTML เจซเจพเจเจฒเจพเจเฅค
- externally_connectable โ เจเฉฑเจฅเฉ เจคเฉเจธเฉเจ เจนเฉเจฐ เจเจเจธเจเฉเจเจธเจผเจจเจพเจ เจ เจคเฉ เจตเฉเจฌ เจชเฉเจเจพเจ เจฆเฉ เจกเฉเจฎเฉเจจเจพเจ เจฆเฉ ID เจจเฉเฉฐ เจธเจชเจธเจผเจ เจคเฉเจฐ 'เจคเฉ เจจเจฟเจธเจผเจเจฟเจค เจเจฐ เจธเจเจฆเฉ เจนเฉ เจเจฟเฉฑเจฅเฉเจ เจคเฉเจธเฉเจ เจเจจเฉเจเจ เจเจฐ เจธเจเจฆเฉ เจนเฉเฅค เจเฉฑเจ เจกเฉเจฎเฉเจจ เจฆเฉเจเจพ เจชเฉฑเจงเจฐ เจเจพเจ เจเฉฑเจเจพ เจนเฉ เจธเจเจฆเจพ เจนเฉเฅค เจซเจพเจเจฐเจซเจพเจเจธ เจตเจฟเฉฑเจ เจเฉฐเจฎ เจจเจนเฉเจ เจเจฐเจฆเจพเฅค
เจเจเจเจผเฉเจเจฟเจเจธเจผเจจ เจชเฉเจฐเจธเฉฐเจ
เจเจเจธเจเฉเจเจธเจผเจจ เจตเจฟเฉฑเจ เจคเจฟเฉฐเจจ เจเฉเจก เจเจเจเจผเฉเจเจฟเจเจธเจผเจจ เจชเฉเจฐเจธเฉฐเจ เจนเจจ, เจฏเจพเจจเฉ, เจเจชเจฒเฉเจเฉเจธเจผเจจ เจตเจฟเฉฑเจ เจฌเฉเจฐเจพเจเจเจผเจฐ API เจคเฉฑเจ เจชเจนเฉเฉฐเจ เจฆเฉ เจตเฉฑเจ-เจตเฉฑเจ เจชเฉฑเจงเจฐเจพเจ เจฆเฉ เจจเจพเจฒ เจคเจฟเฉฐเจจ เจญเจพเจ เจนเฉเฉฐเจฆเฉ เจนเจจเฅค
เจเจเจธเจเฉเจเจธเจผเจจ เจธเฉฐเจฆเจฐเจญ
เจเจผเจฟเจเจฆเจพเจคเจฐ API เจเฉฑเจฅเฉ เจเจชเจฒเจฌเจง เจนเจจเฅค เจเจธ เจธเฉฐเจฆเจฐเจญ เจตเจฟเฉฑเจ เจเจน "เจเฉเจเจเจฆเฉ" เจนเจจ:
- เจชเจฟเจเฉเจเฉ เจชเฉฐเจจเจพ - เจเจเจธเจเฉเจเจธเจผเจจ เจฆเจพ "เจฌเฉเจเจเจเจก" เจนเจฟเฉฑเจธเจพเฅค เจซเจพเจเจฒ เจจเฉเฉฐ "เจฌเฉเจเจเฉเจฐเจพเจเจเจก" เจเฉเฉฐเจเฉ เจฆเฉ เจตเจฐเจคเฉเจ เจเจฐเจเฉ เจฎเฉเจจเฉเจซเฉเจธเจ เจตเจฟเฉฑเจ เจจเจฟเจฐเจงเจพเจฐเจค เจเฉเจคเจพ เจเจฟเจ เจนเฉเฅค
- เจชเฉเจชเจ
เฉฑเจช เจชเฉฐเจจเจพ - เจเฉฑเจ เจชเฉเจชเจ
เฉฑเจช เจชเฉฐเจจเจพ เจเฉ เจเจฆเฉเจ เจชเฉเจฐเจเจ เจนเฉเฉฐเจฆเจพ เจนเฉ เจเจฆเฉเจ เจคเฉเจธเฉเจ เจเจเจธเจเฉเจเจธเจผเจจ เจเจเจเจจ 'เจคเฉ เจเจฒเจฟเฉฑเจ เจเจฐเจฆเฉ เจนเฉเฅค เจฎเฉเจจเฉเจซเฉเจธเจเฉ เจตเจฟเฉฑเจ
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 เจเฉเฉเจฆเฉ เจนเจพเจ - เจ เจคเฉ เจธเจพเจกเฉ เจเจชเจฒเฉเจเฉเจธเจผเจจ เจชเจนเจฟเจฒเจพเจ เจนเฉ เจเฉเจเจฒ เจเจฐเฉเจฎ เจตเจฟเฉฑเจ เจฒเฉเจก เจเฉเจคเฉ เจเจพ เจธเจเจฆเฉ เจนเฉ เจ เจคเฉ เจเจน เจฏเจเฉเจจเฉ เจฌเจฃเจพเจเจเจฆเฉ เจนเฉ เจเจฟ เจเจน เจเฉฐเจฎ เจเจฐเจฆเจพ เจนเฉเฅค
เจเจธเจฆเฉ เจชเฉเจธเจผเจเฉ เจเจฐเจจ เจฒเจ, เจคเฉเจธเฉเจ เจเฉเจก เจฒเฉ เจธเจเจฆเฉ เจนเฉ
เจนเฉเจฃ เจธเจพเจกเจพ เจเจเจธเจเฉเจเจธเจผเจจ เจธเจฅเจพเจชเจฟเจค เจ เจคเฉ เจเฉฐเจฎ เจเจฐ เจฐเจฟเจนเจพ เจนเฉเฅค เจคเฉเจธเฉเจ เจนเฉเจ เจพเจ เจฆเจฟเฉฑเจคเฉ เจ เจจเฉเจธเจพเจฐ เจตเฉฑเจ-เจตเฉฑเจ เจธเฉฐเจฆเจฐเจญเจพเจ เจฒเจ เจกเจฟเจตเฉเจฒเจชเจฐ เจเฉเจฒ เจเจฒเจพ เจธเจเจฆเฉ เจนเฉ:
เจชเฉเจชเจ เฉฑเจช ->
เจธเจฎเฉฑเจเจฐเฉ เจธเจเฉเจฐเจฟเจชเจ เจเฉฐเจธเฉเจฒ เจคเฉฑเจ เจชเจนเฉเฉฐเจ เจเจธ เจชเฉฐเจจเฉ เจฆเฉ เจเฉฐเจธเฉเจฒ เจฆเฉเจเจฐเจพ เจเฉเจคเฉ เจเจพเจเจฆเฉ เจนเฉ เจเจฟเจธ 'เจคเฉ เจเจน เจฒเจพเจเจ เจเฉเจคเจพ เจเจฟเจ เจนเฉเฅค
เจธเฉเจจเฉเจนเฉ เจฆเจพ เจเจฆเจพเจจ เจชเฉเจฐเจฆเจพเจจ
เจเจธ เจฒเจ, เจธเจพเจจเฉเฉฐ เจฆเฉ เจธเฉฐเจเจพเจฐ เจเฉเจจเจฒ เจธเจฅเจพเจชเจค เจเจฐเจจ เจฆเฉ เจฒเฉเฉ เจนเฉ: inpage <-> เจฌเฉเจเจเจฐเจพเจเจเจก เจ เจคเฉ เจชเฉเจชเจ เฉฑเจช <-> เจฌเฉเจเจเจฐเจพเจเจเจกเฅค เจคเฉเจธเฉเจ, เจฌเฉเจธเจผเฉฑเจ, เจธเจฟเจฐเจซเจผ เจชเฉเจฐเจ เจจเฉเฉฐ เจธเฉเจจเฉเจนเฉ เจญเฉเจ เจธเจเจฆเฉ เจนเฉ เจ เจคเฉ เจเจชเจฃเฉ เจเฉเจฆ เจฆเฉ เจชเฉเจฐเฉเจเฉเจเฉเจฒ เจฆเฉ เจเฉเจ เจเจฐ เจธเจเจฆเฉ เจนเฉ, เจชเจฐ เจฎเฉเจ เจเจธ เจชเจนเฉเฉฐเจ เจจเฉเฉฐ เจคเจฐเจเฉเจน เจฆเจฟเฉฐเจฆเจพ เจนเจพเจ เจเฉ เจฎเฉเจ เจฎเฉเจเจพเจฎเจพเจธเจ เจเจชเจจ เจธเฉเจฐเจธ เจชเฉเจฐเฉเจเฉเจเจ เจตเจฟเฉฑเจ เจฆเฉเจเจฟเจ เจธเฉเฅค
เจเจน Ethereum เจจเฉเฉฑเจเจตเจฐเจ เจจเจพเจฒ เจเฉฐเจฎ เจเจฐเจจ เจฒเจ เจเฉฑเจ เจฌเฉเจฐเจพเจเจเจผเจฐ เจเจเจธเจเฉเจเจธเจผเจจ เจนเฉเฅค เจเจธ เจตเจฟเฉฑเจ, เจเจชเจฒเฉเจเฉเจธเจผเจจ เจฆเฉ เจตเฉฑเจ-เจตเฉฑเจ เจนเจฟเฉฑเจธเฉ dnode เจฒเจพเจเจฌเฉเจฐเฉเจฐเฉ เจฆเฉ เจตเจฐเจคเฉเจ เจเจฐเจเฉ RPC เจฐเจพเจนเฉเจ เจธเฉฐเจเจพเจฐ เจเจฐเจฆเฉ เจนเจจเฅค เจเจน เจคเฉเจนเจพเจจเฉเฉฐ เจเฉฑเจ เจเจเจธเจเฉเจเจ เจจเฉเฉฐ เจฌเจนเฉเจค เจคเฉเจเจผเฉ เจจเจพเจฒ เจ เจคเฉ เจธเฉเจตเจฟเจงเจพเจเจจเจ เจขเฉฐเจ เจจเจพเจฒ เจธเฉฐเจเจ เจฟเจค เจเจฐเจจ เจฆเฉ เจเจเจพเจเจผเจค เจฆเจฟเฉฐเจฆเจพ เจนเฉ เจเฉเจเจฐ เจคเฉเจธเฉเจ เจเจธเจจเฉเฉฐ เจเฉฑเจ เจเฉเจฐเจพเจเจธเจชเฉเจฐเจ เจฆเฉ เจคเฉเจฐ เจคเฉ เจเฉฑเจ เจจเฉเจกเจ เจธเจเฉเจฐเฉเจฎ เจชเฉเจฐเจฆเจพเจจ เจเจฐเจฆเฉ เจนเฉ (เจฎเจคเจฒเจฌ เจเฉฑเจ เจตเจธเจคเฉ เจเฉ เจเฉฑเจเฉ เจเฉฐเจเจฐเจซเฉเจธ เจจเฉเฉฐ เจฒเจพเจเฉ เจเจฐเจฆเฉ เจนเฉ):
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 เจเจฌเจเฉเจเจ เจฌเจฃเจพเจเจเจพ, เจ เจคเฉ เจเจนเจจเจพเจ เจฒเจ เจเฉฑเจ เจกเฉเจจเฉเจก เจฌเจฃเจพเจเจเจพ:
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)
})
}
}
เจเฉฑเจฅเฉ เจ เจคเฉ เจนเฉเจ เจพเจ, เจเจฒเฉเจฌเจฒ เจเจฐเฉเจฎ เจเจฌเจเฉเจเจ เจฆเฉ เจฌเจเจพเจ, เจ เจธเฉเจ เจเจเจธเจเฉเจเจธเจผเจจ เจเจชเฉเจเจ เจฆเฉ เจตเจฐเจคเฉเจ เจเจฐเจฆเฉ เจนเจพเจ, เจเฉ เจเจฟ Google เจฆเฉ เจฌเฉเจฐเจพเจเจเจผเจฐ เจตเจฟเฉฑเจ Chrome เจ เจคเฉ เจนเฉเจฐเจพเจ เจตเจฟเฉฑเจ เจฌเฉเจฐเจพเจเจเจผเจฐ เจคเฉฑเจ เจชเจนเฉเฉฐเจ เจเจฐเจฆเจพ เจนเฉเฅค เจเจน เจเจฐเจพเจธ-เจฌเฉเจฐเจพเจเจเจผเจฐ เจ เจจเฉเจเฉเจฒเจคเจพ เจฒเจ เจเฉเจคเจพ เจเจฟเจ เจนเฉ, เจชเจฐ เจเจธ เจฒเฉเจ เจฆเฉ เจเจฆเฉเจธเจผเจพเจ เจฒเจ เจเฉเจ เจธเจฟเจฐเจซเจผ '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 เจธเจเฉเจฐเฉเจฎเจพเจ เจจเจพเจฒ เจเฉฐเจฎ เจเจฐเจฆเจพ เจนเฉ, เจ เจคเฉ เจธเจพเจจเฉเฉฐ เจเฉฑเจ เจชเฉเจฐเจ เจชเฉเจฐเจพเจชเจค เจนเฉเฉฐเจฆเจพ เจนเฉ, เจเฉฑเจ เจ เจกเจพเจชเจเจฐ เจเจฒเจพเจธ เจฆเฉ เจฒเฉเฉ เจนเฉเฉฐเจฆเฉ เจนเฉเฅค เจเจน เจชเฉเฉเจนเจจเจฏเฉเจ-เจธเจเฉเจฐเฉเจฎ เจฒเจพเจเจฌเฉเจฐเฉเจฐเฉ เจฆเฉ เจตเจฐเจคเฉเจ เจเจฐเจเฉ เจฌเจฃเจพเจเจ เจเจฟเจ เจนเฉ, เจเฉ เจฌเฉเจฐเจพเจเจเจผเจฐ เจตเจฟเฉฑเจ เจจเฉเจกเจ เจธเจเฉเจฐเฉเจฎ เจจเฉเฉฐ เจฒเจพเจเฉ เจเจฐเจฆเจพ เจนเฉ:
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 เจตเจฟเฉฑเจ เจเฉฐเจเฉเจเจ เจเจฐเฉเฅค เจธเจเฉเจฐเจฟเจชเจ เจจเฉเฉฐ เจกเจพเจเจจเจฒเฉเจก เจเจฐเฉ (เจฎเฉเจจเฉเจซเฉเจธเจ เจตเจฟเฉฑเจ เจเจธ เจคเฉฑเจ เจชเจนเฉเฉฐเจ เจฆเฉ เจเจเจพเจเจผเจค เจธเฉ) เจ
เจคเฉ เจเฉฑเจ เจเฉเจ เจฌเจฃเจพเจ
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;
}
เจ
เจธเฉเจ เจคเจฟเจเจฐ เจนเจพเจ
เจเจพเจฒเฉ API เจ เจคเฉ เจฎเฉเจฒเฅค เจชเฉเจ เจธเจพเจเจก 'เจคเฉ, เจ เจธเฉเจ เจนเฉเจฒเฉ เจซเฉฐเจเจธเจผเจจ เจจเฉเฉฐ เจเจธ เจคเจฐเฉเจนเจพเจ เจเจพเจฒ เจเจฐ เจธเจเจฆเฉ เจนเจพเจ:
เจเจงเฉเจจเจฟเจ JS เจตเจฟเฉฑเจ เจเจพเจฒเจฌเฉเจ เจซเฉฐเจเจธเจผเจจเจพเจ เจจเจพเจฒ เจเฉฐเจฎ เจเจฐเจจเจพ เจฎเจพเฉเจพ เจตเจฟเจตเจนเจพเจฐ เจนเฉ, เจเจธเจฒเจ เจเจ เจเฉฑเจ เจกเฉเจจเฉเจก เจฌเจฃเจพเจเจฃ เจฒเจ เจเฉฑเจ เจเฉเจเจพ เจธเจนเจพเจเจ เจฒเจฟเจเฉเจ เจเฉ เจคเฉเจนเจพเจจเฉเฉฐ เจเจชเจฏเฉเจเจคเจพเจตเจพเจ เจตเจฟเฉฑเจ เจเฉฑเจ API เจตเจธเจคเฉ เจจเฉเฉฐ เจชเจพเจธ เจเจฐเจจ เจฆเฉ เจเจเจฟเจ เจฆเจฟเฉฐเจฆเจพ เจนเฉเฅค
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 เจฌเจฃเจพ เจธเจเจฆเฉ เจนเจพเจเฅค เจธเจฟเจงเจพเจเจค เจตเจฟเฉฑเจ, เจกเจจเฉเจก เจจเฉเฉฐ เจเจฟเจคเฉ เจตเฉ เจตเจฐเจคเจฟเจ เจเจพ เจธเจเจฆเจพ เจนเฉ, เจฎเฉเฉฑเจ เจเฉฑเจฒ เจเจน เจนเฉ เจเจฟ เจจเฉเจกเจเจผ เจธเจเฉเจฐเฉเจฎ เจฆเฉ เจฐเฉเจช เจตเจฟเฉฑเจ เจเฉเจฐเจพเจเจธเจชเฉเจฐเจ เจจเฉเฉฐ เจธเจฎเฉเจเจฃเจพ.
เจเฉฑเจ เจตเจฟเจเจฒเจช 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 เจเฉฐเจธเฉเจฒ เจคเฉเจ เจเฉเจ เจเฉเฉฐเจเฉเจเจ เจเฉเฉเฉเจ เจ เจคเฉ เจตเฉเจเฉเจ เจเจฟ เจฐเจพเจ เจจเจพเจฒ เจเฉ เจนเฉเฉฐเจฆเจพ เจนเฉ:
เจฐเจพเจ เจจเฉเฉฐ เจธเจฅเจพเจ เจฌเจฃเจพเจเจฃ เจฆเฉ เจฒเฉเฉ เจนเฉ เจคเจพเจ เจเฉ เจฎเฉเฉ เจเจพเจฒเฉ เจเจฐเจจ เจตเฉเจฒเฉ เจเฉเฉฐเจเฉเจเจ เจเฉเฉฐเจฎ เจจเจพ เจนเฉเจฃ.
เจ เจธเฉเจ เจเจธเจจเฉเฉฐ เจฒเฉเจเจฒ เจธเจเฉเจฐเฉเจ เจตเจฟเฉฑเจ เจธเจเฉเจฐ เจเจฐเจพเจเจเฉ, เจนเจฐ เจฌเจฆเจฒเจพเจ เจฆเฉ เจจเจพเจฒ เจเจธเจจเฉเฉฐ เจเจตเจฐเจฐเจพเจเจ เจเจฐเจพเจเจเฉเฅค เจเจธ เจคเฉเจ เจฌเจพเจ เจฆ, 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 เจเฉเจคเจฐเจพเจ เจคเฉฑเจ เจชเจนเฉเฉฐเจ เจจเฉเฉฐ เจเจฐเฉเจ เจเจฐเจฆเจพ เจนเฉเฅค เจชเฉเจฐเฉเจเจธเฉ เจตเจธเจคเฉเจเจ เจฆเฉ เจชเฉเจฐเจพเจชเจค เจเจฐเจจ เจตเจพเจฒเฉ เจ เจคเฉ เจธเฉเจเจฐเจธ เจเฉ เจฒเจพเจเจฌเฉเจฐเฉเจฐเฉ เจฌเจฃเจพเจเจเจฆเจพ เจนเฉ เจตเจฐเจคเฉ เจเจพเจเจฆเฉ เจนเจจเฅค
เจเจเจธเจผเจจ เจธเจเจพเจตเจ เจฆเฉ เจเจฆเฉเจธเจผเจพเจ เจฆเฉ เจชเฉเจฐเจคเฉ เจเจฐเจฆเฉ เจนเจจ:
- เจเจจเจซเฉเจฐเจธ เจเจเจธเจผเจจ เจซเจฒเฉเจ เจฆเฉ เจจเจพเจฒ เจธเจเจค เจฎเฉเจก เจตเจฟเฉฑเจ, mobx เจฐเจพเจ เจจเฉเฉฐ เจธเจฟเฉฑเจงเฉ เจคเฉเจฐ 'เจคเฉ เจฌเจฆเจฒเจฃ เจฆเฉ เจฎเจจเจพเจนเฉ เจเจฐเจฆเจพ เจนเฉเฅค เจธเจเจผเจค เจนเจพเจฒเจคเจพเจ เจตเจฟเจ เจเฉฐเจฎ เจเจฐเจจเจพ เจเฉฐเจเจพ เจ เจญเจฟเจเจธ เจฎเฉฐเจจเจฟเจ เจเจพเจเจฆเจพ เจนเฉเฅค
- เจญเจพเจตเฉเจ เจเฉเจ เจซเฉฐเจเจธเจผเจจ เจเจ เจตเจพเจฐ เจธเจฅเจฟเจคเฉ เจจเฉเฉฐ เจฌเจฆเจฒเจฆเจพ เจนเฉ - เจเจฆเจพเจนเจฐเจจ เจฒเจ, เจ เจธเฉเจ เจเฉเจก เจฆเฉเจเจ เจเจ เจฒเจพเจเจจเจพเจ เจตเจฟเฉฑเจ เจเจ เจเฉเจคเจฐเจพเจ เจจเฉเฉฐ เจฌเจฆเจฒเจฆเฉ เจนเจพเจ - เจจเจฟเจฐเฉเจเจเจพเจ เจจเฉเฉฐ เจเจฆเฉเจ เจนเฉ เจธเฉเจเจฟเจค เจเฉเจคเจพ เจเจพเจเจฆเจพ เจนเฉ เจเจฆเฉเจ เจเจน เจชเฉเจฐเจพ เจนเฉเฉฐเจฆเจพ เจนเฉเฅค เจเจน เจซเจฐเฉฐเจเจเจเจก เจฒเจ เจเจพเจธ เจคเฉเจฐ 'เจคเฉ เจฎเจนเฉฑเจคเจตเจชเฉเจฐเจจ เจนเฉ, เจเจฟเฉฑเจฅเฉ เจฌเฉเจฒเฉเฉเฉ เจธเจเฉเจ เจ เฉฑเจชเจกเฉเจ เจคเฉฑเจคเจพเจ เจฆเฉ เจฌเฉเจฒเฉเฉเฉ เจชเฉเจธเจผเจเจพเจฐเฉ เจตเฉฑเจฒ เจฒเฉ เจเจพเจเจฆเฉ เจนเจจเฅค เจธเจพเจกเฉ เจเฉเจธ เจตเจฟเฉฑเจ, เจจเจพ เจคเจพเจ เจชเจนเจฟเจฒเจพ เจ เจคเฉ เจจเจพ เจนเฉ เจฆเฉเจเจพ เจเจพเจธ เจคเฉเจฐ 'เจคเฉ เจขเฉเจเจตเจพเจ เจนเฉ, เจชเจฐ เจ เจธเฉเจ เจธเจญ เจคเฉเจ เจตเจงเฉเจ เจ เจญเจฟเจเจธเจพเจ เจฆเฉ เจชเจพเจฒเจฃเจพ เจเจฐเจพเจเจเฉเฅค เจธเจเจพเจตเจ เจเจฐเจจ เจตเจพเจฒเจฟเจเจ เจจเฉเฉฐ เจธเจพเจฐเฉ เจซเฉฐเจเจธเจผเจจเจพเจ เจจเจพเจฒ เจเฉเฉเจจ เจฆเจพ เจฐเจฟเจตเจพเจ เจนเฉ เจเฉ เจจเจฟเจฐเฉเจเจฃ เจเฉเจคเฉ เจเฉเจคเจฐเจพเจ เจฆเฉ เจธเจฅเจฟเจคเฉ เจจเฉเฉฐ เจฌเจฆเจฒเจฆเฉ เจนเจจ.
เจฌเฉเจเจเฉเจฐเจพเจเจเจก เจตเจฟเฉฑเจ เจ เจธเฉเจ เจฒเฉเจเจฒ เจธเจเฉเจฐเฉเจ เจตเจฟเฉฑเจ เจธเจเฉเจ เจจเฉเฉฐ เจธเจผเฉเจฐเฉเจเจคเฉ เจ เจคเฉ เจธเฉเจตเจฟเฉฐเจ เจเฉเฉเจพเจเจเฉ:
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
. เจเจน เจ
เจธเจฒเฉ เจเฉเจคเจฐเจพเจ เจจเจพเจฒ เจฌเจฆเจฒเฉเจเจ เจธเจพเจฐเฉเจเจ เจชเฉเจฐเฉเจเจธเฉเจเจผ เจฆเฉ เจจเจพเจฒ เจเฉฑเจ เจจเจตเฉเจ เจตเจธเจคเฉ เจตเจพเจชเจธ เจเจฐเจฆเจพ เจนเฉเฅค เจเจเจเจผเฉเจเจฟเจเจธเจผเจจ เจฆเฉ เจฆเฉเจฐเจพเจจ, เจเจน เจเจฌเจเฉเจเจ เจฆเฉ เจธเจพเจฐเฉ เจเฉเจคเจฐเจพเจ เจจเฉเฉฐ เจชเฉเฉเจนเจฆเจพ เจนเฉ - เจเจธ เจฒเจ เจเฉเจเจฐเจธ เจจเฉเฉฐ เจเจพเจฒเฉ เจเฉเจคเจพ เจเจพเจเจฆเจพ เจนเฉเฅค
เจชเฉเจชเจ เฉฑเจช เจเฉฐเจธเฉเจฒ เจตเจฟเฉฑเจ เจ เจธเฉเจ เจฆเฉเจฌเจพเจฐเจพ เจเจ เจเฉเฉฐเจเฉเจเจ เจเฉเฉเจพเจเจเฉเฅค เจเจธ เจตเจพเจฐ เจเจน เจฒเฉเจเจฒ เจธเจเฉเจฐเฉเจ เจตเจฟเฉฑเจ เจตเฉ เจเจคเจฎ เจนเฉเจ:
เจเจฆเฉเจ เจฌเฉเจเจเฉเจฐเจพเจเจเจก เจชเฉเจ เจจเฉเฉฐ เจฐเฉเจฒเฉเจก เจเฉเจคเจพ เจเจพเจเจฆเจพ เจนเฉ, เจคเจพเจ เจเจพเจฃเจเจพเจฐเฉ เจฅเจพเจ 'เจคเฉ เจฐเจนเจฟเฉฐเจฆเฉ เจนเฉเฅค
เจเจธ เจฌเจฟเฉฐเจฆเฉ เจคเฉฑเจ เจฆเฉ เจธเจพเจฐเฉ เจเจชเจฒเฉเจเฉเจธเจผเจจ เจเฉเจก เจจเฉเฉฐ เจฆเฉเจเจฟเจ เจเจพ เจธเจเจฆเจพ เจนเฉ
เจชเฉเจฐเจพเจเจตเฉเจ เจเฉเฉฐเจเฉเจเจ เจฆเฉ เจธเฉเจฐเฉฑเจเจฟเจ เจค เจธเจเฉเจฐเฉเจ
เจธเจชเจธเจผเจ เจเฉเจเจธเจ เจตเจฟเฉฑเจ เจจเจฟเฉฑเจเฉ เจเฉเฉฐเจเฉเจเจ เจจเฉเฉฐ เจธเจเฉเจฐ เจเจฐเจจเจพ เจ เจธเฉเจฐเฉฑเจเจฟเจ เจค เจนเฉ: เจนเจฎเฉเจธเจผเจพ เจเฉฑเจ เจฎเฉเจเจพ เจนเฉเฉฐเจฆเจพ เจนเฉ เจเจฟ เจคเฉเจนเจพเจจเฉเฉฐ เจนเฉเจ เจเฉเจคเจพ เจเจพเจตเฉเจเจพ, เจคเฉเจนเจพเจกเฉ เจเฉฐเจชเจฟเจเจเจฐ เจคเฉฑเจ เจชเจนเฉเฉฐเจ เจชเฉเจฐเจพเจชเจค เจเจฐเฉ, เจ เจคเฉ เจเจธ เจคเจฐเฉเจนเจพเจ เจนเฉเจฐ เจตเฉเฅค เจเจธ เจฒเจ, เจฒเฉเจเจฒ เจธเจเฉเจฐเฉเจ เจตเจฟเฉฑเจ เจ เจธเฉเจ เจเฉเฉฐเจเฉเจเจ เจจเฉเฉฐ เจเฉฑเจ เจชเจพเจธเจตเจฐเจก-เจเจจเจเฉเจฐเจฟเจชเจเจก เจฐเฉเจช เจตเจฟเฉฑเจ เจธเจเฉเจฐ เจเจฐเจพเจเจเฉเฅค
เจตเจงเฉเจฐเฉ เจธเฉเจฐเฉฑเจเจฟเจ เจฒเจ, เจ เจธเฉเจ เจเจชเจฒเฉเจเฉเจธเจผเจจ เจตเจฟเฉฑเจ เจเฉฑเจ เจฒเจพเจ เจธเจเฉเจ เจเฉเฉเจพเจเจเฉ, เจเจฟเจธ เจตเจฟเฉฑเจ เจเฉเฉฐเจเฉเจเจ เจคเฉฑเจ เจเฉเจ เจชเจนเฉเฉฐเจ เจจเจนเฉเจ เจนเฉเจตเฉเจเฉเฅค เจธเจฎเจพเจ เจธเจฎเจพเจชเจค เจนเฉเจฃ เจเจพเจฐเจจ เจ เจธเฉเจ เจเจชเจฃเฉ เจเจช เจเจเจธเจเฉเจเจธเจผเจจ เจจเฉเฉฐ เจฒเฉเจ เจเฉเจคเฉ เจธเจฅเจฟเจคเฉ เจตเจฟเฉฑเจ เจคเจฌเจฆเฉเจฒ เจเจฐ เจฆเฉเจตเจพเจเจเฉเฅค
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 เจฆเฉ เจฌเจฒเฉเจ เจนเฉเจฃ 'เจคเฉ เจฒเฉเจ เจธเฉเฉฑเจ เจเฉเจคเจพ เจเจพเจเจฆเจพ เจนเฉเฅค เจ
เจธเฉเจ เจฒเฉเจเจฒ เจธเจเฉเจฐเฉเจ เจตเจฟเฉฑเจ เจธเฉเจต เจเจฐเจจ เจฒเจ เจเฉเจฃเจเจพเจฐ เจจเฉเฉฐ เจตเฉ เจฌเจฆเจฒเจพเจเจเฉ:
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
เจฌเจนเฉเจค เจนเฉ เจธเจงเจพเจฐเจจ: เจ
เจธเฉเจ เจธเจฟเจฐเจซเจผ เจธเฉเจจเฉเจนเฉ เจฆเฉ เจธเจฅเจฟเจคเฉ เจจเฉเฉฐ เจฌเจฆเจฒเจฆเฉ เจนเจพเจ, เจเฉเจเจฐ เจฒเฉเฉ เจนเฉเจตเฉ เจคเจพเจ เจเจธ 'เจคเฉ เจฆเจธเจคเจเจค เจเจฐเจจ เจคเฉเจ เจฌเจพเจ
เจฆเฅค
เจ เจธเฉเจ UI API เจตเจฟเฉฑเจ เจฎเจจเจเจผเฉเจฐ เจ เจคเฉ เจ เจธเจตเฉเจเจพเจฐ เจเจฐเจฆเฉ เจนเจพเจ, เจชเฉฐเจจเจพ 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