Repository: ipython/ipython

Issue:
Large html-notebooks hang on load on a slow machine
I've noticed that sometimes, large html notebooks fail to load.

It goes like this:
1. Click on the notebook name when at http://localhost:8888
2. The notebook page starts to open in the browser as usual, but 
3. The loading progress gets stuck at a point where there is a MathJax message "Processing math: 0%".
4. After that, the web browser (Chrome; same seemed to occur also on Firefox) tab is unresponsive and just consumes 100% CPU, and no progress seems to happen. Chrome offers to kill the tab process after a while (clicking "Continue" does not seem to help).

This does not happen always, sometimes the page opens after a while, but I had a notebook for which it seemed to occur in majority of the attempts.

I'm working with the HTML notebook with Google Chrome (15) running on a relatively slow machine (EEE 1000 laptop), and the notebook server + kernels running either on a server through SSH tunnel or on the same machine. When running the browser on a faster machine, this issue does not seem to happen. Ipython is from Git master (b9c427f25585d3). Problems seemed to occur regardless whether MathJax was installed locally or not.

Because of the MathJax message, I tried to check out what happens if I remove all calls to MathJax from Ipython. With this change, all notebooks seem to load reliably, and I haven't seen this issue to reappear, at least yet. Maybe there's some MathJax-related timing issue at page load time that can cause a deadlock?

I'll follow up if I learn something new. Also, suggestions on how to track this further could be useful.



Discussion:
@minrk, @ellisonbg: do you guys think we could make mathjax loading optional? There are a number of scenarios where we've seen it can cause problems, so it might be worth making it optional (if nothing else, if a user has forgotten to install it locally and they don't have internet, they're hosed and can't even open their files).  I'm making this a blocker so that at least we make a decision for 0.12, even if the decision is to for now leave it as-is... Just so it's high on our radar of things not to forget.

I'd vote for making it optional, on by default but easy to disable...

Yes, we can put the MatJax rendering code inside a function that first
checks to make sure that MathJax is loaded.  Don't know if that would
help this issue though, but it may.

On Wed, Nov 30, 2011 at 10:50 AM, Fernando Perez
reply@reply.github.com
wrote:

> @minrk, @ellisonbg: do you guys think we could make mathjax loading optional? There are a number of scenarios where we've seen it can cause problems, so it might be worth making it optional (if nothing else, if a user has forgotten to install it locally and they don't have internet, they're hosed and can't even open their files).  I'm making this a blocker so that at least we make a decision for 0.12, even if the decision is to for now leave it as-is... Just so it's high on our radar of things not to forget.
> 
> I'd vote for making it optional, on by default but easy to disable...
> 
> ---
> 
> Reply to this email directly or view it on GitHub:
> https://github.com/ipython/ipython/issues/1071#issuecomment-2964247

## 

Brian E. Granger
Cal Poly State University, San Luis Obispo
bgranger@calpoly.edu and ellisonbg@gmail.com

On Wed, Nov 30, 2011 at 11:07 AM, Brian E. Granger
reply@reply.github.com
wrote:

> Yes, we can put the MatJax rendering code inside a function that first
> checks to make sure that MathJax is loaded.  Don't know if that would
> help this issue though, but it may.

That would already be a big improvement, since it would prevent the
lockup problem when there's no mathjax to be found.  But I was
actually thinking of an option to disable it altogether, for users
where it may cause problems due to bringing the js engine in the
browser to its knees (that's what I suspect is happening to Pauli).

Since mathjax is so huge, it makes sense for it to be optional.  Should this be a server-level config?  Ideally, it would be per-browser (stored in a cookie), since you can connect to a single server from both fast and slow connections, making it server-level is probably a good idea.

I think our need to have a little control panel (must be on the front page, because it has to be prior to loading a notebook) and mechanism for storing some config browser-side is getting severe.

On Wed, Nov 30, 2011 at 1:37 PM, Min RK
reply@reply.github.com
wrote:

> Since mathjax is so huge, it makes sense for it to be optional.  Should this be a server-level config?  Ideally, it would be per-browser (stored in a cookie), since you can connect to a single server from both fast and slow connections, making it server-level is probably a good idea.

Agreed.

> I think our need to have a little control panel (must be on the front page, because it has to be prior to loading a notebook) and mechanism for storing some config browser-side is getting severe.

Truer words have rarely been spoken, I'm afraid :)

Agreed on both points.

On Wed, Nov 30, 2011 at 1:39 PM, Fernando Perez
reply@reply.github.com
wrote:

> On Wed, Nov 30, 2011 at 1:37 PM, Min RK
> reply@reply.github.com
> wrote:
> 
> > Since mathjax is so huge, it makes sense for it to be optional.  Should this be a server-level config?  Ideally, it would be per-browser (stored in a cookie), since you can connect to a single server from both fast and slow connections, making it server-level is probably a good idea.
> 
> Agreed.
> 
> > I think our need to have a little control panel (must be on the front page, because it has to be prior to loading a notebook) and mechanism for storing some config browser-side is getting severe.
> 
> Truer words have rarely been spoken, I'm afraid :)
> 
> ---
> 
> Reply to this email directly or view it on GitHub:
> https://github.com/ipython/ipython/issues/1071#issuecomment-2966820

## 

Brian E. Granger
Cal Poly State University, San Luis Obispo
bgranger@calpoly.edu and ellisonbg@gmail.com


Resolving patch:
diff --git a/IPython/frontend/html/notebook/handlers.py b/IPython/frontend/html/notebook/handlers.py
--- a/IPython/frontend/html/notebook/handlers.py
+++ b/IPython/frontend/html/notebook/handlers.py
@@ -219,6 +219,7 @@ def get(self):
             base_project_url=u'/', base_kernel_url=u'/',
             kill_kernel=False,
             read_only=False,
+            mathjax_url=self.application.ipython_app.mathjax_url,
         )
 
 
@@ -237,6 +238,7 @@ def get(self, notebook_id):
             base_project_url=u'/', base_kernel_url=u'/',
             kill_kernel=False,
             read_only=self.read_only,
+            mathjax_url=self.application.ipython_app.mathjax_url,
         )
 
 
diff --git a/IPython/frontend/html/notebook/notebookapp.py b/IPython/frontend/html/notebook/notebookapp.py
--- a/IPython/frontend/html/notebook/notebookapp.py
+++ b/IPython/frontend/html/notebook/notebookapp.py
@@ -128,6 +128,17 @@ def __init__(self, ipython_app, kernel_manager, notebook_manager, log):
     {'NotebookApp' : {'open_browser' : False}},
     "Don't open the notebook in a browser after startup."
 )
+flags['no-mathjax']=(
+    {'NotebookApp' : {'enable_mathjax' : False}},
+    """Disable MathJax
+    
+    MathJax is the javascript library IPython uses to render math/LaTeX. It is
+    very large, so you may want to disable it if you have a slow internet
+    connection, or for offline use of the notebook.
+    
+    When disabled, equations etc. will appear as their untransformed TeX source.
+    """
+)
 flags['read-only'] = (
     {'NotebookApp' : {'read_only' : True}},
     """Allow read-only access to notebooks.
@@ -144,7 +155,7 @@ def __init__(self, ipython_app, kernel_manager, notebook_manager, log):
 # the flags that are specific to the frontend
 # these must be scrubbed before being passed to the kernel,
 # or it will raise an error on unrecognized flags
-notebook_flags = ['no-browser', 'read-only']
+notebook_flags = ['no-browser', 'no-mathjax', 'read-only']
 
 aliases = dict(ipkernel_aliases)
 
@@ -230,6 +241,42 @@ def _ip_changed(self, name, old, new):
     read_only = Bool(False, config=True,
         help="Whether to prevent editing/execution of notebooks."
     )
+    
+    enable_mathjax = Bool(True, config=True,
+        help="""Whether to enable MathJax for typesetting math/TeX
+
+        MathJax is the javascript library IPython uses to render math/LaTeX. It is
+        very large, so you may want to disable it if you have a slow internet
+        connection, or for offline use of the notebook.
+
+        When disabled, equations etc. will appear as their untransformed TeX source.
+        """
+    )
+    def _enable_mathjax_changed(self, name, old, new):
+        """set mathjax url to empty if mathjax is disabled"""
+        if not new:
+            self.mathjax_url = u''
+    
+    mathjax_url = Unicode("", config=True,
+        help="""The url for MathJax.js."""
+    )
+    def _mathjax_url_default(self):
+        if not self.enable_mathjax:
+            return u''
+        static_path = os.path.join(os.path.dirname(__file__), "static")
+        if os.path.exists(os.path.join(static_path, 'mathjax', "MathJax.js")):
+            self.log.info("Using local MathJax")
+            return u"static/mathjax/MathJax.js"
+        else:
+            self.log.info("Using MathJax from CDN")
+            return u"http://cdn.mathjax.org/mathjax/latest/MathJax.js"
+    
+    def _mathjax_url_changed(self, name, old, new):
+        if new and not self.enable_mathjax:
+            # enable_mathjax=False overrides mathjax_url
+            self.mathjax_url = u''
+        else:
+            self.log.info("Using MathJax: %s", new)
 
     def parse_command_line(self, argv=None):
         super(NotebookApp, self).parse_command_line(argv)


Test patch: