Commit 2cb0168a authored by Laurent Wouters's avatar Laurent Wouters
Browse files

Fixed issue cweb #10129176 Factoriser le téléchargement de la définition des vues

parent 82e46b03936c
......@@ -24,108 +24,60 @@ import {
MIME,
Tag,
DataSourceLinked,
TabData,
DataSourcePage,
Link,
DataSource,
tryNegotiateData,
MimeInfo,
refersToPrimaryTopic,
refersToData
refersToData,
TabRegistry,
resolveTabData
} from "../common/data";
import { Message } from "../common/messages";
import { Message, updateTab } from "../common/messages";
/// <reference path="./fallback.d.ts"/>
let F = require("./fallback");
import "chrome";
import { DEFAULTS_SOURCE } from "../common/view-defaults";
/**
* The storage key for sources of views
*/
const STORAGE_SOURCES: string = "STORAGE_SOURCES";
import {
reloadRegistryFromStorage,
addViewSource,
removeViewSource
} from "../common/registry";
/**
* The data about the tabs
*/
let allTabs: { [index: number]: TabData } = {};
let allTabs: TabRegistry = {};
/**
* The reference view registry
*/
let registry: definition.ViewRegistry = new definition.ViewRegistry();
function reloadRegistryFromStorage(): Promise<definition.ViewRegistry> {
return new Promise<definition.ViewRegistry>(
(
resolve: (registry: definition.ViewRegistry) => void,
reject: (reason: string) => void
) => {
function onLoadedItems(items: { [key: string]: any }): void {
let sources = items[STORAGE_SOURCES];
if (sources == undefined || sources == null || sources.length == 0) {
registry.sources = [DEFAULTS_SOURCE];
} else {
registry.sources = sources;
}
definition
.loadRegistry(registry)
.then((registry: definition.ViewRegistry) => {
resolve(registry);
})
.catch((reason: any) => {
reject("Failed to load the registry from storage: " + reason);
});
}
let r = chrome.storage.local.get(
STORAGE_SOURCES,
(items: { [key: string]: any }) => onLoadedItems(items)
);
if (r != null && r != undefined) {
let promise = (r as any) as Promise<{ [key: string]: any }>;
promise
.then((items: { [key: string]: any }) => onLoadedItems(items))
.catch((reason: any) => {
reject("Failed to load the registry from storage: " + reason);
});
}
}
);
}
reloadRegistryFromStorage()
/**
* The cache for the remote resources
*/
let cache: definition.ViewResourceCache = {};
reloadRegistryFromStorage(registry)
.then((registry: definition.ViewRegistry) => {})
.catch((reason: string) => {
console.log(reason);
});
/**
* Gets the data about a tab
* @param id The identifier of a tab
*/
function getTabData(id: number): TabData {
if (!allTabs.hasOwnProperty(id)) {
allTabs[id] = new TabData();
}
return allTabs[id];
}
/**
* Updates a tab
* @param id The identifier of a tab
*/
function updateTab(tabId: number): void {
updateIcon(tabId);
chrome.tabs.sendMessage(tabId, {
requestType: "Update",
senderId: "background",
payload: getTabData(tabId)
});
function onTabUpdated(tabId: number): void {
updateTabIcon(tabId);
updateTab(tabId, resolveTabData(allTabs, tabId));
}
/**
* Updates the icon of a tab
* @param id The identifier of a tab
*/
function updateIcon(tabId: number): void {
let data = getTabData(tabId);
function updateTabIcon(tabId: number): void {
let data = resolveTabData(allTabs, tabId);
chrome.browserAction.setIcon({
path: data.command.isActive
? {
......@@ -149,27 +101,12 @@ function updateIcon(tabId: number): void {
});
}
/**
* Toggle the activation state of a tab
* @param tab The activated tab
*/
function toggleActive(tab: chrome.tabs.Tab): void {
let data = getTabData(tab.id);
data.command.isActive = !data.command.isActive;
updateTab(tab.id);
}
/**
* Listen for the activation/de-activation actions
*/
chrome.browserAction.onClicked.addListener(toggleActive);
/**
* When a tab has been activated
* @param tabId The identifier of a tab
*/
function onTabActivated(tabId: number): void {
updateIcon(tabId);
updateTabIcon(tabId);
}
// listen to tab URL changes
......@@ -332,7 +269,7 @@ function doPreempt(
): chrome.webRequest.BlockingResponse {
let headers = details.responseHeaders;
setHeader(headers, "Content-Type", "text/plain");
let data = getTabData(details.tabId);
let data = resolveTabData(allTabs, details.tabId);
data.sources.push(new DataSourcePage(details.url, mime.mime));
data.command.isActive = true; // activate by default
data.selectMain();
......@@ -366,7 +303,7 @@ function onHeadersReceived(
redirectUrl: chrome.extension.getURL("ldviews/index.html") + "?uri=" + uri
};
}
let data = getTabData(details.tabId);
let data = resolveTabData(allTabs, details.tabId);
if (data.mainSource != NO_DATA)
// already detected something
return {};
......@@ -390,13 +327,13 @@ function onHeadersReceived(
.then((source: DataSourceLinked) => {
data.sources.push(source);
data.selectMain();
updateTab(details.tabId);
onTabUpdated(details.tabId);
})
.catch((reason: string) => {
// do nothing
});
}
updateIcon(details.tabId);
updateTabIcon(details.tabId);
return {};
}
......@@ -408,10 +345,10 @@ function onBeforeNavigate(
details: chrome.webNavigation.WebNavigationFramedCallbackDetails
): void {
if (details.tabId == -1 || details.frameId != 0) return;
let data = getTabData(details.tabId);
let data = resolveTabData(allTabs, details.tabId);
data.clear();
data.url = details.url;
updateIcon(details.tabId);
updateTabIcon(details.tabId);
}
/**
......@@ -422,7 +359,7 @@ function onCompleted(
details: chrome.webNavigation.WebNavigationFramedCallbackDetails
): void {
if (details.tabId == -1 || details.frameId != 0) return;
updateTab(details.tabId);
onTabUpdated(details.tabId);
}
// listen to received headers
......@@ -437,33 +374,30 @@ F.registerNavigations(onBeforeNavigate, onCompleted);
chrome.runtime.onMessage.addListener(
(request: Message<any>, sender, sendResponse) => {
if (request.requestType == "SubmitHtmlLinks") {
let data = getTabData(sender.tab.id);
let data = resolveTabData(allTabs, sender.tab.id);
if (data.primaryTopic == null) {
data.primaryTopic = detectTopicOnlinks(request.payload);
}
data.sources = data.sources.concat(detectDataOnLinks(request.payload));
data.selectMain();
updateTab(sender.tab.id);
onTabUpdated(sender.tab.id);
} else if (request.requestType == "GetTabData") {
let tabId = request.payload;
if (tabId == null || tabId == undefined) {
// deduce from the sender
tabId = sender.tab.id;
}
let data = getTabData(tabId);
let data = resolveTabData(allTabs, tabId);
sendResponse(data);
} else if (request.requestType == "UpdateTabData") {
let data = getTabData(request.payload.tabId);
} else if (request.requestType == "SetTabData") {
let data = resolveTabData(allTabs, request.payload.tabId);
data.updateWith(request.payload.tabData);
updateTab(request.payload.tabId);
onTabUpdated(request.payload.tabId);
sendResponse(data);
} else if (request.requestType == "GetViewRegistry") {
sendResponse(registry);
} else if (request.requestType == "AddNewSource") {
registry.sources.push(request.payload);
chrome.storage.local.set({ STORAGE_SOURCES: registry.sources });
definition
.loadRegistry(registry)
addViewSource(registry, request.payload)
.then((newRegistry: definition.ViewRegistry) => {
sendResponse(newRegistry);
})
......@@ -472,10 +406,7 @@ chrome.runtime.onMessage.addListener(
});
return true;
} else if (request.requestType == "RemoveSource") {
registry.sources.splice(request.payload, 1);
chrome.storage.local.set({ STORAGE_SOURCES: registry.sources });
definition
.loadRegistry(registry)
removeViewSource(registry, request.payload)
.then((newRegistry: definition.ViewRegistry) => {
sendResponse(newRegistry);
})
......@@ -483,8 +414,9 @@ chrome.runtime.onMessage.addListener(
console.log("Failed to reload registry: " + reason);
});
return true;
} else if (request.requestType == "ReloadRegistry") {
reloadRegistryFromStorage()
} else if (request.requestType == "ReloadViewRegistry") {
cache = {};
reloadRegistryFromStorage(registry)
.then((newRegistry: definition.ViewRegistry) => {
sendResponse(newRegistry);
})
......@@ -492,6 +424,16 @@ chrome.runtime.onMessage.addListener(
console.log("Failed to reload registry: " + reason);
});
return true;
} else if (request.requestType == "GetResourceContent") {
definition
.getResourceContent(cache, request.payload)
.then((content: string) => {
sendResponse(content);
})
.catch((reason: any) => {
console.log("Failed to fetch the resource: " + reason);
});
return true;
}
}
);
......@@ -477,3 +477,22 @@ export function tryNegotiateData(target: string): Promise<DataSourceLinked> {
xmlHttp.send();
});
}
/**
* A registry of tab data
*/
export interface TabRegistry {
[index: number]: TabData;
}
/**
* Gets the data about a tab
* @param registry The registry of tab data
* @param id The identifier of a tab
*/
export function resolveTabData(registry: TabRegistry, id: number): TabData {
if (!registry.hasOwnProperty(id)) {
registry[id] = new TabData();
}
return registry[id];
}
......@@ -18,20 +18,220 @@
* with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import { TabData } from "./data";
import { definition } from "@logilab/libview";
import "chrome";
/**
* A message between parts of the extension
*/
export interface Message<P, S = string> {
export interface Message<P> {
/**
* The type of request
*/
requestType: string;
/**
* The sender's identification
*/
senderId: S;
/**
* The payload for this message
*/
payload: P;
}
/**
* Gets the view registry from the background
*/
export function getViewRegistry(): Promise<definition.ViewRegistry> {
return new Promise<definition.ViewRegistry>(
(
resolve: (registry: definition.ViewRegistry) => void,
reject: (reason: any) => void
) => {
chrome.runtime.sendMessage(
{
requestType: "GetViewRegistry",
payload: null
},
(registry: definition.ViewRegistry) => {
resolve(registry);
}
);
}
);
}
/**
* Reloads the view registry from the background
*/
export function reloadViewRegistry(): Promise<definition.ViewRegistry> {
return new Promise<definition.ViewRegistry>(
(
resolve: (registry: definition.ViewRegistry) => void,
reject: (reason: any) => void
) => {
chrome.runtime.sendMessage(
{
requestType: "ReloadViewRegistry",
payload: null
},
(registry: definition.ViewRegistry) => {
resolve(registry);
}
);
}
);
}
/**
* Registers a new source of views in the registry
* @param source The source to register
*/
export function addViewSource(
source: definition.ViewRegistrySource
): Promise<definition.ViewRegistry> {
return new Promise<definition.ViewRegistry>(
(
resolve: (registry: definition.ViewRegistry) => void,
reject: (reason: any) => void
) => {
chrome.runtime.sendMessage(
{
requestType: "AddNewSource",
payload: source
},
(registry: definition.ViewRegistry) => {
resolve(registry);
}
);
}
);
}
/**
* Removes a source of views from the registry
* @param index The index of the source to remove in the registry
*/
export function removeViewSource(
index: number
): Promise<definition.ViewRegistry> {
return new Promise<definition.ViewRegistry>(
(
resolve: (registry: definition.ViewRegistry) => void,
reject: (reason: any) => void
) => {
chrome.runtime.sendMessage(
{
requestType: "RemoveSource",
payload: index
},
(registry: definition.ViewRegistry) => {
resolve(registry);
}
);
}
);
}
/**
* Fetches the content of a resource from the background
* @param resource The resource to fetch
*/
export function getResourceContent(
resource: definition.ViewResource
): Promise<string> {
return new Promise<string>(
(resolve: (registry: string) => void, reject: (reason: any) => void) => {
chrome.runtime.sendMessage(
{
requestType: "GetResourceContent",
payload: resource
},
(content: string) => {
resolve(content);
}
);
}
);
}
/**
* Gets data about the current tab to the background
* @param tabId The corresponding tab number
*/
export function getTabData(tabId: number): Promise<TabData> {
return new Promise<TabData>(
(resolve: (tabData: TabData) => void, reject: (reason: any) => void) => {
chrome.runtime.sendMessage(
{
requestType: "GetTabData",
payload: tabId
},
(tabData: TabData) => {
resolve(tabData);
}
);
}
);
}
/**
* Sets data about the current tab to the background
* @param tabId The corresponding tab number
* @param tabData The new data for the tab
*/
export function setTabData(tabId: number, tabData: TabData): Promise<TabData> {
return new Promise<TabData>(
(resolve: (tabData: TabData) => void, reject: (reason: any) => void) => {
chrome.runtime.sendMessage(
{
requestType: "SetTabData",
payload: {
tabId: tabId,
tabData: tabData
}
},
(tabData: TabData) => {
resolve(tabData);
}
);
}
);
}
/**
* Sends a request for update to a tab
* @param id The identifier of a tab
* @param tabData The data for the tab
*/
export function updateTab(tabId: number, tabData: TabData): void {
chrome.tabs.sendMessage(tabId, {
requestType: "Update",
payload: tabData
});
}
/**
* Sends a request for refresh to a tab
* @param id The identifier of a tab
*/
export function refreshTab(tabId: number): void {
chrome.tabs.sendMessage(tabId, {
requestType: "Refresh",
payload: null
});
}
/**
* Gets the identifier of the currently active tab
*/
export function getCurrentTabId(): Promise<number> {
return new Promise<number>(
(resolve: (tabId: number) => void, reject: (reason: any) => void) => {
chrome.tabs.query(
{ active: true, currentWindow: true },
(tabs: chrome.tabs.Tab[]) => {
let tabId = tabs[0].id;
resolve(tabId);
}
);
}
);
}
/*******************************************************************************
* Copyright 2003-2018 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
* contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
*
* This file is part of CubicWeb.
*
* CubicWeb is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 2.1 of the License, or (at your option)
* any later version.
*
* CubicWeb is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License along
* with CubicWeb. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
import { definition } from "@logilab/libview";
import { DEFAULTS_SOURCE } from "./view-defaults";
/**
* The storage key for sources of views
*/
const STORAGE_SOURCES: string = "STORAGE_SOURCES";
/**
* Loads a registry from the browser's storage
* @param registry The registry to reload
*/
export function reloadRegistryFromStorage(
registry: definition.ViewRegistry
): Promise<definition.ViewRegistry> {
return new Promise<definition.ViewRegistry>(
(
resolve: (registry: definition.ViewRegistry) => void,
reject: (reason: string) => void
) => {
function onLoadedItems(items: { [key: string]: any }): void {
let sources = items[STORAGE_SOURCES];
if (sources == undefined || sources == null || sources.length == 0) {
registry.sources = [DEFAULTS_SOURCE];
} else {
registry.sources = sources;
}
definition
.loadRegistry(registry)
.then((registry: definition.ViewRegistry) => {
resolve(registry);
})
.catch((reason: any) => {
reject("Failed to load the registry from storage: " + reason);
});
}
let r = chrome.storage.local.get(
STORAGE_SOURCES,
(items: { [key: string]: any }) => onLoadedItems(items)
);
if (r != null && r != undefined) {
let promise = (r as any) as Promise<{ [key: string]: any }>;
promise
.then((items: { [key: string]: any }) => onLoadedItems(items))