diff --git a/mobile/android/base/App.java.in b/mobile/android/base/App.java.in index 00697008b834..94d080731ebd 100644 --- a/mobile/android/base/App.java.in +++ b/mobile/android/base/App.java.in @@ -6,13 +6,13 @@ #filter substitution package @ANDROID_PACKAGE_NAME@; -import org.mozilla.gecko.GeckoApp; +import org.mozilla.gecko.BrowserApp; import org.mozilla.gecko.GeckoAppShell; import org.mozilla.gecko.GeckoEvent; import org.mozilla.gecko.R; import android.view.MenuItem; -public class App extends GeckoApp { +public class App extends BrowserApp { public String getPackageName() { return "@ANDROID_PACKAGE_NAME@"; } diff --git a/mobile/android/base/BrowserApp.java b/mobile/android/base/BrowserApp.java new file mode 100644 index 000000000000..a6a487ce9018 --- /dev/null +++ b/mobile/android/base/BrowserApp.java @@ -0,0 +1,478 @@ +/* -*- Mode: Java; c-basic-offset: 4; tab-width: 4; indent-tabs-mode: nil; -*- + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +package org.mozilla.gecko; + +import org.mozilla.gecko.db.BrowserDB; +import org.mozilla.gecko.gfx.CairoImage; +import org.mozilla.gecko.gfx.BufferedCairoImage; +import org.mozilla.gecko.gfx.FloatSize; +import org.mozilla.gecko.gfx.GeckoLayerClient; +import org.mozilla.gecko.gfx.IntSize; +import org.mozilla.gecko.gfx.Layer; +import org.mozilla.gecko.gfx.LayerController; +import org.mozilla.gecko.gfx.LayerView; +import org.mozilla.gecko.gfx.PluginLayer; +import org.mozilla.gecko.gfx.RectUtils; +import org.mozilla.gecko.gfx.SurfaceTextureLayer; +import org.mozilla.gecko.gfx.ViewportMetrics; +import org.mozilla.gecko.gfx.ImmutableViewportMetrics; + +import java.io.*; +import java.util.*; +import java.util.regex.Pattern; +import java.util.regex.Matcher; +import java.util.zip.*; +import java.net.URL; +import java.nio.*; +import java.nio.channels.FileChannel; +import java.util.concurrent.*; +import java.lang.reflect.*; +import java.net.*; + +import org.json.*; + +import android.os.*; +import android.app.*; +import android.text.*; +import android.text.format.Time; +import android.view.*; +import android.view.inputmethod.*; +import android.view.ViewGroup.LayoutParams; +import android.content.*; +import android.content.res.*; +import android.graphics.*; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.BitmapDrawable; +import android.widget.*; +import android.hardware.*; +import android.location.*; +import android.view.accessibility.AccessibilityManager; +import android.view.accessibility.AccessibilityEvent; + +import android.util.*; +import android.net.*; +import android.database.*; +import android.database.sqlite.*; +import android.provider.*; +import android.content.pm.*; +import android.content.pm.PackageManager.*; +import dalvik.system.*; + +abstract public class BrowserApp extends GeckoApp { + private static final String LOGTAG = "GeckoBrowserApp"; + + public static BrowserToolbar mBrowserToolbar; + private AboutHomeContent mAboutHomeContent; + + @Override + public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { + switch(msg) { + case LOCATION_CHANGE: + if (Tabs.getInstance().isSelectedTab(tab)) { + String url = tab.getURL(); + if (url.equals("about:home")) + showAboutHome(); + else + hideAboutHome(); + maybeCancelFaviconLoad(tab); + } + break; + case START: + if (Tabs.getInstance().isSelectedTab(tab)) { + invalidateOptionsMenu(); + } + case STOP: + if (Tabs.getInstance().isSelectedTab(tab)) { + invalidateOptionsMenu(); + } + break; + case SELECTED: + if ("about:home".equals(tab.getURL())) + showAboutHome(); + else + hideAboutHome(); + break; + } + super.onTabChanged(tab, msg, data); + } + + @Override + void handlePageShow(final int tabId) { + super.handlePageShow(tabId); + final Tab tab = Tabs.getInstance().getTab(tabId); + if (tab == null) + return; + + mMainHandler.post(new Runnable() { + public void run() { + loadFavicon(tab); + } + }); + } + + @Override + void handleLinkAdded(final int tabId, String rel, final String href, int size) { + super.handleLinkAdded(tabId, rel, href, size); + if (rel.indexOf("[icon]") == -1) + return; + + final Tab tab = Tabs.getInstance().getTab(tabId); + if (tab == null) + return; + + // If tab is not loading and the favicon is updated, we + // want to load the image straight away. If tab is still + // loading, we only load the favicon once the page's content + // is fully loaded (see handleContentLoaded()). + if (tab.getState() != Tab.STATE_LOADING) { + mMainHandler.post(new Runnable() { + public void run() { + loadFavicon(tab); + } + }); + } + } + + @Override + void handleClearHistory() { + updateAboutHomeTopSites(); + super.handleClearHistory(); + } + + @Override + void handleSecurityChange(final int tabId, final JSONObject identityData) { + super.handleSecurityChange(tabId, identityData); + final Tab tab = Tabs.getInstance().getTab(tabId); + if (tab == null) + return; + + mMainHandler.post(new Runnable() { + public void run() { + if (Tabs.getInstance().isSelectedTab(tab)) + mBrowserToolbar.setSecurityMode(tab.getSecurityMode()); + } + }); + } + + void handleReaderEnabled(final int tabId) { + super.handleReaderEnabled(tabId); + final Tab tab = Tabs.getInstance().getTab(tabId); + if (tab == null) + return; + + mMainHandler.post(new Runnable() { + public void run() { + if (Tabs.getInstance().isSelectedTab(tab)) + mBrowserToolbar.setReaderVisibility(tab.getReaderEnabled()); + } + }); + } + + @Override + void onStatePurged() { + mMainHandler.post(new Runnable() { + public void run() { + if (mAboutHomeContent != null) + mAboutHomeContent.setLastTabsVisibility(false); + } + }); + + super.onStatePurged(); + } + + @Override + protected void loadRequest(String url, AwesomeBar.Target target, String searchEngine, boolean userEntered) { + mBrowserToolbar.setTitle(url); + super.loadRequest(url, target, searchEngine, userEntered); + } + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + + LinearLayout actionBar = (LinearLayout) findViewById(R.id.browser_toolbar); + + mBrowserToolbar = new BrowserToolbar(mAppContext); + mBrowserToolbar.from(actionBar); + + if (savedInstanceState != null) { + mBrowserToolbar.setTitle(savedInstanceState.getString(SAVED_STATE_TITLE)); + } + } + + @Override + public void onDestroy() { + super.onDestroy(); + if (mAboutHomeContent != null) + mAboutHomeContent.onDestroy(); + } + + @Override + public void onContentChanged() { + super.onContentChanged(); + if (mAboutHomeContent != null) + mAboutHomeContent.onActivityContentChanged(this); + } + + @Override + protected void finishProfileMigration() { + // Update about:home with the new information. + updateAboutHomeTopSites(); + + super.finishProfileMigration(); + } + + @Override void initializeChrome(String uri, Boolean isExternalURL) { + super.initializeChrome(uri, isExternalURL); + + mBrowserToolbar.updateBackButton(false); + mBrowserToolbar.updateForwardButton(false); + + Intent intent = getIntent(); + String action = intent.getAction(); + String args = intent.getStringExtra("args"); + if (args != null && args.contains("-profile")) { + Pattern p = Pattern.compile("(?:-profile\\s*)(\\w*)(\\s*)"); + Matcher m = p.matcher(args); + if (m.find()) { + mBrowserToolbar.setTitle(null); + } + } + + if (uri != null && uri.length() > 0) { + mBrowserToolbar.setTitle(uri); + } + + if (!isExternalURL) { + // show about:home if we aren't restoring previous session + if (mRestoreMode == GeckoAppShell.RESTORE_NONE) { + mBrowserToolbar.updateTabCount(1); + showAboutHome(); + } + } else { + mBrowserToolbar.updateTabCount(1); + } + + mBrowserToolbar.setProgressVisibility(isExternalURL || (mRestoreMode != GeckoAppShell.RESTORE_NONE)); + } + + void toggleChrome(final boolean aShow) { + mMainHandler.post(new Runnable() { + public void run() { + if (aShow) { + mBrowserToolbar.show(); + } else { + mBrowserToolbar.hide(); + } + } + }); + + super.toggleChrome(aShow); + } + + @Override + void focusChrome() { + mMainHandler.post(new Runnable() { + public void run() { + mBrowserToolbar.setVisibility(View.VISIBLE); + mBrowserToolbar.requestFocusFromTouch(); + } + }); + } + + @Override + public void refreshChrome() { + if (Build.VERSION.SDK_INT >= 11) { + mBrowserToolbar.requestLayout(); + mBrowserToolbar.refresh(); + invalidateOptionsMenu(); + mTabsPanel.refresh(); + } + } + + void addTab() { + showAwesomebar(AwesomeBar.Target.NEW_TAB); + } + + public void showLocalTabs() { + showTabs(TabsPanel.Panel.LOCAL_TABS); + } + + public void showRemoteTabs() { + showTabs(TabsPanel.Panel.REMOTE_TABS); + } + + private void showTabs(TabsPanel.Panel panel) { + if (!sIsGeckoReady) + return; + + mTabsPanel.show(panel); + mBrowserToolbar.updateTabs(true); + } + + public void hideTabs() { + mTabsPanel.hide(); + mBrowserToolbar.updateTabs(false); + } + + public boolean areTabsShown() { + return mTabsPanel.isShown(); + } + + /* Doorhanger notification methods */ + @Override + void updatePopups(final Tab tab) { + mDoorHangerPopup.updatePopup(mBrowserToolbar.mFavicon); + } + + @Override + void addDoorHanger(String message, String value, JSONArray buttons, Tab tab, JSONObject options) { + mDoorHangerPopup.addDoorHanger(message, value, buttons, tab, options, mBrowserToolbar.mFavicon); + } + + /* Favicon methods */ + private void loadFavicon(final Tab tab) { + maybeCancelFaviconLoad(tab); + + long id = mFavicons.loadFavicon(tab.getURL(), tab.getFaviconURL(), + new Favicons.OnFaviconLoadedListener() { + + public void onFaviconLoaded(String pageUrl, Drawable favicon) { + // Leave favicon UI untouched if we failed to load the image + // for some reason. + if (favicon == null) + return; + + Log.i(LOGTAG, "Favicon successfully loaded for URL = " + pageUrl); + + // The tab might be pointing to another URL by the time the + // favicon is finally loaded, in which case we simply ignore it. + if (!tab.getURL().equals(pageUrl)) + return; + + Log.i(LOGTAG, "Favicon is for current URL = " + pageUrl); + + tab.updateFavicon(favicon); + tab.setFaviconLoadId(Favicons.NOT_LOADING); + + if (Tabs.getInstance().isSelectedTab(tab)) + mBrowserToolbar.setFavicon(tab.getFavicon()); + + Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.FAVICON); + } + }); + + tab.setFaviconLoadId(id); + } + + private void maybeCancelFaviconLoad(Tab tab) { + long faviconLoadId = tab.getFaviconLoadId(); + + if (faviconLoadId == Favicons.NOT_LOADING) + return; + + // Cancel pending favicon load task + mFavicons.cancelFaviconLoad(faviconLoadId); + + // Reset favicon load state + tab.setFaviconLoadId(Favicons.NOT_LOADING); + } + + + /* About:home UI */ + void updateAboutHomeTopSites() { + if (mAboutHomeContent == null) + return; + + GeckoApp.mAppContext.mMainHandler.post(new Runnable() { + public void run() { + mAboutHomeContent.update(GeckoApp.mAppContext, + EnumSet.of(AboutHomeContent.UpdateFlags.TOP_SITES)); + } + }); + } + + public void showAboutHome() { + Runnable r = new AboutHomeRunnable(true); + mMainHandler.postAtFrontOfQueue(r); + } + + public void hideAboutHome() { + Runnable r = new AboutHomeRunnable(false); + mMainHandler.postAtFrontOfQueue(r); + } + + public class AboutHomeRunnable implements Runnable { + boolean mShow; + AboutHomeRunnable(boolean show) { + mShow = show; + } + + public void run() { + mFormAssistPopup.hide(); + if (mShow) { + if (mAboutHomeContent == null) { + mAboutHomeContent = (AboutHomeContent) findViewById(R.id.abouthome_content); + mAboutHomeContent.init(); + mAboutHomeContent.update(GeckoApp.mAppContext, AboutHomeContent.UpdateFlags.ALL); + mAboutHomeContent.setUriLoadCallback(new AboutHomeContent.UriLoadCallback() { + public void callback(String url) { + mBrowserToolbar.setProgressVisibility(true); + loadUrl(url, AwesomeBar.Target.CURRENT_TAB); + } + }); + } else { + mAboutHomeContent.update(GeckoApp.mAppContext, + EnumSet.of(AboutHomeContent.UpdateFlags.TOP_SITES, + AboutHomeContent.UpdateFlags.REMOTE_TABS)); + } + + mAboutHomeContent.setVisibility(View.VISIBLE); + } else { + findViewById(R.id.abouthome_content).setVisibility(View.GONE); + } + } + } + + @Override + public boolean onCreateOptionsMenu(Menu menu) + { + sMenu = menu; + + // Inform the menu about the action-items bar. + if (menu instanceof GeckoMenu && isTablet()) + ((GeckoMenu) menu).setActionItemBarPresenter(mBrowserToolbar); + + MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.gecko_menu, sMenu); + return true; + } + + @Override + public void openOptionsMenu() { + // Scroll custom menu to the top + if (mMenuPanel != null) + mMenuPanel.scrollTo(0, 0); + + if (!mBrowserToolbar.openOptionsMenu()) + super.openOptionsMenu(); + } + + @Override + public void closeOptionsMenu() { + if (!mBrowserToolbar.closeOptionsMenu()) + super.closeOptionsMenu(); + } + + @Override + public void setFullScreen(final boolean fullscreen) { + super.setFullScreen(fullscreen); + if (fullscreen) + mBrowserToolbar.hide(); + else + mBrowserToolbar.show(); + } +} diff --git a/mobile/android/base/BrowserToolbar.java b/mobile/android/base/BrowserToolbar.java index 99366f6284a6..930a31e05cd1 100644 --- a/mobile/android/base/BrowserToolbar.java +++ b/mobile/android/base/BrowserToolbar.java @@ -46,6 +46,7 @@ import android.widget.Toast; import android.widget.ViewSwitcher; public class BrowserToolbar implements ViewSwitcher.ViewFactory, + Tabs.OnTabsChangedListener, GeckoMenu.ActionItemBarPresenter { private static final String LOGTAG = "GeckoToolbar"; private LinearLayout mLayout; @@ -93,6 +94,7 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory, mInflater = LayoutInflater.from(context); sActionItems = new ArrayList(); + Tabs.getInstance().registerOnTabsChangedListener(this); } public void from(LinearLayout layout) { @@ -180,7 +182,7 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory, // Calculate the left margin for the arrow based on the position of the lock icon. int leftMargin = lockLocation[0] - lockLayoutParams.rightMargin; - SiteIdentityPopup.getInstance().show(leftMargin); + SiteIdentityPopup.getInstance().show(mSiteSecurity, leftMargin); } }); @@ -266,6 +268,49 @@ public class BrowserToolbar implements ViewSwitcher.ViewFactory, mLayout.invalidate(); } + public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { + switch(msg) { + case TITLE: + // if (sameDocument) + if (Tabs.getInstance().isSelectedTab(tab)) { + setTitle(tab.getDisplayTitle()); + } + break; + case START: + if (Tabs.getInstance().isSelectedTab(tab)) { + setSecurityMode(tab.getSecurityMode()); + setReaderVisibility(tab.getReaderEnabled()); + updateBackButton(tab.canDoBack()); + updateForwardButton(tab.canDoForward()); + Boolean showProgress = (Boolean)data; + if (showProgress && tab.getState() == Tab.STATE_LOADING) + setProgressVisibility(true); + } + break; + case STOP: + if (Tabs.getInstance().isSelectedTab(tab)) { + updateBackButton(tab.canDoBack()); + updateForwardButton(tab.canDoForward()); + setProgressVisibility(false); + } + break; + case RESTORED: + case SELECTED: + case LOCATION_CHANGE: + case LOAD_ERROR: + if (Tabs.getInstance().isSelectedTab(tab)) { + refresh(); + } + break; + case CLOSED: + case ADDED: + updateTabCountAndAnimate(Tabs.getInstance().getCount()); + updateBackButton(false); + updateForwardButton(false); + break; + } + } + @Override public View makeView() { // This returns a TextView for the TextSwitcher. diff --git a/mobile/android/base/DoorHanger.java b/mobile/android/base/DoorHanger.java index a3e56d9e9baf..454c4aee0d2d 100644 --- a/mobile/android/base/DoorHanger.java +++ b/mobile/android/base/DoorHanger.java @@ -90,7 +90,7 @@ public class DoorHanger extends LinearLayout implements Button.OnClickListener { // This will hide the doorhanger (and hide the popup if there are no // more doorhangers to show) - GeckoApp.mDoorHangerPopup.updatePopup(); + ((GeckoApp)mContext).updatePopups(mTab); } public void show() { diff --git a/mobile/android/base/DoorHangerPopup.java b/mobile/android/base/DoorHangerPopup.java index d83629fbf1da..3074231fb044 100644 --- a/mobile/android/base/DoorHangerPopup.java +++ b/mobile/android/base/DoorHangerPopup.java @@ -10,8 +10,10 @@ import java.util.HashMap; import android.content.Context; import android.graphics.drawable.BitmapDrawable; import android.util.Log; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.ViewGroup; +import android.view.View; import android.widget.PopupWindow; import android.widget.LinearLayout; import android.widget.RelativeLayout; @@ -48,7 +50,7 @@ public class DoorHangerPopup extends PopupWindow { } public void addDoorHanger(String message, String value, JSONArray buttons, - Tab tab, JSONObject options) { + Tab tab, JSONObject options, View v) { Log.i(LOGTAG, "Adding a DoorHanger to Tab: " + tab.getId()); if (!mInflated) @@ -81,11 +83,15 @@ public class DoorHangerPopup extends PopupWindow { // Only update the popup if we're adding a notifcation to the selected tab if (tab.equals(Tabs.getInstance().getSelectedTab())) - updatePopup(); + updatePopup(v); } // Updates popup contents to show doorhangers for the selected tab public void updatePopup() { + updatePopup(null); + } + + public void updatePopup(View v) { Tab tab = Tabs.getInstance().getSelectedTab(); if (tab == null) { hidePopup(); @@ -115,24 +121,25 @@ public class DoorHangerPopup extends PopupWindow { dh.show(); } - showPopup(); + if (v == null) + showAtLocation(((GeckoApp)mContext).getView(), Gravity.TOP, 0, 0); + else + showPopup(v); } public void hidePopup() { if (isShowing()) { - Log.i(LOGTAG, "Hiding the DoorHangerPopup"); dismiss(); } } - public void showPopup() { - Log.i(LOGTAG, "Showing the DoorHangerPopup"); + public void showPopup(View v) { fixBackgroundForFirst(); if (isShowing()) update(); else - showAsDropDown(GeckoApp.mBrowserToolbar.mFavicon); + showAsDropDown(v); } private void fixBackgroundForFirst() { diff --git a/mobile/android/base/GeckoApp.java b/mobile/android/base/GeckoApp.java index 78df0af82b1d..0ebf530ac150 100644 --- a/mobile/android/base/GeckoApp.java +++ b/mobile/android/base/GeckoApp.java @@ -65,7 +65,9 @@ abstract public class GeckoApp extends GeckoActivity implements GeckoEventListener, SensorEventListener, LocationListener, GeckoApplication.ApplicationLifecycleCallbacks, - TabsPanel.TabsLayoutChangeListener { + Tabs.OnTabsChangedListener, + TabsPanel.TabsLayoutChangeListener +{ private static final String LOGTAG = "GeckoApp"; public static enum StartupMode { @@ -87,10 +89,11 @@ abstract public class GeckoApp StartupMode mStartupMode = null; private LinearLayout mMainLayout; private RelativeLayout mGeckoLayout; + public View getView() { return mGeckoLayout; } public static SurfaceView cameraView; public static GeckoApp mAppContext; public static boolean mDOMFullScreen = false; - private MenuPanel mMenuPanel; + protected MenuPanel mMenuPanel; public static Menu sMenu; private static GeckoThread sGeckoThread = null; public Handler mMainHandler; @@ -101,7 +104,6 @@ abstract public class GeckoApp private GeckoConnectivityReceiver mConnectivityReceiver; private GeckoBatteryManager mBatteryReceiver; - public static BrowserToolbar mBrowserToolbar; public static DoorHangerPopup mDoorHangerPopup; public static FormAssistPopup mFormAssistPopup; public TabsPanel mTabsPanel; @@ -109,7 +111,6 @@ abstract public class GeckoApp private static LayerController mLayerController; private static GeckoLayerClient mLayerClient; - private AboutHomeContent mAboutHomeContent; private static AbsoluteLayout mPluginContainer; private static FindInPageBar mFindInPageBar; @@ -120,7 +121,7 @@ abstract public class GeckoApp private HashMap mWakeLocks = new HashMap(); - private int mRestoreMode = GeckoAppShell.RESTORE_NONE; + protected int mRestoreMode = GeckoAppShell.RESTORE_NONE; private boolean mInitialized = false; static Vector sAddonMenuItems = new Vector(); @@ -158,6 +159,42 @@ abstract public class GeckoApp } } + void toggleChrome(final Boolean aShow) { } + + void focusChrome() { } + + public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { + switch(msg) { + case LOCATION_CHANGE: + if (Tabs.getInstance().isSelectedTab(tab)) { + hidePlugins(tab); + updatePopups(tab); + invalidateOptionsMenu(); + } + break; + case LOAD_ERROR: + case START: + case STOP: + if (Tabs.getInstance().isSelectedTab(tab)) { + invalidateOptionsMenu(); + } + break; + case UNSELECTED: + case CLOSED: + invalidateOptionsMenu(); + updatePopups(tab); + hidePlugins(tab); + break; + case ADDED: + case SELECTED: + invalidateOptionsMenu(); + updatePopups(tab); + break; + } + } + + public void refreshChrome() { } + public static final String PLUGIN_ACTION = "android.webkit.PLUGIN"; /** @@ -449,10 +486,6 @@ abstract public class GeckoApp { sMenu = menu; - // Inform the menu about the action-items bar. - if (menu instanceof GeckoMenu && isTablet()) - ((GeckoMenu) menu).setActionItemBarPresenter(mBrowserToolbar); - MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.gecko_menu, sMenu); return true; @@ -512,22 +545,6 @@ abstract public class GeckoApp return true; } - @Override - public void openOptionsMenu() { - // Scroll custom menu to the top - if (mMenuPanel != null) - mMenuPanel.scrollTo(0, 0); - - if (!mBrowserToolbar.openOptionsMenu()) - super.openOptionsMenu(); - } - - @Override - public void closeOptionsMenu() { - if (!mBrowserToolbar.closeOptionsMenu()) - super.closeOptionsMenu(); - } - @Override public MenuInflater getMenuInflater() { if (Build.VERSION.SDK_INT >= 11) @@ -772,51 +789,12 @@ abstract public class GeckoApp } } - private void maybeCancelFaviconLoad(Tab tab) { - long faviconLoadId = tab.getFaviconLoadId(); - - if (faviconLoadId == Favicons.NOT_LOADING) - return; - - // Cancel pending favicon load task - mFavicons.cancelFaviconLoad(faviconLoadId); - - // Reset favicon load state - tab.setFaviconLoadId(Favicons.NOT_LOADING); + void updatePopups(final Tab tab) { + mDoorHangerPopup.updatePopup(); } - private void loadFavicon(final Tab tab) { - maybeCancelFaviconLoad(tab); - - long id = mFavicons.loadFavicon(tab.getURL(), tab.getFaviconURL(), - new Favicons.OnFaviconLoadedListener() { - - public void onFaviconLoaded(String pageUrl, Drawable favicon) { - // Leave favicon UI untouched if we failed to load the image - // for some reason. - if (favicon == null) - return; - - Log.i(LOGTAG, "Favicon successfully loaded for URL = " + pageUrl); - - // The tab might be pointing to another URL by the time the - // favicon is finally loaded, in which case we simply ignore it. - if (!tab.getURL().equals(pageUrl)) - return; - - Log.i(LOGTAG, "Favicon is for current URL = " + pageUrl); - - tab.updateFavicon(favicon); - tab.setFaviconLoadId(Favicons.NOT_LOADING); - - if (Tabs.getInstance().isSelectedTab(tab)) - mBrowserToolbar.setFavicon(tab.getFavicon()); - - Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.FAVICON); - } - }); - - tab.setFaviconLoadId(id); + void addDoorHanger(String message, String value, JSONArray buttons, Tab tab, JSONObject options) { + mDoorHangerPopup.addDoorHanger(message, value, buttons, tab, options, null); } void handleLocationChange(final int tabId, final String uri, @@ -826,27 +804,9 @@ abstract public class GeckoApp if (tab == null) return; - if (Tabs.getInstance().isSelectedTab(tab)) { - if (uri.equals("about:home")) - showAboutHome(); - else - hideAboutHome(); - } - tab.updateURL(uri); tab.setDocumentURI(documentURI); - if (sameDocument) { - mMainHandler.post(new Runnable() { - public void run() { - if (Tabs.getInstance().isSelectedTab(tab)) { - mBrowserToolbar.setTitle(uri); - } - } - }); - return; - } - tab.setContentType(contentType); tab.clearFavicon(); tab.updateIdentityData(null); @@ -859,18 +819,9 @@ abstract public class GeckoApp tab.setHasTouchListeners(false); tab.setCheckerboardColor(Color.WHITE); - maybeCancelFaviconLoad(tab); - mMainHandler.post(new Runnable() { public void run() { - if (Tabs.getInstance().isSelectedTab(tab)) { - mBrowserToolbar.refresh(); - invalidateOptionsMenu(); - mDoorHangerPopup.updatePopup(); - - if (tab != null) - hidePlugins(tab); - } + Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.LOCATION_CHANGE); } }); } @@ -881,13 +832,6 @@ abstract public class GeckoApp return; tab.updateIdentityData(identityData); - - mMainHandler.post(new Runnable() { - public void run() { - if (Tabs.getInstance().isSelectedTab(tab)) - mBrowserToolbar.setSecurityMode(tab.getSecurityMode()); - } - }); } void handleReaderEnabled(final int tabId) { @@ -896,58 +840,28 @@ abstract public class GeckoApp return; tab.setReaderEnabled(true); - - mMainHandler.post(new Runnable() { - public void run() { - if (Tabs.getInstance().isSelectedTab(tab)) - mBrowserToolbar.setReaderVisibility(tab.getReaderEnabled()); - } - }); } void handleLoadError(final int tabId, final String uri, final String title) { final Tab tab = Tabs.getInstance().getTab(tabId); if (tab == null) return; - + // When a load error occurs, the URLBar can get corrupt so we reset it mMainHandler.post(new Runnable() { public void run() { - if (Tabs.getInstance().isSelectedTab(tab)) { - mBrowserToolbar.refresh(); - invalidateOptionsMenu(); - } + Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.LOAD_ERROR); } }); } - void handlePageShow(final int tabId) { - final Tab tab = Tabs.getInstance().getTab(tabId); - if (tab == null) - return; - - mMainHandler.post(new Runnable() { - public void run() { - loadFavicon(tab); - } - }); + void handlePageShow(final int tabId) { } + + private String getDefaultProfileName() { + return "default"; } - void updateAboutHomeTopSites() { - if (mAboutHomeContent == null) - return; - - GeckoApp.mAppContext.mMainHandler.post(new Runnable() { - public void run() { - mAboutHomeContent.update(GeckoApp.mAppContext, - EnumSet.of(AboutHomeContent.UpdateFlags.TOP_SITES)); - } - }); - } - - void handleClearHistory() { - updateAboutHomeTopSites(); - } + void handleClearHistory() { } public StartupMode getStartupMode() { // This function might touch the disk and should not @@ -962,7 +876,10 @@ abstract public class GeckoApp // This key should be profile-dependent. For now, we're simply hardcoding // the "default" profile here. - String keyName = packageName + ".default.startup_version"; + String profileName = getDefaultProfileName(); + if (profileName == null) + profileName = "default"; + String keyName = packageName + "." + profileName + ".startup_version"; String appVersion = null; try { @@ -994,34 +911,17 @@ abstract public class GeckoApp } } - void addTab() { - showAwesomebar(AwesomeBar.Target.NEW_TAB); - } + void addTab() { } - public void showLocalTabs() { - showTabs(TabsPanel.Panel.LOCAL_TABS); - } + public void showLocalTabs() { } - public void showRemoteTabs() { - showTabs(TabsPanel.Panel.REMOTE_TABS); - } + public void showRemoteTabs() { } - private void showTabs(TabsPanel.Panel panel) { - if (!sIsGeckoReady) - return; + private void showTabs(TabsPanel.Panel panel) { } - mTabsPanel.show(panel); - mBrowserToolbar.updateTabs(true); - } + public void hideTabs() { } - public void hideTabs() { - mTabsPanel.hide(); - mBrowserToolbar.updateTabs(false); - } - - public boolean areTabsShown() { - return mTabsPanel.isShown(); - } + public boolean areTabsShown() { return false; } @Override public void onTabsLayoutChange(int width, int height) { @@ -1164,24 +1064,11 @@ abstract public class GeckoApp GeckoAppShell.sendPendingEventsToGecko(); connectGeckoLayerClient(); } else if (event.equals("ToggleChrome:Hide")) { - mMainHandler.post(new Runnable() { - public void run() { - mBrowserToolbar.hide(); - } - }); + toggleChrome(false); } else if (event.equals("ToggleChrome:Show")) { - mMainHandler.post(new Runnable() { - public void run() { - mBrowserToolbar.show(); - } - }); + toggleChrome(true); } else if (event.equals("ToggleChrome:Focus")) { - mMainHandler.post(new Runnable() { - public void run() { - mBrowserToolbar.setVisibility(View.VISIBLE); - mBrowserToolbar.requestFocusFromTouch(); - } - }); + focusChrome(); } else if (event.equals("DOMFullScreen:Start")) { mDOMFullScreen = true; } else if (event.equals("DOMFullScreen:Stop")) { @@ -1262,12 +1149,7 @@ abstract public class GeckoApp } }); } else if (event.equals("Session:StatePurged")) { - mMainHandler.post(new Runnable() { - public void run() { - if (mAboutHomeContent != null) - mAboutHomeContent.setLastTabsVisibility(false); - } - }); + onStatePurged(); } else if (event.equals("Bookmark:Insert")) { final String url = message.getString("url"); final String title = message.getString("title"); @@ -1336,47 +1218,7 @@ abstract public class GeckoApp } } - public void showAboutHome() { - Runnable r = new AboutHomeRunnable(true); - mMainHandler.postAtFrontOfQueue(r); - } - - public void hideAboutHome() { - Runnable r = new AboutHomeRunnable(false); - mMainHandler.postAtFrontOfQueue(r); - } - - public class AboutHomeRunnable implements Runnable { - boolean mShow; - AboutHomeRunnable(boolean show) { - mShow = show; - } - - public void run() { - mFormAssistPopup.hide(); - if (mShow) { - if (mAboutHomeContent == null) { - mAboutHomeContent = (AboutHomeContent) findViewById(R.id.abouthome_content); - mAboutHomeContent.init(); - mAboutHomeContent.update(GeckoApp.mAppContext, AboutHomeContent.UpdateFlags.ALL); - mAboutHomeContent.setUriLoadCallback(new AboutHomeContent.UriLoadCallback() { - public void callback(String url) { - mBrowserToolbar.setProgressVisibility(true); - loadUrl(url, AwesomeBar.Target.CURRENT_TAB); - } - }); - } else { - mAboutHomeContent.update(GeckoApp.mAppContext, - EnumSet.of(AboutHomeContent.UpdateFlags.TOP_SITES, - AboutHomeContent.UpdateFlags.REMOTE_TABS)); - } - - mAboutHomeContent.setVisibility(View.VISIBLE); - } else { - findViewById(R.id.abouthome_content).setVisibility(View.GONE); - } - } - } + void onStatePurged() { } /** * @param aPermissions @@ -1458,7 +1300,7 @@ abstract public class GeckoApp public void run() { Tab tab = Tabs.getInstance().getTab(tabId); if (tab != null) - mDoorHangerPopup.addDoorHanger(message, value, buttons, tab, options); + addDoorHanger(message, value, buttons, tab, options); } }); } @@ -1475,7 +1317,7 @@ abstract public class GeckoApp if (tab == null) return; tab.removeDoorHanger(value); - mDoorHangerPopup.updatePopup(); + updatePopups(tab); } }); } @@ -1492,16 +1334,7 @@ abstract public class GeckoApp getLayerController().getView().getRenderer().resetCheckerboard(); mMainHandler.post(new Runnable() { public void run() { - if (Tabs.getInstance().isSelectedTab(tab)) { - mBrowserToolbar.setSecurityMode(tab.getSecurityMode()); - mBrowserToolbar.setReaderVisibility(tab.getReaderEnabled()); - mBrowserToolbar.updateBackButton(tab.canDoBack()); - mBrowserToolbar.updateForwardButton(tab.canDoForward()); - invalidateOptionsMenu(); - if (showProgress && tab.getState() == Tab.STATE_LOADING) - mBrowserToolbar.setProgressVisibility(true); - } - Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.START); + Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.START, showProgress); } }); } @@ -1515,12 +1348,6 @@ abstract public class GeckoApp mMainHandler.post(new Runnable() { public void run() { - if (Tabs.getInstance().isSelectedTab(tab)) { - mBrowserToolbar.updateBackButton(tab.canDoBack()); - mBrowserToolbar.updateForwardButton(tab.canDoForward()); - invalidateOptionsMenu(); - mBrowserToolbar.setProgressVisibility(false); - } Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.STOP); } }); @@ -1570,14 +1397,6 @@ abstract public class GeckoApp return; tab.updateTitle(title); - - mMainHandler.post(new Runnable() { - public void run() { - if (Tabs.getInstance().isSelectedTab(tab)) - mBrowserToolbar.setTitle(tab.getDisplayTitle()); - Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.TITLE); - } - }); } void handleLinkAdded(final int tabId, String rel, final String href, int size) { @@ -1589,18 +1408,6 @@ abstract public class GeckoApp return; tab.updateFaviconURL(href, size); - - // If tab is not loading and the favicon is updated, we - // want to load the image straight away. If tab is still - // loading, we only load the favicon once the page's content - // is fully loaded (see handleContentLoaded()). - if (tab.getState() != Tab.STATE_LOADING) { - mMainHandler.post(new Runnable() { - public void run() { - loadFavicon(tab); - } - }); - } } void handleWindowClose(final int tabId) { @@ -1853,16 +1660,6 @@ abstract public class GeckoApp return false; } - // The ActionBar needs to be refreshed on rotation as different orientation uses different resources - public void refreshActionBar() { - if (Build.VERSION.SDK_INT >= 11) { - mBrowserToolbar.requestLayout(); - mBrowserToolbar.refresh(); - invalidateOptionsMenu(); - mTabsPanel.refresh(); - } - } - /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) @@ -1901,32 +1698,27 @@ abstract public class GeckoApp setContentView(R.layout.gecko_app); - LinearLayout actionBar = (LinearLayout) findViewById(R.id.browser_toolbar); - - mBrowserToolbar = new BrowserToolbar(mAppContext); - mBrowserToolbar.from(actionBar); - // setup gecko layout mGeckoLayout = (RelativeLayout) findViewById(R.id.gecko_layout); mMainLayout = (LinearLayout) findViewById(R.id.main_layout); // setup tabs panel mTabsPanel = (TabsPanel) findViewById(R.id.tabs_panel); - mTabsPanel.setTabsLayoutChangeListener(this); + if (mTabsPanel != null) + mTabsPanel.setTabsLayoutChangeListener(this); if (savedInstanceState != null) { - mBrowserToolbar.setTitle(savedInstanceState.getString(SAVED_STATE_TITLE)); mRestoreMode = GeckoAppShell.RESTORE_OOM; } ((GeckoApplication) getApplication()).addApplicationLifecycleCallbacks(this); } + void initializeChrome(String uri, Boolean isExternalURL) { } + private void initialize() { mInitialized = true; - mBrowserToolbar.updateBackButton(false); - mBrowserToolbar.updateForwardButton(false); invalidateOptionsMenu(); Intent intent = getIntent(); @@ -1937,7 +1729,6 @@ abstract public class GeckoApp Matcher m = p.matcher(args); if (m.find()) { mProfile = GeckoProfile.get(this, m.group(1)); - mBrowserToolbar.setTitle(null); } } @@ -1950,24 +1741,13 @@ abstract public class GeckoApp String uri = getURIFromIntent(intent); if (uri != null && uri.length() > 0) { passedUri = uri; - mBrowserToolbar.setTitle(uri); } if (mRestoreMode == GeckoAppShell.RESTORE_NONE && getProfile().shouldRestoreSession()) mRestoreMode = GeckoAppShell.RESTORE_CRASH; boolean isExternalURL = passedUri != null && !passedUri.equals("about:home"); - if (!isExternalURL) { - // show about:home if we aren't restoring previous session - if (mRestoreMode == GeckoAppShell.RESTORE_NONE) { - mBrowserToolbar.updateTabCount(1); - showAboutHome(); - } - } else { - mBrowserToolbar.updateTabCount(1); - } - - mBrowserToolbar.setProgressVisibility(isExternalURL || (mRestoreMode != GeckoAppShell.RESTORE_NONE)); + initializeChrome(uri, isExternalURL); // Start migrating as early as possible, can do this in // parallel with Gecko load. @@ -2005,7 +1785,8 @@ abstract public class GeckoApp mFavicons = new Favicons(this); - Tabs.getInstance().setContentResolver(getContentResolver()); + Tabs.getInstance().setContentResolver(getContentResolver()); + Tabs.getInstance().registerOnTabsChangedListener(this); if (cameraView == null) { cameraView = new SurfaceView(this); @@ -2326,7 +2107,7 @@ abstract public class GeckoApp if (mOrientation != newOrientation) { mOrientation = newOrientation; - refreshActionBar(); + refreshChrome(); } } @@ -2428,17 +2209,12 @@ abstract public class GeckoApp if (mBatteryReceiver != null) mBatteryReceiver.unregisterFor(mAppContext); - if (mAboutHomeContent != null) - mAboutHomeContent.onDestroy(); - ((GeckoApplication) getApplication()).removeApplicationLifecycleCallbacks(this); } @Override public void onContentChanged() { super.onContentChanged(); - if (mAboutHomeContent != null) - mAboutHomeContent.onActivityContentChanged(this); } @@ -2454,7 +2230,7 @@ abstract public class GeckoApp if (mFormAssistPopup != null) mFormAssistPopup.hide(); SiteIdentityPopup.getInstance().dismiss(); - refreshActionBar(); + refreshChrome(); } } @@ -2658,14 +2434,16 @@ abstract public class GeckoApp long timeDiff = SystemClock.uptimeMillis() - currentTime; Log.i(LOGTAG, "Profile migration took " + timeDiff + " ms"); - // Update about:home with the new information. - updateAboutHomeTopSites(); + finishProfileMigration(); } }} ); } } + protected void finishProfileMigration() { + } + private void checkMigrateSync() { final File profileDir = getProfile().getDir(); if (profileDir != null) { @@ -2907,7 +2685,7 @@ abstract public class GeckoApp @Override public void onBackPressed() { - if (mTabsPanel.isShown()) { + if (mTabsPanel != null && mTabsPanel.isShown()) { mTabsPanel.hide(); return; } @@ -3110,8 +2888,7 @@ abstract public class GeckoApp // If searchEngine is provided, url will be used as the search query. // Otherwise, the url is loaded. - private void loadRequest(String url, AwesomeBar.Target target, String searchEngine, boolean userEntered) { - mBrowserToolbar.setTitle(url); + protected void loadRequest(String url, AwesomeBar.Target target, String searchEngine, boolean userEntered) { Log.d(LOGTAG, target.name()); JSONObject args = new JSONObject(); try { diff --git a/mobile/android/base/Makefile.in b/mobile/android/base/Makefile.in index 91e92b3d40c9..452352c515ce 100644 --- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -26,6 +26,7 @@ FENNEC_JAVA_FILES = \ AlertNotification.java \ AwesomeBar.java \ AwesomeBarTabs.java \ + BrowserApp.java \ BrowserToolbar.java \ ConfirmPreference.java \ SyncPreference.java \ diff --git a/mobile/android/base/SiteIdentityPopup.java b/mobile/android/base/SiteIdentityPopup.java index 225f31bfc72d..6dde53770bee 100644 --- a/mobile/android/base/SiteIdentityPopup.java +++ b/mobile/android/base/SiteIdentityPopup.java @@ -79,7 +79,7 @@ public class SiteIdentityPopup extends PopupWindow { mInflated = true; } - public void show(int leftMargin) { + public void show(View v, int leftMargin) { Tab selectedTab = Tabs.getInstance().getSelectedTab(); if (selectedTab == null) { Log.e(LOGTAG, "Selected tab is null"); @@ -154,6 +154,6 @@ public class SiteIdentityPopup extends PopupWindow { mArrow.setLayoutParams(newLayoutParams); // This will place the popup at the correct vertical position - showAsDropDown(GeckoApp.mBrowserToolbar.mSiteSecurity); + showAsDropDown(v); } } diff --git a/mobile/android/base/Tab.java b/mobile/android/base/Tab.java index 0d47f507c282..edaa943807b3 100644 --- a/mobile/android/base/Tab.java +++ b/mobile/android/base/Tab.java @@ -243,6 +243,13 @@ public final class Tab { Log.i(LOGTAG, "Updated title: " + mTitle + " for tab with id: " + mId); updateHistory(mUrl, mTitle); + final Tab tab = this; + + GeckoAppShell.getMainHandler().post(new Runnable() { + public void run() { + Tabs.getInstance().notifyListeners(tab, Tabs.TabEvents.TITLE); + } + }); } private void updateHistory(final String uri, final String title) { diff --git a/mobile/android/base/Tabs.java b/mobile/android/base/Tabs.java index b9d5ffd23fb6..7aeee1d5aab5 100644 --- a/mobile/android/base/Tabs.java +++ b/mobile/android/base/Tabs.java @@ -59,17 +59,14 @@ public class Tabs implements GeckoEventListener { int parentId = params.getInt("parentId"); String title = params.getString("title"); - Tab tab = new Tab(id, url, external, parentId, title); + final Tab tab = new Tab(id, url, external, parentId, title); tabs.put(id, tab); order.add(tab); if (!mRestoringSession) { GeckoApp.mAppContext.mMainHandler.post(new Runnable() { public void run() { - GeckoApp.mBrowserToolbar.updateTabCountAndAnimate(getCount()); - GeckoApp.mBrowserToolbar.updateBackButton(false); - GeckoApp.mBrowserToolbar.updateForwardButton(false); - GeckoApp.mAppContext.invalidateOptionsMenu(); + notifyListeners(tab, TabEvents.ADDED); } }); } @@ -97,24 +94,16 @@ public class Tabs implements GeckoEventListener { if (tab == null) return null; - if ("about:home".equals(tab.getURL())) - GeckoApp.mAppContext.showAboutHome(); - else - GeckoApp.mAppContext.hideAboutHome(); - selectedTab = tab; GeckoApp.mAppContext.mMainHandler.post(new Runnable() { public void run() { GeckoApp.mFormAssistPopup.hide(); if (isSelectedTab(tab)) { String url = tab.getURL(); - GeckoApp.mBrowserToolbar.refresh(); - GeckoApp.mAppContext.invalidateOptionsMenu(); - GeckoApp.mDoorHangerPopup.updatePopup(); notifyListeners(tab, TabEvents.SELECTED); if (oldTab != null) - GeckoApp.mAppContext.hidePlugins(oldTab); + notifyListeners(oldTab, TabEvents.UNSELECTED); } } }); @@ -174,9 +163,6 @@ public class Tabs implements GeckoEventListener { GeckoApp.mAppContext.mMainHandler.post(new Runnable() { public void run() { notifyListeners(tab, TabEvents.CLOSED); - GeckoApp.mBrowserToolbar.updateTabCountAndAnimate(Tabs.getInstance().getCount()); - GeckoApp.mDoorHangerPopup.updatePopup(); - GeckoApp.mAppContext.hidePlugins(tab); tab.onDestroy(); } }); @@ -319,7 +305,7 @@ public class Tabs implements GeckoEventListener { } public interface OnTabsChangedListener { - public void onTabChanged(Tab tab, TabEvents msg); + public void onTabChanged(Tab tab, TabEvents msg, Object data); } private static ArrayList mTabsChangedListeners; @@ -342,20 +328,30 @@ public class Tabs implements GeckoEventListener { CLOSED, START, LOADED, + LOAD_ERROR, STOP, FAVICON, THUMBNAIL, TITLE, - SELECTED + SELECTED, + UNSELECTED, + ADDED, + RESTORED, + LOCATION_CHANGE } public void notifyListeners(Tab tab, TabEvents msg) { + notifyListeners(tab, msg, ""); + } + + public void notifyListeners(Tab tab, TabEvents msg, Object data) { if (mTabsChangedListeners == null) return; Iterator items = mTabsChangedListeners.iterator(); while (items.hasNext()) { - items.next().onTabChanged(tab, msg); + items.next().onTabChanged(tab, msg, data); } } + } diff --git a/mobile/android/base/TabsTray.java b/mobile/android/base/TabsTray.java index 09cd7f3560cd..57d2d6f75fc0 100644 --- a/mobile/android/base/TabsTray.java +++ b/mobile/android/base/TabsTray.java @@ -87,7 +87,7 @@ public class TabsTray extends LinearLayout Tabs.registerOnTabsChangedListener(this); Tabs.getInstance().refreshThumbnails(); - onTabChanged(null, null); + onTabChanged(null, null, null); } @Override @@ -98,7 +98,7 @@ public class TabsTray extends LinearLayout mTabsAdapter.notifyDataSetChanged(); } - public void onTabChanged(Tab tab, Tabs.TabEvents msg) { + public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { if (mTabsAdapter == null) { mTabsAdapter = new TabsAdapter(mContext, Tabs.getInstance().getTabsInOrder()); mList.setAdapter(mTabsAdapter); diff --git a/mobile/android/base/gfx/TouchEventHandler.java b/mobile/android/base/gfx/TouchEventHandler.java index 13fb1b0c2348..6cf30a5d1a0f 100644 --- a/mobile/android/base/gfx/TouchEventHandler.java +++ b/mobile/android/base/gfx/TouchEventHandler.java @@ -342,7 +342,7 @@ public final class TouchEventHandler implements Tabs.OnTabsChangedListener { // Tabs.OnTabsChangedListener implementation - public void onTabChanged(Tab tab, Tabs.TabEvents msg) { + public void onTabChanged(Tab tab, Tabs.TabEvents msg, Object data) { if ((Tabs.getInstance().isSelectedTab(tab) && msg == Tabs.TabEvents.STOP) || msg == Tabs.TabEvents.SELECTED) { mWaitForTouchListeners = tab.getHasTouchListeners(); }