merge fx-team to mozilla-central a=merge

This commit is contained in:
Carsten "Tomcat" Book 2014-07-31 13:01:27 +02:00
commit beada222aa
41 changed files with 726 additions and 117 deletions

View File

@ -821,26 +821,26 @@ pref("plugin.state.f5 sam inspection host plugin", 2);
pref("plugin.state.nprobloxproxy", 2);
#endif
#ifdef XP_MACOSX
pref("plugins.state.nproblox", 2);
pref("plugin.state.nproblox", 2);
#endif
// Box Edit, bug 1029654
#ifdef XP_WIN
pref("plugins.state.npboxedit", 2);
pref("plugin.state.npboxedit", 2);
#endif
#ifdef XP_MACOSX
pref("plugins.state.box edit", 2);
pref("plugin.state.box edit", 2);
#endif
// Nexus Personal, bug 1024965
#ifdef XP_WIN
pref("plugins.state.np_prsnl", 2);
pref("plugin.state.np_prsnl", 2);
#endif
#ifdef XP_MACOSX
pref("plugins.state.personalplugin", 2);
pref("plugin.state.personalplugin", 2);
#endif
#ifdef UNIX_BUT_NOT_MAC
pref("plugins.state.libplugins", 2);
pref("plugin.state.libplugins", 2);
#endif
// display door hanger if flash not installed

View File

@ -464,11 +464,12 @@ SocialShare = {
return;
this.panel.hidden = false;
// create and initialize the panel for this window
let iframe = document.createElement("iframe");
let iframe = document.createElement("browser");
iframe.setAttribute("type", "content");
iframe.setAttribute("class", "social-share-frame");
iframe.setAttribute("context", "contentAreaContextMenu");
iframe.setAttribute("tooltip", "aHTMLTooltip");
iframe.setAttribute("disableglobalhistory", "true");
iframe.setAttribute("flex", "1");
panel.appendChild(iframe);
this.populateProviderMenu();
@ -591,7 +592,15 @@ SocialShare = {
this.anchor.removeAttribute("open");
this.iframe.removeEventListener("click", this._onclick, true);
this.iframe.setAttribute("src", "data:text/plain;charset=utf8,");
// make sure that the frame is unloaded after it is hidden
this.iframe.docShell.createAboutBlankContentViewer(null);
this.currentShare = null;
// share panel use is over, purge any history
if (this.iframe.sessionHistory) {
let purge = this.iframe.sessionHistory.count;
if (purge > 0)
this.iframe.sessionHistory.PurgeHistory(purge);
}
},
setErrorMessage: function() {
@ -702,6 +711,14 @@ SocialShare = {
iframe.contentDocument.documentElement.dispatchEvent(evt);
}, true);
}
// if the user switched between share providers we do not want that history
// available.
if (iframe.sessionHistory) {
let purge = iframe.sessionHistory.count;
if (purge > 0)
iframe.sessionHistory.PurgeHistory(purge);
}
// always ensure that origin belongs to the endpoint
let uri = Services.io.newURI(shareEndpoint, null, null);
iframe.setAttribute("origin", provider.origin);

View File

@ -81,7 +81,7 @@ function runSocialTestWithProvider(manifest, callback, finishcallback) {
SessionStore.setWindowValue(window, "socialSidebar", "");
for (let i = 0; i < manifests.length; i++) {
let m = manifests[i];
for (let what of ['sidebarURL', 'workerURL', 'iconURL']) {
for (let what of ['sidebarURL', 'workerURL', 'iconURL', 'shareURL', 'markURL']) {
if (m[what]) {
yield promiseSocialUrlNotRemembered(m[what]);
}

View File

@ -8,6 +8,7 @@
var port = navigator.mozSocial.getWorker().port;
port.postMessage({topic: "share-data-message", result: shareData});
// share windows self-close
history.back(); // bug 1042991, ensure history is available
window.close();
})
</script>

View File

@ -236,10 +236,17 @@ var gPermissionManager = {
}
this._tree.treeBoxObject.invalidate();
}
// No UI other than this window causes this method to be sent a "deleted"
// notification, so we don't need to implement it since Delete is handled
// directly by the Permission Removal handlers. If that ever changes, those
// implementations will have to move into here.
else if (aData == "deleted") {
for (var i = 0; i < this._permissions.length; i++) {
if (this._permissions[i].host == permission.host) {
this._permissions.splice(i, 1);
this._view._rowCount--;
this._tree.treeBoxObject.rowCountChanged(this._view.rowCount - 1, -1);
this._tree.treeBoxObject.invalidate();
break;
}
}
}
}
},

View File

@ -114,6 +114,17 @@ function windowLoad(event, win, dialog) {
pm.remove("test.com", "popup");
},
},
{
test: function() {
url.value = "test.com";
btnAllow.doCommand();
pm.remove("test.com", "cookie");
is(tree.view.rowCount, 0, "display should update when cookie permission is deleted");
},
observances: [{ type: "cookie", host: "test.com", data: "added",
capability: allow },
{ type: "cookie", host: "test.com", data: "deleted" }]
},
];
let permObserver = {

View File

@ -14,6 +14,7 @@ import org.mozilla.gecko.R;
import org.mozilla.gecko.menu.MenuItemActionBar;
import org.mozilla.gecko.menu.MenuItemDefault;
import org.mozilla.gecko.tests.UITestContext;
import org.mozilla.gecko.tests.helpers.DeviceHelper;
import org.mozilla.gecko.tests.helpers.WaitHelper;
import org.mozilla.gecko.util.HardwareUtils;
@ -27,9 +28,12 @@ import com.jayway.android.robotium.solo.Solo;
* A class representing any interactions that take place on the app menu.
*/
public class AppMenuComponent extends BaseComponent {
private Boolean hasLegacyMenu = null;
public enum MenuItem {
FORWARD(R.string.forward),
NEW_TAB(R.string.new_tab),
PAGE(R.string.page),
RELOAD(R.string.reload);
private final int resourceID;
@ -48,6 +52,27 @@ public class AppMenuComponent extends BaseComponent {
}
};
public enum PageMenuItem {
SAVE_AS_PDF(R.string.save_as_pdf);
private static final MenuItem PARENT_MENU = MenuItem.PAGE;
private final int resourceID;
private String stringResource;
PageMenuItem(final int resourceID) {
this.resourceID = resourceID;
}
public String getString(final Solo solo) {
if (stringResource == null) {
stringResource = solo.getString(resourceID);
}
return stringResource;
}
};
public AppMenuComponent(final UITestContext testContext) {
super(testContext);
}
@ -56,6 +81,54 @@ public class AppMenuComponent extends BaseComponent {
fAssertFalse("Menu is not open", isMenuOpen());
}
/**
* Legacy Android devices doesn't have hierarchical menus. Sub-menus, such as "Page", are missing in these devices.
* Try to determine if the menu item "Page" is present.
*
* TODO : This fragile way to determine legacy menus should be replaced with a check for 6-panel menu item.
*
* @return true if there is a legacy menu.
*/
private boolean hasLegacyMenu() {
if (hasLegacyMenu == null) {
hasLegacyMenu = findAppMenuItemView(MenuItem.PAGE.getString(mSolo)) == null;
}
return hasLegacyMenu;
}
public void assertMenuItemIsDisabledAndVisible(PageMenuItem pageMenuItem) {
openAppMenu();
if (!hasLegacyMenu()) {
// Non-legacy devices have hierarchical menu, check for parent menu item "page".
final View parentMenuItemView = findAppMenuItemView(MenuItem.PAGE.getString(mSolo));
if (parentMenuItemView.isEnabled()) {
fAssertTrue("The parent 'page' menu item is enabled", parentMenuItemView.isEnabled());
fAssertEquals("The parent 'page' menu item is visible", View.VISIBLE,
parentMenuItemView.getVisibility());
// Parent menu "page" is enabled, open page menu and check for menu item represented by pageMenuItem.
pressMenuItem(MenuItem.PAGE.getString(mSolo));
final View pageMenuItemView = findAppMenuItemView(pageMenuItem.getString(mSolo));
fAssertFalse("The page menu item is not enabled", pageMenuItemView.isEnabled());
fAssertEquals("The page menu item is visible", View.VISIBLE, pageMenuItemView.getVisibility());
} else {
fAssertFalse("The parent 'page' menu item is not enabled", parentMenuItemView.isEnabled());
fAssertEquals("The parent 'page' menu item is visible", View.VISIBLE, parentMenuItemView.getVisibility());
}
} else {
// Legacy devices don't have parent menu item "page", check for menu item represented by pageMenuItem.
final View pageMenuItemView = findAppMenuItemView(pageMenuItem.getString(mSolo));
fAssertFalse("The page menu item is not enabled", pageMenuItemView.isEnabled());
fAssertEquals("The page menu item is visible", View.VISIBLE, pageMenuItemView.getVisibility());
}
// Close the App Menu.
mSolo.goBack();
}
private View getOverflowMenuButtonView() {
return mSolo.getView(R.id.menu);
}
@ -87,32 +160,61 @@ public class AppMenuComponent extends BaseComponent {
return null;
}
public void pressMenuItem(MenuItem menuItem) {
openAppMenu();
/**
* Helper function to let Robotium locate and click menu item from legacy Android menu (devices with Android 2.x).
*
* Robotium will also try to open the menu if there are no open dialog.
*
* @param menuItemText, The title of menu item to open.
*/
private void pressLegacyMenuItem(final String menuItemTitle) {
mSolo.clickOnMenuItem(menuItemTitle, true);
}
final String text = menuItem.getString(mSolo);
final View menuItemView = findAppMenuItemView(text);
private void pressMenuItem(final String menuItemTitle) {
fAssertTrue("Menu is open", isMenuOpen(menuItemTitle));
if (menuItemView != null) {
fAssertTrue("The menu item is enabled", menuItemView.isEnabled());
fAssertEquals("The menu item is visible", View.VISIBLE, menuItemView.getVisibility());
if (!hasLegacyMenu()) {
final View menuItemView = findAppMenuItemView(menuItemTitle);
fAssertTrue(String.format("The menu item %s is enabled", menuItemTitle), menuItemView.isEnabled());
fAssertEquals(String.format("The menu item %s is visible", menuItemTitle), View.VISIBLE,
menuItemView.getVisibility());
mSolo.clickOnView(menuItemView);
} else {
// We could not find a view representing this menu item: Let's let Robotium try to
// locate and click it in the legacy Android menu (devices with Android 2.x).
//
// Even though we already opened the menu to see if we can locate the menu item,
// Robotium will also try to open the menu if it doesn't find an open dialog (Does
// not happen in this case).
mSolo.clickOnMenuItem(text, true);
pressLegacyMenuItem(menuItemTitle);
}
}
private void pressSubMenuItem(final String parentMenuItemTitle, final String childMenuItemTitle) {
openAppMenu();
if (!hasLegacyMenu()) {
pressMenuItem(parentMenuItemTitle);
// Child menu item is not pressed yet, Click on it.
pressMenuItem(childMenuItemTitle);
} else {
pressLegacyMenuItem(childMenuItemTitle);
}
}
public void pressMenuItem(MenuItem menuItem) {
openAppMenu();
pressMenuItem(menuItem.getString(mSolo));
}
public void pressMenuItem(final PageMenuItem pageMenuItem) {
pressSubMenuItem(PageMenuItem.PARENT_MENU.getString(mSolo), pageMenuItem.getString(mSolo));
}
private void openAppMenu() {
assertMenuIsNotOpen();
if (HardwareUtils.hasMenuButton()) {
// This is a hack needed for tablets where the OverflowMenuButton is always in the GONE state,
// so we press the menu key instead.
if (HardwareUtils.hasMenuButton() || DeviceHelper.isTablet()) {
mSolo.sendKey(Solo.MENU);
} else {
pressOverflowMenuButton();
@ -130,10 +232,24 @@ public class AppMenuComponent extends BaseComponent {
mSolo.clickOnView(overflowMenuButton, true);
}
/**
* Determines whether the app menu is open by searching for the text "New tab".
*
* @return true if app menu is open.
*/
private boolean isMenuOpen() {
// The presence of the "New tab" menu item is our best guess about whether
// the menu is open or not.
return mSolo.searchText(MenuItem.NEW_TAB.getString(mSolo));
return isMenuOpen(MenuItem.NEW_TAB.getString(mSolo));
}
/**
* Determines whether the app menu is open by searching for the text in menuItemTitle.
*
* @param menuItemTitle, The contentDescription of menu item to search.
*
* @return true if app menu is open.
*/
private boolean isMenuOpen(String menuItemTitle) {
return mSolo.searchText(menuItemTitle);
}
private void waitForMenuOpen() {

View File

@ -122,6 +122,7 @@ skip-if = android_version == "10"
[testAboutHomeVisibility]
# disabled on Android 2.3; bug 946656
skip-if = android_version == "10"
[testAppMenuPathways]
[testEventDispatcher]
[testInputConnection]
# disabled on Android 2.3; bug 1025968

View File

@ -0,0 +1,34 @@
package org.mozilla.gecko.tests;
import org.mozilla.gecko.tests.components.AppMenuComponent;
import org.mozilla.gecko.tests.helpers.GeckoHelper;
import org.mozilla.gecko.tests.helpers.NavigationHelper;
/**
* Set of tests to test UI App menu and submenus the user interact with.
*/
public class testAppMenuPathways extends UITest {
/**
* Robocop supports only a single test function per test class. Therefore, we
* have a single top-level test function that dispatches to sub-tests.
*/
public void testAppMenuPathways() {
GeckoHelper.blockForReady();
_testSaveAsPDFPathway();
}
public void _testSaveAsPDFPathway() {
// Page menu should be disabled in about:home.
mAppMenu.assertMenuItemIsDisabledAndVisible(AppMenuComponent.PageMenuItem.SAVE_AS_PDF);
// Navigate to a page to test save as pdf functionality.
NavigationHelper.enterAndLoadUrl(StringHelper.ROBOCOP_BLANK_PAGE_01_URL);
mToolbar.assertTitle(StringHelper.ROBOCOP_BLANK_PAGE_01_TITLE);
// Test save as pdf functionality.
// The following call doesn't wait for the resulting pdf but checks that no exception are thrown.
mAppMenu.pressMenuItem(AppMenuComponent.PageMenuItem.SAVE_AS_PDF);
}
}

View File

@ -25,4 +25,6 @@ public class Constants {
public static final String INTENT_START_SEARCH = "org.mozilla.search.intent.START_SEARCH";
public static final String INTENT_START_SEARCH_QUERY_EXTRA = "org.mozilla.search.intent.START_SEARCH_QUERY_EXTRA";
public static final int SUGGESTION_MAX = 5;
}

View File

@ -8,6 +8,7 @@ import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Rect;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager;
@ -21,6 +22,7 @@ import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ListView;
import org.mozilla.gecko.db.BrowserContract;
import org.mozilla.gecko.db.BrowserContract.SearchHistory;
import org.mozilla.search.AcceptsSearchQuery.SuggestionAnimation;
@ -34,7 +36,12 @@ public class PreSearchFragment extends Fragment {
private ListView listView;
private final String[] PROJECTION = new String[]{SearchHistory.QUERY, SearchHistory._ID};
private static final String[] PROJECTION = new String[]{ SearchHistory.QUERY, SearchHistory._ID };
// Limit search history query results to 5 items. This value matches the number of search
// suggestions we return in SearchFragment.
private static final Uri SEARCH_HISTORY_URI = SearchHistory.CONTENT_URI.buildUpon().
appendQueryParameter(BrowserContract.PARAM_LIMIT, String.valueOf(Constants.SUGGESTION_MAX)).build();
private static final int LOADER_ID_SEARCH_HISTORY = 1;
@ -128,8 +135,8 @@ public class PreSearchFragment extends Fragment {
private class SearchHistoryLoaderCallbacks implements LoaderManager.LoaderCallbacks<Cursor> {
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(getActivity(), SearchHistory.CONTENT_URI,
PROJECTION, null, null, SearchHistory.DATE_LAST_VISITED + " DESC");
return new CursorLoader(getActivity(), SEARCH_HISTORY_URI, PROJECTION, null, null,
SearchHistory.DATE_LAST_VISITED + " DESC");
}
@Override

View File

@ -0,0 +1,124 @@
/* -*- 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.search;
import org.mozilla.gecko.AppConstants;
import android.annotation.SuppressLint;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.widget.RemoteViews;
import android.util.Log;
/* Provides a really simple widget with two buttons, one to launch Fennec
* and one to launch the search activity. All intents are actually sent back
* here and then forwarded on to start the real activity. */
public class SearchWidget extends AppWidgetProvider {
final private static String LOGTAG = "GeckoSearchWidget";
final public static String ACTION_LAUNCH_BROWSER = "org.mozilla.widget.LAUNCH_BROWSER";
final public static String ACTION_LAUNCH_SEARCH = "org.mozilla.widget.LAUNCH_SEARCH";
@SuppressLint("NewApi")
@Override
public void onUpdate(final Context context, final AppWidgetManager manager, final int[] ids) {
for (int id : ids) {
final Bundle bundle;
if (AppConstants.Versions.feature16Plus) {
bundle = manager.getAppWidgetOptions(id);
} else {
bundle = null;
}
addView(manager, context, id, bundle);
}
super.onUpdate(context, manager, ids);
}
@SuppressLint("NewApi")
@Override
public void onAppWidgetOptionsChanged(final Context context,
final AppWidgetManager manager,
final int id,
final Bundle options) {
addView(manager, context, id, options);
if (AppConstants.Versions.feature16Plus) {
super.onAppWidgetOptionsChanged(context, manager, id, options);
}
}
@Override
public void onReceive(final Context context, final Intent intent) {
// This will hold the intent to redispatch
final Intent redirect;
Log.i(LOGTAG, "Got intent " + intent.getAction());
if (intent.getAction().equals(ACTION_LAUNCH_BROWSER)) {
redirect = buildRedirectIntent(Intent.ACTION_VIEW,
AppConstants.ANDROID_PACKAGE_NAME,
AppConstants.BROWSER_INTENT_CLASS_NAME,
intent);
} else if (intent.getAction().equals(ACTION_LAUNCH_SEARCH)) {
redirect = buildRedirectIntent(Intent.ACTION_VIEW,
AppConstants.SEARCH_PACKAGE_NAME,
AppConstants.SEARCH_INTENT_CLASS_NAME,
intent);
} else {
redirect = null;
}
if (redirect != null) {
try {
context.startActivity(redirect);
} catch(Exception ex) {
// When this is built stand alone, its hardcoded to try and launch nightly.
// If that fails, just fire a generic VIEW intent.
Intent redirect2 = buildRedirectIntent(Intent.ACTION_VIEW, null, null, intent);
context.startActivity(redirect2);
}
}
super.onReceive(context, intent);
}
// Utility to create the view for this widget and attach any event listeners to it
private void addView(final AppWidgetManager manager, final Context context, final int id, final Bundle options) {
final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.search_widget);
addClickIntent(context, views, R.id.search_button, ACTION_LAUNCH_SEARCH);
addClickIntent(context, views, R.id.new_tab_button, ACTION_LAUNCH_BROWSER);
// Clicking the logo also launches the browser
addClickIntent(context, views, R.id.logo_button, ACTION_LAUNCH_BROWSER);
manager.updateAppWidget(id, views);
}
// Utility for adding a pending intent to be fired when a View is clicked.
private void addClickIntent(final Context context, final RemoteViews views, final int viewId, final String action) {
final Intent intent = new Intent(context, SearchWidget.class);
intent.setAction(action);
intent.setData(Uri.parse("about:home"));
final PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, 0);
views.setOnClickPendingIntent(viewId, pendingIntent);
}
// Utility for building an intent to be redispatched (i.e. to launch the browser or the search intent).
private Intent buildRedirectIntent(final String action, final String pkg, final String className, final Intent source) {
final Intent activity = new Intent(action);
if (pkg != null && className != null) {
activity.setClassName(pkg, className);
}
activity.setData(source.getData());
activity.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
return activity;
}
}

View File

@ -22,6 +22,7 @@ import android.widget.ListView;
import org.mozilla.search.AcceptsSearchQuery;
import org.mozilla.search.AcceptsSearchQuery.SuggestionAnimation;
import org.mozilla.search.Constants;
import org.mozilla.search.R;
import java.util.ArrayList;
@ -41,9 +42,6 @@ public class SearchFragment extends Fragment implements AcceptsJumpTaps {
// Timeout for the suggestion client to respond
private static final int SUGGESTION_TIMEOUT = 3000;
// Maximum number of results returned by the suggestion client
private static final int SUGGESTION_MAX = 5;
// Color of search term match in search suggestion
private static final int SUGGESTION_HIGHLIGHT_COLOR = 0xFF999999;
@ -80,9 +78,9 @@ public class SearchFragment extends Fragment implements AcceptsJumpTaps {
// TODO: Don't hard-code this template string (bug 1039758)
final String template = "https://search.yahoo.com/sugg/ff?" +
"output=fxjson&appid=ffm&command=__searchTerms__&nresults=" + SUGGESTION_MAX;
"output=fxjson&appid=ffm&command=__searchTerms__&nresults=" + Constants.SUGGESTION_MAX;
suggestClient = new SuggestClient(activity, template, SUGGESTION_TIMEOUT, SUGGESTION_MAX);
suggestClient = new SuggestClient(activity, template, SUGGESTION_TIMEOUT, Constants.SUGGESTION_MAX);
suggestionLoaderCallbacks = new SuggestionLoaderCallbacks();
autoCompleteAdapter = new AutoCompleteAdapter(activity, this);

View File

@ -13,8 +13,28 @@
<category android:name="android.intent.category.DEFAULT"/>
</intent-filter>
</activity>
<!-- Basic launcher widget. -->
<receiver android:name="org.mozilla.search.SearchWidget"
android:label="@string/search_widget_name">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<intent-filter>
<action android:name="org.mozilla.widget.LAUNCH_BROWSER"/>
</intent-filter>
<intent-filter>
<action android:name="org.mozilla.widget.LAUNCH_SEARCH"/>
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/search_widget_info" />
</receiver>
<activity
android:name="org.mozilla.search.SearchPreferenceActivity"
android:label="@string/search_pref_title"

Binary file not shown.

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 140 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 144 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@ -2,24 +2,9 @@
- 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/. -->
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape>
<padding
android:bottom="@dimen/card_background_padding_y"
android:top="@dimen/card_background_padding_y"
android:left="@dimen/card_background_padding_x"
android:right="@dimen/card_background_padding_x"/>
<solid android:color="@android:color/transparent"/>
</shape>
</item>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Background -->
<item>
<shape>
<solid android:color="@color/card_background"/>
<corners android:radius="5dp"/>
<stroke android:width="1dp" android:color="@color/card_border" />
</shape>
</item>
</layer-list>
<item android:state_pressed="true" android:drawable="@drawable/search_card_pressed" />
<item android:drawable="@drawable/search_card_default"/>
</selector>

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<!-- Drawable used for buttons in the launch widget -->
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:constantSize="true">
<item android:state_pressed="true"
android:drawable="@drawable/widget_active_bg"/>
<item android:drawable="@drawable/widget_bg"/>
</selector>

View File

@ -26,6 +26,7 @@
android:background="@color/global_background_color"
android:divider="@null"
android:dividerHeight="0dp"
android:listSelector="@android:color/transparent"
android:visibility="gone"/>
</LinearLayout>

View File

@ -12,7 +12,8 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="@null"
android:dividerHeight="0dp"/>
android:dividerHeight="0dp"
android:listSelector="@android:color/transparent"/>
<ImageButton
android:id="@+id/settings_button"

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<!-- A homescreen widget for launching Fennec or the search activity. We can't use styles in here
so make sure any changes you make are also made to launch_widget.xml which doesn't have
the search widget button. -->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/widget_header_height">
<!-- Wrap in a linear layout to center the text in a flexible space. We use a negative margin
to extend this into the firefox logo so that the button background appears to come from behind the logo, but
highlights correctly when tapped. -->
<LinearLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_toLeftOf="@+id/logo_button"
android:layout_marginRight="@dimen/widget_button_offset"
android:paddingRight="@dimen/widget_button_padding"
android:gravity="center"
android:background="@drawable/search_widget_button"
android:orientation="horizontal"
android:id="@+id/search_button">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_url_bar_search"
android:drawablePadding="@dimen/widget_drawable_padding"
android:text="@string/search_widget_button_label"
android:contentDescription="@string/search_widget_button_label"
android:gravity="center"
android:textSize="@dimen/widget_text_size"
android:textColor="@color/text_color_primary"
android:id="@+id/search_button_label"/>
</LinearLayout>
<LinearLayout android:layout_width="match_parent"
android:layout_toRightOf="@+id/logo_button"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/widget_button_offset"
android:paddingLeft="@dimen/widget_button_padding"
android:layout_centerVertical="true"
android:gravity="center"
android:background="@drawable/search_widget_button"
android:orientation="horizontal"
android:id="@+id/new_tab_button">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_widget_new_tab"
android:drawablePadding="@dimen/widget_drawable_padding"
android:gravity="center"
android:text="@string/new_tab"
android:contentDescription="@string/new_tab"
android:textSize="@dimen/widget_text_size"
android:textColor="@color/text_color_primary"
android:id="@+id/new_tab_button_label"/>
</LinearLayout>
<!-- The logo. adjustViewBounds is required for the buttons above to stretch underneath the logo. -->
<ImageView android:id="@+id/logo_button"
android:layout_centerInParent="true"
android:adjustViewBounds="true"
android:layout_width="@dimen/widget_header_height"
android:layout_height="match_parent"
android:src="@drawable/icon"/>
</RelativeLayout>

View File

@ -10,6 +10,7 @@
<!-- card colors -->
<color name="card_background">#ffffff</color>
<color name="card_background_pressed">#DCDCE1</color>
<color name="card_border">#BFBFBF</color>
<!-- Search suggestion highlight color is defined in SearchFragment.java -->

View File

@ -23,4 +23,11 @@
<dimen name="card_padding_x">38dp</dimen>
<dimen name="card_padding_y">23dp</dimen>
<!-- Widget Buttons -->
<dimen name="widget_header_height">70dp</dimen>
<dimen name="widget_button_offset">-50dp</dimen>
<dimen name="widget_button_padding">45dp</dimen>
<dimen name="widget_text_size">15sp</dimen>
<dimen name="widget_drawable_padding">2dp</dimen>
</resources>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- 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/. -->
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="300dp"
android:minHeight="40dp"
android:label="@string/search_widget_name"
android:widgetCategory="home_screen"
android:previewImage="@drawable/launcher_widget"
android:initialLayout="@layout/search_widget"/>

View File

@ -16,4 +16,5 @@ search_activity_sources = [
'java/org/mozilla/search/PostSearchFragment.java',
'java/org/mozilla/search/PreSearchFragment.java',
'java/org/mozilla/search/SearchPreferenceActivity.java',
'java/org/mozilla/search/SearchWidget.java',
]

View File

@ -4,7 +4,7 @@
<!ENTITY search_jump_arrow '&#8598;'>
<!ENTITY search_app_name 'Firefox Search'>
<!ENTITY search_app_name '&brandShortName; Search'>
<!ENTITY search_for_something 'Search for something'>
<!ENTITY search_pref_title 'Settings'>
@ -12,3 +12,6 @@
<!ENTITY search_pref_clear_history_dialog_message 'Delete all search history from this device?'>
<!ENTITY search_pref_clear_history_title 'Clear search history'>
<!ENTITY search_pref_button_content_description 'Settings'>
<!ENTITY search_widget_button_label 'Search'>

View File

@ -8,3 +8,6 @@
<string name="search_pref_clear_history_dialog_message">&search_pref_clear_history_dialog_message;</string>
<string name="search_pref_clear_history_title">&search_pref_clear_history_title;</string>
<string name="search_pref_button_content_description">&search_pref_button_content_description;</string>
<string name="search_widget_name">&search_app_name;</string>
<string name="search_widget_button_label">&search_widget_button_label;</string>

View File

@ -4,9 +4,37 @@
"use strict";
const {Cc, Ci, Cu} = require("chrome");
const { Cc, Ci, Cu } = require("chrome");
let protocol = require("devtools/server/protocol");
let {method, RetVal} = protocol;
let { method, RetVal } = protocol;
const { reportException } = require("devtools/toolkit/DevToolsUtils");
/**
* A method decorator that ensures the actor is in the expected state before
* proceeding. If the actor is not in the expected state, the decorated method
* returns a rejected promise.
*
* @param String expectedState
* The expected state.
*
* @param Function method
* The actor method to proceed with when the actor is in the expected
* state.
*
* @returns Function
* The decorated method.
*/
function expectState(expectedState, method) {
return function(...args) {
if (this.state !== expectedState) {
const msg = "Wrong State: Expected '" + expectedState + "', but current "
+ "state is '" + this.state + "'";
return Promise.reject(new Error(msg));
}
return method.apply(this, args);
};
}
/**
* An actor that returns memory usage data for its parent actor's window.
@ -17,18 +45,59 @@ let {method, RetVal} = protocol;
let MemoryActor = protocol.ActorClass({
typeName: "memory",
initialize: function(conn, tabActor) {
get dbg() {
if (!this._dbg) {
this._dbg = this.parent.makeDebugger();
}
return this._dbg;
},
initialize: function(conn, parent) {
protocol.Actor.prototype.initialize.call(this, conn);
this.tabActor = tabActor;
this.parent = parent;
this._mgr = Cc["@mozilla.org/memory-reporter-manager;1"]
.getService(Ci.nsIMemoryReporterManager);
this.state = "detached";
this._dbg = null;
},
destroy: function() {
this._mgr = null;
if (this.state === "attached") {
this.detach();
}
protocol.Actor.prototype.destroy.call(this);
},
/**
* Attach to this MemoryActor.
*/
attach: method(expectState("detached", function() {
this.dbg.addDebuggees();
this.dbg.enabled = true;
this.state = "attached";
}), {
request: {},
response: {
type: "attached"
}
}),
/**
* Detach from this MemoryActor.
*/
detach: method(expectState("attached", function() {
this.dbg.removeAllDebuggees();
this.dbg.enabled = false;
this._dbg = null;
this.state = "detached";
}), {
request: {},
response: {
type: "detached"
}
}),
/**
* A method that returns a detailed breakdown of the memory consumption of the
* associated window.
@ -49,7 +118,7 @@ let MemoryActor = protocol.ActorClass({
let nonJSMilliseconds = {};
try {
this._mgr.sizeOfTab(this.tabActor.window, jsObjectsSize, jsStringsSize, jsOtherSize,
this._mgr.sizeOfTab(this.parent.window, jsObjectsSize, jsStringsSize, jsOtherSize,
domSize, styleSize, otherSize, totalSize, jsMilliseconds, nonJSMilliseconds);
result.total = totalSize.value;
result.domSize = domSize.value;
@ -61,9 +130,7 @@ let MemoryActor = protocol.ActorClass({
result.jsMilliseconds = jsMilliseconds.value.toFixed(1);
result.nonJSMilliseconds = nonJSMilliseconds.value.toFixed(1);
} catch (e) {
console.error(e);
let url = this.tabActor.url;
console.error("Error getting size of " + url);
reportException("MemoryActor.prototype.measure", e);
}
return result;

View File

@ -7,6 +7,7 @@ support-files =
nonchrome_unsafeDereference.html
inspector_getImageData.html
large-image.jpg
memory-helpers.js
small-image.gif
Debugger.Source.prototype.element.js
Debugger.Source.prototype.element-2.js
@ -66,6 +67,8 @@ skip-if = buildapp == 'mulet'
[test_inspector_getImageData.html]
skip-if = buildapp == 'mulet'
[test_memory.html]
[test_memory_attach_01.html]
[test_memory_attach_02.html]
[test_preference.html]
[test_connectToChild.html]
skip-if = buildapp == 'mulet'

View File

@ -0,0 +1,54 @@
let Cu = Components.utils;
let Cc = Components.classes;
let Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm");
// Always log packets when running tests.
Services.prefs.setBoolPref("devtools.debugger.log", true);
SimpleTest.registerCleanupFunction(function() {
Services.prefs.clearUserPref("devtools.debugger.log");
});
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/devtools/Loader.jsm");
let { require } = devtools;
let { MemoryFront } = require("devtools/server/actors/memory");
function startServerAndGetSelectedTabMemory() {
DebuggerServer.init(() => true);
DebuggerServer.addBrowserActors();
var client = new DebuggerClient(DebuggerServer.connectPipe());
return new Promise((resolve, reject) => {
client.connect(response => {
if (response.error) {
reject(new Error(response.error + ": " + response.message));
return;
}
client.listTabs(response => {
if (response.error) {
reject(new Error(response.error + ": " + response.message));
return;
}
var form = response.tabs[response.selected];
var memory = MemoryFront(client, form);
resolve({ memory, client });
});
});
});
}
function destroyServerAndFinish(client) {
client.close(() => {
DebuggerServer.destroy();
SimpleTest.finish()
});
}

View File

@ -11,59 +11,26 @@ Bug 923275 - Add a memory monitor widget to the developer toolbar
</head>
<body>
<pre id="test">
<script src="memory-helpers.js" type="application/javascript;version=1.8"></script>
<script>
window.onload = function() {
var Cu = Components.utils;
var Cc = Components.classes;
var Ci = Components.interfaces;
Cu.import("resource://gre/modules/Services.jsm");
// Always log packets when running tests.
Services.prefs.setBoolPref("devtools.debugger.log", true);
SimpleTest.registerCleanupFunction(function() {
Services.prefs.clearUserPref("devtools.debugger.log");
});
Cu.import("resource://gre/modules/devtools/Loader.jsm");
Cu.import("resource://gre/modules/devtools/dbg-client.jsm");
Cu.import("resource://gre/modules/devtools/dbg-server.jsm");
SimpleTest.waitForExplicitFinish();
var {MemoryFront} = devtools.require("devtools/server/actors/memory");
DebuggerServer.init(function () { return true; });
DebuggerServer.addBrowserActors();
var client = new DebuggerClient(DebuggerServer.connectPipe());
client.connect(function onConnect() {
client.listTabs(function onListTabs(aResponse) {
var form = aResponse.tabs[aResponse.selected];
var front = MemoryFront(client, form);
front.measure().then(measurement => {
ok(measurement.total > 0, "total memory is valid");
ok(measurement.domSize > 0, "domSize is valid");
ok(measurement.styleSize > 0, "styleSize is valid");
ok(measurement.jsObjectsSize > 0, "jsObjectsSize is valid");
ok(measurement.jsStringsSize > 0, "jsStringsSize is valid");
ok(measurement.jsOtherSize > 0, "jsOtherSize is valid");
ok(measurement.otherSize > 0, "otherSize is valid");
ok(measurement.jsMilliseconds, "jsMilliseconds is valid");
ok(measurement.nonJSMilliseconds, "nonJSMilliseconds is valid");
client.close(() => {
DebuggerServer.destroy();
SimpleTest.finish()
});
});
});
Task.spawn(function* () {
var { memory, client } = yield startServerAndGetSelectedTabMemory();
var measurement = yield memory.measure();
ok(measurement.total > 0, "total memory is valid");
ok(measurement.domSize > 0, "domSize is valid");
ok(measurement.styleSize > 0, "styleSize is valid");
ok(measurement.jsObjectsSize > 0, "jsObjectsSize is valid");
ok(measurement.jsStringsSize > 0, "jsStringsSize is valid");
ok(measurement.jsOtherSize > 0, "jsOtherSize is valid");
ok(measurement.otherSize > 0, "otherSize is valid");
ok(measurement.jsMilliseconds, "jsMilliseconds is valid");
ok(measurement.nonJSMilliseconds, "nonJSMilliseconds is valid");
destroyServerAndFinish(client);
});
}
};
</script>
</pre>
</body>

View File

@ -0,0 +1,31 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 960671 - Test attaching and detaching from a memory actor.
-->
<head>
<meta charset="utf-8">
<title>Memory monitoring actor test</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<pre id="test">
<script src="memory-helpers.js" type="application/javascript;version=1.8"></script>
<script>
window.onload = function() {
SimpleTest.waitForExplicitFinish();
Task.spawn(function* () {
var { memory, client } = yield startServerAndGetSelectedTabMemory();
yield memory.attach();
ok(true, "Shouldn't have gotten an error attaching.");
yield memory.detach();
ok(true, "Shouldn't have gotten an error detaching.");
destroyServerAndFinish(client);
});
};
</script>
</pre>
</body>
</html>

View File

@ -0,0 +1,49 @@
<!DOCTYPE HTML>
<html>
<!--
Bug 960671 - Test attaching and detaching while in the wrong state.
-->
<head>
<meta charset="utf-8">
<title>Memory monitoring actor test</title>
<script type="application/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body>
<pre id="test">
<script src="memory-helpers.js" type="application/javascript;version=1.8"></script>
<script>
window.onload = function() {
SimpleTest.waitForExplicitFinish();
Task.spawn(function* () {
var { memory, client } = yield startServerAndGetSelectedTabMemory();
var e = null;
try {
yield memory.detach();
}
catch (ee) {
e = ee;
}
ok(e, "Should have hit the wrongState error");
yield memory.attach();
e = null;
try {
yield memory.attach();
}
catch (ee) {
e = ee;
}
ok(e, "Should have hit the wrongState error");
yield memory.detach();
destroyServerAndFinish(client);
});
};
</script>
</pre>
</body>
</html>

View File

@ -1001,7 +1001,7 @@ AndroidBridge::InitCamera(const nsCString& contentType, uint32_t camera, uint32_
AutoLocalJNIFrame jniFrame(env, 1);
jintArray arr = mozilla::widget::android::GeckoAppShell::InitCameraWrapper
(NS_ConvertUTF8toUTF16(contentType), (int32_t) camera, (int32_t) width, (int32_t) height);
(NS_ConvertUTF8toUTF16(contentType), (int32_t) camera, (int32_t) *width, (int32_t) *height);
if (!arr)
return false;