From bbb96e9c89e6e12fc2a83dc291514ba1859db325 Mon Sep 17 00:00:00 2001 From: Tomeu Vizoso Date: Fri, 4 May 2007 19:32:25 +0200 Subject: [PATCH] Implemented saving web history to the journal. Ifdefed by now while we wait for a patch to be applied to mozilla upstream. --- browser/GeckoDirectoryProvider.cpp | 66 +++ browser/GeckoDirectoryProvider.h | 38 ++ browser/Makefile.am | 12 +- browser/sessionstore/Makefile.am | 34 ++ browser/sessionstore/nsISessionStore.idl | 63 +++ browser/sessionstore/nsSessionStore.js | 541 +++++++++++++++++++++++ browser/sugar-browser.cpp | 100 ++++- browser/sugar-browser.h | 4 + configure.ac | 30 +- sugar/browser/_sugarbrowser.defs | 15 + 10 files changed, 875 insertions(+), 28 deletions(-) create mode 100644 browser/GeckoDirectoryProvider.cpp create mode 100644 browser/GeckoDirectoryProvider.h create mode 100644 browser/sessionstore/Makefile.am create mode 100644 browser/sessionstore/nsISessionStore.idl create mode 100644 browser/sessionstore/nsSessionStore.js diff --git a/browser/GeckoDirectoryProvider.cpp b/browser/GeckoDirectoryProvider.cpp new file mode 100644 index 00000000..c94a7cf0 --- /dev/null +++ b/browser/GeckoDirectoryProvider.cpp @@ -0,0 +1,66 @@ +#include "GeckoDirectoryProvider.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +NS_IMPL_ISUPPORTS2 (GeckoDirectoryProvider, + nsIDirectoryServiceProvider, + nsIDirectoryServiceProvider2) + +GeckoDirectoryProvider::GeckoDirectoryProvider(const char *sugar_path) +{ + mComponentPath = g_strconcat(sugar_path, "/mozilla/components", NULL); +} + +GeckoDirectoryProvider::~GeckoDirectoryProvider() +{ + if(mComponentPath) + g_free(mComponentPath); +} + +/* nsIFile getFile (in string prop, out PRBool persistent); */ +NS_IMETHODIMP +GeckoDirectoryProvider::GetFile (const char *prop, + PRBool *persistent, + nsIFile **_retval) +{ + return NS_ERROR_FAILURE; +} + +/* nsISimpleEnumerator getFiles (in string aProperty); */ +NS_IMETHODIMP +GeckoDirectoryProvider::GetFiles (const char *aProperty, nsISimpleEnumerator **aResult) +{ + nsresult rv = NS_ERROR_FAILURE; + + if (!strcmp(aProperty, NS_XPCOM_COMPONENT_DIR_LIST)) { + if (mComponentPath) { + nsCOMPtr componentDir; + rv = NS_NewNativeLocalFile(nsDependentCString(mComponentPath), + PR_TRUE, + getter_AddRefs (componentDir)); + NS_ENSURE_SUCCESS (rv, rv); + + nsCOMArray array; + + rv = array.AppendObject (componentDir); + NS_ENSURE_SUCCESS (rv, rv); + + rv = NS_NewArrayEnumerator (aResult, array); + NS_ENSURE_SUCCESS (rv, rv); + + rv = NS_SUCCESS_AGGREGATE_RESULT; + } + } + + return rv; +} diff --git a/browser/GeckoDirectoryProvider.h b/browser/GeckoDirectoryProvider.h new file mode 100644 index 00000000..5b74169f --- /dev/null +++ b/browser/GeckoDirectoryProvider.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2007, One Laptop Per Child + * + * This library 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 of the License, or (at your option) any later version. + * + * This library 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 this library; if not, write to the + * Free Software Foundation, Inc., 59 Temple Place - Suite 330, + * Boston, MA 02111-1307, USA. + */ +#ifndef GECKO_DIRECTORY_PROVIDER_H +#define GECKO_DIRECTORY_PROVIDER_H + +#include + +class GeckoDirectoryProvider : public nsIDirectoryServiceProvider2 +{ + public: + NS_DECL_ISUPPORTS + NS_DECL_NSIDIRECTORYSERVICEPROVIDER + NS_DECL_NSIDIRECTORYSERVICEPROVIDER2 + + GeckoDirectoryProvider(const char *sugar_path); + virtual ~GeckoDirectoryProvider(); + + private: + char *mComponentPath; +}; + +#endif /* GECKO_DIRECTORY_PROVIDER_H */ diff --git a/browser/Makefile.am b/browser/Makefile.am index b4185837..9a7e4b09 100644 --- a/browser/Makefile.am +++ b/browser/Makefile.am @@ -1,9 +1,13 @@ +SUBDIRS = sessionstore + libsugarbrowser_la_CPPFLAGS = \ $(WARN_CFLAGS) \ $(LIB_CFLAGS) \ $(GECKO_CFLAGS) \ $(NSPR_CFLAGS) \ - -I$(MOZILLA_INCLUDE_DIR)/commandhandler \ + -I$(MOZILLA_INCLUDE_DIR)/chrome \ + -I$(MOZILLA_INCLUDE_DIR)/commandhandler \ + -I$(MOZILLA_INCLUDE_DIR)/content \ -I$(MOZILLA_INCLUDE_DIR)/dom \ -I$(MOZILLA_INCLUDE_DIR)/docshell \ -I$(MOZILLA_INCLUDE_DIR)/exthandler \ @@ -35,8 +39,10 @@ libsugarbrowser_la_SOURCES = \ GeckoContentHandler.cpp \ GeckoDocumentObject.h \ GeckoDocumentObject.cpp \ - GeckoDragDropHooks.h \ - GeckoDragDropHooks.cpp \ + GeckoDirectoryProvider.h \ + GeckoDirectoryProvider.cpp \ + GeckoDragDropHooks.h \ + GeckoDragDropHooks.cpp \ GeckoDownload.h \ GeckoDownload.cpp \ sugar-address-entry.c \ diff --git a/browser/sessionstore/Makefile.am b/browser/sessionstore/Makefile.am new file mode 100644 index 00000000..3c9a055c --- /dev/null +++ b/browser/sessionstore/Makefile.am @@ -0,0 +1,34 @@ +sessionstoredir = $(pkgdatadir)/mozilla/components + +sessionstore_DATA = \ + nsISessionStore.xpt \ + nsSessionStore.js + +EXTRA_DIST = $(sessionstore_DATA) + +BUILT_SOURCES = \ + nsISessionStore.xpt \ + nsISessionStore.h + +stamp_files = \ + stamp-nsISessionStore.xpt \ + stamp-nsISessionStore.h + +nsISessionStore.xpt: stamp-nsISessionStore.xpt + @true +stamp-nsISessionStore.xpt: nsISessionStore.idl + $(XPIDL) -m typelib -w -v -I $(MOZILLA_IDL_DIR) -e nsISessionStore.xpt \ + nsISessionStore.idl \ + && echo timestamp > $(@F) + +nsISessionStore.h: stamp-nsISessionStore.h + @true +stamp-nsISessionStore.h: nsISessionStore.idl + $(XPIDL) -m header -w -v -I $(MOZILLA_IDL_DIR) -e nsISessionStore.h \ + nsISessionStore.idl \ + && echo timestamp > $(@F) + +CLEANFILES = $(stamp_files) $(BUILT_SOURCES) +DISTCLEANFILES = $(stamp_files) $(BUILT_SOURCES) +MAINTAINERCLEANFILES = $(stamp_files) $(BUILT_SOURCES) + diff --git a/browser/sessionstore/nsISessionStore.idl b/browser/sessionstore/nsISessionStore.idl new file mode 100644 index 00000000..97f9668e --- /dev/null +++ b/browser/sessionstore/nsISessionStore.idl @@ -0,0 +1,63 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the Session Restore component. + * + * The Initial Developer of the Original Code is + * Simon Bünzli + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dietrich Ayala + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +#include "nsISupports.idl" + +interface nsIWebBrowser; + +/** + * nsISessionStore keeps track of the current browsing state - i.e. + * tab history, cookies, scroll state, form data, POSTDATA and window features + * - and allows to restore everything into one window. + */ + +[scriptable, uuid(11852a90-20de-11db-a98b-0800200c9a66)] +interface nsISessionStore : nsISupports +{ + /** + * @param aBrowser is the browser whose state is to be returned. + * + * @return a JSON string representing the session state. + */ + AString getBrowserState(in nsIWebBrowser aBrowser); + + /** + * @param aBrowser is the browser whose state is to be set. + * @param aState is a JSON string representing a session state. + */ + void setBrowserState(in nsIWebBrowser aBrowser, in AString aState); +}; diff --git a/browser/sessionstore/nsSessionStore.js b/browser/sessionstore/nsSessionStore.js new file mode 100644 index 00000000..0ce34017 --- /dev/null +++ b/browser/sessionstore/nsSessionStore.js @@ -0,0 +1,541 @@ +/* ***** BEGIN LICENSE BLOCK ***** + * Version: MPL 1.1/GPL 2.0/LGPL 2.1 + * + * The contents of this file are subject to the Mozilla Public License Version + * 1.1 (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * http://www.mozilla.org/MPL/ + * + * Software distributed under the License is distributed on an "AS IS" basis, + * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License + * for the specific language governing rights and limitations under the + * License. + * + * The Original Code is the nsSessionStore component. + * + * The Initial Developer of the Original Code is + * Simon Bünzli + * Portions created by the Initial Developer are Copyright (C) 2006 + * the Initial Developer. All Rights Reserved. + * + * Contributor(s): + * Dietrich Ayala + * + * Alternatively, the contents of this file may be used under the terms of + * either the GNU General Public License Version 2 or later (the "GPL"), or + * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"), + * in which case the provisions of the GPL or the LGPL are applicable instead + * of those above. If you wish to allow use of your version of this file only + * under the terms of either the GPL or the LGPL, and not to allow others to + * use your version of this file under the terms of the MPL, indicate your + * decision by deleting the provisions above and replace them with the notice + * and other provisions required by the GPL or the LGPL. If you do not delete + * the provisions above, a recipient may use your version of this file under + * the terms of any one of the MPL, the GPL or the LGPL. + * + * ***** END LICENSE BLOCK ***** */ + +/* + * Heavily adapted to xulrunner for the OLPC from the firefox code in + * http://lxr.mozilla.org/seamonkey/source/browser/components/sessionstore. + * + * May 2007 Tomeu Vizoso + */ + +/** + * Session Storage and Restoration + * + * Overview + * This service keeps track of a user's session, storing the various bits + * required to return the browser to it's current state. The relevant data is + * stored in memory, and is periodically saved to disk in a file in the + * profile directory. The service is started at first window load, in + * delayedStartup, and will restore the session from the data received from + * the nsSessionStartup service. + */ + +/* :::::::: Constants and Helpers ::::::::::::::: */ + +const Cc = Components.classes; +const Ci = Components.interfaces; +const Cr = Components.results; + +const CID = Components.ID("{5280606b-2510-4fe0-97ef-9b5a22eafe6b}"); +const CONTRACT_ID = "@mozilla.org/browser/sessionstore;1"; +const CLASS_NAME = "Browser Session Store Service"; + +// sandbox to evaluate JavaScript code from non-trustable sources +var EVAL_SANDBOX = new Components.utils.Sandbox("about:blank"); + +function debug(aMsg) { + aMsg = ("SessionStore: " + aMsg).replace(/\S{80}/g, "$&\n"); + Cc["@mozilla.org/consoleservice;1"].getService(Ci.nsIConsoleService) + .logStringMessage(aMsg); +} + +/* :::::::: The Service ::::::::::::::: */ + +function SessionStoreService() { +} + +SessionStoreService.prototype = { + +/* ........ nsISessionStore API .............. */ + + getBrowserState: function sss_getBrowserState(aBrowser) { + dump("nsSessionStore::getBrowserState\n") + return this._toJSONString(this._getWindowState(aBrowser)); + }, + + setBrowserState: function sss_setBrowserState(aBrowser, aState) { + dump("nsSessionStore::setBrowserState\n") + this.restoreWindow(aBrowser, "(" + aState + ")"); + }, + +/* ........ Saving Functionality .............. */ + + /** + * Store all session data for a window + * @param aSHistory + * nsISHistory reference + */ + _saveWindowHistory: function sss_saveWindowHistory(aSHistory) { + var entries = []; + dump("nsSessionStore._saveWindowHistory " + aSHistory.count); + for (var i = 0; i < aSHistory.count; i++) { + entries.push(this._serializeHistoryEntry(aSHistory.getEntryAtIndex(i, false))); + } + + return entries; + }, + + /** + * Get an object that is a serialized representation of a History entry + * Used for data storage + * @param aEntry + * nsISHEntry instance + * @returns object + */ + _serializeHistoryEntry: function sss_serializeHistoryEntry(aEntry) { + var entry = { url: aEntry.URI.spec, children: [] }; + + if (aEntry.title && aEntry.title != entry.url) { + entry.title = aEntry.title; + } + if (aEntry.isSubFrame) { + entry.subframe = true; + } + if (!(aEntry instanceof Ci.nsISHEntry)) { + return entry; + } + + var cacheKey = aEntry.cacheKey; + if (cacheKey && cacheKey instanceof Ci.nsISupportsPRUint32) { + entry.cacheKey = cacheKey.data; + } + entry.ID = aEntry.ID; + + var x = {}, y = {}; + aEntry.getScrollPosition(x, y); + entry.scroll = x.value + "," + y.value; + + try { + var prefPostdata = this._prefBranch.getIntPref("sessionstore.postdata"); + if (prefPostdata && aEntry.postData && this._checkPrivacyLevel(aEntry.URI.schemeIs("https"))) { + aEntry.postData.QueryInterface(Ci.nsISeekableStream). + seek(Ci.nsISeekableStream.NS_SEEK_SET, 0); + var stream = Cc["@mozilla.org/scriptableinputstream;1"]. + createInstance(Ci.nsIScriptableInputStream); + stream.init(aEntry.postData); + var postdata = stream.read(stream.available()); + if (prefPostdata == -1 || postdata.replace(/^(Content-.*\r\n)+(\r\n)*/, "").length <= prefPostdata) { + entry.postdata = postdata; + } + } + } + catch (ex) { debug(ex); } // POSTDATA is tricky - especially since some extensions don't get it right + + if (!(aEntry instanceof Ci.nsISHContainer)) { + return entry; + } + + for (var i = 0; i < aEntry.childCount; i++) { + var child = aEntry.GetChildAt(i); + if (child) { + entry.children.push(this._serializeHistoryEntry(child)); + } + else { // to maintain the correct frame order, insert a dummy entry + entry.children.push({ url: "about:blank" }); + } + } + + return entry; + }, + + /** + * serialize session data for a window + * @param aBrowser + * Browser reference + * @returns string + */ + _getWindowState: function sss_getWindowState(aBrowser) { + dump("nsSessionStore::_getWindowState: " + aBrowser + "\n") + windowState = this._collectWindowData(aBrowser); + + /* + this._updateCookies(windowState); + */ + + return windowState; + }, + + _collectWindowData: function sss_collectWindowData(aBrowser) { + dump("nsSessionStore::_collectWindowData\n") + aBrowser.QueryInterface(Ci.nsIWebNavigation); + historyState = this._saveWindowHistory(aBrowser.sessionHistory); + /* + this._updateTextAndScrollData(aWindow); + this._updateCookieHosts(aWindow); + this._updateWindowFeatures(aWindow); + */ + + return {history: historyState/*, textAndScroll: textAndScrollState*/}; + }, + +/* ........ Restoring Functionality .............. */ + + /** + * restore features to a single window + * @param aBrowser + * Browser reference + * @param aState + * JS object or its eval'able source + */ + restoreWindow: function sss_restoreWindow(aBrowser, aState) { + try { + var winData = typeof aState == "string" ? this._safeEval(aState) : aState; + } + catch (ex) { // invalid state object - don't restore anything + debug(ex); + dump(ex); + return; + } + + this.restoreHistoryPrecursor(aBrowser, winData.history); + }, + + /** + * Manage history restoration for a window + * @param aBrowser + * Browser reference + * @param aHistoryData + * History data to be restored + */ + restoreHistoryPrecursor: function sss_restoreHistoryPrecursor(aBrowser, aHistoryData) { + /* + // make sure that all browsers and their histories are available + // - if one's not, resume this check in 100ms (repeat at most 10 times) + for (var t = aIx; t < aTabs.length; t++) { + try { + if (!tabbrowser.getBrowserForTab(aTabs[t]._tab).webNavigation.sessionHistory) { + throw new Error(); + } + } + catch (ex) { // in case browser or history aren't ready yet + if (aCount < 10) { + var restoreHistoryFunc = function(self) { + self.restoreHistoryPrecursor(aWindow, aTabs, aSelectTab, aIx, aCount + 1); + } + aWindow.setTimeout(restoreHistoryFunc, 100, this); + return; + } + } + } + */ + + // helper hash for ensuring unique frame IDs + var aIdMap = { used: {} }; + this.restoreHistory(aBrowser, aHistoryData, aIdMap); + }, + + /** + * Restory history for a window + * @param aBrowser + * Browser reference + * @param aHistoryData + * History data to be restored + * @param aIdMap + * Hash for ensuring unique frame IDs + */ + restoreHistory: function sss_restoreHistory(aBrowser, aHistoryData, aIdMap) { + dump("nsSessionStore::restoreHistory\n") + + aBrowser.QueryInterface(Ci.nsIWebNavigation); + aSHistory = aBrowser.sessionHistory; + aSHistory.QueryInterface(Ci.nsISHistoryInternal); + + if (aSHistory.count > 0) { + aSHistory.PurgeHistory(aSHistory.count); + } + + if (!aHistoryData) { + aHistoryData = []; + } + + for (var i = 0; i < aHistoryData.length; i++) { + aSHistory.addEntry(this._deserializeHistoryEntry(aHistoryData[i], aIdMap), true); + } + + /* + // make sure to reset the capabilities and attributes, in case this tab gets reused + var disallow = (aHistoryData.disallow)?aHistoryData.disallow.split(","):[]; + CAPABILITIES.forEach(function(aCapability) { + browser.docShell["allow" + aCapability] = disallow.indexOf(aCapability) == -1; + }); + Array.filter(tab.attributes, function(aAttr) { + return (_this.xulAttributes.indexOf(aAttr.name) > -1); + }).forEach(tab.removeAttribute, tab); + if (aHistoryData.xultab) { + aHistoryData.xultab.split(" ").forEach(function(aAttr) { + if (/^([^\s=]+)=(.*)/.test(aAttr)) { + tab.setAttribute(RegExp.$1, decodeURI(RegExp.$2)); + } + }); + } + */ + try { + aBrowser.gotoIndex(aHistoryData.length - 1); + } + catch (ex) { dump(ex + "\n"); } // ignore an invalid aHistoryData.index + }, + + /** + * expands serialized history data into a session-history-entry instance + * @param aEntry + * Object containing serialized history data for a URL + * @param aIdMap + * Hash for ensuring unique frame IDs + * @returns nsISHEntry + */ + _deserializeHistoryEntry: function sss_deserializeHistoryEntry(aEntry, aIdMap) { + var shEntry = Cc["@mozilla.org/browser/session-history-entry;1"]. + createInstance(Ci.nsISHEntry); + + var ioService = Cc["@mozilla.org/network/io-service;1"]. + getService(Ci.nsIIOService); + shEntry.setURI(ioService.newURI(aEntry.url, null, null)); + shEntry.setTitle(aEntry.title || aEntry.url); + shEntry.setIsSubFrame(aEntry.subframe || false); + shEntry.loadType = Ci.nsIDocShellLoadInfo.loadHistory; + + if (aEntry.cacheKey) { + var cacheKey = Cc["@mozilla.org/supports-PRUint32;1"]. + createInstance(Ci.nsISupportsPRUint32); + cacheKey.data = aEntry.cacheKey; + shEntry.cacheKey = cacheKey; + } + if (aEntry.ID) { + // get a new unique ID for this frame (since the one from the last + // start might already be in use) + var id = aIdMap[aEntry.ID] || 0; + if (!id) { + for (id = Date.now(); aIdMap.used[id]; id++); + aIdMap[aEntry.ID] = id; + aIdMap.used[id] = true; + } + shEntry.ID = id; + } + + var scrollPos = (aEntry.scroll || "0,0").split(","); + scrollPos = [parseInt(scrollPos[0]) || 0, parseInt(scrollPos[1]) || 0]; + shEntry.setScrollPosition(scrollPos[0], scrollPos[1]); + + if (aEntry.postdata) { + var stream = Cc["@mozilla.org/io/string-input-stream;1"]. + createInstance(Ci.nsIStringInputStream); + stream.setData(aEntry.postdata, -1); + shEntry.postData = stream; + } + + if (aEntry.children && shEntry instanceof Ci.nsISHContainer) { + for (var i = 0; i < aEntry.children.length; i++) { + shEntry.AddChild(this._deserializeHistoryEntry(aEntry.children[i], aIdMap), i); + } + } + + return shEntry; + }, + +/* ........ Auxiliary Functions .............. */ + + /** + * don't save sensitive data if the user doesn't want to + * (distinguishes between encrypted and non-encrypted sites) + * @param aIsHTTPS + * Bool is encrypted + * @returns bool + */ + _checkPrivacyLevel: function sss_checkPrivacyLevel(aIsHTTPS) { + return this._prefBranch.getIntPref("sessionstore.privacy_level") < (aIsHTTPS ? PRIVACY_ENCRYPTED : PRIVACY_FULL); + }, + + /** + * safe eval'ing + */ + _safeEval: function sss_safeEval(aStr) { + return Components.utils.evalInSandbox(aStr, EVAL_SANDBOX); + }, + + /** + * Converts a JavaScript object into a JSON string + * (see http://www.json.org/ for the full grammar). + * + * The inverse operation consists of eval("(" + JSON_string + ")"); + * and should be provably safe. + * + * @param aJSObject is the object to be converted + * @return the object's JSON representation + */ + _toJSONString: function sss_toJSONString(aJSObject) { + // these characters have a special escape notation + const charMap = { "\b": "\\b", "\t": "\\t", "\n": "\\n", "\f": "\\f", + "\r": "\\r", '"': '\\"', "\\": "\\\\" }; + // we use a single string builder for efficiency reasons + var parts = []; + + // this recursive function walks through all objects and appends their + // JSON representation to the string builder + function jsonIfy(aObj) { + if (typeof aObj == "boolean") { + parts.push(aObj ? "true" : "false"); + } + else if (typeof aObj == "number" && isFinite(aObj)) { + // there is no representation for infinite numbers or for NaN! + parts.push(aObj.toString()); + } + else if (typeof aObj == "string") { + aObj = aObj.replace(/[\\"\x00-\x1F\u0080-\uFFFF]/g, function($0) { + // use the special escape notation if one exists, otherwise + // produce a general unicode escape sequence + return charMap[$0] || + "\\u" + ("0000" + $0.charCodeAt(0).toString(16)).slice(-4); + }); + parts.push('"' + aObj + '"') + } + else if (aObj == null) { + parts.push("null"); + } + else if (aObj instanceof Array || aObj instanceof EVAL_SANDBOX.Array) { + parts.push("["); + for (var i = 0; i < aObj.length; i++) { + jsonIfy(aObj[i]); + parts.push(","); + } + if (parts[parts.length - 1] == ",") + parts.pop(); // drop the trailing colon + parts.push("]"); + } + else if (typeof aObj == "object") { + parts.push("{"); + for (var key in aObj) { + jsonIfy(key.toString()); + parts.push(":"); + jsonIfy(aObj[key]); + parts.push(","); + } + if (parts[parts.length - 1] == ",") + parts.pop(); // drop the trailing colon + parts.push("}"); + } + else { + throw new Error("No JSON representation for this object!"); + } + } + jsonIfy(aJSObject); + + var newJSONString = parts.join(" "); + // sanity check - so that API consumers can just eval this string + if (/[^,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t]/.test( + newJSONString.replace(/"(\\.|[^"\\])*"/g, "") + )) + throw new Error("JSON conversion failed unexpectedly!"); + + return newJSONString; + }, + +/* ........ QueryInterface .............. */ + + QueryInterface: function(aIID) { + if (!aIID.equals(Ci.nsISupports) && + !aIID.equals(Ci.nsIObserver) && + !aIID.equals(Ci.nsISupportsWeakReference) && + !aIID.equals(Ci.nsIDOMEventListener) && + !aIID.equals(Ci.nsISessionStore)) { + Components.returnCode = Cr.NS_ERROR_NO_INTERFACE; + return null; + } + + return this; + } +}; + +/* :::::::: Service Registration & Initialization ::::::::::::::: */ + +/* ........ nsIModule .............. */ + +const SessionStoreModule = { + + getClassObject: function(aCompMgr, aCID, aIID) { + if (aCID.equals(CID)) { + return SessionStoreFactory; + } + + Components.returnCode = Cr.NS_ERROR_NOT_REGISTERED; + return null; + }, + + registerSelf: function(aCompMgr, aFileSpec, aLocation, aType) { + aCompMgr.QueryInterface(Ci.nsIComponentRegistrar); + aCompMgr.registerFactoryLocation(CID, CLASS_NAME, CONTRACT_ID, aFileSpec, aLocation, aType); + }, + + unregisterSelf: function(aCompMgr, aLocation, aType) { + aCompMgr.QueryInterface(Ci.nsIComponentRegistrar); + aCompMgr.unregisterFactoryLocation(CID, aLocation); + }, + + canUnload: function(aCompMgr) { + return true; + } +} + +/* ........ nsIFactory .............. */ + +const SessionStoreFactory = { + + createInstance: function(aOuter, aIID) { + if (aOuter != null) { + Components.returnCode = Cr.NS_ERROR_NO_AGGREGATION; + return null; + } + + return (new SessionStoreService()).QueryInterface(aIID); + }, + + lockFactory: function(aLock) { }, + + QueryInterface: function(aIID) { + if (!aIID.equals(Ci.nsISupports) && !aIID.equals(Ci.nsIModule) && + !aIID.equals(Ci.nsIFactory) && !aIID.equals(Ci.nsISessionStore)) { + Components.returnCode = Cr.NS_ERROR_NO_INTERFACE; + return null; + } + + return this; + } +}; + +function NSGetModule(aComMgr, aFileSpec) { + dump("nsSessionStore: NSGetModule\n") + return SessionStoreModule; +} diff --git a/browser/sugar-browser.cpp b/browser/sugar-browser.cpp index df5769b2..f3a3d877 100644 --- a/browser/sugar-browser.cpp +++ b/browser/sugar-browser.cpp @@ -28,6 +28,7 @@ #include "GeckoDragDropHooks.h" #include "GeckoDocumentObject.h" #include "GeckoBrowserPersist.h" +#include "GeckoDirectoryProvider.h" #endif #include @@ -65,6 +66,8 @@ #include #include +#include "sessionstore/nsISessionStore.h" + #define SUGAR_PATH "SUGAR_PATH" enum { @@ -156,6 +159,25 @@ sugar_browser_startup(const char *profile_path, const char *profile_name) old_handler = XSetErrorHandler(error_handler); + #if 0 + GeckoDirectoryProvider *dirProvider = + new GeckoDirectoryProvider(g_getenv(SUGAR_PATH)); + if (!dirProvider) { + g_warning ("failed to create GeckoDirectoryProvider"); + return FALSE; + } + + NS_ADDREF (dirProvider); + + nsCOMPtr dp (do_QueryInterface (dirProvider)); + NS_RELEASE (dirProvider); + dirProvider = nsnull; + + if (!dp) return FALSE; + + gtk_moz_embed_set_directory_service_provider(dp); + #endif + gtk_moz_embed_push_startup(); nsCOMPtr prefService; @@ -354,10 +376,10 @@ sugar_browser_realize(GtkWidget *widget) GTK_WIDGET_CLASS(parent_class)->realize(widget); #ifdef HAVE_NS_WEB_BROWSER - GtkMozEmbed *embed = GTK_MOZ_EMBED(widget); - nsCOMPtr webBrowser; - gtk_moz_embed_get_nsIWebBrowser(embed, getter_AddRefs(webBrowser)); - NS_ENSURE_TRUE(webBrowser, ); + GtkMozEmbed *embed = GTK_MOZ_EMBED(widget); + nsCOMPtr webBrowser; + gtk_moz_embed_get_nsIWebBrowser(embed, getter_AddRefs(webBrowser)); + NS_ENSURE_TRUE(webBrowser, ); nsCOMPtr commandManager = do_GetInterface(webBrowser); if (commandManager) { @@ -732,6 +754,76 @@ sugar_browser_save_document(SugarBrowser *browser, #endif } +char * +sugar_browser_get_session(SugarBrowser *browser) +{ +#if 0 + nsCOMPtr webBrowser; + gtk_moz_embed_get_nsIWebBrowser(GTK_MOZ_EMBED(browser), + getter_AddRefs(webBrowser)); + if (!webBrowser) { + g_warning ("failed to get nsIWebBrowser"); + return NULL; + } + + nsCOMPtr sessionStore; + sessionStore = do_GetService("@mozilla.org/browser/sessionstore;1"); + if (!sessionStore) { + g_warning ("failed to get nsISessionStore"); + return NULL; + } + + nsString session; + nsresult rv = sessionStore->GetBrowserState(webBrowser, session); + if (NS_FAILED(rv)) { + g_warning ("failed to get browser state"); + return NULL; + } + + nsCString sessionUTF8; + NS_UTF16ToCString (session, NS_CSTRING_ENCODING_UTF8, sessionUTF8); + + return g_strdup(sessionUTF8.get()); +#else + return NULL; +#endif +} + +gboolean +sugar_browser_set_session(SugarBrowser *browser, + const char *session) +{ +#if 0 + nsCOMPtr webBrowser; + gtk_moz_embed_get_nsIWebBrowser(GTK_MOZ_EMBED(browser), + getter_AddRefs(webBrowser)); + if (!webBrowser) { + g_warning ("failed to get nsIWebBrowser"); + return FALSE; + } + + nsCOMPtr sessionStore; + sessionStore = do_GetService("@mozilla.org/browser/sessionstore;1"); + if (!sessionStore) { + g_warning ("failed to get nsISessionStore"); + return FALSE; + } + + nsCString sessionUTF8(session); + nsString sessionUTF16; + NS_CStringToUTF16(sessionUTF8, NS_CSTRING_ENCODING_UTF8, sessionUTF16); + nsresult rv = sessionStore->SetBrowserState(webBrowser, sessionUTF16); + if (NS_FAILED(rv)) { + g_warning ("failed to set browser state"); + return FALSE; + } + + return TRUE; +#else + return FALSE; +#endif +} + GType sugar_browser_event_get_type(void) { diff --git a/browser/sugar-browser.h b/browser/sugar-browser.h index 43108ec2..ab74a126 100644 --- a/browser/sugar-browser.h +++ b/browser/sugar-browser.h @@ -74,6 +74,10 @@ gboolean sugar_browser_startup (const char *profile_path, const char *profile_name); void sugar_browser_shutdown (void); +char *sugar_browser_get_session (SugarBrowser *browser); +gboolean sugar_browser_set_session (SugarBrowser *browser, + const char *session); + #define SUGAR_TYPE_BROWSER_EVENT (sugar_browser_event_get_type()) struct _SugarBrowserEvent { diff --git a/configure.ac b/configure.ac index f35f660d..7bcdf0c5 100644 --- a/configure.ac +++ b/configure.ac @@ -19,11 +19,11 @@ AC_PROG_LIBTOOL AC_ARG_ENABLE(ns-web-browser, AC_HELP_STRING([--enable-ns-web-browser], [Enable features which requires access to nsIWebBrowser]), - [have_ns_web_browser=yes], + [have_ns_web_browser=yes], [have_ns_web_browser=no]) if test "x$have_ns_web_browser" != "xno"; then - AC_DEFINE([HAVE_NS_WEB_BROWSER], [1], ["Have nsIWebBrowser"]) + AC_DEFINE([HAVE_NS_WEB_BROWSER], [1], ["Have nsIWebBrowser"]) fi AC_PATH_PROG([GLIB_GENMARSHAL], [glib-genmarshal]) @@ -58,9 +58,14 @@ if test -d "$with_libxul_sdk"; then GECKO_CFLAGS="-I$with_libxul_sdk/sdk/include -DXPCOM_GLUE" XPCOMGLUE_LIBS="-L$with_libxul_sdk/sdk/lib -lxpcomglue" MOZILLA_INCLUDE_DIR="$with_libxul_sdk/include" +XPIDL="$with_libxul_sdk/sdk/bin/xpidl" +MOZILLA_IDL_DIR="$with_libxul_sdk/sdk/idl" AC_SUBST(XPCOMGLUE_LIBS) AC_SUBST(GECKO_CFLAGS) +AC_SUBST(MOZILLA_INCLUDE_DIR) +AC_SUBST(XPIDL) +AC_SUBST(MOZILLA_IDL_DIR) AC_DEFINE([HAVE_GECKO_1_9],[1],[Define if we have gecko 1.9]) @@ -82,25 +87,7 @@ fi else -# xulrunner 1.8 - -PKG_CHECK_MODULES(GECKO, [xulrunner-gtkmozembed >= 1.8], - [have_gecko=true; mozpackage=xulrunner], - [ -PKG_CHECK_MODULES(GECKO, [firefox-gtkmozembed >= 1.5], - [have_gecko=true; mozpackage=firefox], - have_gecko=false) - ]) - -if test "x$have_gecko" = xfalse; then - AC_MSG_ERROR([Could not find xulrunner, mozilla or firefox $mozilla_required_version]) -fi - -GECKO_LDFLAGS="-R`$PKG_CONFIG --variable=libdir $mozpackage-gtkmozembed`" -AC_SUBST(GECKO_LDFLAGS) - -MOZILLA_INCLUDE_DIR="`$PKG_CONFIG --variable=includedir $mozpackage-gtkmozembed`" -AC_SUBST(MOZILLA_INCLUDE_DIR) +AC_MSG_ERROR([Must specify the xulrunner sdk dir (--with-libxul-sdk)]) fi @@ -121,6 +108,7 @@ Makefile bin/Makefile data/Makefile browser/Makefile +browser/sessionstore/Makefile services/Makefile services/presence/Makefile services/clipboard/Makefile diff --git a/sugar/browser/_sugarbrowser.defs b/sugar/browser/_sugarbrowser.defs index ed94f74f..f323d5ec 100644 --- a/sugar/browser/_sugarbrowser.defs +++ b/sugar/browser/_sugarbrowser.defs @@ -113,6 +113,21 @@ (return-type "SugarBrowser*") ) +(define-method get_session + (of-object "SugarBrowser") + (c-name "sugar_browser_get_session") + (return-type "char*") +) + +(define-method set_session + (of-object "SugarBrowser") + (c-name "sugar_browser_set_session") + (return-type "none") + (parameters + '("const-char*" "session") + ) +) + ;; From sugar-key-grabber.h (define-function sugar_key_grabber_get_type