Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support WebExtensions V3 #870

Merged
merged 6 commits into from
Oct 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

*Markdown Here* is a Google Chrome, Firefox, Safari, Opera, and Thunderbird extension that lets you write email<sup>&dagger;</sup> in Markdown<sup>&Dagger;</sup> and render them before sending. It also supports syntax highlighting (just specify the language in a fenced code block).

Writing email with code in it is pretty tedious. Writing Markdown with code in it is easy. I found myself writing email in Markdown in the Github in-browser editor, then copying the preview into email. This is a pretty absurd workflow, so I decided create a tool to write and render Markdown right in the email.
Writing email with code in it is pretty tedious. Writing Markdown with code in it is easy. I found myself writing email in Markdown in the GitHub in-browser editor, then copying the preview into email. This is a pretty absurd workflow, so I decided create a tool to write and render Markdown right in the email.

To discover what can be done with Markdown in *Markdown Here*, check out the [Markdown Here Cheatsheet](https://github.com/adam-p/markdown-here/wiki/Markdown-Here-Cheatsheet) and the other [wiki pages](https://github.com/adam-p/markdown-here/wiki).

Expand Down Expand Up @@ -62,7 +62,7 @@ After installing, make sure to restart Firefox/Thunderbird!
#### Manual/Development

1. Clone this repo.
2. Follow the instructions in the MDN ["Setting up an extension development environment"](https://developer.mozilla.org/en/Setting_up_extension_development_environment) article.
2. Follow the instructions in the MDN ["Your first WebExtension"](https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/Your_first_WebExtension#installing) article.

### Safari

Expand Down Expand Up @@ -184,17 +184,18 @@ node build.js
```


### Chrome and Opera extension
### Chrome, Opera, and Firefox (WebExtension) extension

Create a file with a `.zip` extension containing these files and directories:

```
manifest.json
common/
chrome/
_locales
```

### Firefox/Thunderbird extension
### Thunderbird (XUL) extension

Create a file with a `.xpi` extension containing these files and directories:

Expand Down
101 changes: 72 additions & 29 deletions src/chrome/backgroundscript.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,64 @@
marked:false, hljs:false, Utils:false, CommonLogic:false */
/*jshint devel:true, browser:true*/

/*
* Chrome background script.
*/
if (typeof browser === "undefined") {
// Chrome does not support the browser namespace yet.
// See https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/background
globalThis.browser = chrome;
}

// On each load, check if we should show the options/changelist page.
function onLoad() {
// This timeout is a dirty hack to fix bug #119: "Markdown Here Upgrade
// Notification every time I open Chrome". That issue on Github for details.
// https://github.com/adam-p/markdown-here/issues/119
window.setTimeout(upgradeCheck, 30000);
// We supply a #hash to the background page, so that we know when we're
// loaded via `background.page` (manifest V2 and Firefox manifest V3) vs
// `background.service_worker` (manifest V3 in Chrome and Safari).
var backgroundPage = !!location.hash;

if (!backgroundPage) {
// When loaded via a background page, the support scripts are already
// present. When loaded via a service worker, we need to import them.
// (`importScripts` is only available in service workers.)
importScripts('../common/utils.js');
importScripts('../common/common-logic.js');
importScripts('../common/marked.js');
importScripts('../common/highlightjs/highlight.js');
importScripts('../common/markdown-render.js');
importScripts('../common/options-store.js');
}

// In the interest of improved browser load performace, call `onLoad` after a tick.
window.addEventListener('load', Utils.nextTickFn(onLoad), false);
// Note that this file is both the script for a background page _and_ for a service
// worker. The way these things work are quite different, and we must be cognizant of that
// while writing this file.
//
// The key difference is that a background page is loaded once per browser session; a
// service worker is loaded when extension-related events occur, and then is torn down
// after 30 seconds of inactivity (with lifecycle caveats). This means that we can't rely
// on global variables to store state, and we must be mindful about how we handle
// messages.

// For the background page, this listener is added once and remains active for the browser
// session; for the service worker, this listener is added every time the service worker
// is loaded, and is torn down when the service worker is torn down.
chrome.runtime.onInstalled.addListener((details) => {
if (details.reason !== 'install' && details.reason !== 'update') {
return;
}

// Create the context menu that will signal our main code.
// This must be called only once, when installed or updated, so we do it here.
chrome.contextMenus.create({
id: 'markdown-here-context-menu',
contexts: ['editable'],
title: Utils.getMessage('context_menu_item')
});

// Note: If we find that the upgrade info page opens too often, we may
// need to add delays. See: https://github.com/adam-p/markdown-here/issues/119
upgradeCheck();
});

function upgradeCheck() {
// DISABLED FOR THIS RELEASE
return;

OptionsStore.get(function(options) {
var appManifest = chrome.runtime.getManifest();

Expand All @@ -35,7 +77,7 @@ function upgradeCheck() {
OptionsStore.set({ 'last-version': appManifest.version }, function() {
// This is the very first time the extensions has been run, so show the
// options page.
chrome.tabs.create({ url: chrome.extension.getURL(optionsURL) });
chrome.tabs.create({ url: chrome.runtime.getURL(optionsURL) });
});
}
else if (options['last-version'] !== appManifest.version) {
Expand All @@ -45,23 +87,20 @@ function upgradeCheck() {
// The extension has been newly updated
optionsURL += '?prevVer=' + options['last-version'];

showUpgradeNotification(chrome.extension.getURL(optionsURL));
showUpgradeNotification(chrome.runtime.getURL(optionsURL));
});
}
});
}

// Create the context menu that will signal our main code.
chrome.contextMenus.create({
contexts: ['editable'],
title: Utils.getMessage('context_menu_item'),
onclick: function(info, tab) {
chrome.tabs.sendMessage(tab.id, {action: 'context-click'});
}
// Handle context menu clicks.
chrome.contextMenus.onClicked.addListener(function(info, tab) {
chrome.tabs.sendMessage(tab.id, {action: 'context-click'});
});

// Handle rendering requests from the content script.
// See the comment in markdown-render.js for why we do this.
// Handle rendering requests from the content script. Note that incoming messages will
// revive the service worker, then process the message, then tear down the service worker.
// See the comment in markdown-render.js for why we use these requests.
chrome.runtime.onMessage.addListener(function(request, sender, responseCallback) {
// The content script can load in a not-real tab (like the search box), which
// has an invalid `sender.tab` value. We should just ignore these pages.
Expand Down Expand Up @@ -89,11 +128,11 @@ chrome.runtime.onMessage.addListener(function(request, sender, responseCallback)
}
else if (request.action === 'show-toggle-button') {
if (request.show) {
chrome.browserAction.enable(sender.tab.id);
chrome.browserAction.setTitle({
chrome.action.enable(sender.tab.id);
chrome.action.setTitle({
title: Utils.getMessage('toggle_button_tooltip'),
tabId: sender.tab.id });
chrome.browserAction.setIcon({
chrome.action.setIcon({
path: {
"16": Utils.getLocalURL('/common/images/icon16-button-monochrome.png'),
"19": Utils.getLocalURL('/common/images/icon19-button-monochrome.png'),
Expand All @@ -105,11 +144,11 @@ chrome.runtime.onMessage.addListener(function(request, sender, responseCallback)
return false;
}
else {
chrome.browserAction.disable(sender.tab.id);
chrome.browserAction.setTitle({
chrome.action.disable(sender.tab.id);
chrome.action.setTitle({
title: Utils.getMessage('toggle_button_tooltip_disabled'),
tabId: sender.tab.id });
chrome.browserAction.setIcon({
chrome.action.setIcon({
path: {
"16": Utils.getLocalURL('/common/images/icon16-button-disabled.png'),
"19": Utils.getLocalURL('/common/images/icon19-button-disabled.png'),
Expand Down Expand Up @@ -148,7 +187,7 @@ chrome.runtime.onMessage.addListener(function(request, sender, responseCallback)
});

// Add the browserAction (the button in the browser toolbar) listener.
chrome.browserAction.onClicked.addListener(function(tab) {
chrome.action.onClicked.addListener(function(tab) {
chrome.tabs.sendMessage(tab.id, {action: 'button-click', });
});

Expand Down Expand Up @@ -191,6 +230,10 @@ function showUpgradeNotification(optionsURL) {
});
};

// TODO: This interval won't keep the service worker alive, so if a content script
// doesn't reload in about 30 seconds, we'll lose the interval and the notification
// won't show.
// Maybe use the Alarms API? Maybe restructure this so that it's less hacky?
showUpgradeNotificationInterval = setInterval(askTabsToShowNotification, 5000);
});
}
Expand Down
9 changes: 8 additions & 1 deletion src/common/CHANGES.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
Change Log
==========

2024-10-03: v2.14.0
-------------------

* Fixed [bug #722](https://github.com/adam-p/markdown-here/issues/722): Added support for WebExtensions manifest V3. (If you've seen warnings about the extension lately, this was why.)
- Thanks to [Andrew M. MacFie](https://github.com/amacfie) and [Alexander Popov](https://github.com/AlexWayfer).
* Fixed [bug #865](https://github.com/adam-p/markdown-here/issues/865): Add Chrome Store privacy information.

2018-09-30: v2.13.4
--------------------

* Fixed [bug #524] and [bug #526]: Due to v2.13.3 fix, Markdown Here didn't work in Thunderbird with a non-English UI.
* Fixed [bug #524](https://github.com/adam-p/markdown-here/issues/524) and [bug #526](https://github.com/adam-p/markdown-here/issues/526): Due to v2.13.3 fix, Markdown Here didn't work in Thunderbird with a non-English UI.
- Thanks to [KSR-Yasuda](https://github.com/KSR-Yasuda), [ensleep](https://github.com/ensleep), [Pedro Silva](https://github.com/pmanu93), [Christophe Meyer](https://github.com/stombi), [littdky](https://github.com/littdky), [Michael Lashkevich](https://github.com/lashkevi), [morsedl](https://github.com/morsedl).


Expand Down
10 changes: 6 additions & 4 deletions src/common/common-logic.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,12 @@ function getUpgradeNotification(optionsURL, responseCallback) {

Utils.getLocalFile(
Utils.getLocalURL('/common/upgrade-notification.html'),
'text/html',
'text',
function(html) {
// Get the logo image data
Utils.getLocalFileAsBase64(
Utils.getLocalFile(
Utils.getLocalURL('/common/images/icon32.png'),
'base64',
function(logoBase64) {
// Do some rough template replacement
html = html.replace('{{optionsURL}}', optionsURL)
Expand Down Expand Up @@ -72,7 +73,7 @@ function getForgotToRenderPromptContent(responseCallback) {

Utils.getLocalFile(
Utils.getLocalURL('/common/forgot-to-render-prompt.html'),
'text/html',
'text',
function(html) {
html = html.replace('{{forgot_to_render_prompt_title}}', Utils.getMessage('forgot_to_render_prompt_title'))
.replace('{{forgot_to_render_prompt_info}}', Utils.getMessage('forgot_to_render_prompt_info'))
Expand All @@ -82,8 +83,9 @@ function getForgotToRenderPromptContent(responseCallback) {
.replace('{{forgot_to_render_send_button}}', Utils.getMessage('forgot_to_render_send_button'));

// Get the logo image data
Utils.getLocalFileAsBase64(
Utils.getLocalFile(
Utils.getLocalURL('/common/images/icon48.png'),
'base64',
function(logoBase64) {
// Do some rough template replacement
html = html.replace('{{logoBase64}}', logoBase64);
Expand Down
40 changes: 14 additions & 26 deletions src/common/options-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -122,8 +122,8 @@ var ChromeOptionsStore = {

// The default values or URLs for our various options.
defaults: {
'main-css': {'__defaultFromFile__': '/common/default.css', '__mimeType__': 'text/css'},
'syntax-css': {'__defaultFromFile__': '/common/highlightjs/styles/github.css', '__mimeType__': 'text/css'},
'main-css': {'__defaultFromFile__': '/common/default.css', '__dataType__': 'text'},
'syntax-css': {'__defaultFromFile__': '/common/highlightjs/styles/github.css', '__dataType__': 'text'},
'math-enabled': DEFAULTS['math-enabled'],
'math-value': DEFAULTS['math-value'],
'hotkey': DEFAULTS['hotkey'],
Expand Down Expand Up @@ -286,8 +286,8 @@ var MozillaOptionsStore = {
// The default values or URLs for our various options.
defaults: {
'local-first-run': true,
'main-css': {'__defaultFromFile__': 'resource://markdown_here_common/default.css', '__mimeType__': 'text/css'},
'syntax-css': {'__defaultFromFile__': 'resource://markdown_here_common/highlightjs/styles/github.css', '__mimeType__': 'text/css'},
'main-css': {'__defaultFromFile__': 'resource://markdown_here_common/default.css', '__dataType__': 'text/css'},
'syntax-css': {'__defaultFromFile__': 'resource://markdown_here_common/highlightjs/styles/github.css', '__dataType__': 'text/css'},
'math-enabled': DEFAULTS['math-enabled'],
'math-value': DEFAULTS['math-value'],
'hotkey': DEFAULTS['hotkey'],
Expand Down Expand Up @@ -461,8 +461,8 @@ var SafariOptionsStore = {

// The default values or URLs for our various options.
defaults: {
'main-css': {'__defaultFromFile__': (typeof(safari) !== 'undefined' ? safari.extension.baseURI : '')+'markdown-here/src/common/default.css', '__mimeType__': 'text/css'},
'syntax-css': {'__defaultFromFile__': (typeof(safari) !== 'undefined' ? safari.extension.baseURI : '')+'markdown-here/src/common/highlightjs/styles/github.css', '__mimeType__': 'text/css'},
'main-css': {'__defaultFromFile__': (typeof(safari) !== 'undefined' ? safari.extension.baseURI : '')+'markdown-here/src/common/default.css', '__dataType__': 'text/css'},
'syntax-css': {'__defaultFromFile__': (typeof(safari) !== 'undefined' ? safari.extension.baseURI : '')+'markdown-here/src/common/highlightjs/styles/github.css', '__dataType__': 'text/css'},
'math-enabled': DEFAULTS['math-enabled'],
'math-value': DEFAULTS['math-value'],
'hotkey': DEFAULTS['hotkey'],
Expand Down Expand Up @@ -524,31 +524,19 @@ this.OptionsStore._fillDefaults = function(prefsObj, callback) {
}

// This function may be asynchronous (if XHR occurs) or it may be a straight
// recursion.
// synchronous callback invocation.
function doDefaultForKey(key, callback) {
// Only take action if the key doesn't already have a value set.
if (typeof(prefsObj[key]) === 'undefined') {
if (that.defaults[key].hasOwnProperty('__defaultFromFile__')) {
var xhr = new window.XMLHttpRequest();

if (that.defaults[key]['__mimeType__']) {
xhr.overrideMimeType(that.defaults[key]['__mimeType__']);
}

// Get the default value from the indicated file.
xhr.open('GET', that.defaults[key]['__defaultFromFile__']);

xhr.onreadystatechange = function() {
if (this.readyState === this.DONE) {
// Assume 200 OK -- it's just a local call
prefsObj[key] = this.responseText;

Utils.getLocalFile(
that.defaults[key]['__defaultFromFile__'],
that.defaults[key]['__dataType__'] || 'text',
function(data) {
prefsObj[key] = data;
callback();
return;
}
};

xhr.send();
});
return;
}
else {
// Set the default.
Expand Down
4 changes: 4 additions & 0 deletions src/common/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,10 @@
display: none;
}

input[type="checkbox"], input[type="checkbox"] + label {
cursor: pointer;
}

/* Custom PayPal button styles from http://visitsteve.com/made/diy-paypal-buttons/ */
.paypal-button {
color: #2e3192;
Expand Down
Loading