ChromeからFirefoxへの拡張機能の移植

スクリーンショットを作成するための多くのアプリケーション(clip2net、gyazoなど)がありますが、自分のニーズに合わせて変更して使用できるオープンソースのクロスプラットフォームソリューションはありません(この場合、スクリーンショットを自動的にjiraにアップロードする必要がありました) ) この点で、この機能をブラウザー(Chrome、Firefox)内に実装することは喜ばしい決断でした。これで問題を解決できます。









Chromeにオープンソースプロジェクト「 chrome-screen-capture 」があり、問題なく使用でき、わずかに変更されている場合、Firefoxにはそのような解決策はありません。 この問題を修正することにしました。 これを行うために、Firefox用の拡張機能chrome-screen-captureを移植しました。



Firefoxの拡張機能の開発段階については詳しく説明しません。詳細な手順はMozilla Webサイトにあります。



移植時に発生した問題についてお話したいと思います。



Firefox XULとChrome HTML



おそらく問題ではなく、機能:すべてのFirefox拡張機能はXULを使用してインターフェイスを構築し、ChromeはHTMLを使用します。



「キャプチャエリア」インターフェイスの例を示します。





HTML:

<div id="sc_drag_area_protector"> <div id="sc_drag_shadow_top" style="height: 56px; width: 766px;"></div> <div id="sc_drag_shadow_bottom" style="height: 205px; width: 765px;"></div> <div id="sc_drag_shadow_left" style="height: 356px; width: 515px;"></div> <div id="sc_drag_shadow_right" style="height: 207px; width: 514px;"></div> <div id="sc_drag_area" style="left: 515px; top: 56px; width: 250px; height: 150px;"> <div id="sc_drag_container"></div> <div id="sc_drag_size">0 x 0</div> <div id="sc_drag_cancel"></div> <div id="sc_drag_crop">OK</div> <div id="sc_drag_north_west"></div> <div id="sc_drag_north_east"></div> <div id="sc_drag_south_east"></div> <div id="sc_drag_south_west"></div> </div> </div>
      
      





XUL:

 <box id="sc_drag_area_protector"> <box id="sc_drag_shadow_top" style="height: 167px; width: 766px;"></box> <box id="sc_drag_shadow_bottom" style="height: 130px; width: 765px;"></box> <box id="sc_drag_shadow_left" style="height: 281px; width: 515px;"></box> <box id="sc_drag_shadow_right" style="height: 318px; width: 514px;"></box> <box id="sc_drag_area" style="left: 515px; top: 167px; width: 250px; height: 150px;"> <box id="sc_drag_container"></box> <box id="sc_drag_size">0 x 0</box> <box id="sc_drag_cancel"></box> <box id="sc_drag_crop">OK</box> <box id="sc_drag_north_west"></box> <box id="sc_drag_north_east"></box> <box id="sc_drag_south_east"></box> <box id="sc_drag_south_west"></box> </box> </box>
      
      







この例では、変更はわずかです。 そしてこれは喜ばしいことです。



次の例は、ドロップダウンメニューの実装です。





Chromeでは、マニフェストを通じて非常に簡単に実装されます。

 "browser_action": { "default_icon": "images/icon_19.png", "default_title": " ", "default_popup": "popup.html" }
      
      





popup.htmlには、必要なメニュー項目がすでに表示されています。 Firefoxでは、この関数はXULを介して実装されます。

 <toolbarpalette id="BrowserToolbarPalette"> <toolbarbutton id="poputchikScreen" type="menu-button" label="Screenshot" class="toolbarbutton-1" oncommand="screenshot.screen.lastAction(); event.stopPropagation();" image="chrome://screenshot/skin/img/icon.png"> <menupopup> <menuitem label=" " image="chrome://screenshot/skin/img/custom.png" oncommand="screenshot.screen.captureArea(); event.stopPropagation();" class="menuitem-iconic"/> <menuitem label="  " image="chrome://screenshot/skin/img/screen.png" oncommand="screenshot.screen.captureWindow(); event.stopPropagation();" class="menuitem-iconic"/> <menuitem label="  " image="chrome://screenshot/skin/img/whole.png" oncommand="screenshot.screen.captureWebpage(); event.stopPropagation();" class="menuitem-iconic"/> </menupopup> </toolbarbutton> </toolbarpalette>
      
      







ローカルストレージ



Firefoxでは、拡張機能にlocalstorageを使用できませんが、chrome-screen-captureでは積極的に使用されます。 sqliteリポジトリに基づいてアナログを実装する必要がありましたが、このlocalStorage.js polyfillのようになりました。

 Object.defineProperty(window, "localStorage", new (function () { var aKeys = [], oStorage = {}; Object.defineProperty(oStorage, "getItem", { value: function (sKey) { return sqliteStorage.getItem(escape(sKey)); }, writable: false, configurable: false, enumerable: false }); Object.defineProperty(oStorage, "key", { value: function (nKeyId) { return aKeys[nKeyId]; }, writable: false, configurable: false, enumerable: false }); Object.defineProperty(oStorage, "setItem", { value: function (sKey, sValue) { if(!sKey) { return; } sqliteStorage.setItem(escape(sKey), escape(sValue)); }, writable: false, configurable: false, enumerable: false }); Object.defineProperty(oStorage, "length", { get: function () { return aKeys.length; }, configurable: false, enumerable: false }); Object.defineProperty(oStorage, "removeItem", { value: function (sKey) { if(!sKey) { return; } sqliteStorage.removeItem(escape(sKey)); }, writable: false, configurable: false, enumerable: false }); this.get = function () { var iThisIndx; for (var sKey in oStorage) { iThisIndx = aKeys.indexOf(sKey); if (iThisIndx === -1) { oStorage.setItem(sKey, oStorage[sKey]); } else { aKeys.splice(iThisIndx, 1); } delete oStorage[sKey]; } for (aKeys; aKeys.length > 0; aKeys.splice(0, 1)) { oStorage.removeItem(aKeys[0]); } var aCouples = sqliteStorage.getAllItems(); for (var iKey in aCouples) { iKey = unescape(iKey); oStorage[iKey] = unescape(aCouples[iKey]); aKeys.push(iKey); } return oStorage; }; this.configurable = false; this.enumerable = true; })());
      
      





sqlite sqliteStorage.jsでデータを作成および保存するためのサンプルコード:

 Object.defineProperty(window, "sqliteStorage", new (function () { var file = Components.classes["@mozilla.org/file/directory_service;1"] .getService(Components.interfaces.nsIProperties) .get("ProfD", Components.interfaces.nsIFile); var storageService = Components.classes["@mozilla.org/storage/service;1"] .getService(Components.interfaces.mozIStorageService); var mDBConn = null; var tableName = 'screenshot'; var aKeys = [], sStorage = {}; file.append("ScreenshotData"); if( !file.exists() || !file.isDirectory() ) { file.create(Components.interfaces.nsIFile.DIRECTORY_TYPE, 0777); } file.append("screenshot.sqlite"); mDBConn = storageService.openDatabase(file); var create = function () { mDBConn.createTable(tableName, "id integer primary key autoincrement, Name_key TEXT, Key_value TEXT"); mDBConn.executeSimpleSQL('CREATE UNIQUE INDEX idx_name_key ON ' + tableName + ' (Name_key)'); }; Object.defineProperty(sStorage, "getItem", { value: function (sKey) { var statement = null; var result = null; if (!mDBConn.tableExists(tableName)) { create(); } statement = mDBConn.createStatement("SELECT Key_value FROM " + tableName + " where Name_key = '" + sKey + "'"); while (statement.step()) { result = statement.row['Key_value']; } return result; }, writable: false, configurable: false, enumerable: false }); Object.defineProperty(sStorage, "setItem", { value: function (sKey, sValue) { if (!mDBConn.tableExists(tableName)) { create(); } mDBConn.executeSimpleSQL("REPLACE INTO " + tableName + " (Name_key, Key_value) VALUES ('"+sKey+"', '"+sValue+"')"); }, writable: false, configurable: false, enumerable: false }); Object.defineProperty(sStorage, "removeItem", { value: function (sKey) { if (!mDBConn.tableExists(tableName)) { create(); } mDBConn.executeSimpleSQL("DELETE FROM " + tableName + " WHERE Name_key = '"+sKey+"'"); }, writable: false, configurable: false, enumerable: false }); Object.defineProperty(sStorage, "getAllItems", { value: function () { var statement = null; var result = {}; if (!mDBConn.tableExists(tableName)) { create(); } statement = mDBConn.createStatement("SELECT Name_key, Key_value FROM " + tableName + ""); while (statement.step()) { result[statement.row['Name_key']] = statement.row['Key_value']; } return result; }, writable: false, configurable: false, enumerable: false }); this.get = function () { var iThisIndx; for (var sKey in sStorage) { iThisIndx = aKeys.indexOf(sKey); if (iThisIndx === -1) { sStorage.setItem(sKey, sStorage[sKey]); } else { aKeys.splice(iThisIndx, 1); } delete sStorage[sKey]; } for (aKeys; aKeys.length > 0; aKeys.splice(0, 1)) { sStorage.removeItem(aKeys[0]); } return sStorage; }; this.configurable = false; this.enumerable = true; })());
      
      





ストレージにデータを書き込むには、localStorage.setItem( 'fontSize'、 '16')メソッドを呼び出す必要があります。 また、値を取得するには、通常のlocalStorageと同様にlocalStorage.fontSizeを呼び出す必要があります。



ローカリゼーション



ローカリゼーションを転送する際にも問題がありました。Chromeでは、すべてのローカライズは_locales / * / messages.jsonに保存され、Firefoxでは、locale / * / screenshot.dtdとlocale / * / screenshot.propertiesの2つのファイルに保存されます。 screenshot.dtdファイルはXUL要素のローカライズに使用され、screenshot.propertiesファイルはJS内のローカライズに使用されます。 このスキームには大きなマイナス点が1つあり、HTMLのローカライズには使用できません。 また、chrome-screen-captureには統合されたHTML画像エディターがあります。 この点で、screenshot.propertiesファイルに改善が追加されました。



それは:

 highlight=Highlight redact=Redact solid_black=Solid Black
      
      





次のようになりました:

 var i18n = new Object(); i18n.highlight='Highlight'; i18n.redact='Redact'; i18n.solid_black='Solid Black';
      
      





Firefoxでは、ローカライズを接続するために次のコードが使用されました。

 <stringbundleset id="stringbundleset"> <stringbundle id="string-bundle" src="chrome://screenshot/locale/screenshot.properties"/> </stringbundleset>
      
      





JSで使用する場合:

 var stringsBundle = document.getElementById("string-bundle"); console.log(stringsBundle.getString(highlight) + " ");
      
      





ローカリゼーションファイルに変更を加えた後、XULとHTMLの両方で使用できるようになりました。



ローカリゼーションファイルを含めます。

 <script src="chrome://screenshot/locale/screenshot.properties"></script>
      
      





JSでの使用:

 console.log(i18n['highlight']);
      
      





また、作業を楽にするために、ローカリゼーションをChrome形式からFirefox形式に変換するスクリプトconvert_locale.jsを作成しました

 var fs = require('fs'); var path = require('path'); var filePath = process.argv[2]; var dirPath = path.dirname(filePath); var messages = {}; fs.readFile(filePath, function (err, data) { if (err) throw err; messages = JSON.parse(data); generationProp(messages); generationDTD(messages); }); function generationDTD (msg) { var resultDTD = ''; for (var key in msg) { resultDTD += '<!ENTITY ' + key + ' "' + msg[key].message + '">' + "\n"; } writeFile('screenshot.dtd', resultDTD); } function generationProp(msg) { var resultProp = ''; for (var key in msg) { resultProp += key + '=' + msg[key].message + "\n"; } writeFile('screenshot.properties', resultProp); } function writeFile(fileName, data) { var writeFile = path.join(dirPath, fileName); fs.writeFile(writeFile, data, function (err) { if (err) throw err; console.log('generation finish: ' + fileName); }); }
      
      





目的のローカライズされたディレクトリで、messages.jsonファイルを移動し、スクリプトを実行します。

 node convert_locale.js ./screenshot/chrome/locale/de-DE/messages.json generation finish: screenshot.properties generation finish: screenshot.dtd
      
      





その結果、ローカリゼーションが目的の形式である2つのファイルが作成されます。



./screenshot/chrome/locale/de-DE/screenshot.properties

./screenshot/chrome/locale/de-DE/screenshot.dtd



結論



アナログを超える利点:

1.オープンソース(opensource)

2.スクリーンショット領域はページに限定されず、アドレスバーまたはタブをキャプチャできます。

3.ブラウザウィンドウとスクロールバーの表示領域を考慮することなく、ページ全体をキャプチャできます。

4.ページに位置が固定されたオブジェクトがある場合、ページ全体をキャプチャするときにオブジェクトの複製は発生しません。

5.修正されたバグ(ウィンドウのサイズ変更時)。



拡張機能の1つの結果: ページ全体のスクリーンショット



内部目的のために、サードパーティのサービス(Picasa、Facebook、Sinaマイクロブログ、Imgur)にスクリーンショットをアップロードする必要はなかったため、アップロード機能はプロジェクトに実装されておらず、ファイルは単にディスクに保存されます。 プロジェクトがユーザーから需要がある場合、この機能を追加することは難しくありません。 それとも、Habrの読者の1人がこの機能を実装したいのでしょうか? あなたのコミットを楽しみにしています。



githubへのリンク。



All Articles