From da78a9e329e272dedb2400b79a3bdeebff387d47 Mon Sep 17 00:00:00 2001 From: Peter Bengtsson Date: Tue, 8 Dec 2020 14:42:17 -0500 Subject: initial commit --- .../your_second_webextension/index.html | 456 +++++++++++++++++++++ 1 file changed, 456 insertions(+) create mode 100644 files/ko/mozilla/add-ons/webextensions/your_second_webextension/index.html (limited to 'files/ko/mozilla/add-ons/webextensions/your_second_webextension/index.html') diff --git a/files/ko/mozilla/add-ons/webextensions/your_second_webextension/index.html b/files/ko/mozilla/add-ons/webextensions/your_second_webextension/index.html new file mode 100644 index 0000000000..1d5bd49ca4 --- /dev/null +++ b/files/ko/mozilla/add-ons/webextensions/your_second_webextension/index.html @@ -0,0 +1,456 @@ +--- +title: Your second extension +slug: Mozilla/Add-ons/WebExtensions/Your_second_WebExtension +translation_of: Mozilla/Add-ons/WebExtensions/Your_second_WebExtension +--- +
{{AddonSidebar}} +

Your first extension 장을 읽었다면, 확장앱을 만드는 법을 알게 되었을 것이다. 이번 장에서는 몇개의 API를 사용하는 약간 더 복잡한 확장앱을 만들어볼 것이다.

+ +

이 확장앱은 파이어폭스 툴바에 새로운 버튼을 추가한다. 사용자가 이 버튼을 클릭하면 동물을 선택하는 팝업을 보여준다. 동물을 선택하면 현재 웹페이지의 내용을 선택한 동물 이미지로 변경한다.

+ +

구현내용:

+ + + +

확장앱의 구조를 표현하면 아래와 같다:

+ +

+ +

간단한 확장앱이지만 WebExtensions API의 기본 개념을 잘 보여줍니다.

+ + + +

깃헙 예제 소스코드.

+ +

이 확장앱을 만드려면 파이어폭스 45이상이 필요하다.

+ +

Writing the extension

+ +

새 디렉토리 생성:

+ +
mkdir beastify
+cd beastify
+ +

manifest.json

+ +

이제 "manifest.json" 파일을 아래와 같은 내용으로 생성:

+ +
{
+
+  "manifest_version": 2,
+  "name": "Beastify",
+  "version": "1.0",
+
+  "description": "Adds a browser action icon to the toolbar. Click the button to choose a beast. The active tab's body content is then replaced with a picture of the chosen beast. See https://developer.mozilla.org/en-US/Add-ons/WebExtensions/Examples#beastify",
+  "homepage_url": "https://github.com/mdn/webextensions-examples/tree/master/beastify",
+  "icons": {
+    "48": "icons/beasts-48.png"
+  },
+
+  "permissions": [
+    "activeTab"
+  ],
+
+  "browser_action": {
+    "default_icon": "icons/beasts-32.png",
+    "default_title": "Beastify",
+    "default_popup": "popup/choose_beast.html"
+  },
+
+  "web_accessible_resources": [
+    "beasts/frog.jpg",
+    "beasts/turtle.jpg",
+    "beasts/snake.jpg"
+  ]
+
+}
+
+ + + +

manifest.json와 연관된 모든 경로의 것들을 기술한다.

+ +

The icon

+ +

확장앱은 아이콘을 가져야한다. 이 아이콘은 애드온 매니저에서 확장앱의 목록에서 보여진다.("about:addons" url을 통해 열수 있다). 이번에 만든 manifest.json는 "icons / beasts-48.png" 아이콘을 정의하였다

+ +

"icons"라는 디렉토리를 만들고 "beasts-48.png" 파일을 그 아래에 저장한다. 우리 예제는 Aha-Soft’s Free Retina iconset에서 가져온 이미지를 라이센스 조건에 따라 사용했다.

+ +

자신만의 아이콘을 사용하려면 48x48 픽셀이어야한다. 또한 고해상도를 위한 96x96 필셀도 지원한다. 만약 고해상도를 지원하게 하고싶다면 manifest.json의 아이콘 부분을 아래처럼 작성하면 된다.

+ +
"icons": {
+  "48": "icons/beasts-48.png",
+  "96": "icons/beasts-96.png"
+}
+ +

The toolbar button

+ +

툴바 버튼도 아이콘이 필요한데, 우리 manifest.json 파일에 툴바 버튼의 아이콘을 "icons/beasts-32.png"으로 기술하였다.

+ +

"icons" 디렉토리 아래 "beasts-32.png"  파일을 저장한다. 우리는  우리 예제의 이미지IconBeast Lite icon set에서 가져온 이미지를 라이센스 조건에 따라 사용했다.

+ +

팝업을 제공하지 않으면 사용자가 버튼을 클릭 할 때 클릭 이벤트가 없어지게된다. 팝업을 제공한다면 이벤트는 없이지지않고 대신 팝업이 열린다. 우리는 팝업을 열어야하니 다음에서 팝업을 정의한다.

+ +

The popup

+ +

이 팝업의 기능은 세 동물중 하나를 선택하는 기능이다.

+ +

확장앱 로트 아래 "popup" 디렉토리를 생성한다. 여기에대가 팝업 관련 파일들을 저장할 것 이다. 이 팝업이 가지는 세가 파일:

+ + + +
mkdir popup
+cd popup
+touch choose_beast.html choose_beast.css choose_beast.js
+
+ +

choose_beast.html

+ +

HTML파일의 내용:

+ +
<!DOCTYPE html>
+
+<html>
+  <head>
+    <meta charset="utf-8">
+    <link rel="stylesheet" href="choose_beast.css"/>
+  </head>
+
+<body>
+  <div id="popup-content">
+    <div class="button beast">Frog</div>
+    <div class="button beast">Turtle</div>
+    <div class="button beast">Snake</div>
+    <div class="button reset">Reset</div>
+  </div>
+  <div id="error-content" class="hidden">
+    <p>Can't beastify this web page.</p><p>Try a different page.</p>
+  </div>
+  <script src="choose_beast.js"></script>
+</body>
+
+</html>
+
+ +

각각의 동물들을 보여주는 항목을 가지는 <div> 요소의 ID에 "popup-content" 를 정의하였다. 또 다른 "error-content" ID를 가지는 <div>는  "hidden" 이라는 class를 정의하였고 팝업을 초기화 할때 문제가 생기는 경우 사용할 것이다.

+ +

이 HTML파일은 일반 웹페이지처럼 CSS파일과 JS파일을 포함한다.

+ +

choose_beast.css

+ +

이 CSS는 팝업의 크기를 정의하고 선택항목의 공간을 정의하는등 기본적인 스타일링을 한다. 또한 class="hidden" 이라고 정의한 항목을 숨긴다. 이것은 "error-content" <div> 가 기본적으로는 숨겨진다는 것을 뜻한다.

+ +
html, body {
+  width: 100px;
+}
+
+.hidden {
+  display: none;
+}
+
+.button {
+  margin: 3% auto;
+  padding: 4px;
+  text-align: center;
+  font-size: 1.5em;
+  cursor: pointer;
+}
+
+.beast:hover {
+  background-color: #CFF2F2;
+}
+
+.beast {
+  background-color: #E5F2F2;
+}
+
+.reset {
+  background-color: #FBFBC9;
+}
+
+.reset:hover {
+  background-color: #EAEA9D;
+}
+
+
+ +

choose_beast.js

+ +

팝업의 자바스크립트 코드는 아래와 같다:

+ +
/**
+ * CSS to hide everything on the page,
+ * except for elements that have the "beastify-image" class.
+ */
+const hidePage = `body > :not(.beastify-image) {
+                    display: none;
+                  }`;
+
+/**
+ * Listen for clicks on the buttons, and send the appropriate message to
+ * the content script in the page.
+ */
+function listenForClicks() {
+  document.addEventListener("click", (e) => {
+
+    /**
+     * Given the name of a beast, get the URL to the corresponding image.
+     */
+    function beastNameToURL(beastName) {
+      switch (beastName) {
+        case "Frog":
+          return browser.extension.getURL("beasts/frog.jpg");
+        case "Snake":
+          return browser.extension.getURL("beasts/snake.jpg");
+        case "Turtle":
+          return browser.extension.getURL("beasts/turtle.jpg");
+      }
+    }
+
+    /**
+     * Insert the page-hiding CSS into the active tab,
+     * then get the beast URL and
+     * send a "beastify" message to the content script in the active tab.
+     */
+    function beastify(tabs) {
+      browser.tabs.insertCSS({code: hidePage}).then(() => {
+        let url = beastNameToURL(e.target.textContent);
+        browser.tabs.sendMessage(tabs[0].id, {
+          command: "beastify",
+          beastURL: url
+        });
+      });
+    }
+
+    /**
+     * Remove the page-hiding CSS from the active tab,
+     * send a "reset" message to the content script in the active tab.
+     */
+    function reset(tabs) {
+      browser.tabs.removeCSS({code: hidePage}).then(() => {
+        browser.tabs.sendMessage(tabs[0].id, {
+          command: "reset",
+        });
+      });
+    }
+
+    /**
+     * Just log the error to the console.
+     */
+    function reportError(error) {
+      console.error(`Could not beastify: ${error}`);
+    }
+
+    /**
+     * Get the active tab,
+     * then call "beastify()" or "reset()" as appropriate.
+     */
+    if (e.target.classList.contains("beast")) {
+      browser.tabs.query({active: true, currentWindow: true})
+        .then(beastify)
+        .catch(reportError);
+    }
+    else if (e.target.classList.contains("reset")) {
+      browser.tabs.query({active: true, currentWindow: true})
+        .then(reset)
+        .catch(reportError);
+    }
+  });
+}
+
+/**
+ * There was an error executing the script.
+ * Display the popup's error message, and hide the normal UI.
+ */
+function reportExecuteScriptError(error) {
+  document.querySelector("#popup-content").classList.add("hidden");
+  document.querySelector("#error-content").classList.remove("hidden");
+  console.error(`Failed to execute beastify content script: ${error.message}`);
+}
+
+/**
+ * When the popup loads, inject a content script into the active tab,
+ * and add a click handler.
+ * If we couldn't inject the script, handle the error.
+ */
+browser.tabs.executeScript({file: "/content_scripts/beastify.js"})
+.then(listenForClicks)
+.catch(reportExecuteScriptError);
+
+
+ +

이 코드의 시작접은 96번째 라인이다. 이 스크립트는 browser.tabs.executeScript () API를 사용해 팝업이 로드될때 활성화된 탭에 content script (beastify.js)를 주입한다. content script를 성공적으로 주입하면 사용자가 탭을 닫거나 페이지를 이동할 때까지 content script가 주입 된 상태로 유지된다.

+ +

browser.tabs.executeScript()API호출이 실패하는 공통적인 이유는 모든 웹페이지에 content scripts를 주입할 수 없기 때문이다. 예를들어 about:debugging 같은 권한이 있는 페이지에서는 content scripts를 주입할 수 없고 addons.mozilla.org 도 마찬가지이다. 이처럼 실패할때는 reportExecuteScriptError()가 호출되어 "popup-content" <div> 를 숨기고 "error-content" <div>를 보여주고 콘솔에 에러를 로깅한다.

+ +

content script 주입이 성공하면 listenForClicks()이 호출 된다. 이 함수는 팝업에서 클릭을 위한 리스너이다.

+ + + +

beastify() 함수는 3가지 기능을 한다:

+ + + +

reset() 함수는 페이지가 동물그림으로 변경된 것을 취소한다:

+ + + +

The content script

+ +

확장앱의 루트에 "content_scripts"라는 디렉토리를 생성하고, "beastify.js" 파일을 아래 내용으로 작성한다:

+ +
(function() {
+  /**
+   * Check and set a global guard variable.
+   * If this content script is injected into the same page again,
+   * it will do nothing next time.
+   */
+  if (window.hasRun) {
+    return;
+  }
+  window.hasRun = true;
+
+  /**
+   * Given a URL to a beast image, remove all existing beasts, then
+   * create and style an IMG node pointing to
+   * that image, then insert the node into the document.
+   */
+  function insertBeast(beastURL) {
+    removeExistingBeasts();
+    let beastImage = document.createElement("img");
+    beastImage.setAttribute("src", beastURL);
+    beastImage.style.height = "100vh";
+    beastImage.className = "beastify-image";
+    document.body.appendChild(beastImage);
+  }
+
+  /**
+   * Remove every beast from the page.
+   */
+  function removeExistingBeasts() {
+    let existingBeasts = document.querySelectorAll(".beastify-image");
+    for (let beast of existingBeasts) {
+      beast.remove();
+    }
+  }
+
+  /**
+   * Listen for messages from the background script.
+   * Call "beastify()" or "reset()".
+  */
+  browser.runtime.onMessage.addListener((message) => {
+    if (message.command === "beastify") {
+      insertBeast(message.beastURL);
+    } else if (message.command === "reset") {
+      removeExistingBeasts();
+    }
+  });
+
+})();
+
+ +

content script는 먼저 전역 변수 window.hasRun을 확인한다: content script가 이미 실행되어 있다면 아무작업도 하지 않고, 아직 주입되어 있지않으면 window.hasRun를 true로 셋팅한뒤 잡업을 계속한다. 이 작업을 하는 이유는 팝업을 열 때마다 활성화된 탭에 content script를 실행하기 때문에 스크립트가 중복으로 실행되기 때문에 첫번째 팝업오픈시에만 content script를 실행해야한다.

+ +

그런 다음 40번째 라인에서 content script는 browser.runtime.onMessage API로 팝업의 메시지를 받는다. 위에서 봤던 팝업의 스크립트는 "beastify"와 "reset" 두 종류의 메시지를 보낸다.

+ + + +

The beasts

+ +

마지막으로 동물의 이미지를 추가한다.

+ +

"beasts" 디렉토리를 만들고 그 아래 적절한 이름의 이미지 세개를 추가한다.  GitHub 저장소에서 이미지를 가져올 수 있다.

+ +

+ +

Testing it out

+ +

먼저 파일들이 제자리에 있는 지 확인한다:

+ +
beastify/
+
+    beasts/
+        frog.jpg
+        snake.jpg
+        turtle.jpg
+
+    content_scripts/
+        beastify.js
+
+    icons/
+        beasts-32.png
+        beasts-48.png
+
+    popup/
+        choose_beast.css
+        choose_beast.html
+        choose_beast.js
+
+    manifest.json
+ +

파이어폭스 45에서 디스크로부터 임시로 확장 기능을 설치할 수 있다.

+ +

파이어폭스에서 about:debugging를 열고 "Load Temporary Add-on"를 클릭한다음 manifest.json 파일을 선택한다. 그러면 파이어폭스 툴바에서 버튼을 볼수 있을것이다.

+ +

{{EmbedYouTube("sAM78GU4P34")}}

+ +

웹페이지를 열고 툴바 버튼을 클릭하고 동물을 선택하면 웹페이지가 바뀌는것을 볼 수 있을것이다.

+ +

{{EmbedYouTube("YMQXyAQSiE8")}}

+ +

Developing from the command line

+ +

아래처럼 web-ext툴을 이용해 설치할 수도 있다.

+ +
cd beastify
+web-ext run
+
-- cgit v1.2.3-54-g00ecf