Bug 1321418 - Use GekcoBundle events in GeckoApp/BrowserApp; r=snorp r=sebastian r=gbrown

Bug 1321418 - 1. Use GekcoBundle events in GeckoApp; r=snorp r=sebastian

Switch GeckoApp to using GeckoBundle events everywhere. UI or Gecko
events are used if the event requires the UI or Gecko thread,
respectively, and background events are used for all other events.

There are changes to some other Java classes, such as SnackbarBuilder
and GeckoAccessibility, due to the switch to GeckoBundle.

For "Snackbar:Show", we need the global EventDispatcher because the
event can be sent to both GeckoApp and GeckoPreferences. Howveer, we
only want one listener registered at the same time, so we register and
unregister in GeckoApp's and GeckoPreferences's onPause and onResume
methods.

Bug 1321418 - 2. Use appropriate JS EventDispatcher to send GeckoApp events; r=snorp r=sebastian

Change JS code that sends events to GeckoApp to use either the global
EventDispatcher or the per-window EventDispatcher.

"Session:StatePurged" is not used so it's removed. "Gecko:Ready" in
geckoview.js is not necessary because it's only used for GeckoApp, so
it's removed from geckoview.js.

Bug 1321418 - 3. Use GeckoBundle events in BrowserApp; r=snorp r=sebastian

Switch BrowserApp to using GeckoBundle events, in a similar vein as
GeckoApp. UI or Gecko events are used if the event handlers required UI
or Gecko thread, respectively, and background events are used for all
other events.

Some other Java classes also have to be modified as a result of
switching to GeckoBundle.

Bug 1321418 - 4. Use global EventDispatcher to send BrowserApp events; r=snorp r=sebastian

Change JS code that sends events to BrowserApp to use the global
EventDispatcher instead of "Messaging".

Bug 1321418 - 5. Update usages of events in tests; r=gbrown

Update cases where we use or wait for events in tests.
This commit is contained in:
Jim Chen 2016-12-09 12:32:45 -05:00
parent 87a621d48b
commit e9532b9a3f
40 changed files with 730 additions and 851 deletions

View File

@ -33,8 +33,7 @@ this.AccessFu = { // jshint ignore:line
Utils.init(aWindow);
try {
Services.androidBridge.handleGeckoMessage(
{ type: 'Accessibility:Ready' });
Services.androidBridge.dispatch('Accessibility:Ready');
Services.obs.addObserver(this, 'Accessibility:Settings', false);
} catch (x) {
// Not on Android
@ -602,7 +601,6 @@ var Output = {
}
for (let androidEvent of aDetails) {
androidEvent.type = 'Accessibility:Event';
if (androidEvent.bounds) {
androidEvent.bounds = AccessFu.adjustContentBounds(
androidEvent.bounds, aBrowser);
@ -622,7 +620,9 @@ var Output = {
androidEvent.brailleOutput);
break;
}
this.androidBridge.handleGeckoMessage(androidEvent);
let win = Utils.win;
let view = win && win.QueryInterface(Ci.nsIAndroidView);
view.dispatch('Accessibility:Event', androidEvent);
}
},
@ -818,8 +818,9 @@ var Input = {
if (Utils.MozBuildApp == 'mobile/android') {
// Return focus to native Android browser chrome.
Services.androidBridge.handleGeckoMessage(
{ type: 'ToggleChrome:Focus' });
let win = Utils.win;
let view = win && win.QueryInterface(Ci.nsIAndroidView);
view.dispatch('ToggleChrome:Focus');
}
break;
case aEvent.DOM_VK_RETURN:

View File

@ -103,12 +103,9 @@ import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.FloatUtils;
import org.mozilla.gecko.util.GamepadUtils;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.IntentUtils;
import org.mozilla.gecko.util.MenuUtils;
import org.mozilla.gecko.util.NativeEventListener;
import org.mozilla.gecko.util.NativeJSObject;
import org.mozilla.gecko.util.PrefUtils;
import org.mozilla.gecko.util.StringUtils;
import org.mozilla.gecko.util.ThreadUtils;
@ -202,8 +199,7 @@ public class BrowserApp extends GeckoApp
OnUrlOpenInBackgroundListener,
AnchoredPopup.OnVisibilityChangeListener,
ActionModeCompat.Presenter,
LayoutInflater.Factory,
BundleEventListener {
LayoutInflater.Factory {
private static final String LOGTAG = "GeckoBrowserApp";
private static final int TABS_ANIMATION_DURATION = 450;
@ -726,32 +722,36 @@ public class BrowserApp extends GeckoApp
mDoorhangerOverlay = findViewById(R.id.doorhanger_overlay);
EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener)this,
"Gecko:DelayedStartup",
EventDispatcher.getInstance().registerGeckoThreadListener(this,
"Search:Keyword",
"Favicon:CacheLoad",
null);
EventDispatcher.getInstance().registerUiThreadListener(this,
"Menu:Open",
"Menu:Update",
"Menu:Add",
"Menu:Remove",
"LightweightTheme:Update",
"Search:Keyword",
"Tab:Added",
"Video:Play");
EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener)this,
"Video:Play",
"CharEncoding:Data",
"CharEncoding:State",
"Download:AndroidDownloadManager",
"Settings:Show",
"Updater:Launch",
null);
EventDispatcher.getInstance().registerBackgroundThreadListener(this,
"Experiments:GetActive",
"Experiments:SetOverride",
"Experiments:ClearOverride",
"Favicon:CacheLoad",
"Feedback:MaybeLater",
"Menu:Add",
"Menu:Remove",
"Sanitize:ClearHistory",
"Sanitize:ClearSyncedTabs",
"Settings:Show",
"Telemetry:Gather",
"Updater:Launch",
"Website:Metadata");
"Download:AndroidDownloadManager",
"Website:Metadata",
null);
getAppEventDispatcher().registerUiThreadListener(this, "Prompt:ShowTop");
@ -1430,32 +1430,36 @@ public class BrowserApp extends GeckoApp
mSearchEngineManager.unregisterListeners();
EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener) this,
"Gecko:DelayedStartup",
EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
"Search:Keyword",
"Favicon:CacheLoad",
null);
EventDispatcher.getInstance().unregisterUiThreadListener(this,
"Menu:Open",
"Menu:Update",
"Menu:Add",
"Menu:Remove",
"LightweightTheme:Update",
"Search:Keyword",
"Tab:Added",
"Video:Play");
EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener) this,
"Video:Play",
"CharEncoding:Data",
"CharEncoding:State",
"Download:AndroidDownloadManager",
"Settings:Show",
"Updater:Launch",
null);
EventDispatcher.getInstance().unregisterBackgroundThreadListener(this,
"Experiments:GetActive",
"Experiments:SetOverride",
"Experiments:ClearOverride",
"Favicon:CacheLoad",
"Feedback:MaybeLater",
"Menu:Add",
"Menu:Remove",
"Sanitize:ClearHistory",
"Sanitize:ClearSyncedTabs",
"Settings:Show",
"Telemetry:Gather",
"Updater:Launch",
"Website:Metadata");
"Download:AndroidDownloadManager",
"Website:Metadata",
null);
getAppEventDispatcher().unregisterUiThreadListener(this, "Prompt:ShowTop");
@ -1548,25 +1552,6 @@ public class BrowserApp extends GeckoApp
alphaAnimator.start();
}
private void handleClearHistory(final boolean clearSearchHistory) {
final BrowserDB db = BrowserDB.from(getProfile());
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
db.clearHistory(getContentResolver(), clearSearchHistory);
}
});
}
private void handleClearSyncedTabs() {
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
FennecTabsRepository.deleteNonLocalClientsAndTabs(getContext());
}
});
}
private void setToolbarMargin(int margin) {
((RelativeLayout.LayoutParams) mGeckoLayout.getLayoutParams()).topMargin = margin;
mGeckoLayout.requestLayout();
@ -1663,29 +1648,21 @@ public class BrowserApp extends GeckoApp
@Override
void toggleChrome(final boolean aShow) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
if (aShow) {
mBrowserChrome.setVisibility(View.VISIBLE);
} else {
mBrowserChrome.setVisibility(View.GONE);
}
}
});
if (aShow) {
mBrowserChrome.setVisibility(View.VISIBLE);
} else {
mBrowserChrome.setVisibility(View.GONE);
}
super.toggleChrome(aShow);
}
@Override
void focusChrome() {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
mBrowserChrome.setVisibility(View.VISIBLE);
mActionBarFlipper.requestFocusFromTouch();
}
});
mBrowserChrome.setVisibility(View.VISIBLE);
mActionBarFlipper.requestFocusFromTouch();
super.focusChrome();
}
@Override
@ -1707,6 +1684,124 @@ public class BrowserApp extends GeckoApp
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
switch (event) {
case "Gecko:Ready":
EventDispatcher.getInstance().registerUiThreadListener(this, "Gecko:DelayedStartup");
// Handle this message in GeckoApp, but also enable the Settings
// menuitem, which is specific to BrowserApp.
super.handleMessage(event, message, callback);
final Menu menu = mMenu;
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
if (menu != null) {
menu.findItem(R.id.settings).setEnabled(true);
menu.findItem(R.id.help).setEnabled(true);
}
}
});
// Display notification for Mozilla data reporting, if data should be collected.
if (AppConstants.MOZ_DATA_REPORTING &&
Restrictions.isAllowed(this, Restrictable.DATA_CHOICES)) {
DataReportingNotification.checkAndNotifyPolicy(GeckoAppShell.getContext());
}
break;
case "Gecko:DelayedStartup":
EventDispatcher.getInstance().unregisterUiThreadListener(this, "Gecko:DelayedStartup");
// Force tabs panel inflation once the initial pageload is finished.
ensureTabsPanelExists();
if (AppConstants.NIGHTLY_BUILD && mZoomedView == null) {
ViewStub stub = (ViewStub) findViewById(R.id.zoomed_view_stub);
mZoomedView = (ZoomedView) stub.inflate();
}
if (AppConstants.MOZ_MEDIA_PLAYER) {
// Check if the fragment is already added. This should never be true
// here, but this is a nice safety check. If casting is disabled,
// these classes aren't built. We use reflection to initialize them.
final Class<?> mediaManagerClass = getMediaPlayerManager();
if (mediaManagerClass != null) {
try {
final String tag = "";
mediaManagerClass.getDeclaredField("MEDIA_PLAYER_TAG").get(tag);
Log.i(LOGTAG, "Found tag " + tag);
final Fragment frag = getSupportFragmentManager().findFragmentByTag(tag);
if (frag == null) {
final Method getInstance = mediaManagerClass.getMethod(
"getInstance", (Class[]) null);
final Fragment mpm = (Fragment) getInstance.invoke(null);
getSupportFragmentManager().beginTransaction()
.disallowAddToBackStack().add(mpm, tag).commit();
}
} catch (Exception ex) {
Log.e(LOGTAG, "Error initializing media manager", ex);
}
}
}
if (AppConstants.MOZ_STUMBLER_BUILD_TIME_ENABLED &&
Restrictions.isAllowed(this, Restrictable.DATA_CHOICES)) {
// Start (this acts as ping if started already) the stumbler lib; if
// the stumbler has queued data it will upload it. Stumbler operates
// on its own thread, and startup impact is further minimized by
// delaying work (such as upload) a few seconds. Avoid any potential
// startup CPU/thread contention by delaying the pref broadcast.
GeckoPreferences.broadcastStumblerPref(BrowserApp.this);
}
if (AppConstants.MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE) {
// TODO: Better scheduling of sync action (Bug 1257492)
DownloadContentService.startSync(this);
DownloadContentService.startVerification(this);
}
FeedService.setup(this);
break;
case "Menu:Open":
if (mBrowserToolbar.isEditing()) {
mBrowserToolbar.cancelEdit();
}
openOptionsMenu();
break;
case "Menu:Update":
updateAddonMenuItem(message.getInt("id"), message.getBundle("options"));
break;
case "Menu:Add":
final MenuItemInfo info = new MenuItemInfo();
info.label = message.getString("name");
info.id = message.getInt("id") + ADDON_MENU_OFFSET;
info.checked = message.getBoolean("checked", false);
info.enabled = message.getBoolean("enabled", true);
info.visible = message.getBoolean("visible", true);
info.checkable = message.getBoolean("checkable", false);
final int parent = message.getInt("parent", 0);
info.parent = parent <= 0 ? parent : parent + ADDON_MENU_OFFSET;
addAddonMenuItem(info);
break;
case "Menu:Remove":
removeAddonMenuItem(message.getInt("id") + ADDON_MENU_OFFSET);
break;
case "LightweightTheme:Update":
mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
break;
case "Search:Keyword":
storeSearchQuery(message.getString("query"));
recordSearch(GeckoSharedPrefs.forProfile(this), message.getString("identifier"),
TelemetryContract.Method.ACTIONBAR);
break;
case "Prompt:ShowTop":
// Bring this activity to front so the prompt is visible..
Intent bringToFrontIntent = new Intent();
@ -1715,21 +1810,32 @@ public class BrowserApp extends GeckoApp
bringToFrontIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(bringToFrontIntent);
break;
}
}
@Override
public void handleMessage(final String event, final NativeJSObject message,
final EventCallback callback) {
switch (event) {
case "Tab:Added":
if (message.getBoolean("cancelEditMode")) {
// Set the target tab to null so it does not get selected (on editing
// mode exit) in lieu of the tab that we're going to open and select.
mTargetTabForEditingMode = null;
mBrowserToolbar.cancelEdit();
}
break;
case "Video:Play":
if (SwitchBoard.isInExperiment(this, Experiments.HLS_VIDEO_PLAYBACK)) {
mVideoPlayer.start(Uri.parse(message.getString("uri")));
Telemetry.sendUIEvent(TelemetryContract.Event.SHOW,
TelemetryContract.Method.CONTENT, "playhls");
}
break;
case "CharEncoding:Data":
final NativeJSObject[] charsets = message.getObjectArray("charsets");
final GeckoBundle[] charsets = message.getBundleArray("charsets");
final int selected = message.getInt("selected");
final String[] titleArray = new String[charsets.length];
final String[] codeArray = new String[charsets.length];
for (int i = 0; i < charsets.length; i++) {
final NativeJSObject charset = charsets[i];
final GeckoBundle charset = charsets[i];
titleArray[i] = charset.getString("title");
codeArray[i] = charset.getString("code");
}
@ -1750,26 +1856,15 @@ public class BrowserApp extends GeckoApp
dialog.dismiss();
}
});
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
dialogBuilder.show();
}
});
dialogBuilder.show();
break;
case "CharEncoding:State":
final boolean visible = message.getString("visible").equals("true");
final boolean visible = "true".equals(message.getString("visible"));
GeckoPreferences.setCharEncodingState(visible);
final Menu menu = mMenu;
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
if (menu != null) {
menu.findItem(R.id.char_encoding).setVisible(visible);
}
}
});
if (mMenu != null) {
mMenu.findItem(R.id.char_encoding).setVisible(visible);
}
break;
case "Experiments:GetActive":
@ -1779,7 +1874,8 @@ public class BrowserApp extends GeckoApp
break;
case "Experiments:SetOverride":
Experiments.setOverride(getContext(), message.getString("name"), message.getBoolean("isEnabled"));
Experiments.setOverride(getContext(), message.getString("name"),
message.getBoolean("isEnabled"));
break;
case "Experiments:ClearOverride":
@ -1792,52 +1888,25 @@ public class BrowserApp extends GeckoApp
break;
case "Feedback:MaybeLater":
resetFeedbackLaunchCount();
break;
case "Menu:Add":
final MenuItemInfo info = new MenuItemInfo();
info.label = message.getString("name");
info.id = message.getInt("id") + ADDON_MENU_OFFSET;
info.checked = message.optBoolean("checked", false);
info.enabled = message.optBoolean("enabled", true);
info.visible = message.optBoolean("visible", true);
info.checkable = message.optBoolean("checkable", false);
final int parent = message.optInt("parent", 0);
info.parent = parent <= 0 ? parent : parent + ADDON_MENU_OFFSET;
final MenuItemInfo menuItemInfo = info;
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
addAddonMenuItem(menuItemInfo);
}
});
break;
case "Menu:Remove":
final int id = message.getInt("id") + ADDON_MENU_OFFSET;
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
removeAddonMenuItem(id);
}
});
SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE);
settings.edit().putInt(getPackageName() + ".feedback_launch_count", 0).apply();
break;
case "Sanitize:ClearHistory":
handleClearHistory(message.optBoolean("clearSearchHistory", false));
callback.sendSuccess(true);
BrowserDB.from(getProfile()).clearHistory(
getContentResolver(), message.getBoolean("clearSearchHistory", false));
callback.sendSuccess(null);
break;
case "Sanitize:ClearSyncedTabs":
handleClearSyncedTabs();
callback.sendSuccess(true);
FennecTabsRepository.deleteNonLocalClientsAndTabs(getContext());
callback.sendSuccess(null);
break;
case "Settings:Show":
final String resource =
message.optString(GeckoPreferences.INTENT_EXTRA_RESOURCES, null);
final Intent settingsIntent = new Intent(this, GeckoPreferences.class);
final String resource = message.getString(GeckoPreferences.INTENT_EXTRA_RESOURCES);
GeckoPreferences.setResourceToOpen(settingsIntent, resource);
startActivityForResult(settingsIntent, ACTIVITY_REQUEST_PREFERENCES);
@ -1851,30 +1920,58 @@ public class BrowserApp extends GeckoApp
case "Telemetry:Gather":
final BrowserDB db = BrowserDB.from(getProfile());
final ContentResolver cr = getContentResolver();
Telemetry.addToHistogram("PLACES_PAGES_COUNT", db.getCount(cr, "history"));
Telemetry.addToHistogram("FENNEC_BOOKMARKS_COUNT", db.getCount(cr, "bookmarks"));
Telemetry.addToHistogram("BROWSER_IS_USER_DEFAULT", (isDefaultBrowser(Intent.ACTION_VIEW) ? 1 : 0));
Telemetry.addToHistogram("FENNEC_CUSTOM_HOMEPAGE", (TextUtils.isEmpty(getHomepage()) ? 0 : 1));
Telemetry.addToHistogram("BROWSER_IS_USER_DEFAULT",
(isDefaultBrowser(Intent.ACTION_VIEW) ? 1 : 0));
Telemetry.addToHistogram("FENNEC_CUSTOM_HOMEPAGE",
(TextUtils.isEmpty(getHomepage()) ? 0 : 1));
final SharedPreferences prefs = GeckoSharedPrefs.forProfile(getContext());
final boolean hasCustomHomepanels =
prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY) || prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY_OLD);
prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY) ||
prefs.contains(HomeConfigPrefsBackend.PREFS_CONFIG_KEY_OLD);
Telemetry.addToHistogram("FENNEC_HOMEPANELS_CUSTOM", hasCustomHomepanels ? 1 : 0);
Telemetry.addToHistogram("FENNEC_READER_VIEW_CACHE_SIZE",
SavedReaderViewHelper.getSavedReaderViewHelper(getContext()).getDiskSpacedUsedKB());
SavedReaderViewHelper.getSavedReaderViewHelper(getContext())
.getDiskSpacedUsedKB());
if (Versions.feature16Plus) {
Telemetry.addToHistogram("BROWSER_IS_ASSIST_DEFAULT", (isDefaultBrowser(Intent.ACTION_ASSIST) ? 1 : 0));
Telemetry.addToHistogram("BROWSER_IS_ASSIST_DEFAULT",
(isDefaultBrowser(Intent.ACTION_ASSIST) ? 1 : 0));
}
break;
case "Updater:Launch":
handleUpdaterLaunch();
/**
* Launch UI that lets the user update Firefox.
*
* This depends on the current channel: Release and Beta both direct to
* the Google Play Store. If updating is enabled, Aurora, Nightly, and
* custom builds open about:, which provides an update interface.
*
* If updating is not enabled, this simply logs an error.
*/
if (AppConstants.RELEASE_OR_BETA) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id=" + getPackageName()));
startActivity(intent);
break;
}
if (AppConstants.MOZ_UPDATER) {
Tabs.getInstance().loadUrlInTab(AboutPages.UPDATER);
break;
}
Log.w(LOGTAG, "No candidate updater found; ignoring launch request.");
break;
case "Download:AndroidDownloadManager":
// Downloading via Android's download manager
final String uri = message.getString("uri");
final String filename = message.getString("filename");
final String mimeType = message.getString("mimeType");
@ -1883,54 +1980,49 @@ public class BrowserApp extends GeckoApp
request.setMimeType(mimeType);
try {
request.setDestinationInExternalFilesDir(this, Environment.DIRECTORY_DOWNLOADS, filename);
request.setDestinationInExternalFilesDir(
this, Environment.DIRECTORY_DOWNLOADS, filename);
} catch (IllegalStateException e) {
Log.e(LOGTAG, "Cannot create download directory");
return;
break;
}
request.allowScanningByMediaScanner();
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.setNotificationVisibility(
DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
request.addRequestHeader("User-Agent", HardwareUtils.isTablet() ?
AppConstants.USER_AGENT_FENNEC_TABLET :
AppConstants.USER_AGENT_FENNEC_MOBILE);
try {
DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);
DownloadManager manager = (DownloadManager)
getSystemService(Context.DOWNLOAD_SERVICE);
manager.enqueue(request);
Log.d(LOGTAG, "Enqueued download (Download Manager)");
} catch (RuntimeException e) {
Log.e(LOGTAG, "Download failed: " + e);
}
break;
case "Website:Metadata":
final NativeJSObject metadata = message.getObject("metadata");
final String location = message.getString("location");
final boolean hasImage = message.getBoolean("hasImage");
final String metadata = message.getString("metadata");
final boolean hasImage = !TextUtils.isEmpty(metadata.optString("image_url", null));
final String metadataJSON = metadata.toString();
ThreadUtils.postToBackgroundThread(new Runnable() {
@Override
public void run() {
final ContentProviderClient contentProviderClient = getContentResolver()
.acquireContentProviderClient(BrowserContract.PageMetadata.CONTENT_URI);
if (contentProviderClient == null) {
Log.w(LOGTAG, "Failed to obtain content provider client for: " + BrowserContract.PageMetadata.CONTENT_URI);
return;
}
try {
GlobalPageMetadata.getInstance().add(
BrowserDB.from(getProfile()),
contentProviderClient,
location, hasImage, metadataJSON);
} finally {
contentProviderClient.release();
}
}
});
final ContentProviderClient contentProviderClient = getContentResolver()
.acquireContentProviderClient(BrowserContract.PageMetadata.CONTENT_URI);
if (contentProviderClient == null) {
Log.w(LOGTAG, "Failed to obtain content provider client for: " +
BrowserContract.PageMetadata.CONTENT_URI);
return;
}
try {
GlobalPageMetadata.getInstance().add(
BrowserDB.from(getProfile()),
contentProviderClient,
location, hasImage, metadata);
} finally {
contentProviderClient.release();
}
break;
@ -1994,164 +2086,6 @@ public class BrowserApp extends GeckoApp
return (TextUtils.equals(packageName, getPackageName()));
}
@Override
public void handleMessage(String event, JSONObject message) {
try {
switch (event) {
case "Menu:Open":
if (mBrowserToolbar.isEditing()) {
mBrowserToolbar.cancelEdit();
}
openOptionsMenu();
break;
case "Menu:Update":
final int id = message.getInt("id") + ADDON_MENU_OFFSET;
final JSONObject options = message.getJSONObject("options");
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
updateAddonMenuItem(id, options);
}
});
break;
case "Gecko:DelayedStartup":
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
// Force tabs panel inflation once the initial
// pageload is finished.
ensureTabsPanelExists();
if (AppConstants.NIGHTLY_BUILD && mZoomedView == null) {
ViewStub stub = (ViewStub) findViewById(R.id.zoomed_view_stub);
mZoomedView = (ZoomedView) stub.inflate();
}
}
});
if (AppConstants.MOZ_MEDIA_PLAYER) {
// Check if the fragment is already added. This should never be true here, but this is
// a nice safety check.
// If casting is disabled, these classes aren't built. We use reflection to initialize them.
final Class<?> mediaManagerClass = getMediaPlayerManager();
if (mediaManagerClass != null) {
try {
final String tag = "";
mediaManagerClass.getDeclaredField("MEDIA_PLAYER_TAG").get(tag);
Log.i(LOGTAG, "Found tag " + tag);
final Fragment frag = getSupportFragmentManager().findFragmentByTag(tag);
if (frag == null) {
final Method getInstance = mediaManagerClass.getMethod("getInstance", (Class[]) null);
final Fragment mpm = (Fragment) getInstance.invoke(null);
getSupportFragmentManager().beginTransaction().disallowAddToBackStack().add(mpm, tag).commit();
}
} catch (Exception ex) {
Log.e(LOGTAG, "Error initializing media manager", ex);
}
}
}
if (AppConstants.MOZ_STUMBLER_BUILD_TIME_ENABLED && Restrictions.isAllowed(this, Restrictable.DATA_CHOICES)) {
// Start (this acts as ping if started already) the stumbler lib; if the stumbler has queued data it will upload it.
// Stumbler operates on its own thread, and startup impact is further minimized by delaying work (such as upload) a few seconds.
// Avoid any potential startup CPU/thread contention by delaying the pref broadcast.
final long oneSecondInMillis = 1000;
ThreadUtils.getBackgroundHandler().postDelayed(new Runnable() {
@Override
public void run() {
GeckoPreferences.broadcastStumblerPref(BrowserApp.this);
}
}, oneSecondInMillis);
}
if (AppConstants.MOZ_ANDROID_DOWNLOAD_CONTENT_SERVICE) {
// TODO: Better scheduling of sync action (Bug 1257492)
DownloadContentService.startSync(this);
DownloadContentService.startVerification(this);
}
FeedService.setup(this);
super.handleMessage(event, message);
break;
case "Gecko:Ready":
// Handle this message in GeckoApp, but also enable the Settings
// menuitem, which is specific to BrowserApp.
super.handleMessage(event, message);
final Menu menu = mMenu;
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
if (menu != null) {
menu.findItem(R.id.settings).setEnabled(true);
menu.findItem(R.id.help).setEnabled(true);
}
}
});
// Display notification for Mozilla data reporting, if data should be collected.
if (AppConstants.MOZ_DATA_REPORTING && Restrictions.isAllowed(this, Restrictable.DATA_CHOICES)) {
DataReportingNotification.checkAndNotifyPolicy(GeckoAppShell.getContext());
}
break;
case "Search:Keyword":
storeSearchQuery(message.getString("query"));
recordSearch(GeckoSharedPrefs.forProfile(this), message.getString("identifier"),
TelemetryContract.Method.ACTIONBAR);
break;
case "LightweightTheme:Update":
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
mDynamicToolbar.setVisible(true, VisibilityTransition.ANIMATE);
}
});
break;
case "Video:Play":
if (SwitchBoard.isInExperiment(this, Experiments.HLS_VIDEO_PLAYBACK)) {
final String uri = message.getString("uri");
final String uuid = message.getString("uuid");
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
mVideoPlayer.start(Uri.parse(uri));
Telemetry.sendUIEvent(TelemetryContract.Event.SHOW, TelemetryContract.Method.CONTENT, "playhls");
}
});
}
break;
case "Tab:Added":
if (message.getBoolean("cancelEditMode")) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
// Set the target tab to null so it does not get selected (on editing
// mode exit) in lieu of the tab that we're going to open and select.
mTargetTabForEditingMode = null;
mBrowserToolbar.cancelEdit();
}
});
}
break;
default:
super.handleMessage(event, message);
break;
}
} catch (Exception e) {
Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
}
}
@Override
public void addTab() {
Tabs.getInstance().addTab();
@ -3248,16 +3182,16 @@ public class BrowserApp extends GeckoApp
mMenu.removeItem(id);
}
private void updateAddonMenuItem(int id, JSONObject options) {
private void updateAddonMenuItem(int id, final GeckoBundle options) {
// Set attribute for the menu item in cache, if available
if (mAddonMenuItemsCache != null && !mAddonMenuItemsCache.isEmpty()) {
for (MenuItemInfo item : mAddonMenuItemsCache) {
if (item.id == id) {
item.label = options.optString("name", item.label);
item.checkable = options.optBoolean("checkable", item.checkable);
item.checked = options.optBoolean("checked", item.checked);
item.enabled = options.optBoolean("enabled", item.enabled);
item.visible = options.optBoolean("visible", item.visible);
item.label = options.getString("name", item.label);
item.checkable = options.getBoolean("checkable", item.checkable);
item.checked = options.getBoolean("checked", item.checked);
item.enabled = options.getBoolean("enabled", item.enabled);
item.visible = options.getBoolean("visible", item.visible);
item.added = (mMenu != null);
break;
}
@ -3270,11 +3204,11 @@ public class BrowserApp extends GeckoApp
final MenuItem menuItem = mMenu.findItem(id);
if (menuItem != null) {
menuItem.setTitle(options.optString("name", menuItem.getTitle().toString()));
menuItem.setCheckable(options.optBoolean("checkable", menuItem.isCheckable()));
menuItem.setChecked(options.optBoolean("checked", menuItem.isChecked()));
menuItem.setEnabled(options.optBoolean("enabled", menuItem.isEnabled()));
menuItem.setVisible(options.optBoolean("visible", menuItem.isVisible()));
menuItem.setTitle(options.getString("name", menuItem.getTitle().toString()));
menuItem.setCheckable(options.getBoolean("checkable", menuItem.isCheckable()));
menuItem.setChecked(options.getBoolean("checked", menuItem.isChecked()));
menuItem.setEnabled(options.getBoolean("enabled", menuItem.isEnabled()));
menuItem.setVisible(options.getBoolean("visible", menuItem.isVisible()));
}
}
@ -4029,11 +3963,6 @@ public class BrowserApp extends GeckoApp
});
}
private void resetFeedbackLaunchCount() {
SharedPreferences settings = getPreferences(Activity.MODE_PRIVATE);
settings.edit().putInt(getPackageName() + ".feedback_launch_count", 0).apply();
}
// HomePager.OnUrlOpenListener
@Override
public void onUrlOpen(String url, EnumSet<OnUrlOpenListener.Flags> flags) {
@ -4147,34 +4076,6 @@ public class BrowserApp extends GeckoApp
return mReadingListHelper;
}
/**
* Launch UI that lets the user update Firefox.
*
* This depends on the current channel: Release and Beta both direct to the
* Google Play Store. If updating is enabled, Aurora, Nightly, and custom
* builds open about:, which provides an update interface.
*
* If updating is not enabled, this simply logs an error.
*
* @return true if update UI was launched.
*/
protected boolean handleUpdaterLaunch() {
if (AppConstants.RELEASE_OR_BETA) {
Intent intent = new Intent(Intent.ACTION_VIEW);
intent.setData(Uri.parse("market://details?id=" + getPackageName()));
startActivity(intent);
return true;
}
if (AppConstants.MOZ_UPDATER) {
Tabs.getInstance().loadUrlInTab(AboutPages.UPDATER);
return true;
}
Log.w(LOGTAG, "No candidate updater found; ignoring launch request.");
return false;
}
/* Implementing ActionModeCompat.Presenter */
@Override
public void startActionModeCompat(final ActionModeCompat.Callback callback) {

View File

@ -39,12 +39,12 @@ import org.mozilla.gecko.text.TextSelection;
import org.mozilla.gecko.updater.UpdateServiceHelper;
import org.mozilla.gecko.util.ActivityResultHandler;
import org.mozilla.gecko.util.ActivityUtils;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.FileUtils;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.GeckoRequest;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.NativeEventListener;
import org.mozilla.gecko.util.NativeJSObject;
import org.mozilla.gecko.util.PrefUtils;
import org.mozilla.gecko.util.ThreadUtils;
@ -130,12 +130,11 @@ import java.util.concurrent.TimeUnit;
public abstract class GeckoApp
extends GeckoActivity
implements
BundleEventListener,
ContextGetter,
GeckoAppShell.GeckoInterface,
GeckoEventListener,
GeckoMenu.Callback,
GeckoMenu.MenuPresenter,
NativeEventListener,
Tabs.OnTabsChangedListener,
ViewTreeObserver.OnGlobalLayoutListener {
@ -616,36 +615,52 @@ public abstract class GeckoApp
public boolean areTabsShown() { return false; }
@Override
public void handleMessage(final String event, final NativeJSObject message,
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
if ("Accessibility:Ready".equals(event)) {
if (event.equals("Gecko:Ready")) {
mGeckoReadyStartupTimer.stop();
geckoConnected();
// This method is already running on the background thread, so we
// know that mHealthRecorder will exist. That doesn't stop us being
// paranoid.
// This method is cheap, so don't spawn a new runnable.
final HealthRecorder rec = mHealthRecorder;
if (rec != null) {
rec.recordGeckoStartupTime(mGeckoReadyStartupTimer.getElapsed());
}
GeckoApplication.get().onDelayedStartup();
} else if (event.equals("Gecko:Exited")) {
// Gecko thread exited first; let GeckoApp die too.
doShutdown();
} else if ("Accessibility:Ready".equals(event)) {
GeckoAccessibility.updateAccessibilitySettings(this);
} else if ("Accessibility:Event".equals(event)) {
GeckoAccessibility.sendAccessibilityEvent(message);
} else if ("Bookmark:Insert".equals(event)) {
final String url = message.getString("url");
final String title = message.getString("title");
final Context context = this;
final BrowserDB db = BrowserDB.from(getProfile());
ThreadUtils.postToBackgroundThread(new Runnable() {
final boolean bookmarkAdded = db.addBookmark(
getContentResolver(), message.getString("title"), message.getString("url"));
final int resId = bookmarkAdded ? R.string.bookmark_added
: R.string.bookmark_already_added;
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
final boolean bookmarkAdded = db.addBookmark(getContentResolver(), title, url);
final int resId = bookmarkAdded ? R.string.bookmark_added : R.string.bookmark_already_added;
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
SnackbarBuilder.builder(GeckoApp.this)
.message(resId)
.duration(Snackbar.LENGTH_LONG)
.buildAndShow();
}
});
SnackbarBuilder.builder(GeckoApp.this)
.message(resId)
.duration(Snackbar.LENGTH_LONG)
.buildAndShow();
}
});
} else if ("Contact:Add".equals(event)) {
final String email = message.optString("email", null);
final String phone = message.optString("phone", null);
final String email = message.getString("email", null);
final String phone = message.getString("phone", null);
if (email != null) {
Uri contactUri = Uri.parse(email);
Intent i = new Intent(ContactsContract.Intents.SHOW_OR_CREATE_CONTACT, contactUri);
@ -685,51 +700,12 @@ public abstract class GeckoApp
setLocale(message.getString("locale"));
} else if ("Permissions:Data".equals(event)) {
final NativeJSObject[] permissions = message.getObjectArray("permissions");
final GeckoBundle[] permissions = message.getBundleArray("permissions");
showSiteSettingsDialog(permissions);
} else if ("PrivateBrowsing:Data".equals(event)) {
mPrivateBrowsingSession = message.optString("session", null);
mPrivateBrowsingSession = message.getString("session", null);
} else if ("Session:StatePurged".equals(event)) {
onStatePurged();
} else if ("Share:Text".equals(event)) {
final String text = message.getString("text");
final Tab tab = Tabs.getInstance().getSelectedTab();
String title = "";
if (tab != null) {
title = tab.getDisplayTitle();
}
IntentHelper.openUriExternal(text, "text/plain", "", "", Intent.ACTION_SEND, title, false);
// Context: Sharing via chrome list (no explicit session is active)
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST, "text");
} else if ("Snackbar:Show".equals(event)) {
SnackbarBuilder.builder(this)
.fromEvent(message)
.callback(callback)
.buildAndShow();
} else if ("SystemUI:Visibility".equals(event)) {
setSystemUiVisible(message.getBoolean("visible"));
} else if ("ToggleChrome:Focus".equals(event)) {
focusChrome();
} else if ("ToggleChrome:Hide".equals(event)) {
toggleChrome(false);
} else if ("ToggleChrome:Show".equals(event)) {
toggleChrome(true);
} else if ("Update:Check".equals(event)) {
UpdateServiceHelper.checkForUpdate(this);
} else if ("Update:Download".equals(event)) {
UpdateServiceHelper.downloadUpdate(this);
} else if ("Update:Install".equals(event)) {
UpdateServiceHelper.applyUpdate(this);
} else if ("RuntimePermissions:Prompt".equals(event)) {
String[] permissions = message.getStringArray("permissions");
if (callback == null || permissions == null) {
@ -750,54 +726,64 @@ public abstract class GeckoApp
callback.sendSuccess(true);
}
});
}
}
@Override
public void handleMessage(String event, JSONObject message) {
try {
if (event.equals("Gecko:Ready")) {
mGeckoReadyStartupTimer.stop();
geckoConnected();
// This method is already running on the background thread, so we
// know that mHealthRecorder will exist. That doesn't stop us being
// paranoid.
// This method is cheap, so don't spawn a new runnable.
final HealthRecorder rec = mHealthRecorder;
if (rec != null) {
rec.recordGeckoStartupTime(mGeckoReadyStartupTimer.getElapsed());
}
GeckoApplication.get().onDelayedStartup();
} else if (event.equals("Gecko:Exited")) {
// Gecko thread exited first; let GeckoApp die too.
doShutdown();
return;
} else if (event.equals("Accessibility:Event")) {
GeckoAccessibility.sendAccessibilityEvent(message);
} else if ("Share:Text".equals(event)) {
final String text = message.getString("text");
final Tab tab = Tabs.getInstance().getSelectedTab();
String title = "";
if (tab != null) {
title = tab.getDisplayTitle();
}
} catch (Exception e) {
Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
IntentHelper.openUriExternal(text, "text/plain", "", "", Intent.ACTION_SEND, title, false);
// Context: Sharing via chrome list (no explicit session is active)
Telemetry.sendUIEvent(TelemetryContract.Event.SHARE, TelemetryContract.Method.LIST, "text");
} else if ("Snackbar:Show".equals(event)) {
SnackbarBuilder.builder(this)
.fromEvent(message)
.callback(callback)
.buildAndShow();
} else if ("SystemUI:Visibility".equals(event)) {
if (message.getBoolean("visible", true)) {
mMainLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
} else {
mMainLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
}
} else if ("ToggleChrome:Focus".equals(event)) {
focusChrome();
} else if ("ToggleChrome:Hide".equals(event)) {
toggleChrome(false);
} else if ("ToggleChrome:Show".equals(event)) {
toggleChrome(true);
} else if ("Update:Check".equals(event)) {
UpdateServiceHelper.checkForUpdate(this);
} else if ("Update:Download".equals(event)) {
UpdateServiceHelper.downloadUpdate(this);
} else if ("Update:Install".equals(event)) {
UpdateServiceHelper.applyUpdate(this);
}
}
void onStatePurged() { }
/**
* @param permissions
* Array of JSON objects to represent site permissions.
* Example: { type: "offline-app", setting: "Store Offline Data", value: "Allow" }
*/
private void showSiteSettingsDialog(final NativeJSObject[] permissions) {
private void showSiteSettingsDialog(final GeckoBundle[] permissions) {
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
builder.setTitle(R.string.site_settings_title);
final ArrayList<HashMap<String, String>> itemList =
new ArrayList<HashMap<String, String>>();
for (final NativeJSObject permObj : permissions) {
for (final GeckoBundle permObj : permissions) {
final HashMap<String, String> map = new HashMap<String, String>();
map.put("setting", permObj.getString("setting"));
map.put("value", permObj.getString("value"));
@ -840,36 +826,29 @@ public abstract class GeckoApp
}
});
ThreadUtils.postToUiThread(new Runnable() {
AlertDialog dialog = builder.create();
dialog.show();
final ListView listView = dialog.getListView();
if (listView != null) {
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
}
final Button clearButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
clearButton.setEnabled(false);
dialog.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void run() {
AlertDialog dialog = builder.create();
dialog.show();
final ListView listView = dialog.getListView();
if (listView != null) {
listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
if (listView.getCheckedItemCount() == 0) {
clearButton.setEnabled(false);
} else {
clearButton.setEnabled(true);
}
final Button clearButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
clearButton.setEnabled(false);
dialog.getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
if (listView.getCheckedItemCount() == 0) {
clearButton.setEnabled(false);
} else {
clearButton.setEnabled(true);
}
}
});
}
});
}
/* package */ void addFullScreenPluginView(View view) {
if (mFullScreenPluginView != null) {
Log.w(LOGTAG, "Already have a fullscreen plugin view");
@ -958,6 +937,35 @@ public abstract class GeckoApp
}
}
private void showSetImageResult(final boolean success, final int message, final String path) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
if (!success) {
SnackbarBuilder.builder(GeckoApp.this)
.message(message)
.duration(Snackbar.LENGTH_LONG)
.buildAndShow();
return;
}
final Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse(path));
// Removes the image from storage once the chooser activity ends.
Intent chooser = Intent.createChooser(intent, getString(message));
ActivityResultHandler handler = new ActivityResultHandler() {
@Override
public void onActivityResult (int resultCode, Intent data) {
getContentResolver().delete(intent.getData(), null, null);
}
};
ActivityHandlerHelper.startIntentForActivity(GeckoApp.this, chooser, handler);
}
});
}
// This method starts downloading an image synchronously and displays the Chooser activity to set the image as wallpaper.
private void setImageAs(final String aSrc) {
boolean isDataURI = aSrc.startsWith("data:");
@ -990,38 +998,17 @@ public abstract class GeckoApp
File dcimDir = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES);
if (!dcimDir.mkdirs() && !dcimDir.isDirectory()) {
SnackbarBuilder.builder(this)
.message(R.string.set_image_path_fail)
.duration(Snackbar.LENGTH_LONG)
.buildAndShow();
showSetImageResult(/* success */ false, R.string.set_image_path_fail, null);
return;
}
String path = Media.insertImage(getContentResolver(), image, null, null);
if (path == null) {
SnackbarBuilder.builder(this)
.message(R.string.set_image_path_fail)
.duration(Snackbar.LENGTH_LONG)
.buildAndShow();
showSetImageResult(/* success */ false, R.string.set_image_path_fail, null);
return;
}
final Intent intent = new Intent(Intent.ACTION_ATTACH_DATA);
intent.addCategory(Intent.CATEGORY_DEFAULT);
intent.setData(Uri.parse(path));
// Removes the image from storage once the chooser activity ends.
Intent chooser = Intent.createChooser(intent, getString(R.string.set_image_chooser_title));
ActivityResultHandler handler = new ActivityResultHandler() {
@Override
public void onActivityResult (int resultCode, Intent data) {
getContentResolver().delete(intent.getData(), null, null);
}
};
ActivityHandlerHelper.startIntentForActivity(this, chooser, handler);
showSetImageResult(/* success */ true, R.string.set_image_chooser_title, path);
} else {
SnackbarBuilder.builder(this)
.message(R.string.set_image_fail)
.duration(Snackbar.LENGTH_LONG)
.buildAndShow();
showSetImageResult(/* success */ false, R.string.set_image_fail, null);
}
} catch (OutOfMemoryError ome) {
Log.e(LOGTAG, "Out of Memory when converting to byte array", ome);
@ -1193,15 +1180,12 @@ public abstract class GeckoApp
}
}
// GeckoThread has to register for "Gecko:Ready" first, so GeckoApp registers
// for events after initializing GeckoThread but before launching it.
EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener)this,
// To prevent races, register startup events before launching the Gecko thread.
EventDispatcher.getInstance().registerGeckoThreadListener(this,
"Accessibility:Ready",
"Gecko:Exited",
"Gecko:Ready",
"Gecko:Exited");
EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener)this,
"Accessibility:Ready");
null);
GeckoThread.launch();
@ -1233,30 +1217,33 @@ public abstract class GeckoApp
mMainLayout = (RelativeLayout) findViewById(R.id.main_layout);
mLayerView = (GeckoView) findViewById(R.id.layer_view);
getAppEventDispatcher().registerGeckoThreadListener((GeckoEventListener)this,
"Accessibility:Event");
getAppEventDispatcher().registerGeckoThreadListener(this,
"Accessibility:Event",
"Locale:Set",
null);
getAppEventDispatcher().registerGeckoThreadListener((NativeEventListener)this,
getAppEventDispatcher().registerBackgroundThreadListener(this,
"Bookmark:Insert",
"Image:SetAs",
null);
getAppEventDispatcher().registerUiThreadListener(this,
"Contact:Add",
"DevToolsAuth:Scan",
"DOMFullScreen:Start",
"DOMFullScreen:Stop",
"Image:SetAs",
"Locale:Set",
"Permissions:Data",
"PrivateBrowsing:Data",
"RuntimePermissions:Prompt",
"Session:StatePurged",
"Share:Text",
"Snackbar:Show",
"SystemUI:Visibility",
"ToggleChrome:Focus",
"ToggleChrome:Hide",
"ToggleChrome:Show",
"Update:Check",
"Update:Download",
"Update:Install");
"Update:Install",
null);
Tabs.getInstance().attachToContext(this, mLayerView);
@ -1683,9 +1670,9 @@ public abstract class GeckoApp
public void run() {
if (TabQueueHelper.TAB_QUEUE_ENABLED && TabQueueHelper.shouldOpenTabQueueUrls(GeckoApp.this)) {
getAppEventDispatcher().registerGeckoThreadListener(new NativeEventListener() {
getAppEventDispatcher().registerGeckoThreadListener(new BundleEventListener() {
@Override
public void handleMessage(String event, NativeJSObject message, EventCallback callback) {
public void handleMessage(String event, GeckoBundle message, EventCallback callback) {
if ("Tabs:TabsOpened".equals(event)) {
getAppEventDispatcher().unregisterGeckoThreadListener(this, "Tabs:TabsOpened");
openTabsRunnable.run();
@ -2083,6 +2070,9 @@ public abstract class GeckoApp
// After an onPause, the activity is back in the foreground.
// Undo whatever we did in onPause.
super.onResume();
EventDispatcher.getInstance().registerUiThreadListener(this, "Snackbar:Show");
if (mIsAbortingAppLaunch) {
return;
}
@ -2164,6 +2154,8 @@ public abstract class GeckoApp
@Override
public void onPause()
{
EventDispatcher.getInstance().unregisterUiThreadListener(this, "Snackbar:Show");
if (mIsAbortingAppLaunch) {
super.onPause();
return;
@ -2247,37 +2239,39 @@ public abstract class GeckoApp
return;
}
EventDispatcher.getInstance().unregisterGeckoThreadListener((GeckoEventListener)this,
EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
"Accessibility:Ready",
"Gecko:Exited",
"Gecko:Ready",
"Gecko:Exited");
null);
EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener)this,
"Accessibility:Ready");
getAppEventDispatcher().unregisterGeckoThreadListener(this,
"Accessibility:Event",
"Locale:Set",
null);
getAppEventDispatcher().unregisterGeckoThreadListener((GeckoEventListener)this,
"Accessibility:Event");
getAppEventDispatcher().unregisterGeckoThreadListener((NativeEventListener)this,
getAppEventDispatcher().unregisterBackgroundThreadListener(this,
"Bookmark:Insert",
"Image:SetAs",
null);
getAppEventDispatcher().unregisterUiThreadListener(this,
"Contact:Add",
"DevToolsAuth:Scan",
"DOMFullScreen:Start",
"DOMFullScreen:Stop",
"Image:SetAs",
"Locale:Set",
"Permissions:Data",
"PrivateBrowsing:Data",
"RuntimePermissions:Prompt",
"Session:StatePurged",
"Share:Text",
"Snackbar:Show",
"SystemUI:Visibility",
"ToggleChrome:Focus",
"ToggleChrome:Hide",
"ToggleChrome:Show",
"Update:Check",
"Update:Download",
"Update:Install");
"Update:Install",
null);
if (mPromptService != null)
mPromptService.destroy();
@ -2799,19 +2793,6 @@ public abstract class GeckoApp
onLocaleChanged(resultant);
}
private void setSystemUiVisible(final boolean visible) {
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
if (visible) {
mMainLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_VISIBLE);
} else {
mMainLayout.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE);
}
}
});
}
protected HealthRecorder createHealthRecorder(final Context context,
final String profilePath,
final EventDispatcher dispatcher,

View File

@ -6,7 +6,7 @@
package org.mozilla.gecko;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.NativeJSObject;
import org.mozilla.gecko.util.GeckoBundle;
import android.app.Activity;
import android.graphics.Color;
@ -173,11 +173,11 @@ public class SnackbarBuilder {
/**
* @param object Populate the builder with data from a Gecko Snackbar:Show event.
*/
public SnackbarBuilder fromEvent(final NativeJSObject object) {
public SnackbarBuilder fromEvent(final GeckoBundle object) {
message = object.getString("message");
duration = object.getInt("duration");
if (object.has("backgroundColor")) {
if (object.containsKey("backgroundColor")) {
final String providedColor = object.getString("backgroundColor");
try {
backgroundColor = Color.parseColor(providedColor);
@ -186,9 +186,9 @@ public class SnackbarBuilder {
}
}
NativeJSObject actionObject = object.optObject("action", null);
GeckoBundle actionObject = object.getBundle("action");
if (actionObject != null) {
action = actionObject.optString("label", null);
action = actionObject.getString("label", null);
}
return this;
}

View File

@ -23,6 +23,9 @@ import org.mozilla.gecko.gfx.LayerView;
import org.mozilla.gecko.mozglue.SafeIntent;
import org.mozilla.gecko.notifications.WhatsNewReceiver;
import org.mozilla.gecko.reader.ReaderModeUtils;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.ThreadUtils;
@ -40,7 +43,7 @@ import android.provider.Browser;
import android.support.v4.content.ContextCompat;
import android.util.Log;
public class Tabs implements GeckoEventListener {
public class Tabs implements BundleEventListener, GeckoEventListener {
private static final String LOGTAG = "GeckoTabs";
// mOrder and mTabs are always of the same cardinality, and contain the same values.
@ -104,8 +107,11 @@ public class Tabs implements GeckoEventListener {
};
private Tabs() {
EventDispatcher.getInstance().registerGeckoThreadListener(this,
EventDispatcher.getInstance().registerUiThreadListener(this,
"Tab:Added",
null);
EventDispatcher.getInstance().registerGeckoThreadListener((GeckoEventListener) this,
"Tab:Close",
"Tab:Select",
"Tab:SelectAndForeground",
@ -473,50 +479,56 @@ public class Tabs implements GeckoEventListener {
return Tabs.TabsInstanceHolder.INSTANCE;
}
@Override // BundleEventListener
public synchronized void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
// "Tab:Added" is a special case because tab will be null if the tab was just added
if ("Tab:Added".equals(event)) {
int id = message.getInt("tabID");
Tab tab = getTab(id);
String url = message.getString("uri");
if (message.getBoolean("cancelEditMode")) {
final Tab oldTab = getSelectedTab();
if (oldTab != null) {
oldTab.setIsEditing(false);
}
}
if (message.getBoolean("stub")) {
if (tab == null) {
// Tab was already closed; abort
return;
}
} else {
tab = addTab(id, url, message.getBoolean("external"),
message.getInt("parentId"),
message.getString("title"),
message.getBoolean("isPrivate"),
message.getInt("tabIndex"));
// If we added the tab as a stub, we should have already
// selected it, so ignore this flag for stubbed tabs.
if (message.getBoolean("selected"))
selectTab(id);
}
if (message.getBoolean("delayLoad"))
tab.setState(Tab.STATE_DELAYED);
if (message.getBoolean("desktopMode"))
tab.setDesktopMode(true);
}
}
// GeckoEventListener implementation
@Override
public void handleMessage(String event, JSONObject message) {
public synchronized void handleMessage(String event, JSONObject message) {
Log.d(LOGTAG, "handleMessage: " + event);
try {
// All other events handled below should contain a tabID property
int id = message.getInt("tabID");
Tab tab = getTab(id);
// "Tab:Added" is a special case because tab will be null if the tab was just added
if (event.equals("Tab:Added")) {
String url = message.isNull("uri") ? null : message.getString("uri");
if (message.getBoolean("cancelEditMode")) {
final Tab oldTab = getSelectedTab();
if (oldTab != null) {
oldTab.setIsEditing(false);
}
}
if (message.getBoolean("stub")) {
if (tab == null) {
// Tab was already closed; abort
return;
}
} else {
tab = addTab(id, url, message.getBoolean("external"),
message.getInt("parentId"),
message.getString("title"),
message.getBoolean("isPrivate"),
message.getInt("tabIndex"));
// If we added the tab as a stub, we should have already
// selected it, so ignore this flag for stubbed tabs.
if (message.getBoolean("selected"))
selectTab(id);
}
if (message.getBoolean("delayLoad"))
tab.setState(Tab.STATE_DELAYED);
if (message.getBoolean("desktopMode"))
tab.setDesktopMode(true);
return;
}
// Tab was already closed; abort
if (tab == null)
return;

View File

@ -538,14 +538,7 @@ public class CombinedHistoryPanel extends HomeFragment implements RemoteClientsD
@Override
public void onClick(View widget) {
Telemetry.sendUIEvent(TelemetryContract.Event.ACTION, TelemetryContract.Method.BUTTON, "hint_private_browsing");
try {
final JSONObject json = new JSONObject();
json.put("type", "Menu:Open");
GeckoApp.getEventDispatcher().dispatchEvent(json, null);
EventDispatcher.getInstance().dispatchEvent(json, null);
} catch (JSONException e) {
Log.e(LOGTAG, "Error forming JSON for Private Browsing contextual hint", e);
}
EventDispatcher.getInstance().dispatch("Menu:Open", null);
}
};

View File

@ -15,7 +15,9 @@ import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoSharedPrefs;
import org.mozilla.gecko.gfx.BitmapUtils;
import org.mozilla.gecko.util.GeckoEventListener;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.WindowUtils;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.ThreadUtils.AssertBehavior;
@ -37,7 +39,7 @@ import android.view.Gravity;
import android.view.View;
import android.view.ViewParent;
public class LightweightTheme implements GeckoEventListener {
public class LightweightTheme implements BundleEventListener {
private static final String LOGTAG = "GeckoLightweightTheme";
private static final String PREFS_URL = "lightweightTheme.headerURL";
@ -163,7 +165,7 @@ public class LightweightTheme implements GeckoEventListener {
mListeners = new ArrayList<OnChangeListener>();
// unregister isn't needed as the lifetime is same as the application.
EventDispatcher.getInstance().registerGeckoThreadListener(this,
EventDispatcher.getInstance().registerUiThreadListener(this,
"LightweightTheme:Update",
"LightweightTheme:Disable");
@ -181,28 +183,19 @@ public class LightweightTheme implements GeckoEventListener {
}
@Override
public void handleMessage(String event, JSONObject message) {
try {
if (event.equals("LightweightTheme:Update")) {
JSONObject lightweightTheme = message.getJSONObject("data");
final String headerURL = lightweightTheme.getString("headerURL");
final String color = lightweightTheme.optString("accentcolor");
public void handleMessage(String event, GeckoBundle message, EventCallback callback) {
if (event.equals("LightweightTheme:Update")) {
GeckoBundle lightweightTheme = message.getBundle("data");
final String headerURL = lightweightTheme.getString("headerURL");
final String color = lightweightTheme.getString("accentcolor");
ThreadUtils.postToBackgroundThread(new LightweightThemeRunnable(headerURL, color));
} else if (event.equals("LightweightTheme:Disable")) {
// Clear the saved data when a theme is disabled.
// Called on the Gecko thread, but should be very lightweight.
clearPrefs();
ThreadUtils.postToBackgroundThread(new LightweightThemeRunnable(headerURL, color));
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
resetLightweightTheme();
}
});
}
} catch (Exception e) {
Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);
} else if (event.equals("LightweightTheme:Disable")) {
// Clear the saved data when a theme is disabled.
// Called on the Gecko thread, but should be very lightweight.
clearPrefs();
resetLightweightTheme();
}
}

View File

@ -41,8 +41,10 @@ import org.mozilla.gecko.tabqueue.TabQueueHelper;
import org.mozilla.gecko.tabqueue.TabQueuePrompt;
import org.mozilla.gecko.updater.UpdateService;
import org.mozilla.gecko.updater.UpdateServiceHelper;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.ContextUtils;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.HardwareUtils;
import org.mozilla.gecko.util.InputOptionsUtils;
import org.mozilla.gecko.util.NativeEventListener;
@ -101,12 +103,12 @@ import java.util.Locale;
import java.util.Map;
public class GeckoPreferences
extends AppCompatPreferenceActivity
implements
GeckoActivityStatus,
NativeEventListener,
OnPreferenceChangeListener,
OnSharedPreferenceChangeListener
extends AppCompatPreferenceActivity
implements BundleEventListener,
GeckoActivityStatus,
NativeEventListener,
OnPreferenceChangeListener,
OnSharedPreferenceChangeListener
{
private static final String LOGTAG = "GeckoPreferences";
@ -369,9 +371,8 @@ OnSharedPreferenceChangeListener
// Use setResourceToOpen to specify these extras.
Bundle intentExtras = getIntent().getExtras();
EventDispatcher.getInstance().registerGeckoThreadListener(this,
"Sanitize:Finished",
"Snackbar:Show");
EventDispatcher.getInstance().registerGeckoThreadListener((NativeEventListener) this,
"Sanitize:Finished");
// Add handling for long-press click.
// This is only for Android 3.0 and below (which use the long-press-context-menu paradigm).
@ -506,9 +507,8 @@ OnSharedPreferenceChangeListener
protected void onDestroy() {
super.onDestroy();
EventDispatcher.getInstance().unregisterGeckoThreadListener(this,
"Sanitize:Finished",
"Snackbar:Show");
EventDispatcher.getInstance().unregisterGeckoThreadListener((NativeEventListener) this,
"Sanitize:Finished");
if (mPrefsRequest != null) {
PrefsHelper.removeObserver(mPrefsRequest);
@ -518,6 +518,8 @@ OnSharedPreferenceChangeListener
@Override
public void onPause() {
EventDispatcher.getInstance().unregisterUiThreadListener(this, "Snackbar:Show");
// Symmetric with onResume.
if (isMultiPane()) {
SharedPreferences prefs = GeckoSharedPrefs.forApp(this);
@ -535,6 +537,8 @@ OnSharedPreferenceChangeListener
public void onResume() {
super.onResume();
EventDispatcher.getInstance().registerUiThreadListener(this, "Snackbar:Show");
if (getApplication() instanceof GeckoApplication) {
((GeckoApplication) getApplication()).onActivityResume(this);
}
@ -607,6 +611,17 @@ OnSharedPreferenceChangeListener
Permissions.onRequestPermissionsResult(this, permissions, grantResults);
}
@Override
public void handleMessage(final String event, final GeckoBundle message,
final EventCallback callback) {
if ("Snackbar:Show".equals(event)) {
SnackbarBuilder.builder(this)
.fromEvent(message)
.callback(callback)
.buildAndShow();
}
}
@Override
public void handleMessage(final String event, final NativeJSObject message, final EventCallback callback) {
try {
@ -620,12 +635,6 @@ OnSharedPreferenceChangeListener
.duration(Snackbar.LENGTH_LONG)
.buildAndShow();
break;
case "Snackbar:Show":
SnackbarBuilder.builder(this)
.fromEvent(message)
.callback(callback)
.buildAndShow();
break;
}
} catch (Exception e) {
Log.e(LOGTAG, "Exception handling message \"" + event + "\":", e);

View File

@ -591,7 +591,7 @@ var ActionBarHandler = {
},
action: function(element, win) {
Messaging.sendRequest({
WindowEventDispatcher.sendRequest({
type: "Share:Text",
text: ActionBarHandler._getSelectedText(),
});

View File

@ -48,7 +48,7 @@ var Feedback = {
break;
case "FeedbackMaybeLater":
Messaging.sendRequest({ type: "Feedback:MaybeLater" });
GlobalEventDispatcher.sendRequest({ type: "Feedback:MaybeLater" });
break;
}

View File

@ -106,7 +106,7 @@ var PermissionsHelper = {
// Keep track of permissions, so we know which ones to clear
this._currentPermissions = permissions;
Messaging.sendRequest({
WindowEventDispatcher.sendRequest({
type: "Permissions:Data",
permissions: permissions
});

View File

@ -126,7 +126,7 @@ var Reader = {
}
case "Reader:SystemUIVisibility":
Messaging.sendRequest({
WindowEventDispatcher.sendRequest({
type: "SystemUI:Visibility",
visible: message.data.visible
});

View File

@ -151,7 +151,7 @@ var RemoteDebugger = {
return this._receivingOOB;
}
this._receivingOOB = Messaging.sendRequestForResult({
this._receivingOOB = WindowEventDispatcher.sendRequestForResult({
type: "DevToolsAuth:Scan"
}).then(data => {
return JSON.parse(data);

View File

@ -78,17 +78,20 @@ function init() {
function checkForUpdates() {
showCheckingMessage();
Services.androidBridge.handleGeckoMessage({ type: "Update:Check" });
let window = Services.wm.getMostRecentWindow("navigator:browser");
window.WindowEventDispatcher.sendRequest({ type: "Update:Check" });
}
function downloadUpdate() {
Services.androidBridge.handleGeckoMessage({ type: "Update:Download" });
let window = Services.wm.getMostRecentWindow("navigator:browser");
window.WindowEventDispatcher.sendRequest({ type: "Update:Download" });
}
function installUpdate() {
showCheckAction();
Services.androidBridge.handleGeckoMessage({ type: "Update:Install" });
let window = Services.wm.getMostRecentWindow("navigator:browser");
window.WindowEventDispatcher.sendRequest({ type: "Update:Install" });
}
let updateLink = document.getElementById("updateLink");

View File

@ -8,8 +8,11 @@
var { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/Messaging.jsm");
Cu.import("resource://gre/modules/SharedPreferences.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher",
"resource://gre/modules/Messaging.jsm");
// Name of Android SharedPreference controlling whether to upload
// health reports.
@ -119,7 +122,7 @@ var healthReportWrapper = {
showSettings: function () {
console.log("AboutHealthReport: showing settings.");
Messaging.sendRequest({
EventDispatcher.instance.sendRequest({
type: "Settings:Show",
resource: "preferences_vendor",
});
@ -127,7 +130,7 @@ var healthReportWrapper = {
launchUpdater: function () {
console.log("AboutHealthReport: launching updater.");
Messaging.sendRequest({
EventDispatcher.instance.sendRequest({
type: "Updater:Launch",
});
},

View File

@ -5,7 +5,6 @@
var Ci = Components.interfaces, Cc = Components.classes, Cu = Components.utils;
Cu.import("resource://services-common/utils.js"); /*global: CommonUtils */
Cu.import("resource://gre/modules/Messaging.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/TelemetryStopwatch.jsm");
@ -19,6 +18,8 @@ XPCOMUtils.defineLazyGetter(window, "gChromeWin", () =>
.getInterface(Ci.nsIDOMWindow)
.QueryInterface(Ci.nsIDOMChromeWindow));
XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher",
"resource://gre/modules/Messaging.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Prompt",
"resource://gre/modules/Prompt.jsm");
@ -416,7 +417,7 @@ var Logins = {
_loadFavicon: function (aImg, aHostname) {
// Load favicon from cache.
Messaging.sendRequestForResult({
EventDispatcher.instance.sendRequestForResult({
type: "Favicon:CacheLoad",
url: aHostname,
}).then(function(faviconUrl) {

View File

@ -390,14 +390,13 @@ var BrowserApp = {
Services.obs.addObserver(this, "android-set-pref", false);
Services.obs.addObserver(this, "gather-telemetry", false);
Services.obs.addObserver(this, "keyword-search", false);
Services.obs.addObserver(this, "sessionstore-state-purge-complete", false);
Services.obs.addObserver(this, "Fonts:Reload", false);
Services.obs.addObserver(this, "Vibration:Request", false);
Messaging.addListener(this.getHistory.bind(this), "Session:GetHistory");
window.addEventListener("fullscreen", function() {
Messaging.sendRequest({
WindowEventDispatcher.sendRequest({
type: window.fullScreen ? "ToggleChrome:Hide" : "ToggleChrome:Show"
});
}, false);
@ -409,7 +408,7 @@ var BrowserApp = {
// (per spec). This means the last event on enabling will be for the innermost
// document, which will have fullscreenElement set correctly.
let doc = e.target;
Messaging.sendRequest({
WindowEventDispatcher.sendRequest({
type: doc.fullscreenElement ? "DOMFullScreen:Start" : "DOMFullScreen:Stop",
rootElement: doc.fullscreenElement == doc.documentElement
});
@ -508,7 +507,7 @@ var BrowserApp = {
}
// Notify Java that Gecko has loaded.
Messaging.sendRequest({ type: "Gecko:Ready" });
GlobalEventDispatcher.sendRequest({ type: "Gecko:Ready" });
this.deck.addEventListener("DOMContentLoaded", function BrowserApp_delayedStartup() {
BrowserApp.deck.removeEventListener("DOMContentLoaded", BrowserApp_delayedStartup, false);
@ -517,7 +516,7 @@ var BrowserApp = {
InitLater(() => Cu.import("resource://gre/modules/PresentationDeviceInfoManager.jsm"));
InitLater(() => Services.obs.notifyObservers(window, "browser-delayed-startup-finished", ""));
InitLater(() => Messaging.sendRequest({ type: "Gecko:DelayedStartup" }));
InitLater(() => GlobalEventDispatcher.sendRequest({ type: "Gecko:DelayedStartup" }));
if (!AppConstants.RELEASE_OR_BETA) {
InitLater(() => WebcompatReporter.init());
@ -573,7 +572,7 @@ var BrowserApp = {
*/
setLocale: function (locale) {
console.log("browser.js: requesting locale set: " + locale);
Messaging.sendRequest({ type: "Locale:Set", locale: locale });
WindowEventDispatcher.sendRequest({ type: "Locale:Set", locale: locale });
},
initContextMenu: function () {
@ -726,7 +725,7 @@ var BrowserApp = {
UITelemetry.addEvent("action.1", "contextmenu", null, "web_contact_email");
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
Messaging.sendRequest({
WindowEventDispatcher.sendRequest({
type: "Contact:Add",
email: url
});
@ -738,7 +737,7 @@ var BrowserApp = {
UITelemetry.addEvent("action.1", "contextmenu", null, "web_contact_phone");
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
Messaging.sendRequest({
WindowEventDispatcher.sendRequest({
type: "Contact:Add",
phone: url
});
@ -752,7 +751,7 @@ var BrowserApp = {
let url = NativeWindow.contextmenus._getLinkURL(aTarget);
let title = aTarget.textContent || aTarget.title || url;
Messaging.sendRequest({
WindowEventDispatcher.sendRequest({
type: "Bookmark:Insert",
url: url,
title: title
@ -889,7 +888,7 @@ var BrowserApp = {
UITelemetry.addEvent("action.1", "contextmenu", null, "web_background_image");
let src = aTarget.src;
Messaging.sendRequest({
WindowEventDispatcher.sendRequest({
type: "Image:SetAs",
url: src
});
@ -1756,7 +1755,7 @@ var BrowserApp = {
let query = isPrivate ? "" : aData;
let engine = aSubject.QueryInterface(Ci.nsISearchEngine);
Messaging.sendRequest({
GlobalEventDispatcher.sendRequest({
type: "Search:Keyword",
identifier: engine.identifier,
name: engine.name,
@ -1902,12 +1901,8 @@ var BrowserApp = {
break;
}
case "sessionstore-state-purge-complete":
Messaging.sendRequest({ type: "Session:StatePurged" });
break;
case "gather-telemetry":
Messaging.sendRequest({ type: "Telemetry:Gather" });
GlobalEventDispatcher.sendRequest({ type: "Telemetry:Gather" });
break;
case "Locale:OS":
@ -2179,21 +2174,21 @@ var NativeWindow = {
options.type = "Menu:Add";
options.id = this._menuId;
Messaging.sendRequest(options);
GlobalEventDispatcher.sendRequest(options);
this._callbacks[this._menuId] = options.callback;
this._menuId++;
return this._menuId - 1;
},
remove: function(aId) {
Messaging.sendRequest({ type: "Menu:Remove", id: aId });
GlobalEventDispatcher.sendRequest({ type: "Menu:Remove", id: aId });
},
update: function(aId, aOptions) {
if (!aOptions)
return;
Messaging.sendRequest({
GlobalEventDispatcher.sendRequest({
type: "Menu:Update",
id: aId,
options: aOptions
@ -3504,7 +3499,7 @@ Tab.prototype = {
isPrivate: isPrivate,
stub: stub
};
Messaging.sendRequest(message);
GlobalEventDispatcher.sendRequest(message);
}
let flags = Ci.nsIWebProgress.NOTIFY_STATE_ALL |
@ -4535,7 +4530,7 @@ var BrowserEventHandler = {
case 'OpenMediaWithExternalApp': {
let mediaSrc = aEvent.target.currentSrc || aEvent.target.src;
let uuid = uuidgen.generateUUID().toString();
Services.androidBridge.handleGeckoMessage({
GlobalEventDispatcher.sendRequest({
type: "Video:Play",
uri: mediaSrc,
uuid: uuid
@ -5632,7 +5627,7 @@ var CharacterEncoding = {
showCharEncoding = Services.prefs.getComplexValue("browser.menu.showCharacterEncoding", Ci.nsIPrefLocalizedString).data;
} catch (e) { /* Optional */ }
Messaging.sendRequest({
GlobalEventDispatcher.sendRequest({
type: "CharEncoding:State",
visible: showCharEncoding
});
@ -5666,7 +5661,7 @@ var CharacterEncoding = {
}
}
Messaging.sendRequest({
GlobalEventDispatcher.sendRequest({
type: "CharEncoding:Data",
charsets: this._charsets,
selected: selected
@ -6334,7 +6329,7 @@ var Experiments = {
OFFLINE_CACHE: "offline-cache",
init() {
Messaging.sendRequestForResult({
GlobalEventDispatcher.sendRequestForResult({
type: "Experiments:GetActive"
}).then(experiments => {
let names = JSON.parse(experiments);
@ -6361,7 +6356,7 @@ var Experiments = {
},
setOverride(name, isEnabled) {
Messaging.sendRequest({
GlobalEventDispatcher.sendRequest({
type: "Experiments:SetOverride",
name: name,
isEnabled: isEnabled
@ -6369,7 +6364,7 @@ var Experiments = {
},
clearOverride(name) {
Messaging.sendRequest({
GlobalEventDispatcher.sendRequest({
type: "Experiments:ClearOverride",
name: name
});

View File

@ -14,9 +14,6 @@ Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Log",
"resource://gre/modules/AndroidLog.jsm", "AndroidLog");
XPCOMUtils.defineLazyModuleGetter(this, "Messaging",
"resource://gre/modules/Messaging.jsm", "Messaging");
XPCOMUtils.defineLazyModuleGetter(this, "Services",
"resource://gre/modules/Services.jsm", "Services");
@ -26,7 +23,4 @@ function dump(msg) {
function startup() {
dump("zerdatime " + Date.now() + " - geckoivew chrome startup finished.");
// Notify Java that Gecko has loaded.
Messaging.sendRequest({ type: "Gecko:Ready" });
}

View File

@ -26,7 +26,7 @@ Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "RuntimePermissions", "resource://gre/modules/RuntimePermissions.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", "resource://gre/modules/Messaging.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm");
// -----------------------------------------------------------------------
@ -239,7 +239,7 @@ HelperAppLauncherDialog.prototype = {
mimeType = ContentAreaUtils.getMIMETypeForURI(aLauncher.source) || "";
}
Messaging.sendRequest({
EventDispatcher.instance.sendRequest({
'type': 'Download:AndroidDownloadManager',
'uri': aLauncher.source.spec,
'mimeType': mimeType,

View File

@ -311,7 +311,8 @@ SessionStore.prototype = {
this._openTabs(data);
if (data.shouldNotifyTabsOpenedToJava) {
Messaging.sendRequest({
let window = Services.wm.getMostRecentWindow("navigator:browser");
window.WindowEventDispatcher.sendRequest({
type: "Tabs:TabsOpened"
});
}
@ -979,7 +980,8 @@ SessionStore.prototype = {
// If we have private data, send it to Java; otherwise, send null to
// indicate that there is no private data
Messaging.sendRequest({
let window = Services.wm.getMostRecentWindow("navigator:browser");
window.WindowEventDispatcher.sendRequest({
type: "PrivateBrowsing:Data",
session: (privateData.windows.length > 0 && privateData.windows[0].tabs.length > 0) ? JSON.stringify(privateData) : null
});

View File

@ -106,6 +106,9 @@ public final class EventDispatcher extends JNIObject {
try {
synchronized (listenersMap) {
for (final String event : events) {
if (event == null) {
continue;
}
List<T> listeners = listenersMap.get(event);
if (listeners == null) {
// Java doesn't let us put Class<? extends List<T>> as the type for listType.
@ -156,6 +159,9 @@ public final class EventDispatcher extends JNIObject {
final String[] events) {
synchronized (listenersMap) {
for (final String event : events) {
if (event == null) {
continue;
}
List<T> listeners = listenersMap.get(event);
if ((listeners == null ||
!listeners.remove(listener)) && !AppConstants.RELEASE_OR_BETA) {

View File

@ -5,10 +5,10 @@
package org.mozilla.gecko;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.AppConstants.Versions;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.ThreadUtils;
import org.mozilla.gecko.util.UIAsyncTask;
@ -34,7 +34,7 @@ public class GeckoAccessibility {
private static boolean sEnabled;
// Used to store the JSON message and populate the event later in the code path.
private static JSONObject sHoverEnter;
private static GeckoBundle sHoverEnter;
private static AccessibilityNodeInfo sVirtualCursorNode;
private static int sCurrentNode;
@ -76,32 +76,32 @@ public class GeckoAccessibility {
}.execute();
}
private static void populateEventFromJSON (AccessibilityEvent event, JSONObject message) {
final JSONArray textArray = message.optJSONArray("text");
private static void populateEventFromJSON (AccessibilityEvent event, GeckoBundle message) {
final String[] textArray = message.getStringArray("text");
if (textArray != null) {
for (int i = 0; i < textArray.length(); i++)
event.getText().add(textArray.optString(i));
for (int i = 0; i < textArray.length; i++)
event.getText().add(textArray[i]);
}
event.setContentDescription(message.optString("description"));
event.setEnabled(message.optBoolean("enabled", true));
event.setChecked(message.optBoolean("checked"));
event.setPassword(message.optBoolean("password"));
event.setAddedCount(message.optInt("addedCount", -1));
event.setRemovedCount(message.optInt("removedCount", -1));
event.setFromIndex(message.optInt("fromIndex", -1));
event.setItemCount(message.optInt("itemCount", -1));
event.setCurrentItemIndex(message.optInt("currentItemIndex", -1));
event.setBeforeText(message.optString("beforeText"));
event.setToIndex(message.optInt("toIndex", -1));
event.setScrollable(message.optBoolean("scrollable"));
event.setScrollX(message.optInt("scrollX", -1));
event.setScrollY(message.optInt("scrollY", -1));
event.setMaxScrollX(message.optInt("maxScrollX", -1));
event.setMaxScrollY(message.optInt("maxScrollY", -1));
event.setContentDescription(message.getString("description"));
event.setEnabled(message.getBoolean("enabled", true));
event.setChecked(message.getBoolean("checked"));
event.setPassword(message.getBoolean("password"));
event.setAddedCount(message.getInt("addedCount", -1));
event.setRemovedCount(message.getInt("removedCount", -1));
event.setFromIndex(message.getInt("fromIndex", -1));
event.setItemCount(message.getInt("itemCount", -1));
event.setCurrentItemIndex(message.getInt("currentItemIndex", -1));
event.setBeforeText(message.getString("beforeText"));
event.setToIndex(message.getInt("toIndex", -1));
event.setScrollable(message.getBoolean("scrollable"));
event.setScrollX(message.getInt("scrollX", -1));
event.setScrollY(message.getInt("scrollY", -1));
event.setMaxScrollX(message.getInt("maxScrollX", -1));
event.setMaxScrollY(message.getInt("maxScrollY", -1));
}
private static void sendDirectAccessibilityEvent(int eventType, JSONObject message) {
private static void sendDirectAccessibilityEvent(int eventType, GeckoBundle message) {
final Context context = GeckoAppShell.getApplicationContext();
final AccessibilityEvent accEvent = AccessibilityEvent.obtain(eventType);
accEvent.setClassName(GeckoAccessibility.class.getName());
@ -120,11 +120,11 @@ public class GeckoAccessibility {
return sEnabled;
}
public static void sendAccessibilityEvent (final JSONObject message) {
public static void sendAccessibilityEvent(final GeckoBundle message) {
if (!sEnabled)
return;
final int eventType = message.optInt("eventType", -1);
final int eventType = message.getInt("eventType", -1);
if (eventType < 0) {
Log.e(LOGTAG, "No accessibility event type provided");
return;
@ -133,11 +133,11 @@ public class GeckoAccessibility {
sendAccessibilityEvent(message, eventType);
}
public static void sendAccessibilityEvent (final JSONObject message, final int eventType) {
public static void sendAccessibilityEvent(final GeckoBundle message, final int eventType) {
if (!sEnabled)
return;
final String exitView = message.optString("exitView");
final String exitView = message.getString("exitView");
if (exitView.equals("moveNext")) {
sCurrentNode = VIRTUAL_ENTRY_POINT_AFTER;
} else if (exitView.equals("movePrevious")) {
@ -162,29 +162,30 @@ public class GeckoAccessibility {
if (view == null)
return;
if (sVirtualCursorNode == null)
if (sVirtualCursorNode == null) {
sVirtualCursorNode = AccessibilityNodeInfo.obtain(view, VIRTUAL_CURSOR_POSITION);
sVirtualCursorNode.setEnabled(message.optBoolean("enabled", true));
sVirtualCursorNode.setClickable(message.optBoolean("clickable"));
sVirtualCursorNode.setCheckable(message.optBoolean("checkable"));
sVirtualCursorNode.setChecked(message.optBoolean("checked"));
sVirtualCursorNode.setPassword(message.optBoolean("password"));
}
sVirtualCursorNode.setEnabled(message.getBoolean("enabled", true));
sVirtualCursorNode.setClickable(message.getBoolean("clickable"));
sVirtualCursorNode.setCheckable(message.getBoolean("checkable"));
sVirtualCursorNode.setChecked(message.getBoolean("checked"));
sVirtualCursorNode.setPassword(message.getBoolean("password"));
final JSONArray textArray = message.optJSONArray("text");
final String[] textArray = message.getStringArray("text");
StringBuilder sb = new StringBuilder();
if (textArray != null && textArray.length() > 0) {
sb.append(textArray.optString(0));
for (int i = 1; i < textArray.length(); i++) {
sb.append(" ").append(textArray.optString(i));
if (textArray != null && textArray.length > 0) {
sb.append(textArray[0]);
for (int i = 1; i < textArray.length; i++) {
sb.append(" ").append(textArray[i]);
}
sVirtualCursorNode.setText(sb.toString());
}
sVirtualCursorNode.setContentDescription(message.optString("description"));
sVirtualCursorNode.setContentDescription(message.getString("description"));
JSONObject bounds = message.optJSONObject("bounds");
final GeckoBundle bounds = message.getBundle("bounds");
if (bounds != null) {
Rect relativeBounds = new Rect(bounds.optInt("left"), bounds.optInt("top"),
bounds.optInt("right"), bounds.optInt("bottom"));
Rect relativeBounds = new Rect(bounds.getInt("left"), bounds.getInt("top"),
bounds.getInt("right"), bounds.getInt("bottom"));
sVirtualCursorNode.setBoundsInParent(relativeBounds);
int[] locationOnScreen = new int[2];
view.getLocationOnScreen(locationOnScreen);
@ -193,10 +194,10 @@ public class GeckoAccessibility {
sVirtualCursorNode.setBoundsInScreen(screenBounds);
}
final JSONObject braille = message.optJSONObject("brailleOutput");
final GeckoBundle braille = message.getBundle("brailleOutput");
if (braille != null) {
sendBrailleText(view, braille.optString("text"),
braille.optInt("selectionStart"), braille.optInt("selectionEnd"));
sendBrailleText(view, braille.getString("text"),
braille.getInt("selectionStart"), braille.getInt("selectionEnd"));
}
if (eventType == AccessibilityEvent.TYPE_VIEW_HOVER_ENTER) {

View File

@ -551,13 +551,7 @@ public class GeckoThread extends Thread {
// And... we're done.
setState(State.EXITED);
try {
final JSONObject msg = new JSONObject();
msg.put("type", "Gecko:Exited");
EventDispatcher.getInstance().dispatchEvent(msg, null);
} catch (final JSONException e) {
Log.e(LOGTAG, "unable to dispatch event", e);
}
EventDispatcher.getInstance().dispatch("Gecko:Exited", null);
// Remove pumpMessageLoop() idle handler
Looper.myQueue().removeIdleHandler(idleHandler);

View File

@ -8,6 +8,10 @@ var Ci = Components.interfaces;
Components.utils.import("resource://gre/modules/Services.jsm");
Components.utils.import("resource://gre/modules/LightweightThemeManager.jsm");
Components.utils.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher",
"resource://gre/modules/Messaging.jsm");
function LightweightThemeConsumer(aDocument) {
this._doc = aDocument;
@ -39,6 +43,6 @@ LightweightThemeConsumer.prototype = {
let msg = active ? { type: "LightweightTheme:Update", data: aData } :
{ type: "LightweightTheme:Disable" };
Services.androidBridge.handleGeckoMessage(msg);
EventDispatcher.instance.sendRequest(msg);
}
}

View File

@ -8,6 +8,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
this.EXPORTED_SYMBOLS = ["RuntimePermissions"];
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm");
@ -36,6 +37,7 @@ var RuntimePermissions = {
permissions: permissions
};
return Messaging.sendRequestForResult(msg);
let window = Services.wm.getMostRecentWindow("navigator:browser");
return window.WindowEventDispatcher.sendRequestForResult(msg);
}
};
};

View File

@ -13,7 +13,6 @@ Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/LoadContextInfo.jsm");
Cu.import("resource://gre/modules/FormHistory.jsm");
Cu.import("resource://gre/modules/Messaging.jsm");
Cu.import("resource://gre/modules/Task.jsm");
Cu.import("resource://gre/modules/Downloads.jsm");
Cu.import("resource://gre/modules/osfile.jsm");
@ -21,6 +20,8 @@ Cu.import("resource://gre/modules/Accounts.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "DownloadIntegration",
"resource://gre/modules/DownloadIntegration.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher",
"resource://gre/modules/Messaging.jsm");
function dump(a) {
Services.console.logStringMessage(a);
@ -144,7 +145,7 @@ Sanitizer.prototype = {
history: {
clear: function ()
{
return Messaging.sendRequestForResult({ type: "Sanitize:ClearHistory" })
return EventDispatcher.instance.sendRequestForResult({ type: "Sanitize:ClearHistory" })
.catch(e => Cu.reportError("Java-side history clearing failed: " + e))
.then(function() {
try {
@ -170,7 +171,7 @@ Sanitizer.prototype = {
searchHistory: {
clear: function ()
{
return Messaging.sendRequestForResult({ type: "Sanitize:ClearHistory", clearSearchHistory: true })
return EventDispatcher.instance.sendRequestForResult({ type: "Sanitize:ClearHistory", clearSearchHistory: true })
.catch(e => Cu.reportError("Java-side search history clearing failed: " + e))
},
@ -283,7 +284,7 @@ Sanitizer.prototype = {
syncedTabs: {
clear: function ()
{
return Messaging.sendRequestForResult({ type: "Sanitize:ClearSyncedTabs" })
return EventDispatcher.instance.sendRequestForResult({ type: "Sanitize:ClearSyncedTabs" })
.catch(e => Cu.reportError("Java-side synced tabs clearing failed: " + e));
},

View File

@ -10,7 +10,7 @@ this.EXPORTED_SYMBOLS = ["Snackbars"];
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", "resource://gre/modules/Messaging.jsm");
const LENGTH_INDEFINITE = -2;
const LENGTH_LONG = 0;
@ -45,9 +45,9 @@ var Snackbars = {
msg.action.label = aOptions.action.label;
}
Messaging.sendRequestForResult(msg).then(result => aOptions.action.callback());
EventDispatcher.instance.sendRequestForResult(msg).then(result => aOptions.action.callback());
} else {
Messaging.sendRequest(msg);
EventDispatcher.instance.sendRequest(msg);
}
}
};
@ -69,4 +69,4 @@ function migrateToastIfNeeded(aDuration, aOptions) {
};
}
return [duration, options];
}
}

View File

@ -10,7 +10,7 @@ this.EXPORTED_SYMBOLS = ["WebsiteMetadata"];
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Messaging", "resource://gre/modules/Messaging.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", "resource://gre/modules/Messaging.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Task", "resource://gre/modules/Task.jsm");
var WebsiteMetadata = {
@ -32,10 +32,11 @@ var WebsiteMetadata = {
let msg = {
type: 'Website:Metadata',
location: doc.location.href,
metadata: metadata,
hasImage: metadata.image_url && metadata.image_url !== "",
metadata: JSON.stringify(metadata),
};
Messaging.sendRequest(msg);
EventDispatcher.instance.sendRequest(msg);
});
}
};

View File

@ -68,7 +68,8 @@ abstract class BaseTest extends BaseRobocopTest {
protected void blockForDelayedStartup() {
try {
Actions.EventExpecter delayedStartupExpector = mActions.expectGeckoEvent("Gecko:DelayedStartup");
Actions.EventExpecter delayedStartupExpector =
mActions.expectGlobalEvent(Actions.EventType.UI, "Gecko:DelayedStartup");
delayedStartupExpector.blockForEvent(GECKO_READY_WAIT_MS, true);
delayedStartupExpector.unregisterListener();
} catch (Exception e) {
@ -78,7 +79,8 @@ abstract class BaseTest extends BaseRobocopTest {
protected void blockForGeckoReady() {
try {
Actions.EventExpecter geckoReadyExpector = mActions.expectGeckoEvent("Gecko:Ready");
Actions.EventExpecter geckoReadyExpector =
mActions.expectGlobalEvent(Actions.EventType.GECKO, "Gecko:Ready");
if (!GeckoThread.isRunning()) {
geckoReadyExpector.blockForEvent(GECKO_READY_WAIT_MS, true);
}

View File

@ -48,7 +48,7 @@ abstract class ContentContextMenuTest extends PixelTest {
if (!mSolo.searchText(contextMenuOption)) {
openWebContentContextMenu(contextMenuOption); // Open the context menu if it is not already
}
Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added");
Actions.EventExpecter tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added");
mSolo.clickOnText(contextMenuOption);
tabEventExpecter.blockForEvent();
tabEventExpecter.unregisterListener();

View File

@ -26,7 +26,7 @@ public final class GeckoHelper {
}
public static void blockForReady() {
blockForEvent("Gecko:Ready");
blockForEvent(Actions.EventType.GECKO, "Gecko:Ready");
}
/**
@ -34,11 +34,11 @@ public final class GeckoHelper {
* first page load.
*/
public static void blockForDelayedStartup() {
blockForEvent("Gecko:DelayedStartup");
blockForEvent(Actions.EventType.UI, "Gecko:DelayedStartup");
}
private static void blockForEvent(final String eventName) {
final EventExpecter eventExpecter = sActions.expectGeckoEvent(eventName);
private static void blockForEvent(final Actions.EventType type, final String eventName) {
final EventExpecter eventExpecter = sActions.expectGlobalEvent(type, eventName);
if (!GeckoThread.isRunning()) {
eventExpecter.blockForEvent();

View File

@ -29,7 +29,7 @@ public class testAboutPage extends PixelTest {
verifyUrlInContentDescription(url);
// Set up listeners to catch the page load we're about to do.
Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added");
Actions.EventExpecter tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added");
Actions.EventExpecter contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
selectSettingsItem(mStringHelper.MOZILLA_SECTION_LABEL, mStringHelper.ABOUT_LABEL);

View File

@ -29,7 +29,7 @@ public class testAddonManager extends PixelTest {
selectMenuItem(mStringHelper.ADDONS_LABEL);
// Set up listeners to catch the page load we're about to do
tabEventExpecter = mActions.expectGeckoEvent("Tab:Added");
tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added");
contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
// Wait for the new tab and page to load
@ -50,7 +50,7 @@ public class testAddonManager extends PixelTest {
verifyUrlBarTitle(aboutAddonsURL);
// Setup wait for tab to spawn and load
tabEventExpecter = mActions.expectGeckoEvent("Tab:Added");
tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added");
contentEventExpecter = mActions.expectGeckoEvent("DOMContentLoaded");
// Open a new tab

View File

@ -4,17 +4,16 @@
package org.mozilla.gecko.tests;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.Actions;
import org.mozilla.gecko.Element;
import org.mozilla.gecko.R;
import org.mozilla.gecko.util.GeckoBundle;
import org.mozilla.gecko.util.StringUtils;
public class testBookmarksPanel extends AboutHomeTest {
public void testBookmarksPanel() {
final String BOOKMARK_URL = getAbsoluteUrl(mStringHelper.ROBOCOP_BLANK_PAGE_01_URL);
JSONObject data = null;
GeckoBundle data = null;
// Make sure our default bookmarks are loaded.
// Technically this will race with the check below.
@ -40,43 +39,27 @@ public class testBookmarksPanel extends AboutHomeTest {
// Test that "Open in New Tab" works
final Element tabCount = mDriver.findElement(getActivity(), R.id.tabs_counter);
final int tabCountInt = Integer.parseInt(tabCount.getText());
Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added");
Actions.EventExpecter tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added");
mSolo.clickOnText(mStringHelper.BOOKMARK_CONTEXT_MENU_ITEMS[0]);
try {
data = new JSONObject(tabEventExpecter.blockForEventData());
} catch (JSONException e) {
mAsserter.ok(false, "exception getting event data", e.toString());
}
data = tabEventExpecter.blockForBundle();
tabEventExpecter.unregisterListener();
mAsserter.ok(mSolo.searchText(mStringHelper.TITLE_PLACE_HOLDER), "Checking that the tab is not changed", "The tab was not changed");
// extra check here on the Tab:Added message to be sure the right tab opened
int tabID = 0;
try {
mAsserter.is(mStringHelper.ABOUT_FIREFOX_URL, data.getString("uri"), "Checking tab uri");
tabID = data.getInt("tabID");
} catch (JSONException e) {
mAsserter.ok(false, "exception accessing event data", e.toString());
}
mAsserter.is(mStringHelper.ABOUT_FIREFOX_URL, data.getString("uri"), "Checking tab uri");
// close tab so about:firefox can be selected again
closeTab(tabID);
closeTab(data.getInt("tabID"));
// Test that "Open in Private Tab" works
openBookmarkContextMenu(mStringHelper.DEFAULT_BOOKMARKS_URLS[0]);
tabEventExpecter = mActions.expectGeckoEvent("Tab:Added");
tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added");
mSolo.clickOnText(mStringHelper.BOOKMARK_CONTEXT_MENU_ITEMS[1]);
try {
data = new JSONObject(tabEventExpecter.blockForEventData());
} catch (JSONException e) {
mAsserter.ok(false, "exception getting event data", e.toString());
}
data = tabEventExpecter.blockForBundle();
tabEventExpecter.unregisterListener();
mAsserter.ok(mSolo.searchText(mStringHelper.TITLE_PLACE_HOLDER), "Checking that the tab is not changed", "The tab was not changed");
// extra check here on the Tab:Added message to be sure the right tab opened, again
try {
mAsserter.is(mStringHelper.ABOUT_FIREFOX_URL, data.getString("uri"), "Checking tab uri");
} catch (JSONException e) {
mAsserter.ok(false, "exception accessing event data", e.toString());
}
mAsserter.is(mStringHelper.ABOUT_FIREFOX_URL, data.getString("uri"), "Checking tab uri");
// Test that "Edit" works
String[] editedBookmarkValues = new String[] { "New bookmark title", "www.NewBookmark.url", "newBookmarkKeyword" };

View File

@ -7,10 +7,8 @@ package org.mozilla.gecko.tests;
import android.widget.CheckBox;
import android.view.View;
import com.robotium.solo.Condition;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.Actions;
import org.mozilla.gecko.util.GeckoBundle;
/* This test will test if doorhangers are displayed and dismissed
The test will test:
@ -133,7 +131,7 @@ public class testDoorHanger extends BaseTest {
mAsserter.is(mSolo.searchText(mStringHelper.POPUP_MESSAGE), true, "Popup blocker is displayed");
// Wait for the popup to be shown.
Actions.EventExpecter tabEventExpecter = mActions.expectGeckoEvent("Tab:Added");
Actions.EventExpecter tabEventExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added");
waitForCheckBox();
mSolo.clickOnCheckBox(0);
@ -141,18 +139,14 @@ public class testDoorHanger extends BaseTest {
waitForTextDismissed(mStringHelper.POPUP_MESSAGE);
mAsserter.is(mSolo.searchText(mStringHelper.POPUP_MESSAGE), false, "Popup blocker is hidden when popup allowed");
try {
final JSONObject data = new JSONObject(tabEventExpecter.blockForEventData());
final GeckoBundle data = tabEventExpecter.blockForBundle();
// Check to make sure the popup window was opened.
mAsserter.is("data:text/plain;charset=utf-8,a", data.getString("uri"), "Checking popup URL");
// Check to make sure the popup window was opened.
mAsserter.is("data:text/plain;charset=utf-8,a", data.getString("uri"), "Checking popup URL");
// Close the popup window.
closeTab(data.getInt("tabID"));
// Close the popup window.
closeTab(data.getInt("tabID"));
} catch (JSONException e) {
mAsserter.ok(false, "exception getting event data", e.toString());
}
tabEventExpecter.unregisterListener();
// Load page with popup

View File

@ -6,10 +6,9 @@ package org.mozilla.gecko.tests;
import java.util.ArrayList;
import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.gecko.Actions;
import org.mozilla.gecko.Tabs;
import org.mozilla.gecko.util.GeckoBundle;
/**
* The test loads a new private tab and loads a page with a big link on it
@ -27,7 +26,7 @@ public class testPrivateBrowsing extends ContentContextMenuTest {
blockForGeckoReady();
Actions.EventExpecter tabExpecter = mActions.expectGeckoEvent("Tab:Added");
Actions.EventExpecter tabExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added");
Actions.EventExpecter contentExpecter = mActions.expectGeckoEvent("Content:PageShow");
tabs.loadUrl(bigLinkUrl, Tabs.LOADURL_NEW_TAB | Tabs.LOADURL_PRIVATE);
tabExpecter.blockForEvent();
@ -46,18 +45,19 @@ public class testPrivateBrowsing extends ContentContextMenuTest {
mAsserter.ok(!mSolo.searchText(mStringHelper.CONTEXT_MENU_ITEMS_IN_NORMAL_TAB[0]), "Checking that 'Open Link in New Tab' is not displayed in the context menu", "'Open Link in New Tab' is not displayed in the context menu");
// Open the link in a new private tab and check that it is private
tabExpecter = mActions.expectGeckoEvent("Tab:Added");
tabExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added");
contentExpecter = mActions.expectGeckoEvent("Content:PageShow");
mSolo.clickOnText(mStringHelper.CONTEXT_MENU_ITEMS_IN_PRIVATE_TAB[0]);
String eventData = tabExpecter.blockForEventData();
final GeckoBundle eventData = tabExpecter.blockForBundle();
tabExpecter.unregisterListener();
contentExpecter.blockForEvent();
contentExpecter.unregisterListener();
mAsserter.ok(isTabPrivate(eventData), "Checking if the new tab opened from the context menu was a private tab", "The tab was a private tab");
mAsserter.ok(eventData.getBoolean("isPrivate"), "Checking if the new tab opened from the context menu was a private tab", "The tab was a private tab");
verifyTabCount(2);
// Open a normal tab to check later that it was registered in the Firefox Browser History
tabExpecter = mActions.expectGeckoEvent("Tab:Added");
tabExpecter = mActions.expectGlobalEvent(Actions.EventType.UI, "Tab:Added");
contentExpecter = mActions.expectGeckoEvent("Content:PageShow");
tabs.loadUrl(blank2Url, Tabs.LOADURL_NEW_TAB);
tabExpecter.blockForEvent();
@ -76,14 +76,4 @@ public class testPrivateBrowsing extends ContentContextMenuTest {
mAsserter.ok(!firefoxHistory.contains(blank1Url), "Check that the link opened in the private tab from the context menu was not saved", blank1Url + " was not added to history");
mAsserter.ok(firefoxHistory.contains(blank2Url), "Check that the link opened in the normal tab was saved", blank2Url + " was added to history");
}
private boolean isTabPrivate(String eventData) {
try {
JSONObject data = new JSONObject(eventData);
return data.getBoolean("isPrivate");
} catch (JSONException e) {
mAsserter.ok(false, "Error parsing the event data", e.toString());
return false;
}
}
}

View File

@ -5,13 +5,14 @@
package org.mozilla.gecko.tests;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.GeckoApp;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.NativeEventListener;
import org.mozilla.gecko.util.NativeJSObject;
import org.mozilla.gecko.util.GeckoBundle;
import static org.mozilla.gecko.tests.helpers.AssertionHelper.fFail;
public class testRuntimePermissionsAPI extends JavascriptTest implements NativeEventListener {
public class testRuntimePermissionsAPI extends JavascriptTest implements BundleEventListener {
public testRuntimePermissionsAPI() {
super("testRuntimePermissionsAPI.js");
}
@ -20,18 +21,18 @@ public class testRuntimePermissionsAPI extends JavascriptTest implements NativeE
public void setUp() throws Exception {
super.setUp();
EventDispatcher.getInstance().registerGeckoThreadListener(this, "RuntimePermissions:Prompt");
GeckoApp.getEventDispatcher().registerUiThreadListener(this, "RuntimePermissions:Prompt");
}
@Override
public void tearDown() throws Exception {
super.tearDown();
EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "RuntimePermissions:Prompt");
GeckoApp.getEventDispatcher().unregisterUiThreadListener(this, "RuntimePermissions:Prompt");
}
@Override
public void handleMessage(String event, NativeJSObject message, EventCallback callback) {
public void handleMessage(String event, GeckoBundle message, EventCallback callback) {
mAsserter.is(event, "RuntimePermissions:Prompt", "Received RuntimePermissions:Prompt event");
try {

View File

@ -7,11 +7,11 @@ package org.mozilla.gecko.tests;
import static org.mozilla.gecko.tests.helpers.AssertionHelper.fFail;
import org.mozilla.gecko.EventDispatcher;
import org.mozilla.gecko.util.BundleEventListener;
import org.mozilla.gecko.util.EventCallback;
import org.mozilla.gecko.util.NativeEventListener;
import org.mozilla.gecko.util.NativeJSObject;
import org.mozilla.gecko.util.GeckoBundle;
public class testSnackbarAPI extends JavascriptTest implements NativeEventListener {
public class testSnackbarAPI extends JavascriptTest implements BundleEventListener {
// Snackbar.LENGTH_INDEFINITE: To avoid tests depending on the android design support library
private static final int SNACKBAR_LENGTH_INDEFINITE = -2;
@ -20,14 +20,19 @@ public class testSnackbarAPI extends JavascriptTest implements NativeEventListen
}
@Override
public void handleMessage(String event, NativeJSObject message, EventCallback callback) {
public void handleMessage(String event, GeckoBundle message, EventCallback callback) {
if ("Robocop:WaitOnUI".equals(event)) {
callback.sendSuccess(null);
return;
}
mAsserter.is(event, "Snackbar:Show", "Received Snackbar:Show event");
try {
mAsserter.is(message.getString("message"), "This is a Snackbar", "Snackbar message");
mAsserter.is(message.getInt("duration"), SNACKBAR_LENGTH_INDEFINITE, "Snackbar duration");
NativeJSObject action = message.getObject("action");
GeckoBundle action = message.getBundle("action");
mAsserter.is(action.getString("label"), "Click me", "Snackbar action label");
@ -40,13 +45,15 @@ public class testSnackbarAPI extends JavascriptTest implements NativeEventListen
public void setUp() throws Exception {
super.setUp();
EventDispatcher.getInstance().registerGeckoThreadListener(this, "Snackbar:Show");
EventDispatcher.getInstance().registerUiThreadListener(this, "Snackbar:Show");
EventDispatcher.getInstance().registerUiThreadListener(this, "Robocop:WaitOnUI");
}
@Override
public void tearDown() throws Exception {
super.tearDown();
EventDispatcher.getInstance().unregisterGeckoThreadListener(this, "Snackbar:Show");
EventDispatcher.getInstance().unregisterUiThreadListener(this, "Snackbar:Show");
EventDispatcher.getInstance().unregisterUiThreadListener(this, "Robocop:WaitOnUI");
}
}

View File

@ -8,6 +8,7 @@ const { classes: Cc, interfaces: Ci, utils: Cu } = Components;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "Snackbars", "resource://gre/modules/Snackbars.jsm");
XPCOMUtils.defineLazyModuleGetter(this, "EventDispatcher", "resource://gre/modules/Messaging.jsm");
add_task(function* test_snackbar_api() {
Snackbars.show("This is a Snackbar", Snackbars.LENGTH_INDEFINITE, {
@ -16,6 +17,10 @@ add_task(function* test_snackbar_api() {
callback: function () {}
}
});
yield EventDispatcher.instance.sendRequestForResult({
type: "Robocop:WaitOnUI"
});
});
run_next_test();

View File

@ -234,7 +234,7 @@ var Settings = {
el.addEventListener("click", function() {
if (AppConstants.platform == "android") {
Cu.import("resource://gre/modules/Messaging.jsm");
Messaging.sendRequest({
EventDispatcher.instance.sendRequest({
type: "Settings:Show",
resource: "preferences_privacy",
});