blob: dfac1856b410dcf5b782c856922cae9a41c979c4 [file] [log] [blame]
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -07001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.browser;
18
19import android.content.Context;
20import android.os.Bundle;
21import android.util.Config;
22import android.util.Log;
23import android.view.Gravity;
24import android.view.LayoutInflater;
25import android.view.View;
26import android.view.ViewGroup;
27import android.view.View.OnClickListener;
28import android.webkit.JsPromptResult;
29import android.webkit.JsResult;
30import android.webkit.WebBackForwardList;
31import android.webkit.WebChromeClient;
32import android.webkit.WebHistoryItem;
33import android.webkit.WebView;
34import android.webkit.WebViewClient;
35import android.widget.FrameLayout;
36import android.widget.ImageButton;
37
The Android Open Source Projected217d92008-12-17 18:05:52 -080038import java.io.File;
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -070039import java.util.ArrayList;
40import java.util.Vector;
41
42class TabControl {
43 // Log Tag
44 private static final String LOGTAG = "TabControl";
45 // Maximum number of tabs.
46 static final int MAX_TABS = 8;
47 // Static instance of an empty callback.
48 private static final WebViewClient mEmptyClient =
49 new WebViewClient();
50 // Instance of BackgroundChromeClient for background tabs.
51 private final BackgroundChromeClient mBackgroundChromeClient =
52 new BackgroundChromeClient();
53 // Private array of WebViews that are used as tabs.
54 private ArrayList<Tab> mTabs = new ArrayList<Tab>(MAX_TABS);
55 // Queue of most recently viewed tabs.
56 private ArrayList<Tab> mTabQueue = new ArrayList<Tab>(MAX_TABS);
57 // Current position in mTabs.
58 private int mCurrentTab = -1;
59 // A private instance of BrowserActivity to interface with when adding and
60 // switching between tabs.
61 private final BrowserActivity mActivity;
62 // Inflation service for making subwindows.
63 private final LayoutInflater mInflateService;
64 // Subclass of WebViewClient used in subwindows to notify the main
65 // WebViewClient of certain WebView activities.
66 private class SubWindowClient extends WebViewClient {
67 // The main WebViewClient.
68 private final WebViewClient mClient;
69
70 SubWindowClient(WebViewClient client) {
71 mClient = client;
72 }
73 @Override
74 public void doUpdateVisitedHistory(WebView view, String url,
75 boolean isReload) {
76 mClient.doUpdateVisitedHistory(view, url, isReload);
77 }
78 @Override
79 public boolean shouldOverrideUrlLoading(WebView view, String url) {
80 return mClient.shouldOverrideUrlLoading(view, url);
81 }
82 }
83 // Subclass of WebChromeClient to display javascript dialogs.
84 private class SubWindowChromeClient extends WebChromeClient {
85 // This subwindow's tab.
86 private final Tab mTab;
87 // The main WebChromeClient.
88 private final WebChromeClient mClient;
89
90 SubWindowChromeClient(Tab t, WebChromeClient client) {
91 mTab = t;
92 mClient = client;
93 }
94 @Override
95 public void onProgressChanged(WebView view, int newProgress) {
96 mClient.onProgressChanged(view, newProgress);
97 }
98 @Override
99 public boolean onCreateWindow(WebView view, boolean dialog,
100 boolean userGesture, android.os.Message resultMsg) {
101 return mClient.onCreateWindow(view, dialog, userGesture, resultMsg);
102 }
103 @Override
104 public void onCloseWindow(WebView window) {
105 if (Config.DEBUG && window != mTab.mSubView) {
106 throw new AssertionError("Can't close the window");
107 }
108 mActivity.dismissSubWindow(mTab);
109 }
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700110 }
111 // Background WebChromeClient for focusing tabs
112 private class BackgroundChromeClient extends WebChromeClient {
113 @Override
114 public void onRequestFocus(WebView view) {
115 Tab t = getTabFromView(view);
116 if (t != getCurrentTab()) {
117 mActivity.showTab(t);
118 }
119 }
120 }
121
122 /**
123 * Private class for maintaining Tabs with a main WebView and a subwindow.
124 */
125 public class Tab {
126 // Main WebView
127 private WebView mMainView;
128 // Subwindow WebView
129 private WebView mSubView;
130 // Subwindow container
131 private View mSubViewContainer;
132 // Subwindow callback
133 private SubWindowClient mSubViewClient;
134 // Subwindow chrome callback
135 private SubWindowChromeClient mSubViewChromeClient;
136 // Saved bundle for when we are running low on memory. It contains the
137 // information needed to restore the WebView if the user goes back to
138 // the tab.
139 private Bundle mSavedState;
140 // Extra saved information for displaying the tab in the picker.
141 private String mUrl;
142 private String mTitle;
143
144 // Parent Tab. This is the Tab that created this Tab, or null
145 // if the Tab was created by the UI
146 private Tab mParentTab;
147 // Tab that constructed by this Tab. This is used when this
148 // Tab is destroyed, it clears all mParentTab values in the
149 // children.
150 private Vector<Tab> mChildTabs;
151
The Android Open Source Projected217d92008-12-17 18:05:52 -0800152 private Boolean mCloseOnExit;
153
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700154 // Construct a new tab
The Android Open Source Projected217d92008-12-17 18:05:52 -0800155 private Tab(WebView w, boolean closeOnExit) {
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700156 mMainView = w;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800157 mCloseOnExit = closeOnExit;
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700158 }
159
160 /**
161 * Return the top window of this tab; either the subwindow if it is not
162 * null or the main window.
163 * @return The top window of this tab.
164 */
165 public WebView getTopWindow() {
166 if (mSubView != null) {
167 return mSubView;
168 }
169 return mMainView;
170 }
171
172 /**
173 * Return the main window of this tab. Note: if a tab is freed in the
174 * background, this can return null. It is only guaranteed to be
175 * non-null for the current tab.
176 * @return The main WebView of this tab.
177 */
178 public WebView getWebView() {
179 return mMainView;
180 }
181
182 /**
183 * Return the subwindow of this tab or null if there is no subwindow.
184 * @return The subwindow of this tab or null.
185 */
186 public WebView getSubWebView() {
187 return mSubView;
188 }
189
190 /**
191 * Return the subwindow container of this tab or null if there is no
192 * subwindow.
193 * @return The subwindow's container View.
194 */
195 public View getSubWebViewContainer() {
196 return mSubViewContainer;
197 }
198
199 /**
200 * Get the url of this tab. Valid after calling populatePickerData, but
201 * before calling wipePickerData, or if the webview has been destroyed.
202 *
203 * @return The WebView's url or null.
204 */
205 public String getUrl() {
206 return mUrl;
207 }
208
209 /**
210 * Get the title of this tab. Valid after calling populatePickerData,
211 * but before calling wipePickerData, or if the webview has been
212 * destroyed. If the url has no title, use the url instead.
213 *
214 * @return The WebView's title (or url) or null.
215 */
216 public String getTitle() {
217 return mTitle;
218 }
219
220 private void setParentTab(Tab parent) {
221 mParentTab = parent;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800222 // This tab may have been freed due to low memory. If that is the
223 // case, the parent tab index is already saved. If we are changing
224 // that index (most likely due to removing the parent tab) we must
225 // update the parent tab index in the saved Bundle.
226 if (mSavedState != null) {
227 if (parent == null) {
228 mSavedState.remove(PARENTTAB);
229 } else {
230 mSavedState.putInt(PARENTTAB, getTabIndex(parent));
231 }
232 }
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700233 }
234
235 /**
236 * When a Tab is created through the content of another Tab, then
237 * we associate the Tabs.
238 * @param child the Tab that was created from this Tab
239 */
240 public void addChildTab(Tab child) {
241 if (mChildTabs == null) {
242 mChildTabs = new Vector<Tab>();
243 }
244 mChildTabs.add(child);
245 child.setParentTab(this);
246 }
247
248 private void removeFromTree() {
249 // detach the children
250 if (mChildTabs != null) {
251 for(Tab t : mChildTabs) {
252 t.setParentTab(null);
253 }
254 }
255
256 // Find myself in my parent list
257 if (mParentTab != null) {
258 mParentTab.mChildTabs.remove(this);
259 }
260 }
261
262 /**
263 * If this Tab was created through another Tab, then this method
264 * returns that Tab.
265 * @return the Tab parent or null
266 */
267 public Tab getParentTab() {
268 return mParentTab;
269 }
The Android Open Source Projected217d92008-12-17 18:05:52 -0800270
271 /**
272 * Return whether this tab should be closed when it is backing out of
273 * the first page.
274 * @return TRUE if this tab should be closed when exit.
275 */
276 public boolean closeOnExit() {
277 return mCloseOnExit;
278 }
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700279 };
280
The Android Open Source Projected217d92008-12-17 18:05:52 -0800281 // Directory to store thumbnails for each WebView.
282 private final File mThumbnailDir;
283
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700284 /**
285 * Construct a new TabControl object that interfaces with the given
286 * BrowserActivity instance.
287 * @param activity A BrowserActivity instance that TabControl will interface
288 * with.
289 */
290 TabControl(BrowserActivity activity) {
291 mActivity = activity;
292 mInflateService =
293 ((LayoutInflater) activity.getSystemService(
294 Context.LAYOUT_INFLATER_SERVICE));
The Android Open Source Projected217d92008-12-17 18:05:52 -0800295 mThumbnailDir = activity.getDir("thumbnails", 0);
296 }
297
298 File getThumbnailDir() {
299 return mThumbnailDir;
300 }
301
302 BrowserActivity getBrowserActivity() {
303 return mActivity;
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700304 }
305
306 /**
307 * Return the current tab's main WebView. This will always return the main
308 * WebView for a given tab and not a subwindow.
309 * @return The current tab's WebView.
310 */
311 WebView getCurrentWebView() {
312 Tab t = getTab(mCurrentTab);
313 if (t == null) {
314 return null;
315 }
316 return t.mMainView;
317 }
318
319 /**
320 * Return the current tab's top-level WebView. This can return a subwindow
321 * if one exists.
322 * @return The top-level WebView of the current tab.
323 */
324 WebView getCurrentTopWebView() {
325 Tab t = getTab(mCurrentTab);
326 if (t == null) {
327 return null;
328 }
329 return t.mSubView != null ? t.mSubView : t.mMainView;
330 }
331
332 /**
333 * Return the current tab's subwindow if it exists.
334 * @return The subwindow of the current tab or null if it doesn't exist.
335 */
336 WebView getCurrentSubWindow() {
337 Tab t = getTab(mCurrentTab);
338 if (t == null) {
339 return null;
340 }
341 return t.mSubView;
342 }
343
344 /**
345 * Return the tab at the specified index.
346 * @return The Tab for the specified index or null if the tab does not
347 * exist.
348 */
349 Tab getTab(int index) {
350 if (index >= 0 && index < mTabs.size()) {
351 return mTabs.get(index);
352 }
353 return null;
354 }
355
356 /**
357 * Return the current tab.
358 * @return The current tab.
359 */
360 Tab getCurrentTab() {
361 return getTab(mCurrentTab);
362 }
363
364 /**
365 * Return the current tab index.
366 * @return The current tab index
367 */
368 int getCurrentIndex() {
369 return mCurrentTab;
370 }
371
372 /**
373 * Given a Tab, find it's index
374 * @param Tab to find
375 * @return index of Tab or -1 if not found
376 */
377 int getTabIndex(Tab tab) {
378 return mTabs.indexOf(tab);
379 }
380
381 /**
382 * Create a new tab and display the new tab immediately.
383 * @return The newly createTab or null if we have reached the maximum
384 * number of open tabs.
385 */
The Android Open Source Projected217d92008-12-17 18:05:52 -0800386 Tab createNewTab(boolean closeOnExit) {
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700387 int size = mTabs.size();
388 // Return false if we have maxed out on tabs
389 if (MAX_TABS == size) {
390 return null;
391 }
392 // Create a new WebView
393 WebView w = new WebView(mActivity);
394 w.setMapTrackballToArrowKeys(false); // use trackball directly
395 // Add this WebView to the settings observer list and update the
396 // settings
397 final BrowserSettings s = BrowserSettings.getInstance();
398 s.addObserver(w.getSettings()).update(s, null);
399 // Create a new tab and add it to the tab list
The Android Open Source Projected217d92008-12-17 18:05:52 -0800400 Tab t = new Tab(w, closeOnExit);
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700401 mTabs.add(t);
402 return t;
403 }
404
405 /**
406 * Remove the tab from the list. If the tab is the current tab shown, the
407 * last created tab will be shown.
408 * @param t The tab to be removed.
409 */
410 boolean removeTab(Tab t) {
411 if (t == null) {
412 return false;
413 }
414 // Only remove the tab if it is the current one.
415 if (getCurrentTab() == t) {
416 putTabInBackground(t);
417 }
418
419 // Only destroy the WebView if it still exists.
420 if (t.mMainView != null) {
421 // Take down the sub window.
422 dismissSubWindow(t);
423 // Remove the WebView's settings from the BrowserSettings list of
424 // observers.
425 BrowserSettings.getInstance().deleteObserver(
426 t.mMainView.getSettings());
427 // Destroy the main view and subview
428 t.mMainView.destroy();
429 t.mMainView = null;
430 }
431 // clear it's references to parent and children
432 t.removeFromTree();
433
434 // Remove it from our list of tabs.
435 mTabs.remove(t);
The Android Open Source Projected217d92008-12-17 18:05:52 -0800436
437 // The tab indices have shifted, update all the saved state so we point
438 // to the correct index.
439 for (Tab tab : mTabs) {
440 if (tab.mChildTabs != null) {
441 for (Tab child : tab.mChildTabs) {
442 child.setParentTab(tab);
443 }
444 }
445 }
446
447
448 // This tab may have been pushed in to the background and then closed.
449 // If the saved state contains a picture file, delete the file.
450 if (t.mSavedState != null) {
451 if (t.mSavedState.containsKey("picture")) {
452 new File(t.mSavedState.getString("picture")).delete();
453 }
454 }
455
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700456 // Remove it from the queue of viewed tabs.
457 mTabQueue.remove(t);
458 mCurrentTab = -1;
459 return true;
460 }
461
462 /**
463 * Clear the back/forward list for all the current tabs.
464 */
465 void clearHistory() {
466 int size = getTabCount();
467 for (int i = 0; i < size; i++) {
468 Tab t = mTabs.get(i);
469 // TODO: if a tab is freed due to low memory, its history is not
470 // cleared here.
471 if (t.mMainView != null) {
472 t.mMainView.clearHistory();
473 }
474 if (t.mSubView != null) {
475 t.mSubView.clearHistory();
476 }
477 }
478 }
479
480 /**
481 * Destroy all the tabs and subwindows
482 */
483 void destroy() {
484 BrowserSettings s = BrowserSettings.getInstance();
485 for (Tab t : mTabs) {
486 if (t.mMainView != null) {
487 dismissSubWindow(t);
488 s.deleteObserver(t.mMainView.getSettings());
489 t.mMainView.destroy();
490 t.mMainView = null;
491 }
492 }
493 mTabs.clear();
494 mTabQueue.clear();
495 }
496
497 /**
498 * Returns the number of tabs created.
499 * @return The number of tabs created.
500 */
501 int getTabCount() {
502 return mTabs.size();
503 }
504
505 // Used for saving and restoring each Tab
506 private static final String WEBVIEW = "webview";
507 private static final String NUMTABS = "numTabs";
508 private static final String CURRTAB = "currentTab";
509 private static final String CURRURL = "currentUrl";
510 private static final String CURRTITLE = "currentTitle";
The Android Open Source Projected217d92008-12-17 18:05:52 -0800511 private static final String CLOSEONEXIT = "closeonexit";
512 private static final String PARENTTAB = "parentTab";
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700513
514 /**
515 * Save the state of all the Tabs.
516 * @param outState The Bundle to save the state to.
517 */
518 void saveState(Bundle outState) {
519 final int numTabs = getTabCount();
520 outState.putInt(NUMTABS, numTabs);
521 final int index = getCurrentIndex();
522 outState.putInt(CURRTAB, (index >= 0 && index < numTabs) ? index : 0);
523 for (int i = 0; i < numTabs; i++) {
524 final Tab t = getTab(i);
525 if (saveState(t)) {
526 outState.putBundle(WEBVIEW + i, t.mSavedState);
527 }
528 }
529 }
530
531 /**
532 * Restore the state of all the tabs.
533 * @param inState The saved state of all the tabs.
534 * @return True if there were previous tabs that were restored. False if
535 * there was no saved state or restoring the state failed.
536 */
537 boolean restoreState(Bundle inState) {
538 final int numTabs = (inState == null)
539 ? -1 : inState.getInt(NUMTABS, -1);
540 if (numTabs == -1) {
541 return false;
542 } else {
543 final int currentTab = inState.getInt(CURRTAB, -1);
544 for (int i = 0; i < numTabs; i++) {
545 if (i == currentTab) {
The Android Open Source Projected217d92008-12-17 18:05:52 -0800546 Tab t = createNewTab(false);
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700547 // Me must set the current tab before restoring the state
548 // so that all the client classes are set.
549 setCurrentTab(t);
550 if (!restoreState(inState.getBundle(WEBVIEW + i), t)) {
551 Log.w(LOGTAG, "Fail in restoreState, load home page.");
552 t.mMainView.loadUrl(BrowserSettings.getInstance()
553 .getHomePage());
554 }
555 } else {
556 // Create a new tab and don't restore the state yet, add it
557 // to the tab list
The Android Open Source Projected217d92008-12-17 18:05:52 -0800558 Tab t = new Tab(null, false);
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700559 t.mSavedState = inState.getBundle(WEBVIEW + i);
560 if (t.mSavedState != null) {
561 t.mUrl = t.mSavedState.getString(CURRURL);
562 t.mTitle = t.mSavedState.getString(CURRTITLE);
563 }
564 mTabs.add(t);
565 mTabQueue.add(t);
566 }
567 }
The Android Open Source Projected217d92008-12-17 18:05:52 -0800568 // Rebuild the tree of tabs. Do this after all tabs have been
569 // created/restored so that the parent tab exists.
570 for (int i = 0; i < numTabs; i++) {
571 final Bundle b = inState.getBundle(WEBVIEW + i);
572 final Tab t = getTab(i);
573 if (b != null && t != null) {
574 final int parentIndex = b.getInt(PARENTTAB, -1);
575 if (parentIndex != -1) {
576 final Tab parent = getTab(parentIndex);
577 if (parent != null) {
578 parent.addChildTab(t);
579 }
580 }
581 }
582 }
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700583 }
584 return true;
585 }
586
587 /**
588 * Free the memory in this order, 1) free the background tab; 2) free the
589 * WebView cache;
590 */
591 void freeMemory() {
592 // free the least frequently used background tab
593 Tab t = getLeastUsedTab();
594 if (t != null) {
595 Log.w(LOGTAG, "Free a tab in the browser");
596 freeTab(t);
597 // force a gc
598 System.gc();
599 return;
600 }
601
602 // free the WebView cache
603 Log.w(LOGTAG, "Free WebView cache");
604 WebView view = getCurrentWebView();
605 view.clearCache(false);
606 // force a gc
607 System.gc();
608 }
609
610 private Tab getLeastUsedTab() {
611 // Don't do anything if we only have 1 tab.
612 if (getTabCount() == 1) {
613 return null;
614 }
615
616 // Rip through the queue starting at the beginning and teardown the
617 // next available tab.
618 Tab t = null;
619 int i = 0;
620 final int queueSize = mTabQueue.size();
621 if (queueSize == 0) {
622 return null;
623 }
624 do {
625 t = mTabQueue.get(i++);
626 } while (i < queueSize && t != null && t.mMainView == null);
627
628 // Don't do anything if the last remaining tab is the current one.
629 if (t == getCurrentTab()) {
630 return null;
631 }
632
633 return t;
634 }
635
636 private void freeTab(Tab t) {
637 // Store the WebView's state.
638 saveState(t);
639
640 // Tear down the tab.
641 dismissSubWindow(t);
642 // Remove the WebView's settings from the BrowserSettings list of
643 // observers.
644 BrowserSettings.getInstance().deleteObserver(t.mMainView.getSettings());
645 t.mMainView.destroy();
646 t.mMainView = null;
647 }
648
649 /**
650 * Create a new subwindow unless a subwindow already exists.
651 * @return True if a new subwindow was created. False if one already exists.
652 */
653 void createSubWindow() {
654 Tab t = getTab(mCurrentTab);
655 if (t != null && t.mSubView == null) {
656 final View v = mInflateService.inflate(R.layout.browser_subwindow, null);
657 final WebView w = (WebView) v.findViewById(R.id.webview);
658 w.setMapTrackballToArrowKeys(false); // use trackball directly
659 final SubWindowClient subClient =
660 new SubWindowClient(mActivity.getWebViewClient());
661 final SubWindowChromeClient subChromeClient =
662 new SubWindowChromeClient(t,
663 mActivity.getWebChromeClient());
664 w.setWebViewClient(subClient);
665 w.setWebChromeClient(subChromeClient);
666 w.setDownloadListener(mActivity);
667 w.setOnCreateContextMenuListener(mActivity);
668 final BrowserSettings s = BrowserSettings.getInstance();
669 s.addObserver(w.getSettings()).update(s, null);
670 t.mSubView = w;
671 t.mSubViewClient = subClient;
672 t.mSubViewChromeClient = subChromeClient;
673 // FIXME: I really hate having to know the name of the view
674 // containing the webview.
675 t.mSubViewContainer = v.findViewById(R.id.subwindow_container);
676 final ImageButton cancel =
677 (ImageButton) v.findViewById(R.id.subwindow_close);
678 cancel.setOnClickListener(new OnClickListener() {
679 public void onClick(View v) {
680 subChromeClient.onCloseWindow(w);
681 }
682 });
683 }
684 }
685
686 /**
687 * Show the tab that contains the given WebView.
688 * @param view The WebView used to find the tab.
689 */
690 Tab getTabFromView(WebView view) {
691 final int size = getTabCount();
692 for (int i = 0; i < size; i++) {
693 final Tab t = getTab(i);
694 if (t.mSubView == view || t.mMainView == view) {
695 return t;
696 }
697 }
698 return null;
699 }
700
701 /**
702 * Put the current tab in the background and set newTab as the current tab.
703 * @param newTab The new tab. If newTab is null, the current tab is not
704 * set.
705 */
706 boolean setCurrentTab(Tab newTab) {
707 Tab current = getTab(mCurrentTab);
708 if (current == newTab) {
709 return true;
710 }
711 if (current != null) {
712 // Remove the current WebView and the container of the subwindow
713 putTabInBackground(current);
714 }
715
716 if (newTab == null) {
717 return false;
718 }
719
720 // Move the newTab to the end of the queue
721 int index = mTabQueue.indexOf(newTab);
722 if (index != -1) {
723 mTabQueue.remove(index);
724 }
725 mTabQueue.add(newTab);
726
727 WebView mainView;
728 WebView subView;
729
730 // Display the new current tab
731 mCurrentTab = mTabs.indexOf(newTab);
732 mainView = newTab.mMainView;
733 boolean needRestore = (mainView == null);
734 if (needRestore) {
735 // Same work as in createNewTab() except don't do new Tab()
736 newTab.mMainView = mainView = new WebView(mActivity);
737 mainView.setMapTrackballToArrowKeys(false); // use t-ball directly
738
739 // Add this WebView to the settings observer list and update the
740 // settings
741 final BrowserSettings s = BrowserSettings.getInstance();
742 s.addObserver(mainView.getSettings()).update(s, null);
743 }
744 mainView.setWebViewClient(mActivity.getWebViewClient());
745 mainView.setWebChromeClient(mActivity.getWebChromeClient());
746 mainView.setOnCreateContextMenuListener(mActivity);
747 mainView.setDownloadListener(mActivity);
748 // Add the subwindow if it exists
749 if (newTab.mSubViewContainer != null) {
750 subView = newTab.mSubView;
751 subView.setWebViewClient(newTab.mSubViewClient);
752 subView.setWebChromeClient(newTab.mSubViewChromeClient);
753 subView.setOnCreateContextMenuListener(mActivity);
754 subView.setDownloadListener(mActivity);
755 }
756 if (needRestore) {
757 // Have to finish setCurrentTab work before calling restoreState
758 if (!restoreState(newTab.mSavedState, newTab)) {
759 mainView.loadUrl(BrowserSettings.getInstance().getHomePage());
760 }
761 }
762 return true;
763 }
764
765 /*
766 * Put the tab in the background using all the empty/background clients.
767 */
768 private void putTabInBackground(Tab t) {
769 WebView mainView = t.mMainView;
770 // Set an empty callback so that default actions are not triggered.
771 mainView.setWebViewClient(mEmptyClient);
772 mainView.setWebChromeClient(mBackgroundChromeClient);
773 mainView.setOnCreateContextMenuListener(null);
774 // Leave the DownloadManager attached so that downloads can start in
775 // a non-active window. This can happen when going to a site that does
776 // a redirect after a period of time. The user could have switched to
777 // another tab while waiting for the download to start.
778 mainView.setDownloadListener(mActivity);
779 WebView subView = t.mSubView;
780 if (subView != null) {
781 // Set an empty callback so that default actions are not triggered.
782 subView.setWebViewClient(mEmptyClient);
783 subView.setWebChromeClient(mBackgroundChromeClient);
784 subView.setOnCreateContextMenuListener(null);
785 subView.setDownloadListener(mActivity);
786 }
787 }
788
789 /*
790 * Dismiss the subwindow for the given tab.
791 */
792 void dismissSubWindow(Tab t) {
793 if (t != null && t.mSubView != null) {
794 BrowserSettings.getInstance().deleteObserver(
795 t.mSubView.getSettings());
796 t.mSubView.destroy();
797 t.mSubView = null;
798 t.mSubViewContainer = null;
799 }
800 }
801
802 /**
803 * Ensure that Tab t has a title, url, and favicon.
804 * @param t Tab to populate.
805 */
806 /* package */ void populatePickerData(Tab t) {
807 if (t == null || t.mMainView == null) {
808 return;
809 }
810 // FIXME: The only place we cared about subwindow was for
811 // bookmarking (i.e. not when saving state). Was this deliberate?
812 final WebBackForwardList list = t.mMainView.copyBackForwardList();
813 final WebHistoryItem item =
814 list != null ? list.getCurrentItem() : null;
815 populatePickerData(t, item);
816 }
817
818 // Populate the picker data
819 private void populatePickerData(Tab t, WebHistoryItem item) {
820 if (item != null) {
821 t.mUrl = item.getUrl();
822 t.mTitle = item.getTitle();
823 if (t.mTitle == null) {
824 t.mTitle = t.mUrl;
825 }
826 }
827 }
828
829 /**
830 * Clean up the data for all tabs.
831 */
832 /* package */ void wipeAllPickerData() {
833 int size = getTabCount();
834 for (int i = 0; i < size; i++) {
835 final Tab t = getTab(i);
836 if (t != null && t.mSavedState == null) {
837 t.mUrl = null;
838 t.mTitle = null;
839 }
840 }
841 }
842
843 /*
844 * Save the state for an individual tab.
845 */
846 private boolean saveState(Tab t) {
847 if (t != null) {
848 final WebView w = t.mMainView;
849 // If the WebView is null it means we ran low on memory and we
850 // already stored the saved state in mSavedState.
851 if (w == null) {
852 return true;
853 }
854 final Bundle b = new Bundle();
855 final WebBackForwardList list = w.saveState(b);
The Android Open Source Projected217d92008-12-17 18:05:52 -0800856 if (list != null) {
857 final File f = new File(mThumbnailDir, w.hashCode()
858 + "_pic.save");
859 if (w.savePicture(b, f)) {
860 b.putString("picture", f.getPath());
861 }
862 }
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700863
864 // Store some extra info for displaying the tab in the picker.
The Android Open Source Projected217d92008-12-17 18:05:52 -0800865 final WebHistoryItem item =
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700866 list != null ? list.getCurrentItem() : null;
867 populatePickerData(t, item);
868 if (t.mUrl != null) {
869 b.putString(CURRURL, t.mUrl);
870 }
871 if (t.mTitle != null) {
872 b.putString(CURRTITLE, t.mTitle);
873 }
The Android Open Source Projected217d92008-12-17 18:05:52 -0800874 b.putBoolean(CLOSEONEXIT, t.mCloseOnExit);
875
876 // Remember the parent tab so the relationship can be restored.
877 if (t.mParentTab != null) {
878 b.putInt(PARENTTAB, getTabIndex(t.mParentTab));
879 }
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700880
881 // Remember the saved state.
882 t.mSavedState = b;
883 return true;
884 }
885 return false;
886 }
887
888 /*
889 * Restore the state of the tab.
890 */
891 private boolean restoreState(Bundle b, Tab t) {
892 if (b == null) {
893 return false;
894 }
895 final WebView w = t.mMainView;
896 final WebBackForwardList list = w.restoreState(b);
897 if (list == null) {
898 return false;
899 }
The Android Open Source Projected217d92008-12-17 18:05:52 -0800900 if (b.containsKey("picture")) {
901 final File f = new File(b.getString("picture"));
902 w.restorePicture(b, f);
903 f.delete();
904 }
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700905 t.mSavedState = null;
906 t.mUrl = null;
907 t.mTitle = null;
The Android Open Source Projected217d92008-12-17 18:05:52 -0800908 t.mCloseOnExit = b.getBoolean(CLOSEONEXIT);
The Android Open Source Projectba6d7b82008-10-21 07:00:00 -0700909 return true;
910 }
911}