diff --git a/webroot/rsrc/js/core/FileUpload.js b/webroot/rsrc/js/core/FileUpload.js
index e2c3530cbd..ef5089a56e 100644
--- a/webroot/rsrc/js/core/FileUpload.js
+++ b/webroot/rsrc/js/core/FileUpload.js
@@ -1,211 +1,223 @@
 /**
  * @requires javelin-install
  *           javelin-dom
  *           phabricator-notification
  * @provides phabricator-file-upload
  * @javelin
  */
 
 JX.install('PhabricatorFileUpload', {
 
   construct : function() {
     this._notification = new JX.Notification();
   },
 
   properties : {
     name: null,
     totalBytes: null,
     uploadedBytes: null,
     rawFileObject: null,
     allocatedPHID: null,
     ID: null,
     PHID: null,
     URI: null,
     status: null,
     markup: null,
     targetNode: null,
     error: null
   },
 
   members : {
     _notification : null,
     _chunks: null,
     _isResume: false,
 
     addUploadedBytes: function(bytes) {
       var uploaded = this.getUploadedBytes();
       this.setUploadedBytes(uploaded + bytes);
       return this;
     },
 
     /**
      * Get the File Monogram - like 'F123'
      */
     getMonogram: function() {
       return 'F' + this.getID();
     },
 
     /**
      * Get the File page URI - like '/F123'
      */
     getPageURI: function() {
         return '/' + this.getMonogram();
     },
 
     setChunks: function(chunks) {
       var chunk;
       for (var ii = 0; ii < chunks.length; ii++) {
         chunk = chunks[ii];
         if (chunk.complete) {
           this.addUploadedBytes(chunk.byteEnd - chunk.byteStart);
           this._isResume = true;
         }
       }
 
       this._chunks = chunks;
 
       return this;
     },
 
     getChunks: function() {
       return this._chunks;
     },
 
     getRemainingChunks: function() {
       var chunks = this.getChunks();
 
       var result = [];
       for (var ii = 0; ii < chunks.length; ii++) {
         if (!chunks[ii].complete) {
           result.push(chunks[ii]);
         }
       }
 
       return result;
     },
 
     didCompleteChunk: function(chunk) {
       var chunks = this.getRemainingChunks();
       for (var ii = 0; ii < chunks.length; ii++) {
         if (chunks[ii].byteStart == chunk.byteStart) {
           if (chunks[ii].byteEnd == chunk.byteEnd) {
             if (!chunks[ii].complete) {
               chunks[ii].complete = true;
             }
             break;
           }
         }
       }
 
       return this;
     },
 
     update : function() {
       if (!this._notification) {
         return;
       }
 
       this._notification
         .setDuration(0)
         .show();
 
       var content;
 
       // TODO: This stuff needs some work for translations.
 
       switch (this.getStatus()) {
         case 'done':
-          var link = JX.$N('a', {href: this.getPageURI()}, this.getMonogram());
+
+          // In this case the File upload was successful
+
+          var linkAttr = {};
+          linkAttr.href = this.getPageURI();
+
+          // External links are evil as default.
+          // Believe it or not, but some Phorge users brainstormed
+          // for one hour for this specific target="_blank".
+          // https://we.phorge.it/T15172
+          linkAttr.target = '_blank';
+
+          var link = JX.$N('a', linkAttr, this.getMonogram());
 
           content = [
             JX.$N('strong', {}, ['Upload Complete (', link, ')']),
             JX.$N('br'),
             this.getName()
           ];
 
           this._notification
             .setContent(content)
             .alterClassName('jx-notification-done', true)
             .setDuration(12000);
           this._notification = null;
           break;
         case 'error':
           content = [
             JX.$N('strong', {}, 'Upload Failure'),
             JX.$N('br'),
             this.getName(),
             JX.$N('br'),
             JX.$N('br'),
             this.getError()
           ];
 
           this._notification
             .setContent(content)
             .alterClassName('jx-notification-error', true);
           this._notification = null;
           break;
         case 'allocate':
           content = 'Allocating "' + this.getName() + '"...';
           this._notification
             .setContent(content);
           break;
         case 'chunks':
           content = 'Loading chunks for "' + this.getName() + '"...';
           this._notification
             .setContent(content);
           break;
         default:
           var info = '';
           if (this.getTotalBytes()) {
             var p = this._renderPercentComplete();
             var f = this._renderFileSize();
             info = p + ' of ' + f;
           }
 
           var head;
           if (this._isResume) {
             head = 'Resuming:';
           } else if (this._chunks) {
             head = 'Uploading chunks:';
           } else {
             head = 'Uploading:';
           }
 
           info = [
             JX.$N('strong', {}, this.getName()),
             JX.$N('br'),
             head + ' ' + info];
 
           this._notification
             .setContent(info);
           break;
       }
 
       return this;
     },
     _renderPercentComplete : function() {
       if (!this.getTotalBytes()) {
         return null;
       }
       var ratio = this.getUploadedBytes() / this.getTotalBytes();
       return parseInt(100 * ratio, 10) + '%';
     },
     _renderFileSize : function() {
       if (!this.getTotalBytes()) {
         return null;
       }
 
       var s = 3;
       var n = this.getTotalBytes();
       while (s && n >= 1000) {
         n = Math.round(n / 100);
         n = n / 10;
         s--;
       }
 
       s = ['GB', 'MB', 'KB', 'bytes'][s];
       return n + ' ' + s;
     }
   }
 
 });