This source file includes following definitions.
- ExtractSyncedTabDelegate
- weak_ptr_factory_
- weak_ptr_factory_
- Observe
- ApplyChangesFromSyncModel
- StartImpl
- OnNavigationBlocked
- ProcessModifiedTabs
- StartObserving
#include "chrome/browser/sync/glue/session_change_processor.h"
#include <string>
#include <vector>
#include "base/logging.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/extensions/tab_helper.h"
#include "chrome/browser/favicon/favicon_changed_details.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/sync/glue/session_model_associator.h"
#include "chrome/browser/sync/glue/synced_tab_delegate.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "content/public/browser/navigation_controller.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/notification_source.h"
#include "content/public/browser/web_contents.h"
#include "sync/api/sync_error.h"
#include "sync/internal_api/public/base/model_type.h"
#include "sync/internal_api/public/change_record.h"
#include "sync/internal_api/public/read_node.h"
#include "sync/protocol/session_specifics.pb.h"
#if defined(ENABLE_MANAGED_USERS)
#include "chrome/browser/managed_mode/managed_user_service.h"
#include "chrome/browser/managed_mode/managed_user_service_factory.h"
#endif
using content::BrowserThread;
using content::NavigationController;
using content::WebContents;
namespace browser_sync {
namespace {
static const char kNTPOpenTabSyncURL[] = "chrome://newtab/#open_tabs";
SyncedTabDelegate* ExtractSyncedTabDelegate(
const content::NotificationSource& source) {
return SyncedTabDelegate::ImplFromWebContents(
content::Source<NavigationController>(source).ptr()->GetWebContents());
}
}
SessionChangeProcessor::SessionChangeProcessor(
DataTypeErrorHandler* error_handler,
SessionModelAssociator* session_model_associator)
: ChangeProcessor(error_handler),
session_model_associator_(session_model_associator),
profile_(NULL),
setup_for_test_(false),
weak_ptr_factory_(this) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(error_handler);
DCHECK(session_model_associator_);
}
SessionChangeProcessor::SessionChangeProcessor(
DataTypeErrorHandler* error_handler,
SessionModelAssociator* session_model_associator,
bool setup_for_test)
: ChangeProcessor(error_handler),
session_model_associator_(session_model_associator),
profile_(NULL),
setup_for_test_(setup_for_test),
weak_ptr_factory_(this) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(error_handler);
DCHECK(session_model_associator_);
}
SessionChangeProcessor::~SessionChangeProcessor() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
}
void SessionChangeProcessor::Observe(
int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(profile_);
std::vector<SyncedTabDelegate*> modified_tabs;
switch (type) {
case chrome::NOTIFICATION_FAVICON_CHANGED: {
content::Details<FaviconChangedDetails> favicon_details(details);
session_model_associator_->FaviconsUpdated(favicon_details->urls);
return;
}
case chrome::NOTIFICATION_BROWSER_OPENED: {
Browser* browser = content::Source<Browser>(source).ptr();
if (!browser || browser->profile() != profile_) {
return;
}
DVLOG(1) << "Received BROWSER_OPENED for profile " << profile_;
break;
}
case chrome::NOTIFICATION_TAB_PARENTED: {
WebContents* web_contents = content::Source<WebContents>(source).ptr();
SyncedTabDelegate* tab =
SyncedTabDelegate::ImplFromWebContents(web_contents);
if (!tab || tab->profile() != profile_) {
return;
}
modified_tabs.push_back(tab);
DVLOG(1) << "Received TAB_PARENTED for profile " << profile_;
break;
}
case content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME: {
WebContents* web_contents = content::Source<WebContents>(source).ptr();
SyncedTabDelegate* tab =
SyncedTabDelegate::ImplFromWebContents(web_contents);
if (!tab || tab->profile() != profile_) {
return;
}
modified_tabs.push_back(tab);
DVLOG(1) << "Received LOAD_COMPLETED_MAIN_FRAME for profile " << profile_;
break;
}
case content::NOTIFICATION_WEB_CONTENTS_DESTROYED: {
WebContents* web_contents = content::Source<WebContents>(source).ptr();
SyncedTabDelegate* tab =
SyncedTabDelegate::ImplFromWebContents(web_contents);
if (!tab || tab->profile() != profile_)
return;
modified_tabs.push_back(tab);
DVLOG(1) << "Received NOTIFICATION_WEB_CONTENTS_DESTROYED for profile "
<< profile_;
break;
}
case content::NOTIFICATION_NAV_LIST_PRUNED: {
SyncedTabDelegate* tab = ExtractSyncedTabDelegate(source);
if (!tab || tab->profile() != profile_) {
return;
}
modified_tabs.push_back(tab);
DVLOG(1) << "Received NAV_LIST_PRUNED for profile " << profile_;
break;
}
case content::NOTIFICATION_NAV_ENTRY_CHANGED: {
SyncedTabDelegate* tab = ExtractSyncedTabDelegate(source);
if (!tab || tab->profile() != profile_) {
return;
}
modified_tabs.push_back(tab);
DVLOG(1) << "Received NAV_ENTRY_CHANGED for profile " << profile_;
break;
}
case content::NOTIFICATION_NAV_ENTRY_COMMITTED: {
SyncedTabDelegate* tab = ExtractSyncedTabDelegate(source);
if (!tab || tab->profile() != profile_) {
return;
}
modified_tabs.push_back(tab);
DVLOG(1) << "Received NAV_ENTRY_COMMITTED for profile " << profile_;
break;
}
case chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED: {
extensions::TabHelper* extension_tab_helper =
content::Source<extensions::TabHelper>(source).ptr();
if (extension_tab_helper->web_contents()->GetBrowserContext() !=
profile_) {
return;
}
if (extension_tab_helper->extension_app()) {
SyncedTabDelegate* tab = SyncedTabDelegate::ImplFromWebContents(
extension_tab_helper->web_contents());
modified_tabs.push_back(tab);
}
DVLOG(1) << "Received TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED "
<< "for profile " << profile_;
break;
}
default:
LOG(ERROR) << "Received unexpected notification of type "
<< type;
break;
}
ProcessModifiedTabs(modified_tabs);
}
void SessionChangeProcessor::ApplyChangesFromSyncModel(
const syncer::BaseTransaction* trans,
int64 model_version,
const syncer::ImmutableChangeRecordList& changes) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
syncer::ReadNode root(trans);
if (root.InitByTagLookup(syncer::ModelTypeToRootTag(syncer::SESSIONS)) !=
syncer::BaseNode::INIT_OK) {
error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
"Sessions root node lookup failed.");
return;
}
std::string local_tag = session_model_associator_->GetCurrentMachineTag();
for (syncer::ChangeRecordList::const_iterator it =
changes.Get().begin(); it != changes.Get().end(); ++it) {
const syncer::ChangeRecord& change = *it;
syncer::ChangeRecord::Action action(change.action);
if (syncer::ChangeRecord::ACTION_DELETE == action) {
DCHECK_EQ(syncer::GetModelTypeFromSpecifics(it->specifics),
syncer::SESSIONS);
const sync_pb::SessionSpecifics& specifics = it->specifics.session();
if (specifics.session_tag() == local_tag) {
LOG(WARNING) << "Local session data deleted. Ignoring until next local "
<< "navigation event.";
} else {
if (specifics.has_header()) {
session_model_associator_->DisassociateForeignSession(
specifics.session_tag());
}
}
continue;
}
syncer::ReadNode sync_node(trans);
if (sync_node.InitByIdLookup(change.id) != syncer::BaseNode::INIT_OK) {
error_handler()->OnSingleDatatypeUnrecoverableError(FROM_HERE,
"Session node lookup failed.");
return;
}
DCHECK(root.GetId() == sync_node.GetParentId());
DCHECK(syncer::SESSIONS == sync_node.GetModelType());
const sync_pb::SessionSpecifics& specifics(
sync_node.GetSessionSpecifics());
if (specifics.session_tag() == local_tag &&
!setup_for_test_) {
LOG(WARNING) << "Dropping modification to local session.";
return;
}
const base::Time& mtime = sync_node.GetModificationTime();
session_model_associator_->AssociateForeignSpecifics(specifics, mtime);
}
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_FOREIGN_SESSION_UPDATED,
content::Source<Profile>(profile_),
content::NotificationService::NoDetails());
}
void SessionChangeProcessor::StartImpl(Profile* profile) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
DCHECK(profile);
DCHECK(profile_ == NULL);
profile_ = profile;
StartObserving();
}
void SessionChangeProcessor::OnNavigationBlocked(WebContents* web_contents) {
SyncedTabDelegate* tab =
SyncedTabDelegate::ImplFromWebContents(web_contents);
if (!tab)
return;
DCHECK(tab->profile() == profile_);
std::vector<SyncedTabDelegate*> modified_tabs;
modified_tabs.push_back(tab);
ProcessModifiedTabs(modified_tabs);
}
void SessionChangeProcessor::ProcessModifiedTabs(
const std::vector<SyncedTabDelegate*>& modified_tabs) {
if (!modified_tabs.empty()) {
SyncedTabDelegate* tab = modified_tabs.front();
const content::NavigationEntry* entry = tab->GetActiveEntry();
if (!tab->IsBeingDestroyed() &&
entry &&
entry->GetVirtualURL().is_valid() &&
entry->GetVirtualURL().spec() == kNTPOpenTabSyncURL) {
DVLOG(1) << "Triggering sync refresh for sessions datatype.";
const syncer::ModelTypeSet types(syncer::SESSIONS);
content::NotificationService::current()->Notify(
chrome::NOTIFICATION_SYNC_REFRESH_LOCAL,
content::Source<Profile>(profile_),
content::Details<const syncer::ModelTypeSet>(&types));
}
}
bool reassociation_needed = !modified_tabs.empty() &&
!session_model_associator_->AssociateTabs(modified_tabs, NULL);
if (!reassociation_needed) {
reassociation_needed =
!session_model_associator_->AssociateWindows(false, NULL);
}
if (reassociation_needed) {
LOG(WARNING) << "Reassociation of local models triggered.";
syncer::SyncError error;
error = session_model_associator_->DisassociateModels();
error = session_model_associator_->AssociateModels(NULL, NULL);
if (error.IsSet()) {
error_handler()->OnSingleDatatypeUnrecoverableError(
error.location(),
error.message());
}
}
}
void SessionChangeProcessor::StartObserving() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
if (!profile_)
return;
notification_registrar_.Add(this, chrome::NOTIFICATION_TAB_PARENTED,
content::NotificationService::AllSources());
notification_registrar_.Add(this,
content::NOTIFICATION_WEB_CONTENTS_DESTROYED,
content::NotificationService::AllSources());
notification_registrar_.Add(this, content::NOTIFICATION_NAV_LIST_PRUNED,
content::NotificationService::AllSources());
notification_registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_CHANGED,
content::NotificationService::AllSources());
notification_registrar_.Add(this, content::NOTIFICATION_NAV_ENTRY_COMMITTED,
content::NotificationService::AllSources());
notification_registrar_.Add(this, chrome::NOTIFICATION_BROWSER_OPENED,
content::NotificationService::AllBrowserContextsAndSources());
notification_registrar_.Add(this,
chrome::NOTIFICATION_TAB_CONTENTS_APPLICATION_EXTENSION_CHANGED,
content::NotificationService::AllSources());
notification_registrar_.Add(this,
content::NOTIFICATION_LOAD_COMPLETED_MAIN_FRAME,
content::NotificationService::AllBrowserContextsAndSources());
notification_registrar_.Add(this, chrome::NOTIFICATION_FAVICON_CHANGED,
content::Source<Profile>(profile_));
#if defined(ENABLE_MANAGED_USERS)
if (profile_->IsManaged()) {
ManagedUserService* managed_user_service =
ManagedUserServiceFactory::GetForProfile(profile_);
managed_user_service->AddNavigationBlockedCallback(
base::Bind(&SessionChangeProcessor::OnNavigationBlocked,
weak_ptr_factory_.GetWeakPtr()));
}
#endif
}
}