1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/android/tab_model/tab_model_base.h"
6
7#include "base/android/jni_android.h"
8#include "base/android/jni_string.h"
9#include "base/android/jni_weak_ref.h"
10#include "base/metrics/histogram.h"
11#include "base/time/time.h"
12#include "chrome/browser/android/tab_android.h"
13#include "chrome/browser/browser_process.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/profiles/profile_android.h"
16#include "chrome/browser/profiles/profile_manager.h"
17#include "chrome/browser/ui/android/tab_model/tab_model.h"
18#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
19#include "chrome/browser/ui/toolbar/toolbar_model.h"
20#include "content/public/browser/web_contents.h"
21#include "jni/TabModelBase_jni.h"
22
23using base::android::AttachCurrentThread;
24using base::android::CheckException;
25using base::android::ConvertUTF8ToJavaString;
26using base::android::ConvertUTF16ToJavaString;
27using base::android::ScopedJavaLocalRef;
28
29TabModelBase::TabModelBase(JNIEnv* env, jobject obj, Profile* profile)
30    : TabModel(profile),
31      java_object_(env, env->NewWeakGlobalRef(obj)) {
32}
33
34void TabModelBase::Destroy(JNIEnv* env, jobject obj) {
35  TabModelList::RemoveTabModel(this);
36  delete this;
37}
38
39ScopedJavaLocalRef<jobject> TabModelBase::GetProfileAndroid(JNIEnv* env,
40                                                            jobject obj) {
41  ProfileAndroid* profile_android = ProfileAndroid::FromProfile(GetProfile());
42  if (!profile_android)
43    return ScopedJavaLocalRef<jobject>();
44
45  return profile_android->GetJavaObject();
46}
47
48void TabModelBase::TabAddedToModel(JNIEnv* env, jobject obj, jobject jtab) {
49  TabAndroid* tab = TabAndroid::GetNativeTab(env, jtab);
50
51  // Tab#initialize() should have been called by now otherwise we can't push
52  // the window id.
53  DCHECK(tab);
54
55  tab->SetWindowSessionID(GetSessionId());
56}
57
58int TabModelBase::GetTabCount() const {
59  JNIEnv* env = AttachCurrentThread();
60  jint count = Java_TabModelBase_getCount(
61      env, java_object_.get(env).obj());
62  return count;
63}
64
65int TabModelBase::GetActiveIndex() const {
66  JNIEnv* env = AttachCurrentThread();
67  jint index = Java_TabModelBase_index(
68      env, java_object_.get(env).obj());
69  return index;
70}
71
72content::WebContents* TabModelBase::GetWebContentsAt(
73    int index) const {
74  TabAndroid* tab = GetTabAt(index);
75  return tab == NULL ? NULL : tab->web_contents();
76}
77
78TabAndroid* TabModelBase::GetTabAt(int index) const {
79  JNIEnv* env = AttachCurrentThread();
80  ScopedJavaLocalRef<jobject> jtab =
81      Java_TabModelBase_getTabAt(env,
82                                 java_object_.get(env).obj(),
83                                 index);
84  DCHECK(!jtab.is_null());
85
86  return TabAndroid::GetNativeTab(env, jtab.obj());
87}
88
89void TabModelBase::SetActiveIndex(int index) {
90  JNIEnv* env = AttachCurrentThread();
91  Java_TabModelBase_setIndex(
92      env,
93      java_object_.get(env).obj(),
94      index);
95}
96
97void TabModelBase::CloseTabAt(int index) {
98  JNIEnv* env = AttachCurrentThread();
99  ScopedJavaLocalRef<jobject> jtab =
100      Java_TabModelBase_getTabAt(env,
101                                 java_object_.get(env).obj(),
102                                 index);
103  if (!jtab.is_null()) {
104    Java_TabModelBase_closeTab(env,
105                               java_object_.get(env).obj(),
106                               jtab.obj());
107  }
108}
109
110void TabModelBase::CreateTab(content::WebContents* web_contents,
111                             int parent_tab_id) {
112  JNIEnv* env = AttachCurrentThread();
113  Java_TabModelBase_createTabWithNativeContents(
114      env, java_object_.get(env).obj(),
115      web_contents->GetBrowserContext()->IsOffTheRecord(),
116      reinterpret_cast<intptr_t>(web_contents), parent_tab_id);
117}
118
119content::WebContents* TabModelBase::CreateNewTabForDevTools(const GURL& url) {
120  JNIEnv* env = AttachCurrentThread();
121  ScopedJavaLocalRef<jstring> jurl = ConvertUTF8ToJavaString(env, url.spec());
122  ScopedJavaLocalRef<jobject> obj =
123      Java_TabModelBase_createNewTabForDevTools(
124          env,
125          java_object_.get(env).obj(),
126          jurl.obj());
127  if (obj.is_null()) {
128    VLOG(0) << "Failed to create java tab";
129    return NULL;
130  }
131  TabAndroid* tab = TabAndroid::GetNativeTab(env, obj.obj());
132  if (!tab) {
133    VLOG(0) << "Failed to create java tab";
134    return NULL;
135  }
136  return tab->web_contents();
137}
138
139bool TabModelBase::IsSessionRestoreInProgress() const {
140  JNIEnv* env = AttachCurrentThread();
141  return Java_TabModelBase_isSessionRestoreInProgress(
142      env, java_object_.get(env).obj());
143}
144
145void TabModelBase::BroadcastSessionRestoreComplete(JNIEnv* env,
146                                                   jobject obj) {
147  TabModel::BroadcastSessionRestoreComplete();
148}
149
150TabModelBase::~TabModelBase() {
151}
152
153namespace {
154
155static Profile* FindProfile(jboolean is_incognito) {
156  if (g_browser_process == NULL ||
157      g_browser_process->profile_manager() == NULL) {
158    LOG(ERROR) << "Browser process or profile manager not initialized";
159    return NULL;
160  }
161  Profile* profile = ProfileManager::GetActiveUserProfile();
162  if (is_incognito) {
163    return profile->GetOffTheRecordProfile();
164  }
165  return profile;
166}
167
168}  // namespace
169
170// ----------------------------------------------------------------------------
171// Native JNI methods
172// ----------------------------------------------------------------------------
173
174static jlong Init(JNIEnv* env, jobject obj, jboolean is_incognito) {
175  Profile* profile = FindProfile(is_incognito);
176  TabModel* tab_model = new TabModelBase(env, obj, profile);
177  TabModelList::AddTabModel(tab_model);
178  return reinterpret_cast<intptr_t>(tab_model);
179}
180
181inline static base::TimeDelta GetTimeDelta(jlong ms) {
182  return base::TimeDelta::FromMilliseconds(static_cast<int64>(ms));
183}
184
185void LogFromCloseMetric(JNIEnv* env,
186                        jclass jcaller,
187                        jlong ms,
188                        jboolean perceived) {
189  if (perceived) {
190    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromCloseLatency_Perceived",
191                        GetTimeDelta(ms));
192  } else {
193    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromCloseLatency_Actual",
194                        GetTimeDelta(ms));
195  }
196}
197
198void LogFromExitMetric(JNIEnv* env,
199                       jclass jcaller,
200                       jlong ms,
201                       jboolean perceived) {
202  if (perceived) {
203    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromExitLatency_Perceived",
204                        GetTimeDelta(ms));
205  } else {
206    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromExitLatency_Actual",
207                        GetTimeDelta(ms));
208  }
209}
210
211void LogFromNewMetric(JNIEnv* env,
212                      jclass jcaller,
213                      jlong ms,
214                      jboolean perceived) {
215  if (perceived) {
216    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromNewLatency_Perceived",
217                        GetTimeDelta(ms));
218  } else {
219    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromNewLatency_Actual",
220                        GetTimeDelta(ms));
221  }
222}
223
224void LogFromUserMetric(JNIEnv* env,
225                       jclass jcaller,
226                       jlong ms,
227                       jboolean perceived) {
228  if (perceived) {
229    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromUserLatency_Perceived",
230                        GetTimeDelta(ms));
231  } else {
232    UMA_HISTOGRAM_TIMES("Tabs.SwitchFromUserLatency_Actual",
233                        GetTimeDelta(ms));
234  }
235}
236
237// Register native methods
238
239bool RegisterTabModelBase(JNIEnv* env) {
240  return RegisterNativesImpl(env);
241}
242