1
0
mirror of https://github.com/chylex/TweetDuck.git synced 2025-05-08 20:34:05 +02:00

Refactor TweetDeck browser into a separate class

This commit is contained in:
chylex 2017-10-21 15:35:46 +02:00
parent 0b4aaf80dc
commit 351b174b86
4 changed files with 257 additions and 166 deletions

View File

@ -1,13 +1,10 @@
using CefSharp;
using CefSharp.WinForms;
using System;
using System;
using System.Drawing;
using System.Windows.Forms;
using TweetDuck.Configuration;
using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Notification;
using TweetDuck.Core.Notification.Screenshot;
using TweetDuck.Core.Other;
@ -15,9 +12,7 @@
using TweetDuck.Core.Other.Settings;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events;
using TweetDuck.Resources;
using TweetDuck.Updates;
using TweetLib.Audio;
@ -44,15 +39,13 @@ public bool IsWaiting{
public string UpdateInstallerPath { get; private set; }
private readonly ChromiumWebBrowser browser;
private readonly TweetDeckBrowser browser;
private readonly PluginManager plugins;
private readonly UpdateHandler updates;
private readonly FormNotificationTweet notification;
private readonly ContextMenu contextMenu;
private readonly MemoryUsageTracker memoryUsageTracker;
private bool isLoaded;
private bool isBrowserReady;
private FormWindowState prevState;
private TweetScreenshotManager notificationScreenshotManager;
@ -67,12 +60,8 @@ public FormBrowser(UpdaterSettings updaterSettings){
this.plugins = new PluginManager(Program.PluginPath, Program.PluginConfigFilePath);
this.plugins.Reloaded += plugins_Reloaded;
this.plugins.Executed += plugins_Executed;
this.plugins.PluginChangedState += plugins_PluginChangedState;
this.plugins.Reload();
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
this.memoryUsageTracker = new MemoryUsageTracker("TDGF_tryRunCleanup");
this.notification = new FormNotificationTweet(this, plugins){
#if DEBUG
CanMoveWindow = () => (ModifierKeys & Keys.Alt) == Keys.Alt
@ -82,38 +71,13 @@ public FormBrowser(UpdaterSettings updaterSettings){
};
this.notification.Show();
this.browser = new ChromiumWebBrowser("https://tweetdeck.twitter.com/"){
DialogHandler = new FileDialogHandler(),
DragHandler = new DragHandlerBrowser(),
MenuHandler = new ContextMenuBrowser(this),
JsDialogHandler = new JavaScriptDialogHandler(),
KeyboardHandler = new KeyboardHandlerBrowser(this),
LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = new RequestHandlerBrowser()
};
#if DEBUG
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
#endif
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
this.browser.FrameLoadStart += browser_FrameLoadStart;
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
this.browser.LoadError += browser_LoadError;
this.browser.RegisterAsyncJsObject("$TD", new TweetDeckBridge(this, notification));
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
browser.Dock = DockStyle.None;
browser.Location = ControlExtensions.InvisibleLocation;
Controls.Add(browser);
this.browser = new TweetDeckBrowser(this, plugins, new TweetDeckBridge(this, notification));
this.contextMenu = ContextMenuBrowser.CreateMenu(this);
Controls.Add(new MenuStrip{ Visible = false }); // fixes Alt freezing the program in Win 10 Anniversary Update
Disposed += (sender, args) => {
memoryUsageTracker.Dispose();
browser.Dispose();
contextMenu.Dispose();
@ -128,10 +92,7 @@ public FormBrowser(UpdaterSettings updaterSettings){
UpdateTrayIcon();
Config.MuteToggled += Config_MuteToggled;
Config.ZoomLevelChanged += Config_ZoomLevelChanged;
this.updates = new UpdateHandler(browser, updaterSettings);
this.updates = browser.CreateUpdateHandler(updaterSettings);
this.updates.UpdateAccepted += updates_UpdateAccepted;
this.updates.UpdateDismissed += updates_UpdateDismissed;
@ -156,81 +117,11 @@ private void RestoreWindow(){
isLoaded = true;
}
private void OnBrowserReady(){
if (!isBrowserReady){
browser.Location = Point.Empty;
browser.Dock = DockStyle.Fill;
isBrowserReady = true;
}
}
private void UpdateTrayIcon(){
trayIcon.Visible = Config.TrayBehavior.ShouldDisplayIcon();
}
// active event handlers
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
if (!e.IsLoading){
foreach(string word in TwitterUtils.DictionaryWords){
browser.AddWordToDictionary(word);
}
BeginInvoke(new Action(OnBrowserReady));
browser.LoadingStateChanged -= browser_LoadingStateChanged;
}
}
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
if (e.Frame.IsMain){
memoryUsageTracker.Stop();
if (Config.ZoomLevel != 100){
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);
}
if (TwitterUtils.IsTwitterWebsite(e.Frame)){
ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
}
}
}
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){
e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
UpdateProperties(PropertyBridge.Environment.Browser);
TweetDeckBridge.RestoreSessionData(e.Frame);
ScriptLoader.ExecuteFile(e.Frame, "code.js");
InjectBrowserCSS();
ReinjectCustomCSS(Config.CustomBrowserCSS);
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser);
TweetDeckBridge.ResetStaticProperties();
if (Program.SystemConfig.EnableBrowserGCReload){
memoryUsageTracker.Start(this, e.Browser, Program.SystemConfig.BrowserMemoryThreshold);
}
if (Config.FirstRun){
ScriptLoader.ExecuteFile(e.Frame, "introduction.js");
}
}
}
private void browser_LoadError(object sender, LoadErrorEventArgs e){
if (e.ErrorCode == CefErrorCode.Aborted){
return;
}
if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
string errorPage = ScriptLoader.LoadResource("pages/error.html", true);
if (errorPage != null){
browser.LoadHtml(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)), "http://td/error");
}
}
}
// event handlers
private void timerResize_Tick(object sender, EventArgs e){
FormBrowser_ResizeEnd(this, e); // also stops timer
@ -300,14 +191,6 @@ private void FormBrowser_FormClosed(object sender, FormClosedEventArgs e){
}
}
private void Config_MuteToggled(object sender, EventArgs e){
UpdateProperties(PropertyBridge.Environment.Browser);
}
private void Config_ZoomLevelChanged(object sender, EventArgs e){
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Config.ZoomLevel);
}
private void Config_TrayBehaviorChanged(object sender, EventArgs e){
UpdateTrayIcon();
}
@ -339,10 +222,6 @@ private static void plugins_Executed(object sender, PluginErrorEventArgs e){
}
}
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
browser.ExecuteScriptAsync("window.TDPF_setPluginState", e.Plugin, e.IsEnabled);
}
private void updates_UpdateAccepted(object sender, UpdateEventArgs e){
this.InvokeAsyncSafe(() => {
FormManager.CloseAllDialogs();
@ -400,12 +279,12 @@ protected override void WndProc(ref Message m){
}
}
if (isBrowserReady && m.Msg == NativeMethods.WM_PARENTNOTIFY && (m.WParam.ToInt32() & 0xFFFF) == NativeMethods.WM_XBUTTONDOWN){
if (browser.Ready && m.Msg == NativeMethods.WM_PARENTNOTIFY && (m.WParam.ToInt32() & 0xFFFF) == NativeMethods.WM_XBUTTONDOWN){
if (videoPlayer != null && videoPlayer.Running){
videoPlayer.Close();
}
else{
browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (m.WParam.ToInt32() >> 16) & 0xFFFF);
browser.OnMouseClickExtra(m.WParam);
}
return;
@ -428,22 +307,18 @@ public void ResumeNotification(){
notification.ResumeNotification();
}
// javascript calls
public void InjectBrowserCSS(){
browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css").TrimEnd());
}
// browser bridge methods
public void ReinjectCustomCSS(string css){
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
}
public void UpdateProperties(PropertyBridge.Environment environment){
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(environment));
browser.ReinjectCustomCSS(css);
}
public void ReloadToTweetDeck(){
browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'");
browser.ReloadToTweetDeck();
}
public void TriggerTweetScreenshot(){
browser.TriggerTweetScreenshot();
}
// callback handlers
@ -486,19 +361,14 @@ public void OpenSettings(Type startTab){
trayIcon.HasNotifications = false;
}
if (Program.SystemConfig.EnableBrowserGCReload){
memoryUsageTracker.Start(this, browser.GetBrowser(), Program.SystemConfig.BrowserMemoryThreshold);
}
else{
memoryUsageTracker.Stop();
}
browser.RefreshMemoryTracker();
if (form.ShouldReloadBrowser){
FormManager.TryFind<FormPlugins>()?.Close();
plugins.Reload(); // also reloads the browser
}
else{
UpdateProperties(PropertyBridge.Environment.Browser);
browser.UpdateProperties();
}
notification.RequiresResize = true;
@ -551,18 +421,13 @@ public void PlayVideo(string url, string username){
videoPlayer = new VideoPlayer(this);
videoPlayer.ProcessExited += (sender, args) => {
browser.GetBrowser().GetHost().SendFocusEvent(true);
HideVideoOverlay();
browser.HideVideoOverlay(true);
};
}
videoPlayer.Launch(url, username);
}
public void HideVideoOverlay(){
browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
}
public bool ProcessBrowserKey(Keys key){
if (videoPlayer != null && videoPlayer.Running){
videoPlayer.SendKeyEvent(key);
@ -575,15 +440,13 @@ public bool ProcessBrowserKey(Keys key){
public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){
Activate();
using(IFrame frame = browser.GetBrowser().MainFrame){
if (!TwitterUtils.IsTweetDeckWebsite(frame)){
FormMessage.Error("View Tweet Detail", "TweetDeck is not currently loaded.", FormMessage.OK);
return;
}
if (!browser.IsTweetDeckWebsite){
FormMessage.Error("View Tweet Detail", "TweetDeck is not currently loaded.", FormMessage.OK);
return;
}
notification.FinishCurrentNotification();
browser.ExecuteScriptAsync("window.TDGF_showTweetDetail", columnId, chirpId, fallbackUrl);
browser.ShowTweetDetail(columnId, chirpId, fallbackUrl);
}
public void OnTweetScreenshotReady(string html, int width, int height){
@ -604,9 +467,5 @@ public void DisplayTooltip(string text){
toolTip.Show(text, this, position);
}
}
public void TriggerTweetScreenshot(){
browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
}
}
}

View File

@ -3,6 +3,7 @@
using System.Timers;
using System.Windows.Forms;
using CefSharp;
using CefSharp.WinForms;
using Timer = System.Timers.Timer;
namespace TweetDuck.Core.Other.Management{
@ -25,11 +26,11 @@ public MemoryUsageTracker(string cleanupFunctionName){
this.timer.Elapsed += timer_Elapsed;
}
public void Start(Form owner, IBrowser browser, int thresholdMB){
public void Start(ChromiumWebBrowser control, int thresholdMB){
Stop();
this.owner = owner;
this.browser = browser;
this.owner = (Form)control.Parent; // TODO ugly
this.browser = control.GetBrowser();
this.threshold = thresholdMB*1024L*1024L;
this.timer.SynchronizingObject = owner;
this.timer.Start();

230
Core/TweetDeckBrowser.cs Normal file
View File

@ -0,0 +1,230 @@
using System;
using System.Drawing;
using System.Windows.Forms;
using CefSharp;
using CefSharp.WinForms;
using TweetDuck.Core.Bridge;
using TweetDuck.Core.Controls;
using TweetDuck.Core.Handling;
using TweetDuck.Core.Handling.General;
using TweetDuck.Core.Other.Management;
using TweetDuck.Core.Utils;
using TweetDuck.Plugins;
using TweetDuck.Plugins.Enums;
using TweetDuck.Plugins.Events;
using TweetDuck.Resources;
using TweetDuck.Updates;
namespace TweetDuck.Core{
sealed class TweetDeckBrowser : IDisposable{
public bool Ready { get; private set; }
public bool Enabled{
get => browser.Enabled;
set => browser.Enabled = value;
}
public bool IsTweetDeckWebsite{
get{
using(IFrame frame = browser.GetBrowser().MainFrame){
return TwitterUtils.IsTweetDeckWebsite(frame);
}
}
}
private readonly ChromiumWebBrowser browser;
private readonly PluginManager plugins;
private readonly MemoryUsageTracker memoryUsageTracker;
public TweetDeckBrowser(FormBrowser owner, PluginManager plugins, TweetDeckBridge bridge){
this.browser = new ChromiumWebBrowser(TwitterUtils.TweetDeckURL){
DialogHandler = new FileDialogHandler(),
DragHandler = new DragHandlerBrowser(),
MenuHandler = new ContextMenuBrowser(owner),
JsDialogHandler = new JavaScriptDialogHandler(),
KeyboardHandler = new KeyboardHandlerBrowser(owner),
LifeSpanHandler = new LifeSpanHandler(),
RequestHandler = new RequestHandlerBrowser()
};
#if DEBUG
this.browser.ConsoleMessage += BrowserUtils.HandleConsoleMessage;
#endif
this.browser.LoadingStateChanged += browser_LoadingStateChanged;
this.browser.FrameLoadStart += browser_FrameLoadStart;
this.browser.FrameLoadEnd += browser_FrameLoadEnd;
this.browser.LoadError += browser_LoadError;
this.browser.RegisterAsyncJsObject("$TD", bridge);
this.browser.RegisterAsyncJsObject("$TDP", plugins.Bridge);
this.browser.BrowserSettings.BackgroundColor = (uint)TwitterUtils.BackgroundColor.ToArgb();
this.browser.Dock = DockStyle.None;
this.browser.Location = ControlExtensions.InvisibleLocation;
owner.Controls.Add(browser);
this.plugins = plugins;
this.plugins.PluginChangedState += plugins_PluginChangedState;
this.memoryUsageTracker = new MemoryUsageTracker("TDGF_tryRunCleanup");
Program.UserConfig.MuteToggled += UserConfig_MuteToggled;
Program.UserConfig.ZoomLevelChanged += UserConfig_ZoomLevelChanged;
}
// setup and management
private void OnBrowserReady(){
if (!Ready){
browser.Location = Point.Empty;
browser.Dock = DockStyle.Fill;
Ready = true;
}
}
public void Focus(){
browser.Focus();
}
public void Dispose(){
plugins.PluginChangedState -= plugins_PluginChangedState;
Program.UserConfig.MuteToggled -= UserConfig_MuteToggled;
Program.UserConfig.ZoomLevelChanged -= UserConfig_ZoomLevelChanged;
memoryUsageTracker.Dispose();
browser.Dispose();
}
// event handlers
private void browser_LoadingStateChanged(object sender, LoadingStateChangedEventArgs e){
if (!e.IsLoading){
foreach(string word in TwitterUtils.DictionaryWords){
browser.AddWordToDictionary(word);
}
browser.BeginInvoke(new Action(OnBrowserReady));
browser.LoadingStateChanged -= browser_LoadingStateChanged;
}
}
private void browser_FrameLoadStart(object sender, FrameLoadStartEventArgs e){
if (e.Frame.IsMain){
memoryUsageTracker.Stop();
if (Program.UserConfig.ZoomLevel != 100){
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
}
if (TwitterUtils.IsTwitterWebsite(e.Frame)){
ScriptLoader.ExecuteFile(e.Frame, "twitter.js");
}
}
}
private void browser_FrameLoadEnd(object sender, FrameLoadEndEventArgs e){
if (e.Frame.IsMain && TwitterUtils.IsTweetDeckWebsite(e.Frame)){
e.Frame.ExecuteJavaScriptAsync(TwitterUtils.BackgroundColorFix);
UpdateProperties();
TweetDeckBridge.RestoreSessionData(e.Frame);
ScriptLoader.ExecuteFile(e.Frame, "code.js");
InjectBrowserCSS();
ReinjectCustomCSS(Program.UserConfig.CustomBrowserCSS);
plugins.ExecutePlugins(e.Frame, PluginEnvironment.Browser);
TweetDeckBridge.ResetStaticProperties();
if (Program.SystemConfig.EnableBrowserGCReload){
memoryUsageTracker.Start(browser, Program.SystemConfig.BrowserMemoryThreshold);
}
if (Program.UserConfig.FirstRun){
ScriptLoader.ExecuteFile(e.Frame, "introduction.js");
}
}
}
private void browser_LoadError(object sender, LoadErrorEventArgs e){
if (e.ErrorCode == CefErrorCode.Aborted){
return;
}
if (!e.FailedUrl.StartsWith("http://td/", StringComparison.Ordinal)){
string errorPage = ScriptLoader.LoadResource("pages/error.html", true);
if (errorPage != null){
browser.LoadHtml(errorPage.Replace("{err}", BrowserUtils.GetErrorName(e.ErrorCode)), "http://td/error");
}
}
}
private void plugins_PluginChangedState(object sender, PluginChangedStateEventArgs e){
browser.ExecuteScriptAsync("TDPF_setPluginState", e.Plugin, e.IsEnabled);
}
private void UserConfig_MuteToggled(object sender, EventArgs e){
UpdateProperties();
}
private void UserConfig_ZoomLevelChanged(object sender, EventArgs e){
BrowserUtils.SetZoomLevel(browser.GetBrowser(), Program.UserConfig.ZoomLevel);
}
// external handling
public UpdateHandler CreateUpdateHandler(UpdaterSettings settings){
return new UpdateHandler(browser, settings);
}
public void RefreshMemoryTracker(){
if (Program.SystemConfig.EnableBrowserGCReload){
memoryUsageTracker.Start(browser, Program.SystemConfig.BrowserMemoryThreshold);
}
else{
memoryUsageTracker.Stop();
}
}
public void HideVideoOverlay(bool focus){
if (focus){
browser.GetBrowser().GetHost().SendFocusEvent(true);
}
browser.ExecuteScriptAsync("$('#td-video-player-overlay').remove()");
}
// javascript calls
public void ReloadToTweetDeck(){
browser.ExecuteScriptAsync($"if(window.TDGF_reload)window.TDGF_reload();else window.location.href='{TwitterUtils.TweetDeckURL}'");
}
public void UpdateProperties(){
browser.ExecuteScriptAsync(PropertyBridge.GenerateScript(PropertyBridge.Environment.Browser));
}
public void InjectBrowserCSS(){
browser.ExecuteScriptAsync("TDGF_injectBrowserCSS", ScriptLoader.LoadResource("styles/browser.css").TrimEnd());
}
public void ReinjectCustomCSS(string css){
browser.ExecuteScriptAsync("TDGF_reinjectCustomCSS", css?.Replace(Environment.NewLine, " ") ?? string.Empty);
}
public void OnMouseClickExtra(IntPtr param){
browser.ExecuteScriptAsync("TDGF_onMouseClickExtra", (param.ToInt32() >> 16) & 0xFFFF);
}
public void ShowTweetDetail(string columnId, string chirpId, string fallbackUrl){
browser.ExecuteScriptAsync("TDGF_showTweetDetail", columnId, chirpId, fallbackUrl);
}
public void TriggerTweetScreenshot(){
browser.ExecuteScriptAsync("TDGF_triggerScreenshot()");
}
}
}

View File

@ -190,6 +190,7 @@
<Compile Include="Core\Other\Settings\TabSettingsTray.Designer.cs">
<DependentUpon>TabSettingsTray.cs</DependentUpon>
</Compile>
<Compile Include="Core\TweetDeckBrowser.cs" />
<Compile Include="Core\Utils\StringUtils.cs" />
<Compile Include="Core\Utils\TwitterUtils.cs" />
<Compile Include="Data\CombinedFileStream.cs" />