Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 699ed800 authored by George Mount's avatar George Mount Committed by Android (Google) Code Review
Browse files

Merge "Add cut and paste to ContentEditable."

parents 5a89672f 3d09531f
Loading
Loading
Loading
Loading
+42 −11
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package android.webkit;

import android.app.SearchManager;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.provider.Browser;
@@ -27,11 +28,16 @@ import android.view.MenuItem;
class SelectActionModeCallback implements ActionMode.Callback {
    private WebView mWebView;
    private ActionMode mActionMode;
    private boolean mIsTextSelected = true;

    void setWebView(WebView webView) {
        mWebView = webView;
    }

    void setTextSelected(boolean isTextSelected) {
        mIsTextSelected = isTextSelected;
    }

    void finish() {
        // It is possible that onCreateActionMode was never called, in the case
        // where there is no ActionBar, for example.
@@ -52,17 +58,25 @@ class SelectActionModeCallback implements ActionMode.Callback {
        mode.setTitle(allowText ?
                context.getString(com.android.internal.R.string.textSelectionCABTitle) : null);

        if (!mode.isUiFocusable()) {
        // If the action mode UI we're running in isn't capable of taking window focus
        // the user won't be able to type into the find on page UI. Disable this functionality.
        // (Note that this should only happen in floating dialog windows.)
        // This can be removed once we can handle multiple focusable windows at a time
        // in a better way.
            final MenuItem findOnPageItem = menu.findItem(com.android.internal.R.id.find);
            if (findOnPageItem != null) {
                findOnPageItem.setVisible(false);
            }
        }
        ClipboardManager cm = (ClipboardManager)(context
                .getSystemService(Context.CLIPBOARD_SERVICE));
        boolean isFocusable = mode.isUiFocusable();
        boolean isEditable = mWebView.focusCandidateIsEditableText();
        boolean canPaste = isEditable && cm.hasPrimaryClip() && isFocusable;
        boolean canFind = !isEditable && isFocusable;
        boolean canCut = isEditable && mIsTextSelected && isFocusable;
        boolean canCopy = mIsTextSelected;
        boolean canWebSearch = mIsTextSelected;
        setMenuVisibility(menu, canFind, com.android.internal.R.id.find);
        setMenuVisibility(menu, canPaste, com.android.internal.R.id.paste);
        setMenuVisibility(menu, canCut, com.android.internal.R.id.cut);
        setMenuVisibility(menu, canCopy, com.android.internal.R.id.copy);
        setMenuVisibility(menu, canWebSearch, com.android.internal.R.id.websearch);
        mActionMode = mode;
        return true;
    }
@@ -75,11 +89,21 @@ class SelectActionModeCallback implements ActionMode.Callback {
    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        switch(item.getItemId()) {
            case android.R.id.cut:
                mWebView.cutSelection();
                mode.finish();
                break;

            case android.R.id.copy:
                mWebView.copySelection();
                mode.finish();
                break;

            case android.R.id.paste:
                mWebView.pasteFromClipboard();
                mode.finish();
                break;

            case com.android.internal.R.id.share:
                String selection = mWebView.getSelection();
                Browser.sendString(mWebView.getContext(), selection);
@@ -113,4 +137,11 @@ class SelectActionModeCallback implements ActionMode.Callback {
    public void onDestroyActionMode(ActionMode mode) {
        mWebView.selectionDone();
    }

    private void setMenuVisibility(Menu menu, boolean visible, int resourceId) {
        final MenuItem item = menu.findItem(resourceId);
        if (item != null) {
            item.setVisible(visible);
        }
    }
}
+79 −0
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import android.annotation.Widget;
import android.app.ActivityManager;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.ComponentCallbacks2;
import android.content.Context;
@@ -797,6 +798,8 @@ public class WebView extends AbsoluteLayout
    static final int UPDATE_ZOOM_DENSITY                = 139;
    static final int EXIT_FULLSCREEN_VIDEO              = 140;

    static final int COPY_TO_CLIPBOARD                  = 141;

    private static final int FIRST_PACKAGE_MSG_ID = SCROLL_TO_MSG_ID;
    private static final int LAST_PACKAGE_MSG_ID = HIT_TEST_RESULT;

@@ -4520,6 +4523,11 @@ public class WebView extends AbsoluteLayout
        final boolean isSelecting = selectText();
        if (isSelecting) {
            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
        } else if (focusCandidateIsEditableText()) {
            mSelectCallback = new SelectActionModeCallback();
            mSelectCallback.setWebView(this);
            mSelectCallback.setTextSelected(false);
            startActionMode(mSelectCallback);
        }
        return isSelecting;
    }
@@ -5729,11 +5737,48 @@ public class WebView extends AbsoluteLayout
            ClipboardManager cm = (ClipboardManager)getContext()
                    .getSystemService(Context.CLIPBOARD_SERVICE);
            cm.setText(selection);
            int[] handles = new int[4];
            nativeGetSelectionHandles(mNativeClass, handles);
            mWebViewCore.sendMessage(EventHub.COPY_TEXT, handles);
        }
        invalidate(); // remove selection region and pointer
        return copiedSomething;
    }

    /**
     * Cut the selected text into the clipboard
     *
     * @hide This is an implementation detail
     */
    public void cutSelection() {
        copySelection();
        int[] handles = new int[4];
        nativeGetSelectionHandles(mNativeClass, handles);
        mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles);
    }

    /**
     * Paste text from the clipboard to the cursor position.
     *
     * @hide This is an implementation detail
     */
    public void pasteFromClipboard() {
        ClipboardManager cm = (ClipboardManager)getContext()
                .getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clipData = cm.getPrimaryClip();
        if (clipData != null) {
            ClipData.Item clipItem = clipData.getItemAt(0);
            CharSequence pasteText = clipItem.getText();
            if (pasteText != null) {
                int[] handles = new int[4];
                nativeGetSelectionHandles(mNativeClass, handles);
                mWebViewCore.sendMessage(EventHub.DELETE_TEXT, handles);
                mWebViewCore.sendMessage(EventHub.INSERT_TEXT,
                        pasteText.toString());
            }
        }
    }

    /**
     * @hide This is an implementation detail.
     */
@@ -8914,6 +8959,10 @@ public class WebView extends AbsoluteLayout
                    nativeSelectAt(msg.arg1, msg.arg2);
                    break;

                case COPY_TO_CLIPBOARD:
                    copyToClipboard((String) msg.obj);
                    break;

                default:
                    super.handleMessage(msg);
                    break;
@@ -9591,6 +9640,18 @@ public class WebView extends AbsoluteLayout
        mCurrentTouchInterval = interval;
    }

    /**
     * Copy text into the clipboard. This is called indirectly from
     * WebViewCore.
     * @param text The text to put into the clipboard.
     */
    private void copyToClipboard(String text) {
        ClipboardManager cm = (ClipboardManager)getContext()
                .getSystemService(Context.CLIPBOARD_SERVICE);
        ClipData clip = ClipData.newPlainText(getTitle(), text);
        cm.setPrimaryClip(clip);
    }

    /**
     *  Update our cache with updatedText.
     *  @param updatedText  The new text to put in our cache.
@@ -9677,6 +9738,23 @@ public class WebView extends AbsoluteLayout
        return nativeTileProfilingGetFloat(frame, tile, key);
    }

    /**
     * Checks the focused content for an editable text field. This can be
     * text input or ContentEditable.
     * @return true if the focused item is an editable text field.
     */
    boolean focusCandidateIsEditableText() {
        boolean isEditable = false;
        // TODO: reverse sDisableNavcache so that its name is positive
        boolean isNavcacheEnabled = !sDisableNavcache;
        if (isNavcacheEnabled) {
            isEditable = nativeFocusCandidateIsEditableText(mNativeClass);
        } else if (mFocusedNode != null) {
            isEditable = mFocusedNode.mEditable;
        }
        return isEditable;
    }

    private native int nativeCacheHitFramePointer();
    private native boolean  nativeCacheHitIsPlugin();
    private native Rect nativeCacheHitNodeBounds();
@@ -9722,6 +9800,7 @@ public class WebView extends AbsoluteLayout
    /* package */ native boolean  nativeFocusCandidateIsPassword();
    private native boolean  nativeFocusCandidateIsRtlText();
    private native boolean  nativeFocusCandidateIsTextInput();
    private native boolean nativeFocusCandidateIsEditableText(int nativeClass);
    /* package */ native int      nativeFocusCandidateMaxLength();
    /* package */ native boolean  nativeFocusCandidateIsAutoComplete();
    /* package */ native boolean  nativeFocusCandidateIsSpellcheck();
+57 −0
Original line number Diff line number Diff line
@@ -1126,6 +1126,11 @@ public final class WebViewCore {
        // private message ids
        private static final int DESTROY =     200;

        // for cut & paste
        static final int COPY_TEXT = 210;
        static final int DELETE_TEXT = 211;
        static final int INSERT_TEXT = 212;

        // Private handler for WebCore messages.
        private Handler mHandler;
        // Message queue for containing messages before the WebCore thread is
@@ -1737,6 +1742,27 @@ public final class WebViewCore {
                            Rect rect = (Rect) msg.obj;
                            nativeScrollLayer(mNativeClass, nativeLayer,
                                    rect);

                        case DELETE_TEXT: {
                            int[] handles = (int[]) msg.obj;
                            nativeDeleteText(mNativeClass, handles[0],
                                    handles[1], handles[2], handles[3]);
                            break;
                        }
                        case COPY_TEXT: {
                            int[] handles = (int[]) msg.obj;
                            String copiedText = nativeGetText(mNativeClass,
                                    handles[0], handles[1], handles[2],
                                    handles[3]);
                            if (copiedText != null) {
                                mWebView.mPrivateHandler.obtainMessage(WebView.COPY_TO_CLIPBOARD, copiedText)
                                        .sendToTarget();
                            }
                            break;
                        }
                        case INSERT_TEXT:
                            nativeInsertText(mNativeClass, (String) msg.obj);
                            break;
                    }
                }
            };
@@ -2976,4 +3002,35 @@ public final class WebViewCore {

    private native void nativeAutoFillForm(int nativeClass, int queryId);
    private native void nativeScrollLayer(int nativeClass, int layer, Rect rect);

    /**
     * Deletes editable text between two points. Note that the selection may
     * differ from the WebView's selection because the algorithms for selecting
     * text differs for non-LTR text. Any text that isn't editable will be
     * left unchanged.
     * @param nativeClass The pointer to the native class (mNativeClass)
     * @param startX The X position of the top-left selection point.
     * @param startY The Y position of the top-left selection point.
     * @param endX The X position of the bottom-right selection point.
     * @param endY The Y position of the bottom-right selection point.
     */
    private native void nativeDeleteText(int nativeClass,
            int startX, int startY, int endX, int endY);
    /**
     * Inserts text at the current cursor position. If the currently-focused
     * node does not have a cursor position then this function does nothing.
     */
    private native void nativeInsertText(int nativeClass, String text);
    /**
     * Gets the text between two selection points. Note that the selection
     * may differ from the WebView's selection because the algorithms for
     * selecting text differs for non-LTR text.
     * @param nativeClass The pointer to the native class (mNativeClass)
     * @param startX The X position of the top-left selection point.
     * @param startY The Y position of the top-left selection point.
     * @param endX The X position of the bottom-right selection point.
     * @param endY The Y position of the bottom-right selection point.
     */
    private native String nativeGetText(int nativeClass,
            int startX, int startY, int endX, int endY);
}
+10 −0
Original line number Diff line number Diff line
@@ -19,11 +19,21 @@
        android:title="@string/selectAll"
        android:showAsAction="ifRoom|withText"
        />
    <item android:id="@+id/cut"
        android:icon="?android:attr/actionModeCutDrawable"
        android:title="@string/cut"
        android:showAsAction="ifRoom|withText"
        />
    <item android:id="@+id/copy"
        android:icon="?android:attr/actionModeCopyDrawable"
        android:title="@string/copy"
        android:showAsAction="ifRoom|withText"
        />
    <item android:id="@+id/paste"
        android:icon="?android:attr/actionModePasteDrawable"
        android:title="@string/paste"
        android:showAsAction="ifRoom|withText"
        />
    <item android:id="@+id/share"
        android:icon="?android:attr/actionModeShareDrawable"
        android:title="@string/share"