Index: tests/regressiontests/admin_related_lookup_popup/__init__.py
===================================================================
Index: tests/regressiontests/admin_related_lookup_popup/tests.py
===================================================================
--- tests/regressiontests/admin_related_lookup_popup/tests.py	(revision 0)
+++ tests/regressiontests/admin_related_lookup_popup/tests.py	(revision 0)
@@ -0,0 +1,103 @@
+"""
+This file demonstrates writing tests using the unittest module. These will pass
+when you run "manage.py test".
+
+Replace this with more appropriate tests for your application.
+"""
+
+from django.contrib.admin.views.main import IS_POPUP_VAR, POPUP_CALLBACK_VAR
+from django.contrib.auth.models import User
+from django.core.urlresolvers import reverse
+from django.test import TestCase
+from models import *
+
+
+class login(object):
+    def __init__(self, testcase, user, password):
+        self.testcase = testcase
+        success = testcase.client.login(username=user, password=password)
+        self.testcase.assertTrue(
+            success,
+            "login with username=%r, password=%r failed" % (user, password)
+        )
+
+    def __enter__(self):
+        pass
+
+    def __exit__(self, *args):
+        self.testcase.client.logout()
+
+
+class RelatedLookupPopupTest(TestCase):
+
+    def setUp(self):
+        User.objects.create_superuser('root', 'root@example.com', '123')
+        RelatedItem.objects.create(title='lorem')
+        RelatedItem.objects.create(title='ipsum')
+        RelatedItem.objects.create(title='dolorem')
+
+        AnotherRelatedItem.objects.create(title='lorem')
+        AnotherRelatedItem.objects.create(title='ipsum')
+        AnotherRelatedItem.objects.create(title='dolorem')
+        AnotherRelatedItem.objects.create(title='lorem')
+        AnotherRelatedItem.objects.create(title='ipsum')
+
+
+    def test_choose_from_changelist(self):
+        with login(self, 'root', '123'):
+            callbacks = {
+                'dismissRelatedItemLookupPopup': (reverse('admin:admin_related_lookup_popup_relateditem_changelist'), 3),
+                'dismissAnoterRelatedItemLookupPopup': (reverse('admin:admin_related_lookup_popup_anotherrelateditem_changelist'), 5),
+            }
+
+            for callback_name, opts in callbacks.items():
+                changelist_url, count = opts
+
+                response = self.client.get("%s?_callback=%s" % (changelist_url, callback_name))
+                self.assertContains(response, '<a href="1/">', 1, 200)
+                self.assertContains(response, '<a href="2/">', 1, 200)
+                self.assertContains(response, '<a href="3/">', 1, 200)
+                self.assertContains(response, 'onclick="opener.%s(' % callback_name, 0, 200)
+
+                response = self.client.get("%s?%s=1&_callback=%s" % \
+                                           (changelist_url, IS_POPUP_VAR, callback_name))
+                self.assertContains(response, '<a href="1/">', 0, 200)
+                self.assertContains(response, '<a href="2/">', 0, 200)
+                self.assertContains(response, '<a href="3/">', 0, 200)
+                self.assertContains(response, 'onclick="opener.%s(' % callback_name, count, 200)
+
+
+    def test_add_new_related(self):
+        with login(self, 'root', '123'):
+            callback_name = 'dismissRelatedItemLookupPopup'
+            add_url = reverse('admin:admin_related_lookup_popup_relateditem_add')
+
+            # check correct rendering form
+            response = self.client.get("%s?_popup=1&_callback=%s" % (add_url, callback_name))
+            self.assertEqual(response.status_code, 200)
+            self.assertContains(response, '<input type="hidden" name="_popup" value="1" />', 1, 200)
+            self.assertContains(response, '<input type="hidden" name="_callback" value="%s" />' % callback_name, 1, 200)
+
+            # check correct posting form
+            response = self.client.post(add_url, {
+                'title': 'zxc',
+                '_popup': 1,
+                '_callback': callback_name,
+            })
+            self.assertEqual(response.status_code, 200)
+            self.assertContains(response, '<script')
+            self.assertContains(response, callback_name)
+
+
+    def test_choose_new_from_changelist(self):
+        with login(self, 'root', '123'):
+            callback_name = 'dismissRelatedItemLookupPopup'
+            changelist_url = reverse('admin:admin_related_lookup_popup_relateditem_changelist')
+
+            response = self.client.get("%s?%s=1" % (changelist_url, IS_POPUP_VAR))
+            self.assertContains(response, '"add/?_popup=1"', 1, 200)
+
+            response = self.client.get("%s?%s=1&%s=%s" % \
+                        (changelist_url, IS_POPUP_VAR, POPUP_CALLBACK_VAR, callback_name))
+            self.assertContains(response, '"add/?_popup=1&_callback=%s"' % callback_name, 1, 200)
+
Index: tests/regressiontests/admin_related_lookup_popup/models.py
===================================================================
--- tests/regressiontests/admin_related_lookup_popup/models.py	(revision 0)
+++ tests/regressiontests/admin_related_lookup_popup/models.py	(revision 0)
@@ -0,0 +1,22 @@
+from django.db import models
+from django.contrib import admin
+
+
+__all__ = ['RelatedItem', 'AnotherRelatedItem']
+
+
+class RelatedItem(models.Model):
+    title = models.CharField(max_length=25, blank=True, null=True)
+
+    def __unicode__(self):
+        return self.title
+
+class AnotherRelatedItem(models.Model):
+    title = models.CharField(max_length=15, blank=True, null=True)
+
+    def __unicode__(self):
+        return self.title
+
+
+admin.site.register(RelatedItem, None)
+admin.site.register(AnotherRelatedItem, None)
Index: django/contrib/admin/options.py
===================================================================
--- django/contrib/admin/options.py	(revision 16402)
+++ django/contrib/admin/options.py	(working copy)
@@ -742,12 +742,15 @@
             self.message_user(request, msg + ' ' + _("You may edit it again below."))
             if "_popup" in request.POST:
                 post_url_continue += "?_popup=1"
+                if '_callback' in request.REQUEST:
+                    post_url_continue += '&_callback=%s' % request.REQUEST.get('_callback')
             return HttpResponseRedirect(post_url_continue % pk_value)
 
         if "_popup" in request.POST:
-            return HttpResponse('<script type="text/javascript">opener.dismissAddAnotherPopup(window, "%s", "%s");</script>' % \
+            action = request.REQUEST.get('_callback', 'dismissAddAnotherPopup')
+            return HttpResponse('<script type="text/javascript">opener.%s(window, "%s", "%s");</script>' % \
                 # escape() calls force_unicode.
-                (escape(pk_value), escapejs(obj)))
+                (action, escape(pk_value), escapejs(obj)))
         elif "_addanother" in request.POST:
             self.message_user(request, msg + ' ' + (_("You may add another %s below.") % force_unicode(opts.verbose_name)))
             return HttpResponseRedirect(request.path)
@@ -957,6 +960,8 @@
             'root_path': self.admin_site.root_path,
             'app_label': opts.app_label,
         }
+        if '_callback' in request.REQUEST:
+            context.update({'popup_callback': request.REQUEST.get('_callback')})
         context.update(extra_context or {})
         return self.render_change_form(request, context, form_url=form_url, add=True)
 
@@ -1187,6 +1192,7 @@
             'selection_note_all': selection_note_all % {'total_count': cl.result_count},
             'title': cl.title,
             'is_popup': cl.is_popup,
+            'popup_callback': cl.popup_callback,
             'cl': cl,
             'media': media,
             'has_add_permission': self.has_add_permission(request),
Index: django/contrib/admin/templatetags/admin_list.py
===================================================================
--- django/contrib/admin/templatetags/admin_list.py	(revision 16402)
+++ django/contrib/admin/templatetags/admin_list.py	(working copy)
@@ -216,7 +216,7 @@
             value = result.serializable_value(attr)
             result_id = repr(force_unicode(value))[1:]
             yield mark_safe(u'<%s%s><a href="%s"%s>%s</a></%s>' % \
-                (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.dismissRelatedLookupPopup(window, %s); return false;"' % result_id or ''), conditional_escape(result_repr), table_tag))
+                (table_tag, row_class, url, (cl.is_popup and ' onclick="opener.%s(window, %s); return false;"' % (cl.get_popup_callback(), result_id) or ''), conditional_escape(result_repr), table_tag))
         else:
             # By default the fields come from ModelAdmin.list_editable, but if we pull
             # the fields out of the form instead of list_editable custom admins
Index: django/contrib/admin/views/main.py
===================================================================
--- django/contrib/admin/views/main.py	(revision 16402)
+++ django/contrib/admin/views/main.py	(working copy)
@@ -24,10 +24,14 @@
 SEARCH_VAR = 'q'
 TO_FIELD_VAR = 't'
 IS_POPUP_VAR = 'pop'
+POPUP_CALLBACK_VAR = '_callback'
+POPUP_CALLBACK_DEFAULT = 'dismissRelatedLookupPopup'
 ERROR_FLAG = 'e'
 
 IGNORED_PARAMS = (
-    ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, IS_POPUP_VAR, TO_FIELD_VAR)
+    ALL_VAR, ORDER_VAR, ORDER_TYPE_VAR, SEARCH_VAR, \
+    IS_POPUP_VAR, TO_FIELD_VAR, POPUP_CALLBACK_VAR
+)
 
 # Text to display within change-list table cells if the value is blank.
 EMPTY_CHANGELIST_VALUE = ugettext_lazy('(None)')
@@ -65,6 +69,7 @@
             self.page_num = 0
         self.show_all = ALL_VAR in request.GET
         self.is_popup = IS_POPUP_VAR in request.GET
+        self.popup_callback = request.REQUEST.get(POPUP_CALLBACK_VAR)
         self.to_field = request.GET.get(TO_FIELD_VAR)
         self.params = dict(request.GET.items())
         if PAGE_VAR in self.params:
@@ -378,3 +383,8 @@
 
     def url_for_result(self, result):
         return "%s/" % quote(getattr(result, self.pk_attname))
+
+    def get_popup_callback(self):
+        if self.popup_callback is not None:
+            return self.popup_callback
+        return POPUP_CALLBACK_DEFAULT
Index: django/contrib/admin/templates/admin/change_list.html
===================================================================
--- django/contrib/admin/templates/admin/change_list.html	(revision 16402)
+++ django/contrib/admin/templates/admin/change_list.html	(working copy)
@@ -60,7 +60,7 @@
         <ul class="object-tools">
           {% block object-tools-items %}
             <li>
-              <a href="add/{% if is_popup %}?_popup=1{% endif %}" class="addlink">
+              <a href="add/{% if is_popup %}?_popup=1{% if popup_callback %}&_callback={{ popup_callback }}{% endif %}{% endif %}" class="addlink">
                 {% blocktrans with cl.opts.verbose_name as name %}Add {{ name }}{% endblocktrans %}
               </a>
             </li>
Index: django/contrib/admin/templates/admin/change_form.html
===================================================================
--- django/contrib/admin/templates/admin/change_form.html	(revision 16402)
+++ django/contrib/admin/templates/admin/change_form.html	(working copy)
@@ -36,7 +36,7 @@
 {% endblock %}
 <form {% if has_file_field %}enctype="multipart/form-data" {% endif %}action="{{ form_url }}" method="post" id="{{ opts.module_name }}_form">{% csrf_token %}{% block form_top %}{% endblock %}
 <div>
-{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% endif %}
+{% if is_popup %}<input type="hidden" name="_popup" value="1" />{% if popup_callback %}<input type="hidden" name="_callback" value="{{ popup_callback }}" />{% endif %}{% endif %}
 {% if save_on_top %}{% submit_row %}{% endif %}
 {% if errors %}
     <p class="errornote">
