kumabook/bebop

View on GitHub
src/background.js

Summary

Maintainability
A
3 hrs
Test Coverage
import browser from 'webextension-polyfill';
import logger from 'kiroku';
import search, { init as candidateInit } from './candidates';
import { init as actionInit, find as findAction } from './actions';
import {
  toggle as togglePopupWindow,
  onWindowFocusChanged,
  onTabActivated,
  onTabRemoved,
} from './popup_window';
import { getActiveContentTab } from './utils/tabs';
import idb from './utils/indexedDB';
import { getArgListener, setPostMessageFunction } from './utils/args';
import {
  createObjectStore,
  needDownload,
  downloadBookmarks,
} from './utils/hatebu';
import migrateOptions from './utils/options_migrator';
import config from './config';

let contentScriptPorts = {};
let popupPorts         = {};

if (process.env.NODE_ENV === 'production') {
  logger.setLevel('FATAL');
}
logger.info(`bebop starts initialization. log level ${logger.getLevel()}`);

export function getContentScriptPorts() {
  return Object.values(contentScriptPorts);
}

export function getPopupPorts() {
  return Object.values(popupPorts);
}

function postMessageToContentScript(type, payload) {
  const currentWindow = true;
  const active        = true;
  return browser.tabs.query({ currentWindow, active }).then((tabs) => {
    if (tabs.length > 0) {
      const targetUrl = tabs[0].url;
      getContentScriptPorts().forEach(p => p.postMessage({
        type,
        payload,
        targetUrl,
      }));
    }
  });
}

export function postMessageToPopup(type, payload) {
  getPopupPorts().forEach(p => p.postMessage({
    type,
    payload,
  }));
}

export function executeAction(actionId, candidates) {
  const action = findAction(actionId);
  if (action && action.handler) {
    const f = action.handler;
    return f.call(this, candidates);
  }
  return Promise.resolve();
}

function toggleContentPopup() {
  const msg = { type: 'TOGGLE_POPUP' };
  return getActiveContentTab().then(t => browser.tabs.sendMessage(t.id, msg));
}

function handleContentScriptMessage() {}

function connectListener(port) {
  const { name } = port;
  logger.info(`connect channel: ${name}`);
  if (name.startsWith('content-script')) {
    contentScriptPorts[name] = port;
    port.onDisconnect.addListener(() => {
      delete contentScriptPorts[name];
      port.onMessage.removeListener(handleContentScriptMessage);
    });
    port.onMessage.addListener(handleContentScriptMessage);
  } else if (name.startsWith('popup')) {
    popupPorts[name] = port;
    port.onDisconnect.addListener(() => {
      delete popupPorts[name];
      postMessageToContentScript('POPUP_CLOSE');
    });
  }
  logger.info(`There are ${Object.values(contentScriptPorts).length} channel`);
}

function activatedListener(payload) {
  getPopupPorts().forEach(p => p.postMessage({
    type: 'TAB_CHANGED',
    payload,
  }));
  setTimeout(() => onTabActivated(payload), 10);
}

function downloadHatebu(userName) {
  try {
    if (needDownload()) {
      downloadBookmarks(userName);
    }
  } catch (e) {
    logger.trace(e);
  }
}

export function messageListener(request) {
  switch (request.type) {
    case 'SEND_MESSAGE_TO_ACTIVE_CONTENT_TAB': {
      return getActiveContentTab().then((tab) => {
        if (tab.url.startsWith('chrome://')) {
          return Promise.resolve();
        }
        return browser.tabs.sendMessage(tab.id, request.payload);
      });
    }
    case 'SEARCH_CANDIDATES': {
      const query = request.payload;
      return search(query);
    }
    case 'EXECUTE_ACTION': {
      const { actionId, candidates } = request.payload;
      return executeAction(actionId, candidates);
    }
    case 'RESPONSE_ARG': {
      const listener = getArgListener();
      listener(request.payload);
      break;
    }
    case 'DOWNLOAD_HATEBU': {
      downloadHatebu(request.payload);
      break;
    }
    default:
      break;
  }
  return null;
}

export function commandListener(command) {
  switch (command) {
    case 'toggle_popup_window':
      togglePopupWindow();
      break;
    case 'toggle_content_popup':
      toggleContentPopup();
      break;
    default:
      break;
  }
}

async function loadOptions() {
  const state = await browser.storage.local.get();
  migrateOptions(state);
  candidateInit(state);
  actionInit();
}

export async function storageChangedListener() {
  await loadOptions();
}

export async function init() {
  setPostMessageFunction(postMessageToPopup);
  contentScriptPorts = {};
  popupPorts         = {};

  await loadOptions();
  try {
    await idb.upgrade(config.dbName, config.dbVersion, db => createObjectStore(db));
    logger.info('create indexedDB');
  } catch (e) {
    logger.info(e);
  }

  browser.windows.onFocusChanged.addListener(onWindowFocusChanged);
  browser.runtime.onConnect.addListener(connectListener);
  browser.tabs.onActivated.addListener(activatedListener);
  browser.tabs.onRemoved.addListener(onTabRemoved);
  browser.runtime.onMessage.addListener(messageListener);
  browser.commands.onCommand.addListener(commandListener);
  browser.storage.onChanged.addListener(storageChangedListener);

  logger.info('bebop is initialized.');
}

init();