Commit 38f9258e authored by wjywbs@gmail.com's avatar wjywbs@gmail.com

Add onChanged callback for chrome.sessions API.

Currently there is no method to receive the changes of recently closed pages
in chrome.sessions APIs. This callback can give extensions a chance to know
there is a change and update by calling GetRecentlyClosed again.

R=kalman@chromium.org
BUG=353007

Review URL: https://codereview.chromium.org/201393002

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@260464 0039d316-1c4b-4281-b951-d872f2087c98
parent 944074f2
......@@ -35,6 +35,7 @@
#include "content/public/browser/web_contents.h"
#include "extensions/browser/extension_function_dispatcher.h"
#include "extensions/browser/extension_function_registry.h"
#include "extensions/browser/extension_system.h"
#include "extensions/common/error_utils.h"
#include "net/base/net_util.h"
#include "ui/base/layout.h"
......@@ -593,4 +594,59 @@ bool SessionsRestoreFunction::RunImpl() {
: RestoreLocalSession(*session_id, browser);
}
SessionsEventRouter::SessionsEventRouter(Profile* profile)
: profile_(profile),
tab_restore_service_(TabRestoreServiceFactory::GetForProfile(profile)) {
// TabRestoreServiceFactory::GetForProfile() can return NULL (i.e., when in
// incognito mode)
if (tab_restore_service_) {
tab_restore_service_->LoadTabsFromLastSession();
tab_restore_service_->AddObserver(this);
}
}
SessionsEventRouter::~SessionsEventRouter() {
if (tab_restore_service_)
tab_restore_service_->RemoveObserver(this);
}
void SessionsEventRouter::TabRestoreServiceChanged(
TabRestoreService* service) {
scoped_ptr<base::ListValue> args(new base::ListValue());
EventRouter::Get(profile_)->BroadcastEvent(make_scoped_ptr(
new Event(api::sessions::OnChanged::kEventName, args.Pass())));
}
void SessionsEventRouter::TabRestoreServiceDestroyed(
TabRestoreService* service) {
tab_restore_service_ = NULL;
}
SessionsAPI::SessionsAPI(content::BrowserContext* context)
: browser_context_(context) {
EventRouter::Get(browser_context_)->RegisterObserver(this,
api::sessions::OnChanged::kEventName);
}
SessionsAPI::~SessionsAPI() {
}
void SessionsAPI::Shutdown() {
EventRouter::Get(browser_context_)->UnregisterObserver(this);
}
static base::LazyInstance<BrowserContextKeyedAPIFactory<SessionsAPI> >
g_factory = LAZY_INSTANCE_INITIALIZER;
BrowserContextKeyedAPIFactory<SessionsAPI>*
SessionsAPI::GetFactoryInstance() {
return g_factory.Pointer();
}
void SessionsAPI::OnListenerAdded(const EventListenerInfo& details) {
sessions_event_router_.reset(
new SessionsEventRouter(Profile::FromBrowserContext(browser_context_)));
EventRouter::Get(browser_context_)->UnregisterObserver(this);
}
} // namespace extensions
......@@ -13,6 +13,8 @@
#include "chrome/common/extensions/api/sessions.h"
#include "chrome/common/extensions/api/tabs.h"
#include "chrome/common/extensions/api/windows.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/event_router.h"
class Profile;
......@@ -79,6 +81,62 @@ class SessionsRestoreFunction : public ChromeSyncExtensionFunction {
Browser* browser);
};
class SessionsEventRouter : public TabRestoreServiceObserver {
public:
explicit SessionsEventRouter(Profile* profile);
virtual ~SessionsEventRouter();
// Observer callback for TabRestoreServiceObserver. Sends data on
// recently closed tabs to the javascript side of this page to
// display to the user.
virtual void TabRestoreServiceChanged(TabRestoreService* service) OVERRIDE;
// Observer callback to notice when our associated TabRestoreService
// is destroyed.
virtual void TabRestoreServiceDestroyed(TabRestoreService* service) OVERRIDE;
private:
Profile* profile_;
// TabRestoreService that we are observing.
TabRestoreService* tab_restore_service_;
DISALLOW_COPY_AND_ASSIGN(SessionsEventRouter);
};
class SessionsAPI : public BrowserContextKeyedAPI,
public extensions::EventRouter::Observer {
public:
explicit SessionsAPI(content::BrowserContext* context);
virtual ~SessionsAPI();
// BrowserContextKeyedService implementation.
virtual void Shutdown() OVERRIDE;
// BrowserContextKeyedAPI implementation.
static BrowserContextKeyedAPIFactory<SessionsAPI>* GetFactoryInstance();
// EventRouter::Observer implementation.
virtual void OnListenerAdded(const extensions::EventListenerInfo& details)
OVERRIDE;
private:
friend class BrowserContextKeyedAPIFactory<SessionsAPI>;
content::BrowserContext* browser_context_;
// BrowserContextKeyedAPI implementation.
static const char* service_name() {
return "SessionsAPI";
}
static const bool kServiceIsNULLWhileTesting = true;
// Created lazily upon OnListenerAdded.
scoped_ptr<SessionsEventRouter> sessions_event_router_;
DISALLOW_COPY_AND_ASSIGN(SessionsAPI);
};
} // namespace extensions
#endif // CHROME_BROWSER_EXTENSIONS_API_SESSIONS_SESSIONS_API_H__
......@@ -36,6 +36,7 @@
#include "chrome/browser/extensions/api/push_messaging/push_messaging_api.h"
#include "chrome/browser/extensions/api/runtime/runtime_api.h"
#include "chrome/browser/extensions/api/serial/serial_connection.h"
#include "chrome/browser/extensions/api/sessions/sessions_api.h"
#include "chrome/browser/extensions/api/settings_overrides/settings_overrides_api.h"
#include "chrome/browser/extensions/api/signed_in_devices/signed_in_devices_manager.h"
#include "chrome/browser/extensions/api/streams_private/streams_private_api.h"
......@@ -132,6 +133,7 @@ void EnsureBrowserContextKeyedServiceFactoriesBuilt() {
extensions::ProcessesAPI::GetFactoryInstance();
extensions::PushMessagingAPI::GetFactoryInstance();
extensions::RuntimeAPI::GetFactoryInstance();
extensions::SessionsAPI::GetFactoryInstance();
extensions::SettingsOverridesAPI::GetFactoryInstance();
extensions::SignedInDevicesManager::GetFactoryInstance();
#if defined(ENABLE_SPELLCHECK)
......
......@@ -107,6 +107,13 @@
]
}
],
"events": [
{
"name": "onChanged",
"description": "Fired when recently closed tabs and/or windows are changed. This event does not monitor synced sessions changes.",
"type": "function"
}
],
"properties": {
"MAX_SESSION_RESULTS": {
"value": 25,
......
......@@ -59,6 +59,36 @@ function checkEntries(expectedEntries, actualEntries) {
});
}
function checkOnChangedEvent(expectedCallbackCount) {
// The frequency in ms between checking whether the right events have
// fired. Every 10 attempts progress is logged.
var retryPeriod = 100;
var callbackCount = 0;
var done = chrome.test.listenForever(chrome.sessions.onChanged, function() {
callbackCount++;
}
);
return function() {
var retry = 0;
var checkEvent = function() {
if (callbackCount < expectedCallbackCount) {
retry++;
if (retry % 10 == 0)
console.log("Waiting for " +
(expectedCallbackCount - callbackCount) +
" more onChanged events");
window.setTimeout(checkEvent, retryPeriod);
} else {
assertEq(callbackCount, expectedCallbackCount);
done();
}
};
window.setTimeout(checkEvent, retryPeriod);
};
}
chrome.test.runTests([
// After setupWindows
//
......@@ -108,6 +138,8 @@ chrome.test.runTests([
function retrieveClosedTabs() {
// Check that the recently closed list contains what we expect
// after removing tabs.
var checkEvent = checkOnChangedEvent(2);
callForEach(
chrome.tabs.remove,
firstWindowTabIds.slice(0, 2).reverse(),
......@@ -125,6 +157,7 @@ chrome.test.runTests([
entries.forEach(function(entry) {
recentlyClosedTabIds.push(entry.tab.sessionId);
});
checkEvent();
})
);
}
......@@ -134,6 +167,8 @@ chrome.test.runTests([
function retrieveClosedWindows() {
// Check that the recently closed list contains what we expect
// after removing windows.
var checkEvent = checkOnChangedEvent(2);
callForEach(
chrome.windows.remove,
windowIds.slice(1, 3).reverse(),
......@@ -154,6 +189,7 @@ chrome.test.runTests([
entries.forEach(function(entry) {
recentlyClosedWindowIds.push(entry.window.sessionId);
});
checkEvent();
})
);
}
......@@ -197,6 +233,8 @@ chrome.test.runTests([
},
function restoreClosedTabs() {
var checkEvent = checkOnChangedEvent(2);
chrome.windows.get(windowIds[0], {"populate": true},
callbackPass(function(win) {
var tabCountBeforeRestore = win.tabs.length;
......@@ -212,6 +250,7 @@ chrome.test.runTests([
win.tabs.forEach(function(tab, i) {
assertEq(pages[i++], tab.url);
});
checkEvent();
})
);
})
......@@ -219,6 +258,8 @@ chrome.test.runTests([
},
function restoreTabInClosedWindow() {
var checkEvent = checkOnChangedEvent(1);
chrome.windows.getAll({"populate": true}, callbackPass(function(win) {
var windowCountBeforeRestore = win.length;
chrome.sessions.restore(recentlyClosedSecondWindowTabIds[0],
......@@ -229,6 +270,7 @@ chrome.test.runTests([
assertEq(windowCountBeforeRestore + 1, win.length);
assertEq(1, win[win.length - 1].tabs.length);
assertEq(pages[0], win[win.length - 1].tabs[0].url);
checkEvent();
})
);
})
......@@ -237,11 +279,14 @@ chrome.test.runTests([
},
function restoreClosedWindows() {
var checkEvent = checkOnChangedEvent(1);
chrome.windows.getAll({"populate": true}, callbackPass(function(win) {
var windowCountBeforeRestore = win.length;
chrome.sessions.restore(recentlyClosedWindowIds[0],
function(win_session) {
assertEq(1, win_session.window.tabs.length);
checkEvent();
});
function done() {
chrome.windows.getAll({"populate": true},
......@@ -285,6 +330,8 @@ chrome.test.runTests([
},
function restoreMostRecentEntry() {
var checkEvent = checkOnChangedEvent(1);
chrome.windows.getAll({"populate": true}, callbackPass(function(win) {
var windowCountBeforeRestore = win.length;
chrome.sessions.restore(callbackPass(function(win_session) {
......@@ -292,6 +339,7 @@ chrome.test.runTests([
chrome.windows.getAll({"populate": true},
callbackPass(function(win) {
assertEq(windowCountBeforeRestore + 1, win.length);
checkEvent();
})
);
}));
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment