diff --git a/Core/Bridge/TweetDeckBridge.cs b/Core/Bridge/TweetDeckBridge.cs
index 48b95893..4dd3115f 100644
--- a/Core/Bridge/TweetDeckBridge.cs
+++ b/Core/Bridge/TweetDeckBridge.cs
@@ -3,7 +3,7 @@
 using System.Windows.Forms;
 using CefSharp;
 using TweetDuck.Core.Controls;
-using TweetDuck.Core.Handling;
+using TweetDuck.Core.Management;
 using TweetDuck.Core.Notification;
 using TweetDuck.Core.Other;
 using TweetDuck.Core.Utils;
@@ -13,20 +13,12 @@ namespace TweetDuck.Core.Bridge{
     class TweetDeckBridge{
         public static string FontSize { get; private set; }
         public static string NotificationHeadLayout { get; private set; }
-
-        public static string LastHighlightedTweetUrl = string.Empty;
-        public static string LastHighlightedQuoteUrl = string.Empty;
-        private static string LastHighlightedTweetAuthors = string.Empty;
-        private static string LastHighlightedTweetImages = string.Empty;
-
-        public static string[] LastHighlightedTweetAuthorsArray => LastHighlightedTweetAuthors.Split(';');
-        public static string[] LastHighlightedTweetImagesArray => LastHighlightedTweetImages.Split(';');
+        public static readonly ContextInfo ContextInfo = new ContextInfo();
 
         private static readonly Dictionary<string, string> SessionData = new Dictionary<string, string>(2);
 
         public static void ResetStaticProperties(){
             FontSize = NotificationHeadLayout = null;
-            LastHighlightedTweetUrl = LastHighlightedQuoteUrl = LastHighlightedTweetAuthors = LastHighlightedTweetImages = string.Empty;
         }
 
         public static void RestoreSessionData(IFrame frame){
@@ -72,13 +64,8 @@ public void LoadNotificationLayout(string fontSize, string headLayout){
                 });
             }
 
-            public void SetLastHighlightedTweet(string tweetUrl, string quoteUrl, string authors, string imageList){
-                form.InvokeAsyncSafe(() => {
-                    LastHighlightedTweetUrl = tweetUrl;
-                    LastHighlightedQuoteUrl = quoteUrl;
-                    LastHighlightedTweetAuthors = authors;
-                    LastHighlightedTweetImages = imageList;
-                });
+            public void SetRightClickedChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
+                ContextInfo.SetChirp(tweetUrl, quoteUrl, chirpAuthors, chirpImages);
             }
 
             public void DisplayTooltip(string text){
@@ -112,8 +99,8 @@ public void ShowTweetDetail(){
 
         // Global
 
-        public void SetLastRightClickInfo(string type, string link){
-            form.InvokeAsyncSafe(() => ContextMenuBase.SetContextInfo(type, link));
+        public void SetLastRightClickInfo(string type, string url){
+            ContextInfo.SetLink(type, url);
         }
 
         public void OnTweetPopup(string columnId, string chirpId, string columnName, string tweetHtml, int tweetCharacters, string tweetUrl, string quoteUrl){
diff --git a/Core/Handling/ContextMenuBase.cs b/Core/Handling/ContextMenuBase.cs
index f2ec36f5..46ecc44b 100644
--- a/Core/Handling/ContextMenuBase.cs
+++ b/Core/Handling/ContextMenuBase.cs
@@ -3,11 +3,10 @@
 using System.Text.RegularExpressions;
 using System.Windows.Forms;
 using CefSharp;
-using TweetDuck.Core.Bridge;
 using TweetDuck.Core.Controls;
 using TweetDuck.Core.Utils;
-using System.Collections.Generic;
 using System.Linq;
+using TweetDuck.Core.Bridge;
 using TweetDuck.Core.Management;
 using TweetDuck.Core.Notification;
 using TweetDuck.Core.Other;
@@ -18,19 +17,6 @@ abstract class ContextMenuBase : IContextMenuHandler{
         public static readonly bool HasDevTools = File.Exists(Path.Combine(Program.ProgramPath, "devtools_resources.pak"));
 
         private static TwitterUtils.ImageQuality ImageQuality => Program.UserConfig.TwitterImageQuality;
-        
-        private static KeyValuePair<string, string> ContextInfo;
-        private static bool IsLink => ContextInfo.Key == "link";
-        private static bool IsImage => ContextInfo.Key == "image";
-        private static bool IsVideo => ContextInfo.Key == "video";
-
-        public static void SetContextInfo(string type, string link){
-            ContextInfo = new KeyValuePair<string, string>(string.IsNullOrEmpty(link) ? null : type, link);
-        }
-
-        private static string GetMediaLink(IContextMenuParams parameters){
-            return IsImage || IsVideo ? ContextInfo.Value : parameters.SourceUrl;
-        }
 
         private const CefMenuCommand MenuOpenLinkUrl     = (CefMenuCommand)26500;
         private const CefMenuCommand MenuCopyLinkUrl     = (CefMenuCommand)26501;
@@ -42,8 +28,8 @@ private static string GetMediaLink(IContextMenuParams parameters){
         private const CefMenuCommand MenuSaveTweetImages = (CefMenuCommand)26507;
         private const CefMenuCommand MenuOpenDevTools    = (CefMenuCommand)26599;
         
-        private string[] lastHighlightedTweetAuthors;
-        private string[] lastHighlightedTweetImageList;
+        protected ContextInfo.LinkInfo LastLink { get; private set; }
+        protected ContextInfo.ChirpInfo LastChirp { get; private set; }
 
         private readonly AnalyticsFile.IProvider analytics;
 
@@ -51,19 +37,23 @@ protected ContextMenuBase(AnalyticsFile.IProvider analytics){
             this.analytics = analytics;
         }
 
+        private void ResetContextInfo(){
+            LastLink = default(ContextInfo.LinkInfo);
+            LastChirp = default(ContextInfo.ChirpInfo);
+            TweetDeckBridge.ContextInfo.Reset();
+        }
+
         public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model){
             if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
-                lastHighlightedTweetAuthors = StringUtils.EmptyArray;
-                lastHighlightedTweetImageList = StringUtils.EmptyArray;
-                ContextInfo = default(KeyValuePair<string, string>);
+                ResetContextInfo();
             }
             else{
-                lastHighlightedTweetAuthors = TweetDeckBridge.LastHighlightedTweetAuthorsArray;
-                lastHighlightedTweetImageList = TweetDeckBridge.LastHighlightedTweetImagesArray;
+                LastLink = TweetDeckBridge.ContextInfo.Link;
+                LastChirp = TweetDeckBridge.ContextInfo.Chirp;
             }
 
-            bool hasTweetImage = IsImage;
-            bool hasTweetVideo = IsVideo;
+            bool hasTweetImage = LastLink.IsImage;
+            bool hasTweetVideo = LastLink.IsVideo;
 
             string TextOpen(string name) => "Open "+name+" in browser";
             string TextCopy(string name) => "Copy "+name+" address";
@@ -95,7 +85,7 @@ public virtual void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser bro
                 model.AddItem(MenuCopyMediaUrl, TextCopy("image"));
                 model.AddItem(MenuSaveMedia, TextSave("image"));
 
-                if (lastHighlightedTweetImageList.Length > 1){
+                if (LastChirp.Images.Length > 1){
                     model.AddItem(MenuSaveTweetImages, TextSave("all images"));
                 }
 
@@ -108,11 +98,11 @@ public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser br
 
             switch(commandId){
                 case MenuOpenLinkUrl:
-                    OpenBrowser(control, IsLink ? ContextInfo.Value : parameters.LinkUrl);
+                    OpenBrowser(control, LastLink.GetUrl(parameters, true));
                     break;
 
                 case MenuCopyLinkUrl:
-                    SetClipboardText(control, IsLink ? ContextInfo.Value : parameters.UnfilteredLinkUrl);
+                    SetClipboardText(control, LastLink.GetUrl(parameters, false));
                     break;
 
                 case MenuCopyUsername:
@@ -122,35 +112,37 @@ public virtual bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser br
                     break;
 
                 case MenuOpenMediaUrl:
-                    OpenBrowser(control, TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
+                    OpenBrowser(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality));
                     break;
 
                 case MenuCopyMediaUrl:
-                    SetClipboardText(control, TwitterUtils.GetMediaLink(GetMediaLink(parameters), ImageQuality));
+                    SetClipboardText(control, TwitterUtils.GetMediaLink(LastLink.GetMediaSource(parameters), ImageQuality));
                     break;
 
                 case MenuViewImage:
-                    string url = GetMediaLink(parameters);
-                    string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url));
-
-                    void ViewFile(){
-                        string ext = Path.GetExtension(file);
+                    void ViewImage(string path){
+                        string ext = Path.GetExtension(path);
 
                         if (TwitterUtils.ValidImageExtensions.Contains(ext)){
-                            WindowsUtils.OpenAssociatedProgram(file);
+                            WindowsUtils.OpenAssociatedProgram(path);
                         }
                         else{
                             FormMessage.Error("Image Download", "Invalid file extension "+ext, FormMessage.OK);
                         }
                     }
 
+                    string url = LastLink.GetMediaSource(parameters);
+                    string file = Path.Combine(BrowserCache.CacheFolder, TwitterUtils.GetImageFileName(url));
+
                     if (File.Exists(file)){
-                        ViewFile();
+                        ViewImage(file);
                     }
                     else{
                         control.InvokeAsyncSafe(analytics.AnalyticsFile.ViewedImages.Trigger);
 
-                        BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, ViewFile, ex => {
+                        BrowserUtils.DownloadFileAsync(TwitterUtils.GetMediaLink(url, ImageQuality), file, () => {
+                            ViewImage(file);
+                        }, ex => {
                             FormMessage.Error("Image Download", "An error occurred while downloading the image: "+ex.Message, FormMessage.OK);
                         });
                     }
@@ -158,20 +150,20 @@ void ViewFile(){
                     break;
 
                 case MenuSaveMedia:
-                    if (IsVideo){
+                    if (LastLink.IsVideo){
                         control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedVideos.Trigger);
-                        TwitterUtils.DownloadVideo(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault());
+                        TwitterUtils.DownloadVideo(LastLink.GetMediaSource(parameters), LastChirp.Authors.LastOrDefault());
                     }
                     else{
                         control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger);
-                        TwitterUtils.DownloadImage(GetMediaLink(parameters), lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
+                        TwitterUtils.DownloadImage(LastLink.GetMediaSource(parameters), LastChirp.Authors.LastOrDefault(), ImageQuality);
                     }
 
                     break;
 
                 case MenuSaveTweetImages:
                     control.InvokeAsyncSafe(analytics.AnalyticsFile.DownloadedImages.Trigger);
-                    TwitterUtils.DownloadImages(lastHighlightedTweetImageList, lastHighlightedTweetAuthors.LastOrDefault(), ImageQuality);
+                    TwitterUtils.DownloadImages(LastChirp.Images, LastChirp.Authors.LastOrDefault(), ImageQuality);
                     break;
                     
                 case MenuOpenDevTools:
@@ -183,7 +175,7 @@ void ViewFile(){
         }
 
         public virtual void OnContextMenuDismissed(IWebBrowser browserControl, IBrowser browser, IFrame frame){
-            ContextInfo = default(KeyValuePair<string, string>);
+            ResetContextInfo();
         }
 
         public virtual bool RunContextMenu(IWebBrowser browserControl, IBrowser browser, IFrame frame, IContextMenuParams parameters, IMenuModel model, IRunContextMenuCallback callback){
diff --git a/Core/Handling/ContextMenuBrowser.cs b/Core/Handling/ContextMenuBrowser.cs
index 3f00c9c6..126ccccc 100644
--- a/Core/Handling/ContextMenuBrowser.cs
+++ b/Core/Handling/ContextMenuBrowser.cs
@@ -1,8 +1,6 @@
 using CefSharp;
 using System.Windows.Forms;
-using TweetDuck.Core.Bridge;
 using TweetDuck.Core.Controls;
-using TweetDuck.Core.Utils;
 
 namespace TweetDuck.Core.Handling{
     sealed class ContextMenuBrowser : ContextMenuBase{
@@ -26,10 +24,7 @@ sealed class ContextMenuBrowser : ContextMenuBase{
         private const string TitleAboutProgram = "About "+Program.BrandName;
 
         private readonly FormBrowser form;
-
-        private string lastHighlightedTweetUrl;
-        private string lastHighlightedQuoteUrl;
-
+        
         public ContextMenuBrowser(FormBrowser form) : base(form){
             this.form = form;
         }
@@ -52,20 +47,12 @@ public override void OnBeforeContextMenu(IWebBrowser browserControl, IBrowser br
 
             base.OnBeforeContextMenu(browserControl, browser, frame, parameters, model);
 
-            lastHighlightedTweetUrl = TweetDeckBridge.LastHighlightedTweetUrl;
-            lastHighlightedQuoteUrl = TweetDeckBridge.LastHighlightedQuoteUrl;
-
-            if (!TwitterUtils.IsTweetDeckWebsite(frame) || browser.IsLoading){
-                lastHighlightedTweetUrl = string.Empty;
-                lastHighlightedQuoteUrl = string.Empty;
-            }
-
-            if (!string.IsNullOrEmpty(lastHighlightedTweetUrl) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
+            if (!string.IsNullOrEmpty(LastChirp.TweetUrl) && (parameters.TypeFlags & (ContextMenuType.Editable | ContextMenuType.Selection)) == 0){
                 model.AddItem(MenuOpenTweetUrl, "Open tweet in browser");
                 model.AddItem(MenuCopyTweetUrl, "Copy tweet address");
                 model.AddItem(MenuScreenshotTweet, "Screenshot tweet to clipboard");
 
-                if (!string.IsNullOrEmpty(lastHighlightedQuoteUrl)){
+                if (!string.IsNullOrEmpty(LastChirp.QuoteUrl)){
                     model.AddSeparator();
                     model.AddItem(MenuOpenQuotedTweetUrl, "Open quoted tweet in browser");
                     model.AddItem(MenuCopyQuotedTweetUrl, "Copy quoted tweet address");
@@ -126,11 +113,11 @@ public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser b
                     return true;
 
                 case MenuOpenTweetUrl:
-                    OpenBrowser(form, lastHighlightedTweetUrl);
+                    OpenBrowser(form, LastChirp.TweetUrl);
                     return true;
 
                 case MenuCopyTweetUrl:
-                    SetClipboardText(form, lastHighlightedTweetUrl);
+                    SetClipboardText(form, LastChirp.TweetUrl);
                     return true;
 
                 case MenuScreenshotTweet:
@@ -138,11 +125,11 @@ public override bool OnContextMenuCommand(IWebBrowser browserControl, IBrowser b
                     return true;
 
                 case MenuOpenQuotedTweetUrl:
-                    OpenBrowser(form, lastHighlightedQuoteUrl);
+                    OpenBrowser(form, LastChirp.QuoteUrl);
                     return true;
 
                 case MenuCopyQuotedTweetUrl:
-                    SetClipboardText(form, lastHighlightedQuoteUrl);
+                    SetClipboardText(form, LastChirp.QuoteUrl);
                     return true;
 
                 case MenuInputApplyROT13:
diff --git a/Core/Management/ContextInfo.cs b/Core/Management/ContextInfo.cs
new file mode 100644
index 00000000..643f42d3
--- /dev/null
+++ b/Core/Management/ContextInfo.cs
@@ -0,0 +1,68 @@
+using CefSharp;
+using TweetDuck.Core.Utils;
+
+namespace TweetDuck.Core.Management{
+    sealed class ContextInfo{
+        public LinkInfo Link { get; private set; }
+        public ChirpInfo Chirp { get; private set; }
+        
+        public ContextInfo(){
+            Reset();
+        }
+        
+        public void SetLink(string type, string url){
+            Link = new LinkInfo(string.IsNullOrEmpty(type) ? null : type, url);
+        }
+
+        public void SetChirp(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
+            Chirp = new ChirpInfo(tweetUrl, quoteUrl, chirpAuthors, chirpImages);
+        }
+
+        public void Reset(){
+            Link = new LinkInfo();
+            Chirp = new ChirpInfo();
+        }
+
+        // Data structures
+
+        public struct LinkInfo{
+            public bool IsLink => type == "link";
+            public bool IsImage => type == "image";
+            public bool IsVideo => type == "video";
+
+            public string GetUrl(IContextMenuParams parameters, bool safe){
+                return IsLink ? url : (safe ? parameters.LinkUrl : parameters.UnfilteredLinkUrl);
+            }
+
+            public string GetMediaSource(IContextMenuParams parameters){
+                return IsImage || IsVideo ? url : parameters.SourceUrl;
+            }
+
+            private readonly string type;
+            private readonly string url;
+
+            public LinkInfo(string type, string url){
+                this.type = type;
+                this.url = url;
+            }
+        }
+
+        public struct ChirpInfo{
+            public string TweetUrl { get; }
+            public string QuoteUrl { get; }
+
+            public string[] Authors => chirpAuthors?.Split(';') ?? StringUtils.EmptyArray;
+            public string[] Images => chirpImages?.Split(';') ?? StringUtils.EmptyArray;
+            
+            private readonly string chirpAuthors;
+            private readonly string chirpImages;
+
+            public ChirpInfo(string tweetUrl, string quoteUrl, string chirpAuthors, string chirpImages){
+                this.TweetUrl = tweetUrl;
+                this.QuoteUrl = quoteUrl;
+                this.chirpAuthors = chirpAuthors;
+                this.chirpImages = chirpImages;
+            }
+        }
+    }
+}
diff --git a/Resources/Scripts/code.js b/Resources/Scripts/code.js
index 011380c6..b64fe951 100644
--- a/Resources/Scripts/code.js
+++ b/Resources/Scripts/code.js
@@ -595,22 +595,15 @@
   // Block: Update highlighted column and tweet for context menu and other functionality.
   //
   (function(){
-    var lastTweet = "";
-    
     const updateHighlightedColumn = function(ele){
       highlightedColumnEle = ele;
       highlightedColumnObj = ele ? TD.controller.columnManager.get(ele.attr("data-column")) : null;
       return !!highlightedColumnObj;
     };
     
-    const updateHighlightedTweet = function(ele, obj, tweetUrl, quoteUrl, authors, imageList){
+    const updateHighlightedTweet = function(ele, obj){
       highlightedTweetEle = ele;
       highlightedTweetObj = obj;
-      
-      if (lastTweet !== tweetUrl){
-        $TD.setLastHighlightedTweet(tweetUrl, quoteUrl, authors, imageList);
-        lastTweet = tweetUrl;
-      }
     };
     
     const processMedia = function(chirp){
@@ -626,6 +619,19 @@
       
       mouseleave: function(){
         updateHighlightedColumn(null);
+      },
+      
+      contextmenu: function(){
+        let tweet = highlightedTweetObj;
+        
+        if (tweet && tweet.chirpType === TD.services.ChirpBase.TWEET){
+          let tweetUrl = tweet.getChirpURL();
+          let quoteUrl = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : "";
+          let chirpAuthors = tweet.quotedTweet ? [ tweet.getMainUser().screenName, tweet.quotedTweet.getMainUser().screenName ].join(";") : tweet.getMainUser().screenName;
+          let chirpImages = tweet.hasImage() ? processMedia(tweet) : tweet.quotedTweet && tweet.quotedTweet.hasImage() ? processMedia(tweet.quotedTweet) : "";
+          
+          $TD.setRightClickedChirp(tweetUrl || "", quoteUrl || "", chirpAuthors, chirpImages);
+        }
       }
     });
     
@@ -637,21 +643,11 @@
         let tweet = highlightedColumnObj.findChirp(me.attr("data-tweet-id")) || highlightedColumnObj.findChirp(me.attr("data-key"));
         return if !tweet;
         
-        if (tweet.chirpType === TD.services.ChirpBase.TWEET){
-          let tweetUrl = tweet.getChirpURL();
-          let quoteUrl = tweet.quotedTweet ? tweet.quotedTweet.getChirpURL() : "";
-          let authors = tweet.quotedTweet ? [ tweet.getMainUser().screenName, tweet.quotedTweet.getMainUser().screenName ].join(";") : tweet.getMainUser().screenName;
-          let imageList = tweet.hasImage() ? processMedia(tweet) : tweet.quotedTweet && tweet.quotedTweet.hasImage() ? processMedia(tweet.quotedTweet) : "";
-          
-          updateHighlightedTweet(me, tweet, tweetUrl || "", quoteUrl || "", authors, imageList);
-        }
-        else{
-          updateHighlightedTweet(me, tweet, "", "", "", "");
-        }
+        updateHighlightedTweet(me, tweet);
       },
       
       mouseleave: function(){
-        updateHighlightedTweet(null, null, "", "", "", "");
+        updateHighlightedTweet(null, null);
       }
     });
   })();
diff --git a/TweetDuck.csproj b/TweetDuck.csproj
index 2927b1b4..82d0a630 100644
--- a/TweetDuck.csproj
+++ b/TweetDuck.csproj
@@ -108,6 +108,7 @@
     <Compile Include="Core\Handling\RequestHandlerBrowser.cs" />
     <Compile Include="Core\Handling\ResourceHandlerNotification.cs" />
     <Compile Include="Core\ITweetDeckBrowser.cs" />
+    <Compile Include="Core\Management\ContextInfo.cs" />
     <Compile Include="Core\Notification\Example\FormNotificationExample.cs">
       <SubType>Form</SubType>
     </Compile>