public static GeckoEvent createIMESelectEvent(int start, int end) { GeckoEvent event = new GeckoEvent(IME_EVENT); event.mAction = IME_SET_SELECTION; event.mStart = start; event.mEnd = end; return event; }
/* Informs Gecko that the screen size has changed. */ private void sendResizeEventIfNecessary(boolean force) { DisplayMetrics metrics = mContext.getResources().getDisplayMetrics(); IntSize newScreenSize = new IntSize(metrics.widthPixels, metrics.heightPixels); IntSize newWindowSize = new IntSize(mView.getWidth(), mView.getHeight()); boolean screenSizeChanged = !mScreenSize.equals(newScreenSize); boolean windowSizeChanged = !mWindowSize.equals(newWindowSize); if (!force && !screenSizeChanged && !windowSizeChanged) { return; } mScreenSize = newScreenSize; mWindowSize = newWindowSize; if (screenSizeChanged) { Log.d(LOGTAG, "Screen-size changed to " + mScreenSize); } if (windowSizeChanged) { Log.d(LOGTAG, "Window-size changed to " + mWindowSize); } GeckoEvent event = GeckoEvent.createSizeChangedEvent( mWindowSize.width, mWindowSize.height, mScreenSize.width, mScreenSize.height); GeckoAppShell.sendEventToGecko(event); GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Window:Resize", "")); }
public static GeckoEvent createScreenshotEvent( int tabId, int sx, int sy, int sw, int sh, int dx, int dy, int dw, int dh, int bw, int bh, int token, ByteBuffer buffer) { GeckoEvent event = new GeckoEvent(SCREENSHOT); event.mPoints = new Point[5]; event.mPoints[0] = new Point(sx, sy); event.mPoints[1] = new Point(sw, sh); event.mPoints[2] = new Point(dx, dy); event.mPoints[3] = new Point(dw, dh); event.mPoints[4] = new Point(bw, bh); event.mMetaState = tabId; event.mFlags = token; event.mBuffer = buffer; return event; }
public static GeckoEvent createSizeChangedEvent(int w, int h, int screenw, int screenh) { GeckoEvent event = new GeckoEvent(SIZE_CHANGED); event.mPoints = new Point[2]; event.mPoints[0] = new Point(w, h); event.mPoints[1] = new Point(screenw, screenh); return event; }
public static GeckoEvent createIMECompositionEvent(int start, int end) { GeckoEvent event = new GeckoEvent(IME_EVENT); event.mAction = IME_UPDATE_COMPOSITION; event.mStart = start; event.mEnd = end; return event; }
// Run all queued methods private static void flushQueuedNativeCallsLocked(final State state) { int lastSkipped = -1; for (int i = 0; i < QUEUED_CALLS.size(); i++) { final QueuedCall call = QUEUED_CALLS.get(i); if (call == null) { // We already handled the call. continue; } if (!state.isAtLeast(call.state)) { // The call is not ready yet; skip it. lastSkipped = i; continue; } // Mark as handled. QUEUED_CALLS.set(i, null); if (call.method == null) { final GeckoEvent e = (GeckoEvent) call.target; GeckoAppShell.notifyGeckoOfEvent(e); e.recycle(); continue; } invokeMethod(call.method, call.target, call.args); } if (lastSkipped < 0) { // We're done here; release the memory QUEUED_CALLS.clear(); QUEUED_CALLS.trimToSize(); } else if (lastSkipped < QUEUED_CALLS.size() - 1) { // We skipped some; free up null entries at the end, // but keep all the previous entries for later. QUEUED_CALLS.subList(lastSkipped + 1, QUEUED_CALLS.size()).clear(); } }
/** Restore default search engines in Gecko and retrigger a search engine refresh. */ protected void restoreDefaultSearchEngines() { GeckoAppShell.sendEventToGecko( GeckoEvent.createBroadcastEvent("SearchEngines:RestoreDefaults", null)); // Send message to Gecko to get engines. SearchPreferenceCategory listens for the response. GeckoAppShell.sendEventToGecko( GeckoEvent.createBroadcastEvent("SearchEngines:GetVisible", null)); }
public static GeckoEvent createIMEReplaceEvent(int start, int end, String text) { GeckoEvent event = new GeckoEvent(IME_EVENT); event.mAction = IME_REPLACE_TEXT; event.mStart = start; event.mEnd = end; event.mCharacters = text; return event; }
private boolean processKeyDown(int keyCode, KeyEvent event, boolean isPreIme) { if (DEBUG) { Log.d( LOGTAG, "IME: processKeyDown(keyCode=" + keyCode + ", event=" + event + ", " + isPreIme + ")"); } clampSelection(); switch (keyCode) { case KeyEvent.KEYCODE_MENU: case KeyEvent.KEYCODE_BACK: case KeyEvent.KEYCODE_VOLUME_UP: case KeyEvent.KEYCODE_VOLUME_DOWN: case KeyEvent.KEYCODE_SEARCH: return false; case KeyEvent.KEYCODE_DEL: // See comments in GeckoInputConnection.onKeyDel if (onKeyDel()) { return true; } break; case KeyEvent.KEYCODE_ENTER: if ((event.getFlags() & KeyEvent.FLAG_EDITOR_ACTION) != 0 && mIMEActionHint.equalsIgnoreCase("next")) event = new KeyEvent(event.getAction(), KeyEvent.KEYCODE_TAB); break; default: break; } if (isPreIme && mIMEState != IME_STATE_DISABLED && (event.getMetaState() & KeyEvent.META_ALT_ON) != 0) // Let active IME process pre-IME key events return false; View view = GeckoApp.mAppContext.getLayerController().getView(); KeyListener keyListener = TextKeyListener.getInstance(); // KeyListener returns true if it handled the event for us. if (mIMEState == IME_STATE_DISABLED || keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DEL || keyCode == KeyEvent.KEYCODE_TAB || (event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 || !keyListener.onKeyDown(view, mEditable, keyCode, event)) { // Make sure selection in Gecko is up-to-date final Editable content = getEditable(); int a = Selection.getSelectionStart(content); int b = Selection.getSelectionEnd(content); GeckoAppShell.sendEventToGecko( GeckoEvent.createIMEEvent(GeckoEvent.IME_SET_SELECTION, a, b - a)); GeckoAppShell.sendEventToGecko(GeckoEvent.createKeyEvent(event)); } return true; }
private boolean processKeyUp(int keyCode, KeyEvent event, boolean isPreIme) { if (DEBUG) { Log.d( LOGTAG, "IME: processKeyUp(keyCode=" + keyCode + ", event=" + event + ", " + isPreIme + ")"); } switch (keyCode) { case KeyEvent.KEYCODE_BACK: case KeyEvent.KEYCODE_SEARCH: case KeyEvent.KEYCODE_MENU: return false; default: break; } if (isPreIme && mIMEState != IME_STATE_DISABLED && (event.getMetaState() & KeyEvent.META_ALT_ON) != 0) // Let active IME process pre-IME key events return false; View view = GeckoApp.mAppContext.getLayerController().getView(); KeyListener keyListener = TextKeyListener.getInstance(); if (mIMEState == IME_STATE_DISABLED || keyCode == KeyEvent.KEYCODE_ENTER || keyCode == KeyEvent.KEYCODE_DEL || (event.getFlags() & KeyEvent.FLAG_SOFT_KEYBOARD) != 0 || !keyListener.onKeyUp(view, mEditable, keyCode, event)) { GeckoAppShell.sendEventToGecko(GeckoEvent.createKeyEvent(event)); } return true; }
boolean scrollBy(PointF displacement) { if (!mOverridePanning) { return false; } if (!mOverrideScrollAck) { mOverrideScrollPending = true; mPendingDisplacement.x += displacement.x; mPendingDisplacement.y += displacement.y; return true; } JSONObject json = new JSONObject(); try { json.put("x", displacement.x); json.put("y", displacement.y); } catch (JSONException e) { Log.e(LOGTAG, "Error forming subwindow scroll message: ", e); } GeckoAppShell.sendEventToGecko( GeckoEvent.createBroadcastEvent(MESSAGE_SCROLL, json.toString())); mOverrideScrollAck = false; mOverrideScrollPending = false; // clear the |mPendingDisplacement| after serializing |displacement| to // JSON because they might be the same object mPendingDisplacement.x = 0; mPendingDisplacement.y = 0; return true; }
/* * DoorHanger.OnButtonClickListener implementation */ @Override public void onButtonClick(DoorHanger dh, String tag) { JSONObject response = new JSONObject(); try { response.put("callback", tag); CheckBox checkBox = dh.getCheckBox(); // If the checkbox is being used, pass its value if (checkBox != null) { response.put("checked", checkBox.isChecked()); } List<PromptInput> doorHangerInputs = dh.getInputs(); if (doorHangerInputs != null) { JSONObject inputs = new JSONObject(); for (PromptInput input : doorHangerInputs) { inputs.put(input.getId(), input.getValue()); } response.put("inputs", inputs); } } catch (JSONException e) { Log.e(LOGTAG, "Error creating onClick response", e); } GeckoEvent e = GeckoEvent.createBroadcastEvent("Doorhanger:Reply", response.toString()); GeckoAppShell.sendEventToGecko(e); removeDoorHanger(dh); updatePopup(); }
@Override public boolean setSelection(int start, int end) { GeckoAppShell.sendEventToGecko( GeckoEvent.createIMEEvent(GeckoEvent.IME_SET_SELECTION, start, end - start)); return super.setSelection(start, end); }
/** Send current network state and connection type as a GeckoEvent, to whomever is listening. */ private void sendNetworkStateToListeners() { final Context applicationContext = GeckoAppShell.getApplicationContext(); final GeckoEvent networkEvent = GeckoEvent.createNetworkEvent( currentConnectionType.value, currentConnectionType == ConnectionType.WIFI, wifiDhcpGatewayAddress(applicationContext), currentConnectionSubtype.value); final GeckoEvent networkLinkChangeValueEvent = GeckoEvent.createNetworkLinkChangeEvent(currentNetworkStatus.value); final GeckoEvent networkLinkChangeNotificationEvent = GeckoEvent.createNetworkLinkChangeEvent(LINK_DATA_CHANGED); GeckoAppShell.sendEventToGecko(networkEvent); GeckoAppShell.sendEventToGecko(networkLinkChangeValueEvent); GeckoAppShell.sendEventToGecko(networkLinkChangeNotificationEvent); }
public static GeckoEvent createViewportEvent( ImmutableViewportMetrics metrics, DisplayPortMetrics displayPort) { GeckoEvent event = new GeckoEvent(VIEWPORT); event.mCharacters = "Viewport:Change"; StringBuffer sb = new StringBuffer(256); sb.append("{ \"x\" : ") .append(metrics.viewportRectLeft) .append(", \"y\" : ") .append(metrics.viewportRectTop) .append(", \"zoom\" : ") .append(metrics.zoomFactor) .append(", \"displayPort\" :") .append(displayPort.toJSON()) .append('}'); event.mCharactersExtra = sb.toString(); return event; }
public AllPagesTab(Context context) { super(context); mSearchEngines = new ArrayList<SearchEngine>(); registerEventListener("SearchEngines:Data"); GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("SearchEngines:Get", null)); mHandler = new AllPagesHandler(); }
@Override public void requestZoomedViewRender() { if (stopUpdateView) { return; } // remove pending runnable ThreadUtils.removeCallbacksFromUiThread(requestRenderRunnable); // "requestZoomedViewRender" can be called very often by Gecko (endDrawing in LayerRender) // without // any thing changed in the zoomed area (useless calls from the "zoomed area" point of view). // "requestZoomedViewRender" can take time to re-render the zoomed view, it depends of the // complexity // of the html on this area. // To avoid to slow down the application, the 2 following cases are tested: // 1- Last render is still running, plan another render later. if (isRendering()) { // post a new runnable DELAY_BEFORE_NEXT_RENDER_REQUEST_MS later // We need to post with a delay to be sure that the last call to requestZoomedViewRender will // be done. // For a static html page WITHOUT any animation/video, there is a last call to endDrawing and // we need to make // the zoomed render on this last call. ThreadUtils.postDelayedToUiThread(requestRenderRunnable, DELAY_BEFORE_NEXT_RENDER_REQUEST_MS); return; } // 2- Current render occurs too early, plan another render later. if (renderFrequencyTooHigh()) { // post a new runnable DELAY_BEFORE_NEXT_RENDER_REQUEST_MS later // We need to post with a delay to be sure that the last call to requestZoomedViewRender will // be done. // For a page WITH animation/video, the animation/video can be stopped, and we need to make // the zoomed render on this last call. ThreadUtils.postDelayedToUiThread(requestRenderRunnable, DELAY_BEFORE_NEXT_RENDER_REQUEST_MS); return; } startTimeReRender = System.nanoTime(); // Allocate the buffer if it's the first call. // Change the buffer size if it's not the right size. updateBufferSize(); int tabId = Tabs.getInstance().getSelectedTab().getId(); ImmutableViewportMetrics metrics = layerView.getViewportMetrics(); PointF origin = metrics.getOrigin(); final int xPos = (int) origin.x + lastPosition.x; final int yPos = (int) origin.y + lastPosition.y; GeckoEvent e = GeckoEvent.createZoomedViewEvent( tabId, xPos, yPos, viewWidth, viewHeight, zoomFactor * metrics.zoomFactor, buffer); GeckoAppShell.sendEventToGecko(e); }
public void finishDialog(String aReturn) { mInputs = null; mButtons = null; mDialog = null; mSelected = null; mPromptQueue.offer(aReturn); // poke the Gecko thread in case it's waiting for new events GeckoAppShell.sendEventToGecko(GeckoEvent.createNoOpEvent()); }
private void connectToGecko() { GeckoThread.setLaunchState(GeckoThread.LaunchState.GeckoRunning); Tab selectedTab = Tabs.getInstance().getSelectedTab(); if (selectedTab != null) Tabs.getInstance().notifyListeners(selectedTab, Tabs.TabEvents.SELECTED); geckoConnected(); GeckoAppShell.setLayerClient(getLayerClientObject()); GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Viewport:Flush", null)); }
/** * Handle a confirmation response from the user. * * @param value String value to return to the browser context. */ public void confirmWithValue(String value) { JSONObject result = makeResult(RESULT_OK); try { result.put("textbox0", value); } catch (JSONException ex) { } GeckoAppShell.sendEventToGecko( GeckoEvent.createBroadcastEvent("Prompt:Reply", result.toString())); }
@Override public void onClick(View v) { switch (v.getId()) { case R.id.find_prev: GeckoAppShell.sendEventToGecko( GeckoEvent.createBroadcastEvent("FindInPage:Prev", mFindText.getText().toString())); getInputMethodManager(mFindText).hideSoftInputFromWindow(mFindText.getWindowToken(), 0); break; case R.id.find_next: GeckoAppShell.sendEventToGecko( GeckoEvent.createBroadcastEvent("FindInPage:Next", mFindText.getText().toString())); getInputMethodManager(mFindText).hideSoftInputFromWindow(mFindText.getWindowToken(), 0); break; case R.id.find_close: hide(); break; } }
public static GeckoEvent createIMERangeEvent( int start, int end, int rangeType, int rangeStyles, int rangeForeColor, int rangeBackColor) { GeckoEvent event = new GeckoEvent(IME_EVENT); event.mAction = IME_ADD_COMPOSITION_RANGE; event.mStart = start; event.mEnd = end; event.mRangeType = rangeType; event.mRangeStyles = rangeStyles; event.mRangeForeColor = rangeForeColor; event.mRangeBackColor = rangeBackColor; return event; }
/** Implementation of LayerView.Listener */ public void compositionResumeRequested(int width, int height) { // Asking Gecko to resume the compositor takes too long (see // https://bugzilla.mozilla.org/show_bug.cgi?id=735230#c23), so we // resume the compositor directly. We still need to inform Gecko about // the compositor resuming, so that Gecko knows that it can now draw. if (mCompositorCreated) { GeckoAppShell.scheduleResumeComposition(width, height); GeckoAppShell.sendEventToGecko(GeckoEvent.createCompositorResumeEvent()); } }
protected void onActivityResume(GeckoActivityStatus activity) { if (mPausedGecko) { GeckoAppShell.sendEventToGecko(GeckoEvent.createAppForegroundingEvent()); mPausedGecko = false; } GeckoConnectivityReceiver.getInstance().start(); GeckoNetworkManager.getInstance().start(); mInBackground = false; }
public void handleNotificationIntent(Intent i) { final Uri data = i.getData(); if (data == null) { Log.e(LOGTAG, "handleNotificationEvent: empty data"); return; } final String id = data.getQueryParameter(ID_ATTR); final String notificationType = data.getQueryParameter(EVENT_TYPE_ATTR); if (id == null || notificationType == null) { Log.e(LOGTAG, "handleNotificationEvent: invalid intent parameters"); return; } // In case the user swiped out the notification, we empty the id set. if (CLEARED_EVENT.equals(notificationType)) { mClearableNotifications.remove(id); // If Gecko isn't running, we throw away events where the notification was cancelled. // i.e. Don't bug the user if they're just closing a bunch of notifications. if (!GeckoThread.checkLaunchState(GeckoThread.LaunchState.GeckoRunning)) { return; } } JSONObject args = new JSONObject(); // The handler and cookie parameters are optional final String handler = data.getQueryParameter(HANDLER_ATTR); final String cookie = i.getStringExtra(COOKIE_ATTR); try { args.put(ID_ATTR, id); args.put(EVENT_TYPE_ATTR, notificationType); args.put(HANDLER_ATTR, handler); args.put(COOKIE_ATTR, cookie); if (BUTTON_EVENT.equals(notificationType)) { final String actionName = data.getQueryParameter(ACTION_ID_ATTR); args.put(ACTION_ID_ATTR, actionName); } Log.i(LOGTAG, "Send " + args.toString()); GeckoAppShell.sendEventToGecko( GeckoEvent.createBroadcastEvent("Notification:Event", args.toString())); } catch (JSONException e) { Log.e(LOGTAG, "Error building JSON notification arguments.", e); } // If the notification was clicked, we are closing it. This must be executed after // sending the event to js side because when the notification is canceled no event can be // handled. if (CLICK_EVENT.equals(notificationType) && !i.getBooleanExtra(ONGOING_ATTR, false)) { hideNotification(id, handler, cookie); } }
public static void addPendingEvent(final GeckoEvent e) { synchronized (QUEUED_CALLS) { if (isRunning()) { // We may just have switched to running state. GeckoAppShell.notifyGeckoOfEvent(e); e.recycle(); } else { QUEUED_CALLS.add(new QueuedCall(null, e, null, State.RUNNING)); } } }
@Override public boolean onActionItemClicked(ActionModeCompat mode, MenuItem item) { try { final JSONObject obj = mItems.getJSONObject(item.getItemId()); GeckoAppShell.sendEventToGecko( GeckoEvent.createBroadcastEvent("TextSelection:Action", obj.optString("id"))); return true; } catch (Exception ex) { Log.i(LOGTAG, "Exception calling action", ex); } return false; }
/** * Load a URL resource into the Browser. * * @param url The URL string. */ public void loadUrl(String url) { JSONObject args = new JSONObject(); try { args.put("url", url); args.put("parentId", -1); args.put("newTab", false); args.put("tabID", mId); } catch (Exception e) { Log.w(LOGTAG, "Error building JSON arguments for loadUrl.", e); } GeckoAppShell.sendEventToGecko(GeckoEvent.createBroadcastEvent("Tab:Load", args.toString())); }
private void updateNetworkType() { NetworkType previousNetworkType = mNetworkType; mNetworkType = getNetworkType(); if (mNetworkType == previousNetworkType || !mShouldNotify) { return; } GeckoAppShell.sendEventToGecko( GeckoEvent.createNetworkEvent( getNetworkSpeed(mNetworkType), isNetworkUsuallyMetered(mNetworkType))); }
/** Implementation of LayerView.Listener */ public void compositionPauseRequested() { // We need to coordinate with Gecko when pausing composition, to ensure // that Gecko never executes a draw event while the compositor is paused. // This is sent synchronously to make sure that we don't attempt to use // any outstanding Surfaces after we call this (such as from a // surfaceDestroyed notification), and to make sure that any in-flight // Gecko draw events have been processed. When this returns, composition is // definitely paused -- it'll synchronize with the Gecko event loop, which // in turn will synchronize with the compositor thread. if (mCompositorCreated) { GeckoAppShell.sendEventToGeckoSync(GeckoEvent.createCompositorPauseEvent()); } }