אם כבר עברת את המאמר ההרחבה הראשונה שלך , כבר קיבלת מושג איך לכתוב הרחבה. במאמר זה, נכתוב הרחבה קצת יותר מורכבת המדגימה עוד קצת מממשקי הפיתוח.

ההרחבה מוסיפה כפתור חדש לסרגל הכלים של פיירפוקס. כשהמשתמש/ת מקליק/ה על הכפתור, נציג חלון קופץ המאפשר לבחור חיה. בהיבחר חיה, נחליף את תוכן הדף בחיה שנבחרה

כדי לממש זאת, אנו:

  • נגדיר פעולת דפדפן, שהיא כפתור המחובר לסרגל הכלים של פיירפוקס.
    לכפתור זה נספק:
    • צלמית בשם "beasts-32.png"
    • חלון קופץ שייפתח כשהכפתור נלחץ. החלון הקופץ יכיל HTML, CSS, ו- JavaScript.
  • נגדיר צלמית עבור ההרחבה, ושמה "beasts-48.png". היא תהיה מוצגת  ב"ניהול ההרחבות שלך".
  • נכתוב תסריט תוכן, "beastify.js" שיוחדר לתוך דפי הרשת.
    זה הקוד שמשנה בפועל את הדפים.
  • נארוז כמה תמונות של חיות, להחליף תץמונות בדף הרשת.
    נכין את התמונות "web accessible resources" כך שדף הרשת יפנה אליהן.

תוכלו לדמיין את המבנה בכללותו של ההרחבה כך:

זוהי הרחבה פשוטה, אך מציגה רבים מהקונספטים הבסיסיים של ממשק פיתוח הרחבות הרשת:

  • הוספת כפתור לסרגל הכלים
  • הוספת פאנל של חלון קו]ץ באמצעות HTML, CSS, ו-JavaScript
  • החדרת תסריט תוכן לתוך דפי רשת
  • תקשורת בין תסריטי תוכן לבין שאר ההרחבה
  • אריזת משאבים היכולים להיות בשימוש על ידי הדפים ברשת עם ההרחבה

תוכלו למצוא complete source code for the extension on GitHub.

כדי לכתוב את ההרחבה, יש צורך בפיירפוקס מגרסא 45 או חדשה יותר.

כתיבת ההרחבה

צרו תיקייה חדשה ונווטו לתוכה:

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_version, name, ו- version, הם חובה ומכילים מטאדאדה בסיסית עבור ההרחבה .
  • description ו- homepage_url הם אופציונאליים, אך מומלצים: הם מספקים מידע שימושי אודות ההרחבה.
  • icons הוא אופציונאלי, אך מומלץ: הוא מאפשר לציין צלמית עבור ההרחבה , שתהיה מוצגת ב"ניהול ההרחבות שלך".
  • permissions מהווה את רשימת ההרשאות שההרחבה זקוקה להן . אנו רק מבקשים  הרשאותactiveTab כאן.
  • browser_action מציין את הכפתור בסרגל הכלים. We'רנו מספקים 3 פיסות מידע כאן:
    • default_icon הוא חובה, ומצביע לצלמית שבכפתור
    • default_title הוא אופציונאלי, ויוצג כעצת הכלי
    • default_popup יהיה בשימוש אם תרצו שחלון קופץ יוצג כאשר המשתמש/ת מקליק/ה על הכפתור . אנו רוצים זאת, אז כללנו את המפתח וגרמנו לו להצביע לקובץ  HTML הכלול בהרחבה.
  • web_accessible_resources מהווה רשימת קבצים שברמטננו להפוך לנגישים לדפי רשת . מאחר וההרחבה מחליפה את תוכן הדף בתמונות  שארזנו בתוך ההרחבה, נצטרך להפוך תמונות אלה לנגישות לדף.

לתשומת לבך כל המסלולים הם יחסיים ל-manifest.json עצמו.

הצלמית

להרחבה צריכה להיות צלמית. היא תהיה מוצגת על יד רישום ההרחבה ב"ניהול ההרחבות שלך" (תוכלו לפתוח אותה על ידי ביקור בכתובת "about:addons"). ה- manifest.json הבטיח שתהיה לנו צלמית לסרגל הכלים ב-  "icons/beasts-48.png".

צרו את התיקייה "icons" ושמרו שם את הצלמית בשם  "beasts-48.png".  ניתן להשתמש ב- the one from our example, הלקוחה מתוך ה-Aha-Soft’s Free Retina iconset, ונמצאת בשימוש תחת תנאי הרשיון שלה

אם תבחרו לספק צלמית משלכם, היא צריכה להיות בגודל 48x48 פיקסלים. תוכלו לספק גם צלמית בגודל 96x96 פיקסלים  , לתצוגות ברזולוציה יותר גבוהה, ואם תעשו זאת it היא תהיה מצויינת כתכונה 96 של אובייקט ה-icons ב-manifest.json:

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

הכפתור בסרגל הכלים

הכפתור בסרגל הכלים צריך גם צלמית, וה- manifest.json מבטיח שתהיה לנו צלמית לכפתור בסרגל הכלים ב"icons/beasts-32.png".

שמרו צלמית בשם "beasts-32.png" בתיקייה "icons" . תוכלו להשתמש בזו מהדוגמה שלנו, הלקוחה מתוך IconBeast Lite icon set ובשימוש תחת תנאי הרשיון. שלה.

אם לא יסופק חלון קופץ, יישלח אירוע הקלקה להרחבה כאשר המשתמש/ת מקיש/ה על הכפתור. אם כן יסופק חלון קופץ, אירוע ההקלקה לא יישלח , אך במקום זאת, החלון הקופץ ייפתח . אנו רוצים חלון קופץ, אז הבה ניצור אותו כצעד הבא.

החלון הקופץ

תפקיד החלון הקופץ הוא לאפשר למשתמש/ת לבחור אחת מתוך שלוש חיות.

צרו תיקייה חדשה בשם "popup" תחת שורש ההרחבה. זה המקום שבו נשמור את הקוד עבור החלון הקופץ. החלון הקופץ יהיה מורכב משלושה קבצים:

  • choose_beast.html מגדיר את תוכן הלוח
  • choose_beast.css מסגנן את התוכן
  • choose_beast.js מטפל בבחירת המשתמש/ת על ידי הרצת תסריט תוכן בלשונית הפעילה.
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" המכיל אלמנט עבור כל חיה שנבחרה. יש לנו עוד אלמנט <div> עם ID שערכו "error-content" וסיווגו "hidden". נשתמש בו במקרה שיש בעיה לאתחל את החלון הקופץ .

יש לשים לב לכך שאני כוללים את קובצי  ה-  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(). אם ביצוע תסריט התוכן הצליח , תסריט התוכן יישאר טעון בעמוד עד שהלשונית נסגרת או שהמשתמש/ת מנווט/ת לדף שונה.

סיבה נפוצה שהקריאה ל- browser.tabs.executeScript() עלולה להיכשל היא שלא ניתן לבתע תסריטי תוכן בכל הדפים. לדוגמא, לא ניתן לבצע אותם בדפים פריגילגיים של הדפדפן כגון "about:debugging" , ולא ניתן לבצע אותם בדפים בתוך המתחם  addons.mozilla.org. אם זה נכשל,, reportExecuteScriptError() יסתיר את ה- "popup-content" <div>, יציג את ה- "error-content" <div>, וירשום שגיאה ל-console.

אם ביצוע תסריט התוכן יצליח, נקרא ל- listenForClicks(). הוא יאזין לקליקים בחלון הקופץ..

  • אם נלחץ כםתור עם class="beast", אזי נקרא ל-beastify().
  • אם נלחץ כפתור עם class="reset", אזי נקרא ל-reset().

הפונקציה beastify() מבצעת שלושה דברים:

  • ממפה את הכפתור הנלחץ לכתובת URL המצביע לתמונה של חיה מסויימת
  • מסתירה את כל התוכן של הדף על ידי החדרת CSS, באמצעות ממשק הפיתוח browser.tabs.insertCSS()
  • שולחת הודעת "beastify" לתסריט התוכן באמצעות ממשק הפיתוח browser.tabs.sendMessage(), תוך בקשה לשנות את העמוד לתמונת חיה, ושליחת כתובת ה- URL לתמונת החיה.

הפונקציה reset() בהכרח מבטלת את פעולת  beastify:

  • מסירה את ה- CSS שהוספנו, באמצעות ממשק הפיתוח  browser.tabs.removeCSS()
  • שולחת הודעת  "reset" לתסריט התוכן בבקשה להשיב את הדף לקדמותו.

תסריט התוכן

צרו תיקייה חדשה, תחת שורש ההרחבה, ששמה "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();
    }
  });

})();

הדבר הראשון שתסריט התוכן עושה  הוא לבדוק שקיים משתנה גלובלי בשם window.hasRun: אם הוא מוגדר כאמת התסריט ירוץ מוקדם, אחרת הוא מציב ערך אמת ל0 window.hasRun וממשיך. הסיבה שאנו עושים זאת היא שכל פעם שהמשתמש/ת פותח/ת את החלון הקופץ , החלון הקופץ מבצע תסריט תוכן בלשונית הפעילה , כך שיכולים להיות לנו מופעים מרובים של התסריט רצים בלשונית יחידה. אם זה קורה, נצטרך לדאוג שרק המופע הראשון יעשה משהו באמת.

אחר כך, המקום להתחיל הוא שורה 40, בה תסריט התוכן מאזין להודעות מהחלון הקופץ, באמצעות ממשק הפיתוח  browser.runtime.onMessage. ראינו לעיל כי תסריט החלון הקופץ יכול לשלוח שני סוגי הודעות שונים : "beastify" ו-"reset".

  • אם ההודעה היא "beastify", נצפה ממנה לכלול כתובת URL המצביעה לתמונת חיה. נסיר כל חיה שייתכן ונוספה על ידי קריאות קודמות ל-  "beastify" אז נבנות קריאות ומספחות אלמנט   <img> שהמאפיין src שלו מוגדר להיות כתובת   URL של חיה.
  • אם ההודעה היא "reset", פשוט נסיר כל חיה  שייתכן שנוספה.

החיות

לסיום, עלינו לכלול את התמונות של החיות.

צרו תיקייה חדשה ששמה "beasts", והוסיפו שלוש תמונות לתיקייה זו, אם השמות הנכונים. תוכלו להשיג את התמונות ממאגר ה-GitHub , או מפה:

לנסות את זה

תחילה, בדקו היטב שהקבצים הנכונים נמצאים במקומות הנכונים:

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" בפיירפוקס, הקליקו  "טעינת תוספות זמניות...", ובחרו את ה- manifest.json שלכם. תצטרכו לראות כעת את צלמית ההרחבה מופיעה בסרגל הכלים של פיירפוקס

פתחו דף ברשת, ואז הקליקו על הצלמית, בחרו חיה , ורארו את הדף משתנה:

פיתוח משורת הפקודה

תוכלו לבצע אוטומציה של צעד ההתקנה הזמנית על ידי שימוש בכלי  web-ext. נסו את זה:

cd beastify
web-ext run

Document Tags and Contributors

Contributors to this page: AmitYaron
Last updated by: AmitYaron,