From: Nutchanon Wetchasit <Nutchanon.Wetchasit@gmail.com>
Subject: Make ExternalInterface use user-specified "this" instance in callback.
Bug: https://savannah.gnu.org/bugs/?37223

This completes the fix of bug #37223.

diff --git a/libcore/asobj/flash/external/ExternalInterface_as.cpp b/libcore/asobj/flash/external/ExternalInterface_as.cpp
index db21e1b..04c12bf 100644
--- a/libcore/asobj/flash/external/ExternalInterface_as.cpp
+++ b/libcore/asobj/flash/external/ExternalInterface_as.cpp
@@ -239,8 +239,6 @@ attachExternalInterfaceStaticInterface(as_object& o)
 }
 
 /// This adds a function that can be called from javascript.
-//
-/// TODO: addCallback takes three arguments; only two are handled here.
 as_value
 externalinterface_addCallback(const fn_call& fn)
 {
@@ -253,6 +251,7 @@ externalinterface_addCallback(const fn_call& fn)
 
     if (fn.nargs >= 3) {
         const as_value& name_as = fn.arg(0);
+        const as_value& instance_as = fn.arg(1);
         const as_value& method_as = fn.arg(2);
         std::string name = name_as.to_string();
 
@@ -262,8 +261,9 @@ externalinterface_addCallback(const fn_call& fn)
         }
 
         log_debug("adding callback %s", name);
+        as_object* asInstance = toObject(instance_as, getVM(fn));
         as_object* asCallback = toObject(method_as, getVM(fn));
-        mr.addExternalCallback(name, asCallback);
+        mr.addExternalCallback(name, asCallback, asInstance);
     } else {
         // Invalid addCallback call
         return as_value(false);
diff --git a/libcore/movie_root.cpp b/libcore/movie_root.cpp
index 434f122..8d35546 100644
--- a/libcore/movie_root.cpp
+++ b/libcore/movie_root.cpp
@@ -1824,18 +1824,25 @@ movie_root::findDropTarget(std::int32_t x, std::int32_t y,
 
 /// This should store a callback object in movie_root.
 void
-movie_root::addExternalCallback(const std::string& name, as_object* callback)
+movie_root::addExternalCallback(const std::string& name, as_object* callback,
+                                as_object* instance)
 {
-    // Store registered callback for later use by callExternalCallback()
+    // Store registered callback and instance reference for later use by
+    // callExternalCallback()
     if(_externalCallbackMethods.count(name)>0) {
         _externalCallbackMethods.erase(name);
+        _externalCallbackInstances.erase(name);
     }
     _externalCallbackMethods.insert(
         std::pair<std::string, as_object*>(name,callback)
     );
+    _externalCallbackInstances.insert(
+        std::pair<std::string, as_object*>(name,instance)
+    );
     
-    // Set callback as reachable (avoid garbage collection)
+    // Set callback and instance as reachable (avoid garbage collection)
     if (callback!=NULL) callback->setReachable();
+    if (instance!=NULL) instance->setReachable();
     
     // When an external callback is added, we have to notify the plugin
     // that this method is available.
@@ -1897,9 +1904,9 @@ movie_root::callExternalCallback(const std::string &name,
                  const std::vector<as_value> &fnargs)
 {
     ExternalCallbackMethods::iterator method_iterator;
-    MovieClip *mc = getLevel(0);
+    ExternalCallbackInstances::iterator instance_iterator;
     as_object *method;
-    as_object *instance = getObject(mc);
+    as_object *instance;
     fn_call::Args args;
     as_value val;
 
@@ -1910,6 +1917,13 @@ movie_root::callExternalCallback(const std::string &name,
     } else {
         method = method_iterator->second;
 
+        // Look up for Object instance to use as "this" in the callback
+        instance_iterator = _externalCallbackInstances.find(name);
+        if (instance_iterator == _externalCallbackInstances.end()) {
+            instance = as_value((as_object*)NULL).to_object(getVM());
+        }
+        instance = instance_iterator->second;
+
         // Populate function call arguments
         for (std::vector<as_value>::const_iterator args_iterator
                  = fnargs.begin();
diff --git a/libcore/movie_root.h b/libcore/movie_root.h
index 970c313..1fe163b 100644
--- a/libcore/movie_root.h
+++ b/libcore/movie_root.h
@@ -786,14 +786,20 @@ public:
     const RunResources& runResources() const { return _runResources; }
 
     typedef std::map<std::string, as_object*> ExternalCallbackMethods;
+    typedef std::map<std::string, as_object*> ExternalCallbackInstances;
     ExternalCallbackMethods _externalCallbackMethods;
+    ExternalCallbackInstances _externalCallbackInstances;
 
     /// Add an ExternalInterface callback object with an associated name.
     //
     /// @param name     Callback name, exposed to host container.
     /// @param callback ActionScript function to be invoked if the callback
     ///                 is called.
-    void addExternalCallback(const std::string& name, as_object* callback);
+    /// @param instance ActionScript Object to be used as "this" instance
+    ///                 inside the callback. ActionScript null value is
+    ///                 allowed.
+    void addExternalCallback(const std::string& name, as_object* callback,
+                             as_object* instance);
 
     bool processInvoke(ExternalInterface::invoke_t *);
 
