Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(andorid) geckoview use browser.runtime.connectNative("browser") is work when launch app, but it is not work when click to use port.postMessage(port undefine) #136

Open
xiaotianguys opened this issue May 25, 2020 · 24 comments

Comments

@xiaotianguys
Copy link

test.html

<!DOCTYPE html>
<html>
<head>
    <title>Javascript中点击事件方法二</title>
    <link rel="manifest" href="/manifest.webmanifest">
    <script src="../api.js"></script>
</head>
<body>
<button id="btn">click</button>
<script type="text/javascript">
		var btn = document.getElementById("btn");
		btn.addEventListener('click',function(){
			console.log("test click event ... ");
			sendMessageToNativeApplication("Message from test html ... ");
		},false)
	</script>
</body>
</html>

api.js

const port = browser.runtime.connectNative("browser");
port.onMessage.addListener(response => {
  port.postMessage(`Received: ${JSON.stringify(response)}`);
});

function sendMessageToNativeApplication(message){
  if(port){
   port.postMessage(message);
  }
}
sendMessageToNativeApplication("Hello from WebExtension!");

// 通过 message 向 app 发送数据,仅支持字符串类型参数
function postMessage(message) {
  console.log('test receive message: ', message);
  if (window && window.ReactNativeWebView && typeof message === 'string') {
    window.ReactNativeWebView.postMessage(message);
  }
   sendMessageToNativeApplication("Message from Api.js file ... ");
}
postMessage();

Result Log:

2020-05-25 ? D/SystemUI.DriveMode: drive mode handleUpdateState
2020-05-25 ? D/GeckoViewModule: dispatch GeckoView:LoadUri, data={"uri":"resource://android/assets/bundle/html/test.html","flags":0}
2020-05-25 ? D/GeckoViewNavigation: onEvent: event=GeckoView:LoadUri, data={"uri":"resource://android/assets/bundle/html/test.html","flags":0}
2020-05-25 com.example.geckoviewdemo:tab0 D/GeckoViewWebBrowserChrome[C]: shouldLoadURI resource://android/assets/bundle/html/test.html
2020-05-25 com.example.geckoviewdemo I/test MessageDelegate: onConnect prot = org.mozilla.geckoview.WebExtension$Port@842647c
2020-05-25 com.example.geckoviewdemo I/test PortDelegate: Received message from extension: Hello from WebExtension!
2020-05-25 com.example.geckoviewdemo I/test PortDelegate: Received message from extension: Message from Api.js file ...      
---------    launch after click btn button( invoke sendMessageToNativeApplication method ), no response and no message send  --------------

Reference :

https://firefox-source-docs.mozilla.org/mobile/android/geckoview/consumer/web-extensions.html#connection-based-messaging

@xiaotianguys
Copy link
Author

@Amejia481

@xiaotianguys
Copy link
Author

Any good suggestions? and the error log when invoke postMessage method :

JavaScript Error: "ReferenceError: can't access lexical declaration 'port' before initialization"

but i do port like this in api.js :

const port = browser.runtime.connectNative("browser");

@Amejia481
Copy link
Contributor

it looks like you are not able to access browser.runtime.connectNative("browser") could you please post your manifest.json or is there any place where you have you code hosted? Or a host a minimal example app?

@Amejia481
Copy link
Contributor

I also helped with this sample web extension app, it could help you to see a full example and maybe spot a possible issue

@agi90
Copy link
Contributor

agi90 commented May 26, 2020

@xiaotianguys how are you loading text.html in the app? can you show us some of the java code?

@xiaotianguys
Copy link
Author

3Q for you response @agi90 yes, i do like this:

session.loadUri("resource://android/assets/bundle/html/test.html");

MainActivity.java Code

package com.example.geckoviewdemo;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;

import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;

import org.json.JSONException;
import org.json.JSONObject;
import org.mozilla.geckoview.GeckoResult;
import org.mozilla.geckoview.GeckoRuntime;
import org.mozilla.geckoview.GeckoSession;
import org.mozilla.geckoview.GeckoView;
import org.mozilla.geckoview.WebExtension;

public class MainActivity extends AppCompatActivity {

    private static final String EXTENSION_ID = "messaging@example.com";
    private static final String EXTENSION_VERSION = "1.0";
    private static final String EXTENSION_LOCATION = "resource://android/assets/bundle/messaging/";
    private WebExtension.Port mPort;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        GeckoView view = findViewById(R.id.geckoview);
        GeckoSession session = new GeckoSession();
        GeckoRuntime sRuntime = GeckoRuntime.create(this);

        WebExtension.PortDelegate portDelegate = new WebExtension.PortDelegate() {
            @Override
            public void onPortMessage(final @NonNull Object message,
                                      final @NonNull WebExtension.Port port) {
                Log.i("test PortDelegate", "Received message from extension: "
                        + message);
            }

            @Override
            public void onDisconnect(final @NonNull WebExtension.Port port) {
                // This port is not usable anymore.
                if (port == mPort) {
                    mPort = null;
                }
            }
        };

        WebExtension.MessageDelegate messageDelegate = new WebExtension.MessageDelegate() {
            @Override
            @Nullable
            public void onConnect(final @NonNull WebExtension.Port port) {
                Log.i("test MessageDelegate", "onConnect prot = " + port);
                mPort = port;
                mPort.setDelegate(portDelegate);
            }
        };

        sRuntime.getWebExtensionController()
                .installBuiltIn("resource://android/assets/bundle/")
                .accept(
                        // Register message delegate for background script
                        extension -> extension.setMessageDelegate(messageDelegate, "browser"),
                        e -> Log.i("test MessageDelegate", "Error registering WebExtension", e)
                );
        session.open(sRuntime);
        view.setSession(session);
        session.loadUri("resource://android/assets/bundle/html/test.html");
    }

    @Override
    public boolean onKeyLongPress(int keyCode, KeyEvent event) {
        Log.i("test MessageDelegate", "onKeyLongPress ... mPort = " + mPort);
        if (mPort == null) {
            // No extension registered yet, let's ignore this message
            return false;
        }

        JSONObject message = new JSONObject();
        try {
            message.put("keyCode", "wucan");
            message.put("event", "handsome");
        } catch (JSONException ex) {
            throw new RuntimeException(ex);
        }
        mPort.postMessage(message);
        return true;
    }
}

@xiaotianguys
Copy link
Author

3Q for you response @Amejia481 ;
i have try as say like https://github.com/ankitrawat46/GeckoJavascript to test;
use browser.runtime.sendNativeMessage(message) instead of port.postMessage(message) in function sendMessageToNativeApplication(message) ;
the final result is the same (send or receiver message success when launch app, but it is stuck when trigger function sendMessageToNativeApplication(message) by btn button

@xiaotianguys
Copy link
Author

https://github.com/ankitrawat46/GeckoJavascript is work fine in my demo

// Establish connection with app
let port = browser.runtime.connectNative("browser");
port.onMessage.addListener(response => {
    // Let's just echo the message back
    port.postMessage(`Received: ${JSON.stringify(response)}`);
});
port.postMessage("Hello from WebExtension!");

what can i do for use port.postMessage() in other js file @Amejia481

@agi90
Copy link
Contributor

agi90 commented May 27, 2020

@xiaotianguys ok I see the problem, you can only use extensions APIs like browser.runtime.sendNativeMessage in an extension page, so what you want is to call loadUri with the extension URL, like so:

 session.loadUri(extension.metaData.baseUrl + "html/test.html");

@Amejia481
Copy link
Contributor

I agree with @agi90. Something that can help to have a complete picture of what is happening is to use Remote debugging. It gives you access to all the dev tool that Firefox has, this way you can debug the website and the extension code end to end. To activate it you just have to use runtime.getSettings().setRemoteDebuggingEnabled(true);

@xiaotianguys
Copy link
Author

I do't understand what's differentsession.loadUri("resource://android/assets/bundle/html/test.html"); between session.loadUri(extension.metaData.baseUrl + "html/test.html");
or how to convert resource://android/assets/bundle/html/test.htm to extension.metaData.baseUrl + "html/test.html" @agi90

@xiaotianguys
Copy link
Author

@Amejia481 use firefox debug as picture:
WeChata60d0adde0a65d3482fa94b62d1310ec
and i find the same question issue https://bugzilla.mozilla.org/show_bug.cgi?id=1600981 (I do't find how did you solve it)

@xiaotianguys
Copy link
Author

ReferenceError: browser is not definedresource://android/assets/bundle/api.js:2:14
    <anonymous> resource://android/assets/bundle/api.js:2 

error code :

// Establish connection with app
const port = browser.runtime.connectNative("browser");

@xiaotianguys
Copy link
Author

No offense; @Amejia481 you sure is work fine ? https://github.com/ankitrawat46/GeckoJavascript ; I am clone and run project , app crash;
so I made the following modifications :
session.loadUri("https://www.lejuhub.com")
the result as picture:
WeChatc5cf1b5483175dcaf2f8be9087f26524

@Amejia481
Copy link
Contributor

No offense; @Amejia481 you sure is work fine ? https://github.com/ankitrawat46/GeckoJavascript ; I am clone and run project , app crash;
so I made the following modifications :
session.loadUri("https://www.lejuhub.com")
the result as picture:
WeChatc5cf1b5483175dcaf2f8be9087f26524

No problem :)
It's weird it was working for https://en.m.wikipedia.org/wiki/JavaScript, I will take a look later in the day!

@xiaotianguys
Copy link
Author

@Amejia481 how's the progress , Any information update ?

@Amejia481
Copy link
Contributor

@xiaotianguys sorry for the delay, the initial example was based on an old version of geckoview which API was deprecated. I cloned and updated the previous example using the new extension api. Hope it helps.

I added two version:

  1. Communication from the ext -> the app. It's based on this example

  2. Communication from the app -> the extension. It's based on this example

In version 1 you can see the extension interacting with the site web content as is changing the background of some items on Wikipedia :)
image

@xiaotianguys
Copy link
Author

3Q @Amejia481 ; #136 (comment) is same as official document description ;My problem is that it is stuck when use port.postMessage in js function, not demo can't work ;(like i said before my demo is work fine) so i can not send message when invoke button by myself

@Amejia481
Copy link
Contributor

As @agi90 mentioned on #136 (comment), this API do not exists on a normal website context, to use the web extension APIs you need to be in web extension context.

For this reason, he suggested to load the page using the web extension context:

Extension context -> extension.metaData.baseUrl equals to moz-extension://12399f9f-dc0b-41b4-8b47-a5645a746a63/

VS

Website context -> resource://android/assets/bundle/

Another alternative is to set an options_ui in the manifest that can be accessed via ext.metaData!!.optionsPageUrl!!

@xiaotianguys
Copy link
Author

it's a pity that can not work too; @Amejia481 can you show me a demo?(about use port.postMessage send message when invoke button by ourself) looking forward to your update

@xiaotianguys
Copy link
Author

#136 (comment) it is work fine , and i have done ; but i do't know how to use port.postMessage , as like click the Button and send message @Amejia481

@Amejia481
Copy link
Contributor

To help you better could you tell us which is the end goal or use case that you are trying to achieve? This way we can steer you up through all the possible options.

If you prefer you can also could chat with the whole team using Matrix: gv-webext:mozilla.org chat room (How to connect), this way you can have a more diverse opinions.

@flaviolenz
Copy link

Is everybody struggling with the same thing ?

Could you, please, check issue #163

@God-SunCAT
Copy link

不要把它放OnCreate里!
` WebExtension.PortDelegate portDelegate = new WebExtension.PortDelegate() {
@OverRide
public void onPortMessage(final @nonnull Object message,
final @nonnull WebExtension.Port port) {
Log.i("test PortDelegate", "Received message from extension: "
+ message);
}

        @Override
        public void onDisconnect(final @NonNull WebExtension.Port port) {
            // This port is not usable anymore.
            if (port == mPort) {
                mPort = null;
            }
        }
    };

    WebExtension.MessageDelegate messageDelegate = new WebExtension.MessageDelegate() {
        @Override
        @Nullable
        public void onConnect(final @NonNull WebExtension.Port port) {
            Log.i("test MessageDelegate", "onConnect prot = " + port);
            mPort = port;
            mPort.setDelegate(portDelegate);
        }
    };`

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants