Imported Upstream version 0.5.11
This commit is contained in:
parent
50549da7c3
commit
ccd28b22eb
384 changed files with 235692 additions and 107186 deletions
16
.gitignore
vendored
16
.gitignore
vendored
|
@ -1,16 +0,0 @@
|
|||
Makefile
|
||||
|
||||
.waf-*
|
||||
.lock-wscript
|
||||
_build
|
||||
|
||||
po/.intltool-merge-cache
|
||||
po/LINGUAS
|
||||
po/POTFILES
|
||||
po/midori.pot
|
||||
po/stamp-it
|
||||
po/*.gmo
|
||||
|
||||
midori.desktop
|
||||
|
||||
packages.version
|
294
CMakeLists.txt
Normal file
294
CMakeLists.txt
Normal file
|
@ -0,0 +1,294 @@
|
|||
# Copyright (C) 2013 Christian Dywan <christian@twotoasts.de>
|
||||
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
cmake_policy(VERSION 2.6)
|
||||
# Work-around a bug in the included FindGettext fixed with 2.8.8
|
||||
# See http://www.cmake.org/pipermail/cmake-commits/2012-February/012117.html
|
||||
if ("${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}.${CMAKE_PATCH_VERSION}" VERSION_LESS "2.8.8")
|
||||
cmake_policy(SET CMP0002 OLD)
|
||||
endif ()
|
||||
project(midori C)
|
||||
add_definitions("-DPACKAGE_NAME=\"${CMAKE_PROJECT_NAME}\"")
|
||||
add_definitions("-DPACKAGE_BUGREPORT=\"https://bugs.launchpad.net/midori\"")
|
||||
|
||||
set(VERSION 0.5.11)
|
||||
add_definitions("-DMIDORI_VERSION_SUFFIX=\"${VERSION}\"")
|
||||
|
||||
string(REPLACE "." ";" VERSION_LIST ${VERSION})
|
||||
LIST(GET VERSION_LIST 0 MIDORI_MAJOR_VERSION)
|
||||
add_definitions("-DMIDORI_MAJOR_VERSION=${MIDORI_MAJOR_VERSION}")
|
||||
LIST(GET VERSION_LIST 1 MIDORI_MINOR_VERSION)
|
||||
add_definitions("-DMIDORI_MINOR_VERSION=${MIDORI_MINOR_VERSION}")
|
||||
LIST(GET VERSION_LIST 2 MIDORI_MICRO_VERSION)
|
||||
add_definitions("-DMIDORI_MICRO_VERSION=${MIDORI_MICRO_VERSION}")
|
||||
|
||||
execute_process(COMMAND "bzr" "revno"
|
||||
OUTPUT_VARIABLE REVISION
|
||||
ERROR_QUIET
|
||||
OUTPUT_STRIP_TRAILING_WHITESPACE)
|
||||
if (REVISION)
|
||||
set(VERSION "${VERSION}~r${REVISION}")
|
||||
# All warnings are errors in development builds
|
||||
set(VALAFLAGS ${VALAFLAGS} --fatal-warnings)
|
||||
set(CFLAGS "${CFLAGS}")
|
||||
endif ()
|
||||
add_definitions("-DPACKAGE_VERSION=\"${VERSION}\"")
|
||||
|
||||
list(APPEND CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake)
|
||||
# Disallow building during install to avoid permission problems
|
||||
set(CMAKE_SKIP_INSTALL_ALL_DEPENDENCY 1)
|
||||
|
||||
find_package(Vala REQUIRED)
|
||||
vala_require("0.16.0")
|
||||
set(VALAFLAGS ${VALAFLAGS}
|
||||
--enable-deprecated
|
||||
--debug
|
||||
)
|
||||
|
||||
include(GNUInstallDirs)
|
||||
set(DATADIR ${CMAKE_INSTALL_FULL_DATADIR})
|
||||
add_definitions("-DMDATADIR=\"${DATADIR}\"")
|
||||
add_definitions("-DSYSCONFDIR=\"${CMAKE_INSTALL_FULL_SYSCONFDIR}\"")
|
||||
add_definitions("-DLIBDIR=\"${CMAKE_INSTALL_FULL_LIBDIR}\"")
|
||||
add_definitions("-DDOCDIR=\"${CMAKE_INSTALL_FULL_DOCDIR}\"")
|
||||
|
||||
add_definitions("-DENABLE_NLS=1")
|
||||
add_definitions("-DLOCALEDIR=\"${CMAKE_INSTALL_FULL_LOCALEDIR}\"")
|
||||
|
||||
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/config.h" "/* # generated file (stub) */")
|
||||
add_definitions("-DHAVE_CONFIG_H=1")
|
||||
|
||||
find_file (UNISTD unistd.h)
|
||||
if (UNISTD)
|
||||
add_definitions("-DHAVE_UNISTD_H")
|
||||
endif ()
|
||||
|
||||
if (UNIX)
|
||||
find_file (SIGNAL signal.h)
|
||||
if (SIGNAL)
|
||||
add_definitions("-DHAVE_SIGNAL_H")
|
||||
endif ()
|
||||
|
||||
find_file (EXEC_INFO execinfo.h)
|
||||
if (EXEC_INFO)
|
||||
set(VALAFLAGS ${VALAFLAGS} -D HAVE_EXECINFO_H)
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
find_library (X11 X11)
|
||||
if (X11)
|
||||
# Pass /usr/X11R6/include for OpenBSD
|
||||
find_file (SCRNSAVER X11/extensions/scrnsaver.h /usr/X11R6/include)
|
||||
find_library (XSS Xss /usr/lib/X11R6/lib)
|
||||
if (SCRNSAVER AND XSS)
|
||||
add_definitions("-DHAVE_X11_EXTENSIONS_SCRNSAVER_H")
|
||||
set(OPTS_LIBRARIES "${OPTS_LIBRARIES};${XSS};${X11}")
|
||||
endif ()
|
||||
endif ()
|
||||
|
||||
if (WIN32)
|
||||
set(VALAFLAGS ${VALAFLAGS} -D HAVE_WIN32)
|
||||
endif ()
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD")
|
||||
set(VALAFLAGS ${VALAFLAGS} -D HAVE_FREEBSD)
|
||||
endif ()
|
||||
|
||||
if (${CMAKE_SYSTEM_NAME} MATCHES "DragonFly")
|
||||
set(VALAFLAGS ${VALAFLAGS} -D HAVE_DRAGONFLY)
|
||||
set(DFLY 1)
|
||||
endif ()
|
||||
|
||||
if (APPLE)
|
||||
add_definitions("-DHAVE_OSX=1")
|
||||
set(VALAFLAGS ${VALAFLAGS} -D HAVE_OSX)
|
||||
else ()
|
||||
add_definitions("-DHAVE_OSX=0")
|
||||
endif ()
|
||||
|
||||
find_package(PkgConfig)
|
||||
pkg_check_modules(DEPS REQUIRED
|
||||
libxml-2.0>=2.6
|
||||
sqlite3>=3.6.19
|
||||
gmodule-2.0
|
||||
gio-2.0>=2.32.3
|
||||
libsoup-gnome-2.4>=2.37.1
|
||||
)
|
||||
add_definitions("-DHAVE_LIBXML")
|
||||
add_definitions("-DGIO_VERSION=\"${DEPS_gio-2.0_VERSION}\"")
|
||||
add_definitions("-DLIBSOUP_VERSION=\"${DEPS_libsoup-gnome-2.4_VERSION}\"")
|
||||
set(PKGS posix linux libxml-2.0 sqlite3 gmodule-2.0 gio-2.0 libsoup-2.4)
|
||||
if (${DEPS_libsoup-gnome-2.4_VERSION} VERSION_GREATER "2.40.0")
|
||||
# valac 0.16 didn't have the bindings yet
|
||||
# For consistency we need to ensure C code makes the same assumptions
|
||||
if (${VALA_VERSION} VERSION_GREATER "0.17.0")
|
||||
add_definitions("-DHAVE_LIBSOUP_2_40_0")
|
||||
set(VALAFLAGS ${VALAFLAGS} -D HAVE_LIBSOUP_2_40_0)
|
||||
endif ()
|
||||
endif ()
|
||||
if (${DEPS_libsoup-gnome-2.4_VERSION} VERSION_GREATER "2.48.0")
|
||||
add_definitions("-DHAVE_LIBSOUP_2_48_0")
|
||||
set(VALAFLAGS ${VALAFLAGS} -D HAVE_LIBSOUP_2_48_0)
|
||||
endif ()
|
||||
|
||||
if (${DEPS_gio-2.0_VERSION} VERSION_GREATER "2.40.0" OR WIN32)
|
||||
add_definitions("-DLIBNOTIFY_VERSION=\"No\"")
|
||||
else ()
|
||||
pkg_check_modules(NOTIFY REQUIRED libnotify)
|
||||
add_definitions("-DLIBNOTIFY_VERSION=\"${NOTIFY_VERSION}\"")
|
||||
add_definitions("-DHAVE_LIBNOTIFY")
|
||||
set(OPTS_INCLUDE_DIRS "${OPTS_INCLUDE_DIRS};${NOTIFY_INCLUDE_DIRS}")
|
||||
set(OPTS_LIBRARIES "${OPTS_LIBRARIES};${NOTIFY_LIBRARIES}")
|
||||
set(PKGS ${PKGS} libnotify)
|
||||
endif ()
|
||||
|
||||
option(USE_GTK3 "Use GTK+3" OFF)
|
||||
option(HALF_BRO_INCOM_WEBKIT2 "Serve as a guniea pig" OFF)
|
||||
option(USE_ZEITGEIST "Zeitgeist history integration" ON)
|
||||
option(USE_GRANITE "Fancy notebook and pop-overs" OFF)
|
||||
option(USE_APIDOCS "API documentation" OFF)
|
||||
option(USE_GIR "Generate GObject Introspection bindings" OFF)
|
||||
option(EXTRA_WARNINGS "Additional compiler warnings" OFF)
|
||||
|
||||
# GTK+3 is implied here, whether set or not
|
||||
if (USE_GRANITE OR HALF_BRO_INCOM_WEBKIT2)
|
||||
set(USE_GTK3 ON)
|
||||
endif ()
|
||||
|
||||
if (USE_GRANITE)
|
||||
pkg_check_modules(GRANITE granite>=0.2)
|
||||
set(OPTS_INCLUDE_DIRS "${OPTS_INCLUDE_DIRS};${GRANITE_INCLUDE_DIRS}")
|
||||
set(OPTS_LIBRARIES "${OPTS_LIBRARIES};${GRANITE_LIBRARIES}")
|
||||
add_definitions("-DHAVE_GRANITE")
|
||||
add_definitions("-DGRANITE_VERSION=\"${GRANITE_VERSION}\"")
|
||||
set(VALAFLAGS ${VALAFLAGS} -D HAVE_GRANITE)
|
||||
set(PKGS ${PKGS} granite)
|
||||
else ()
|
||||
add_definitions("-DGRANITE_VERSION=\"No\"")
|
||||
endif()
|
||||
|
||||
if (USE_ZEITGEIST)
|
||||
pkg_check_modules(ZEITGEIST zeitgeist-2.0>=0.3.14)
|
||||
set(OPTS_INCLUDE_DIRS "${OPTS_INCLUDE_DIRS};${ZEITGEIST_INCLUDE_DIRS}")
|
||||
set(OPTS_LIBRARIES "${OPTS_LIBRARIES};${ZEITGEIST_LIBRARIES}")
|
||||
add_definitions("-DHAVE_ZEITGEIST")
|
||||
set(PKGS ${PKGS} zeitgeist-2.0)
|
||||
endif()
|
||||
|
||||
if (WIN32)
|
||||
add_definitions("-DGCR_VERSION=\"No\"")
|
||||
else ()
|
||||
if (USE_GTK3)
|
||||
pkg_check_modules(GCR REQUIRED gcr-3>=2.32)
|
||||
else ()
|
||||
pkg_check_modules(GCR REQUIRED gcr-base-3>=2.32)
|
||||
endif ()
|
||||
add_definitions("-DGCR_VERSION=\"${GCR_VERSION}\"")
|
||||
add_definitions("-DHAVE_GCR")
|
||||
set(OPTS_INCLUDE_DIRS ${OPTS_INCLUDE_DIRS} ${GCR_INCLUDE_DIRS})
|
||||
set(OPTS_LIBRARIES ${OPTS_LIBRARIES} ${GCR_LIBRARIES})
|
||||
endif ()
|
||||
|
||||
if (HALF_BRO_INCOM_WEBKIT2)
|
||||
pkg_check_modules(DEPS_GTK REQUIRED
|
||||
gtk+-3.0>=3.10.0
|
||||
webkit2gtk-4.0>=2.3.91
|
||||
)
|
||||
add_definitions("-DHAVE_WEBKIT2")
|
||||
add_definitions("-DGTK_VERSION=\"${DEPS_GTK_gtk+-3.0_VERSION}\"")
|
||||
add_definitions("-DWEBKIT_VERSION=\"${DEPS_GTK_webkit2gtk-4.0_VERSION}\"")
|
||||
set(PKGS ${PKGS} gtk+-3.0)
|
||||
# set(EXTRA_VAPIS ${EXTRA_VAPIS} "${CMAKE_SOURCE_DIR}/midori/webkit2gtk-web-extension-4.0.vapi")
|
||||
set(EXTRA_VAPIS ${EXTRA_VAPIS} "${CMAKE_SOURCE_DIR}/midori/webkit2gtk-4.0.vapi")
|
||||
set(VALAFLAGS ${VALAFLAGS} -D HAVE_GTK3)
|
||||
set(VALAFLAGS ${VALAFLAGS} -D HAVE_WEBKIT2)
|
||||
set(VALAFLAGS ${VALAFLAGS} -D HAVE_WEBKIT2_3_91)
|
||||
elseif (USE_GTK3)
|
||||
pkg_check_modules(DEPS_GTK REQUIRED
|
||||
gtk+-3.0>=3.10.0
|
||||
webkitgtk-3.0>=1.8.1
|
||||
javascriptcoregtk-3.0
|
||||
)
|
||||
add_definitions("-DGTK_VERSION=\"${DEPS_GTK_gtk+-3.0_VERSION}\"")
|
||||
add_definitions("-DWEBKIT_VERSION=\"${DEPS_GTK_webkitgtk-3.0_VERSION}\"")
|
||||
set(PKGS ${PKGS} gtk+-3.0)
|
||||
set(EXTRA_VAPIS ${EXTRA_VAPIS} "${CMAKE_SOURCE_DIR}/midori/webkitgtk-3.0.vapi")
|
||||
set(VALAFLAGS ${VALAFLAGS} -D HAVE_GTK3)
|
||||
else ()
|
||||
pkg_check_modules(DEPS_GTK REQUIRED
|
||||
gtk+-2.0>=2.24.0
|
||||
webkit-1.0>=1.8.1
|
||||
javascriptcoregtk-1.0
|
||||
)
|
||||
add_definitions("-DGTK_VERSION=\"${DEPS_GTK_gtk+-2.0_VERSION}\"")
|
||||
add_definitions("-DWEBKIT_VERSION=\"${DEPS_GTK_webkit-1.0_VERSION}\"")
|
||||
set(PKGS ${PKGS} gtk+-2.0)
|
||||
set(EXTRA_VAPIS ${EXTRA_VAPIS} "${CMAKE_SOURCE_DIR}/midori/webkitgtk-3.0.vapi")
|
||||
endif ()
|
||||
set(EXTRA_VAPIS ${EXTRA_VAPIS} "${CMAKE_SOURCE_DIR}/katze/katze.vapi")
|
||||
|
||||
# dh_translations detects this if there's no variable used
|
||||
set (GETTEXT_PACKAGE "midori")
|
||||
add_definitions("-DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\"")
|
||||
|
||||
set(CFLAGS "${CFLAGS} -Wall -Wundef -Wno-deprecated-declarations -g")
|
||||
|
||||
if (EXTRA_WARNINGS)
|
||||
LIST(APPEND EXTRA_CFLAGS_LIST
|
||||
-Wextra
|
||||
-Wno-unused-parameter
|
||||
-Wno-missing-field-initializers
|
||||
-Wno-comment
|
||||
-Waggregate-return
|
||||
-Wredundant-decls
|
||||
-Wshadow -Wpointer-arith -Wcast-align
|
||||
-Winline -Wformat-security -fno-common
|
||||
-Winit-self -Wundef
|
||||
-Wnested-externs
|
||||
)
|
||||
string(REPLACE ";" " " EXTRA_CFLAGS "${EXTRA_CFLAGS_LIST}")
|
||||
set(CFLAGS "${CFLAGS} ${EXTRA_CFLAGS}")
|
||||
else ()
|
||||
if (REVISION)
|
||||
set(CFLAGS "${CFLAGS} -Werror")
|
||||
endif()
|
||||
endif ()
|
||||
|
||||
# Explicitly add -fPIC for older toolchains
|
||||
set(VALA_CFLAGS "-g -fPIC")
|
||||
|
||||
# With compiler versions that can, enable exactly the non-spurious warnings
|
||||
# in Vala-generated C, otherwise disable warnings
|
||||
if ((CMAKE_C_COMPILER_ID STREQUAL "GNU" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER "5.0.0")
|
||||
OR (CMAKE_C_COMPILER_ID STREQUAL "Clang" AND CMAKE_C_COMPILER_VERSION VERSION_GREATER "3.0.0"))
|
||||
set(VALA_CFLAGS "${VALA_CFLAGS} -Werror=implicit-function-declaration")
|
||||
set(VALA_CFLAGS "${VALA_CFLAGS} -Wno-incompatible-pointer-types")
|
||||
set(VALA_CFLAGS "${VALA_CFLAGS} -Wno-discarded-qualifiers")
|
||||
set(VALA_CFLAGS "${VALA_CFLAGS} -Wno-deprecated-declarations")
|
||||
else ()
|
||||
set(VALA_CFLAGS "${VALA_CFLAGS} -w")
|
||||
endif ()
|
||||
|
||||
set(LIBMIDORI "${CMAKE_PROJECT_NAME}-core")
|
||||
|
||||
# CMake provides no uninstall target by design
|
||||
add_custom_target (uninstall
|
||||
COMMAND "xargs" "rm" "-v" "<" "install_manifest.txt")
|
||||
|
||||
install(FILES AUTHORS COPYING ChangeLog EXPAT README DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||
|
||||
add_subdirectory (midori)
|
||||
add_subdirectory (extensions)
|
||||
enable_testing()
|
||||
add_subdirectory (tests)
|
||||
add_subdirectory (po)
|
||||
add_subdirectory (icons)
|
||||
add_subdirectory (data)
|
||||
add_subdirectory (config)
|
||||
|
||||
if (USE_APIDOCS)
|
||||
add_subdirectory (docs/api)
|
||||
endif ()
|
||||
if (USE_GIR)
|
||||
add_subdirectory (gir)
|
||||
endif ()
|
403
ChangeLog
403
ChangeLog
|
@ -1,5 +1,408 @@
|
|||
This file is licensed under the terms of the expat license, see the file EXPAT.
|
||||
|
||||
v0.5.11
|
||||
Add fake theme for built-in icons
|
||||
* Don't truncate long speed dial titles if there's room to display them
|
||||
Fix warnings for -Wformat-security
|
||||
Ensure vala knows the prototypes of functions it calls, fixing pointer truncation in tests
|
||||
Add unit test to check appmenu/menubar visibility
|
||||
Fix last known GTK2 entry placeholder text bugs
|
||||
Make sure that only one of appmenu and menubar are visible *initially* as well as when changed
|
||||
Move adblock icons to hicolor
|
||||
Limit bookmarks panel callbacks to the lifetime of the panel to fix a crash
|
||||
Fix fallout (broken bookmarks and history panel search) from tweaks to GTK2 entry placeholder
|
||||
fix property binding to ensure that exactly one of appmenu button and menubar is always visible
|
||||
Skip open-with codepath with abp links, they are internal
|
||||
Use find_file to locate execinfo.h
|
||||
Fix middle/ctrl/normal clicking bookmarks (not folders) in the bookmarkbar.
|
||||
Add copright header to sanitize_bar.sh
|
||||
Adblock fixup: Escape . in filter with \
|
||||
Don't shadow variable uri in midori_browser_save_uri
|
||||
Switch Adblock icons to 24px color
|
||||
Always include app menu in toolbar
|
||||
Fix various mis[sing ]annotations and style issues in GIR
|
||||
Compile typelib from gir
|
||||
Fix assert when resetting webapp state after inactivity reset
|
||||
clean up handling of double-valued db column in Tabby
|
||||
Add a comment to explain MidoriBrowser popup callback
|
||||
fix warnings printed when right-clicking resize grip between location and search entries
|
||||
Win32: Use Dr. MinGW if present to preserve crash info
|
||||
Fix menubar warning caused by direct cast instead of `as`
|
||||
Helper script for setting up bzr with some usefull plugins and settings
|
||||
Stop using Gtk.Entry.max_width_chars
|
||||
avoid deprecated SoupServer API with libsoup 2.48
|
||||
Use unowned in foreach loops in Midori.Window
|
||||
Use unowned in foreach loops in Midori.Completion
|
||||
Use unowned with Adblock.Subscription and Element in foreach loops
|
||||
Use unowned strings in foreach loops
|
||||
Enable openWith in app mode and make it work with view-new
|
||||
Implement Midori.Window class with toolbar/ headerbar
|
||||
Drop support for libsoup-gnome-2.4 < 2.37.1
|
||||
Make search icons for engines work correctly
|
||||
Move to WebKit2 4.0 which broke ABI
|
||||
Port to zeitgeist-2.0
|
||||
win32: Bump shipped GrayBird theme version to fix some rendering issues
|
||||
avoid deprecated GtkDialog API with GTK+2 >= 2.22
|
||||
Title case for "Export Certificate" button
|
||||
fix incorrect type of MAX(sorting) in Tabby
|
||||
|
||||
v0.5.10
|
||||
use exit instead of return in license script
|
||||
Fix HAVE_GCR guards after GtkPopover port
|
||||
Remove example app and .desktop before creating it in the unit test
|
||||
Fix cache dir path in Adblock and always mkdir tmp
|
||||
Port location action from Granite.PopOver to Gtk.Popover
|
||||
Match https site when user-style is using domain syntax
|
||||
Always disable developer tools on Win32
|
||||
Reimplement Midori.URI.unescape and add various tests
|
||||
Make the inspector resizable with GTK3 by packing into a GtkScrolledWindow
|
||||
Don't build tabs2one in release builds
|
||||
Don't assume GNotification works on Win32
|
||||
update copyright date in About dialog
|
||||
Don't entity-escape history and bookmark results in location completion
|
||||
Only set tabs' error state if errors come from the main frame
|
||||
Implement Paste and Proceed as an action
|
||||
No Gcr on Win for the moment
|
||||
Yet another Speed Dial CSS update:
|
||||
Port bookmark popover from Granite to Gtk.Popover
|
||||
Make application choosers resizable with a sane default size
|
||||
Use GNotification >= 2.40 and use Midori.App API in webmedia
|
||||
Rework mouse button handling in KatzeArrayAction
|
||||
Don't bind :day in HistoryDatabase.query
|
||||
Make GCR mandatory for all builds
|
||||
Update coub support in mediaHerald
|
||||
history-list: Fix gtk+3 build caused by dropping "using Gtk;"
|
||||
Drop all remaining usages of "using *;"
|
||||
Don't open search engines menu when clearing search action
|
||||
Only remove apps in the sidepanel when left-clicking the delete icon
|
||||
Improve robustness of GTK3-compatibility placeholder text fallback
|
||||
Clean up vapi dependency
|
||||
tls_flags from webkit_web_view_get_tls_info need to be 0
|
||||
Don't add failed pages to history
|
||||
Throw error for wrong paramter in Statement.bind
|
||||
Replace NoJS "allow all pages" setting with "allow local pages"
|
||||
Avoid bugs due to race condition in addons delete dialog
|
||||
Calculate transfer progress at regular intervals to fix 0B/s bug and recalcitrant progess bars
|
||||
Fix warnings occurring with EXTRA_WARNINGS
|
||||
Escape parentheses in adblock_fixup_regexp()
|
||||
Use File.query_exist() on win32 when checking for db to attach
|
||||
Handle _NEW_WINDOW_ACTION explicitly to make _blank targets work
|
||||
Fix undefined behavior uint in mouse gestures
|
||||
fix JavaScript keyup event by calling inherited key-release-event handler in MidoriBrowser
|
||||
Inline renaming of speed dials
|
||||
Handle current_size and last_size of Download being equal
|
||||
Add proper copyright headers to element_hider and autosuggestcontrol
|
||||
Add X-GNOME-UsesNotifications to indicate the use of notifications
|
||||
Fix typo in Bookmarks menu UI definition
|
||||
|
||||
v0.5.9
|
||||
Remove dead code from browser and preferences
|
||||
Build-fix: Make PanedAction's Child.widget public
|
||||
fixes tab history undo
|
||||
Set a placeholder text on the URL entry
|
||||
Add "Add Bookmark" to menu
|
||||
Show search menu upon left icon click in location bar
|
||||
Fix crash when saving with associated resources
|
||||
Fix webkit2 downloads based on older branch
|
||||
don't hide window decorations for Midori-Granite
|
||||
Connect bookmarks-db singleton correctly to fix menus
|
||||
Fix some symbol names and transfer annotations in doc comments
|
||||
Use correct signature for window-state-event handler
|
||||
Do not overescape page titles in view completion
|
||||
Make adblock skip non-standard last update metadata strings
|
||||
Drop deprecated Granite LightWindow used for the Clear Private Data dialog
|
||||
Keep storing the last web media tab played.
|
||||
Allocate CookiePermissionManagerModalInfobar correctly
|
||||
Make middle clicking reload button duplicate the current tab, similar to other browsers
|
||||
Use network-changed of GNetworkMonitor to reload all tabs if network becomes available
|
||||
Show different messages based on network connectivity.
|
||||
Fix crash when activating the edit menu
|
||||
Fix "open all in tabs" for bookmarks
|
||||
Fix a few simple leaks
|
||||
Don't focus the locationaction when leaving blank pages
|
||||
Fix leaks of two references to the MidoriApp in Tabby
|
||||
Compile with valac 0.16 again
|
||||
Never display about:new in the urlbar
|
||||
fix crash right-clicking forms on local pages
|
||||
Share 'youtube, vimeo, dailymotion' that you are playing in Midori using org.midori.mediaHerald
|
||||
Give the SoupURI a path when checking cookie relevance
|
||||
Resolve ellipsis and title stripping in completion
|
||||
Add www. and .com/.country_domain and proceed with Ctrl+Enter/Shift+Enter with (readable code)
|
||||
Clean up browser tab/ uri/ title notify
|
||||
Drop pseudo Granite distinction in completion layout
|
||||
Fix visibility of SpeedDial, Toolbar, Bookmarkbar context menu items
|
||||
Distinguish between desc file missing and other parsing issues
|
||||
Use dependencies to clear test folders before execution
|
||||
win32: Drop dropbox usage from win release script, rename resulting output files
|
||||
|
||||
v0.5.8
|
||||
Use png icon instead of svg in set_status
|
||||
We must not pass a Cancellable to FaviconDatabase.get_favicon_pixbuf
|
||||
Retain spelling suggestion menu items from WebKit
|
||||
Properly guard usage of gtk3 get_style_context
|
||||
Mimic the look of Granite.DynamicNotebook when compiled with --enable-granite.
|
||||
Fix X11 lib underlink in midori-core
|
||||
Fix bookmarkbar bookmark click not opening links
|
||||
Use sanitized app URI as wm_classname/ StartupWMClass
|
||||
Make trunk build with WebKit2 again
|
||||
Fix for incorrect tstamp for background tabs
|
||||
Don't declare sorting doubles are nullable and print values when database tracing is enabled
|
||||
Correctly apply saved entry state and treat urlbar as a regular editable item
|
||||
Add missing conditional includes for granite flavoured build
|
||||
Open URIs dragged on tab label or new tab button
|
||||
Small adblock bugfixes
|
||||
Work around GTK3's hard-coded minimum stackswitcher button width
|
||||
Fix building with mingw packages from fedora 18
|
||||
Set page title as basis for print filename
|
||||
Rename notes inline
|
||||
Use EXTRA_WARNINGS option when building for windows
|
||||
Drop forgotten clutter init and obsolete header declarations
|
||||
Rework history-step handling and make it work again
|
||||
Port Tabby to DatabaseStatement API
|
||||
Replace bookmark stracing with generic profiling in Midori.Database
|
||||
Port autocompleter test to async job
|
||||
Finishing touches for Adblock
|
||||
Add filters and defaults
|
||||
Implement and use ContextAction.escaped
|
||||
printf URI in show_message_dialog for download error
|
||||
Improve docs and GIR annotations for KatzeItem, KatzeArray, and MidoriWebSettings
|
||||
Drop redundant TabNew from compact menu and put button in Tab Panel
|
||||
Fix loading file:// pages
|
||||
Implement Send Page Link by Email
|
||||
Use GtkStackSwitcher with GTK+ >= 3.10
|
||||
Implements context popup menu on menu entries of bookmark bar and bookmark menu.
|
||||
Fix building with newer mingw versions
|
||||
Display locationbar suggestions in the correct order
|
||||
Don't bother adblocking internal pages and favicons
|
||||
Don't use trailing comma on last list element in Adblock tests
|
||||
Rewrite Adblock more modularly, add Whitelist support
|
||||
Add support of DragonFlyBSD
|
||||
Change tooltips of Reload and ReloadStop actions while shift modifier is pressed
|
||||
Implement Midori.Database.attach method
|
||||
Allow :memory: as folder to make schema detection work
|
||||
More robust app/ profile creation
|
||||
Add helper callbacks to modify bookmark's tree store with unneded access to bookmarks db
|
||||
Implement more flexible fallback behavior for Cookie Permissions
|
||||
|
||||
v0.5.7:
|
||||
Modify actions and internal items in browser without changing settings
|
||||
Delay tab loading after Midori crashed
|
||||
Uncomment failing assertions about view_source in tab test
|
||||
Fallback to about:home if startup is anything but blank
|
||||
Don't try to create formhistory database if config_dir is NULL
|
||||
Handle url arguments for blank sessions
|
||||
Execute commands given at start time
|
||||
Introduce high-level prepare/ DatabaseStatement API
|
||||
Drop unused GraniteClutter-based animation support
|
||||
Drop uncommented contractor support
|
||||
Drop deprecated StaticNotebook used in KatzePreferences
|
||||
Introduce notebook class converging separate implementations
|
||||
Work around symbol relocation issue old version of gcc present on Ubuntu LTS
|
||||
NULL-check treeview in midori_search_action_get_editor
|
||||
Adjust CMakeList .ico check to not skip nojs icons
|
||||
Enable sidepanel in private mode
|
||||
Move Preferences menu entry above About
|
||||
Set minimum value of 0 on spin button for maximum cache size
|
||||
Give NextForward its own label for toolbar editor
|
||||
Correctly disable favicon database in app and private mode
|
||||
Change preferences to refer to proxy address as a "URI" (not "hostname")
|
||||
Add close tabs to right feature
|
||||
Allow printing without confirmation dialog on kiosk setups
|
||||
|
||||
v0.5.6:
|
||||
instead of creating devpet status icon on extension load, create it only to show new messages
|
||||
Open speed dial or homepage according to preference
|
||||
handle tab duplication
|
||||
Add copyright note to appdata file
|
||||
Tweak searching for resources when running from build folder
|
||||
Swap NULL-check with main frame check
|
||||
Use correct signal when clearing the trash
|
||||
Hide WEbGL preference if it is unavailable
|
||||
Remove stored popup sessions from the database
|
||||
Check all browsers for opened sessions and whether they're popups
|
||||
removed unused preference dialog and related code
|
||||
Fix check for found valac and mention VALAC variable
|
||||
Fix autoscrolling if page contains a frame with our custom error page
|
||||
Don't use context-menu signal in WebKitGTK+ < 1.10.0
|
||||
Fix building on Ubuntu 12.04
|
||||
Reset item ids when re-importing bookmarks
|
||||
Check path being NULL in export before trying to inspect it
|
||||
restore the last closed sessions if no session is opened
|
||||
Cast WebKitDOMHtmlElement for getting source content
|
||||
Use font-set signal and font family for GTK+ 3.2 font chooser
|
||||
add function to view dom source
|
||||
remove unused variable
|
||||
Resolve compiler warnings in current trunk
|
||||
Update win32-release script for cmake, move unused docs/scripts to old folder
|
||||
Try to handle previous runs of cmake in configure wrapper
|
||||
Correct view source assertions in tab unit test
|
||||
Build fix: found undeclared in midori_bookmarks_db_remove_item_recursive
|
||||
Cache bookmark items to avoid their recreation on database reads
|
||||
allow "view source" on about pages
|
||||
Enable old target policy on cmake < 2.8.8
|
||||
Re-arrange data file installing to be more explicit
|
||||
option to modify the number of tabs which will be restored in each idle callback
|
||||
Implement MidoriBookmarksDatabase class by inheritence from MidoriDatabase
|
||||
Ensure tab spinners update as often as the menubar spinner to avoid desync
|
||||
Use tabby sorting increment when importing session.xbel tabs
|
||||
Only install config files to /etc if prefix equals /usr
|
||||
handle urls as argument when starting midori
|
||||
Make tabby compile with Webkit2
|
||||
Drop waf build system and provide cmake-based "configure" script
|
||||
midori_panel_action_activate_cb forgot to update the action group
|
||||
Fixes bug where certificate Security overlay failed to close
|
||||
handle tab movement
|
||||
add tab sorting
|
||||
Untangle implicit GTK+3 for Granite and WebKit2
|
||||
Allow running test under debug tools with cmake
|
||||
Install config files to /etc when install prefix is /usr
|
||||
Add missing PO_FILES argument to GETTEXT_PROCESS_PO_FILES
|
||||
Add USE_APIDOCS to build API docs with CMake
|
||||
Rasterize SVG to PNG with rsvg-convert
|
||||
fix bookmarks test regression after fix-1179200-4
|
||||
Add CMakeLists.txt for config directory
|
||||
Install mo files in locale dir
|
||||
don't change uri/title if the tab isn't loaded
|
||||
use a separate signal to store the tab title
|
||||
Check if execinfo.h header exists on BSD
|
||||
fix endless loop in Midori.Database.init
|
||||
Use destructive-action style class in ClearPrivateData
|
||||
Initialize priv->element to avoid crash when freeing
|
||||
Introduces KatzeArray::update-item to handle metadata changes
|
||||
Refactor excuting schema from file into a function
|
||||
Use stock as string in liststore
|
||||
Drop needless (and wrong) HAVE_LIBNOTIFY in preferences
|
||||
Flip horizontal position of the overlay when hit by the mouse
|
||||
Add Midori.URI.get_base_domain and use it in NoJS
|
||||
Introduce Midori.Database and use for history and tabby
|
||||
ctrl+shift+w should trigger a delete-event
|
||||
Implement dialog windows opened via javascript
|
||||
Make get_res_filename work with different hierarchies
|
||||
fix check for new database
|
||||
Speed up session import
|
||||
Import tab title from old sessions
|
||||
Separate CFLAGS for C and add missing HAVE_
|
||||
Install top-level text files and FAQ html/ css to doc dir
|
||||
Provide and install .appdata.xml file for app stores
|
||||
Move bookmarks db handling to midori-bookmarks-db
|
||||
Add XSS to OPTS_LIBRARIES
|
||||
Update condition for UBUNTU_MENUPROXY to work on Saucy
|
||||
Introduce tabby, the new session manager
|
||||
Fix typo in katze_item_set_meta_integer call
|
||||
Allow bookmark bar update on additions resulting from imports
|
||||
Re-work midori_array_query_recursive to not include folder items twice
|
||||
Fix syntax of icon sizes passed to foreach
|
||||
Add bzr revision number to version if available
|
||||
Unify nojs and cookie policy dialogs, make policy changeable within the list
|
||||
Drop all G_ENABLE_DEBUG guards
|
||||
Add -g to CFLAGS to enable debugging symbols
|
||||
Adjust cmake build for Win32
|
||||
Implement CMake build setup
|
||||
Port MidoriApp from Unique/ sockets to GApplication
|
||||
New signal about-content to provide content for about uris
|
||||
Check if browser is NULL in midori_view_get_tab_menu to prevent a crash. Fixes bug #1215652.
|
||||
Ensure proxy setting widgets callbacks don't outlive the widgets themselves
|
||||
Fix webkit2 build error
|
||||
Show the bookmarks import location combobox.
|
||||
Rename internal completion URLs to avoid confusion
|
||||
|
||||
v0.5.5:
|
||||
Fix name and text fields inversion in XBEL folder import
|
||||
Correct packing of cookie and nojs permission dialog.
|
||||
Don't set tab title/special when a non-main frame displays an error
|
||||
Revise "cookies" debug output, merge expiry check and disallow revival of old cookies
|
||||
Drop now unused cgit module.xml file
|
||||
Use SoupProxyResolverGnome unconditionally and disable prefetching if proxy is active
|
||||
win32: Hide gui for profiles in webapp manager, as they are currently broken on Windows
|
||||
win32: support additional mouse buttons for going back/forward in history
|
||||
Enrich app error messages with filenames
|
||||
Fix segfault if url contains " %00"
|
||||
Replace 'Run as app' in bookmark dialog with 'Create launcher'
|
||||
Split config files and install from folders recursively
|
||||
Implement GTK+ theme switching via Preferences (Win32)
|
||||
Enable set_disk_cache_directory with WebKit2
|
||||
Introduce Midori.ContextAction and refactor page menu from scratch
|
||||
Define large dialog icon size relative to dialog icon size
|
||||
Extension Devpet which shows error messages and backtraces in systray
|
||||
WebKit2 cookie support
|
||||
Check the hit test result for editable to see if , should search
|
||||
Use SoupCookieJarSqlite and drop KatzeHttpCookies(Sqlite)
|
||||
Show folder tree when editing bookmarks
|
||||
Handle double value in _midori_browser_activate_action
|
||||
Add privacy preferences in web app mode
|
||||
Escape parentheses in adblock_fixup_regexp
|
||||
Introduce object oriented API for access to History Database
|
||||
Allow rss feeds with version 0.92
|
||||
Rename History completion to Bookmarks and History
|
||||
Don't show rss feed icon on twitter, underlying API was retired
|
||||
Read apps/ profiles from folder, leave launchers separate
|
||||
Fill in bookmark folder attributes in bookmarkbar populate
|
||||
|
||||
v0.5.4:
|
||||
Refactor history step and allow multiple title updates
|
||||
Call midori_browser_connect_tab with correct type
|
||||
Don't add HistoryCompletion if there's no history
|
||||
Restore reload button icon in error pages
|
||||
Don't insert folders into the log
|
||||
If an url is specified the fallback url should not be loaded
|
||||
Fixed crashes when closing a loading tab + granite's tab moving
|
||||
Test if plugins are redundant instead of skipping them all
|
||||
Avoid selecting bookmark uris that begin by 'javascript:' for completion
|
||||
Set FOREIGN_KEYS pragma on db initialization
|
||||
Implement a default zoom level preference
|
||||
Fix tautological use of G_MAXINT with enum
|
||||
Take current selection into account for bookmark folders when adding/editing bookmark
|
||||
Improve error page visuals, show suggestions on network errors
|
||||
Bump vala to 0.16.0
|
||||
Downgrade glib requirement to 2.32.3 to re-enable building under Ubuntu 12.04 (LTS)
|
||||
Bump glib2 version to 2.32.4
|
||||
Improve and unify thumbnail generation
|
||||
Omit speed dial and blank pages from view completion
|
||||
Makes the elements of the speed dial non-selectable
|
||||
Use NULL-safe comparison in katze_item_icon_loaded_cb
|
||||
Drop non-DOM style sheet injection code path
|
||||
Clean small leftovers from GTK and WebKit version bumps
|
||||
Bump GTK+ requirement to 2.24 and drop support for earlier versions
|
||||
Check for app mode to set browser icon instead of readonly
|
||||
Escape square brackets in adblock_fixup_regexp
|
||||
Fix showing (sub)folders in bookmarkbar
|
||||
Bump WebKit requirement to 1.8.3 and drop support for earlier versions
|
||||
Set menu on dynamic notebook tab
|
||||
Do not run toolbar editor's GtkDialog in its own main loop by prevent calling gtk_dialog_run(). Instead just set the GtkDialog modal and show it.
|
||||
Remove unnecesary harmful code from tab_switched_cb
|
||||
Fix segfault when deleteing tabs with history list
|
||||
Specify int64 id item as a string in bookmark remove/update queries
|
||||
Distinguish between box and event box in the tab label when colouring tabs
|
||||
Show visual feedback when hovering over items in bookmark panel
|
||||
Replace INSTALL/ HACKING with exported Contribute wiki page
|
||||
Delete tabs from history list with Del
|
||||
Check brightness of backgroung color when deciding foreground color of given tab
|
||||
Clean launcher filenames, double-click to open and delete button
|
||||
Avoid declaring browser twice within the same function
|
||||
Add ./waf --update-pot
|
||||
Fix memory leak introduced in r6184
|
||||
Use old function name g_dbus_generate_guid for old valac
|
||||
Move Import and Export into menu Bookmarks
|
||||
Collect multiple download notifications within a minute
|
||||
Fix segfault when right clicking on a web view.
|
||||
Make libnotify mandatory except on Windows
|
||||
Remove the rather unnecessary ./waf --run feature
|
||||
Send a notification after creating a launcher
|
||||
Ambiguous 'Open as App' context menu item was removed
|
||||
Apply label color to label rather than event box
|
||||
Store data of app mode based on URL in ~/.local/share/midori/apps
|
||||
Split colorful tabs code into helper functions and add unit tests
|
||||
Fix History List memory leak when closing Midori window.
|
||||
Replace .gitignore with a .bzrignore
|
||||
Always define GCR_VERSION in GTK+2 build
|
||||
Fix bookmarks dialog rename regression introduced in r6167.
|
||||
Drop check for gcr-3-gtk2 which isn't being maintained.
|
||||
Scrap unneeded background variables in location renderer callbacks
|
||||
Title case and proper packing in bookmark dialog
|
||||
Delete PO files Launchpad spewed into root directory when it couldn't find po/*.pot file.
|
||||
Issue a warning when trying to use MIDORI_DEBUG while running
|
||||
Update dates to 2013 to fix bug #1167075.
|
||||
|
||||
v0.5.2:
|
||||
Re-release with a proper version number and changelog
|
||||
|
||||
|
|
27
GNUmakefile.in
Normal file
27
GNUmakefile.in
Normal file
|
@ -0,0 +1,27 @@
|
|||
# Based on "http://iany.me/wiki/Makefile/" by "Ian Yang" licensed under "CC by 3.0"
|
||||
|
||||
BUILD_FOLDER := _build
|
||||
|
||||
CUSTOM_TARGETS := cmake
|
||||
|
||||
# Do not try to use custom target when invoking external makefile
|
||||
EXTERNAL_TARGETS := $(filter-out $(CUSTOM_TARGETS), $(MAKECMDGOALS))
|
||||
|
||||
# Call all targets using `Makefile` in build directory in one `make` command.
|
||||
$(or $(lastword $(EXTERNAL_TARGETS)),all):
|
||||
$(MAKE) -C $(BUILD_FOLDER) $(EXTERNAL_TARGETS)
|
||||
|
||||
# If no targets are specified, use the dummy `all` target
|
||||
.PHONY: $(EXTERNAL_TARGETS) all
|
||||
|
||||
# Do nothing for all targets but last. Also quiet the message "Noting to be done on xxx"
|
||||
$(filter-out $(lastword $(EXTERNAL_TARGETS)), $(EXTERNAL_TARGETS)):
|
||||
@cd .
|
||||
|
||||
cmake: $(BUILD_FOLDER)
|
||||
cd $(BUILD_FOLDER) && cmake ..
|
||||
|
||||
$(BUILD_FOLDER):
|
||||
mkdir $(BUILD_FOLDER)
|
||||
|
||||
.PHONY: cmake
|
476
HACKING
476
HACKING
|
@ -1,169 +1,383 @@
|
|||
This file is licensed under the terms of the expat license, see the file EXPAT.
|
||||
====== Midori - Contribute ======
|
||||
|
||||
+ Hacking guide for Midori +
|
||||
**This document is licensed under the LGPL 2.1.**
|
||||
|
||||
- How to contribute
|
||||
- Coding style
|
||||
- Source files in the project
|
||||
- Examplary source code
|
||||
====== Check out the sources ======
|
||||
|
||||
+++ How to contribute +++
|
||||
bzr branch lp:midori
|
||||
|
||||
There are several ways to contribute to the project:
|
||||
The development **trunk** (master, tip) is the latest iteration of the next release. Browse it online and look for other branches at http://code.launchpad.net/midori or http://bazaar.launchpad.net/~midori/midori/trunk/tarball download a tarball of the latest revision.
|
||||
|
||||
For translating, have a look at the file TRANSLATE.
|
||||
//The code used to be hosted in git.xfce.org/apps/midori.//
|
||||
|
||||
For helping with testing and triaging bug reports, you should register with the bug tracker at https://bugs.launchpad.net/midori and join #midori on irc.freenode.net where a lot of problems are discussed. You can start right away by trying to reproduce bug reports and comment with your findings.
|
||||
Keep your copy updated:
|
||||
|
||||
If you are interested in contributing code, there are a few options. You can join #midori to discuss a particular problem you would like to look into, or a feature you would want to implement. Opening a bug report or feature request if there isn't already one is the next step. To attract some attention, if you attached a patch or have questions, ask in #midori.
|
||||
bzr merge --pull
|
||||
====== Join IRC chat rooms ======
|
||||
|
||||
+++ Coding style +++
|
||||
Join irc://irc.freenode.net/midori #midori on Freenode https://kiwiirc.com/client/irc.freenode.net/midori or use webchat to talk about Midori, discuss bugs and new ideas.
|
||||
====== Contribute other than touching code ======
|
||||
|
||||
Indentation is 4 spaces, no tabs, preferrably at 80 to 120 columns per line to
|
||||
avoid automated line-breaks. Trailing whitespace is not desirable.
|
||||
* http://bugs.launchpad.net/midori Go through problem reports and check Unconfirmed bugs or those lacking information and mark any duplicates you spot
|
||||
* https://www.bountysource.com/#trackers/130181-midori Add a bounty for a feature or bug you'd like to support
|
||||
* https://translations.launchpad.net/midori/trunk/+pots/trunk Translate to your own language
|
||||
* https://github.com/eustasy/midori-browser.org/issues Report website bugs
|
||||
* Write http://wiki.xfce.org/midori/tutorial your own extension - granted that's code, too, but maybe a little easier than hacking the core.
|
||||
|
||||
Declarations go to the beginning of a block, not inline. Variables of one plain
|
||||
type may be grouped in one declaration, pointer types may not be grouped. The
|
||||
asterisk goes next to the type.
|
||||
Variables should be ordered in the order they are used.
|
||||
====== Documentation resources ======
|
||||
|
||||
Comments explain functionality, they should not state facts. The appropriate
|
||||
style is always C style /* */, not C++ style.
|
||||
* https://wiki.gnome.org/Projects/Vala/Tutorial Vala Tutorial
|
||||
* http://midori-browser.org/docs/api/vala/midori/ Midori Vala Docs
|
||||
* http://midori-browser.org/docs/api/c/html/ Midori C Docs
|
||||
|
||||
Variable and function names should be animal, animal_shelter or animalsc in
|
||||
case it would otherwise be very long. Loop counters may be single letters.
|
||||
Type names should be Animal, AnimalShelter or AnimalSC. No prefixes from third
|
||||
party projects should be used, such as GTK or WebKit, while underlines may be
|
||||
used but do not have a particular meaning.
|
||||
====== Build the code ======
|
||||
|
||||
There is a space between functions or keywords and round brackets, and curly
|
||||
brackets always go on separate lines, at the indentation level of the
|
||||
function, conditional or loop expression. Curly brackets are left out for single
|
||||
statement conditionals and loops unless they notably help readability.
|
||||
The type of a function goes on a separate line before the function.
|
||||
Preprocessor instructions are indented with the code they relate to.
|
||||
|
||||
Code history is precious, so one should avoid renaming a lot of functions at
|
||||
once or moving whole paragraphs only to add or remove a level of indentation.
|
||||
Moving blocks of code around is also undesriable because it makes patches less
|
||||
readable since the result looks like new code.
|
||||
mkdir _build
|
||||
cd _build
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr ..
|
||||
make
|
||||
sudo make install
|
||||
sudo gtk-update-icon-cache /usr/share/icons/hicolor
|
||||
|
||||
|
||||
+++ Source files in the project +++
|
||||
//Advanced Tip: Pass "-G Ninja" to cmake to use http://martine.github.io/ninja/ Ninja instead of make (usually packaged as ninja or ninja-build).//
|
||||
|
||||
Core:
|
||||
Files prefixed with "midori-" in the folder "midori" make up the core. They
|
||||
are essential to running the browser and mostly tailored to the browser.
|
||||
All header files prefixed with "midori-" are considered supported API and
|
||||
can be used to implement extensions.
|
||||
"sokoke" is a collection of very specialized helper functions which change
|
||||
from time to time as needed. In the past some of the code was moved to
|
||||
"katze" when it was considered generally useful.
|
||||
"socket" is a socket implementation with no dependency on other parts of
|
||||
the core, which is used if Midori is built without an external single
|
||||
instance support library.
|
||||
Panels:
|
||||
Files in the "panels" folder are classes that implement MidoriViewable and
|
||||
which are loaded into the MidoriPanel at startup. These panels are in
|
||||
principle optional.
|
||||
Katze:
|
||||
Re-usable classes and utility functions that don't depend on the core and
|
||||
some of that code indeed found its way into other projects.
|
||||
Extensions:
|
||||
These are extensions, written in C, which are loaded optionally if the user
|
||||
so chooses. Extensions can use API from "midori-" and "katze-" headers. Each
|
||||
module consists of either a single .c file or a folder with .c files.
|
||||
Tests:
|
||||
Unit tests are run regularly to verify possible regressions or measure
|
||||
performance of implementations. Except for select cases code changes should
|
||||
not cause failure of unit tests.
|
||||
If using GTK+3 you'll want to add -DUSE_GTK3=1 to the cmake command line.
|
||||
|
||||
You can build Midori using another C Compiler for example Clang. Just
|
||||
add -DCMAKE_C_COMPILER=/path/to/compiler to the cmake arguments.
|
||||
Then you can build following your normal procedure. Like this:
|
||||
cmake -DCMAKE_INSTALL_PREFIX=/usr -DCMAKE_C_COMPILER=/usr/bin/clang ..
|
||||
make
|
||||
|
||||
Midori can be **run without being installed**.
|
||||
|
||||
_build/midori/midori
|
||||
|
||||
You can use a **temporary folder for testing** without affecting normal settings
|
||||
|
||||
_build/midori/midori -c /tmp/midoridev
|
||||
|
||||
You'll want to **unit test** the code if you're testing a new version or contributed your own changes:
|
||||
|
||||
xvfb-run make check
|
||||
|
||||
Automated daily builds in Launchpad (https://launchpad.net/~elementary-os/+archive/daily ppa:elementary-os/daily and https://launchpad.net/~midori/+archive/midori-dev ppa:midori/midori-dev) run these tests as well.
|
||||
====== Debugging issues ======
|
||||
|
||||
Testing an installed release may reveal crashers or memory corruption which require investigating from a local build and obtaining a stacktrace (backtrace, crash log).
|
||||
|
||||
_build/midori/midori -g [OPTIONAL ARGUMENTS]
|
||||
|
||||
If the problem is a warning, not a crash GLib has a handy feature
|
||||
|
||||
env G_DEBUG=all _build/midori/midori -g
|
||||
|
||||
For more specific debugging output, depending on the feature in question you may use
|
||||
|
||||
env MIDORI_DEBUG=help _build/midori/midori
|
||||
|
||||
Whilst -g is convenient you may want to use proper gdb:
|
||||
|
||||
gdb
|
||||
file _build/midori/midori
|
||||
run
|
||||
…
|
||||
bt
|
||||
|
||||
On Windows you can open the folder where Midori is installed and double-click gdb.exe. A black command line window should appear.
|
||||
|
||||
file midori.exe
|
||||
run
|
||||
…
|
||||
bt
|
||||
|
||||
To verify a regression you might need to revert a particular change:
|
||||
|
||||
|
||||
+++ Examplary source code +++
|
||||
# Revert only r6304
|
||||
bzr merge . -r 6304..6303
|
||||
|
||||
/*
|
||||
Copyright
|
||||
LICENSE TEXT
|
||||
*/
|
||||
====== Coding style and quality ======
|
||||
|
||||
#include "foo.h"
|
||||
Midori code should in general have:
|
||||
|
||||
#include "bar.h"
|
||||
* 4 space indentation, no tabs
|
||||
* Between 80 to 120 columns
|
||||
* Prefer /* */ style comments
|
||||
* Call variables //animal// and //animal_shelter// instead of <del>camelCase</del>
|
||||
* Keep a space between functions/ keywords and round parentheses
|
||||
|
||||
#include <glib.h>
|
||||
For Vala:
|
||||
|
||||
void
|
||||
foobar (FooEnum bar, const gchar* foo)
|
||||
{
|
||||
gint n, i;
|
||||
* Prefer //new Gtk.Widget ()// over //using Gtk; new Widget ()//
|
||||
* Stick to standard Vala-style curly parentheses on the same line
|
||||
* Cuddled //} else {// and //} catch (Error error) {//
|
||||
|
||||
if (!foo)
|
||||
return;
|
||||
For C:
|
||||
|
||||
#ifdef BAR_STRICT
|
||||
if (bar == FOO_N)
|
||||
{
|
||||
g_print ("illegal value for 'bar'.\n");
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
* Always keep { and } on their own line
|
||||
|
||||
/* this is an example */
|
||||
switch (bar)
|
||||
{
|
||||
case FOO_FOO:
|
||||
n = bar + 1;
|
||||
break;
|
||||
case FOO_BAR:
|
||||
n = bar + 10;
|
||||
break;
|
||||
default:
|
||||
n = 1;
|
||||
}
|
||||
//Extensions may historically diverge from the standard styling on a case-by-case basis//
|
||||
|
||||
for (i = 0; i < n; i++)
|
||||
{
|
||||
g_print ("%s\n", foo);
|
||||
}
|
||||
}
|
||||
====== Committing code ======
|
||||
|
||||
Header file example:
|
||||
Tell Bazaar your name if you haven't yet
|
||||
bzr whoami "Real Name <email@address>"
|
||||
|
||||
/*
|
||||
Copyright
|
||||
LICENSE TEXT
|
||||
*/
|
||||
See what you did so far
|
||||
bzr diff
|
||||
|
||||
#ifndef __FOO_H__
|
||||
#define __FOO_H__ 1
|
||||
Get an overview of changed and new files
|
||||
bzr status
|
||||
|
||||
#ifdef HAVE_BAR_H
|
||||
#define BAR_STRICT
|
||||
#endif
|
||||
Add new files, move/ rename or delete
|
||||
bzr add FILENAME
|
||||
bzr mv OLDFILE NEWFILE
|
||||
bzr rm FILENAME
|
||||
|
||||
/* Types */
|
||||
Commit all current changes - Bazaar automatically picks up edited files. //If you're used to git, think of an implicit staging area.//
|
||||
bzr commit -p
|
||||
|
||||
typedef enum
|
||||
{
|
||||
FOO_FOO,
|
||||
FOO_BAR,
|
||||
FOO_N
|
||||
} FooEnum;
|
||||
If you have one or more related bug reports you should pass them as arguments. Once these commits are merged the bug will automatically be closed and the commit log shows clickable links to the reports.
|
||||
bzr commit -p --fixes=lp:1111999
|
||||
|
||||
typedef struct
|
||||
{
|
||||
FooEnum foo_bar;
|
||||
} FooStruct;
|
||||
If you've done several commits
|
||||
bzr log | less
|
||||
bzr log -p | less
|
||||
|
||||
/* Declarations */
|
||||
In the case you committed something wrong or want to ammend it:
|
||||
bzr uncommit
|
||||
|
||||
void
|
||||
foo_bar (FooEnum bar,
|
||||
const gchar* foo);
|
||||
If you end up with unrelated debugging code or other patches in the current changes, it's sometimes handy to temporarily clean up. //This may be seen as bzr's version of git stash.//
|
||||
bzr shelve
|
||||
bzr commit -p
|
||||
bzr unshelve
|
||||
|
||||
const gchar*
|
||||
foo_foo (FooStruct foo_struct,
|
||||
guint number,
|
||||
gboolean flag);
|
||||
Remember to keep your branch updated:
|
||||
bzr merge --pull
|
||||
|
||||
#endif /* !__FOO_H__ */
|
||||
As a general rule of thumb, ''bzr help COMMAND'' gives you an explanation of any command and ''bzr help commands'' lists all available commands.
|
||||
|
||||
//If you're a die-hard git user, http://zyga.github.io/git-lp/ checkout git-lp to use git commands with the Bazaar repository.//
|
||||
====== Push proposed changes ======
|
||||
|
||||
If you haven't yet, https://launchpad.net/~/+editsshkeys check that Launchpad has your SSH key - you can create an SSH key with **Passwords and Keys** aka **Seahorse** or ''ssh-keygen -t rsa'' - and use ''bzr launchpad-login'' to make youself known to bzr locally.
|
||||
|
||||
If you checked out trunk, and added your patch(es), just **push it under your username** in Launchpad and you can **propose it for merging into trunk**. This will automatically request a **review from other developers** who can then comment on it and provide feedback.
|
||||
|
||||
bzr push --remember lp:~USERNAME/midori/fix-bug1120383 && bzr lp-propose-merge lp:midori
|
||||
|
||||
lp-propose-merge command may not be working on some distributions like Arch or Fedora.
|
||||
In case you get error like //bzr: ERROR: Unable to import library "launchpadlib": No module named launchpadlib// just use Launchpad's Web UI to propose a merge.
|
||||
|
||||
|
||||
**What happens to all the branches?**
|
||||
|
||||
Leave the branches alone, **approved branches are cleared automatically** by Launchpad.
|
||||
|
||||
For larger feature branches, **use the team** in Launchpad to allow other developers to work on the code with you.
|
||||
|
||||
bzr push --remember lp:~midori/midori/featuritis && bzr lp-propose-merge lp:midori
|
||||
|
||||
What if I want to help out on an **existing merge request** that I can't push to?
|
||||
|
||||
|
||||
bzr branch ~OTHERPERSON/midori/fix-bug1120383
|
||||
cd fix-bug1120383
|
||||
# make commits
|
||||
bzr push lp:USERNAME~/midori/fix-bug1120383
|
||||
bzr lp-propose-merge ~OTHERPERSON/midori/fix-bug1120383
|
||||
|
||||
|
||||
Updating a branch that may be out of sync with trunk:
|
||||
|
||||
|
||||
bzr pull
|
||||
bzr: ERROR: These branches have diverged
|
||||
bzr merge lp:midori
|
||||
# Hand-edit conflicting changes
|
||||
bzr resolve FILENAME
|
||||
# If any conflicts remain continue fixing
|
||||
bzr commit -m 'Merge lp:midori'
|
||||
|
||||
|
||||
Save a little bandwidth, **branch from an existing local copy** that you keep around:
|
||||
|
||||
|
||||
bzr branch lp:midori midori
|
||||
bzr branch midori midori.fix-bug1120383
|
||||
cd midori.fix-bug1120383
|
||||
bzr pull lp:midori
|
||||
|
||||
|
||||
====== Backwards compatibility ======
|
||||
As of Midori 0.5.4 the formula is:
|
||||
* Required dependencies need to be available on the previous stable https://apps.fedoraproject.org/packages/s/webkit Fedora and http://packages.ubuntu.com/search?suite=quantal&keywords=webkit&searchon=names Ubuntu
|
||||
* For reference http://openports.se/www/webkit OpenBSD
|
||||
* Windows XP through 8 are to date ABI compatible, all dependencies are included
|
||||
|
||||
^ package ^ F17 (2012-05-29) ^ U 12.10 (2012-10-18) ^
|
||||
| glib2 | 2.32.4 | 2.34.0 |
|
||||
| vala | 0.16.1 | 0.16 |
|
||||
| gtk3 | 3.4.4 | 3.6.0 |
|
||||
| gtk2 | 2.24.13 | 2.24.13 |
|
||||
| soup | 2.38.1 | 2.40 |
|
||||
| webkit | 1.8.3-1.fc17 | 1.10.0-0ubuntu1 |
|
||||
====== Midori with(out) Granite ======
|
||||
When built with Granite (-DUSE_GRANITE=1 or --enable-granite) there're a few key differences:
|
||||
* Preferences uses a http://valadoc.elementaryos.org/Granite/Granite.Widgets.StaticNotebook.html Granite.Widgets.StaticNotebook
|
||||
* URL completion styling is slightly different
|
||||
* Clear Private Data uses **Granite.Widgets.LightWindow**
|
||||
* Edit Bookmark and Security Details use http://valadoc.elementaryos.org/Granite/Granite.Widgets.PopOver.html Granite.Widgets.PopOver instead of Gtk.Window
|
||||
|
||||
====== Midori for Windows ======
|
||||
|
||||
===== For Linux developers =====
|
||||
==== Dependencies ====
|
||||
Midori for Windows is compiled on a Linux host and MinGW stack. For the current build Fedora 18 packages are used. Packages needed are listed below:
|
||||
|
||||
yum install gcc vala intltool
|
||||
|
||||
For a native build
|
||||
yum install libsoup-devel webkitgtk3-devel sqlite-devel
|
||||
|
||||
For cross-compilation
|
||||
yum install mingw{32,64}-webkitgtk3 mingw{32,64}-glib-networking mingw{32,64}-gdb mingw{32,64}-gstreamer-plugins-good
|
||||
|
||||
Packages needed when assembling the archive
|
||||
yum install faenza-icon-theme p7zip mingw32-nsis greybird-gtk3-theme
|
||||
|
||||
Installing those should get you the packages needed to successfully build and develop Midori for Win32.
|
||||
==== Building ====
|
||||
For 32-bit builds:
|
||||
|
||||
mkdir _mingw32
|
||||
cd _mingw32
|
||||
mingw32-cmake .. -DUSE_ZEITGEIST=0 -DUSE_GTK3=1 -DCMAKE_INSTALL_PREFIX=/usr/i686-w64-mingw32/sys-root/mingw -DCMAKE_VERBOSE_MAKEFILE=0
|
||||
make
|
||||
sudo make install
|
||||
|
||||
For 64-bit builds:
|
||||
|
||||
mkdir _mingw64
|
||||
cd _mingw64
|
||||
mingw64-cmake .. -DUSE_ZEITGEIST=0 -DUSE_GTK3=1 -DCMAKE_INSTALL_PREFIX=/usr/x86_64-w64-mingw32/sys-root/mingw -DCMAKE_VERBOSE_MAKEFILE=0
|
||||
make
|
||||
sudo make install
|
||||
|
||||
Once built and tested you can assemble the Midori archive with a helper script
|
||||
32-bit build:
|
||||
env MINGW_PREFIX="/usr/i686-w64-mingw32/sys-root/mingw" ./win32/makedist/makedist.midori
|
||||
64-bit build:
|
||||
env MINGW_PREFIX="/usr/x86_64-w64-mingw32/sys-root/mingw/" ./win32/makedist/makedist.midori x64
|
||||
===== Testing =====
|
||||
For testing your changes unfortuantely a real system is needed because Midori and WebKitGTK+ don't work properly under Wine. Even if it works some problems are not visible when using Wine, but are present when running under a real Windows system and vice versa.
|
||||
|
||||
One way around it is to virtualize Windows on a Linux host and mount your MinGW directories as a network drive or shared folder.
|
||||
|
||||
===== For Windows developers =====
|
||||
Rough list of prerequisites for building with MinGW on Windows
|
||||
|
||||
If in doubt whether to get 32 or 64 bit versions use 32 bit ones, they are more
|
||||
universal and tend to be less broken.
|
||||
|
||||
|
||||
==== MinGW compiler ====
|
||||
Compiler should match the one that was used to build packages ideally.
|
||||
|
||||
* We will user *mingw64 rubenvb* release
|
||||
* Lastest stable release is gcc 4.8.0
|
||||
|
||||
[[http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting
|
||||
%20Win64/Personal%20Builds/rubenvb/gcc-4.8-release/|Releases]]
|
||||
|
||||
[[http://sourceforge.net/projects/mingw-w64/files/Toolchains%20targetting
|
||||
%20Win64/Personal%20Builds/rubenvb/gcc-4.8-release/x86_64-w64-mingw32-gcc-4.8.0-
|
||||
win32_rubenvb.7z/download|Download]]
|
||||
|
||||
|
||||
==== 7zip ====
|
||||
We will need 7zip to extract various archives
|
||||
|
||||
http://www.7-zip.org/download.html Homepage
|
||||
|
||||
|
||||
http://downloads.sourceforge.net/sevenzip/7z920.exe 32bit Installer
|
||||
|
||||
|
||||
==== Python3 (to extract rpms) ====
|
||||
We will need python3 to use download-mingw-rpm.py script.
|
||||
If you don't plan to use it you can safely skip this step.
|
||||
|
||||
We get python3, whatever is the lastes stable release.
|
||||
|
||||
http://www.python.org/download/releases/3.3.5 Releases
|
||||
|
||||
http://www.python.org/downloads/release/python-335/ Download
|
||||
|
||||
http://www.python.org/ftp/python/3.3.5/python-3.3.5.amd64.msi Installer
|
||||
|
||||
Install Python and be sure to check "addd python.exe to path" installer checkbox.
|
||||
|
||||
==== download-mingw-rpm.py ====
|
||||
We get download-mingw-rpm.py script from github. It uses Python3 and should fetch and
|
||||
unpack rpm files for us.
|
||||
|
||||
[[https://github.com/mkbosmans/download-mingw-rpm/blob/master/download-
|
||||
mingw-rpm.py|View Script]]
|
||||
|
||||
[[https://github.com/mkbosmans/download-mingw-rpm/raw/master/download-
|
||||
mingw-rpm.py|Download Script]]
|
||||
|
||||
Usage:
|
||||
|
||||
* Launch cmd.exe
|
||||
* Navigate to folder where the script was saved
|
||||
* Make sure that python can access 7z.exe
|
||||
* Run command and wait, it should extract the packages into your current directory
|
||||
|
||||
c:\Python33\python.exe download-mingw-rpm.py -u http://ftp.wsisiz.edu.pl/pub/linux/fedora/linux/updates/18/i386/ --deps mingw32-webkitgtk mingw32-glib-networking mingw32-gdb mingw32-gstreamer-plugins-good
|
||||
|
||||
[[http://dl.fedoraproject.org/pub/fedora/linux/releases/18/Everything/i386/os/Packages
|
||||
/m/|Fedora 18 packages]]
|
||||
|
||||
The above URL for some reason does not work with the script.
|
||||
|
||||
==== MSYS ====
|
||||
Msys contains shell and some small utilities
|
||||
|
||||
[[http://sourceforge.net/projects/mingw-w64/files/External%20binary
|
||||
%20packages%20%28Win64%20hosted%29/MSYS%20%2832-bit%29/MSYS-20111123.zip/download|Download]]
|
||||
|
||||
|
||||
==== CMake ====
|
||||
http://www.cmake.org/cmake/resources/software.html Homepage
|
||||
http://www.cmake.org/files/v2.8/cmake-2.8.12.2-win32-x86.exe Installer
|
||||
|
||||
When installing check the installer checkbox "add to path"
|
||||
|
||||
|
||||
==== Bazaar ====
|
||||
http://wiki.bazaar.canonical.com/WindowsDownloads Homepage
|
||||
|
||||
We will get 2.4 Stable Release (standalone)
|
||||
|
||||
http://launchpad.net/bzr/2.4/2.4.2/+download/bzr-2.4.2-1-setup.exe Installer
|
||||
|
||||
When installing check the installer checkbox "add to path"
|
||||
|
||||
==== Vala ====
|
||||
http://ftp.gnome.org/pub/gnome/sources/vala/0.20/vala-0.20.0.tar.xz Source
|
||||
|
||||
==== Globbing it all together ====
|
||||
|
||||
Extracted rpms msys and mingw packages should form uniform unix like folder.
|
||||
You use msys.bat to launch a shell
|
||||
|
||||
====== Jargon ======
|
||||
* freeze: a period of bug fixes only eg. 4/2 cycle means 4 weeks of features and 2 weeks to focus on resolving existing problems
|
||||
* MR: merge request, a branch proposed for review
|
||||
* ninja: an internal tab, usually empty label, used for taking screenshots
|
||||
* fortress: user of an ancient release like 0.4.3 as found on Raspberry Pie, Debian, Ubuntu
|
||||
* katze, sokoke, tabby: API names and coincidentally cat breeds
|
82
INSTALL
82
INSTALL
|
@ -1,82 +0,0 @@
|
|||
This file is licensed under the terms of the expat license, see the file EXPAT.
|
||||
|
||||
+++ Installing Midori +++
|
||||
|
||||
Building and installing Midori is straightforward.
|
||||
|
||||
Make sure you have Python 2.4 or higher installed.
|
||||
|
||||
Change to the Midori folder on your hard disk in a terminal.
|
||||
|
||||
Run './waf configure'
|
||||
|
||||
Run './waf build'
|
||||
|
||||
You can now run Midori from the build folder like so
|
||||
|
||||
'_build/default/midori/midori'
|
||||
|
||||
Midori will pick up extensions and resources from the build folder;
|
||||
it will NOT use localizations.
|
||||
|
||||
You can install it with './waf install'
|
||||
|
||||
If you need to do a clean rebuild, you can do either './waf clean'
|
||||
in order to remove binaries or './waf distclean' which deletes generated
|
||||
configuration files as well.
|
||||
|
||||
For further options run './waf --help'
|
||||
|
||||
+++ Debugging Midori +++
|
||||
|
||||
Midori is by default built with debugging symbols, make sure you have
|
||||
installed 'gdb', the GNU Debugger.
|
||||
|
||||
It's a good idea to execute all unit test cases and see that they pass.
|
||||
|
||||
'xvfb-run ./waf check'
|
||||
|
||||
In this example, Xvfb is used to avoid relying on the local user setup.
|
||||
|
||||
You can also run Midori proper as 'gdb _build/default/midori/midori'.
|
||||
|
||||
Inside gdb, type 'run'.
|
||||
|
||||
Try to reproduce a crash that you experienced earlier,
|
||||
this time Midori will freeze at the point of the crash.
|
||||
Switch to your terminal, type bt ('backtrace') and hit Return.
|
||||
What you obtained now is a backtrace that should include
|
||||
function names and line numbers.
|
||||
|
||||
If the problem is a warning and not a crash, try this:
|
||||
|
||||
'G_DEBUG=all gdb _build/default/midori/midori'
|
||||
|
||||
If you are interested in HTTP communication, try this:
|
||||
|
||||
'MIDORI_DEBUG=headers _build/default/midori/midori'
|
||||
|
||||
Where 'headers' can be replaced with 'body' to get full message contents.
|
||||
|
||||
If you are interested in (non-) touchscreen behaviour, try this:
|
||||
|
||||
'MIDORI_TOUCHSCREEN=1 _build/default/midori/midori', or
|
||||
|
||||
'MIDORI_TOUCHSCREEN=0 _build/default/midori/midori'
|
||||
|
||||
If you want to "dry run" without WebKitGTK+ rendering, try this:
|
||||
|
||||
'MIDORI_DEBUG=unarmed _build/default/midori/midori'
|
||||
|
||||
If you want to test bookmarks, you can enable database tracing:
|
||||
|
||||
'MIDORI_DEBUG=bookmarks _build/default/midori/midori'
|
||||
|
||||
To disable Netscape plugins, use MOZ_PLUGIN_PATH=/.
|
||||
|
||||
When running from the build folder, extensions will also be located
|
||||
in the build folder (setting MIDORI_EXTENSION_PATH is no longer needed).
|
||||
|
||||
For further information a tutorial for gdb and
|
||||
reading up on how you can install debugging
|
||||
symbols for libraries used by Midori are recommended.
|
31
README
31
README
|
@ -1,22 +1,25 @@
|
|||
This file is licensed under the terms of the expat license, see the file EXPAT.
|
||||
|
||||
Midori is a lightweight web browser.
|
||||
Midori is a fast little WebKit browser with support for HTML5. It can manage
|
||||
many open tabs and windows. The URL bar completes history, bookmarks, search
|
||||
engines and open tabs out of the box. Web developers can use the powerful
|
||||
web inspector that is a part of WebKit. Individual pages can easily be turned
|
||||
into web apps and new profiles can be created on demand.
|
||||
|
||||
* Full integration with GTK+2 and GTK+3.
|
||||
* Fast rendering with WebKit and HTML5 video with GStreamer.
|
||||
* Tabs, windows and session management.
|
||||
* History completion and configurable Web Search.
|
||||
* User scripts and user styles support.
|
||||
* Adblock Plus compatible, external download manager support.
|
||||
* Straightforward bookmark management.
|
||||
* Customizable interface, extensions written in C and Vala.
|
||||
A number of extensions are included by default:
|
||||
|
||||
Requirements: GLib 2.22, GTK+ 2.16, WebkitGTK+ 1.1.17, libXML2,
|
||||
libsoup 2.27.90, sqlite 3.0, Vala 0.14
|
||||
* Adblock with support for ABP filter lists and custom rules is built-in.
|
||||
* You can download files with Aria2 or SteadyFlow.
|
||||
* User scripts and styles support a la Greasemonkey.
|
||||
* Managing cookies and scripts via NoJS and Cookie Security Manager.
|
||||
* Switching open tabs in a vertical panel or a popup window.
|
||||
|
||||
Optional: GTK+ 3.0, Unique 0.9, libnotify, gcr, Granite 0.2, WebKit2GTK+ 1.11.91/ 2.0.0
|
||||
Requirements: GLib 2.32.3, GTK+ 2.24, WebkitGTK+ 1.8.1, libXML2,
|
||||
libsoup 2.27.90, sqlite 3.0, Vala 0.16, libnotify
|
||||
|
||||
For installation instructions read INSTALL.
|
||||
Optional: GTK+ 3.0, gcr, Granite 0.2, WebKit2GTK+ 1.11.91/ 2.0.0
|
||||
|
||||
For installation instructions read the file HACKING.
|
||||
|
||||
Please report comments, suggestions and bugs to:
|
||||
https://bugs.launchpad.net/midori
|
||||
|
@ -24,4 +27,4 @@ Please report comments, suggestions and bugs to:
|
|||
And join the IRC channel #midori on irc.freenode.net
|
||||
|
||||
Check for new versions at:
|
||||
http://www.twotoasts.de
|
||||
http://www.midori-browser.org
|
||||
|
|
55
cmake/ContainTest.cmake
Normal file
55
cmake/ContainTest.cmake
Normal file
|
@ -0,0 +1,55 @@
|
|||
# Copyright (C) 2013 Christian Dywan <christian@twotoasts.de>
|
||||
|
||||
include(ParseArguments)
|
||||
|
||||
macro(contain_test test_name executable)
|
||||
parse_arguments(ARGS "test_name;executable" "" ${ARGN})
|
||||
set(TEST_ENV "")
|
||||
foreach(VARIABLE XDG_CACHE_HOME XDG_CONFIG_HOME XDG_DATA_HOME XDG_RUNTIME_DIR TMPDIR)
|
||||
set(CONTAINER "${CMAKE_CURRENT_BINARY_DIR}/${test_name}-folders/${VARIABLE}")
|
||||
set(TEST_ENV "${TEST_ENV}${VARIABLE}=${CONTAINER};")
|
||||
endforeach()
|
||||
|
||||
add_dependencies(check contain-${test_name})
|
||||
|
||||
set_tests_properties(${test_name} PROPERTIES
|
||||
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
|
||||
TIMEOUT 42
|
||||
ENVIRONMENT "${TEST_ENV}"
|
||||
)
|
||||
add_custom_target("contain-${test_name}"
|
||||
COMMAND ${CMAKE_COMMAND} -E remove_directory ${CMAKE_CURRENT_BINARY_DIR}/${test_name}-folders
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${test_name}-folders/XDG_CACHE_HOME
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${test_name}-folders/XDG_CONFIG_HOME
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${test_name}-folders/XDG_DATA_HOME
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${test_name}-folders/XDG_RUNTIME_DIR
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/${test_name}-folders/TMPDIR
|
||||
)
|
||||
|
||||
string(REPLACE ${executable} ";" " " executable)
|
||||
add_custom_target("gdb-${test_name}"
|
||||
COMMAND env ${TEST_ENV} gdb
|
||||
--batch -ex 'set print thread-events off'
|
||||
-ex 'run' -ex 'bt'
|
||||
--args ${executable}
|
||||
DEPENDS "contain-${test_name}"
|
||||
)
|
||||
|
||||
add_custom_target("valgrind-${test_name}"
|
||||
COMMAND env ${TEST_ENV} valgrind
|
||||
-q --leak-check=no --num-callers=4
|
||||
--show-possibly-lost=no
|
||||
--undef-value-errors=yes
|
||||
--track-origins=yes
|
||||
${executable}
|
||||
DEPENDS "contain-${test_name}"
|
||||
)
|
||||
|
||||
add_custom_target("callgrind-${test_name}"
|
||||
COMMAND env ${TEST_ENV} valgrind
|
||||
--tool=callgrind
|
||||
--callgrind-out-file=${UNIT}.callgrind
|
||||
${executable}
|
||||
DEPENDS "contain-${test_name}"
|
||||
)
|
||||
endmacro(contain_test)
|
19
cmake/FindConvert.cmake
Normal file
19
cmake/FindConvert.cmake
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Copyright (C) 2013 Christian Dywan
|
||||
# Copyright (C) 2013 Olivier Duchateau
|
||||
|
||||
find_program (RSVG_CONVERT rsvg-convert)
|
||||
|
||||
if (RSVG_CONVERT)
|
||||
set (CONVERT_FOUND TRUE)
|
||||
macro (SVG2PNG filename install_destination)
|
||||
string(REPLACE "/" "_" target ${filename})
|
||||
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${filename}")
|
||||
add_custom_target ("${target}.png" ALL
|
||||
${RSVG_CONVERT} --keep-aspect-ratio --format=png "${CMAKE_CURRENT_SOURCE_DIR}/${filename}.svg"
|
||||
--output "${CMAKE_CURRENT_BINARY_DIR}/${filename}.png"
|
||||
)
|
||||
install (FILES "${CMAKE_CURRENT_BINARY_DIR}/${filename}.png"
|
||||
DESTINATION ${install_destination})
|
||||
endmacro (SVG2PNG filename)
|
||||
endif ()
|
||||
|
38
cmake/FindIntltool.cmake
Normal file
38
cmake/FindIntltool.cmake
Normal file
|
@ -0,0 +1,38 @@
|
|||
# FindIntltool.cmake
|
||||
#
|
||||
# Jim Nelson <jim@yorba.org>
|
||||
# Copyright 2012-2013 Yorba Foundation
|
||||
# Copyright (C) 2013 Christian Dywan
|
||||
|
||||
find_program (INTLTOOL_MERGE_EXECUTABLE intltool-merge)
|
||||
find_program (INTLTOOL_UPDATE_EXECUTABLE intltool-update)
|
||||
|
||||
if (INTLTOOL_MERGE_EXECUTABLE)
|
||||
set (INTLTOOL_MERGE_FOUND TRUE)
|
||||
macro (INTLTOOL_MERGE_DESKTOP desktop_id po_dir)
|
||||
add_custom_target ("${desktop_id}.desktop" ALL
|
||||
${INTLTOOL_MERGE_EXECUTABLE} --desktop-style ${CMAKE_SOURCE_DIR}/${po_dir}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${desktop_id}.desktop.in ${desktop_id}.desktop
|
||||
)
|
||||
install (FILES "${CMAKE_CURRENT_BINARY_DIR}/${desktop_id}.desktop"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/applications")
|
||||
endmacro (INTLTOOL_MERGE_DESKTOP desktop_id po_dir)
|
||||
macro (INTLTOOL_MERGE_APPDATA desktop_id po_dir)
|
||||
add_custom_target ("${desktop_id}.appdata.xml" ALL
|
||||
${INTLTOOL_MERGE_EXECUTABLE} --xml-style ${CMAKE_SOURCE_DIR}/${po_dir}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/${desktop_id}.appdata.xml.in ${desktop_id}.appdata.xml
|
||||
)
|
||||
install (FILES "${CMAKE_CURRENT_BINARY_DIR}/${desktop_id}.appdata.xml"
|
||||
DESTINATION "${CMAKE_INSTALL_PREFIX}/share/appdata")
|
||||
endmacro (INTLTOOL_MERGE_APPDATA desktop_id po_dir)
|
||||
endif ()
|
||||
|
||||
if (INTLTOOL_UPDATE_EXECUTABLE)
|
||||
set (INTLTOOL_UPDATE_FOUND TRUE)
|
||||
add_custom_target (pot
|
||||
COMMAND ${INTLTOOL_UPDATE_EXECUTABLE} "-p" "-g" ${GETTEXT_PACKAGE}
|
||||
WORKING_DIRECTORY "${CMAKE_SOURCE_DIR}/po"
|
||||
)
|
||||
endif ()
|
||||
|
||||
|
18
cmake/FindVala.cmake
Normal file
18
cmake/FindVala.cmake
Normal file
|
@ -0,0 +1,18 @@
|
|||
# Copyright (C) 2013 Christian Dywan <christian@twotoasts.de>
|
||||
|
||||
find_program(VALA_EXECUTABLE NAMES $ENV{VALAC} valac)
|
||||
if (VALA_EXECUTABLE)
|
||||
execute_process(COMMAND ${VALA_EXECUTABLE} "--version" OUTPUT_VARIABLE "VALA_VERSION")
|
||||
string(REPLACE "Vala " "" VALA_VERSION ${VALA_VERSION})
|
||||
string(STRIP ${VALA_VERSION} VALA_VERSION)
|
||||
else ()
|
||||
message(FATAL_ERROR "valac not found - install Vala compiler or specify compiler name eg. VALAC=valac-0.20")
|
||||
endif ()
|
||||
|
||||
macro(vala_require VALA_REQUIRED)
|
||||
if (${VALA_VERSION} VERSION_GREATER ${VALA_REQUIRED} OR ${VALA_VERSION} VERSION_EQUAL ${VALA_REQUIRED})
|
||||
message(STATUS "valac ${VALA_VERSION} found")
|
||||
else ()
|
||||
message(FATAL_ERROR "valac >= ${VALA_REQUIRED} or later required")
|
||||
endif ()
|
||||
endmacro(vala_require)
|
38
cmake/GIR.cmake
Normal file
38
cmake/GIR.cmake
Normal file
|
@ -0,0 +1,38 @@
|
|||
# GIR.cmake
|
||||
#
|
||||
# Macros for building Gobject Introspection bindings for Midori API
|
||||
find_program (GIR_SCANNER_BIN g-ir-scanner)
|
||||
find_program (GIR_COMPILER_BIN g-ir-compiler)
|
||||
|
||||
if (GIR_SCANNER_BIN AND GIR_COMPILER_BIN)
|
||||
|
||||
set (GIR_FOUND TRUE)
|
||||
set (GIR_VERSION "${MIDORI_MAJOR_VERSION}.${MIDORI_MINOR_VERSION}")
|
||||
macro (gir_build module namespace)
|
||||
add_custom_target ("g-ir-scanner_${module}" ALL
|
||||
${GIR_SCANNER_BIN} -Imidori -I${CMAKE_SOURCE_DIR}/ -I${CMAKE_BINARY_DIR}/midori -I${CMAKE_SOURCE_DIR}/${module} -I${CMAKE_SOURCE_DIR}/toolbars -I.
|
||||
--header-only -n ${namespace} --identifier-prefix ${namespace}
|
||||
${CMAKE_SOURCE_DIR}/${module}/${module}-*.c ${CMAKE_SOURCE_DIR}/${module}/${module}-*.h
|
||||
--pkg gtk+-2.0 --pkg webkit-1.0 --pkg gio-2.0 --pkg gobject-2.0
|
||||
--warn-all -iGObject-2.0 -iGLib-2.0 -iGtk-2.0
|
||||
--nsversion ${GIR_VERSION}
|
||||
-o ${CMAKE_CURRENT_BINARY_DIR}/${namespace}-${GIR_VERSION}.gir
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
DEPENDS ${CMAKE_PROJECT_NAME})
|
||||
add_custom_target ("g-ir-compiler_${module}" ALL
|
||||
${GIR_COMPILER_BIN} ${CMAKE_CURRENT_BINARY_DIR}/${namespace}-${GIR_VERSION}.gir
|
||||
--output ${CMAKE_CURRENT_BINARY_DIR}/${namespace}-${GIR_VERSION}.typelib
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}"
|
||||
DEPENDS g-ir-scanner_${module})
|
||||
|
||||
endmacro (gir_build module namespace)
|
||||
|
||||
macro (gir module namespace)
|
||||
gir_build (${module} ${namespace})
|
||||
|
||||
install (FILES "${CMAKE_CURRENT_BINARY_DIR}/${namespace}-${GIR_VERSION}.gir"
|
||||
DESTINATION "${CMAKE_INSTALL_DATADIR}/gir-1.0/")
|
||||
install (FILES "${CMAKE_CURRENT_BINARY_DIR}/${namespace}-${GIR_VERSION}.typelib"
|
||||
DESTINATION "${CMAKE_INSTALL_LIBDIR}/girepository-1.0/")
|
||||
endmacro (gir module)
|
||||
endif ()
|
71
cmake/GLibHelpers.cmake
Normal file
71
cmake/GLibHelpers.cmake
Normal file
|
@ -0,0 +1,71 @@
|
|||
# Copyright (C) 2010 David Sansome <me@davidsansome.com>
|
||||
cmake_minimum_required(VERSION 2.6)
|
||||
if(POLICY CMP0011)
|
||||
cmake_policy(SET CMP0011 NEW)
|
||||
endif(POLICY CMP0011)
|
||||
|
||||
find_program(GLIB_MKENUMS glib-mkenums)
|
||||
find_program(GLIB_GENMARSHAL glib-genmarshal)
|
||||
|
||||
macro(add_glib_marshal outfiles name prefix otherinclude)
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.h"
|
||||
COMMAND ${GLIB_GENMARSHAL} --header "--prefix=${prefix}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${name}.list"
|
||||
> "${CMAKE_CURRENT_BINARY_DIR}/${name}.h"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${name}.list"
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
|
||||
COMMAND echo "\\#include \\\"${otherinclude}\\\"" > "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
|
||||
COMMAND echo "\\#include \\\"glib-object.h\\\"" >> "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
|
||||
COMMAND echo "\\#include \\\"${name}.h\\\"" >> "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
|
||||
COMMAND ${GLIB_GENMARSHAL} --body "--prefix=${prefix}"
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/${name}.list"
|
||||
>> "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
|
||||
DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/${name}.list"
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${name}.h"
|
||||
)
|
||||
list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${name}.c")
|
||||
endmacro(add_glib_marshal)
|
||||
|
||||
macro(add_glib_enumtypes_t outfiles name htemplate ctemplate)
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.h"
|
||||
COMMAND ${GLIB_MKENUMS}
|
||||
--template "${htemplate}"
|
||||
${ARGN} > "${CMAKE_CURRENT_BINARY_DIR}/${name}.h"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${ARGN} "${htemplate}"
|
||||
)
|
||||
add_custom_command(
|
||||
OUTPUT "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
|
||||
COMMAND ${GLIB_MKENUMS}
|
||||
--template "${ctemplate}"
|
||||
${ARGN} > "${CMAKE_CURRENT_BINARY_DIR}/${name}.c"
|
||||
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
DEPENDS ${ARGN} ${ctemplate}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${name}.h"
|
||||
)
|
||||
list(APPEND ${outfiles} "${CMAKE_CURRENT_BINARY_DIR}/${name}.c")
|
||||
endmacro(add_glib_enumtypes_t)
|
||||
|
||||
macro(add_glib_enumtypes outfiles name includeguard)
|
||||
set(htemplate "${CMAKE_CURRENT_BINARY_DIR}/${name}.h.template")
|
||||
set(ctemplate "${CMAKE_CURRENT_BINARY_DIR}/${name}.c.template")
|
||||
|
||||
# Write the .h template
|
||||
add_custom_command(
|
||||
OUTPUT ${htemplate} ${ctemplate}
|
||||
COMMAND ${CMAKE_COMMAND}
|
||||
"-Dctemplate=${ctemplate}"
|
||||
"-Dhtemplate=${htemplate}"
|
||||
"-Dname=${name}"
|
||||
"-Dincludeguard=${includeguard}"
|
||||
"\"-Dheaders=${ARGN}\""
|
||||
-P "${CMAKE_SOURCE_DIR}/CMake/MakeGLibEnumTemplates.cmake"
|
||||
DEPENDS "${CMAKE_SOURCE_DIR}/CMake/MakeGLibEnumTemplates.cmake" ${headers}
|
||||
)
|
||||
|
||||
add_glib_enumtypes_t(${outfiles} ${name} ${htemplate} ${ctemplate} ${ARGN})
|
||||
endmacro(add_glib_enumtypes)
|
61
cmake/GtkDoc.cmake
Normal file
61
cmake/GtkDoc.cmake
Normal file
|
@ -0,0 +1,61 @@
|
|||
# GtkDoc.cmake
|
||||
#
|
||||
# Macros for building Midori API documentation.
|
||||
# Copyright (C) 2013 Olivier Duchateau
|
||||
|
||||
find_program (GTKDOC_SCAN_BIN gtkdoc-scan)
|
||||
find_program (GTKDOC_MKDB_BIN gtkdoc-mkdb)
|
||||
find_program (GTKDOC_MKHTML_BIN gtkdoc-mkhtml)
|
||||
find_program (GTKDOC_MKTMPL_BIN gtkdoc-mktmpl)
|
||||
|
||||
if (GTKDOC_SCAN_BIN AND GTKDOC_MKTMPL_BIN AND GTKDOC_MKDB_BIN
|
||||
AND GTKDOC_MKHTML_BIN)
|
||||
|
||||
set (GTKDOC_FOUND TRUE)
|
||||
|
||||
macro (gtkdoc_build module)
|
||||
message("gtkdoc: module ${module}")
|
||||
# file (MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${module}")
|
||||
add_custom_target ("gtkdoc-scan_${module}" ALL
|
||||
${GTKDOC_SCAN_BIN} --module=${module}
|
||||
--source-dir="${CMAKE_SOURCE_DIR}/${module}"
|
||||
--output-dir="${CMAKE_CURRENT_BINARY_DIR}/${module}"
|
||||
--rebuild-sections --rebuild-types
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
|
||||
add_custom_target ("gtkdoc-tmpl_${module}" ALL
|
||||
${GTKDOC_MKTMPL_BIN} --module=${module}
|
||||
--output-dir="${CMAKE_CURRENT_BINARY_DIR}"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${module}"
|
||||
DEPENDS "gtkdoc-scan_${module}")
|
||||
|
||||
add_custom_target ("gtkdoc-docbook_${module}" ALL
|
||||
${GTKDOC_MKDB_BIN} --module=${module}
|
||||
--output-dir="xml"
|
||||
--source-dir="${CMAKE_SOURCE_DIR}/${module}"
|
||||
--source-suffixes=c,h --output-format=xml
|
||||
--default-includes=${module}/${module}.h
|
||||
--sgml-mode --main-sgml-file=${module}.sgml
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${module}"
|
||||
DEPENDS "gtkdoc-tmpl_${module}")
|
||||
|
||||
# Keep this target alone, otherwise build fails
|
||||
add_custom_target ("gtkdoc-html_${module}" ALL
|
||||
${GTKDOC_MKHTML_BIN} ${module}
|
||||
"${CMAKE_CURRENT_BINARY_DIR}/${module}/${module}.sgml"
|
||||
WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${module}/html"
|
||||
DEPENDS "gtkdoc-docbook_${module}")
|
||||
|
||||
endmacro (gtkdoc_build module)
|
||||
|
||||
macro (gtkdoc module)
|
||||
file (MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${module}/html")
|
||||
gtkdoc_build (${module})
|
||||
|
||||
set (DOC_DIR "html/midori-${MIDORI_MAJOR_VERSION}-${MIDORI_MINOR_VERSION}")
|
||||
install (DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${module}/html/"
|
||||
DESTINATION "${CMAKE_INSTALL_DATADIR}/gtk-doc/${DOC_DIR}/${module}"
|
||||
PATTERN "html/*"
|
||||
PATTERN "index.sgml" EXCLUDE)
|
||||
endmacro (gtkdoc module)
|
||||
endif ()
|
36
cmake/ParseArguments.cmake
Normal file
36
cmake/ParseArguments.cmake
Normal file
|
@ -0,0 +1,36 @@
|
|||
##
|
||||
# This is a helper Macro to parse optional arguments in Macros/Functions
|
||||
# It has been taken from the public CMake wiki.
|
||||
# See http://www.cmake.org/Wiki/CMakeMacroParseArguments for documentation and
|
||||
# licensing.
|
||||
##
|
||||
macro(parse_arguments prefix arg_names option_names)
|
||||
set(DEFAULT_ARGS)
|
||||
foreach(arg_name ${arg_names})
|
||||
set(${prefix}_${arg_name})
|
||||
endforeach(arg_name)
|
||||
foreach(option ${option_names})
|
||||
set(${prefix}_${option} FALSE)
|
||||
endforeach(option)
|
||||
|
||||
set(current_arg_name DEFAULT_ARGS)
|
||||
set(current_arg_list)
|
||||
foreach(arg ${ARGN})
|
||||
set(larg_names ${arg_names})
|
||||
list(FIND larg_names "${arg}" is_arg_name)
|
||||
if(is_arg_name GREATER -1)
|
||||
set(${prefix}_${current_arg_name} ${current_arg_list})
|
||||
set(current_arg_name ${arg})
|
||||
set(current_arg_list)
|
||||
else(is_arg_name GREATER -1)
|
||||
set(loption_names ${option_names})
|
||||
list(FIND loption_names "${arg}" is_option)
|
||||
if(is_option GREATER -1)
|
||||
set(${prefix}_${arg} TRUE)
|
||||
else(is_option GREATER -1)
|
||||
set(current_arg_list ${current_arg_list} ${arg})
|
||||
endif(is_option GREATER -1)
|
||||
endif(is_arg_name GREATER -1)
|
||||
endforeach(arg)
|
||||
set(${prefix}_${current_arg_name} ${current_arg_list})
|
||||
endmacro(parse_arguments)
|
236
cmake/ValaPrecompile.cmake
Normal file
236
cmake/ValaPrecompile.cmake
Normal file
|
@ -0,0 +1,236 @@
|
|||
##
|
||||
# Copyright 2009-2010 Jakob Westhoff. All rights reserved.
|
||||
# Copyright 2012 elementary.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
#
|
||||
# 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
# this list of conditions and the following disclaimer in the documentation
|
||||
# and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY JAKOB WESTHOFF ``AS IS'' AND ANY EXPRESS OR
|
||||
# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
# EVENT SHALL JAKOB WESTHOFF OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
||||
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
|
||||
# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
#
|
||||
# The views and conclusions contained in the software and documentation are those
|
||||
# of the authors and should not be interpreted as representing official policies,
|
||||
# either expressed or implied, of Jakob Westhoff
|
||||
##
|
||||
|
||||
include(ParseArguments)
|
||||
find_package(Vala REQUIRED)
|
||||
|
||||
##
|
||||
# Compile vala files to their c equivalents for further processing.
|
||||
#
|
||||
# The "vala_precompile" macro takes care of calling the valac executable on the
|
||||
# given source to produce c files which can then be processed further using
|
||||
# default cmake functions.
|
||||
#
|
||||
# The first parameter provided is a variable, which will be filled with a list
|
||||
# of c files outputted by the vala compiler. This list can than be used in
|
||||
# conjuction with functions like "add_executable" or others to create the
|
||||
# neccessary compile rules with CMake.
|
||||
#
|
||||
# The initial variable is followed by a list of .vala files to be compiled.
|
||||
# Please take care to add every vala file belonging to the currently compiled
|
||||
# project or library as Vala will otherwise not be able to resolve all
|
||||
# dependencies.
|
||||
#
|
||||
# The following sections may be specified afterwards to provide certain options
|
||||
# to the vala compiler:
|
||||
#
|
||||
# PACKAGES
|
||||
# A list of vala packages/libraries to be used during the compile cycle. The
|
||||
# package names are exactly the same, as they would be passed to the valac
|
||||
# "--pkg=" option.
|
||||
#
|
||||
# OPTIONS
|
||||
# A list of optional options to be passed to the valac executable. This can be
|
||||
# used to pass "--thread" for example to enable multi-threading support.
|
||||
#
|
||||
# CUSTOM_VAPIS
|
||||
# A list of custom vapi files to be included for compilation. This can be
|
||||
# useful to include freshly created vala libraries without having to install
|
||||
# them in the system.
|
||||
#
|
||||
# GENERATE_VAPI
|
||||
# Pass all the needed flags to the compiler to create an internal vapi for
|
||||
# the compiled library. The provided name will be used for this and a
|
||||
# <provided_name>.vapi file will be created.
|
||||
#
|
||||
# GENERATE_HEADER
|
||||
# Let the compiler generate a header file for the compiled code. There will
|
||||
# be a header file as well as an internal header file being generated called
|
||||
# <provided_name>.h and <provided_name>_internal.h
|
||||
#
|
||||
# GENERATE_GIR
|
||||
# Have the compiler generate a GObject-Introspection repository file with
|
||||
# name: <provided_name>.gir. This can be later used to create a binary typelib
|
||||
# using the GI compiler.
|
||||
#
|
||||
# GENERATE_SYMBOLS
|
||||
# Output a <provided_name>.symbols file containing all the exported symbols.
|
||||
#
|
||||
# The following call is a simple example to the vala_precompile macro showing
|
||||
# an example to every of the optional sections:
|
||||
#
|
||||
# vala_precompile(VALA_C mytargetname
|
||||
# source1.vala
|
||||
# source2.vala
|
||||
# source3.vala
|
||||
# PACKAGES
|
||||
# gtk+-2.0
|
||||
# gio-1.0
|
||||
# posix
|
||||
# DIRECTORY
|
||||
# gen
|
||||
# OPTIONS
|
||||
# --thread
|
||||
# CUSTOM_VAPIS
|
||||
# some_vapi.vapi
|
||||
# GENERATE_VAPI
|
||||
# myvapi
|
||||
# GENERATE_HEADER
|
||||
# myheader
|
||||
# GENERATE_GIR
|
||||
# mygir
|
||||
# GENERATE_SYMBOLS
|
||||
# mysymbols
|
||||
# )
|
||||
#
|
||||
# Most important is the variable VALA_C which will contain all the generated c
|
||||
# file names after the call.
|
||||
##
|
||||
|
||||
macro(vala_precompile output target_name)
|
||||
parse_arguments(ARGS "TARGET;PACKAGES;OPTIONS;DIRECTORY;GENERATE_GIR;GENERATE_SYMBOLS;GENERATE_HEADER;GENERATE_VAPI;CUSTOM_VAPIS" "" ${ARGN})
|
||||
|
||||
if(ARGS_DIRECTORY)
|
||||
set(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/${ARGS_DIRECTORY})
|
||||
else(ARGS_DIRECTORY)
|
||||
set(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
|
||||
endif(ARGS_DIRECTORY)
|
||||
include_directories(${DIRECTORY})
|
||||
set(vala_pkg_opts "")
|
||||
foreach(pkg ${ARGS_PACKAGES})
|
||||
list(APPEND vala_pkg_opts "--pkg=${pkg}")
|
||||
endforeach(pkg ${ARGS_PACKAGES})
|
||||
set(in_files "")
|
||||
set(out_files "")
|
||||
set(out_files_display "")
|
||||
set(${output} "")
|
||||
|
||||
foreach(src ${ARGS_DEFAULT_ARGS})
|
||||
string(REGEX MATCH "^/" IS_MATCHED ${src})
|
||||
if(${IS_MATCHED} MATCHES "/")
|
||||
set(src_file_path ${src})
|
||||
else()
|
||||
set(src_file_path ${CMAKE_CURRENT_SOURCE_DIR}/${src})
|
||||
endif()
|
||||
list(APPEND in_files ${src_file_path})
|
||||
string(REPLACE ".vala" ".c" src ${src})
|
||||
string(REPLACE ".gs" ".c" src ${src})
|
||||
if(${IS_MATCHED} MATCHES "/")
|
||||
get_filename_component(VALA_FILE_NAME ${src} NAME)
|
||||
set(out_file "${CMAKE_CURRENT_BINARY_DIR}/${VALA_FILE_NAME}")
|
||||
list(APPEND out_files "${CMAKE_CURRENT_BINARY_DIR}/${VALA_FILE_NAME}")
|
||||
else()
|
||||
set(out_file "${DIRECTORY}/${src}")
|
||||
list(APPEND out_files "${DIRECTORY}/${src}")
|
||||
endif()
|
||||
list(APPEND ${output} ${out_file})
|
||||
list(APPEND out_files_display "${src}")
|
||||
endforeach(src ${ARGS_DEFAULT_ARGS})
|
||||
|
||||
set(custom_vapi_arguments "")
|
||||
if(ARGS_CUSTOM_VAPIS)
|
||||
foreach(vapi ${ARGS_CUSTOM_VAPIS})
|
||||
if(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
|
||||
list(APPEND custom_vapi_arguments ${vapi})
|
||||
else (${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
|
||||
list(APPEND custom_vapi_arguments ${CMAKE_CURRENT_SOURCE_DIR}/${vapi})
|
||||
endif(${vapi} MATCHES ${CMAKE_SOURCE_DIR} OR ${vapi} MATCHES ${CMAKE_BINARY_DIR})
|
||||
endforeach(vapi ${ARGS_CUSTOM_VAPIS})
|
||||
endif(ARGS_CUSTOM_VAPIS)
|
||||
|
||||
set(vapi_arguments "")
|
||||
if(ARGS_GENERATE_VAPI)
|
||||
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_VAPI}.vapi")
|
||||
list(APPEND out_files_display "${ARGS_GENERATE_VAPI}.vapi")
|
||||
set(vapi_arguments "--library=${ARGS_GENERATE_VAPI}" "--vapi=${ARGS_GENERATE_VAPI}.vapi")
|
||||
|
||||
# Header and internal header is needed to generate internal vapi
|
||||
if (NOT ARGS_GENERATE_HEADER)
|
||||
set(ARGS_GENERATE_HEADER ${ARGS_GENERATE_VAPI})
|
||||
endif(NOT ARGS_GENERATE_HEADER)
|
||||
endif(ARGS_GENERATE_VAPI)
|
||||
|
||||
set(header_arguments "")
|
||||
if(ARGS_GENERATE_HEADER)
|
||||
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_HEADER}.h")
|
||||
list(APPEND out_files_display "${ARGS_GENERATE_HEADER}.h")
|
||||
list(APPEND header_arguments "--header=${ARGS_GENERATE_HEADER}.h")
|
||||
endif(ARGS_GENERATE_HEADER)
|
||||
|
||||
set(gir_arguments "")
|
||||
if(ARGS_GENERATE_GIR)
|
||||
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_GIR}.gir")
|
||||
list(APPEND out_files_display "${ARGS_GENERATE_GIR}.gir")
|
||||
set(gir_arguments "--gir=${ARGS_GENERATE_GIR}.gir")
|
||||
endif(ARGS_GENERATE_GIR)
|
||||
|
||||
set(symbols_arguments "")
|
||||
if(ARGS_GENERATE_SYMBOLS)
|
||||
list(APPEND out_files "${DIRECTORY}/${ARGS_GENERATE_SYMBOLS}.symbols")
|
||||
list(APPEND out_files_display "${ARGS_GENERATE_SYMBOLS}.symbols")
|
||||
set(symbols_arguments "--symbols=${ARGS_GENERATE_SYMBOLS}.symbols")
|
||||
endif(ARGS_GENERATE_SYMBOLS)
|
||||
|
||||
# Workaround for a bug that would make valac run twice. This file is written
|
||||
# after the vala compiler generates C source code.
|
||||
set(OUTPUT_STAMP ${CMAKE_CURRENT_BINARY_DIR}/${target_name}_valac.stamp)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT
|
||||
${OUTPUT_STAMP}
|
||||
COMMAND
|
||||
${VALA_EXECUTABLE}
|
||||
ARGS
|
||||
"-C"
|
||||
${header_arguments}
|
||||
${vapi_arguments}
|
||||
${gir_arguments}
|
||||
${symbols_arguments}
|
||||
"-b" ${CMAKE_CURRENT_SOURCE_DIR}
|
||||
"-d" ${DIRECTORY}
|
||||
${vala_pkg_opts}
|
||||
${ARGS_OPTIONS}
|
||||
${in_files}
|
||||
${custom_vapi_arguments}
|
||||
COMMAND
|
||||
touch
|
||||
ARGS
|
||||
${OUTPUT_STAMP}
|
||||
DEPENDS
|
||||
${in_files}
|
||||
${ARGS_CUSTOM_VAPIS}
|
||||
COMMENT
|
||||
"Generating ${out_files_display}"
|
||||
)
|
||||
|
||||
# This command will be run twice for some reason (pass a non-empty string to COMMENT
|
||||
# in order to see it). Since valac is not executed from here, this won't be a problem.
|
||||
add_custom_command(OUTPUT ${out_files} DEPENDS ${OUTPUT_STAMP} COMMENT "")
|
||||
endmacro(vala_precompile)
|
23
config/CMakeLists.txt
Normal file
23
config/CMakeLists.txt
Normal file
|
@ -0,0 +1,23 @@
|
|||
# Copyright (C) 2013 Olivier Duchateau
|
||||
|
||||
set (SYSCONFDIR ${CMAKE_INSTALL_FULL_SYSCONFDIR})
|
||||
set (XDG_CONFIG_DIR "xdg/${CMAKE_PROJECT_NAME}")
|
||||
|
||||
file (GLOB_RECURSE CONFIG_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *)
|
||||
list (REMOVE_ITEM CONFIG_FILES "CMakeLists.txt")
|
||||
|
||||
if (${CMAKE_INSTALL_PREFIX} STREQUAL "/usr")
|
||||
set(CMAKE_INSTALL_SYSCONFDIR "/etc")
|
||||
endif()
|
||||
|
||||
foreach (FILE ${CONFIG_FILES})
|
||||
string (FIND ${FILE} "adblock" ADBLOCK_CONF)
|
||||
if (ADBLOCK_CONF GREATER -1)
|
||||
string (REPLACE "config" "" dirname ${FILE})
|
||||
install (FILES ${FILE}
|
||||
DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/${XDG_CONFIG_DIR}/${dirname}")
|
||||
else ()
|
||||
install (FILES ${FILE}
|
||||
DESTINATION "${CMAKE_INSTALL_SYSCONFDIR}/${XDG_CONFIG_DIR}")
|
||||
endif ()
|
||||
endforeach ()
|
224
configure
vendored
224
configure
vendored
|
@ -1,157 +1,83 @@
|
|||
#! /bin/sh
|
||||
|
||||
# waf configure wrapper
|
||||
|
||||
# Fancy colors used to beautify the output a bit.
|
||||
#
|
||||
if [ "$NOCOLOR" ] ; then
|
||||
NORMAL=""
|
||||
BOLD=""
|
||||
RED=""
|
||||
YELLOW=""
|
||||
GREEN=""
|
||||
else
|
||||
NORMAL="\033[0m"
|
||||
BOLD="\033[1m"
|
||||
RED="\033[91m"
|
||||
YELLOW="\033[01;93m"
|
||||
GREEN="\033[92m"
|
||||
fi
|
||||
|
||||
EXIT_SUCCESS=0
|
||||
EXIT_FAILURE=1
|
||||
EXIT_ERROR=2
|
||||
EXIT_BUG=10
|
||||
|
||||
CUR_DIR=$PWD
|
||||
|
||||
#possible relative path
|
||||
WORKINGDIR=`dirname $0`
|
||||
cd $WORKINGDIR
|
||||
#abs path
|
||||
WORKINGDIR=`pwd`
|
||||
cd $CUR_DIR
|
||||
|
||||
# Checks for Python interpreter. Honours $PYTHON if set. Stores path to
|
||||
# interpreter in $PYTHON.
|
||||
# Copyright (C) 2013 Christian Dywan <christian@twotoasts.de>
|
||||
#
|
||||
checkPython()
|
||||
{
|
||||
if [ -z "$PYTHON" ] ; then
|
||||
PYTHON=`which python2 2>/dev/null`
|
||||
fi
|
||||
if [ -z "$PYTHON" ] ; then
|
||||
PYTHON=`which python 2>/dev/null`
|
||||
fi
|
||||
printf "Checking for Python\t\t\t: "
|
||||
if [ ! -x "$PYTHON" ] ; then
|
||||
printf $RED"not found!"$NORMAL"\n"
|
||||
echo "Please make sure that the Python interpreter is available in your PATH"
|
||||
echo "or invoke configure using the PYTHON flag, e.g."
|
||||
echo "$ PYTHON=/usr/local/bin/python configure"
|
||||
exit $EXIT_FAILURE
|
||||
fi
|
||||
printf $GREEN"$PYTHON"$NORMAL"\n"
|
||||
}
|
||||
|
||||
# Checks for WAF. Honours $WAF if set. Stores path to 'waf' in $WAF.
|
||||
# Requires that $PYTHON is set.
|
||||
# This library is free software; you can redistribute it and/or
|
||||
# modify it under the terms of the GNU Lesser General Public
|
||||
# License as published by the Free Software Foundation; either
|
||||
# version 2.1 of the License, or (at your option) any later version.
|
||||
#
|
||||
checkWAF()
|
||||
{
|
||||
printf "Checking for WAF\t\t\t: "
|
||||
#installed miniwaf in sourcedir
|
||||
if [ -z "$WAF" ] ; then
|
||||
if [ -f "${WORKINGDIR}/waf" ] ; then
|
||||
WAF="${WORKINGDIR}/waf"
|
||||
if [ ! -x "$WAF" ] ; then
|
||||
chmod +x $WAF
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
if [ -z "$WAF" ] ; then
|
||||
if [ -f "${WORKINGDIR}/waf-light" ] ; then
|
||||
${WORKINGDIR}/waf-light --make-waf
|
||||
WAF="${WORKINGDIR}/waf"
|
||||
fi
|
||||
fi
|
||||
#global installed waf with waf->waf.py link
|
||||
if [ -z "$WAF" ] ; then
|
||||
WAF=`which waf 2>/dev/null`
|
||||
fi
|
||||
# neither waf nor miniwaf could be found
|
||||
if [ ! -x "$WAF" ] ; then
|
||||
printf $RED"not found"$NORMAL"\n"
|
||||
echo "Go to http://code.google.com/p/waf/"
|
||||
echo "and download a waf version"
|
||||
exit $EXIT_FAILURE
|
||||
else
|
||||
printf $GREEN"$WAF"$NORMAL"\n"
|
||||
fi
|
||||
WAF="$PYTHON $WAF"
|
||||
}
|
||||
|
||||
# Generates a Makefile. Requires that $WAF is set.
|
||||
# See the file COPYING for the full license text.
|
||||
#
|
||||
#~ Usage:
|
||||
#~ ./configure [OPTIONS]
|
||||
#~ Options:
|
||||
#~ --prefix=PREFIX Installation prefix
|
||||
#~ --enable-gtk3 Use GTK+3
|
||||
#~ --disable-zeitgeist Disable Zeitgeist history integration
|
||||
#~ --enable-granite Fancy notebook and pop-overs
|
||||
#~ --enable-apidocs API documentation
|
||||
#~
|
||||
#~ Environment:
|
||||
#~ VALAC if defined the valac executable to use, for example valac-0.16
|
||||
#
|
||||
generateMakefile()
|
||||
{
|
||||
cat > Makefile << EOF
|
||||
#!/usr/bin/make -f
|
||||
# Waf Makefile wrapper
|
||||
WAF_HOME=$CUR_DIR
|
||||
|
||||
all:
|
||||
@$WAF build
|
||||
|
||||
all-debug:
|
||||
@$WAF -v build
|
||||
|
||||
all-progress:
|
||||
@$WAF -p build
|
||||
|
||||
install:
|
||||
@if test -n "\$(DESTDIR)"; then \\
|
||||
$WAF install --destdir="\$(DESTDIR)"; \\
|
||||
else \\
|
||||
$WAF install; \\
|
||||
fi;
|
||||
|
||||
.PHONY: install
|
||||
|
||||
uninstall:
|
||||
@if test -n "\$(DESTDIR)"; then \\
|
||||
$WAF uninstall --destdir="\$(DESTDIR)"; \\
|
||||
else \\
|
||||
$WAF uninstall; \\
|
||||
fi;
|
||||
|
||||
clean:
|
||||
@$WAF clean
|
||||
|
||||
distclean:
|
||||
@$WAF distclean
|
||||
@-rm -rf _build
|
||||
@-rm -f Makefile
|
||||
|
||||
check:
|
||||
@$WAF check
|
||||
|
||||
dist:
|
||||
@$WAF dist
|
||||
|
||||
EOF
|
||||
}
|
||||
|
||||
checkPython
|
||||
checkWAF
|
||||
|
||||
echo "calling waf configure with parameters"
|
||||
$WAF configure $* || exit $EXIT_ERROR
|
||||
|
||||
if [ -f "Makefile" ] ; then
|
||||
echo ""
|
||||
else
|
||||
generateMakefile
|
||||
if [ -z `command -v cmake` ]; then
|
||||
echo Fatal: cmake not installed
|
||||
exit 1
|
||||
fi
|
||||
|
||||
exit $EXIT_SUCCESS
|
||||
while [ $# != 0 ]; do
|
||||
case $1 in
|
||||
--enable-gtk3)
|
||||
ARGS="$ARGS -DUSE_GTK3=1";;
|
||||
--disable-zeitgeist)
|
||||
ARGS="$ARGS -DUSE_ZEITGEIST=0";;
|
||||
--enable-granite)
|
||||
ARGS="$ARGS -DUSE_GRANITE=1";;
|
||||
--enable-apidocs)
|
||||
ARGS="$ARGS -DUSE_APIDOCS=1";;
|
||||
--extra-warnings)
|
||||
ARGS="$ARGS -DEXTRA_WARNINGS=1";;
|
||||
--prefix=*)
|
||||
ARGS="$ARGS -DCMAKE_INSTALL_PREFIX=${1#*=}";;
|
||||
*)
|
||||
grep -e '^#~' $0 | sed s/#~//
|
||||
exit
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
BUILD_DIR="_build"
|
||||
|
||||
if [ ! -f GNUmakefile ]; then
|
||||
cp -v GNUmakefile.in GNUmakefile || exit 1
|
||||
fi
|
||||
|
||||
# cmake was invoked in toplevel folder before
|
||||
# clean up cmake generated build files to prevent conflicts
|
||||
if [ -f CMakeCache.txt ]; then
|
||||
echo
|
||||
echo '####################################################################################'
|
||||
echo 'CMake build files detected in toplevel folder !!'
|
||||
echo 'Please always run "cmake" command from distinct folder when you use cmake yourself.'
|
||||
echo '####################################################################################'
|
||||
echo
|
||||
echo 'Cleaning up...'
|
||||
echo
|
||||
|
||||
rm -fr $BUILD_DIR
|
||||
rm CMakeCache.txt config.h Makefile
|
||||
find . -iname CMakeFiles -type d|xargs rm -fr
|
||||
find . -iname cmake_install.cmake -exec rm {} \;
|
||||
find . -iname CTestTestfile.cmake -exec rm {} \;
|
||||
|
||||
find . -iname *-folders -type d|xargs rm -fr
|
||||
rm -fr data/logo-shade
|
||||
fi
|
||||
|
||||
mkdir -p $BUILD_DIR && cd $BUILD_DIR || exit 1
|
||||
cmake $ARGS .. || exit 1
|
||||
|
||||
echo
|
||||
echo "Configuring done, run \"make\" to compile"
|
||||
|
|
58
data/CMakeLists.txt
Normal file
58
data/CMakeLists.txt
Normal file
|
@ -0,0 +1,58 @@
|
|||
# Copyright (C) 2013 Christian Dywan <christian@twotoasts.de>
|
||||
|
||||
include(FindConvert)
|
||||
if (NOT CONVERT_FOUND)
|
||||
message(FATAL_ERROR "rsvg-convert not found")
|
||||
endif ()
|
||||
|
||||
include(FindIntltool)
|
||||
if (NOT INTLTOOL_MERGE_FOUND)
|
||||
message(FATAL_ERROR "intltool-merge not found")
|
||||
elseif (NOT INTLTOOL_UPDATE_FOUND)
|
||||
message(FATAL_ERROR "intltool-update not found")
|
||||
endif ()
|
||||
|
||||
file(GLOB_RECURSE DATA_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *)
|
||||
list(REMOVE_ITEM DATA_FILES "CMakeLists.txt")
|
||||
|
||||
foreach(FILE ${DATA_FILES})
|
||||
if (${FILE} MATCHES "faq.")
|
||||
install(FILES ${FILE} DESTINATION ${CMAKE_INSTALL_DOCDIR})
|
||||
elseif (${FILE} MATCHES ".desktop")
|
||||
if (NOT WIN32)
|
||||
string(REPLACE ".desktop.in" "" DESKTOP_ID ${FILE})
|
||||
INTLTOOL_MERGE_DESKTOP (${DESKTOP_ID} po)
|
||||
endif ()
|
||||
elseif (${FILE} MATCHES ".appdata.xml")
|
||||
if (NOT WIN32)
|
||||
string(REPLACE ".appdata.xml.in" "" DESKTOP_ID ${FILE})
|
||||
INTLTOOL_MERGE_APPDATA (${DESKTOP_ID} po)
|
||||
endif ()
|
||||
elseif (${FILE} MATCHES "\\.svg$")
|
||||
string(REPLACE ".svg" "" IMG_ID ${FILE})
|
||||
string (FIND ${FILE} "/" IS_DIR)
|
||||
if (IS_DIR GREATER -1)
|
||||
string(REPLACE "/" ";" DIR_LIST ${FILE})
|
||||
LIST(GET DIR_LIST 0 S_DIR)
|
||||
SVG2PNG (${IMG_ID} "${CMAKE_INSTALL_DATADIR}/midori/res/${S_DIR}")
|
||||
else ()
|
||||
SVG2PNG (${IMG_ID} "${CMAKE_INSTALL_DATADIR}/midori/res/")
|
||||
endif()
|
||||
# These are being handled in add_executable for the "midori" binary
|
||||
elseif (${FILE} MATCHES "\\.ico$")
|
||||
elseif (${FILE} MATCHES "\\.rc$")
|
||||
# This is only meant for testing, and not used in production
|
||||
elseif (${FILE} MATCHES "\\.swf$")
|
||||
else()
|
||||
string(FIND ${FILE} "/" IS_DIR)
|
||||
if (IS_DIR GREATER -1)
|
||||
string(REPLACE "/" ";" DIR_LIST ${FILE})
|
||||
LIST(GET DIR_LIST 0 S_DIR)
|
||||
LIST(GET DIR_LIST 1 S_FILE)
|
||||
install(FILES ${S_DIR}/${S_FILE} DESTINATION ${CMAKE_INSTALL_DATADIR}/midori/res/${S_DIR})
|
||||
else ()
|
||||
install(FILES ${FILE} DESTINATION ${CMAKE_INSTALL_DATADIR}/midori/res/)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
endforeach()
|
|
@ -3,25 +3,14 @@
|
|||
This file is licensed under the terms of the expat license, see the file EXPAT.
|
||||
*/
|
||||
body {
|
||||
background-color: #eee;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#container {
|
||||
background: #f6fff3;
|
||||
min-width: 70%;
|
||||
max-width: 70%;
|
||||
margin: 2em auto 1em;
|
||||
padding: 1em;
|
||||
border: 0.2em solid #9acb7f;
|
||||
-webkit-border-radius: 1em;
|
||||
}
|
||||
|
||||
#icon {
|
||||
float: left;
|
||||
padding-left: 1%;
|
||||
padding-top: 1%;
|
||||
background-color: #dedede;
|
||||
color: #222222;
|
||||
font-family: 'Open Sans', 'Droid Sans', Arial, sans-serif;
|
||||
font-size: 14px;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
font-weight: normal;
|
||||
margin-top: 100px;
|
||||
}
|
||||
|
||||
html[dir="rtl"] #icon {
|
||||
|
@ -29,24 +18,40 @@ html[dir="rtl"] #icon {
|
|||
padding-right: 1%;
|
||||
}
|
||||
|
||||
.indent {
|
||||
margin-left: 5%;
|
||||
}
|
||||
|
||||
#main {
|
||||
float: right;
|
||||
width: 75%;
|
||||
max-width: 50%;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
min-width: 480px;
|
||||
background-repeat: no-repeat;
|
||||
background-color: #ffffff;
|
||||
border: 1px solid rgba(0, 0, 0, .3);
|
||||
padding: 25px;
|
||||
-webkit-border-radius: 4px;
|
||||
-webkit-box-shadow: 0 1px 2px rgba(0,0,0,.1);
|
||||
background-position-x: 22px;
|
||||
background-position-y: 54px;
|
||||
}
|
||||
|
||||
#text {
|
||||
margin-left: 15%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 1.4em;
|
||||
font-weight: bold;
|
||||
white-space: nowrap;
|
||||
font-family: 'Open Sans', 'Droid Sans', arial, sans-serif;
|
||||
font-size: 32px;
|
||||
font-style: normal;
|
||||
font-variant: normal;
|
||||
font-weight: 300;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#logo {
|
||||
position: absolute; bottom: 15px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
html[dir="ltr"] #logo {
|
||||
right: 15px;
|
||||
}
|
||||
|
@ -61,11 +66,35 @@ button img {
|
|||
padding: 2px 1px;
|
||||
}
|
||||
|
||||
#message {
|
||||
font-size: 1.1em;
|
||||
word-wrap: break-word;
|
||||
button {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
#description {
|
||||
font-size: 1em;
|
||||
.message {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.description {
|
||||
font-size: 1em;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#suggestions {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
#button {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#logo {
|
||||
position: absolute; bottom: 15px;
|
||||
z-index: -1;
|
||||
}
|
||||
|
||||
form {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
|
|
55
data/adblock.list
Normal file
55
data/adblock.list
Normal file
|
@ -0,0 +1,55 @@
|
|||
[Adblock Plus 2.0]
|
||||
! Version: 201402142200
|
||||
! Title: Exercise
|
||||
! Last modified: 11 Feb 2014 22:00 UTC
|
||||
! Expires: 3 days (update frequency)
|
||||
! Homepage: http://www.midori-browser.org
|
||||
! Licence: https://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt
|
||||
! Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
|
||||
!
|
||||
! Some freeform text:
|
||||
! Yadayada http://example.com/ e-mail (somebody@example.com).
|
||||
!
|
||||
!-----Spam eggs--------!
|
||||
! *** la:le/lu_foo_bar.txt ***
|
||||
|/http://ads.blub.boing/*$domain=xxx.com
|
||||
|/http://ads.blub.boing/*$domain=xxx.com,foo.fr,coco.at
|
||||
/market.php?$domain=adf.ly|foo.com
|
||||
/?placement=$script,domain=putlocker.com|sockshare.com
|
||||
|
||||
! Some basic filters
|
||||
*ads.foo.bar*
|
||||
*ads.bogus.name*
|
||||
||^http://ads.bla.blub/*
|
||||
engine.adct.ru/*?
|
||||
/addyn|*|adtech;
|
||||
doubleclick.net/pfadx/*.mtvi
|
||||
objects.tremormedia.com/embed/xml/*.xml?r=
|
||||
videostrip.com^*/admatcherclient.
|
||||
test.dom/test?var
|
||||
/adpage.
|
||||
br.gcl.ru/cgi-bin/br/
|
||||
_300x600.
|
||||
_rectangle_ads.
|
||||
+adverts/
|
||||
-2/ads/
|
||||
|
||||
! CSS elements
|
||||
old.tv,delicio.us,longc.at###box
|
||||
##.zRightAdNote
|
||||
###advertisingModule160x600
|
||||
##a[href$="/vghd.shtml"]
|
||||
imagetwist.com###left[align="center"] > center > a[target="_blank"]
|
||||
|
||||
! Options
|
||||
||videobox.com/?tid=$popup
|
||||
||sexsearchcom.com^$popup,third-party
|
||||
||206.217.206.137^$third-party
|
||||
/spopunder^$popup
|
||||
||putlocker.com^*.php?*title$subdocument
|
||||
://ads.$popup
|
||||
|
||||
! Whitelist
|
||||
@@||hortifor.com/images/*120x60$~third-party
|
||||
@@||stickam.com/wb/www/category/300x250/$image
|
||||
@@||adultadworld.com/adhandler/$subdocument
|
35
data/adblock/element_hider.js
Normal file
35
data/adblock/element_hider.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Copyright (C) 2014 Alexander V. Butenko <a.butenka@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
function getElementsByAttribute (strTagName, strAttributeName, arrAttributeValue) {
|
||||
var arrElements = document.getElementsByTagName (strTagName);
|
||||
var arrReturnElements = new Array();
|
||||
for (var j=0; j<arrAttributeValue.length; j++) {
|
||||
var strAttributeValue = arrAttributeValue[j];
|
||||
for (var i=0; i<arrElements.length; i++) {
|
||||
var oCurrent = arrElements[i];
|
||||
var oAttribute = oCurrent.getAttribute && oCurrent.getAttribute (strAttributeName);
|
||||
if (oAttribute && oAttribute.length > 0 && strAttributeValue.indexOf (oAttribute) != -1)
|
||||
arrReturnElements.push (oCurrent);
|
||||
}
|
||||
}
|
||||
return arrReturnElements;
|
||||
};
|
||||
|
||||
function hideElementBySrc (uris) {
|
||||
var oElements = getElementsByAttribute('img', 'src', uris);
|
||||
if (oElements.length == 0)
|
||||
oElements = getElementsByAttribute ('iframe', 'src', uris);
|
||||
for (var i=0; i<oElements.length; i++) {
|
||||
oElements[i].style.visibility = 'hidden !important';
|
||||
oElements[i].style.width = '0';
|
||||
oElements[i].style.height = '0';
|
||||
}
|
||||
};
|
|
@ -1,7 +1,15 @@
|
|||
/**
|
||||
* An autosuggest textbox control.
|
||||
* from Nicholas C. Zakas (Author) example: http://www.nczonline.net/
|
||||
* Adopted for Midori by Alexander V. Butenko <a.butenka@gmail.com>
|
||||
/*
|
||||
An autosuggest textbox control.
|
||||
Copyright (C) 2012 Nicholas C. Zakas (Author) example: http://www.nczonline.net/
|
||||
Adopted for Midori by Alexander V. Butenko <a.butenka@gmail.com>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||
|
||||
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
|
||||
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
|
||||
Neither the name of the Nicholas C. Zakas nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS “AS IS” AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
function AutoSuggestControl(oTextbox /*:HTMLInputElement*/,
|
||||
|
|
91
data/bookmarks/Create.sql
Normal file
91
data/bookmarks/Create.sql
Normal file
|
@ -0,0 +1,91 @@
|
|||
CREATE TABLE IF NOT EXISTS bookmarks
|
||||
(
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
parentid INTEGER DEFAULT NULL,
|
||||
title TEXT,
|
||||
uri TEXT,
|
||||
desc TEXT,
|
||||
app INTEGER,
|
||||
toolbar INTEGER,
|
||||
pos_panel INTEGER,
|
||||
pos_bar INTEGER,
|
||||
created DATE DEFAULT CURRENT_TIMESTAMP,
|
||||
last_visit DATE,
|
||||
visit_count INTEGER DEFAULT 0,
|
||||
nick TEXT,
|
||||
|
||||
FOREIGN KEY(parentid) REFERENCES bookmarks(id) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
/* trigger: insert panel position */
|
||||
CREATE TRIGGER IF NOT EXISTS bookmarkInsertPosPanel
|
||||
AFTER INSERT ON bookmarks FOR EACH ROW
|
||||
BEGIN UPDATE bookmarks SET pos_panel = (
|
||||
SELECT ifnull(MAX(pos_panel),0)+1 FROM bookmarks WHERE
|
||||
(NEW.parentid IS NOT NULL AND parentid = NEW.parentid)
|
||||
OR (NEW.parentid IS NULL AND parentid IS NULL))
|
||||
WHERE id = NEW.id; END;
|
||||
|
||||
/* trigger: insert Bookmarkbar position */
|
||||
CREATE TRIGGER IF NOT EXISTS bookmarkInsertPosBar
|
||||
AFTER INSERT ON bookmarks FOR EACH ROW WHEN NEW.toolbar=1
|
||||
BEGIN UPDATE bookmarks SET pos_bar = (
|
||||
SELECT ifnull(MAX(pos_bar),0)+1 FROM bookmarks WHERE
|
||||
((NEW.parentid IS NOT NULL AND parentid = NEW.parentid)
|
||||
OR (NEW.parentid IS NULL AND parentid IS NULL)) AND toolbar=1)
|
||||
WHERE id = NEW.id; END;
|
||||
|
||||
/* trigger: update panel position */
|
||||
CREATE TRIGGER IF NOT EXISTS bookmarkUpdatePosPanel
|
||||
BEFORE UPDATE OF parentid ON bookmarks FOR EACH ROW
|
||||
WHEN ((NEW.parentid IS NULL OR OLD.parentid IS NULL)
|
||||
AND NEW.parentid IS NOT OLD.parentid) OR
|
||||
((NEW.parentid IS NOT NULL AND OLD.parentid IS NOT NULL)
|
||||
AND NEW.parentid!=OLD.parentid)
|
||||
BEGIN UPDATE bookmarks SET pos_panel = pos_panel-1
|
||||
WHERE ((OLD.parentid IS NOT NULL AND parentid = OLD.parentid)
|
||||
OR (OLD.parentid IS NULL AND parentid IS NULL)) AND pos_panel > OLD.pos_panel;
|
||||
UPDATE bookmarks SET pos_panel = (
|
||||
SELECT ifnull(MAX(pos_panel),0)+1 FROM bookmarks
|
||||
WHERE (NEW.parentid IS NOT NULL AND parentid = NEW.parentid)
|
||||
OR (NEW.parentid IS NULL AND parentid IS NULL))
|
||||
WHERE id = OLD.id; END;
|
||||
|
||||
/* trigger: update Bookmarkbar position */
|
||||
CREATE TRIGGER IF NOT EXISTS bookmarkUpdatePosBar0
|
||||
AFTER UPDATE OF parentid, toolbar ON bookmarks FOR EACH ROW
|
||||
WHEN ((NEW.parentid IS NULL OR OLD.parentid IS NULL)
|
||||
AND NEW.parentid IS NOT OLD.parentid)
|
||||
OR ((NEW.parentid IS NOT NULL AND OLD.parentid IS NOT NULL)
|
||||
AND NEW.parentid!=OLD.parentid) OR (OLD.toolbar=1 AND NEW.toolbar=0)
|
||||
BEGIN UPDATE bookmarks SET pos_bar = NULL WHERE id = NEW.id;
|
||||
UPDATE bookmarks SET pos_bar = pos_bar-1
|
||||
WHERE ((OLD.parentid IS NOT NULL AND parentid = OLD.parentid)
|
||||
OR (OLD.parentid IS NULL AND parentid IS NULL)) AND pos_bar > OLD.pos_bar; END;
|
||||
|
||||
/* trigger: update Bookmarkbar position */
|
||||
CREATE TRIGGER IF NOT EXISTS bookmarkUpdatePosBar1
|
||||
BEFORE UPDATE OF parentid, toolbar ON bookmarks FOR EACH ROW
|
||||
WHEN ((NEW.parentid IS NULL OR OLD.parentid IS NULL)
|
||||
AND NEW.parentid IS NOT OLD.parentid) OR
|
||||
((NEW.parentid IS NOT NULL AND OLD.parentid IS NOT NULL)
|
||||
AND NEW.parentid!=OLD.parentid) OR (OLD.toolbar=0 AND NEW.toolbar=1)
|
||||
BEGIN UPDATE bookmarks SET pos_bar = (
|
||||
SELECT ifnull(MAX(pos_bar),0)+1 FROM bookmarks WHERE
|
||||
(NEW.parentid IS NOT NULL AND parentid = NEW.parentid)
|
||||
OR (NEW.parentid IS NULL AND parentid IS NULL))
|
||||
WHERE id = OLD.id; END;
|
||||
|
||||
/* trigger: delete panel position */
|
||||
CREATE TRIGGER IF NOT EXISTS bookmarkDeletePosPanel
|
||||
AFTER DELETE ON bookmarks FOR EACH ROW
|
||||
BEGIN UPDATE bookmarks SET pos_panel = pos_panel-1
|
||||
WHERE ((OLD.parentid IS NOT NULL AND parentid = OLD.parentid)
|
||||
OR (OLD.parentid IS NULL AND parentid IS NULL)) AND pos_panel > OLD.pos_panel; END;
|
||||
|
||||
/* trigger: delete Bookmarkbar position */
|
||||
CREATE TRIGGER IF NOT EXISTS bookmarkDeletePosBar
|
||||
AFTER DELETE ON bookmarks FOR EACH ROW WHEN OLD.toolbar=1
|
||||
BEGIN UPDATE bookmarks SET pos_bar = pos_bar-1
|
||||
WHERE ((OLD.parentid IS NOT NULL AND parentid = OLD.parentid)
|
||||
OR (OLD.parentid IS NULL AND parentid IS NULL)) AND pos_bar > OLD.pos_bar; END;
|
6
data/bookmarks/Import_old_db_bookmarks.sql
Normal file
6
data/bookmarks/Import_old_db_bookmarks.sql
Normal file
|
@ -0,0 +1,6 @@
|
|||
INSERT INTO main.bookmarks (parentid, title, uri, desc, app, toolbar)
|
||||
SELECT NULL AS parentid, title, uri, desc, app, toolbar
|
||||
FROM old_db.bookmarks;
|
||||
UPDATE main.bookmarks SET parentid = (
|
||||
SELECT id FROM main.bookmarks AS b1 WHERE b1.title = (
|
||||
SELECT folder FROM old_db.bookmarks WHERE title = main.bookmarks.title));
|
BIN
data/close.png
BIN
data/close.png
Binary file not shown.
Before Width: | Height: | Size: 1.4 KiB |
|
@ -1,3 +0,0 @@
|
|||
[D-BUS Service]
|
||||
Name=com.nokia.midori
|
||||
Exec=/usr/bin/midori
|
|
@ -9,20 +9,21 @@
|
|||
<link rel="stylesheet" type="text/css" href="res://about.css" />
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<img id="logo" src="res://logo-shade.png" />
|
||||
<img id="icon" src="stock://gtk-dialog-error" />
|
||||
<div id="main">
|
||||
<div id="main" style="background-image: url({error_icon});">
|
||||
<div id="text">
|
||||
<h1>{title}</h1>
|
||||
<p id="message">{message}</p>
|
||||
<p id="description">{description}</p>
|
||||
<form method="GET" action="{uri}">
|
||||
<button type="submit" onclick="location.reload(); return false;">
|
||||
<p class="message">{message}<br><i>{description}</i></p>
|
||||
{suggestions}
|
||||
</div>
|
||||
<form method="GET" action="{uri}" id="button">
|
||||
<button type="submit" onclick="location.reload(); return false;" {autofocus}>
|
||||
<img style="{hide-button-images}" src="stock://gtk-refresh"/>
|
||||
<span>{tryagain}</span>
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<br style="clear: both;"/>
|
||||
</div>
|
||||
</body>
|
||||
|
|
323
data/faq.html
323
data/faq.html
|
@ -4,21 +4,20 @@
|
|||
<meta charset="utf-8" />
|
||||
<title>midori:faq</title>
|
||||
<meta name="generator" content="DokuWiki"/>
|
||||
<meta name="robots" content="noindex,nofollow"/>
|
||||
<meta name="date" content="2013-05-13T12:47:33+0200"/>
|
||||
<meta name="robots" content="index,follow"/>
|
||||
<meta name="keywords" content="midori,faq"/>
|
||||
<link rel="search" type="application/opensearchdescription+xml" href="/lib/exe/opensearch.php" title="Xfce Wiki"/>
|
||||
<link rel="start" href="/"/>
|
||||
<link rel="contents" href="/midori/faq?do=index" title="Sitemap"/>
|
||||
<link rel="alternate" type="application/rss+xml" title="Recent Changes" href="/feed.php"/>
|
||||
<link rel="alternate" type="application/rss+xml" title="Current Namespace" href="/feed.php?mode=list&ns=midori"/>
|
||||
<link rel="alternate" type="application/rss+xml" title="Recent changes" href="/feed.php"/>
|
||||
<link rel="alternate" type="application/rss+xml" title="Current namespace" href="/feed.php?mode=list&ns=midori"/>
|
||||
<link rel="alternate" type="text/html" title="Plain HTML" href="/_export/xhtml/midori/faq"/>
|
||||
<link rel="alternate" type="text/plain" title="Wiki Markup" href="/_export/raw/midori/faq"/>
|
||||
<link rel="canonical" href="http://wiki.xfce.org/midori/faq"/>
|
||||
<link rel="stylesheet" href="faq.css" />
|
||||
<script type="text/javascript">/*<![CDATA[*/var NS='midori';var JSINFO = {"id":"midori:faq","namespace":"midori"};
|
||||
/*!]]>*/</script>
|
||||
<script type="text/javascript" charset="utf-8" src="/lib/exe/js.php?tseed=1358197876"></script>
|
||||
<script type="text/javascript" charset="utf-8" src="/lib/exe/js.php?tseed=af5aadc3291d3baa2d8665b51a0cedf3"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div class="dokuwiki export">
|
||||
|
@ -29,7 +28,7 @@
|
|||
|
||||
<ul class="toc">
|
||||
<li class="level1"><div class="li"><a href="#midori_-_frequently_asked_questions">Midori - Frequently asked questions</a></div></li>
|
||||
<li class="level1"><div class="li"><a href="#getting_started">Getting started</a></div></li>
|
||||
<li class="level1"><div class="li"><a href="#about_midori">About Midori</a></div></li>
|
||||
<li class="level1"><div class="li"><a href="#common_problems">Common problems</a></div>
|
||||
<ul class="toc">
|
||||
<li class="level2"><div class="li"><a href="#security_features">Security features</a></div></li>
|
||||
|
@ -58,7 +57,14 @@
|
|||
<li class="level1"><div class="li"><a href="#user_scripts_and_styles">User scripts and styles</a></div>
|
||||
<ul class="toc">
|
||||
<li class="level2"><div class="li"><a href="#user_styles">User styles</a></div></li>
|
||||
</ul></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="level1"><div class="li"><a href="#midori_architecture">Midori Architecture</a></div>
|
||||
<ul class="toc">
|
||||
<li class="level2"><div class="li"><a href="#webkit_version_numbers">WebKit Version Numbers</a></div></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="level1"><div class="li"><a href="#midori_and_mediaherald">Midori and mediaHerald</a></div></li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -72,12 +78,12 @@
|
|||
</p>
|
||||
|
||||
<p>
|
||||
This is <a href="http://wiki.xfce.org/midori/faq">a snapshot of the online FAQ</a> about the Midori Web Browser. Anyone feel free to improve and/ or extend this page, but keep it clean and easy to read for other Xfce users.
|
||||
This is <a href="http://wiki.xfce.org/midori/faq">a snapshot of the online FAQ</a> about the Midori Web Browser. Anyone should feel free to improve or extend this page, but keep it clean and easy to read for other users.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT1 SECTION "Midori - Frequently asked questions" [1-289] -->
|
||||
<h1 class="sectionedit2" id="getting_started">Getting started</h1>
|
||||
<!-- EDIT1 SECTION "Midori - Frequently asked questions" [1-286] -->
|
||||
<h1 class="sectionedit2" id="about_midori">About Midori</h1>
|
||||
<div class="level1">
|
||||
|
||||
</div>
|
||||
|
@ -86,7 +92,7 @@ This is <a href="http://wiki.xfce.org/midori/faq">a snapshot of the online FAQ</
|
|||
<div class="level4">
|
||||
|
||||
<p>
|
||||
Midori is a Web Browser, that aims to be lightweight and fast. It aligns well with the Xfce philosophy of making the most out of available resources.
|
||||
Midori is a Web browser that aims to be lightweight and fast. It aligns well with the Xfce philosophy of making the most out of available resources. It has a customizable interface using the GTK+ toolkit.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
@ -113,7 +119,7 @@ The paw of a green cat. Obviously. Also it resembles the letter “M” in “Mi
|
|||
<div class="level4">
|
||||
|
||||
<p>
|
||||
Midori is basically very portable and should run on all platforms that its dependencies support.
|
||||
Midori is portable and should run on all platforms that its dependencies support. Releases exist on various Linux distributions, for Windows versions prior to 8.1 (for now), and BSD.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
@ -126,7 +132,7 @@ Midori and all delivered artwork are licensed under the LGPL2.
|
|||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT2 SECTION "Getting started" [290-1188] -->
|
||||
<!-- EDIT2 SECTION "About Midori" [287-1323] -->
|
||||
<h1 class="sectionedit3" id="common_problems">Common problems</h1>
|
||||
<div class="level1">
|
||||
|
||||
|
@ -136,19 +142,15 @@ Midori and all delivered artwork are licensed under the LGPL2.
|
|||
<div class="level4">
|
||||
|
||||
<p>
|
||||
Some websites discriminate against your browser.
|
||||
Some websites give up if they don't recognize your browser as Chrome, Firefox, <abbr title="Internet Explorer">IE</abbr>, etc.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you have Midori older than 0.3.5, go to Preferences > Network > Identify As, and choose Custom. Paste this into the entry:
|
||||
You can change the browser name sent to web sites at Preferences > Network > Identify As
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Mozilla/5.0 (X11; Linux) AppleWebKit/531.2+ Midori/0.3
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If this doesn't do the trick, try choosing Safari or Firefox.
|
||||
Either choose a predefined user-agent or choose Custom and find a suitable string in <a href="http://useragentstring.com/pages/useragentstring.php" class="urlextern" title="http://useragentstring.com/pages/useragentstring.php" rel="nofollow">a list</a>.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
@ -156,7 +158,7 @@ Many other websites use similar means of detecting the browser.
|
|||
</p>
|
||||
|
||||
<p>
|
||||
Ideally Google would follow <a href="http://code.google.com/p/doctype/wiki/ArticleGoogleChromeCompatFAQ#UserAgent_Detection" class="urlextern" title="http://code.google.com/p/doctype/wiki/ArticleGoogleChromeCompatFAQ#UserAgent_Detection" rel="nofollow">their own recommendation</a> at some point.
|
||||
Google <a href="http://web.archive.org/web/20100625211333/http://code.google.com/p/doctype/wiki/ArticleGoogleChromeCompatFAQ" class="urlextern" title="http://web.archive.org/web/20100625211333/http://code.google.com/p/doctype/wiki/ArticleGoogleChromeCompatFAQ" rel="nofollow">previously recommended</a> the superior practice for website creators of checking which features are present rather than browser name/version, but has since weakened this stance with their browser's growing market share.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
@ -165,7 +167,28 @@ Ideally Google would follow <a href="http://code.google.com/p/doctype/wiki/Artic
|
|||
<div class="level4">
|
||||
|
||||
<p>
|
||||
The set of themed icons Midori can use is very limited. For instance icons for a new tab or the throbber are not guaranteed to be available. To fix this, install a Freedesktop.org <abbr title="specification">spec</abbr> compliant icon theme, such as Elementary, Faenza, Buuf or GNOME.
|
||||
Midori uses a wide variety of icons which may not be present in all themes. For instance icons for a new tab, to represent scripts, or the throbber may not available. To fix this, install a Freedesktop.org <abbr title="specification">spec</abbr> compliant icon theme, such as Elementary, Faenza, Buuf or GNOME.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
if you need to set a custom path for these to be “searched in” (Kiosks and embedded devices for example):
|
||||
</p>
|
||||
|
||||
<p>
|
||||
XDG_DATA_HOME=/path/to/location
|
||||
</p>
|
||||
|
||||
<p>
|
||||
will add an extra path for the icons/… directory
|
||||
</p>
|
||||
|
||||
<p>
|
||||
In addition, GTK3 may remove icons from menus. This may be changed by placing
|
||||
</p>
|
||||
<pre class="code">gtk-menu-images=true</pre>
|
||||
|
||||
<p>
|
||||
in the file ~/.config/gtk-3.0/settings.ini
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
@ -201,6 +224,21 @@ Enable changing hotkeys while hovering menu items:
|
|||
|
||||
</div>
|
||||
|
||||
<h4 id="disable_middle_click_pasting">Disable middle click pasting</h4>
|
||||
<div class="level4">
|
||||
|
||||
<p>
|
||||
As of GTK+ >= 3.4 one can disable it globally in ~/.gtkrc-2.0
|
||||
</p>
|
||||
<pre class="code">gtk-enable-primary-paste = 0</pre>
|
||||
|
||||
<p>
|
||||
Otherwise by adding a line to ~/.config/midori/config
|
||||
</p>
|
||||
<pre class="code">middle-click-opens-selection=false</pre>
|
||||
|
||||
</div>
|
||||
|
||||
<h4 id="midori_crashes_shortly_before_pages_are_loaded">Midori crashes shortly before pages are loaded</h4>
|
||||
<div class="level4">
|
||||
|
||||
|
@ -222,7 +260,7 @@ export XDG_CACHE_HOME=/dev/shm
|
|||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT3 SECTION "Common problems" [1189-3300] -->
|
||||
<!-- EDIT3 SECTION "Common problems" [1324-4267] -->
|
||||
<h2 class="sectionedit4" id="security_features">Security features</h2>
|
||||
<div class="level2">
|
||||
|
||||
|
@ -240,6 +278,10 @@ Midori >= 0.4.7 automatically picks up the Strict-Transport-Security header a
|
|||
<h4 id="certificate_handling">Certificate Handling</h4>
|
||||
<div class="level4">
|
||||
|
||||
<p>
|
||||
Midori uses the system's ca-certificates, the exact locations depend on the distribution.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Midori >= 0.4.7 supports <a href="http://git.gnome.org/browse/gcr/tree/gcr" class="urlextern" title="http://git.gnome.org/browse/gcr/tree/gcr" rel="nofollow">gcr</a> for certificate display and management, you can click the lock in the urlbar to see detailed information. Earlier versions, or one without gcr will not handle certificates beyond the lock icon in the urlbar.
|
||||
</p>
|
||||
|
@ -250,7 +292,7 @@ Midori >= 0.4.7 supports <a href="http://git.gnome.org/browse/gcr/tree/gcr" c
|
|||
<div class="level5">
|
||||
|
||||
<p>
|
||||
No key store is available or it's incorrectly setup. By default GNOME keyring can do this. Under Xfce it is recommended to enable “GNOME services” under “Session and Startup settings”. Otherwise this can occur if a script doesn't correctly send the output of “gnome-keyring –startup” to the environment.
|
||||
No key store is available or it's incorrectly setup. By default GNOME keyring can do this. Under Xfce it is recommended to enable “GNOME services” under “Session and Startup settings”. To make sure, that the output of “gnome-keyring –startup” is correctly sent to the environment, you can add “export `gnome-keyring-daemon –start`” to .xinitrc.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
@ -263,7 +305,7 @@ No key store is available or it's incorrectly setup. By default GNOME keyri
|
|||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT4 SECTION "Security features" [3301-4386] -->
|
||||
<!-- EDIT4 SECTION "Security features" [4268-5485] -->
|
||||
<h2 class="sectionedit5" id="flash_doesn_t_work">Flash doesn't work</h2>
|
||||
<div class="level2">
|
||||
|
||||
|
@ -302,10 +344,15 @@ You can either run that above line and run Midori in the same terminal afterward
|
|||
<p>
|
||||
nspluginwrapper is a program that runs Flash and other Netscape plugins in a separate process. So a crash can't crash the whole browser and Flash, which is GTK+2 can run in GTK+3.
|
||||
</p>
|
||||
<pre class="code bash"><span class="kw2">sudo</span> <span class="kw2">apt-get install</span> flashplugin-installer
|
||||
<span class="kw2">sudo</span> <span class="kw2">apt-get install</span> nspluginwrapper
|
||||
<span class="kw2">sudo</span> nspluginwrapper <span class="re5">-i</span> <span class="sy0">/</span>usr<span class="sy0">/</span>lib<span class="sy0">/</span>flashplugin-installer<span class="sy0">/</span>libflashplayer.so
|
||||
nspluginwrapper <span class="re5">-v</span> <span class="re5">-a</span> <span class="re5">-n</span> <span class="re5">-i</span></pre>
|
||||
<pre class="code bash"><span class="kw2">sudo</span> <span class="kw2">apt-get install</span> nspluginwrapper
|
||||
<span class="co0"># On Debian/ Ubuntu - on other systems http://get.adobe.com/de/flashplayer/</span>
|
||||
<span class="kw2">sudo</span> <span class="kw2">apt-get install</span> flashplugin-installer
|
||||
<span class="co0"># cd into the folder where the plugin was installed</span>
|
||||
nspluginwrapper <span class="re5">-v</span> <span class="re5">-a</span> <span class="re5">-n</span> <span class="re5">-i</span> libflashplayer.so</pre>
|
||||
|
||||
<p>
|
||||
~/.mozilla can also be used with Adobe's tarball if system-wide install is not an option. The approach is confirmed to work with x86-64 as well.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Another remedy is using WebKit2 - starting with Midori 0.4.9 experimental support is available, <a href="https://trac.webkit.org/wiki/WebKitGTK/WebKit2Roadmap" class="urlextern" title="https://trac.webkit.org/wiki/WebKitGTK/WebKit2Roadmap" rel="nofollow">progress on WebKit2GTK+ can be seen in the WebKit wiki</a>.
|
||||
|
@ -335,6 +382,10 @@ There's no official support right now. It's possible to <a href="https
|
|||
<h4 id="scroll_with_middle_mouse_buttonpan-scrolling">Scroll with middle mouse button/ pan-scrolling</h4>
|
||||
<div class="level4">
|
||||
|
||||
<p>
|
||||
Windows-style middle-click behavior is
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<a href="http://ubuntuforums.org/showthread.php?t=478418" class="urlextern" title="http://ubuntuforums.org/showthread.php?t=478418" rel="nofollow">http://ubuntuforums.org/showthread.php?t=478418</a>
|
||||
</p>
|
||||
|
@ -347,24 +398,24 @@ Upstream Bug: <a href="https://bugs.webkit.org/show_bug.cgi?id=50561" class="url
|
|||
|
||||
</div>
|
||||
|
||||
<h4 id="html5_video_doesn_t_play">HTML5 Video doesn't play</h4>
|
||||
<h4 id="html5_videoaudio_doesn_t_play">HTML5 Video/ Audio doesn't play</h4>
|
||||
<div class="level4">
|
||||
|
||||
<p>
|
||||
You need to have GStreamer plugins installed which implement the codecs.
|
||||
<a href="#midori_architecture" title="midori:faq ↵" class="wikilink1">Midori uses GStreamer</a> for HTML5 audio and video support. Codecs, which handle particular formats of audio or video, are provided by GStreamer plugins which may need to be installed separately. Midori may be built with GTK+2 or GTK+3 (visit about:version to check), which correspond to GStreamer versions of 0.10 or 1.0 respectively.
|
||||
</p>
|
||||
<ol>
|
||||
<li class="level1"><div class="li"> You need gstreamer0.10-pulse if you're using PulseAudio.</div>
|
||||
<li class="level1"><div class="li"> You need gstreamer(0.10/1.0)-pulse if you're using PulseAudio.</div>
|
||||
</li>
|
||||
<li class="level1"><div class="li"> You may need gstreamer0.10-alsa for ALSA, depending on your distribution.</div>
|
||||
<li class="level1"><div class="li"> You may need gstreamer(0.10/1.0)-alsa for ALSA, depending on your distribution.</div>
|
||||
</li>
|
||||
<li class="level1"><div class="li"> You need plugins for Theora, gstreamer0.10-base and <abbr title="Motion Picture Experts Group">MPEG</abbr>-4 incluing aac (e.g. gst-plugins-faad), gstreamer0.10-bad. For WebM, you'll need plugins for vorbis (-base), matroska (-good), and vp8 (-bad). Have a look at <a href="http://www.gstreamer.net/documentation/plugins.html" class="urlextern" title="http://www.gstreamer.net/documentation/plugins.html" rel="nofollow">http://www.gstreamer.net/documentation/plugins.html</a> for details.</div>
|
||||
<li class="level1"><div class="li"> You need plugins for Theora, gstreamer(0.10/1.0)-base and MPEG-4 incluing aac (e.g. gst-plugins-faad), gstreamer(0.10/1.0)-bad. For WebM, you'll need plugins for vorbis (-base), matroska (-good), and vp8 (-bad). Have a look at <a href="http://www.gstreamer.net/documentation/plugins.html" class="urlextern" title="http://www.gstreamer.net/documentation/plugins.html" rel="nofollow">http://www.gstreamer.net/documentation/plugins.html</a> for details.</div>
|
||||
</li>
|
||||
<li class="level1"><div class="li"> For Youtube or Vimeo, you need WebKitGTK+ 1.1.20 or newer.</div>
|
||||
</li>
|
||||
<li class="level1"><div class="li"> You can <a href="http://ie.microsoft.com/testdrive/Graphics/VideoFormatSupport/Default.html" class="urlextern" title="http://ie.microsoft.com/testdrive/Graphics/VideoFormatSupport/Default.html" rel="nofollow">test your installed codecs here</a>..</div>
|
||||
</li>
|
||||
<li class="level1"><div class="li"> Since Midori 0.3.5 you can look at “about:version” to see which video codecs you have installed.</div>
|
||||
<li class="level1"><div class="li"> You can look at about:version to see which video codecs you have installed.</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
|
@ -472,7 +523,7 @@ Place the following in ~/.mutt/mailcap or ~/.mailcap:
|
|||
<div class="level4">
|
||||
|
||||
<p>
|
||||
Midori opens files with GIO, and falls back to xdg-open, exo-open or gnome-open if these are available. All of this relies on freedesktop.org <abbr title="Multipurpose Internet Mail Extension">MIME</abbr> configuration. To tweak this there are multiple options:
|
||||
Midori opens files with GIO, and falls back to xdg-open, exo-open or gnome-open if these are available. All of this relies on freedesktop.org MIME configuration. To tweak this there are multiple options:
|
||||
</p>
|
||||
<ol>
|
||||
<li class="level1"><div class="li"> Use 'Open With' with a graphical file manager</div>
|
||||
|
@ -589,12 +640,12 @@ Most settings listed at <a href="http://webkitgtk.org/reference/webkitgtk/stable
|
|||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT5 SECTION "Flash doesn't work" [4387-12724] -->
|
||||
<!-- EDIT5 SECTION "Flash doesn't work" [5486-14378] -->
|
||||
<h1 class="sectionedit6" id="privacy">Privacy</h1>
|
||||
<div class="level1">
|
||||
|
||||
</div>
|
||||
<!-- EDIT6 SECTION "Privacy" [12725-12747] -->
|
||||
<!-- EDIT6 SECTION "Privacy" [14379-14401] -->
|
||||
<h2 class="sectionedit7" id="blacklist_cookies">Blacklist cookies</h2>
|
||||
<div class="level2">
|
||||
|
||||
|
@ -603,11 +654,11 @@ As of Midori 0.4.4 you can add a hidden option to ~/.config/midori/config like s
|
|||
</p>
|
||||
<pre class="code">site-data-rules=-google.com,-facebook.com,!bugzilla.gnome.org,+bugs.launchpad.net</pre>
|
||||
<ol>
|
||||
<li class="level1"><div class="li"> Values prefixed with ”-” are always blocked</div>
|
||||
<li class="level1"><div class="li"> Values prefixed with “-” are always blocked</div>
|
||||
</li>
|
||||
<li class="level1"><div class="li"> Values prefixed with ”+” are always accepted</div>
|
||||
<li class="level1"><div class="li"> Values prefixed with “+” are always accepted</div>
|
||||
</li>
|
||||
<li class="level1"><div class="li"> Values prefixed with ”!” are not cleared in Clear Private Data</div>
|
||||
<li class="level1"><div class="li"> Values prefixed with “!” are not cleared in Clear Private Data</div>
|
||||
</li>
|
||||
<li class="level1"><div class="li"> No wildcards.</div>
|
||||
</li>
|
||||
|
@ -620,7 +671,7 @@ The feature is currently experimental and will change in future versions.
|
|||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT7 SECTION "Blacklist cookies" [12748-13284] -->
|
||||
<!-- EDIT7 SECTION "Blacklist cookies" [14402-14938] -->
|
||||
<h2 class="sectionedit8" id="adblock">Adblock</h2>
|
||||
<div class="level2">
|
||||
|
||||
|
@ -629,12 +680,12 @@ The Advertisement Blocker can be activated under Extensions. It uses the same li
|
|||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT8 SECTION "Adblock" [13285-13540] -->
|
||||
<!-- EDIT8 SECTION "Adblock" [14939-15194] -->
|
||||
<h1 class="sectionedit9" id="modes">Modes</h1>
|
||||
<div class="level1">
|
||||
|
||||
</div>
|
||||
<!-- EDIT9 SECTION "Modes" [13541-13561] -->
|
||||
<!-- EDIT9 SECTION "Modes" [15195-15215] -->
|
||||
<h2 class="sectionedit10" id="web_applications">Web Applications</h2>
|
||||
<div class="level2">
|
||||
|
||||
|
@ -655,7 +706,7 @@ There are two closely related features to open websites as dedicated windows of
|
|||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT10 SECTION "Web Applications" [13562-14078] -->
|
||||
<!-- EDIT10 SECTION "Web Applications" [15216-15732] -->
|
||||
<h2 class="sectionedit11" id="private_browsing">Private Browsing</h2>
|
||||
<div class="level2">
|
||||
|
||||
|
@ -668,7 +719,7 @@ A private window is a separate process, so crashes don't affect the normal
|
|||
</p>
|
||||
|
||||
<p>
|
||||
As of Midori 0.2.9 Private Browsing uses preferences, cookies, keyboard shortcuts and search engines from the normal session, but it won't save any changes. This behaviour can be emulated from the command line with ”-a” and ”-c”.
|
||||
As of Midori 0.2.9 Private Browsing uses preferences, cookies, keyboard shortcuts and search engines from the normal session, but it won't save any changes. This behaviour can be emulated from the command line with “-a” and “-c”.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
@ -681,7 +732,7 @@ The same options available to -a/ –app can be used for private browsing mode.
|
|||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT11 SECTION "Private Browsing" [14079-14998] -->
|
||||
<!-- EDIT11 SECTION "Private Browsing" [15733-16652] -->
|
||||
<h2 class="sectionedit12" id="portable_modewin32">Portable mode/ Win32</h2>
|
||||
<div class="level2">
|
||||
|
||||
|
@ -690,7 +741,7 @@ On Windows builds, -P/ –portable causes all data to be written to the “profi
|
|||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT12 SECTION "Portable mode/ Win32" [14999-15312] -->
|
||||
<!-- EDIT12 SECTION "Portable mode/ Win32" [16653-16966] -->
|
||||
<h2 class="sectionedit13" id="kiosk_mode">Kiosk mode</h2>
|
||||
<div class="level2">
|
||||
|
||||
|
@ -704,7 +755,7 @@ Available commands for -e can be listed with “midori –help-execute”.
|
|||
</p>
|
||||
|
||||
<p>
|
||||
If needed, a customized profile can be created with “midori -c /path/to/folder”. Using the shortcut editor extension, keyboard shortcuts can be removed as needed. Afterwards just append ”-c /path/to/folder” to the kiosk mode command line.
|
||||
If needed, a customized profile can be created with “midori -c /path/to/folder”. Using the shortcut editor extension, keyboard shortcuts can be removed as needed. Afterwards just append “-c /path/to/folder” to the kiosk mode command line.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
|
@ -722,7 +773,7 @@ Any links outside end up in an error page. All images and other files won't
|
|||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT13 SECTION "Kiosk mode" [15313-16350] -->
|
||||
<!-- EDIT13 SECTION "Kiosk mode" [16967-18004] -->
|
||||
<h2 class="sectionedit14" id="always_open_midori_in_fullscreen">Always open Midori in Fullscreen</h2>
|
||||
<div class="level2">
|
||||
|
||||
|
@ -755,7 +806,7 @@ If for whatever reason this isn't enough, <a href="https://live.gnome.org/D
|
|||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT14 SECTION "Always open Midori in Fullscreen" [16351-16878] -->
|
||||
<!-- EDIT14 SECTION "Always open Midori in Fullscreen" [18005-18532] -->
|
||||
<h2 class="sectionedit15" id="overriding_settings_and_loading_extensions">Overriding settings and loading extensions</h2>
|
||||
<div class="level2">
|
||||
|
||||
|
@ -772,7 +823,7 @@ As of Midori 0.5.0 the –execute command line switch got more powerful:
|
|||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT15 SECTION "Overriding settings and loading extensions" [16879-17086] -->
|
||||
<!-- EDIT15 SECTION "Overriding settings and loading extensions" [18533-18740] -->
|
||||
<h1 class="sectionedit16" id="proxy_servers">Proxy servers</h1>
|
||||
<div class="level1">
|
||||
|
||||
|
@ -831,7 +882,7 @@ As of Midori 0.5.0 and libSoup 2.40 SOCKS proxies can be used, the Preferences d
|
|||
</p>
|
||||
|
||||
<p>
|
||||
libSoup < 2.40 only supports <abbr title="Hyper Text Transfer Protocol">HTTP</abbr> proxy servers directly. A way to use SOCKS on Unix is to use tsocks with <abbr title="Secure Shell">SSH</abbr> as follows:
|
||||
libSoup < 2.40 only supports HTTP proxy servers directly. A way to use SOCKS on Unix is to use tsocks with SSH as follows:
|
||||
</p>
|
||||
<ol>
|
||||
<li class="level1"><div class="li"> Install 'tsocks'</div>
|
||||
|
@ -843,20 +894,20 @@ server_type = 5
|
|||
server_port = 5555</pre>
|
||||
</div>
|
||||
</li>
|
||||
<li class="level1"><div class="li"> Open an <abbr title="Secure Shell">SSH</abbr> connection with the same port: <pre class="code"> ssh -D localhost:5555 myhost.com </pre>
|
||||
<li class="level1"><div class="li"> Open an SSH connection with the same port: <pre class="code"> ssh -D localhost:5555 myhost.com </pre>
|
||||
</div>
|
||||
</li>
|
||||
<li class="level1"><div class="li"> Run Midori with “tsocks” in front of it: <pre class="code"> tsocks midori </pre>
|
||||
</div>
|
||||
</li>
|
||||
<li class="level1"><div class="li"> Now you can use for example <a href="http://www.whatsmyip.org/" class="urlextern" title="http://www.whatsmyip.org/" rel="nofollow">http://www.whatsmyip.org/</a> to verify that you are using a SOCKS connection. The IP address should match the one of your <abbr title="Secure Shell">SSH</abbr> host. Remember to keep the <abbr title="Secure Shell">SSH</abbr> login running, and don't suspend it, otherwise it won't work.</div>
|
||||
<li class="level1"><div class="li"> Now you can use for example <a href="http://www.whatsmyip.org/" class="urlextern" title="http://www.whatsmyip.org/" rel="nofollow">http://www.whatsmyip.org/</a> to verify that you are using a SOCKS connection. The IP address should match the one of your SSH host. Remember to keep the SSH login running, and don't suspend it, otherwise it won't work.</div>
|
||||
</li>
|
||||
<li class="level1"><div class="li"> If the connection fails for some reason, you should see a connection error.</div>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
</div>
|
||||
<!-- EDIT16 SECTION "Proxy servers" [17087-18908] -->
|
||||
<!-- EDIT16 SECTION "Proxy servers" [18741-20562] -->
|
||||
<h1 class="sectionedit17" id="keyboard_hotkeys">Keyboard Hotkeys</h1>
|
||||
<div class="level1">
|
||||
|
||||
|
@ -921,7 +972,7 @@ Default shortcuts for Find are:
|
|||
</p>
|
||||
|
||||
<p>
|
||||
Find: Ctrl+f ”/” and ”,”<br/>
|
||||
Find: Ctrl+f “/” and “,”<br/>
|
||||
|
||||
FindNext: Ctrl+g and Enter<br/>
|
||||
|
||||
|
@ -934,11 +985,11 @@ Dismissing Find:
|
|||
</p>
|
||||
|
||||
<p>
|
||||
When using Ctrl+f to bring up Find, use Ctrl+f again or ESC. When using ”/” or ”,” to bring up Find, the previous works here as well and by simply moving focus away from the Find box. For example: a Tab or a mouse click anywhere[besides links of course].
|
||||
When using Ctrl+f to bring up Find, use Ctrl+f again or ESC. When using “/” or “,” to bring up Find, the previous works here as well and by simply moving focus away from the Find box. For example: a Tab or a mouse click anywhere[besides links of course].
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT17 SECTION "Keyboard Hotkeys" [18909-20241] -->
|
||||
<!-- EDIT17 SECTION "Keyboard Hotkeys" [20563-21895] -->
|
||||
<h1 class="sectionedit18" id="mouse_gestures">Mouse Gestures</h1>
|
||||
<div class="level1">
|
||||
|
||||
|
@ -947,10 +998,10 @@ By default the right mouse button initiates gestures.
|
|||
</p>
|
||||
|
||||
<p>
|
||||
You can change the button using a hidden option:
|
||||
You can change the button (for example, to the middle mouse button) using a hidden option:
|
||||
</p>
|
||||
<ol>
|
||||
<li class="level1"><div class="li"> Create a text file ~/.config/midori/extensions/libmouse-gestures.so/config .</div>
|
||||
<li class="level1"><div class="li"> Create a text file ~/.config/midori/extensions/libmouse-gestures.so/<strong>config</strong> .</div>
|
||||
</li>
|
||||
<li class="level1"><div class="li"> Type the following in there:</div>
|
||||
</li>
|
||||
|
@ -959,23 +1010,20 @@ You can change the button using a hidden option:
|
|||
button=2</pre>
|
||||
|
||||
<p>
|
||||
As of Midori 0.5.0 individual gestures can be configured freely, consult “midori –help-execute” for a list of available left-hand actions:
|
||||
As of Midori 0.5.0 individual gestures can be configured freely in the file ~/.config/midori/extensions/libmouse-gestures.so/<strong>gestures</strong> .
|
||||
Consult “midori –help-execute” for a list of available actions, which are placed on the left of the equals sign. On the right goes a sequence of directions, (W)est, (E)east, (N)orth, (S)outh, (S)outh(W)est, etc., with a semicolon (;) after each, as shown below:
|
||||
</p>
|
||||
<pre class="code"> [gestures]
|
||||
Quit=W;E;
|
||||
TabPrevious=SW;
|
||||
TabNext=SE;</pre>
|
||||
|
||||
<p>
|
||||
Separated by ; the right-hand values are cardinal directions, (W)est, (E)east, (N)orth, (S)outh. You can also combine eg. WE for West East.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Additionally, there are programs allowing mouse gestures system-wide, for example <a href="http://easystroke.wiki.sourceforge.net/" class="urlextern" title="http://easystroke.wiki.sourceforge.net/" rel="nofollow">EasyStroke</a>.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT18 SECTION "Mouse Gestures" [20242-21006] -->
|
||||
<!-- EDIT18 SECTION "Mouse Gestures" [21896-22828] -->
|
||||
<h1 class="sectionedit19" id="user_scripts_and_styles">User scripts and styles</h1>
|
||||
<div class="level1">
|
||||
|
||||
|
@ -1005,7 +1053,7 @@ To manually install a userscript, you have to download the script as a file, and
|
|||
</p>
|
||||
|
||||
<p>
|
||||
If the script is only shown as source code on the page, you first have to create a new text file in a text editor, copy the source code into the new file, and save it as my-user-script.js where ”.js” is the extension.
|
||||
If the script is only shown as source code on the page, you first have to create a new text file in a text editor, copy the source code into the new file, and save it as my-user-script.js where “.js” is the extension.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
@ -1036,7 +1084,7 @@ You can also use <a href="http://rightfootin.blogspot.com/2009/04/flashblock-wan
|
|||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT19 SECTION "User scripts and styles" [21007-23561] -->
|
||||
<!-- EDIT19 SECTION "User scripts and styles" [22829-25383] -->
|
||||
<h2 class="sectionedit20" id="user_styles">User styles</h2>
|
||||
<div class="level2">
|
||||
|
||||
|
@ -1058,7 +1106,7 @@ To install a user style, you have to download the style as a file, and put it in
|
|||
</p>
|
||||
|
||||
<p>
|
||||
Note, if the style is only shown as source code on the page, you first have to create a new text file in a text editor, copy the source code into the new file, and save it as my-user-style.css where ”.css” is the extension.
|
||||
Note, if the style is only shown as source code on the page, you first have to create a new text file in a text editor, copy the source code into the new file, and save it as my-user-style.css where “.css” is the extension.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
@ -1082,22 +1130,127 @@ This user css is used to display the corresponding url when a link is hovered. T
|
|||
<p>
|
||||
Customize as needed:
|
||||
</p>
|
||||
<pre class="code">a[href]:hover {
|
||||
text-decoration: none !important;
|
||||
}
|
||||
a[href]:hover:after {
|
||||
content: attr(href);
|
||||
position: fixed; left: 4px; bottom: 4px;
|
||||
padding: 0 6px !important;
|
||||
max-width: 95%; overflow: hidden;
|
||||
white-space: nowrap; text-overflow: ellipsis;
|
||||
font:10pt sans-serif !important; text-shadow: 0 0 12px white;
|
||||
background-color: ButtonFace !important; color: ButtonText !important;
|
||||
opacity: 0.8; outline: ButtonFace solid thick;
|
||||
z-index: 9999;
|
||||
}</pre>
|
||||
<pre class="code css">a<span class="br0">[</span>href<span class="br0">]</span><span class="re2">:hover </span><span class="br0">{</span>
|
||||
<span class="kw1">text-decoration</span><span class="sy0">:</span> <span class="kw2">none</span> !important<span class="sy0">;</span>
|
||||
<span class="br0">}</span>
|
||||
a<span class="br0">[</span>href<span class="br0">]</span><span class="re2">:hover</span><span class="re2">:after </span><span class="br0">{</span>
|
||||
<span class="kw1">content</span><span class="sy0">:</span> attr<span class="br0">(</span>href<span class="br0">)</span><span class="sy0">;</span>
|
||||
<span class="kw1">position</span><span class="sy0">:</span> <span class="kw2">fixed</span><span class="sy0">;</span> <span class="kw1">left</span><span class="sy0">:</span> <span class="re3">4px</span><span class="sy0">;</span> <span class="kw1">bottom</span><span class="sy0">:</span> <span class="re3">4px</span><span class="sy0">;</span>
|
||||
<span class="kw1">padding</span><span class="sy0">:</span> <span class="nu0">0</span> <span class="re3">6px</span> !important<span class="sy0">;</span>
|
||||
<span class="kw1">max-width</span><span class="sy0">:</span> <span class="re3">95%</span><span class="sy0">;</span> <span class="kw1">overflow</span><span class="sy0">:</span> <span class="kw2">hidden</span><span class="sy0">;</span>
|
||||
<span class="kw1">white-space</span><span class="sy0">:</span> <span class="kw2">nowrap</span><span class="sy0">;</span> text-overflow<span class="sy0">:</span> ellipsis<span class="sy0">;</span>
|
||||
<span class="kw1">font</span><span class="sy0">:</span><span class="re3">10pt</span> <span class="kw2">sans-serif</span> !important<span class="sy0">;</span> <span class="kw1">text-shadow</span><span class="sy0">:</span> <span class="nu0">0</span> <span class="nu0">0</span> <span class="re3">12px</span> <span class="kw2">white</span><span class="sy0">;</span>
|
||||
<span class="kw1">background-color</span><span class="sy0">:</span> ButtonFace !important<span class="sy0">;</span> <span class="kw1">color</span><span class="sy0">:</span> ButtonText !important<span class="sy0">;</span>
|
||||
opacity<span class="sy0">:</span> <span class="nu0">0.8</span><span class="sy0">;</span> <span class="kw1">outline</span><span class="sy0">:</span> ButtonFace <span class="kw2">solid</span> <span class="kw2">thick</span><span class="sy0">;</span>
|
||||
<span class="kw1">z-index</span><span class="sy0">:</span> <span class="nu0">9999</span><span class="sy0">;</span>
|
||||
<span class="br0">}</span></pre>
|
||||
|
||||
</div>
|
||||
<!-- EDIT20 SECTION "User styles" [23562-] --></div>
|
||||
|
||||
<h4 id="tweaking_fonts_via_css">Tweaking fonts via CSS</h4>
|
||||
<div class="level4">
|
||||
|
||||
<p>
|
||||
If changing system-wide font settings isn't bringing the desired results or rendering should be tweaked only for websites <abbr title="Cascading Style Sheets">CSS</abbr> can be an alternative. Add the following to <strong>~/.local/share/midori/styles</strong>, then restart Midori and make sure that it is enabled Tools → Userstyles.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Customize as needed:
|
||||
</p>
|
||||
<pre class="code css"><span class="sy0">*</span> <span class="br0">{</span>
|
||||
font-smooth<span class="sy0">:</span><span class="kw2">always</span><span class="sy0">;</span>
|
||||
-webkit-font-smoothing<span class="sy0">:</span> antialiased<span class="sy0">;</span>
|
||||
text-rendering<span class="sy0">:</span> optimizeLegibility
|
||||
<span class="br0">}</span></pre>
|
||||
|
||||
</div>
|
||||
<!-- EDIT20 SECTION "User styles" [25384-28087] -->
|
||||
<h1 class="sectionedit21" id="midori_architecture">Midori Architecture</h1>
|
||||
<div class="level1">
|
||||
|
||||
<p>
|
||||
Midori stands on the shoulders of three giants in particular: the software libraries <a href="http://www.gtk.org/" class="urlextern" title="http://www.gtk.org/" rel="nofollow">GTK+</a>, <a href="http://webkitgtk.org/" class="urlextern" title="http://webkitgtk.org/" rel="nofollow">WebKitGTK+</a>, and <a href="https://developer.gnome.org/libsoup/" class="urlextern" title="https://developer.gnome.org/libsoup/" rel="nofollow">libsoup</a>. GTK+ provides the buttons, windows and menus, WebKitGTK+ draws and controls web pages, and libsoup downloads those pages.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
WebKitGTK+ itself uses two other important libraries: <a href="https://www.webkit.org/projects/javascript/index.html" class="urlextern" title="https://www.webkit.org/projects/javascript/index.html" rel="nofollow">JavaScriptCore</a>, a WebKit project which runs scripts on web pages; and <a href="http://gstreamer.freedesktop.org/" class="urlextern" title="http://gstreamer.freedesktop.org/" rel="nofollow">GStreamer</a>, which plays HTML5 video and audio.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT21 SECTION "Midori Architecture" [28088-28718] -->
|
||||
<h2 class="sectionedit22" id="webkit_version_numbers">WebKit Version Numbers</h2>
|
||||
<div class="level2">
|
||||
|
||||
<p>
|
||||
WebKit is the core of the Midori browser, and it determines how web pages are rendered. Because WebKit is a complex piece of software and compatible with various libraries, its version numbers and naming schemes can at times be confusing.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
WebKit itself is a library which works in many environments, such as Windows, <abbr title="Operating System">OS</abbr> X, and various Linux DE. There are different “ports”, one of which corresponds to each of these environments, and each of which is slightly different in bugs and features at any given time. The WebKit port used by Midori (because Midori is built with GTK+) is WebKitGTK+.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
WebKitGTK+ can be compiled against either GTK+2 or GTK+3. This will result in library filenames like libwebkitgtk-1.0.so or libwebkitgtk-3.0.so, respectively. This has nothing to do with the version of WebKit itself.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
WebKit has a “new <abbr title="Application Programming Interface">API</abbr> layer … designed from the ground up to support a split process model”–so pages can crash without the entire browser crashing. This layer is called WebKit2, and for WebKitGTK+ it requires building against GTK+3, producing a library file called libwebkit2gtk-3.0.so.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
To find out the version of WebKitGTK+ your build of Midori is using, visit about:version.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
|
||||
<h4 id="version_number_interactions">Version Number Interactions</h4>
|
||||
<div class="level4">
|
||||
|
||||
<p>
|
||||
The WebKit2 <abbr title="Application Programming Interface">API</abbr> layer is available from fairly old WebKit versions through the present, but Midori's WebKit2 support requires version 2.0.0 or newer of WebKitGTK+. Current versions of WebKitGTK+ continue to support GTK+2 and GTK+3 (the latter since 1.4.x or so). As stated above, the WebKit2 <abbr title="Application Programming Interface">API</abbr> layer is only available with GTK+3.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
Midori's support for WebKit2 is still provisional, and likely unsuitable for real-world daily usage; much work is being done in this area so that Midori can use WebKit2 by default at some point in the future.
|
||||
</p>
|
||||
|
||||
</div>
|
||||
<!-- EDIT22 SECTION "WebKit Version Numbers" [28719-30528] -->
|
||||
<h1 class="sectionedit23" id="midori_and_mediaherald">Midori and mediaHerald</h1>
|
||||
<div class="level1">
|
||||
|
||||
<p>
|
||||
mediaHerald is a dbus service (/org/midori/mediaHeraldallow) users to connect to dbus and check the titme and url of the video that midori plays in <strong>YOUTUBE</strong>, <strong>VIMEO</strong> or <strong>DAILYMOTION</strong>, the extension which does the work is called webmedia-now-playing.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
If you want to get the video title and the uri is easy more than easy <img src="/lib/images/smileys/icon_smile.gif" class="icon" alt=":-)" /> .
|
||||
</p>
|
||||
<pre class="code bash"><span class="co0">#!/bin/sh</span>
|
||||
|
||||
<span class="kw3">eval</span> $<span class="br0">(</span>dbus-send <span class="re5">--session</span> <span class="re5">--print-reply</span> <span class="re5">--dest</span>=org.midori.mediaHerald <span class="sy0">/</span>org<span class="sy0">/</span>midori<span class="sy0">/</span>mediaHerald org.freedesktop.DBus.Properties.GetAll string:<span class="st0">"org.midori.mediaHerald"</span> <span class="sy0">|</span> <span class="kw2">awk</span> <span class="st_h">'
|
||||
/string *"VideoTitle/{
|
||||
while (1) {
|
||||
getline line
|
||||
if (line ~ /string "/)
|
||||
sub(/.*string /, "TITLE=", line)
|
||||
print line
|
||||
break
|
||||
}
|
||||
}
|
||||
/string *"VideoUri/{
|
||||
while (1) {
|
||||
getline line
|
||||
if (line ~ /string "/)
|
||||
sub(/.*string /, "URI=", line)
|
||||
print line
|
||||
break
|
||||
}
|
||||
}
|
||||
'</span><span class="br0">)</span>
|
||||
<span class="kw3">echo</span> <span class="st0">"Midori is now playing: <span class="es2">$TITLE</span> ,the uri is: <span class="es2">$URI</span>"</span></pre>
|
||||
|
||||
</div>
|
||||
<!-- EDIT23 SECTION "Midori and mediaHerald" [30529-] --></div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
6
data/flummi/Create.sql
Normal file
6
data/flummi/Create.sql
Normal file
|
@ -0,0 +1,6 @@
|
|||
CREATE TABLE IF NOT EXISTS tasks
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
once INTEGER DEFAULT 1,
|
||||
command TEXT DEFAULT NULL
|
||||
);
|
6
data/forms/Create.sql
Normal file
6
data/forms/Create.sql
Normal file
|
@ -0,0 +1,6 @@
|
|||
CREATE TABLE IF NOT EXISTS forms
|
||||
(
|
||||
domain text,
|
||||
field text,
|
||||
value text
|
||||
)
|
|
@ -5,6 +5,7 @@
|
|||
-GtkWidget-focus-line-width: 0;
|
||||
-GtkWidget-focus-padding: 0;
|
||||
padding: 0;
|
||||
border-width: 1px 1px 0 0;
|
||||
}
|
||||
|
||||
GtkOverlay > * {
|
||||
|
|
13
data/history/Create.sql
Normal file
13
data/history/Create.sql
Normal file
|
@ -0,0 +1,13 @@
|
|||
CREATE TABLE IF NOT EXISTS history
|
||||
(
|
||||
uri text,
|
||||
title text,
|
||||
date integer,
|
||||
day integer
|
||||
);
|
||||
CREATE TABLE IF NOT EXISTS search
|
||||
(
|
||||
keywords text,
|
||||
uri text,
|
||||
day integer
|
||||
);
|
14
data/history/Day.sql
Normal file
14
data/history/Day.sql
Normal file
|
@ -0,0 +1,14 @@
|
|||
CREATE TEMPORARY TABLE backup
|
||||
(
|
||||
uri text,
|
||||
title text,
|
||||
date integer
|
||||
);
|
||||
INSERT INTO backup SELECT uri, title, date FROM history;
|
||||
DROP TABLE history;
|
||||
CREATE TABLE history (uri text, title text, date integer, day integer);
|
||||
INSERT INTO history SELECT uri, title, date,
|
||||
julianday(date(date,'unixepoch','start of day','+1 day'))
|
||||
- julianday('0001-01-01','start of day')
|
||||
FROM backup;
|
||||
DROP TABLE backup;
|
28
data/midori.appdata.xml.in
Normal file
28
data/midori.appdata.xml.in
Normal file
|
@ -0,0 +1,28 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!-- Copyright 2013 Christian Dywan -->
|
||||
<application>
|
||||
<id type="desktop">midori.desktop</id>
|
||||
<licence>CC0</licence>
|
||||
<description>
|
||||
<p>
|
||||
Midori is a fast little WebKit browser with support for HTML5. It can manage
|
||||
many open tabs and windows. The URL bar completes history, bookmarks, search
|
||||
engines and open tabs out of the box. Web developers can use the powerful
|
||||
web inspector that is a part of WebKit. Individual pages can easily be turned
|
||||
into web apps and new profiles can be created on demand.
|
||||
</p>
|
||||
<p>A number of extensions are included by default:</p>
|
||||
<ul>
|
||||
<li>Adblock with support for ABP filter lists and custom rules is built-in</li>
|
||||
<li>You can download files with Aria2 or SteadyFlow</li>
|
||||
<li>User scripts and styles support a la Greasemonkey</li>
|
||||
<li>Managing cookies and scripts via NoJS and Cookie Security Manager</li>
|
||||
<li>Switching open tabs in a vertical panel or a popup window</li>
|
||||
</ul>
|
||||
</description>
|
||||
<url type="homepage">http://www.midori-browser.org/</url>
|
||||
<screenshots>
|
||||
<screenshot type="default">http://www.midori-browser.org/images/screenshots/rdio.png</screenshot>
|
||||
</screenshots>
|
||||
<updatecontact>christian@twotoasts.de</updatecontact>
|
||||
</application>
|
|
@ -13,6 +13,7 @@ Exec=midori %U
|
|||
Icon=midori
|
||||
Terminal=false
|
||||
StartupNotify=true
|
||||
X-GNOME-UsesNotifications=true
|
||||
X-Osso-Type=application/x-executable
|
||||
X-Osso-Service=midori
|
||||
Actions=TabNew;WindowNew;Private;
|
||||
|
|
BIN
data/nojs/nojs-statusicon-allowed.png
Normal file
BIN
data/nojs/nojs-statusicon-allowed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 444 B |
BIN
data/nojs/nojs-statusicon-denied.png
Normal file
BIN
data/nojs/nojs-statusicon-denied.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 605 B |
BIN
data/nojs/nojs-statusicon-mixed.png
Normal file
BIN
data/nojs/nojs-statusicon-mixed.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 630 B |
8
data/notes/Create.sql
Normal file
8
data/notes/Create.sql
Normal file
|
@ -0,0 +1,8 @@
|
|||
CREATE TABLE IF NOT EXISTS notes
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
uri TEXT,
|
||||
title TEXT,
|
||||
note_content TEXT,
|
||||
tstamp INTEGER
|
||||
);
|
|
@ -27,6 +27,7 @@
|
|||
cursor: default;
|
||||
font-size: 13px;
|
||||
color: #4d4d4d;
|
||||
-webkit-user-select: none;
|
||||
}
|
||||
|
||||
html, body {
|
||||
|
@ -34,7 +35,11 @@
|
|||
width: 100%;
|
||||
height: 100%;
|
||||
outline: 0;
|
||||
background: #E1E1E1;
|
||||
background: #eee;
|
||||
}
|
||||
|
||||
input {
|
||||
width: 85%;
|
||||
}
|
||||
|
||||
#content {
|
||||
|
@ -55,54 +60,102 @@
|
|||
width: 85%;
|
||||
height: 75%;
|
||||
margin: auto;
|
||||
-webkit-box-shadow: 0 2px 5px rgba(0,0,0,.3), 0 0 0px #fff inset;
|
||||
border: 1px solid #bcbcbc;
|
||||
border-bottom-color: #a0a0a0;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.12),
|
||||
0 1px 2px rgba(0,0,0,0.24);
|
||||
position: relative;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
transition: all 200ms ease-in-out;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
div.shortcut .preview img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
cursor: pointer;
|
||||
-webkit-border-radius: 3px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
div.shortcut .preview.new {
|
||||
background-color: rgba(0,0,0,0.05);
|
||||
border: 1px solid rgba(0,0,0,0.15);
|
||||
|
||||
box-shadow: inset 0 0 1px 1px rgba(0,0,0,0.05),
|
||||
0 1px 0 0 rgba(255,255,255,0.40);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
div.shortcut .preview.new .add {
|
||||
display: block;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
cursor: pointer;
|
||||
-webkit-box-shadow: 0 2px 5px rgba(0,0,0,.3), 0 0 0px #fff inset;
|
||||
background-image: -webkit-gradient(
|
||||
linear, center top, center bottom,
|
||||
from(#f6f6f6), to(#e3e3e3));
|
||||
background-repeat: repeat-x;
|
||||
-webkit-border-radius: 3px;
|
||||
background-color: #777;
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
line-height: 64px;
|
||||
text-align: center;
|
||||
border-radius: 32px;
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
margin-left: -32px;
|
||||
margin-top: -32px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.12),
|
||||
0 1px 2px rgba(0,0,0,0.24);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
div.shortcut .preview.new .add:after {
|
||||
content: "+";
|
||||
color: #fff;
|
||||
font-size: 48px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.title {
|
||||
background: transparent;
|
||||
border: 2px solid transparent;
|
||||
display: block;
|
||||
text-align: center;
|
||||
margin: 8px;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
cursor: text;
|
||||
text-shadow: 0 1px 0 rgba(255,255,255,0.5);
|
||||
outline: none;
|
||||
transition: all 200ms ease-in-out;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.preview.new ~ .title {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.title.active {
|
||||
border-bottom-color: #4CAF50;
|
||||
}
|
||||
|
||||
.cross {
|
||||
color: #fff;
|
||||
display: block;
|
||||
width: 27px;
|
||||
height: 27px;
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
text-align: center;
|
||||
line-height: 32px;
|
||||
top: -14px;
|
||||
right: -14px;
|
||||
background: url(res://close.png);
|
||||
position: absolute;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
background-color: #555;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 1px 3px rgba(0,0,0,0.12),
|
||||
0 1px 2px rgba(0,0,0,0.24);
|
||||
}
|
||||
|
||||
.cross:after {
|
||||
content: '+';
|
||||
-webkit-transform: rotate(45deg);
|
||||
font-size: 22px;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
div.shortcut .preview:hover .cross {
|
||||
|
@ -114,13 +167,23 @@
|
|||
display:none;
|
||||
}
|
||||
|
||||
.selected {
|
||||
outline: 1px dotted black;
|
||||
background-color: #eef;
|
||||
.selectable {
|
||||
-webkit-user-select: text;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
var previousName = "";
|
||||
|
||||
function input_key_down (ev) {
|
||||
// 13 is the key code for enter
|
||||
if(ev.keyCode == 13) ev.target.blur();
|
||||
// 27 is the key code for escape
|
||||
if(ev.keyCode == 27) {
|
||||
ev.target.value = previousName;
|
||||
ev.target.blur();
|
||||
}
|
||||
}
|
||||
|
||||
function add_tile (ev) {
|
||||
ev.preventDefault();
|
||||
|
@ -136,17 +199,24 @@
|
|||
console.log ("speed_dial-save-add " + id + " " + url);
|
||||
}
|
||||
|
||||
function rename_tile (ev) {
|
||||
var old_name = ev.target.textContent;
|
||||
|
||||
var name = prompt ("{enter_shortcut_name}", old_name);
|
||||
if (!name)
|
||||
return;
|
||||
function done_editing_title (ev) {
|
||||
input = ev.target;
|
||||
input.className = "title";
|
||||
|
||||
var name = ev.target.value;
|
||||
if (name == "")
|
||||
name = previousName;
|
||||
var id = ev.target.parentNode.id;
|
||||
console.log ("speed_dial-save-rename " + id + " " + name);
|
||||
}
|
||||
|
||||
function editing_tile (ev) {
|
||||
input = ev.target;
|
||||
previousName = input.value;
|
||||
// indicate to user they are editing the title
|
||||
input.className = input.className + " active";
|
||||
}
|
||||
|
||||
function delete_tile (ev) {
|
||||
ev.preventDefault();
|
||||
|
||||
|
@ -157,7 +227,6 @@
|
|||
console.log ("speed_dial-save-delete " + id);
|
||||
}
|
||||
|
||||
|
||||
var firstNode, secondNode;
|
||||
var cursor;
|
||||
|
||||
|
@ -176,6 +245,23 @@
|
|||
if (ev == undefined)
|
||||
return;
|
||||
|
||||
/* If the target of the event is an element of class title
|
||||
* and not also of class add-shortcut we are editing title
|
||||
*/
|
||||
var classes = ev.target.className.split(" ");
|
||||
var edit_title = false;
|
||||
for (var i = 0; i < classes.length; i++) {
|
||||
if(classes[i] == "add-shortcut") {
|
||||
edit_title = false;
|
||||
break;
|
||||
}
|
||||
if(classes[i] == "title")
|
||||
edit_title = true;
|
||||
}
|
||||
|
||||
if(edit_title)
|
||||
return;
|
||||
|
||||
ev.preventDefault();
|
||||
var ele = ev.target;
|
||||
cursor = ele.style.cursor;
|
||||
|
@ -197,7 +283,8 @@
|
|||
var eparent = get_dial_div (ele);
|
||||
|
||||
ele.style.cursor = cursor;
|
||||
secondNode = eparent.id;
|
||||
if(eparent != undefined) secondNode = eparent.id;
|
||||
else return;
|
||||
|
||||
/* ommit just mere clicking the dial */
|
||||
if (firstNode != secondNode && firstNode != undefined)
|
||||
|
@ -236,8 +323,11 @@
|
|||
var titles = document.getElementsByClassName ("title");
|
||||
var len = titles.length;
|
||||
for (var i = 0; i < len; i++) {
|
||||
if (titles[i].parentNode.childNodes[0].className != "preview new")
|
||||
titles[i].addEventListener ('click', rename_tile, false);
|
||||
if (titles[i].parentNode.childNodes[0].className != "preview new") {
|
||||
titles[i].addEventListener ('click', editing_tile, false);
|
||||
titles[i].addEventListener ('blur', done_editing_title, false);
|
||||
titles[i].addEventListener ('keydown', input_key_down, false);
|
||||
}
|
||||
}
|
||||
|
||||
var crosses = document.getElementsByClassName ("cross");
|
||||
|
|
33
data/tabby/Create.sql
Normal file
33
data/tabby/Create.sql
Normal file
|
@ -0,0 +1,33 @@
|
|||
CREATE TABLE IF NOT EXISTS sessions
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
parent_id INTEGER DEFAULT 0,
|
||||
crdate INTEGER DEFAULT 0,
|
||||
tstamp INTEGER DEFAULT 0,
|
||||
closed INTEGER DEFAULT 0,
|
||||
title TEXT DEFAULT NULL,
|
||||
FOREIGN KEY(parent_id) REFERENCES sessions(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tabs
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
session_id INTEGER NOT NULL,
|
||||
uri TEXT DEFAULT NULL,
|
||||
icon TEXT DEFAULT NULL,
|
||||
title TEXT DEFAULT NULL,
|
||||
crdate INTEGER DEFAULT 0,
|
||||
tstamp INTEGER DEFAULT 0,
|
||||
closed INTEGER DEFAULT 0,
|
||||
FOREIGN KEY(session_id) REFERENCES sessions(id)
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS tab_history
|
||||
(
|
||||
id INTEGER PRIMARY KEY,
|
||||
tab_id INTEGER,
|
||||
url TEXT,
|
||||
icon TEXT,
|
||||
title TEXT,
|
||||
FOREIGN KEY(tab_id) REFERENCES tabs(id)
|
||||
);
|
4
data/tabby/Update1.sql
Normal file
4
data/tabby/Update1.sql
Normal file
|
@ -0,0 +1,4 @@
|
|||
ALTER TABLE tabs ADD sorting REAL DEFAULT 0;
|
||||
|
||||
CREATE INDEX sorting on tabs (sorting ASC);
|
||||
CREATE INDEX tstamp on tabs (tstamp ASC);
|
14
docs/api/CMakeLists.txt
Normal file
14
docs/api/CMakeLists.txt
Normal file
|
@ -0,0 +1,14 @@
|
|||
# Copyright (C) 2013 Olivier Duchateau
|
||||
|
||||
include (GtkDoc)
|
||||
|
||||
if (GTKDOC_FOUND)
|
||||
list (APPEND MODULES "katze" "midori")
|
||||
foreach (MOD ${MODULES})
|
||||
if (EXISTS "${CMAKE_SOURCE_DIR}/${MOD}")
|
||||
gtkdoc (${MOD})
|
||||
endif ()
|
||||
endforeach ()
|
||||
else ()
|
||||
message (FATAL_ERROR "gtk-doc not found")
|
||||
endif ()
|
|
@ -1,38 +0,0 @@
|
|||
#! /usr/bin/env python
|
||||
# WAF build script for midori
|
||||
# This file is licensed under the terms of the expat license, see the file EXPAT.
|
||||
|
||||
import pproc as subprocess
|
||||
import os
|
||||
import Utils
|
||||
|
||||
blddir = '_build' # recognized by ack
|
||||
|
||||
for module in ('midori', 'katze'):
|
||||
try:
|
||||
if not os.access (blddir, os.F_OK):
|
||||
Utils.check_dir (blddir)
|
||||
if not os.access (blddir + '/docs', os.F_OK):
|
||||
Utils.check_dir (blddir + '/docs')
|
||||
if not os.access (blddir + '/docs/api', os.F_OK):
|
||||
Utils.check_dir (blddir + '/docs/api')
|
||||
subprocess.call (['gtkdoc-scan', '--module=' + module,
|
||||
'--source-dir=' + module, '--output-dir=' + blddir + '/docs/api/' + module,
|
||||
'--rebuild-sections', '--rebuild-types'])
|
||||
os.chdir (blddir + '/docs/api/' + module)
|
||||
subprocess.call (['gtkdoc-mktmpl', '--module=' + module,
|
||||
'--output-dir=.' + module])
|
||||
subprocess.call (['gtkdoc-mkdb', '--module=' + module,
|
||||
'--source-dir=.', '--output-dir=xml',
|
||||
'--source-suffixes=c,h', '--output-format=xml',
|
||||
'--default-includes=%s/%s.h' % (module, module),
|
||||
'--sgml-mode', '--main-sgml-file=%s.sgml' % module])
|
||||
if not os.access ('html', os.F_OK):
|
||||
Utils.check_dir ('html')
|
||||
os.chdir ('html')
|
||||
subprocess.call (['gtkdoc-mkhtml', module, '../%s.sgml' % module])
|
||||
Utils.pprint ('YELLOW', "Created documentation for %s." % module)
|
||||
os.chdir ('../../../../..')
|
||||
except Exception, msg:
|
||||
print msg
|
||||
Utils.pprint ('RED', "Failed to create documentation for %s." % module)
|
134
extensions/CMakeLists.txt
Normal file
134
extensions/CMakeLists.txt
Normal file
|
@ -0,0 +1,134 @@
|
|||
# Copyright (C) 2013 Christian Dywan <christian@twotoasts.de>
|
||||
|
||||
set(EXTENSIONDIR "${CMAKE_INSTALL_FULL_LIBDIR}/${CMAKE_PROJECT_NAME}")
|
||||
include_directories(
|
||||
"${CMAKE_SOURCE_DIR}"
|
||||
"${CMAKE_SOURCE_DIR}/midori"
|
||||
"${CMAKE_SOURCE_DIR}/katze"
|
||||
${DEPS_INCLUDE_DIRS}
|
||||
${OPTS_INCLUDE_DIRS}
|
||||
${DEPS_GTK_INCLUDE_DIRS}
|
||||
${OPTS_GTK_INCLUDE_DIRS}
|
||||
${CMAKE_BINARY_DIR}
|
||||
"${CMAKE_BINARY_DIR}/midori"
|
||||
)
|
||||
file(GLOB EXTENSIONS RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *)
|
||||
if (HALF_BRO_INCOM_WEBKIT2)
|
||||
list(REMOVE_ITEM EXTENSIONS
|
||||
"cookie-permissions"
|
||||
"addons.c"
|
||||
"formhistory"
|
||||
"external-download-manager.vala"
|
||||
"nojs"
|
||||
"nsplugin-manager.vala"
|
||||
"tabs2one.c"
|
||||
)
|
||||
endif ()
|
||||
|
||||
# FIXME: re-enable webmedia extension
|
||||
# once we have working notifications on win
|
||||
if (WIN32)
|
||||
list(REMOVE_ITEM EXTENSIONS "webmedia-now-playing.vala")
|
||||
endif()
|
||||
|
||||
# FIXME: not stable enough for release
|
||||
if (NOT REVISION)
|
||||
list(REMOVE_ITEM EXTENSIONS "tabs2one.c")
|
||||
endif()
|
||||
|
||||
foreach(UNIT_SRC ${EXTENSIONS})
|
||||
string(FIND ${UNIT_SRC} ".c" UNIT_EXTENSION)
|
||||
if (UNIT_EXTENSION GREATER -1)
|
||||
string(REPLACE ".c" "" UNIT ${UNIT_SRC})
|
||||
add_library(${UNIT} MODULE ${UNIT_SRC})
|
||||
target_link_libraries(${UNIT}
|
||||
${LIBMIDORI}
|
||||
)
|
||||
set_target_properties(${UNIT} PROPERTIES
|
||||
COMPILE_FLAGS ${CFLAGS}
|
||||
)
|
||||
install(TARGETS ${UNIT}
|
||||
LIBRARY DESTINATION ${EXTENSIONDIR}
|
||||
)
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
foreach(UNIT_SRC ${EXTENSIONS})
|
||||
string(FIND ${UNIT_SRC} "." UNIT_EXTENSION)
|
||||
if (UNIT_EXTENSION EQUAL -1)
|
||||
file(GLOB UNIT_FILES RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${UNIT_SRC}/*.c")
|
||||
file(GLOB UNIT_FILES_VALA RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} "${UNIT_SRC}/*.vala")
|
||||
if (UNIT_FILES_VALA)
|
||||
include(ValaPrecompile)
|
||||
vala_precompile(UNIT_SRC_C ${UNIT_SRC}
|
||||
${UNIT_FILES_VALA}
|
||||
PACKAGES
|
||||
${PKGS}
|
||||
OPTIONS
|
||||
${VALAFLAGS}
|
||||
--use-header="${CMAKE_PROJECT_NAME}-core.h"
|
||||
GENERATE_HEADER
|
||||
"${UNIT_SRC}"
|
||||
GENERATE_HEADER
|
||||
${UNIT}
|
||||
CUSTOM_VAPIS
|
||||
${EXTRA_VAPIS}
|
||||
"${CMAKE_SOURCE_DIR}/midori/midori.vapi"
|
||||
"${CMAKE_BINARY_DIR}/midori/${LIBMIDORI}.vapi"
|
||||
)
|
||||
set(UNIT_FILES ${UNIT_FILES} ${UNIT_SRC_C})
|
||||
endif ()
|
||||
if (UNIT_FILES)
|
||||
add_library(${UNIT_SRC} MODULE ${UNIT_FILES})
|
||||
target_link_libraries(${UNIT_SRC}
|
||||
${LIBMIDORI}
|
||||
)
|
||||
install(TARGETS ${UNIT_SRC}
|
||||
LIBRARY DESTINATION ${EXTENSIONDIR}
|
||||
)
|
||||
# extensions with vala code get the lenient VALA_CFLAGS
|
||||
# others get the usual CFLAGS with -Wall and -Werror
|
||||
if (UNIT_FILES_VALA)
|
||||
set_target_properties(${UNIT_SRC} PROPERTIES
|
||||
COMPILE_FLAGS ${VALA_CFLAGS}
|
||||
)
|
||||
else ()
|
||||
set_target_properties(${UNIT_SRC} PROPERTIES
|
||||
COMPILE_FLAGS ${CFLAGS}
|
||||
)
|
||||
endif ()
|
||||
endif ()
|
||||
endif ()
|
||||
endforeach ()
|
||||
|
||||
foreach(UNIT_SRC ${EXTENSIONS})
|
||||
string(FIND ${UNIT_SRC} ".vala" UNIT_EXTENSION)
|
||||
if (UNIT_EXTENSION GREATER -1)
|
||||
string(REPLACE ".vala" "" UNIT ${UNIT_SRC})
|
||||
include(ValaPrecompile)
|
||||
vala_precompile(UNIT_SRC_C ${UNIT}
|
||||
${UNIT_SRC}
|
||||
PACKAGES
|
||||
${PKGS}
|
||||
OPTIONS
|
||||
${VALAFLAGS}
|
||||
--use-header="${CMAKE_PROJECT_NAME}-core.h"
|
||||
GENERATE_HEADER
|
||||
${UNIT}
|
||||
CUSTOM_VAPIS
|
||||
${EXTRA_VAPIS}
|
||||
"${CMAKE_SOURCE_DIR}/midori/midori.vapi"
|
||||
"${CMAKE_BINARY_DIR}/midori/${LIBMIDORI}.vapi"
|
||||
)
|
||||
add_library(${UNIT} MODULE ${UNIT_SRC_C})
|
||||
target_link_libraries(${UNIT}
|
||||
${LIBMIDORI}
|
||||
)
|
||||
set_target_properties(${UNIT} PROPERTIES
|
||||
COMPILE_FLAGS "${VALA_CFLAGS}"
|
||||
)
|
||||
install(TARGETS ${UNIT}
|
||||
LIBRARY DESTINATION ${EXTENSIONDIR}
|
||||
)
|
||||
endif ()
|
||||
endforeach ()
|
373
extensions/about.vala
Normal file
373
extensions/about.vala
Normal file
|
@ -0,0 +1,373 @@
|
|||
/*
|
||||
Copyright (C) 2013 André Stösel <andre@stoesel.de>
|
||||
Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace About {
|
||||
private abstract class Page : GLib.Object {
|
||||
public abstract string uri { get; set; }
|
||||
public abstract void get_contents (Midori.View view, string uri);
|
||||
protected void load_html (Midori.View view, string content, string uri) {
|
||||
#if HAVE_WEBKIT2
|
||||
view.web_view.load_html (content, uri);
|
||||
#else
|
||||
view.web_view.load_html_string (content, uri);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
private class Widgets : Page {
|
||||
public override string uri { get; set; default = "about:widgets"; }
|
||||
public override void get_contents (Midori.View view, string uri) {
|
||||
string[] widgets = {
|
||||
"<input value=\"demo\"%s>",
|
||||
"<p><input type=\"password\" value=\"demo\"%s></p>",
|
||||
"<p><input type=\"checkbox\" value=\"demo\"%s> demo</p>",
|
||||
"<p><input type=\"radio\" value=\"demo\"%s> demo</p>",
|
||||
"<p><select%s><option>foo bar</option><option selected>spam eggs</option></select></p>",
|
||||
"<p><select%s size=\"3\"><option>foo bar</option><option selected>spam eggs</option></select></p>",
|
||||
"<p><input type=\"file\"%s></p>",
|
||||
"<p><input type=\"file\" multiple%s></p>",
|
||||
"<input type=\"button\" value=\"demo\"%s>",
|
||||
"<p><input type=\"email\" value=\"user@localhost.com\"%s></p>",
|
||||
"<input type=\"url\" value=\"http://www.example.com\"%s>",
|
||||
"<input type=\"tel\" value=\"+1 234 567 890\" pattern=\"^[0+][1-9 /-]*$\"%s>",
|
||||
"<input type=\"number\" min=1 max=9 step=1 value=\"4\"%s>",
|
||||
"<input type=\"range\" min=1 max=9 step=1 value=\"4\"%s>",
|
||||
"<input type=\"date\" min=1990-01-01 max=2010-01-01%s>",
|
||||
"<input type=\"search\" placeholder=\"demo\"%s>",
|
||||
"<textarea%s>Lorem ipsum doloret sit amet…</textarea>",
|
||||
"<input type=\"color\" value=\"#d1eeb9\"%s>",
|
||||
"<progress min=1 max=9 value=4 %s></progress>",
|
||||
"<keygen type=\"rsa\" challenge=\"235ldahlae983dadfar\"%s>",
|
||||
"<p><input type=\"reset\"%s></p>",
|
||||
"<input type=\"submit\"%s>"
|
||||
};
|
||||
|
||||
string content = """<html>
|
||||
<head>
|
||||
<style>
|
||||
.fallback::-webkit-slider-thumb,
|
||||
.fallback, .fallback::-webkit-file-upload-button {
|
||||
-webkit-appearance: none !important;
|
||||
}
|
||||
.column {
|
||||
display:inline-block; vertical-align:top;
|
||||
width:25%;
|
||||
margin-right:1%;
|
||||
}
|
||||
</style>
|
||||
<title>%s</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>%s</h1>
|
||||
<div class="column">%s</div>
|
||||
<div class="column">%s</div>
|
||||
<div class="column">%s</div>
|
||||
<p><a href="http://example.com" target="wp" onclick="javascript:window.open('http://example.com','wp','width=320, height=240, toolbar=false'); return false;">Popup window</a></p>
|
||||
</body>
|
||||
</html>""";
|
||||
|
||||
string[] widget_options = {"", " disabled", " class=\"fallback\""};
|
||||
string[] widgets_formated = {"", "", ""};
|
||||
|
||||
for (int i = 0; i < widget_options.length && i < widgets_formated.length; i++) {
|
||||
for (int j = 0; j < widgets.length; j++) {
|
||||
widgets_formated[i] = widgets_formated[i] + widgets[j].printf (widget_options[i]);
|
||||
}
|
||||
}
|
||||
|
||||
this.load_html (view, content.printf (uri, uri, widgets_formated[0], widgets_formated[1], widgets_formated[2]), uri);
|
||||
}
|
||||
}
|
||||
|
||||
private class Version : Page {
|
||||
public override string uri { get; set; }
|
||||
private GLib.HashTable<string, Page> about_pages;
|
||||
|
||||
public Version (string alias, HashTable<string, Page> about_pages) {
|
||||
this.uri = alias;
|
||||
this.about_pages = about_pages;
|
||||
}
|
||||
|
||||
private string list_about_uris () {
|
||||
string links = "";
|
||||
foreach (unowned string uri in about_pages.get_keys ())
|
||||
links = links + "<a href=\"%s\">%s</a> ".printf (uri, uri);
|
||||
return "<p>%s</p>".printf (links);
|
||||
}
|
||||
|
||||
public override void get_contents (Midori.View view, string uri) {
|
||||
string contents = """<html>
|
||||
<head><title>about:version</title></head>
|
||||
<body>
|
||||
<h1>a<span style="position: absolute; left: -1000px; top: -1000px">lias a=b; echo Copy carefully #</span>bout:version</h1>
|
||||
<p>%s</p>
|
||||
<img src="res://logo-shade.png" style="position: absolute; right: 15px; bottom: 15px; z-index: -9;">
|
||||
<table>
|
||||
<tr><td>Command line %s</td></tr>
|
||||
%s
|
||||
<tr><td>Platform %s %s %s</td></tr>
|
||||
<tr><td>Identification %s</td></tr>
|
||||
%s
|
||||
</table>
|
||||
<table>
|
||||
%s
|
||||
</table>
|
||||
%s
|
||||
</body>
|
||||
</html>""";
|
||||
|
||||
GLib.StringBuilder versions = new GLib.StringBuilder ();
|
||||
Midori.View.list_versions (versions, true);
|
||||
|
||||
string ident;
|
||||
unowned string architecture;
|
||||
unowned string platform;
|
||||
unowned string sys_name = Midori.WebSettings.get_system_name (out architecture, out platform);
|
||||
view.settings.get ("user-agent", out ident);
|
||||
|
||||
GLib.StringBuilder video_formats = new GLib.StringBuilder ();
|
||||
view.list_video_formats (video_formats, true);
|
||||
|
||||
GLib.StringBuilder ns_plugins = new GLib.StringBuilder ();
|
||||
view.list_plugins (ns_plugins, true);
|
||||
|
||||
/* TODO: list active extensions */
|
||||
|
||||
this.load_html (view, contents.printf (
|
||||
_("Version numbers in brackets show the version used at runtime."),
|
||||
Midori.Paths.get_command_line_str (true),
|
||||
versions.str,
|
||||
platform, sys_name, architecture != null ? architecture : "",
|
||||
ident,
|
||||
video_formats.str,
|
||||
ns_plugins.str,
|
||||
this.list_about_uris ()
|
||||
), uri);
|
||||
}
|
||||
}
|
||||
|
||||
private class Private : Page {
|
||||
public override string uri { get; set; default = "about:private"; }
|
||||
public override void get_contents (Midori.View view, string uri) {
|
||||
this.load_html (view, """<html dir="ltr">
|
||||
<head>
|
||||
<title>%s</title>
|
||||
<link rel="stylesheet" type="text/css" href="res://about.css">
|
||||
</head>
|
||||
<body>
|
||||
<img id="logo" src="res://logo-shade.png" />
|
||||
<div id="main" style="background-image: url(stock://dialog/gtk-dialog-info);">
|
||||
<div id="text">
|
||||
<h1>%s</h1>
|
||||
<p class="message">%s</p><ul class=" suggestions"><li>%s</li><li>%s</li><li>%s</li></ul>
|
||||
<p class="message">%s</p><ul class=" suggestions"><li>%s</li><li>%s</li><li>%s</li><li>%s</li></ul>
|
||||
</div><br style="clear: both"></div>
|
||||
</body>
|
||||
</html>""".printf (
|
||||
_("Private Browsing"), _("Private Browsing"),
|
||||
_("Midori doesn't store any personal data:"),
|
||||
_("No history or web cookies are being saved."),
|
||||
_("Extensions are disabled."),
|
||||
_("HTML5 storage, local database and application caches are disabled."),
|
||||
_("Midori prevents websites from tracking the user:"),
|
||||
_("Referrer URLs are stripped down to the hostname."),
|
||||
_("DNS prefetching is disabled."),
|
||||
_("The language and timezone are not revealed to websites."),
|
||||
_("Flash and other Netscape plugins cannot be listed by websites.")
|
||||
), uri);
|
||||
}
|
||||
}
|
||||
|
||||
private class Paths : Page {
|
||||
public override string uri { get; set; default = "about:paths"; }
|
||||
public override void get_contents (Midori.View view, string uri) {
|
||||
string res_dir = Midori.Paths.get_res_filename ("about.css");
|
||||
string lib_dir = Midori.Paths.get_lib_path (PACKAGE_NAME);
|
||||
this.load_html (view, """<html>
|
||||
<body>
|
||||
<h1>%s</h1>
|
||||
<p>config: <code>%s</code></p>
|
||||
<p>res: <code>%s</code></p>
|
||||
<p>data: <code>%s/%s</code></p>
|
||||
<p>lib: <code>%s</code></p>
|
||||
<p>cache: <code>%s</code></p>
|
||||
<p>tmp: <code>%s</code></p>
|
||||
</body>
|
||||
</html>""".printf (
|
||||
uri, Midori.Paths.get_config_dir_for_reading (), res_dir,
|
||||
Midori.Paths.get_user_data_dir_for_reading (), PACKAGE_NAME,
|
||||
lib_dir, Midori.Paths.get_cache_dir_for_reading (), Midori.Paths.get_tmp_dir ()
|
||||
), uri);
|
||||
}
|
||||
}
|
||||
|
||||
private class Dial : Page {
|
||||
public override string uri { get; set; default = "about:dial"; }
|
||||
public override void get_contents (Midori.View view, string uri) {
|
||||
var browser = Midori.Browser.get_for_widget (view);
|
||||
Midori.SpeedDial dial;
|
||||
browser.get ("speed-dial", out dial);
|
||||
if (dial == null)
|
||||
return;
|
||||
try {
|
||||
this.load_html (view, dial.get_html (), uri);
|
||||
} catch (Error error) {
|
||||
this.load_html (view, error.message, uri);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class Geolocation : Page {
|
||||
public override string uri { get; set; default = "about:geolocation"; }
|
||||
public override void get_contents (Midori.View view, string uri) {
|
||||
this.load_html (view, """<html>
|
||||
<body>
|
||||
<a href="http://dev.w3.org/geo/api/spec-source.html" id="method"></a>
|
||||
<span id="locationInfo"><noscript>No Geolocation without Javascript</noscript></span>
|
||||
<script>
|
||||
function displayLocation (position) {
|
||||
var geouri = 'geo:' + position.coords.latitude + ',' + position.coords.longitude + ',' + position.coords.altitude + ',u=' + position.coords.accuracy;
|
||||
document.getElementById('locationInfo').innerHTML = '<a href="' + geouri + '">' + geouri + '</a><br><code>'
|
||||
+ ' timestamp: ' + position.timestamp
|
||||
+ ' latitude: ' + position.coords.latitude
|
||||
+ ' longitude: ' + position.coords.longitude
|
||||
+ ' altitude: ' + position.coords.altitude + '<br>'
|
||||
+ ' accuracy: ' + position.coords.accuracy
|
||||
+ ' altitudeAccuracy: ' + position.coords.altitudeAccuracy
|
||||
+ ' heading: ' + position.coords.heading
|
||||
+ ' speed: ' + position.coords.speed
|
||||
+ '</code>';
|
||||
}
|
||||
function handleError (error) {
|
||||
var errorMessage = '<b>' + ['Unknown error', 'Permission denied', 'Position failed', 'Timed out'][error.code] + '</b>';
|
||||
if (error.code == 3) document.getElementById('locationInfo').innerHTML += (' ' + errorMessage);
|
||||
else document.getElementById('locationInfo').innerHTML = errorMessage;
|
||||
}
|
||||
if (navigator.geolocation) {
|
||||
var options = { enableHighAccuracy: true, timeout: 60000, maximumAge: "Infinite" };
|
||||
if (navigator.geolocation.watchPosition) {
|
||||
document.getElementById('method').innerHTML = '<code>geolocation.watchPosition</code>:';
|
||||
navigator.geolocation.watchPosition(displayLocation, handleError, options);
|
||||
} else {
|
||||
document.getElementById('method').innerHTML = '<code>geolocation.getCurrentPosition</code>:';
|
||||
navigator.geolocation.getCurrentPosition(displayLocation, handleError);
|
||||
}
|
||||
} else
|
||||
document.getElementById('locationInfo').innerHTML = 'Geolocation unavailable';
|
||||
</script>
|
||||
</body>
|
||||
</html>""", uri);
|
||||
}
|
||||
}
|
||||
|
||||
private class Redirects : Page {
|
||||
public override string uri { get; set; }
|
||||
private string property;
|
||||
public Redirects (string alias, string property) {
|
||||
this.uri = alias;
|
||||
this.property = property;
|
||||
}
|
||||
public override void get_contents (Midori.View view, string uri) {
|
||||
string new_uri = uri;
|
||||
view.settings.get (property, out new_uri);
|
||||
if (uri == "about:search")
|
||||
new_uri = Midori.URI.for_search (new_uri, "");
|
||||
view.set_uri (new_uri);
|
||||
}
|
||||
}
|
||||
|
||||
private class Manager : Midori.Extension {
|
||||
private GLib.HashTable<string, Page>? about_pages;
|
||||
|
||||
private void register (Page page) {
|
||||
this.about_pages.insert (page.uri, page);
|
||||
}
|
||||
|
||||
private bool about_content (Midori.View view, string uri) {
|
||||
unowned Page? page = this.about_pages.get (uri);
|
||||
if (page != null) {
|
||||
page.get_contents (view, uri);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void tab_added (Midori.Browser browser, Midori.View view) {
|
||||
view.about_content.connect (this.about_content);
|
||||
}
|
||||
|
||||
private void tab_removed (Midori.Browser browser, Midori.View view) {
|
||||
view.about_content.disconnect (this.about_content);
|
||||
}
|
||||
|
||||
private void browser_added (Midori.Browser browser) {
|
||||
foreach (Midori.View tab in browser.get_tabs ()) {
|
||||
this.tab_added (browser, tab);
|
||||
}
|
||||
browser.add_tab.connect (this.tab_added);
|
||||
browser.remove_tab.connect (this.tab_removed);
|
||||
}
|
||||
|
||||
private void browser_removed (Midori.Browser browser) {
|
||||
foreach (Midori.View tab in browser.get_tabs ()) {
|
||||
this.tab_removed (browser, tab);
|
||||
}
|
||||
browser.add_tab.disconnect (this.tab_added);
|
||||
browser.remove_tab.disconnect (this.tab_removed);
|
||||
}
|
||||
|
||||
public void activated (Midori.App app) {
|
||||
this.about_pages = new GLib.HashTable<string, Page> (GLib.str_hash, GLib.str_equal);
|
||||
register (new Widgets ());
|
||||
register (new Version ("about:", about_pages));
|
||||
register (new Version ("about:version", about_pages));
|
||||
register (new Private ());
|
||||
register (new Paths ());
|
||||
register (new Geolocation ());
|
||||
register (new Redirects ("about:new", "tabhome"));
|
||||
register (new Redirects ("about:home", "homepage"));
|
||||
register (new Redirects ("about:search", "location-entry-search"));
|
||||
register (new Dial ());
|
||||
|
||||
foreach (Midori.Browser browser in app.get_browsers ()) {
|
||||
this.browser_added (browser);
|
||||
}
|
||||
app.add_browser.connect (this.browser_added);
|
||||
}
|
||||
|
||||
public void deactivated () {
|
||||
Midori.App app = this.get_app ();
|
||||
foreach (Midori.Browser browser in app.get_browsers ()) {
|
||||
this.browser_removed (browser);
|
||||
}
|
||||
app.add_browser.disconnect (this.browser_added);
|
||||
|
||||
this.about_pages = null;
|
||||
}
|
||||
|
||||
internal Manager () {
|
||||
GLib.Object (name: "About pages",
|
||||
description: "Internal about: handler",
|
||||
version: "0.1",
|
||||
authors: "André Stösel <andre@stoesel.de>");
|
||||
|
||||
this.activate.connect (this.activated);
|
||||
this.deactivate.connect (this.deactivated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Midori.Extension extension_init () {
|
||||
return new About.Manager ();
|
||||
}
|
||||
|
1936
extensions/adblock.c
1936
extensions/adblock.c
File diff suppressed because it is too large
Load diff
146
extensions/adblock/config.vala
Normal file
146
extensions/adblock/config.vala
Normal file
|
@ -0,0 +1,146 @@
|
|||
/*
|
||||
Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace Adblock {
|
||||
public class Config : GLib.Object {
|
||||
List<Subscription> subscriptions;
|
||||
public string? path { get; private set; }
|
||||
KeyFile keyfile;
|
||||
bool should_save;
|
||||
public bool enabled { get; set; }
|
||||
|
||||
public Config (string? path, string? presets) {
|
||||
should_save = false;
|
||||
subscriptions = new GLib.List<Subscription> ();
|
||||
enabled = true;
|
||||
this.path = path;
|
||||
size = 0;
|
||||
load_file (path);
|
||||
load_file (presets);
|
||||
should_save = true;
|
||||
}
|
||||
|
||||
void load_file (string? filename) {
|
||||
if (filename == null)
|
||||
return;
|
||||
|
||||
keyfile = new GLib.KeyFile ();
|
||||
try {
|
||||
keyfile.load_from_file (filename, GLib.KeyFileFlags.NONE);
|
||||
string[] filters = keyfile.get_string_list ("settings", "filters");
|
||||
foreach (unowned string filter in filters) {
|
||||
bool active = false;
|
||||
string uri = filter;
|
||||
if (filter.has_prefix ("http-/"))
|
||||
uri = "http:" + filter.substring (5);
|
||||
else if (filter.has_prefix ("file-/"))
|
||||
uri = "file:" + filter.substring (5);
|
||||
else if (filter.has_prefix ("http-:"))
|
||||
uri = "https" + filter.substring (5);
|
||||
else
|
||||
active = true;
|
||||
Subscription sub = new Subscription (uri);
|
||||
sub.active = active;
|
||||
sub.add_feature (new Updater ());
|
||||
add (sub);
|
||||
}
|
||||
enabled = !keyfile.get_boolean ("settings", "disabled");
|
||||
} catch (KeyFileError.KEY_NOT_FOUND key_error) {
|
||||
/* It's no error if a key is missing */
|
||||
} catch (KeyFileError.GROUP_NOT_FOUND group_error) {
|
||||
/* It's no error if a group is missing */
|
||||
} catch (FileError.NOENT exist_error) {
|
||||
/* It's no error if no config file exists */
|
||||
} catch (GLib.Error settings_error) {
|
||||
warning ("Error reading settings from %s: %s\n", filename, settings_error.message);
|
||||
}
|
||||
|
||||
notify["enabled"].connect (enabled_changed);
|
||||
}
|
||||
|
||||
void enabled_changed (ParamSpec pspec) {
|
||||
keyfile.set_boolean ("settings", "disabled", !enabled);
|
||||
save ();
|
||||
}
|
||||
|
||||
void active_changed (Object subscription, ParamSpec pspec) {
|
||||
update_filters ();
|
||||
}
|
||||
|
||||
void update_filters () {
|
||||
var filters = new StringBuilder ();
|
||||
foreach (unowned Subscription sub in subscriptions) {
|
||||
if (!sub.mutable)
|
||||
continue;
|
||||
if (sub.uri.has_prefix ("http:") && !sub.active)
|
||||
filters.append ("http-" + sub.uri.substring (4));
|
||||
else if (sub.uri.has_prefix ("file:") && !sub.active)
|
||||
filters.append ("file-" + sub.uri.substring (5));
|
||||
else if (sub.uri.has_prefix ("https:") && !sub.active)
|
||||
filters.append ("http-" + sub.uri.substring (5));
|
||||
else
|
||||
filters.append (sub.uri);
|
||||
filters.append_c (';');
|
||||
}
|
||||
|
||||
if (filters.str.has_suffix (";"))
|
||||
filters.truncate (filters.len - 1);
|
||||
string[] list = filters.str.split (";");
|
||||
keyfile.set_string_list ("settings", "filters", list);
|
||||
|
||||
save ();
|
||||
}
|
||||
|
||||
|
||||
public void save () {
|
||||
try {
|
||||
FileUtils.set_contents (path, keyfile.to_data ());
|
||||
} catch (Error error) {
|
||||
warning ("Failed to save settings: %s", error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/* foreach support */
|
||||
public new unowned Subscription? get (uint index) {
|
||||
return subscriptions.nth_data (index);
|
||||
}
|
||||
public uint size { get; private set; }
|
||||
|
||||
bool contains (Subscription subscription) {
|
||||
foreach (unowned Subscription sub in subscriptions)
|
||||
if (sub.uri == subscription.uri)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool add (Subscription sub) {
|
||||
if (contains (sub))
|
||||
return false;
|
||||
|
||||
sub.notify["active"].connect (active_changed);
|
||||
subscriptions.append (sub);
|
||||
size++;
|
||||
if (should_save)
|
||||
update_filters ();
|
||||
return true;
|
||||
}
|
||||
|
||||
public void remove (Subscription sub) {
|
||||
if (!contains (sub))
|
||||
return;
|
||||
|
||||
subscriptions.remove (sub);
|
||||
sub.notify["active"].disconnect (active_changed);
|
||||
update_filters ();
|
||||
size--;
|
||||
}
|
||||
}
|
||||
}
|
36
extensions/adblock/element.vala
Normal file
36
extensions/adblock/element.vala
Normal file
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace Adblock {
|
||||
public class Element : Feature {
|
||||
public HashTable<string, string> blockcssprivate;
|
||||
bool debug_element;
|
||||
|
||||
public Element () {
|
||||
base ();
|
||||
debug_element = "adblock:element" in (Environment.get_variable ("MIDORI_DEBUG") ?? "");
|
||||
}
|
||||
|
||||
public override void clear () {
|
||||
blockcssprivate = new HashTable<string, string> (str_hash, str_equal);
|
||||
}
|
||||
|
||||
public string? lookup (string domain) {
|
||||
return blockcssprivate.lookup (domain);
|
||||
}
|
||||
|
||||
public void insert (string domain, string value) {
|
||||
if (debug_element)
|
||||
stdout.printf ("Element to be blocked %s => %s\n", domain, value);
|
||||
blockcssprivate.insert (domain, value);
|
||||
}
|
||||
}
|
||||
}
|
865
extensions/adblock/extension.vala
Normal file
865
extensions/adblock/extension.vala
Normal file
|
@ -0,0 +1,865 @@
|
|||
/*
|
||||
Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de>
|
||||
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace Adblock {
|
||||
public enum Directive {
|
||||
ALLOW,
|
||||
BLOCK
|
||||
}
|
||||
|
||||
public enum State {
|
||||
ENABLED,
|
||||
DISABLED,
|
||||
BLOCKED
|
||||
}
|
||||
|
||||
public string? parse_subscription_uri (string? uri) {
|
||||
if (uri == null)
|
||||
return null;
|
||||
|
||||
if (uri.has_prefix ("http") || uri.has_prefix ("abp") || uri.has_prefix ("file"))
|
||||
{
|
||||
string sub_uri = uri;
|
||||
if (uri.has_prefix ("abp:")) {
|
||||
uri.replace ("abp://", "abp:");
|
||||
if (uri.has_prefix ("abp:subscribe?location=")) {
|
||||
/* abp://subscripe?location=http://example.com&title=foo */
|
||||
string[] parts = uri.substring (23, -1).split ("&", 2);
|
||||
sub_uri = parts[0];
|
||||
}
|
||||
}
|
||||
|
||||
string decoded_uri = Soup.URI.decode (sub_uri);
|
||||
return decoded_uri;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public class Extension : Midori.Extension {
|
||||
internal Config config;
|
||||
internal Subscription custom;
|
||||
internal StringBuilder hider_selectors;
|
||||
internal StatusIcon status_icon;
|
||||
internal SubscriptionManager manager;
|
||||
internal bool debug_element;
|
||||
#if !USE_CSS_SELECTOR_FOR_BLOCKED_RESOURCES
|
||||
internal string? js_hider_function_body;
|
||||
#endif
|
||||
|
||||
#if HAVE_WEBKIT2
|
||||
#if !HAVE_WEBKIT2_3_91
|
||||
public Extension.WebExtension (WebKit.WebExtension web_extension) {
|
||||
init ();
|
||||
web_extension.page_created.connect (page_created);
|
||||
}
|
||||
|
||||
void page_created (WebKit.WebPage web_page) {
|
||||
web_page.send_request.connect (send_request);
|
||||
}
|
||||
|
||||
bool send_request (WebKit.WebPage web_page, WebKit.URIRequest request, WebKit.URIResponse? redirected_response) {
|
||||
return request_handled (request.uri, web_page.uri);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
public Extension () {
|
||||
GLib.Object (name: _("Advertisement blocker"),
|
||||
description: _("Block advertisements according to a filter list"),
|
||||
version: "2.0",
|
||||
authors: "Christian Dywan <christian@twotoasts.de>");
|
||||
activate.connect (extension_activated);
|
||||
deactivate.connect (extension_deactivated);
|
||||
open_preferences.connect (extension_preferences);
|
||||
}
|
||||
|
||||
void extension_preferences () {
|
||||
manager.add_subscription (null);
|
||||
}
|
||||
|
||||
void extension_activated (Midori.App app) {
|
||||
#if HAVE_WEBKIT2
|
||||
string cache_dir = Environment.get_user_cache_dir ();
|
||||
string wk2path = Path.build_path (Path.DIR_SEPARATOR_S, cache_dir, "wk2ext");
|
||||
Midori.Paths.mkdir_with_parents (wk2path);
|
||||
string filename = "libadblock." + GLib.Module.SUFFIX;
|
||||
var wk2link = File.new_for_path (wk2path).get_child (filename);
|
||||
var library = File.new_for_path (Midori.Paths.get_lib_path (PACKAGE_NAME)).get_child (filename);
|
||||
try {
|
||||
wk2link.make_symbolic_link (library.get_path ());
|
||||
} catch (IOError.EXISTS exist_error) {
|
||||
/* It's no error if the file already exists. */
|
||||
} catch (Error error) {
|
||||
critical ("Failed to create WebKit2 link: %s", error.message);
|
||||
}
|
||||
#endif
|
||||
init ();
|
||||
foreach (var browser in app.get_browsers ())
|
||||
browser_added (browser);
|
||||
app.add_browser.connect (browser_added);
|
||||
app.remove_browser.connect (browser_removed);
|
||||
}
|
||||
|
||||
void extension_deactivated () {
|
||||
var app = get_app ();
|
||||
foreach (var browser in app.get_browsers ())
|
||||
browser_removed (browser);
|
||||
app.add_browser.disconnect (browser_added);
|
||||
app.remove_browser.disconnect (browser_removed);
|
||||
}
|
||||
|
||||
void browser_added (Midori.Browser browser) {
|
||||
foreach (var tab in browser.get_tabs ())
|
||||
tab_added (tab);
|
||||
browser.add_tab.connect (tab_added);
|
||||
browser.remove_tab.connect (tab_removed);
|
||||
|
||||
browser.add_action (status_icon);
|
||||
}
|
||||
|
||||
void browser_removed (Midori.Browser browser) {
|
||||
foreach (var tab in browser.get_tabs ())
|
||||
tab_removed (tab);
|
||||
browser.add_tab.disconnect (tab_added);
|
||||
browser.remove_tab.disconnect (tab_removed);
|
||||
browser.remove_action (status_icon);
|
||||
}
|
||||
|
||||
void tab_added (Midori.View view) {
|
||||
view.navigation_requested.connect (navigation_requested);
|
||||
#if !HAVE_WEBKIT2
|
||||
view.web_view.resource_request_starting.connect (resource_requested);
|
||||
#endif
|
||||
view.notify["load-status"].connect (load_status_changed);
|
||||
view.context_menu.connect (context_menu);
|
||||
}
|
||||
|
||||
void tab_removed (Midori.View view) {
|
||||
#if !HAVE_WEBKIT2
|
||||
view.web_view.resource_request_starting.disconnect (resource_requested);
|
||||
#endif
|
||||
view.navigation_requested.disconnect (navigation_requested);
|
||||
view.notify["load-status"].disconnect (load_status_changed);
|
||||
view.context_menu.disconnect (context_menu);
|
||||
}
|
||||
|
||||
void load_status_changed (Object object, ParamSpec pspec) {
|
||||
var view = object as Midori.View;
|
||||
if (config.enabled) {
|
||||
if (view.load_status == Midori.LoadStatus.FINISHED)
|
||||
inject_css (view, view.uri);
|
||||
}
|
||||
}
|
||||
|
||||
void context_menu (WebKit.HitTestResult hit_test_result, Midori.ContextAction menu) {
|
||||
string label, uri;
|
||||
if ((hit_test_result.context & WebKit.HitTestResultContext.IMAGE) != 0) {
|
||||
label = _("Bl_ock image");
|
||||
uri = hit_test_result.image_uri;
|
||||
} else if ((hit_test_result.context & WebKit.HitTestResultContext.LINK) != 0) {
|
||||
label = _("Bl_ock link");
|
||||
uri = hit_test_result.link_uri;
|
||||
} else
|
||||
return;
|
||||
var action = new Gtk.Action ("BlockElement", label, null, null);
|
||||
action.activate.connect ((action) => {
|
||||
CustomRulesEditor custom_rules_editor = new CustomRulesEditor (custom);
|
||||
custom_rules_editor.set_uri (uri);
|
||||
custom_rules_editor.show();
|
||||
});
|
||||
menu.add (action);
|
||||
}
|
||||
|
||||
#if !HAVE_WEBKIT2
|
||||
void resource_requested (WebKit.WebView web_view, WebKit.WebFrame frame,
|
||||
WebKit.WebResource resource, WebKit.NetworkRequest request, WebKit.NetworkResponse? response) {
|
||||
|
||||
if (request_handled (request.uri, web_view.uri)) {
|
||||
request.set_uri ("about:blank");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
bool navigation_requested (Midori.Tab tab, string uri) {
|
||||
if (uri.has_prefix ("abp:")) {
|
||||
string parsed_uri = parse_subscription_uri (uri);
|
||||
manager.add_subscription (parsed_uri);
|
||||
return true;
|
||||
}
|
||||
status_icon.set_state (config.enabled ? State.ENABLED : State.DISABLED);
|
||||
return false;
|
||||
}
|
||||
|
||||
#if USE_CSS_SELECTOR_FOR_BLOCKED_RESOURCES
|
||||
string? get_hider_css_for_blocked_resources () {
|
||||
if (hider_selectors.str == "")
|
||||
return null;
|
||||
|
||||
/* Hide elements that were blocked, otherwise we will get "broken image" icon */
|
||||
var code = new StringBuilder (hider_selectors.str);
|
||||
string hider_css;
|
||||
if (debug_element)
|
||||
hider_css = " { background-color: red; border: 4px solid green; }";
|
||||
else
|
||||
hider_css = " { visiblility: hidden; width: 0; height: 0; }";
|
||||
|
||||
code.truncate (code.len -3);
|
||||
code.append (hider_css);
|
||||
if (debug_element)
|
||||
stdout.printf ("hider css: %s\n", code.str);
|
||||
return code.str;
|
||||
}
|
||||
#else
|
||||
string? fetch_js_hider_function_body () {
|
||||
string filename = Midori.Paths.get_res_filename ("adblock/element_hider.js");
|
||||
File js_file = GLib.File.new_for_path (filename);
|
||||
try {
|
||||
uint8[] function_body;
|
||||
js_file.load_contents (null, out function_body, null);
|
||||
return (string)function_body;
|
||||
}
|
||||
catch (Error error) {
|
||||
warning ("Error while loading adblock hider js: %s\n", error.message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
string? get_hider_js_for_blocked_resorces () {
|
||||
if (hider_selectors.str == "")
|
||||
return null;
|
||||
|
||||
if (js_hider_function_body == null || js_hider_function_body == "")
|
||||
return null;
|
||||
|
||||
var js = new StringBuilder ("(function() {");
|
||||
js.append (js_hider_function_body);
|
||||
js.append ("var uris=new Array ();");
|
||||
js.append (hider_selectors.str);
|
||||
js.append (" hideElementBySrc (uris);})();");
|
||||
|
||||
return js.str;
|
||||
}
|
||||
#endif
|
||||
|
||||
string[]? get_domains_for_uri (string uri) {
|
||||
if (uri == null)
|
||||
return null;
|
||||
string[]? domains = null;
|
||||
string domain = Midori.URI.parse_hostname (uri, null);
|
||||
string[] subdomains = domain.split (".");
|
||||
if (subdomains == null)
|
||||
return null;
|
||||
int cnt = subdomains.length - 1;
|
||||
var subdomain = new StringBuilder (subdomains[cnt]);
|
||||
subdomain.prepend_c ('.');
|
||||
cnt--;
|
||||
while (cnt >= 0) {
|
||||
subdomain.prepend (subdomains[cnt]);
|
||||
domains += subdomain.str;
|
||||
subdomain.prepend_c ('.');
|
||||
cnt--;
|
||||
}
|
||||
return domains;
|
||||
}
|
||||
|
||||
string? get_hider_css_rules_for_uri (string page_uri) {
|
||||
if (page_uri == null)
|
||||
return null;
|
||||
string[]? domains = get_domains_for_uri (page_uri);
|
||||
if (domains == null)
|
||||
return null;
|
||||
var code = new StringBuilder ();
|
||||
int blockscnt = 0;
|
||||
string? style = null;
|
||||
foreach (unowned Subscription sub in config) {
|
||||
foreach (unowned Feature feature in sub) {
|
||||
var element = feature as Element;
|
||||
if (element != null) {
|
||||
foreach (unowned string subdomain in domains) {
|
||||
style = element.lookup (subdomain);
|
||||
if (style != null) {
|
||||
code.append (style);
|
||||
code.append_c (',');
|
||||
blockscnt++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (blockscnt == 0)
|
||||
return null;
|
||||
code.truncate (code.len - 1);
|
||||
|
||||
string hider_css;
|
||||
if (debug_element)
|
||||
hider_css = " { background-color: red !important; border: 4px solid green !important; }";
|
||||
else
|
||||
hider_css = " { display: none !important }";
|
||||
|
||||
code.append (hider_css);
|
||||
if (debug_element)
|
||||
stdout.printf ("css: %s\n", code.str);
|
||||
|
||||
return code.str;
|
||||
}
|
||||
|
||||
void inject_css (Midori.View view, string page_uri) {
|
||||
/* Don't block ads on internal pages */
|
||||
if (!Midori.URI.is_http (page_uri))
|
||||
return;
|
||||
|
||||
if ("adblock:element" in (Environment.get_variable ("MIDORI_DEBUG") ?? ""))
|
||||
debug_element = true;
|
||||
else
|
||||
debug_element = status_icon.debug_element_toggled;
|
||||
|
||||
#if USE_CSS_SELECTOR_FOR_BLOCKED_RESOURCES
|
||||
string? blocked_css = get_hider_css_for_blocked_resources ();
|
||||
if (blocked_css != null)
|
||||
view.inject_stylesheet (blocked_css);
|
||||
#else
|
||||
string? blocked_js = get_hider_js_for_blocked_resorces ();
|
||||
if (blocked_js != null)
|
||||
view.execute_script (blocked_js, null);
|
||||
#endif
|
||||
string? style = get_hider_css_rules_for_uri (page_uri);
|
||||
if (style != null)
|
||||
view.inject_stylesheet (style);
|
||||
}
|
||||
|
||||
internal void init () {
|
||||
hider_selectors = new StringBuilder ();
|
||||
load_config ();
|
||||
manager = new SubscriptionManager (config);
|
||||
status_icon = new StatusIcon (config, manager);
|
||||
foreach (unowned Subscription sub in config) {
|
||||
try {
|
||||
sub.parse ();
|
||||
} catch (GLib.Error error) {
|
||||
warning ("Error parsing %s: %s", sub.uri, error.message);
|
||||
}
|
||||
}
|
||||
config.notify["size"].connect (subscriptions_added_removed);
|
||||
manager.description_label.activate_link.connect (open_link);
|
||||
#if !USE_CSS_SELECTOR_FOR_BLOCKED_RESOURCES
|
||||
js_hider_function_body = fetch_js_hider_function_body ();
|
||||
#endif
|
||||
}
|
||||
|
||||
bool open_link (string uri) {
|
||||
var browser = get_app ().browser;
|
||||
var view = browser.add_uri (uri);
|
||||
browser.tab = view;
|
||||
return true;
|
||||
}
|
||||
|
||||
void subscriptions_added_removed (ParamSpec pspec) {
|
||||
hider_selectors = new StringBuilder ();
|
||||
}
|
||||
|
||||
void load_config () {
|
||||
#if HAVE_WEBKIT2
|
||||
string config_dir = Path.build_filename (GLib.Environment.get_user_config_dir (), PACKAGE_NAME, "extensions", "libadblock." + GLib.Module.SUFFIX);
|
||||
Midori.Paths.mkdir_with_parents (config_dir);
|
||||
#else
|
||||
string config_dir = Midori.Paths.get_extension_config_dir ("adblock");
|
||||
#endif
|
||||
string presets = Midori.Paths.get_extension_preset_filename ("adblock", "config");
|
||||
string filename = Path.build_filename (config_dir, "config");
|
||||
config = new Config (filename, presets);
|
||||
string custom_list = GLib.Path.build_filename (config_dir, "custom.list");
|
||||
try {
|
||||
custom = new Subscription (Filename.to_uri (custom_list, null));
|
||||
custom.mutable = false;
|
||||
custom.title = _("Custom");
|
||||
config.add (custom);
|
||||
} catch (Error error) {
|
||||
custom = null;
|
||||
warning ("Failed to add custom list %s: %s", custom_list, error.message);
|
||||
}
|
||||
}
|
||||
|
||||
public Adblock.Directive get_directive_for_uri (string request_uri, string page_uri) {
|
||||
if (!config.enabled)
|
||||
return Directive.ALLOW;
|
||||
|
||||
/* Always allow the main page */
|
||||
if (request_uri == page_uri)
|
||||
return Directive.ALLOW;
|
||||
|
||||
/* Skip adblock on internal pages */
|
||||
if (Midori.URI.is_blank (page_uri))
|
||||
return Directive.ALLOW;
|
||||
|
||||
/* Skip adblock on favicons and non http schemes */
|
||||
if (!Midori.URI.is_http (request_uri) || request_uri.has_suffix ("favicon.ico"))
|
||||
return Directive.ALLOW;
|
||||
|
||||
Directive? directive = null;
|
||||
foreach (unowned Subscription sub in config) {
|
||||
directive = sub.get_directive (request_uri, page_uri);
|
||||
if (directive != null)
|
||||
break;
|
||||
}
|
||||
|
||||
if (directive == null)
|
||||
directive = Directive.ALLOW;
|
||||
else if (directive == Directive.BLOCK) {
|
||||
status_icon.set_state (State.BLOCKED);
|
||||
#if USE_CSS_SELECTOR_FOR_BLOCKED_RESOURCES
|
||||
hider_selectors.append ("img[src*=\"%s\"] , iframe[src*=\"%s\"] , ".printf (request_uri, request_uri));
|
||||
#else
|
||||
hider_selectors.append (" uris.push ('%s');\n".printf (request_uri));
|
||||
#endif
|
||||
}
|
||||
return directive;
|
||||
}
|
||||
|
||||
internal bool request_handled (string request_uri, string page_uri) {
|
||||
return get_directive_for_uri (request_uri, page_uri) == Directive.BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
static void debug (string format, ...) {
|
||||
bool debug_match = "adblock:match" in (Environment.get_variable ("MIDORI_DEBUG") ?? "");
|
||||
if (!debug_match)
|
||||
return;
|
||||
|
||||
var args = va_list ();
|
||||
stdout.vprintf (format + "\n", args);
|
||||
}
|
||||
|
||||
internal static string? fixup_regex (string prefix, string? src) {
|
||||
if (src == null)
|
||||
return null;
|
||||
|
||||
var fixed = new StringBuilder ();
|
||||
fixed.append(prefix);
|
||||
|
||||
uint i = 0;
|
||||
if (src[0] == '*')
|
||||
i++;
|
||||
uint l = src.length;
|
||||
while (i < l) {
|
||||
char c = src[i];
|
||||
switch (c) {
|
||||
case '*':
|
||||
fixed.append (".*"); break;
|
||||
case '|':
|
||||
case '^':
|
||||
case '+':
|
||||
break;
|
||||
case '?':
|
||||
case '[':
|
||||
case ']':
|
||||
case '.':
|
||||
case '(':
|
||||
case ')':
|
||||
fixed.append_printf ("\\%c", c); break;
|
||||
default:
|
||||
fixed.append_c (c); break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
return fixed.str;
|
||||
}
|
||||
}
|
||||
|
||||
#if HAVE_WEBKIT2
|
||||
#if !HAVE_WEBKIT2_3_91
|
||||
Adblock.Extension? filter;
|
||||
public static void webkit_web_extension_initialize (WebKit.WebExtension web_extension) {
|
||||
filter = new Adblock.Extension.WebExtension (web_extension);
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
||||
public Midori.Extension extension_init () {
|
||||
return new Adblock.Extension ();
|
||||
}
|
||||
|
||||
static string? tmp_folder = null;
|
||||
string get_test_file (string contents) {
|
||||
if (tmp_folder == null)
|
||||
tmp_folder = Midori.Paths.make_tmp_dir ("adblockXXXXXX");
|
||||
string checksum = Checksum.compute_for_string (ChecksumType.MD5, contents);
|
||||
string file = Path.build_path (Path.DIR_SEPARATOR_S, tmp_folder, checksum);
|
||||
try {
|
||||
FileUtils.set_contents (file, contents, -1);
|
||||
} catch (Error file_error) {
|
||||
GLib.error (file_error.message);
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
struct TestCaseConfig {
|
||||
public string content;
|
||||
public uint size;
|
||||
public bool enabled;
|
||||
}
|
||||
|
||||
const TestCaseConfig[] configs = {
|
||||
{ "", 0, true },
|
||||
{ "[settings]", 0, true },
|
||||
{ "[settings]\nfilters=foo;", 1, true },
|
||||
{ "[settings]\nfilters=foo;\ndisabled=true", 1, false }
|
||||
};
|
||||
|
||||
void test_adblock_config () {
|
||||
assert (new Adblock.Config (null, null).size == 0);
|
||||
|
||||
foreach (var conf in configs) {
|
||||
var config = new Adblock.Config (get_test_file (conf.content), null);
|
||||
if (config.size != conf.size)
|
||||
error ("Wrong size %s rather than %s:\n%s",
|
||||
config.size.to_string (), conf.size.to_string (), conf.content);
|
||||
if (config.enabled != conf.enabled)
|
||||
error ("Wrongly got enabled=%s rather than %s:\n%s",
|
||||
config.enabled.to_string (), conf.enabled.to_string (), conf.content);
|
||||
}
|
||||
}
|
||||
|
||||
struct TestCaseSub {
|
||||
public string uri;
|
||||
public bool active;
|
||||
}
|
||||
|
||||
const TestCaseSub[] subs = {
|
||||
{ "http://foo.com", true },
|
||||
{ "http://bar.com", false },
|
||||
{ "https://spam.com", true },
|
||||
{ "https://eggs.com", false },
|
||||
{ "file:///bla", true },
|
||||
{ "file:///blub", false }
|
||||
};
|
||||
|
||||
void test_adblock_subs () {
|
||||
var config = new Adblock.Config (get_test_file ("""
|
||||
[settings]
|
||||
filters=http://foo.com;http-//bar.com;https://spam.com;http-://eggs.com;file:///bla;file-///blub;http://foo.com;
|
||||
"""), null);
|
||||
|
||||
assert (config.enabled);
|
||||
foreach (var sub in subs) {
|
||||
bool found = false;
|
||||
foreach (unowned Adblock.Subscription subscription in config) {
|
||||
if (subscription.uri == sub.uri) {
|
||||
assert (subscription.active == sub.active);
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
error ("%s not found", sub.uri);
|
||||
}
|
||||
|
||||
/* 6 unique URLs, 1 duplicate */
|
||||
assert (config.size == 6);
|
||||
/* Duplicates aren't added again either */
|
||||
assert (!config.add (new Adblock.Subscription ("https://spam.com")));
|
||||
|
||||
/* Saving the config and loading it should give back identical results */
|
||||
config.save ();
|
||||
var copy = new Adblock.Config (config.path, null);
|
||||
assert (copy.size == config.size);
|
||||
assert (copy.enabled == config.enabled);
|
||||
for (int i = 0; i < config.size; i++) {
|
||||
assert (copy[i].uri == config[i].uri);
|
||||
assert (copy[i].active == config[i].active);
|
||||
}
|
||||
/* Enabled status should be saved and loaded */
|
||||
config.enabled = false;
|
||||
copy = new Adblock.Config (config.path, null);
|
||||
assert (copy.enabled == config.enabled);
|
||||
/* Flipping individual active values should be retained after saving */
|
||||
foreach (unowned Adblock.Subscription sub in config)
|
||||
sub.active = !sub.active;
|
||||
copy = new Adblock.Config (config.path, null);
|
||||
for (uint i = 0; i < config.size; i++) {
|
||||
if (config[i].active != copy[i].active) {
|
||||
string contents;
|
||||
try {
|
||||
FileUtils.get_contents (config.path, out contents, null);
|
||||
} catch (Error file_error) {
|
||||
error (file_error.message);
|
||||
}
|
||||
error ("%s is %s but should be %s:\n%s",
|
||||
copy[i].uri, copy[i].active ? "active" : "disabled", config[i].active ? "active" : "disabled", contents);
|
||||
}
|
||||
}
|
||||
|
||||
/* Adding and removing works, changes size */
|
||||
var s = new Adblock.Subscription ("http://en.de");
|
||||
assert (config.add (s));
|
||||
assert (config.size == 7);
|
||||
config.remove (s);
|
||||
assert (config.size == 6);
|
||||
/* If it was removed before we should be able to add it again */
|
||||
assert (config.add (s));
|
||||
assert (config.size == 7);
|
||||
}
|
||||
|
||||
void test_adblock_init () {
|
||||
/* No config */
|
||||
var extension = new Adblock.Extension ();
|
||||
extension.init ();
|
||||
assert (extension.config.enabled);
|
||||
/* Defaults plus custom */
|
||||
if (extension.config.size != 3)
|
||||
error ("Expected 3 initial subs, got %s".printf (
|
||||
extension.config.size.to_string ()));
|
||||
assert (extension.status_icon.state == Adblock.State.ENABLED);
|
||||
|
||||
/* Add new subscription */
|
||||
string path = Midori.Paths.get_res_filename ("adblock.list");
|
||||
string uri;
|
||||
try {
|
||||
uri = Filename.to_uri (path, null);
|
||||
} catch (Error error) {
|
||||
GLib.error (error.message);
|
||||
}
|
||||
var sub = new Adblock.Subscription (uri);
|
||||
extension.config.add (sub);
|
||||
assert (extension.status_icon.state == Adblock.State.ENABLED);
|
||||
assert (extension.config.size == 4);
|
||||
try {
|
||||
sub.parse ();
|
||||
} catch (GLib.Error error) {
|
||||
GLib.error (error.message);
|
||||
}
|
||||
/* The page itself never hits */
|
||||
assert (!extension.request_handled ("https://ads.bogus.name/blub", "https://ads.bogus.name/blub"));
|
||||
/* Favicons don't either */
|
||||
assert (!extension.request_handled ("https://foo.com", "https://ads.bogus.name/blub/favicon.ico"));
|
||||
assert (extension.status_icon.state == Adblock.State.ENABLED);
|
||||
/* Some sanity checks to be sure there's no earlier problem */
|
||||
assert (sub.title == "Exercise");
|
||||
assert (sub.get_directive ("https://ads.bogus.name/blub", "") == Adblock.Directive.BLOCK);
|
||||
/* A rule hit should add to the cache */
|
||||
assert (extension.request_handled ("https://ads.bogus.name/blub", "https://foo.com"));
|
||||
assert (extension.status_icon.state == Adblock.State.BLOCKED);
|
||||
assert (extension.hider_selectors.str != "");
|
||||
/* Disabled means no request should be handled */
|
||||
extension.config.enabled = false;
|
||||
assert (!extension.request_handled ("https://ads.bogus.name/blub", "https://foo.com"));
|
||||
// FIXME: assert (extension.status_icon.state == Adblock.State.DISABLED);
|
||||
/* Removing a subscription should clear the cached CSS */
|
||||
extension.config.remove (sub);
|
||||
assert (extension.hider_selectors.str == "");
|
||||
assert (extension.config.size == 3);
|
||||
/* Now let's add a custom rule */
|
||||
extension.config.enabled = true;
|
||||
extension.custom.add_rule ("/adpage.");
|
||||
assert (extension.custom.get_directive ("http://www.engadget.com/_uac/adpage.html", "http://foo.com") == Adblock.Directive.BLOCK);
|
||||
assert (extension.request_handled ("http://www.engadget.com/_uac/adpage.html", "http://foo.com"));
|
||||
assert (extension.status_icon.state == Adblock.State.BLOCKED);
|
||||
/* Second attempt, from cache, same result */
|
||||
assert (extension.custom.get_directive ("http://www.engadget.com/_uac/adpage.html", "http://foo.com") == Adblock.Directive.BLOCK);
|
||||
assert (extension.request_handled ("http://www.engadget.com/_uac/adpage.html", "http://foo.com"));
|
||||
/* Another custom rule */
|
||||
extension.custom.add_rule ("/images/*.png");
|
||||
assert (extension.custom.get_directive ("http://alpha.beta.com/images/yota.png", "https://foo.com") == Adblock.Directive.BLOCK);
|
||||
assert (extension.request_handled ("http://alpha.beta.com/images/yota.png", "https://foo.com"));
|
||||
/* Second attempt, from cache, same result */
|
||||
assert (extension.request_handled ("http://alpha.beta.com/images/yota.png", "https://foo.com"));
|
||||
/* Similar uri but .jpg should pass */
|
||||
assert (!extension.request_handled ("http://alpha.beta.com/images/yota.jpg", "https://foo.com"));
|
||||
assert (extension.custom.get_directive ("http://alpha.beta.com/images/yota.jpg", "https://foo.com") != Adblock.Directive.BLOCK);
|
||||
/* Add whitelist rule */
|
||||
extension.custom.add_rule ("@@http://alpha.beta.com/images/drop*bear.png");
|
||||
assert (!extension.request_handled ("http://alpha.beta.com/images/drop-bear.png", "https://foo.com"));
|
||||
assert (!extension.request_handled ("http://alpha.beta.com/images/dropzone_bear.png", "https://foo.com"));
|
||||
assert (extension.custom.get_directive ("http://alpha.beta.com/images/drop-bear.png", "https://foo.com") != Adblock.Directive.BLOCK);
|
||||
/* Doesn't match whitelist, matches *.png rule, should be blocked */
|
||||
assert (extension.request_handled ("http://alpha.beta.com/images/bear.png", "https://foo.com"));
|
||||
assert (extension.custom.get_directive ("http://alpha.beta.com/images/bear.png", "https://foo.com") == Adblock.Directive.BLOCK);
|
||||
}
|
||||
|
||||
struct TestCaseLine {
|
||||
public string line;
|
||||
public string fixed;
|
||||
}
|
||||
|
||||
const TestCaseLine[] lines = {
|
||||
{ null, null },
|
||||
{ "!", "!" },
|
||||
{ "@@", "@@" },
|
||||
{ "##", "##" },
|
||||
{ "[", "\\[" },
|
||||
{ "+advert/", "advert/" },
|
||||
{ "*foo", "foo" },
|
||||
{ "f*oo", "f.*oo" },
|
||||
{ "?foo", "\\?foo" },
|
||||
{ "foo?", "foo\\?" },
|
||||
{ ".*foo/bar", "..*foo/bar" },
|
||||
{ ".*.*.*.info/popup", "\\..*\\..*\\..*\\.info/popup" },
|
||||
{ "http://bla.blub/*", "http://bla.blub/.*" },
|
||||
{ "bag?r[]=*cpa", "bag\\?r\\[\\]=.*cpa" },
|
||||
{ "(facebookLike,", "\\(facebookLike," }
|
||||
};
|
||||
|
||||
void test_adblock_fixup_regexp () {
|
||||
foreach (var line in lines) {
|
||||
Katze.assert_str_equal (line.line, Adblock.fixup_regex ("", line.line), line.fixed);
|
||||
}
|
||||
}
|
||||
|
||||
struct TestCasePattern {
|
||||
public string uri;
|
||||
public Adblock.Directive directive;
|
||||
}
|
||||
|
||||
const TestCasePattern[] patterns = {
|
||||
{ "http://www.engadget.com/_uac/adpage.html", Adblock.Directive.BLOCK },
|
||||
{ "http://test.dom/test?var=1", Adblock.Directive.BLOCK },
|
||||
{ "http://ads.foo.bar/teddy", Adblock.Directive.BLOCK },
|
||||
{ "http://ads.fuu.bar/teddy", Adblock.Directive.ALLOW },
|
||||
{ "https://ads.bogus.name/blub", Adblock.Directive.BLOCK },
|
||||
// FIXME { "http://ads.bla.blub/kitty", Adblock.Directive.BLOCK },
|
||||
// FIXME { "http://ads.blub.boing/soda", Adblock.Directive.BLOCK },
|
||||
{ "http://ads.foo.boing/beer", Adblock.Directive.ALLOW },
|
||||
{ "https://testsub.engine.adct.ru/test?id=1", Adblock.Directive.BLOCK },
|
||||
{ "http://test.ltd/addyn/test/test?var=adtech;&var2=1", Adblock.Directive.BLOCK },
|
||||
{ "http://add.doubleclick.net/pfadx/aaaa.mtvi", Adblock.Directive.BLOCK },
|
||||
{ "http://add.doubleclick.net/pfadx/aaaa.mtv", Adblock.Directive.ALLOW },
|
||||
{ "http://objects.tremormedia.com/embed/xml/list.xml?r=", Adblock.Directive.BLOCK },
|
||||
{ "http://qq.videostrip.c/sub/admatcherclient.php", Adblock.Directive.ALLOW },
|
||||
{ "http://qq.videostrip.com/sub/admatcherclient.php", Adblock.Directive.BLOCK },
|
||||
{ "http://qq.videostrip.com/sub/admatcherclient.php", Adblock.Directive.BLOCK },
|
||||
{ "http://br.gcl.ru/cgi-bin/br/test", Adblock.Directive.BLOCK },
|
||||
{ "https://bugs.webkit.org/buglist.cgi?query_format=advanced&short_desc_type=allwordssubstr&short_desc=&long_desc_type=substring&long_desc=&bug_file_loc_type=allwordssubstr&bug_file_loc=&keywords_type=allwords&keywords=&bug_status=UNCONFIRMED&bug_status=NEW&bug_status=ASSIGNED&bug_status=REOPENED&emailassigned_to1=1&emailtype1=substring&email1=&emailassigned_to2=1&emailreporter2=1&emailcc2=1&emailtype2=substring&email2=&bugidtype=include&bug_id=&votes=&chfieldfrom=&chfieldto=Now&chfieldvalue=&query_based_on=gtkport&field0-0-0=keywords&type0-0-0=anywordssubstr&value0-0-0=Gtk%20Cairo%20soup&field0-0-1=short_desc&type0-0-1=anywordssubstr&value0-0-1=Gtk%20Cairo%20soup%20autoconf%20automake%20autotool&field0-0-2=component&type0-0-2=equals&value0-0-2=WebKit%20Gtk", Adblock.Directive.ALLOW },
|
||||
{ "http://www.engadget.com/2009/09/24/google-hits-android-rom-modder-with-a-cease-and-desist-letter/", Adblock.Directive.ALLOW },
|
||||
{ "http://karibik-invest.com/es/bienes_raices/search.php?sqT=19&sqN=&sqMp=&sqL=0&qR=1&sqMb=&searchMode=1&action=B%FAsqueda", Adblock.Directive.ALLOW },
|
||||
{ "http://google.com", Adblock.Directive.ALLOW }
|
||||
};
|
||||
|
||||
string pretty_directive (Adblock.Directive? directive) {
|
||||
if (directive == null)
|
||||
return "none";
|
||||
return directive.to_string ();
|
||||
}
|
||||
|
||||
void test_adblock_pattern () {
|
||||
string path = Midori.Paths.get_res_filename ("adblock.list");
|
||||
string uri;
|
||||
try {
|
||||
uri = Filename.to_uri (path, null);
|
||||
} catch (Error error) {
|
||||
GLib.error (error.message);
|
||||
}
|
||||
var sub = new Adblock.Subscription (uri);
|
||||
try {
|
||||
sub.parse ();
|
||||
} catch (Error error) {
|
||||
GLib.error (error.message);
|
||||
}
|
||||
foreach (var pattern in patterns) {
|
||||
Adblock.Directive? directive = sub.get_directive (pattern.uri, "");
|
||||
if (directive == null)
|
||||
directive = Adblock.Directive.ALLOW;
|
||||
if (directive != pattern.directive) {
|
||||
error ("%s expected for %s but got %s",
|
||||
pretty_directive (pattern.directive), pattern.uri, pretty_directive (directive));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
string pretty_date (DateTime? date) {
|
||||
if (date == null)
|
||||
return "N/A";
|
||||
return date.to_string ();
|
||||
}
|
||||
|
||||
struct TestUpdateExample {
|
||||
public string content;
|
||||
public bool result;
|
||||
public bool valid;
|
||||
}
|
||||
|
||||
const TestUpdateExample[] examples = {
|
||||
{ "[Adblock Plus 1.1]\n! Last modified: 05 Sep 2010 11:00 UTC\n! This list expires after 48 hours\n", true, true },
|
||||
{ "[Adblock Plus 1.1]\n! Last modified: 05.09.2010 11:00 UTC\n! Expires: 2 days (update frequency)\n", true, true },
|
||||
{ "[Adblock Plus 1.1]\n! Updated: 05 Nov 2024 11:00 UTC\n! Expires: 5 days (update frequency)\n", false, true },
|
||||
{ "[Adblock]\n! dutchblock v3\n! This list expires after 14 days\n|http://b*.mookie1.com/\n", false, true },
|
||||
{ "[Adblock Plus 2.0]\n! Last modification time (GMT): 2012.11.05 13:33\n! Expires: 5 days (update frequency)\n", true, true },
|
||||
{ "[Adblock Plus 2.0]\n! Last modification time (GMT): 2012.11.05 13:33\n", true, true },
|
||||
{ "[Adblock]\n ! dummy, i dont have any dates\n", false, true },
|
||||
/* non-standard update time metadata as found in http://abp.mozilla-hispano.org/nauscopio/filtros.txt */
|
||||
{ "[Adblock Plus 2.0]\n ! Last modified: Oct 26, 2013 18:00 UTC\n ! This list expires after 5 days\n! Last modified by maty: 12Oct2013\n! \n", false, true },
|
||||
{ "\n", false, false }
|
||||
};
|
||||
|
||||
void test_subscription_update () {
|
||||
string uri;
|
||||
FileIOStream iostream;
|
||||
File file;
|
||||
try {
|
||||
file = File.new_tmp ("midori_adblock_update_test_XXXXXX", out iostream);
|
||||
uri = file.get_uri ();
|
||||
} catch (Error error) {
|
||||
GLib.error (error.message);
|
||||
}
|
||||
var sub = new Adblock.Subscription (uri);
|
||||
var updater = new Adblock.Updater ();
|
||||
sub.add_feature (updater);
|
||||
|
||||
foreach (var example in examples) {
|
||||
try {
|
||||
file.replace_contents (example.content.data, null, false, FileCreateFlags.NONE, null);
|
||||
sub.clear ();
|
||||
sub.parse ();
|
||||
} catch (Error error) {
|
||||
GLib.error (error.message);
|
||||
}
|
||||
if (example.valid != sub.valid)
|
||||
error ("Subscription expected to be %svalid but %svalid:\n%s",
|
||||
example.valid ? "" : "in", sub.valid ? "" : "in", example.content);
|
||||
if (example.result != updater.needs_update)
|
||||
error ("Update%s expected for:\n%s\nLast Updated: %s\nExpires: %s",
|
||||
example.result ? "" : " not", example.content,
|
||||
pretty_date (updater.last_updated), pretty_date (updater.expires));
|
||||
}
|
||||
}
|
||||
|
||||
struct TestSubUri {
|
||||
public string? src_uri;
|
||||
public string? dst_uri;
|
||||
}
|
||||
|
||||
const TestSubUri[] suburis =
|
||||
{
|
||||
{ null, null },
|
||||
{ "not-a-link", null },
|
||||
{ "http://some.uri", "http://some.uri" },
|
||||
{ "abp:subscribe?location=https%3A%2F%2Feasylist-downloads.adblockplus.org%2Fabpindo%2Beasylist.txt&title=ABPindo%2BEasyList", "https://easylist-downloads.adblockplus.org/abpindo+easylist.txt" }
|
||||
};
|
||||
|
||||
void test_subscription_uri_parsing () {
|
||||
string? parsed_uri;
|
||||
foreach (var example in suburis) {
|
||||
parsed_uri = Adblock.parse_subscription_uri (example.src_uri);
|
||||
if (parsed_uri != example.dst_uri)
|
||||
error ("Subscription expected to be %svalid but %svalid:\n%s",
|
||||
example.dst_uri, parsed_uri, example.src_uri);
|
||||
}
|
||||
}
|
||||
|
||||
public void extension_test () {
|
||||
Test.add_func ("/extensions/adblock2/config", test_adblock_config);
|
||||
Test.add_func ("/extensions/adblock2/subs", test_adblock_subs);
|
||||
Test.add_func ("/extensions/adblock2/init", test_adblock_init);
|
||||
Test.add_func ("/extensions/adblock2/parse", test_adblock_fixup_regexp);
|
||||
Test.add_func ("/extensions/adblock2/pattern", test_adblock_pattern);
|
||||
Test.add_func ("/extensions/adblock2/update", test_subscription_update);
|
||||
Test.add_func ("/extensions/adblock2/subsparse", test_subscription_uri_parsing);
|
||||
}
|
||||
|
52
extensions/adblock/filter.vala
Normal file
52
extensions/adblock/filter.vala
Normal file
|
@ -0,0 +1,52 @@
|
|||
/*
|
||||
Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de>
|
||||
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace Adblock {
|
||||
public abstract class Filter : Feature {
|
||||
Options optslist;
|
||||
protected HashTable<string, Regex?> rules;
|
||||
|
||||
public virtual void insert (string sig, Regex regex) {
|
||||
rules.insert (sig, regex);
|
||||
}
|
||||
|
||||
public virtual Regex? lookup (string sig) {
|
||||
return rules.lookup (sig);
|
||||
}
|
||||
|
||||
public virtual uint size () {
|
||||
return rules.size ();
|
||||
}
|
||||
|
||||
protected Filter (Options options) {
|
||||
optslist = options;
|
||||
clear ();
|
||||
}
|
||||
|
||||
public override void clear () {
|
||||
rules = new HashTable<string, Regex> (str_hash, str_equal);
|
||||
}
|
||||
|
||||
protected bool check_rule (Regex regex, string pattern, string request_uri, string page_uri) throws Error {
|
||||
if (!regex.match_full (request_uri))
|
||||
return false;
|
||||
|
||||
var opts = optslist.lookup (pattern);
|
||||
if (opts != null && Regex.match_simple (",third-party", opts,
|
||||
RegexCompileFlags.CASELESS, RegexMatchFlags.NOTEMPTY))
|
||||
if (page_uri != null && regex.match_full (page_uri))
|
||||
return false;
|
||||
debug ("blocked by pattern regexp=%s -- %s", regex.get_pattern (), request_uri);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
47
extensions/adblock/keys.vala
Normal file
47
extensions/adblock/keys.vala
Normal file
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de>
|
||||
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace Adblock {
|
||||
public class Keys : Filter {
|
||||
List<Regex> blacklist;
|
||||
|
||||
public Keys (Options options) {
|
||||
base (options);
|
||||
}
|
||||
|
||||
public override void clear () {
|
||||
base.clear ();
|
||||
blacklist = new List<Regex> ();
|
||||
}
|
||||
|
||||
public override Directive? match (string request_uri, string page_uri) throws Error {
|
||||
string? uri = fixup_regex ("", request_uri);
|
||||
if (uri == null)
|
||||
return null;
|
||||
|
||||
int signature_size = 8;
|
||||
int pos, l = uri.length;
|
||||
for (pos = l - signature_size; pos >= 0; pos--) {
|
||||
string signature = uri.offset (pos).ndup (signature_size);
|
||||
var regex = rules.lookup (signature);
|
||||
if (regex == null || blacklist.find (regex) != null)
|
||||
continue;
|
||||
|
||||
if (check_rule (regex, uri, request_uri, page_uri))
|
||||
return Directive.BLOCK;
|
||||
blacklist.prepend (regex);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
32
extensions/adblock/options.vala
Normal file
32
extensions/adblock/options.vala
Normal file
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace Adblock {
|
||||
public class Options : GLib.Object {
|
||||
HashTable<string, string?> optslist;
|
||||
|
||||
public Options () {
|
||||
clear ();
|
||||
}
|
||||
|
||||
public void insert (string sig, string? opts) {
|
||||
optslist.insert (sig, opts);
|
||||
}
|
||||
|
||||
public string? lookup (string sig) {
|
||||
return optslist.lookup (sig);
|
||||
}
|
||||
|
||||
public void clear () {
|
||||
optslist = new HashTable<string, string?> (str_hash, str_equal);
|
||||
}
|
||||
}
|
||||
}
|
26
extensions/adblock/pattern.vala
Normal file
26
extensions/adblock/pattern.vala
Normal file
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de>
|
||||
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace Adblock {
|
||||
public class Pattern : Filter {
|
||||
public Pattern (Options options) {
|
||||
base (options);
|
||||
}
|
||||
|
||||
public override Directive? match (string request_uri, string page_uri) throws Error {
|
||||
foreach (unowned string patt in rules.get_keys ())
|
||||
if (check_rule (rules.lookup (patt), patt, request_uri, page_uri))
|
||||
return Directive.BLOCK;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
404
extensions/adblock/subscriptions.vala
Normal file
404
extensions/adblock/subscriptions.vala
Normal file
|
@ -0,0 +1,404 @@
|
|||
/*
|
||||
Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de>
|
||||
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace Adblock {
|
||||
public abstract class Feature : GLib.Object {
|
||||
public virtual bool header (string key, string value) {
|
||||
return false;
|
||||
}
|
||||
public virtual bool parsed (File file) {
|
||||
return true;
|
||||
}
|
||||
public virtual Directive? match (string request_uri, string page_uri) throws Error {
|
||||
return null;
|
||||
}
|
||||
public virtual void clear () {
|
||||
}
|
||||
}
|
||||
|
||||
public class Subscription : GLib.Object {
|
||||
public string? path;
|
||||
bool debug_parse;
|
||||
public string uri { get; set; default = null; }
|
||||
public string title { get; set; default = null; }
|
||||
public bool active { get; set; default = true; }
|
||||
public bool mutable { get; set; default = true; }
|
||||
public bool valid { get; private set; default = true; }
|
||||
HashTable<string, Directive?> cache;
|
||||
List<Feature> features;
|
||||
public Pattern pattern;
|
||||
public Keys keys;
|
||||
public Options optslist;
|
||||
public Whitelist whitelist;
|
||||
public Element element;
|
||||
#if !HAVE_WEBKIT2
|
||||
WebKit.Download? download;
|
||||
#endif
|
||||
|
||||
public Subscription (string uri) {
|
||||
debug_parse = "adblock:parse" in (Environment.get_variable ("MIDORI_DEBUG") ?? "");
|
||||
|
||||
this.uri = uri;
|
||||
|
||||
this.optslist = new Options ();
|
||||
this.whitelist = new Whitelist (optslist);
|
||||
add_feature (this.whitelist);
|
||||
this.keys = new Keys (optslist);
|
||||
add_feature (this.keys);
|
||||
this.pattern = new Pattern (optslist);
|
||||
add_feature (this.pattern);
|
||||
this.element = new Element ();
|
||||
add_feature (this.element);
|
||||
clear ();
|
||||
}
|
||||
|
||||
public void add_feature (Feature feature) {
|
||||
features.append (feature);
|
||||
size++;
|
||||
}
|
||||
|
||||
/* foreach support */
|
||||
public new unowned Feature? get (uint index) {
|
||||
return features.nth_data (index);
|
||||
}
|
||||
public uint size { get; private set; }
|
||||
|
||||
public void clear () {
|
||||
cache = new HashTable<string, Directive?> (str_hash, str_equal);
|
||||
foreach (unowned Feature feature in features)
|
||||
feature.clear ();
|
||||
optslist.clear ();
|
||||
}
|
||||
|
||||
internal void parse_line (string? line) throws Error {
|
||||
if (line.has_prefix ("@@")) {
|
||||
if (line.contains("$") && line.contains ("domain"))
|
||||
return;
|
||||
if (line.has_prefix ("@@||"))
|
||||
add_url_pattern ("^", "whitelist", line.offset (4));
|
||||
else if (line.has_prefix ("@@|"))
|
||||
add_url_pattern ("^", "whitelist", line.offset (3));
|
||||
else
|
||||
add_url_pattern ("", "whitelist", line.offset (2));
|
||||
return;
|
||||
}
|
||||
/* TODO: [include] [exclude] */
|
||||
if (line[0] == '[')
|
||||
return;
|
||||
|
||||
/* CSS block hider */
|
||||
if (line.has_prefix ("##")) {
|
||||
/* TODO */
|
||||
return;
|
||||
}
|
||||
if (line[0] == '#')
|
||||
return;
|
||||
|
||||
/* TODO: CSS hider whitelist */
|
||||
if ("#@#" in line)
|
||||
return;
|
||||
|
||||
/* Per domain CSS hider rule */
|
||||
if ("##" in line) {
|
||||
frame_add_private (line, "##");
|
||||
return;
|
||||
}
|
||||
if ("#" in line) {
|
||||
frame_add_private (line, "#");
|
||||
return;
|
||||
}
|
||||
|
||||
/* URL blocker rule */
|
||||
if (line.has_prefix ("|")) {
|
||||
/* TODO: handle options and domains excludes */
|
||||
if (line.contains("$"))
|
||||
return;
|
||||
|
||||
if (line.has_prefix ("||"))
|
||||
add_url_pattern ("", "fulluri", line.offset (2));
|
||||
else
|
||||
add_url_pattern ("^", "fulluri", line.offset (1));
|
||||
return /* add_url_pattern */;
|
||||
}
|
||||
|
||||
add_url_pattern ("", "uri", line);
|
||||
return /* add_url_pattern */;
|
||||
}
|
||||
|
||||
void frame_add_private (string line, string sep) {
|
||||
string[] data = line.split (sep, 2);
|
||||
if (!(data[1] != null && data[1] != "")
|
||||
|| data[1].chr (-1, '\'') != null
|
||||
|| (data[1].chr (-1, ':') != null
|
||||
&& !Regex.match_simple (".*\\[.*:.*\\].*", data[1],
|
||||
RegexCompileFlags.CASELESS, RegexMatchFlags.NOTEMPTY))) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data[0].chr (-1, ',') != null) {
|
||||
string[] domains = data[0].split (",", -1);
|
||||
|
||||
foreach (unowned string domain in domains) {
|
||||
/* Ignore Firefox-specific option */
|
||||
if (domain == "~pregecko2")
|
||||
continue;
|
||||
string stripped = domain.strip ();
|
||||
/* FIXME: ~ should negate match */
|
||||
if (stripped[0] == '~')
|
||||
stripped = stripped.substring (1, -1);
|
||||
update_css_hash (stripped, data[1]);
|
||||
}
|
||||
}
|
||||
else {
|
||||
update_css_hash (data[0], data[1]);
|
||||
}
|
||||
}
|
||||
|
||||
bool css_element_seems_valid (string element) {
|
||||
bool is_valid = true;
|
||||
string[] valid_elements = { "::after", "::before", "a", "abbr", "address", "article", "aside",
|
||||
"b", "blockquote", "caption", "center", "cite", "code", "div", "dl", "dt", "dd", "em",
|
||||
"feed", "fieldset", "figcaption", "figure", "font", "footer", "form", "h1", "h2", "h3", "h4", "h5", "h6",
|
||||
"header", "hgroup", "i", "iframe", "iframe html *", "img", "kbd", "label", "legend", "li",
|
||||
"m", "main", "marquee", "menu", "nav", "ol", "option", "p", "pre", "q", "samp", "section",
|
||||
"small", "span", "strong", "summary", "table", "tr", "tbody", "td", "th", "thead", "tt", "ul" };
|
||||
|
||||
if (!element.has_prefix (".") && !element.has_prefix ("#")
|
||||
&& !(element.split("[")[0] in valid_elements))
|
||||
is_valid = false;
|
||||
|
||||
|
||||
bool debug_selectors = "adblock:css" in (Environment.get_variable ("MIDORI_DEBUG") ?? "");
|
||||
if (debug_selectors)
|
||||
stdout.printf ("Adblock '%s' %s: %s\n",
|
||||
this.title, is_valid ? "selector" : "INVALID?", element);
|
||||
|
||||
return is_valid;
|
||||
}
|
||||
|
||||
void update_css_hash (string domain, string value) {
|
||||
if (css_element_seems_valid (value)) {
|
||||
string? olddata = element.lookup (domain);
|
||||
if (olddata != null) {
|
||||
string newdata = olddata + " , " + value;
|
||||
element.insert (domain, newdata);
|
||||
} else {
|
||||
element.insert (domain, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void add_url_pattern (string prefix, string type, string line) throws Error {
|
||||
string[]? data = line.split ("$", 2);
|
||||
if (data == null || data[0] == null)
|
||||
return;
|
||||
|
||||
string patt, opts;
|
||||
patt = data[0];
|
||||
opts = type;
|
||||
|
||||
if (data[1] != null)
|
||||
opts = type + "," + data[1];
|
||||
|
||||
if (Regex.match_simple ("subdocument", opts,
|
||||
RegexCompileFlags.CASELESS, RegexMatchFlags.NOTEMPTY))
|
||||
return;
|
||||
|
||||
string format_patt = fixup_regex (prefix, patt);
|
||||
if (debug_parse)
|
||||
stdout.printf ("got: %s opts %s\n", format_patt, opts);
|
||||
compile_regexp (format_patt, opts);
|
||||
/* return format_patt */
|
||||
}
|
||||
|
||||
bool compile_regexp (string? patt, string opts) throws Error {
|
||||
if (patt == null)
|
||||
return false;
|
||||
try {
|
||||
var regex = new Regex (patt, RegexCompileFlags.OPTIMIZE, RegexMatchFlags.NOTEMPTY);
|
||||
/* is pattern is already a regex? */
|
||||
if (Regex.match_simple ("^/.*[\\^\\$\\*].*/$", patt,
|
||||
RegexCompileFlags.UNGREEDY, RegexMatchFlags.NOTEMPTY)
|
||||
|| (opts != null && opts.contains ("whitelist"))) {
|
||||
if (debug_parse)
|
||||
stdout.printf ("patt: %s\n", patt);
|
||||
if (opts.contains ("whitelist"))
|
||||
this.whitelist.insert (patt, regex);
|
||||
else
|
||||
this.pattern.insert (patt, regex);
|
||||
this.optslist.insert (patt, opts);
|
||||
return false;
|
||||
} else { /* nope, no regex */
|
||||
int pos = 0, len;
|
||||
int signature_size = 8;
|
||||
string sig;
|
||||
len = patt.length;
|
||||
|
||||
/* chop up pattern into substrings for faster matching */
|
||||
for (pos = len - signature_size; pos>=0; pos--)
|
||||
{
|
||||
sig = patt.offset (pos).ndup (signature_size);
|
||||
/* we don't have a * nor \\, does not look like regex, save chunk as "key" */
|
||||
if (!Regex.match_simple ("[\\*]", sig, RegexCompileFlags.UNGREEDY, RegexMatchFlags.NOTEMPTY) && keys.lookup (sig) == null) {
|
||||
this.keys.insert (sig, regex);
|
||||
this.optslist.insert (sig, opts);
|
||||
} else {
|
||||
/* starts with * or \\ - save as regex */
|
||||
if ((sig.has_prefix ("*") || sig.has_prefix("\\")) && this.pattern.lookup (sig) == null) {
|
||||
this.pattern.insert (sig, regex);
|
||||
this.optslist.insert (sig, opts);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch (Error error) {
|
||||
warning ("Adblock compile regexp: %s", error.message);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public void parse_header (string header) throws Error {
|
||||
/* Headers come in two forms
|
||||
! Foo: Bar
|
||||
! Some freeform text
|
||||
*/
|
||||
string key = header;
|
||||
string value = "";
|
||||
if (header.contains (":")) {
|
||||
string[] parts = header.split (":", 2);
|
||||
if (parts[0] != null && parts[0] != ""
|
||||
&& parts[1] != null && parts[1] != "") {
|
||||
key = parts[0].substring (2, -1);
|
||||
value = parts[1].substring (1, -1);
|
||||
}
|
||||
}
|
||||
debug ("Header '%s' says '%s'", key, value);
|
||||
if (key == "Title")
|
||||
title = value;
|
||||
foreach (unowned Feature feature in features) {
|
||||
if (feature.header (key, value))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#if !HAVE_WEBKIT2
|
||||
void download_status (ParamSpec pspec) {
|
||||
if (download.get_status () != WebKit.DownloadStatus.FINISHED)
|
||||
return;
|
||||
|
||||
download = null;
|
||||
try {
|
||||
parse ();
|
||||
} catch (Error error) {
|
||||
warning ("Error parsing %s: %s", uri, error.message);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public void parse () throws Error
|
||||
{
|
||||
if (!active)
|
||||
return;
|
||||
|
||||
debug ("Parsing %s (%s)", uri, path);
|
||||
|
||||
clear ();
|
||||
|
||||
if (uri.has_prefix ("file://"))
|
||||
path = Filename.from_uri (uri);
|
||||
else {
|
||||
string cache_dir = GLib.Path.build_filename (GLib.Environment.get_user_cache_dir (), PACKAGE_NAME, "adblock");
|
||||
Midori.Paths.mkdir_with_parents (cache_dir);
|
||||
string filename = Checksum.compute_for_string (ChecksumType.MD5, this.uri, -1);
|
||||
path = GLib.Path.build_filename (cache_dir, filename);
|
||||
}
|
||||
|
||||
File filter_file = File.new_for_path (path);
|
||||
DataInputStream stream;
|
||||
try {
|
||||
stream = new DataInputStream (filter_file.read ());
|
||||
} catch (IOError.NOT_FOUND exist_error) {
|
||||
#if HAVE_WEBKIT2
|
||||
/* TODO */
|
||||
#else
|
||||
/* Don't bother trying to download local files */
|
||||
if (!uri.has_prefix ("file://")) {
|
||||
if (download != null)
|
||||
return;
|
||||
|
||||
string destination_uri = Filename.to_uri (path, null);
|
||||
debug ("Fetching %s to %s now", uri, destination_uri);
|
||||
download = new WebKit.Download (new WebKit.NetworkRequest (uri));
|
||||
if (!Midori.Download.has_enough_space (download, destination_uri, true))
|
||||
throw new FileError.EXIST ("Can't download to \"%s\"", path);
|
||||
download.destination_uri = destination_uri;
|
||||
download.notify["status"].connect (download_status);
|
||||
download.start ();
|
||||
}
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
valid = false;
|
||||
string? line;
|
||||
while ((line = stream.read_line (null)) != null) {
|
||||
if (line == null)
|
||||
continue;
|
||||
string chomped = line.chomp ();
|
||||
if (chomped == "")
|
||||
continue;
|
||||
if (line[0] == '!')
|
||||
parse_header (chomped);
|
||||
else
|
||||
parse_line (chomped);
|
||||
/* The file isn't completely empty */
|
||||
valid = true;
|
||||
}
|
||||
|
||||
foreach (unowned Feature feature in features) {
|
||||
if (!feature.parsed (filter_file))
|
||||
valid = false;
|
||||
}
|
||||
}
|
||||
|
||||
public Directive? get_directive (string request_uri, string page_uri) {
|
||||
try {
|
||||
Directive? directive = cache.lookup (request_uri);
|
||||
if (directive != null)
|
||||
return directive;
|
||||
foreach (unowned Feature feature in features) {
|
||||
directive = feature.match (request_uri, page_uri);
|
||||
if (directive != null) {
|
||||
debug ("%s gave %s for %s (%s)\n",
|
||||
feature.get_type ().name (), directive.to_string (), request_uri, page_uri);
|
||||
return directive;
|
||||
}
|
||||
}
|
||||
} catch (Error error) {
|
||||
warning ("Adblock match error: %s\n", error.message);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
public void add_rule (string rule) {
|
||||
try {
|
||||
var file = File.new_for_uri (uri);
|
||||
file.append_to (FileCreateFlags.NONE).write (("%s\n".printf (rule)).data);
|
||||
parse ();
|
||||
} catch (Error error) {
|
||||
warning ("Failed to add custom rule: %s", error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
155
extensions/adblock/updater.vala
Normal file
155
extensions/adblock/updater.vala
Normal file
|
@ -0,0 +1,155 @@
|
|||
/*
|
||||
Copyright (C) 2014 Paweł Forysiuk <tuxator@o2.pl>
|
||||
Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace Adblock {
|
||||
public class Updater : Feature {
|
||||
string expires_meta;
|
||||
string last_mod_meta;
|
||||
public DateTime last_updated { get; set; }
|
||||
public DateTime expires { get; set; }
|
||||
public bool needs_update { get; set; }
|
||||
|
||||
public Updater () {
|
||||
}
|
||||
|
||||
public override void clear () {
|
||||
expires_meta = null;
|
||||
last_mod_meta = null;
|
||||
last_updated = null;
|
||||
expires = null;
|
||||
needs_update = false;
|
||||
}
|
||||
|
||||
public override bool header (string key, string value) {
|
||||
if (key.has_prefix ("Last mod") || key == "Updated") {
|
||||
last_mod_meta = value;
|
||||
return true;
|
||||
} else if (key == "Expires") {
|
||||
/* ! Expires: 5 days (update frequency) */
|
||||
expires_meta = value;
|
||||
return true;
|
||||
} else if (key.has_prefix ("! This list expires after")) {
|
||||
/* ! This list expires after 14 days */
|
||||
expires_meta = key.substring (26, -1);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool parsed (File file) {
|
||||
process_dates (file);
|
||||
/* It's not an error to have no update headers, we go for defaults */
|
||||
return true;
|
||||
}
|
||||
|
||||
int get_month_from_string (string? month) {
|
||||
if (month == null)
|
||||
return 0;
|
||||
|
||||
string[] months = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
|
||||
for (int i = 0; i<= months.length; i++)
|
||||
{
|
||||
if (month.has_prefix (months[i]))
|
||||
return i+1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void process_dates (File file) {
|
||||
DateTime now = new DateTime.now_local ();
|
||||
last_updated = null;
|
||||
expires = null;
|
||||
|
||||
/* We have "last modification" metadata */
|
||||
if (last_mod_meta != null && (last_mod_meta.contains (" ") && last_mod_meta[0].isdigit () == true)) {
|
||||
int h = 0, min = 0, d, m, y;
|
||||
/* Date in a form of: 20.08.2012 12:34 */
|
||||
if (last_mod_meta.contains (".") || last_mod_meta.contains("-")) {
|
||||
string[] parts = last_mod_meta.split (" ", 2);
|
||||
string[] date_parts;
|
||||
string split_char = " ";
|
||||
|
||||
/* contains time part ? */
|
||||
if (parts[1] != "" && parts[1].contains (":")) {
|
||||
string[] time_parts = parts[1].split (":", 2);
|
||||
h = int.parse(time_parts[0]);
|
||||
min = int.parse(time_parts[1]);
|
||||
}
|
||||
|
||||
/* check if dot or dash was used as a delimiter */
|
||||
if (parts[0].contains ("."))
|
||||
split_char = ".";
|
||||
else if (parts[0].contains ("-"))
|
||||
split_char = "-";
|
||||
|
||||
date_parts = parts[0].split (split_char, 3);
|
||||
m = int.parse(date_parts[1]);
|
||||
if (date_parts[2].length == 4) {
|
||||
y = int.parse(date_parts[2]);
|
||||
d = int.parse(date_parts[0]);
|
||||
} else {
|
||||
y = int.parse(date_parts[0]);
|
||||
d = int.parse(date_parts[2]);
|
||||
}
|
||||
} else { /* Date in a form of: 20 Mar 2012 12:34 */
|
||||
string[] parts = last_mod_meta.split (" ", 4);
|
||||
/* contains time part ? */
|
||||
if (parts[3] != null && parts[3].contains (":")) {
|
||||
string[] time_parts = parts[3].split (":", 2);
|
||||
h = int.parse(time_parts[0]);
|
||||
min = int.parse(time_parts[1]);
|
||||
}
|
||||
|
||||
m = get_month_from_string (parts[1]);
|
||||
if (parts[2].length == 4) {
|
||||
y = int.parse(parts[2]);
|
||||
d = int.parse(parts[0]);
|
||||
} else {
|
||||
y = int.parse(parts[0]);
|
||||
d = int.parse(parts[2]);
|
||||
}
|
||||
}
|
||||
|
||||
last_updated = new DateTime.local (y, m, d, h, min, 0.0);
|
||||
} else {
|
||||
/* FIXME: use file modification date if there's no update header
|
||||
try {
|
||||
string modified = FileAttribute.TIME_MODIFIED;
|
||||
var info = file.query_filesystem_info (modified);
|
||||
last_updated = new DateTime.from_timeval_local (info.get_modification_time ());
|
||||
} catch (Error error) {
|
||||
last_updated = now;
|
||||
}
|
||||
*/
|
||||
last_updated = now;
|
||||
}
|
||||
|
||||
/* We have "expires" metadata */
|
||||
if (expires_meta != null) {
|
||||
if (expires_meta.contains ("days")) {
|
||||
string[] parts = expires_meta.split (" ");
|
||||
expires = last_updated.add_days (int.parse (parts[0]));
|
||||
} else if (expires_meta.contains ("hours")) {
|
||||
string[] parts = expires_meta.split (" ");
|
||||
expires = last_updated.add_hours (int.parse (parts[0]));
|
||||
}
|
||||
} else {
|
||||
/* No expire metadata found, assume x days */
|
||||
int days_to_expire = 7;
|
||||
expires = last_updated.add_days (days_to_expire);
|
||||
}
|
||||
|
||||
/* Check if we are past expire date */
|
||||
needs_update = now.compare (expires) == 1;
|
||||
}
|
||||
}
|
||||
}
|
30
extensions/adblock/whitelist.vala
Normal file
30
extensions/adblock/whitelist.vala
Normal file
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
|
||||
Copyright (C) 2014 Paweł Forysiuk <tuxator@o2.pl>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace Adblock {
|
||||
public class Whitelist : Filter {
|
||||
public Whitelist (Options options) {
|
||||
base (options);
|
||||
}
|
||||
|
||||
public override Directive? match (string request_uri, string page_uri) throws Error {
|
||||
foreach (unowned string white in rules.get_keys ()) {
|
||||
var regex = rules.lookup (white);
|
||||
if (!regex.match_full (request_uri))
|
||||
return null;
|
||||
if (Regex.match_simple (regex.get_pattern (), request_uri, RegexCompileFlags.UNGREEDY, RegexMatchFlags.NOTEMPTY))
|
||||
return Directive.ALLOW;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
292
extensions/adblock/widgets.vala
Normal file
292
extensions/adblock/widgets.vala
Normal file
|
@ -0,0 +1,292 @@
|
|||
/*
|
||||
Copyright (C) 2009-2014 Christian Dywan <christian@twotoasts.de>
|
||||
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace Adblock {
|
||||
|
||||
|
||||
public class StatusIcon : Midori.ContextAction {
|
||||
Config config;
|
||||
SubscriptionManager manager;
|
||||
public State state;
|
||||
public bool debug_element_toggled;
|
||||
|
||||
public StatusIcon (Adblock.Config config, SubscriptionManager manager) {
|
||||
GLib.Object (name: "AdblockStatusMenu");
|
||||
|
||||
this.config = config;
|
||||
this.manager = manager;
|
||||
this.debug_element_toggled = false;
|
||||
|
||||
var item = new Midori.ContextAction ("Preferences",
|
||||
_("Preferences"), null, Gtk.STOCK_PREFERENCES);
|
||||
item.activate.connect (() => {
|
||||
manager.add_subscription (null);
|
||||
});
|
||||
add (item);
|
||||
|
||||
add (null);
|
||||
|
||||
var checkitem = new Gtk.ToggleAction ("Disable", _("Disable"), null, null);
|
||||
checkitem.set_active (!config.enabled);
|
||||
checkitem.toggled.connect (() => {
|
||||
config.enabled = !checkitem.active;
|
||||
set_state (config.enabled ? Adblock.State.ENABLED : Adblock.State.DISABLED);
|
||||
});
|
||||
add (checkitem);
|
||||
|
||||
var hideritem = new Gtk.ToggleAction ("HiddenElements",
|
||||
_("Display hidden elements"), null, null);
|
||||
hideritem.set_active (debug_element_toggled);
|
||||
hideritem.toggled.connect (() => {
|
||||
this.debug_element_toggled = hideritem.active;
|
||||
});
|
||||
add (hideritem);
|
||||
set_status (config.enabled ? "enabled" : "disabled");
|
||||
}
|
||||
|
||||
void set_status (string status) {
|
||||
gicon = new GLib.ThemedIcon ("adblock-%s".printf (status));
|
||||
}
|
||||
|
||||
public void set_state (Adblock.State state) {
|
||||
this.state = state;
|
||||
|
||||
if (this.state == State.BLOCKED) {
|
||||
set_status ("blocked");
|
||||
tooltip = _("Blocking");
|
||||
} else if (this.state == State.ENABLED) {
|
||||
set_status ("enabled");
|
||||
tooltip = _("Enabled");
|
||||
} else if (this.state == State.DISABLED) {
|
||||
set_status ("disabled");
|
||||
tooltip = _("Disabled");
|
||||
} else
|
||||
assert_not_reached ();
|
||||
}
|
||||
}
|
||||
|
||||
public class SubscriptionManager {
|
||||
Gtk.TreeView treeview;
|
||||
Gtk.ListStore liststore;
|
||||
Adblock.Config config;
|
||||
public Gtk.Label description_label;
|
||||
string description;
|
||||
|
||||
public SubscriptionManager (Config config) {
|
||||
this.config = config;
|
||||
this.liststore = new Gtk.ListStore (1, typeof (Subscription));
|
||||
this.description_label = new Gtk.Label (null);
|
||||
this.description = _("Type the address of a preconfigured filter list in the text entry and hit Enter.\n");
|
||||
this.description += _("You can find more lists by visiting following sites:\n %s, %s\n".printf (
|
||||
"<a href=\"http://adblockplus.org/en/subscriptions\">adblockplus.org/en/subscriptions</a>",
|
||||
"<a href=\"http://easylist.adblockplus.org/\">easylist.adblockplus.org</a>"
|
||||
));
|
||||
}
|
||||
|
||||
public void add_subscription (string? uri) {
|
||||
var dialog = new Gtk.Dialog.with_buttons (_("Configure Advertisement filters"),
|
||||
null,
|
||||
#if !HAVE_GTK3
|
||||
Gtk.DialogFlags.NO_SEPARATOR |
|
||||
#endif
|
||||
Gtk.DialogFlags.DESTROY_WITH_PARENT,
|
||||
Gtk.STOCK_HELP, Gtk.ResponseType.HELP,
|
||||
Gtk.STOCK_CLOSE, Gtk.ResponseType.CLOSE);
|
||||
#if HAVE_GTK3
|
||||
dialog.get_widget_for_response (Gtk.ResponseType.HELP).get_style_context ().add_class ("help_button");
|
||||
#endif
|
||||
dialog.set_icon_name (Gtk.STOCK_PROPERTIES);
|
||||
dialog.set_response_sensitive (Gtk.ResponseType.HELP, false);
|
||||
|
||||
var hbox = new Gtk.HBox (false, 0);
|
||||
(dialog.get_content_area () as Gtk.Box).pack_start (hbox, true, true, 12);
|
||||
var vbox = new Gtk.VBox (false, 0);
|
||||
hbox.pack_start (vbox, true, true, 4);
|
||||
this.description_label.set_markup (this.description);
|
||||
this.description_label.set_line_wrap (true);
|
||||
vbox.pack_start (this.description_label, false, false, 4);
|
||||
|
||||
var entry = new Gtk.Entry ();
|
||||
if (uri != null)
|
||||
entry.set_text (uri);
|
||||
vbox.pack_start (entry, false, false, 4);
|
||||
|
||||
liststore = new Gtk.ListStore (1, typeof (Subscription));
|
||||
treeview = new Gtk.TreeView.with_model (liststore);
|
||||
treeview.set_headers_visible (false);
|
||||
var column = new Gtk.TreeViewColumn ();
|
||||
var renderer_toggle = new Gtk.CellRendererToggle ();
|
||||
column.pack_start (renderer_toggle, false);
|
||||
column.set_cell_data_func (renderer_toggle, (column, renderer, model, iter) => {
|
||||
Subscription sub;
|
||||
liststore.get (iter, 0, out sub);
|
||||
renderer.set ("active", sub.active,
|
||||
"sensitive", sub.mutable);
|
||||
});
|
||||
renderer_toggle.toggled.connect ((path) => {
|
||||
Gtk.TreeIter iter;
|
||||
if (liststore.get_iter_from_string (out iter, path)) {
|
||||
Subscription sub;
|
||||
liststore.get (iter, 0, out sub);
|
||||
sub.active = !sub.active;
|
||||
}
|
||||
});
|
||||
treeview.append_column (column);
|
||||
|
||||
column = new Gtk.TreeViewColumn ();
|
||||
var renderer_text = new Gtk.CellRendererText ();
|
||||
column.pack_start (renderer_text, false);
|
||||
renderer_text.set ("editable", true);
|
||||
// TODO: renderer_text.edited.connect
|
||||
column.set_cell_data_func (renderer_text, (column, renderer, model, iter) => {
|
||||
Subscription sub;
|
||||
liststore.get (iter, 0, out sub);
|
||||
string status = "";
|
||||
foreach (unowned Feature feature in sub) {
|
||||
var updater = feature as Adblock.Updater;
|
||||
if (updater != null) {
|
||||
if (updater.last_updated != null)
|
||||
status = updater.last_updated.format (_("Last update: %x %X"));
|
||||
}
|
||||
}
|
||||
if (!sub.valid)
|
||||
status = _("File incomplete - broken download?");
|
||||
renderer.set ("markup", (Markup.printf_escaped ("<b>%s</b>\n%s",
|
||||
sub.title ?? sub.uri, status)));
|
||||
});
|
||||
treeview.append_column (column);
|
||||
|
||||
column = new Gtk.TreeViewColumn ();
|
||||
Gtk.CellRendererPixbuf renderer_button = new Gtk.CellRendererPixbuf ();
|
||||
column.pack_start (renderer_button, false);
|
||||
column.set_cell_data_func (renderer_button, on_render_button);
|
||||
treeview.append_column (column);
|
||||
|
||||
var scrolled = new Gtk.ScrolledWindow (null, null);
|
||||
scrolled.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
|
||||
scrolled.add (treeview);
|
||||
vbox.pack_start (scrolled);
|
||||
int height;
|
||||
treeview.create_pango_layout ("a\nb").get_pixel_size (null, out height);
|
||||
scrolled.set_size_request (-1, height * 5);
|
||||
|
||||
foreach (unowned Subscription sub in config)
|
||||
liststore.insert_with_values (null, 0, 0, sub);
|
||||
treeview.button_release_event.connect (button_released);
|
||||
|
||||
entry.activate.connect (() => {
|
||||
string? parsed_uri = Adblock.parse_subscription_uri (entry.text);
|
||||
if (parsed_uri != null) {
|
||||
var sub = new Subscription (parsed_uri);
|
||||
if (config.add (sub)) {
|
||||
liststore.insert_with_values (null, 0, 0, sub);
|
||||
try {
|
||||
sub.parse ();
|
||||
} catch (GLib.Error error) {
|
||||
warning ("Error parsing %s: %s", sub.uri, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
entry.text = "";
|
||||
});
|
||||
|
||||
dialog.get_content_area ().show_all ();
|
||||
|
||||
dialog.response.connect ((response)=>{ dialog.destroy (); });
|
||||
dialog.show ();
|
||||
}
|
||||
|
||||
void on_render_button (Gtk.CellLayout column, Gtk.CellRenderer renderer,
|
||||
Gtk.TreeModel model, Gtk.TreeIter iter) {
|
||||
|
||||
Subscription sub;
|
||||
liststore.get (iter, 0, out sub);
|
||||
|
||||
renderer.set ("stock-id", sub.mutable ? Gtk.STOCK_DELETE : null,
|
||||
"stock-size", Gtk.IconSize.MENU);
|
||||
}
|
||||
|
||||
public bool button_released (Gdk.EventButton event) {
|
||||
Gtk.TreePath? path;
|
||||
Gtk.TreeViewColumn column;
|
||||
if (treeview.get_path_at_pos ((int)event.x, (int)event.y, out path, out column, null, null)) {
|
||||
if (path != null) {
|
||||
if (column == treeview.get_column (2)) {
|
||||
Gtk.TreeIter iter;
|
||||
if (liststore.get_iter (out iter, path)) {
|
||||
Subscription sub;
|
||||
liststore.get (iter, 0, out sub);
|
||||
if (sub.mutable) {
|
||||
config.remove (sub);
|
||||
liststore.remove (iter);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
class CustomRulesEditor {
|
||||
Gtk.Dialog dialog;
|
||||
Subscription custom;
|
||||
public string? rule { get; set; }
|
||||
|
||||
public CustomRulesEditor (Subscription custom) {
|
||||
this.custom = custom;
|
||||
}
|
||||
|
||||
public void set_uri (string uri) {
|
||||
this.rule = uri;
|
||||
}
|
||||
|
||||
public void show () {
|
||||
this.dialog = new Gtk.Dialog.with_buttons (_("Edit rule"),
|
||||
null,
|
||||
#if !HAVE_GTK3
|
||||
Gtk.DialogFlags.NO_SEPARATOR |
|
||||
#endif
|
||||
Gtk.DialogFlags.DESTROY_WITH_PARENT,
|
||||
Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
||||
Gtk.STOCK_ADD, Gtk.ResponseType.ACCEPT);
|
||||
dialog.set_icon_name (Gtk.STOCK_ADD);
|
||||
dialog.resizable = false;
|
||||
|
||||
var hbox = new Gtk.HBox (false, 8);
|
||||
var sizegroup = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL);
|
||||
hbox.border_width = 5;
|
||||
var label = new Gtk.Label.with_mnemonic (_("_Rule:"));
|
||||
sizegroup.add_widget (label);
|
||||
hbox.pack_start (label, false, false, 0);
|
||||
(dialog.get_content_area () as Gtk.Box).pack_start (hbox, false, true, 0);
|
||||
|
||||
var entry = new Gtk.Entry ();
|
||||
sizegroup.add_widget (entry);
|
||||
entry.activates_default = true;
|
||||
entry.set_text (this.rule);
|
||||
hbox.pack_start (entry, true, true, 0);
|
||||
|
||||
dialog.get_content_area ().show_all ();
|
||||
|
||||
dialog.set_default_response (Gtk.ResponseType.ACCEPT);
|
||||
if (dialog.run () != Gtk.ResponseType.ACCEPT)
|
||||
return;
|
||||
|
||||
this.rule = entry.get_text ();
|
||||
this.dialog.destroy ();
|
||||
custom.add_rule (this.rule);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -393,6 +393,9 @@ addons_button_delete_clicked_cb (GtkWidget* toolitem,
|
|||
{
|
||||
GtkTreeModel* model;
|
||||
GtkTreeIter iter;
|
||||
GtkTreePath* path;
|
||||
GtkTreeRowReference* row;
|
||||
gchar* fullpath;
|
||||
|
||||
if (katze_tree_view_get_selected_iter (GTK_TREE_VIEW (addons->treeview),
|
||||
&model, &iter))
|
||||
|
@ -403,6 +406,11 @@ addons_button_delete_clicked_cb (GtkWidget* toolitem,
|
|||
gchar* markup;
|
||||
|
||||
gtk_tree_model_get (model, &iter, 0, &element, -1);
|
||||
fullpath = g_strdup (element->fullpath);
|
||||
|
||||
path = gtk_tree_model_get_path (model, &iter);
|
||||
row = gtk_tree_row_reference_new (model, path);
|
||||
gtk_tree_path_free (path);
|
||||
dialog = gtk_message_dialog_new (
|
||||
GTK_WINDOW (gtk_widget_get_toplevel (GTK_WIDGET (addons))),
|
||||
GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
|
||||
|
@ -424,6 +432,9 @@ addons_button_delete_clicked_cb (GtkWidget* toolitem,
|
|||
GTK_MESSAGE_DIALOG (dialog), "%s", markup);
|
||||
g_free (markup);
|
||||
|
||||
/* The execution of gtk_dialog_run allows the directory watcher to
|
||||
rebuild the treeview and the element list, so our references may be
|
||||
invalid afterward */
|
||||
delete_response = gtk_dialog_run (GTK_DIALOG (dialog));
|
||||
gtk_widget_destroy (GTK_WIDGET (dialog));
|
||||
|
||||
|
@ -433,7 +444,7 @@ addons_button_delete_clicked_cb (GtkWidget* toolitem,
|
|||
GFile* file;
|
||||
gboolean result;
|
||||
|
||||
file = g_file_new_for_path (element->fullpath);
|
||||
file = g_file_new_for_path (fullpath);
|
||||
result = g_file_delete (file, NULL, &error);
|
||||
|
||||
if (!result && error)
|
||||
|
@ -452,11 +463,20 @@ addons_button_delete_clicked_cb (GtkWidget* toolitem,
|
|||
g_error_free (error);
|
||||
}
|
||||
|
||||
if (result)
|
||||
/* The row reference may have been invalidated if the
|
||||
filesystem watcher deleted the row concurrently */
|
||||
if (result && gtk_tree_row_reference_valid (row))
|
||||
{
|
||||
path = gtk_tree_row_reference_get_path (row);
|
||||
gtk_tree_model_get_iter (model, &iter, path);
|
||||
gtk_tree_path_free (path);
|
||||
gtk_list_store_remove (GTK_LIST_STORE (model), &iter);
|
||||
}
|
||||
|
||||
gtk_tree_row_reference_free (row);
|
||||
g_object_unref (file);
|
||||
}
|
||||
g_free (fullpath);
|
||||
}
|
||||
}
|
||||
static void
|
||||
|
@ -485,8 +505,8 @@ addons_open_in_editor_clicked_cb (GtkWidget* toolitem,
|
|||
else
|
||||
{
|
||||
gchar* element_uri = g_filename_to_uri (element->fullpath, NULL, NULL);
|
||||
sokoke_show_uri (NULL, element_uri,
|
||||
gtk_get_current_event_time (), NULL);
|
||||
gboolean handled = FALSE;
|
||||
g_signal_emit_by_name (midori_browser_get_current_tab (browser), "open-uri", element_uri, &handled);
|
||||
g_free (element_uri);
|
||||
}
|
||||
|
||||
|
@ -522,8 +542,9 @@ addons_open_target_folder_clicked_cb (GtkWidget* toolitem,
|
|||
folder_uri = g_filename_to_uri (folder, NULL, NULL);
|
||||
g_free (folder);
|
||||
|
||||
sokoke_show_uri (gtk_widget_get_screen (GTK_WIDGET (addons->treeview)),
|
||||
folder_uri, gtk_get_current_event_time (), NULL);
|
||||
MidoriBrowser* browser = midori_browser_get_for_widget (addons->treeview);
|
||||
gboolean handled = FALSE;
|
||||
g_signal_emit_by_name (midori_browser_get_current_tab (browser), "open-uri", folder_uri, &handled);
|
||||
g_free (folder_uri);
|
||||
}
|
||||
|
||||
|
@ -617,7 +638,6 @@ addons_get_toolbar (MidoriViewable* viewable)
|
|||
if (!ADDONS (viewable)->toolbar)
|
||||
{
|
||||
toolbar = gtk_toolbar_new ();
|
||||
gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_BUTTON);
|
||||
toolitem = gtk_tool_item_new ();
|
||||
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
|
||||
gtk_widget_show (GTK_WIDGET (toolitem));
|
||||
|
@ -1044,7 +1064,7 @@ css_metadata_from_file (const gchar* filename,
|
|||
domain = g_strndup (value + begin, end - begin * 2);
|
||||
if (!midori_uri_is_location (domain)
|
||||
&& !g_str_has_prefix (domain, "file://"))
|
||||
tmp_domain = g_strdup_printf ("http://*%s/*", domain);
|
||||
tmp_domain = g_strdup_printf ("http*://*%s/*", domain);
|
||||
else
|
||||
tmp_domain = domain;
|
||||
|
||||
|
@ -1448,6 +1468,12 @@ static gboolean
|
|||
addons_skip_element (struct AddonElement* element,
|
||||
gchar* uri)
|
||||
{
|
||||
if (midori_debug("addons:match"))
|
||||
{
|
||||
g_print("%s: %s on %s matched: %d\n", G_STRFUNC,
|
||||
element->displayname, uri, addons_may_run (uri, &element->includes, &element->excludes));
|
||||
}
|
||||
|
||||
if (!element->enabled || element->broken)
|
||||
return TRUE;
|
||||
if (element->includes || element->excludes)
|
||||
|
@ -1866,6 +1892,8 @@ test_addons_simple_regexp (void)
|
|||
{ "*", "^.*" },
|
||||
{ "http://", "^http://" },
|
||||
{ "https://", "^https://" },
|
||||
{ "http*://", "^http://" },
|
||||
{ "http*://", "^https://" },
|
||||
{ "about:blank", "^about:blank" },
|
||||
{ "file://", "^file://" },
|
||||
{ "ftp://", "^ftp://" },
|
||||
|
|
|
@ -10,7 +10,8 @@
|
|||
*/
|
||||
|
||||
namespace Apps {
|
||||
const string EXEC_PREFIX = PACKAGE_NAME + " -a ";
|
||||
const string APP_PREFIX = PACKAGE_NAME + " -a ";
|
||||
const string PROFILE_PREFIX = PACKAGE_NAME + " -c ";
|
||||
|
||||
private class Launcher : GLib.Object, GLib.Initable {
|
||||
internal GLib.File file;
|
||||
|
@ -19,31 +20,145 @@ namespace Apps {
|
|||
internal string exec;
|
||||
internal string uri;
|
||||
|
||||
internal static async void create (GLib.File folder, string uri, string title) {
|
||||
/* Strip LRE leading character and / */
|
||||
string filename = title.delimit ("/", ' ') + ".desktop";
|
||||
string exec = EXEC_PREFIX + uri;
|
||||
string name = title;
|
||||
// TODO: Midori.Paths.get_icon save to png
|
||||
internal static string get_favicon_name_for_uri (string prefix, GLib.File folder, string uri, bool testing)
|
||||
{
|
||||
string icon_name = Midori.Stock.WEB_BROWSER;
|
||||
string contents = """
|
||||
[Desktop Entry]
|
||||
Version=1.0
|
||||
Type=Application
|
||||
Name=%s
|
||||
Exec=%s
|
||||
TryExec=%s
|
||||
Icon=%s
|
||||
Categories=Network;
|
||||
""".printf (name, exec, PACKAGE_NAME, icon_name);
|
||||
var file = folder.get_child (filename);
|
||||
|
||||
if (testing == true)
|
||||
return icon_name;
|
||||
|
||||
if (prefix != PROFILE_PREFIX)
|
||||
{
|
||||
try {
|
||||
var stream = yield file.replace_async (null, false, GLib.FileCreateFlags.NONE);
|
||||
yield stream.write_async (contents.data);
|
||||
var pixbuf = Midori.Paths.get_icon (uri, null);
|
||||
if (pixbuf == null)
|
||||
throw new FileError.EXIST ("No favicon loaded");
|
||||
string icon_filename = folder.get_child ("icon.png").get_path ();
|
||||
pixbuf.save (icon_filename, "png", null, "compression", "7", null);
|
||||
#if HAVE_WIN32
|
||||
string doubleslash_icon = icon_filename.replace ("\\", "\\\\");
|
||||
icon_name = doubleslash_icon;
|
||||
#else
|
||||
icon_name = icon_filename;
|
||||
#endif
|
||||
}
|
||||
catch (Error error) {
|
||||
// TODO GUI infobar
|
||||
warning ("Failed to create new launcher: %s", error.message);
|
||||
GLib.warning (_("Failed to fetch application icon in %s: %s"), folder.get_path (), error.message);
|
||||
}
|
||||
}
|
||||
return icon_name;
|
||||
}
|
||||
|
||||
internal static string prepare_desktop_file (string prefix, string name, string uri, string title, string icon_name)
|
||||
{
|
||||
string exec;
|
||||
#if HAVE_WIN32
|
||||
string doubleslash_uri = uri.replace ("\\", "\\\\");
|
||||
string quoted_uri = GLib.Shell.quote (doubleslash_uri);
|
||||
exec = prefix + quoted_uri;
|
||||
#else
|
||||
exec = prefix + uri;
|
||||
#endif
|
||||
var keyfile = new GLib.KeyFile ();
|
||||
string entry = "Desktop Entry";
|
||||
|
||||
keyfile.set_string (entry, "Version", "1.0");
|
||||
keyfile.set_string (entry, "Type", "Application");
|
||||
keyfile.set_string (entry, "Name", name);
|
||||
keyfile.set_string (entry, "Exec", exec);
|
||||
keyfile.set_string (entry, "TryExec", PACKAGE_NAME);
|
||||
keyfile.set_string (entry, "Icon", icon_name);
|
||||
keyfile.set_string (entry, "Categories", "Network;");
|
||||
/*
|
||||
Using the sanitized URI as a class matches midori_web_app_new
|
||||
So dock type launchers can distinguish different apps with the same executable
|
||||
*/
|
||||
if (exec.has_prefix (APP_PREFIX))
|
||||
keyfile.set_string (entry, "StartupWMClass", uri.delimit (":.\\/", '_'));
|
||||
|
||||
return keyfile.to_data();
|
||||
}
|
||||
|
||||
internal static File get_app_folder () {
|
||||
var data_dir = File.new_for_path (Midori.Paths.get_user_data_dir ()).get_child (PACKAGE_NAME);
|
||||
return data_dir.get_child ("apps");
|
||||
}
|
||||
|
||||
internal static async File create_app (string uri, string title, Gtk.Widget? proxy) {
|
||||
string checksum = Checksum.compute_for_string (ChecksumType.MD5, uri, -1);
|
||||
var folder = get_app_folder ();
|
||||
yield Launcher.create (APP_PREFIX, folder.get_child (checksum),
|
||||
uri, title, proxy);
|
||||
return folder.get_child (checksum);
|
||||
}
|
||||
|
||||
internal static File get_profile_folder () {
|
||||
var data_dir = File.new_for_path (Midori.Paths.get_user_data_dir ()).get_child (PACKAGE_NAME);
|
||||
return data_dir.get_child ("profiles");
|
||||
}
|
||||
|
||||
internal static async File create_profile (Gtk.Widget? proxy) {
|
||||
string uuid = g_dbus_generate_guid ();
|
||||
string config = Path.build_path (Path.DIR_SEPARATOR_S,
|
||||
Midori.Paths.get_user_data_dir (), PACKAGE_NAME, "profiles", uuid);
|
||||
var folder = get_profile_folder ();
|
||||
yield Launcher.create (PROFILE_PREFIX, folder.get_child (uuid),
|
||||
config, _("Midori (%s)").printf (uuid), proxy);
|
||||
return folder.get_child (uuid);
|
||||
}
|
||||
|
||||
internal static async void create (string prefix, GLib.File folder, string uri, string title, Gtk.Widget proxy) {
|
||||
/* Strip LRE leading character and / */
|
||||
string name = title.delimit ("/", ' ').strip();
|
||||
string filename = Midori.Download.clean_filename (name);
|
||||
string icon_name = Midori.Stock.WEB_BROWSER;
|
||||
bool testing = false;
|
||||
if (proxy == null)
|
||||
testing = true;
|
||||
var file = folder.get_child ("desc");
|
||||
|
||||
try {
|
||||
folder.make_directory_with_parents (null);
|
||||
} catch (IOError.EXISTS exist_error) {
|
||||
/* It's no error if the folder already exists */
|
||||
} catch (Error error) {
|
||||
warning (_("Failed to create new launcher (%s): %s"), file.get_path (), error.message);
|
||||
}
|
||||
|
||||
icon_name = get_favicon_name_for_uri (prefix, folder, uri, testing);
|
||||
string desktop_file = prepare_desktop_file (prefix, name, uri, title, icon_name);
|
||||
|
||||
try {
|
||||
var stream = yield file.replace_async (null, false, GLib.FileCreateFlags.NONE);
|
||||
yield stream.write_async (desktop_file.data);
|
||||
// Create a launcher/ menu
|
||||
#if HAVE_WIN32
|
||||
Midori.Sokoke.create_win32_desktop_lnk (prefix, filename, uri);
|
||||
#else
|
||||
var data_dir = File.new_for_path (Midori.Paths.get_user_data_dir ());
|
||||
var desktop_dir = data_dir.get_child ("applications");
|
||||
try {
|
||||
desktop_dir.make_directory_with_parents (null);
|
||||
} catch (IOError.EXISTS exist_error) {
|
||||
/* It's no error if the folder already exists */
|
||||
}
|
||||
|
||||
yield file.copy_async (desktop_dir.get_child (filename + ".desktop"),
|
||||
GLib.FileCopyFlags.NONE);
|
||||
#endif
|
||||
if (proxy != null) {
|
||||
var browser = proxy.get_toplevel () as Midori.Browser;
|
||||
browser.send_notification (_("Launcher created"),
|
||||
_("You can now run <b>%s</b> from your launcher or menu").printf (name));
|
||||
}
|
||||
}
|
||||
catch (Error error) {
|
||||
warning (_("Failed to create new launcher (%s): %s"), file.get_path (), error.message);
|
||||
if (proxy != null) {
|
||||
var browser = proxy.get_toplevel () as Midori.Browser;
|
||||
browser.send_notification (_("Error creating launcher"),
|
||||
_("Failed to create new launcher (%s): %s").printf (file.get_path (), error.message));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -52,18 +167,20 @@ namespace Apps {
|
|||
}
|
||||
|
||||
bool init (GLib.Cancellable? cancellable) throws GLib.Error {
|
||||
if (!file.get_basename ().has_suffix (".desktop"))
|
||||
return false;
|
||||
|
||||
var keyfile = new GLib.KeyFile ();
|
||||
keyfile.load_from_file (file.get_path (), GLib.KeyFileFlags.NONE);
|
||||
try {
|
||||
keyfile.load_from_file (file.get_child ("desc").get_path (), GLib.KeyFileFlags.NONE);
|
||||
} catch (Error desc_error) {
|
||||
throw new FileError.EXIST (_("No file \"desc\" found"));
|
||||
}
|
||||
|
||||
exec = keyfile.get_string ("Desktop Entry", "Exec");
|
||||
if (!exec.has_prefix (EXEC_PREFIX))
|
||||
if (!exec.has_prefix (APP_PREFIX) && !exec.has_prefix (PROFILE_PREFIX))
|
||||
return false;
|
||||
|
||||
name = keyfile.get_string ("Desktop Entry", "Name");
|
||||
icon_name = keyfile.get_string ("Desktop Entry", "Icon");
|
||||
uri = exec.replace (EXEC_PREFIX, "");
|
||||
uri = exec.replace (APP_PREFIX, "").replace (PROFILE_PREFIX, "");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -73,6 +190,8 @@ namespace Apps {
|
|||
Gtk.ListStore store = new Gtk.ListStore (1, typeof (Launcher));
|
||||
Gtk.TreeView treeview;
|
||||
Katze.Array array;
|
||||
GLib.File app_folder;
|
||||
GLib.File profile_folder;
|
||||
|
||||
public unowned string get_stock_id () {
|
||||
return Midori.Stock.WEB_BROWSER;
|
||||
|
@ -85,12 +204,93 @@ namespace Apps {
|
|||
public Gtk.Widget get_toolbar () {
|
||||
if (toolbar == null) {
|
||||
toolbar = new Gtk.Toolbar ();
|
||||
toolbar.set_icon_size (Gtk.IconSize.BUTTON);
|
||||
|
||||
#if !HAVE_WIN32
|
||||
/* FIXME: Profiles are broken on win32 because of no multi instance support */
|
||||
var profile = new Gtk.ToolButton.from_stock (Gtk.STOCK_ADD);
|
||||
profile.label = _("New _Profile");
|
||||
profile.tooltip_text = _("Creates a new, independent profile and a launcher");
|
||||
profile.use_underline = true;
|
||||
profile.is_important = true;
|
||||
profile.show ();
|
||||
profile.clicked.connect (() => {
|
||||
Launcher.create_profile.begin (this);
|
||||
});
|
||||
toolbar.insert (profile, -1);
|
||||
#endif
|
||||
|
||||
var app = new Gtk.ToolButton.from_stock (Gtk.STOCK_ADD);
|
||||
app.label = _("New _App");
|
||||
app.tooltip_text = _("Creates a new app for a specific site");
|
||||
app.use_underline = true;
|
||||
app.is_important = true;
|
||||
app.show ();
|
||||
app.clicked.connect (() => {
|
||||
var view = (get_toplevel () as Midori.Browser).tab as Midori.View;
|
||||
string checksum = Checksum.compute_for_string (ChecksumType.MD5, view.get_display_uri (), -1);
|
||||
Launcher.create.begin (APP_PREFIX, app_folder.get_child (checksum),
|
||||
view.get_display_uri (), view.get_display_title (), this);
|
||||
});
|
||||
toolbar.insert (app, -1);
|
||||
}
|
||||
return toolbar;
|
||||
}
|
||||
|
||||
public Sidebar (Katze.Array array) {
|
||||
void row_activated (Gtk.TreePath path, Gtk.TreeViewColumn column) {
|
||||
Gtk.TreeIter iter;
|
||||
if (store.get_iter (out iter, path)) {
|
||||
Launcher launcher;
|
||||
store.get (iter, 0, out launcher);
|
||||
try {
|
||||
GLib.Process.spawn_command_line_async (launcher.exec);
|
||||
}
|
||||
catch (Error error) {
|
||||
var browser = get_toplevel () as Midori.Browser;
|
||||
browser.send_notification (_("Error launching"), error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool button_released (Gdk.EventButton event) {
|
||||
Gtk.TreePath? path;
|
||||
Gtk.TreeViewColumn column;
|
||||
if (event.button != 1)
|
||||
return false;
|
||||
if (treeview.get_path_at_pos ((int)event.x, (int)event.y, out path, out column, null, null)) {
|
||||
if (path != null) {
|
||||
if (column == treeview.get_column (2)) {
|
||||
Gtk.TreeIter iter;
|
||||
if (store.get_iter (out iter, path)) {
|
||||
Launcher launcher;
|
||||
store.get (iter, 0, out launcher);
|
||||
try {
|
||||
launcher.file.trash (null);
|
||||
store.remove (iter);
|
||||
|
||||
string filename = Midori.Download.clean_filename (launcher.name);
|
||||
#if HAVE_WIN32
|
||||
string lnk_filename = Midori.Sokoke.get_win32_desktop_lnk_path_for_filename (filename);
|
||||
if (Posix.access (lnk_filename, Posix.F_OK) == 0) {
|
||||
var lnk_file = File.new_for_path (lnk_filename);
|
||||
lnk_file.trash ();
|
||||
}
|
||||
#else
|
||||
var data_dir = File.new_for_path (Midori.Paths.get_user_data_dir ());
|
||||
data_dir.get_child ("applications").get_child (filename + ".desktop").trash ();
|
||||
#endif
|
||||
}
|
||||
catch (Error error) {
|
||||
GLib.critical ("Failed to remove launcher (%s): %s", launcher.file.get_path (), error.message);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public Sidebar (Katze.Array array, GLib.File app_folder, GLib.File profile_folder) {
|
||||
Gtk.TreeViewColumn column;
|
||||
|
||||
treeview = new Gtk.TreeView.with_model (store);
|
||||
|
@ -113,6 +313,14 @@ namespace Apps {
|
|||
column.set_cell_data_func (renderer_text, on_render_text);
|
||||
treeview.append_column (column);
|
||||
|
||||
column = new Gtk.TreeViewColumn ();
|
||||
Gtk.CellRendererPixbuf renderer_button = new Gtk.CellRendererPixbuf ();
|
||||
column.pack_start (renderer_button, false);
|
||||
column.set_cell_data_func (renderer_button, on_render_button);
|
||||
treeview.append_column (column);
|
||||
|
||||
treeview.row_activated.connect (row_activated);
|
||||
treeview.button_release_event.connect (button_released);
|
||||
treeview.show ();
|
||||
pack_start (treeview, true, true, 0);
|
||||
|
||||
|
@ -121,6 +329,9 @@ namespace Apps {
|
|||
array.remove_item.connect (launcher_removed);
|
||||
foreach (GLib.Object item in array.get_items ())
|
||||
launcher_added (item);
|
||||
|
||||
this.app_folder = app_folder;
|
||||
this.profile_folder = profile_folder;
|
||||
}
|
||||
|
||||
private int tree_sort_func (Gtk.TreeModel model, Gtk.TreeIter a, Gtk.TreeIter b) {
|
||||
|
@ -145,11 +356,18 @@ namespace Apps {
|
|||
|
||||
Launcher launcher;
|
||||
model.get (iter, 0, out launcher);
|
||||
if (launcher.icon_name != null)
|
||||
|
||||
try {
|
||||
int icon_width = 48, icon_height = 48;
|
||||
Gtk.icon_size_lookup_for_settings (get_settings (),
|
||||
Gtk.IconSize.DIALOG, out icon_width, out icon_height);
|
||||
var pixbuf = new Gdk.Pixbuf.from_file_at_size (launcher.icon_name, icon_width, icon_height);
|
||||
renderer.set ("pixbuf", pixbuf);
|
||||
}
|
||||
catch (Error error) {
|
||||
renderer.set ("icon-name", launcher.icon_name);
|
||||
else
|
||||
renderer.set ("stock-id", Gtk.STOCK_FILE);
|
||||
renderer.set ("stock-size", Gtk.IconSize.BUTTON,
|
||||
}
|
||||
renderer.set ("stock-size", Gtk.IconSize.DIALOG,
|
||||
"xpad", 4);
|
||||
}
|
||||
|
||||
|
@ -163,12 +381,20 @@ namespace Apps {
|
|||
launcher.name, launcher.uri),
|
||||
"ellipsize", Pango.EllipsizeMode.END);
|
||||
}
|
||||
|
||||
void on_render_button (Gtk.CellLayout column, Gtk.CellRenderer renderer,
|
||||
Gtk.TreeModel model, Gtk.TreeIter iter) {
|
||||
|
||||
renderer.set ("stock-id", Gtk.STOCK_DELETE,
|
||||
"stock-size", Gtk.IconSize.MENU);
|
||||
}
|
||||
}
|
||||
|
||||
private class Manager : Midori.Extension {
|
||||
internal Katze.Array array;
|
||||
internal GLib.File app_folder;
|
||||
internal GLib.FileMonitor? monitor;
|
||||
internal GLib.File profile_folder;
|
||||
internal GLib.List<GLib.FileMonitor> monitors;
|
||||
internal GLib.List<Gtk.Widget> widgets;
|
||||
|
||||
void app_changed (GLib.File file, GLib.File? other, GLib.FileMonitorEvent event) {
|
||||
|
@ -188,24 +414,21 @@ namespace Apps {
|
|||
}
|
||||
}
|
||||
catch (Error error) {
|
||||
warning ("Application changed: %s", error.message);
|
||||
warning ("Application changed (%s): %s", file.get_path (), error.message);
|
||||
}
|
||||
}
|
||||
|
||||
async void populate_apps () {
|
||||
var data_dir = File.new_for_path (Midori.Paths.get_user_data_dir ());
|
||||
app_folder = data_dir.get_child ("applications");
|
||||
async void populate_apps (File app_folder) {
|
||||
try {
|
||||
try {
|
||||
app_folder.make_directory_with_parents (null);
|
||||
}
|
||||
catch (IOError folder_error) {
|
||||
if (!(folder_error is IOError.EXISTS))
|
||||
throw folder_error;
|
||||
} catch (IOError.EXISTS exist_error) {
|
||||
/* It's no error if the folder already exists */
|
||||
}
|
||||
|
||||
monitor = app_folder.monitor_directory (0, null);
|
||||
var monitor = app_folder.monitor_directory (0, null);
|
||||
monitor.changed.connect (app_changed);
|
||||
monitors.append (monitor);
|
||||
|
||||
var enumerator = yield app_folder.enumerate_children_async ("standard::name", 0);
|
||||
while (true) {
|
||||
|
@ -213,48 +436,55 @@ namespace Apps {
|
|||
if (files == null)
|
||||
break;
|
||||
foreach (var info in files) {
|
||||
var desktop_file = app_folder.get_child (info.get_name ());
|
||||
var file = app_folder.get_child (info.get_name ());
|
||||
try {
|
||||
var launcher = new Launcher (desktop_file);
|
||||
var launcher = new Launcher (file);
|
||||
if (launcher.init ())
|
||||
array.add_item (launcher);
|
||||
}
|
||||
catch (Error error) {
|
||||
warning ("Failed to parse launcher: %s", error.message);
|
||||
warning ("Failed to parse launcher (%s): %s", file.get_path (), error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Error io_error) {
|
||||
monitor = null;
|
||||
warning ("Failed to list .desktop files (%s): %s",
|
||||
warning ("Failed to list apps (%s): %s",
|
||||
app_folder.get_path (), io_error.message);
|
||||
}
|
||||
}
|
||||
|
||||
void tool_menu_populated (Midori.Browser browser, Gtk.Menu menu) {
|
||||
var menuitem = new Gtk.MenuItem.with_mnemonic (_("Create _Launcher"));
|
||||
menuitem.show ();
|
||||
menu.append (menuitem);
|
||||
menuitem.activate.connect (() => {
|
||||
var view = browser.tab as Midori.View;
|
||||
Launcher.create.begin (app_folder,
|
||||
view.get_display_uri (), view.get_display_title ());
|
||||
});
|
||||
}
|
||||
|
||||
void browser_added (Midori.Browser browser) {
|
||||
var viewable = new Sidebar (array);
|
||||
var accels = new Gtk.AccelGroup ();
|
||||
browser.add_accel_group (accels);
|
||||
var action_group = browser.get_action_group ();
|
||||
|
||||
var action = new Gtk.Action ("CreateLauncher", _("Create _Launcher"),
|
||||
_("Creates a new app for a specific site"), null);
|
||||
action.activate.connect (() => {
|
||||
var view = browser.tab as Midori.View;
|
||||
Launcher.create_app.begin (view.get_display_uri (), view.get_display_title (), view);
|
||||
});
|
||||
action_group.add_action_with_accel (action, "<Ctrl><Shift>A");
|
||||
action.set_accel_group (accels);
|
||||
action.connect_accelerator ();
|
||||
|
||||
var viewable = new Sidebar (array, app_folder, profile_folder);
|
||||
viewable.show ();
|
||||
browser.panel.append_page (viewable);
|
||||
browser.populate_tool_menu.connect (tool_menu_populated);
|
||||
// TODO website context menu
|
||||
widgets.append (viewable);
|
||||
}
|
||||
|
||||
void activated (Midori.App app) {
|
||||
array = new Katze.Array (typeof (Launcher));
|
||||
populate_apps.begin ();
|
||||
monitors = new GLib.List<GLib.FileMonitor> ();
|
||||
app_folder = Launcher.get_app_folder ();
|
||||
populate_apps.begin (app_folder);
|
||||
/* FIXME: Profiles are broken on win32 because of no multi instance support */
|
||||
profile_folder = Launcher.get_profile_folder ();
|
||||
#if !HAVE_WIN32
|
||||
populate_apps.begin (profile_folder);
|
||||
#endif
|
||||
widgets = new GLib.List<Gtk.Widget> ();
|
||||
foreach (var browser in app.get_browsers ())
|
||||
browser_added (browser);
|
||||
|
@ -263,11 +493,19 @@ namespace Apps {
|
|||
|
||||
void deactivated () {
|
||||
var app = get_app ();
|
||||
if (monitor != null)
|
||||
foreach (var monitor in monitors)
|
||||
monitor.changed.disconnect (app_changed);
|
||||
monitors = null;
|
||||
|
||||
app.add_browser.disconnect (browser_added);
|
||||
foreach (var widget in widgets)
|
||||
widget.destroy ();
|
||||
foreach (var browser in app.get_browsers ()) {
|
||||
var action_group = browser.get_action_group ();
|
||||
var action = action_group.get_action ("CreateLauncher");
|
||||
action_group.remove_action (action);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal Manager () {
|
||||
|
@ -286,3 +524,27 @@ public Midori.Extension extension_init () {
|
|||
return new Apps.Manager ();
|
||||
}
|
||||
|
||||
class ExtensionsAppsDesktop : Midori.Test.Job {
|
||||
public static void test () { new ExtensionsAppsDesktop ().run_sync (); }
|
||||
public override async void run (Cancellable cancellable) throws GLib.Error {
|
||||
string uri = "http://example.com";
|
||||
string checksum = Checksum.compute_for_string (ChecksumType.MD5, uri, -1);
|
||||
var apps = Apps.Launcher.get_app_folder ().get_child (checksum);
|
||||
Midori.Paths.remove_path (apps.get_path ());
|
||||
|
||||
var data_dir = File.new_for_path (Midori.Paths.get_user_data_dir ());
|
||||
var desktop_dir = data_dir.get_child ("applications");
|
||||
Midori.Paths.remove_path (desktop_dir.get_child ("Example.desktop").get_path ());
|
||||
|
||||
var folder = yield Apps.Launcher.create_app (uri, "Example", null);
|
||||
var launcher = new Apps.Launcher (folder);
|
||||
launcher.init ();
|
||||
Katze.assert_str_equal (folder.get_path (), launcher.uri, uri);
|
||||
yield Apps.Launcher.create_profile (null);
|
||||
}
|
||||
}
|
||||
|
||||
public void extension_test () {
|
||||
Test.add_func ("/extensions/apps/desktop", ExtensionsAppsDesktop.test);
|
||||
}
|
||||
|
||||
|
|
|
@ -12,58 +12,91 @@
|
|||
|
||||
#include <midori/midori.h>
|
||||
|
||||
static GdkColor
|
||||
view_get_bgcolor_for_hostname (MidoriView* view, gchar* hostname)
|
||||
static void
|
||||
get_foreground_color_for_GdkColor (GdkColor* color,
|
||||
GdkColor* fgcolor)
|
||||
{
|
||||
GdkColor color;
|
||||
GdkPixbuf* icon = midori_view_get_icon (view);
|
||||
if (icon != NULL)
|
||||
gfloat brightness, r, g, b;
|
||||
|
||||
r = color->red / 255;
|
||||
g = color->green / 255;
|
||||
b = color->blue / 255;
|
||||
|
||||
/* For math used see algorithms for converting from rgb to yuv */
|
||||
brightness = 0.299 * r + 0.587 * g + 0.114 * b;
|
||||
|
||||
/* Ensure high contrast by enforcing black/ white text colour. */
|
||||
/* Brigthness (range 0-255) equals value of y from YUV color space. */
|
||||
if (brightness < 128)
|
||||
gdk_color_parse ("white", fgcolor);
|
||||
else
|
||||
gdk_color_parse ("black", fgcolor);
|
||||
}
|
||||
|
||||
static void
|
||||
adjust_brightness (GdkColor* color)
|
||||
{
|
||||
guint dark_grey = 137 * 255;
|
||||
guint adjustment = 78 * 255;
|
||||
guint blue = 39 * 255;
|
||||
guint readjust = 19 * 255;
|
||||
|
||||
if ((color->red < dark_grey)
|
||||
&& (color->green < dark_grey)
|
||||
&& (color->blue < dark_grey))
|
||||
{
|
||||
color->red += adjustment;
|
||||
color->green += adjustment;
|
||||
color->blue += adjustment;
|
||||
}
|
||||
|
||||
if (color->red < blue)
|
||||
color->red = readjust;
|
||||
else
|
||||
color->red -= readjust;
|
||||
|
||||
if (color->blue < blue)
|
||||
color->blue = readjust;
|
||||
else
|
||||
color->blue -= readjust;
|
||||
|
||||
if (color->green < blue)
|
||||
color->green = readjust;
|
||||
else
|
||||
color->green -= readjust;
|
||||
}
|
||||
|
||||
static void
|
||||
view_get_bgcolor_for_favicon (GdkPixbuf* icon,
|
||||
GdkColor* color)
|
||||
{
|
||||
GdkPixbuf* newpix;
|
||||
guchar* pixels;
|
||||
|
||||
newpix = gdk_pixbuf_scale_simple (icon, 1, 1, GDK_INTERP_BILINEAR);
|
||||
pixels = gdk_pixbuf_get_pixels (newpix);
|
||||
color.red = pixels[0] * 255;
|
||||
color.green = pixels[1] * 255;
|
||||
color.blue = pixels[2] * 255;
|
||||
color->red = pixels[0] * 255;
|
||||
color->green = pixels[1] * 255;
|
||||
color->blue = pixels[2] * 255;
|
||||
|
||||
adjust_brightness (color);
|
||||
}
|
||||
else
|
||||
|
||||
static void
|
||||
view_get_bgcolor_for_hostname (gchar* hostname,
|
||||
GdkColor* color)
|
||||
{
|
||||
gchar* hash, *colorstr;
|
||||
|
||||
hash = g_compute_checksum_for_string (G_CHECKSUM_MD5, hostname, 1);
|
||||
colorstr = g_strndup (hash, 6 + 1);
|
||||
colorstr[0] = '#';
|
||||
gdk_color_parse (colorstr, &color);
|
||||
gdk_color_parse (colorstr, color);
|
||||
|
||||
g_free (hash);
|
||||
g_free (colorstr);
|
||||
}
|
||||
|
||||
if ((color.red < 35000)
|
||||
&& (color.green < 35000)
|
||||
&& (color.blue < 35000))
|
||||
{
|
||||
color.red += 20000;
|
||||
color.green += 20000;
|
||||
color.blue += 20000;
|
||||
}
|
||||
|
||||
if (color.red < 10000)
|
||||
color.red = 5000;
|
||||
else
|
||||
color.red -= 5000;
|
||||
if (color.blue < 10000)
|
||||
color.blue = 5000;
|
||||
else
|
||||
color.blue -= 5000;
|
||||
if (color.green < 10000)
|
||||
color.green = 5000;
|
||||
else
|
||||
color.green -= 5000;
|
||||
|
||||
return color;
|
||||
adjust_brightness (color);
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -71,25 +104,28 @@ colorful_tabs_view_notify_uri_cb (MidoriView* view,
|
|||
GParamSpec* pspec,
|
||||
MidoriExtension* extension)
|
||||
{
|
||||
gchar* hostname;
|
||||
GdkColor color;
|
||||
GdkColor fgcolor;
|
||||
const gchar* uri = midori_view_get_display_uri (view);
|
||||
if (!*uri)
|
||||
return;
|
||||
|
||||
if (!midori_uri_is_blank (midori_view_get_display_uri (view))
|
||||
&& (hostname = midori_uri_parse_hostname (midori_view_get_display_uri (view), NULL))
|
||||
&& midori_view_get_icon_uri (view) != NULL)
|
||||
if (!midori_uri_is_blank (uri))
|
||||
{
|
||||
color = view_get_bgcolor_for_hostname (view, hostname);
|
||||
g_free (hostname);
|
||||
/* Ensure high contrast by enforcing black/ white text colour. */
|
||||
if ((color.red < 41000)
|
||||
&& (color.green < 41000)
|
||||
&& (color.blue < 41000))
|
||||
gdk_color_parse ("#fff", &fgcolor);
|
||||
else
|
||||
gdk_color_parse ("#000", &fgcolor);
|
||||
gchar* hostname = midori_uri_parse_hostname (uri, NULL);
|
||||
if (hostname)
|
||||
{
|
||||
GdkColor fgcolor, color;
|
||||
GdkPixbuf* icon = midori_view_get_icon (view);
|
||||
|
||||
if (icon)
|
||||
view_get_bgcolor_for_favicon (icon, &color);
|
||||
else
|
||||
view_get_bgcolor_for_hostname (hostname, &color);
|
||||
|
||||
get_foreground_color_for_GdkColor (&color, &fgcolor);
|
||||
midori_view_set_colors (view, &fgcolor, &color);
|
||||
|
||||
g_free (hostname);
|
||||
}
|
||||
}
|
||||
else
|
||||
midori_view_set_colors (view, NULL, NULL);
|
||||
|
@ -115,7 +151,6 @@ colorful_tabs_deactivate_cb (MidoriExtension* extension,
|
|||
MidoriBrowser* browser)
|
||||
{
|
||||
GList* children;
|
||||
GtkWidget* view;
|
||||
MidoriApp* app = midori_extension_get_app (extension);
|
||||
|
||||
g_signal_handlers_disconnect_by_func (
|
||||
|
@ -170,6 +205,51 @@ colorful_tabs_activate_cb (MidoriExtension* extension,
|
|||
g_object_unref (browsers);
|
||||
}
|
||||
|
||||
void test_colour_for_hostname (void)
|
||||
{
|
||||
GdkColor color;
|
||||
GdkColor fgcolor;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
const gchar* host;
|
||||
const gchar* fgcolor;
|
||||
const gchar* color;
|
||||
} ColorItem;
|
||||
|
||||
static const ColorItem items[] = {
|
||||
{ "www.last.fm", "#ffffffffffff", "#12ed7da312ed" },
|
||||
{ "git.xfce.org", "#ffffffffffff", "#1c424c72e207" },
|
||||
{ "elementaryos.org", "#000000000000", "#50dbac36b43e" },
|
||||
{ "news.ycombinator.com", "#000000000000", "#a5cba6cc5278" },
|
||||
{ "cgit.freedesktop.org", "#000000000000", "#95bb8db37ca2" },
|
||||
{ "get.cm", "#ffffffffffff", "#1c424c72e207" },
|
||||
};
|
||||
|
||||
guint i;
|
||||
for (i = 0; i < G_N_ELEMENTS (items); i++)
|
||||
{
|
||||
view_get_bgcolor_for_hostname ((gchar*)items[i].host, &color);
|
||||
get_foreground_color_for_GdkColor (&color, &fgcolor);
|
||||
|
||||
g_assert_cmpstr (items[i].color, ==, gdk_color_to_string (&color));
|
||||
g_assert_cmpstr (items[i].fgcolor, ==, gdk_color_to_string (&fgcolor));
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
extension_test (void)
|
||||
{
|
||||
#ifndef HAVE_WEBKIT2
|
||||
g_object_set_data (G_OBJECT (webkit_get_default_session ()),
|
||||
"midori-session-initialized", (void*)1);
|
||||
#endif
|
||||
|
||||
/* TODO: Add test which uses favicon codepath */
|
||||
|
||||
g_test_add_func ("/extensions/colorful_tabs/hostname_colour", test_colour_for_hostname);
|
||||
}
|
||||
|
||||
MidoriExtension*
|
||||
extension_init (void)
|
||||
{
|
||||
|
|
|
@ -87,10 +87,6 @@ static void cm_create_toolbar(CookieManagerPage *cmp)
|
|||
GtkToolItem *toolitem;
|
||||
|
||||
priv->toolbar = toolbar = gtk_toolbar_new();
|
||||
gtk_toolbar_set_style(GTK_TOOLBAR(toolbar), GTK_TOOLBAR_BOTH_HORIZ);
|
||||
gtk_toolbar_set_icon_size(GTK_TOOLBAR(toolbar), GTK_ICON_SIZE_BUTTON);
|
||||
gtk_widget_show(toolbar);
|
||||
|
||||
toolitem = gtk_tool_button_new_from_stock(GTK_STOCK_DELETE);
|
||||
gtk_tool_item_set_is_important(toolitem, TRUE);
|
||||
g_signal_connect(toolitem, "clicked", G_CALLBACK(cm_button_delete_clicked_cb), cmp);
|
||||
|
@ -167,7 +163,7 @@ static void cookie_manager_page_cookies_changed_cb(CookieManager *cm, CookieMana
|
|||
g_object_unref(priv->filter);
|
||||
|
||||
/* if a filter is set, apply it again but ignore the place holder text */
|
||||
if (!g_object_get_data (G_OBJECT (priv->filter_entry), "sokoke_has_default"))
|
||||
if (!g_object_get_data (G_OBJECT (priv->filter_entry), "sokoke_showing_default"))
|
||||
{
|
||||
filter_text = gtk_entry_get_text(GTK_ENTRY(priv->filter_entry));
|
||||
if (*filter_text != '\0')
|
||||
|
@ -579,7 +575,7 @@ static void cm_button_delete_all_clicked_cb(GtkToolButton *button, CookieManager
|
|||
if (toplevel != NULL)
|
||||
gtk_window_set_icon_name(GTK_WINDOW(dialog), gtk_window_get_icon_name(GTK_WINDOW(toplevel)));
|
||||
|
||||
if (!g_object_get_data (G_OBJECT (priv->filter_entry), "sokoke_has_default"))
|
||||
if (!g_object_get_data (G_OBJECT (priv->filter_entry), "sokoke_showing_default"))
|
||||
{
|
||||
filter_text = gtk_entry_get_text(GTK_ENTRY(priv->filter_entry));
|
||||
if (*filter_text != '\0')
|
||||
|
@ -664,17 +660,9 @@ static gchar *cm_get_cookie_description_text(SoupCookie *cookie)
|
|||
if (cookie->expires != NULL)
|
||||
{
|
||||
time_t expiration_time = soup_date_to_time_t(cookie->expires);
|
||||
#if GLIB_CHECK_VERSION (2, 26, 0)
|
||||
GDateTime* date = g_date_time_new_from_unix_local(expiration_time);
|
||||
expires = g_date_time_format(date, "%c");
|
||||
g_date_time_unref(date);
|
||||
#else
|
||||
static gchar date_fmt[512];
|
||||
const struct tm *tm = localtime(&expiration_time);
|
||||
/* Some GCC versions falsely complain about "%c" */
|
||||
strftime(date_fmt, sizeof(date_fmt), "%c", tm);
|
||||
expires = g_strdup(date_fmt);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
expires = g_strdup(_("At the end of the session"));
|
||||
|
@ -820,7 +808,7 @@ static void cm_filter_entry_changed_cb(GtkEditable *editable, CookieManagerPage
|
|||
if (priv->ignore_changed_filter)
|
||||
return;
|
||||
|
||||
if (!g_object_get_data (G_OBJECT (editable), "sokoke_has_default"))
|
||||
if (!g_object_get_data (G_OBJECT (editable), "sokoke_showing_default"))
|
||||
text = gtk_entry_get_text(GTK_ENTRY(editable));
|
||||
else
|
||||
text = NULL;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
#include "config.h"
|
||||
#include <midori/midori.h>
|
||||
#include "katze/katze.h"
|
||||
#include <libsoup/soup-cookie-jar-sqlite.h>
|
||||
|
||||
#include "cookie-manager.h"
|
||||
#include "cookie-manager-page.h"
|
||||
|
@ -259,6 +260,7 @@ static void cookie_manager_finalize(GObject *object)
|
|||
|
||||
g_object_unref(priv->store);
|
||||
g_free(priv->filter_text);
|
||||
g_object_unref(priv->jar);
|
||||
|
||||
G_OBJECT_CLASS(cookie_manager_parent_class)->finalize(object);
|
||||
}
|
||||
|
@ -267,7 +269,6 @@ static void cookie_manager_finalize(GObject *object)
|
|||
static void cookie_manager_init(CookieManager *self)
|
||||
{
|
||||
CookieManagerPrivate *priv;
|
||||
SoupSession *session;
|
||||
|
||||
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
|
||||
COOKIE_MANAGER_TYPE, CookieManagerPrivate);
|
||||
|
@ -279,8 +280,15 @@ static void cookie_manager_init(CookieManager *self)
|
|||
COOKIE_MANAGER_COL_NAME, GTK_SORT_ASCENDING);
|
||||
|
||||
/* setup soup */
|
||||
session = webkit_get_default_session();
|
||||
#ifdef HAVE_WEBKIT2
|
||||
gchar *filename = midori_paths_get_config_filename_for_writing ("cookies.db");
|
||||
priv->jar = soup_cookie_jar_sqlite_new (filename, FALSE);
|
||||
g_free(filename);
|
||||
#else
|
||||
SoupSession *session = webkit_get_default_session();
|
||||
priv->jar = SOUP_COOKIE_JAR(soup_session_get_feature(session, soup_cookie_jar_get_type()));
|
||||
g_object_ref(priv->jar);
|
||||
#endif
|
||||
g_signal_connect(priv->jar, "changed", G_CALLBACK(cookie_manager_jar_changed_cb), self);
|
||||
|
||||
cookie_manager_refresh_store(self);
|
||||
|
|
|
@ -43,16 +43,17 @@ struct _CookiePermissionManagerPreferencesWindowPrivate
|
|||
GtkListStore *listStore;
|
||||
GtkWidget *list;
|
||||
GtkTreeSelection *listSelection;
|
||||
GtkWidget *editingCombo;
|
||||
GtkWidget *deleteButton;
|
||||
GtkWidget *deleteAllButton;
|
||||
GtkWidget *askForUnknownPolicyCheckbox;
|
||||
GtkWidget *unknownPolicyCombo;
|
||||
GtkWidget *addDomainEntry;
|
||||
GtkWidget *addDomainPolicyCombo;
|
||||
GtkWidget *addDomainButton;
|
||||
|
||||
gint signalManagerChangedDatabaseID;
|
||||
gint signalManagerAskForUnknownPolicyID;
|
||||
gint signalAskForUnknownPolicyID;
|
||||
gint signalManagerUnknownPolicyID;
|
||||
gint signalUnknownPolicyID;
|
||||
};
|
||||
|
||||
enum
|
||||
|
@ -314,35 +315,130 @@ static void _cookie_permission_manager_preferences_window_manager_database_chang
|
|||
return;
|
||||
}
|
||||
|
||||
/* Ask-for-unknown-policy in manager changed or check-box changed */
|
||||
static void _cookie_permission_manager_preferences_window_manager_ask_for_unknown_policy_changed(CookiePermissionManagerPreferencesWindow *self,
|
||||
/* unknown-policy in manager changed or drop-down changed */
|
||||
static void _cookie_permission_manager_preferences_window_manager_unknown_policy_changed(CookiePermissionManagerPreferencesWindow *self,
|
||||
GParamSpec *inSpec,
|
||||
gpointer inUserData)
|
||||
{
|
||||
CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
|
||||
CookiePermissionManager *manager=COOKIE_PERMISSION_MANAGER(inUserData);
|
||||
gboolean doAsk;
|
||||
CookiePermissionManagerPolicy policy;
|
||||
|
||||
/* Get new ask-for-unknown-policy value */
|
||||
g_object_get(manager, "ask-for-unknown-policy", &doAsk, NULL);
|
||||
/* Get new unknown-policy value */
|
||||
g_object_get(manager, "unknown-policy", &policy, NULL);
|
||||
|
||||
/* Set toogle in widget (but block signal for toggle) */
|
||||
g_signal_handler_block(priv->askForUnknownPolicyCheckbox, priv->signalAskForUnknownPolicyID);
|
||||
gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(priv->askForUnknownPolicyCheckbox), doAsk);
|
||||
g_signal_handler_unblock(priv->askForUnknownPolicyCheckbox, priv->signalAskForUnknownPolicyID);
|
||||
/* Set value in combobox (blocking signal to avoid loops) */
|
||||
g_signal_handler_block(priv->unknownPolicyCombo, priv->signalUnknownPolicyID);
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(priv->unknownPolicyCombo), policy);
|
||||
g_signal_handler_unblock(priv->unknownPolicyCombo, priv->signalUnknownPolicyID);
|
||||
}
|
||||
|
||||
static void _cookie_permission_manager_preferences_window_ask_for_unknown_policy_changed(CookiePermissionManagerPreferencesWindow *self,
|
||||
static void _cookie_permission_manager_preferences_window_unknown_policy_changed(CookiePermissionManagerPreferencesWindow *self,
|
||||
gpointer *inUserData)
|
||||
{
|
||||
CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
|
||||
gboolean doAsk;
|
||||
CookiePermissionManagerPolicy policy;
|
||||
GtkTreeIter policyIter;
|
||||
|
||||
if(!gtk_combo_box_get_active_iter(GTK_COMBO_BOX(priv->unknownPolicyCombo), &policyIter))
|
||||
return;
|
||||
|
||||
gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(priv->unknownPolicyCombo)),
|
||||
&policyIter,
|
||||
0, &policy,
|
||||
-1);
|
||||
|
||||
/* Get toogle state of widget (but block signal for manager) and set in manager */
|
||||
g_signal_handler_block(priv->manager, priv->signalManagerAskForUnknownPolicyID);
|
||||
doAsk=gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(priv->askForUnknownPolicyCheckbox));
|
||||
g_object_set(priv->manager, "ask-for-unknown-policy", doAsk, NULL);
|
||||
g_signal_handler_unblock(priv->manager, priv->signalManagerAskForUnknownPolicyID);
|
||||
g_signal_handler_block(priv->manager, priv->signalManagerUnknownPolicyID);
|
||||
g_object_set(priv->manager, "unknown-policy", policy, NULL);
|
||||
g_signal_handler_unblock(priv->manager, priv->signalManagerUnknownPolicyID);
|
||||
}
|
||||
|
||||
static void _cookie_permission_manager_preferences_on_policy_editing_started(CookiePermissionManagerPreferencesWindow *self,
|
||||
GtkCellEditable *editable,
|
||||
gchar *path,
|
||||
gpointer *inUserData)
|
||||
{
|
||||
CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
|
||||
|
||||
priv->editingCombo=NULL;
|
||||
|
||||
if(!GTK_IS_COMBO_BOX(editable)) return;
|
||||
|
||||
priv->editingCombo=GTK_WIDGET(editable);
|
||||
}
|
||||
|
||||
static void _cookie_permission_manager_preferences_on_policy_editing_canceled(CookiePermissionManagerPreferencesWindow *self,
|
||||
gpointer *inUserData)
|
||||
{
|
||||
CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
|
||||
|
||||
priv->editingCombo=NULL;
|
||||
}
|
||||
|
||||
static void _cookie_permission_manager_preferences_on_policy_edited(CookiePermissionManagerPreferencesWindow *self,
|
||||
gchar *path,
|
||||
gchar *newText,
|
||||
gpointer *inUserData)
|
||||
{
|
||||
CookiePermissionManagerPreferencesWindowPrivate *priv=self->priv;
|
||||
gchar *domain;
|
||||
GtkTreeIter iter;
|
||||
GtkTreeIter policyIter;
|
||||
|
||||
g_return_if_fail(priv->database);
|
||||
|
||||
if (priv->editingCombo == NULL) return;
|
||||
|
||||
gtk_tree_model_get_iter_from_string(GTK_TREE_MODEL(priv->listStore), &iter, path);
|
||||
|
||||
gtk_tree_model_get(GTK_TREE_MODEL(priv->listStore),
|
||||
&iter,
|
||||
DOMAIN_COLUMN, &domain,
|
||||
-1);
|
||||
|
||||
/* Get policy from combo box */
|
||||
if(gtk_combo_box_get_active_iter(GTK_COMBO_BOX(priv->editingCombo), &policyIter))
|
||||
{
|
||||
gchar *sql;
|
||||
gchar *error=NULL;
|
||||
gint success;
|
||||
gint policy;
|
||||
gchar *policyName;
|
||||
|
||||
/* Get policy value to set for domain */
|
||||
gtk_tree_model_get(gtk_combo_box_get_model(GTK_COMBO_BOX(priv->editingCombo)),
|
||||
&policyIter,
|
||||
0, &policy,
|
||||
1, &policyName,
|
||||
-1);
|
||||
|
||||
g_return_if_fail(g_strcmp0(policyName, newText)==0);
|
||||
|
||||
/* Add domain name and the selected policy to database */
|
||||
sql=sqlite3_mprintf("UPDATE policies SET value = %d WHERE domain = '%q';",
|
||||
policy,
|
||||
domain);
|
||||
success=sqlite3_exec(priv->database, sql, NULL, NULL, &error);
|
||||
|
||||
/* Show error message if any */
|
||||
if(success==SQLITE_OK)
|
||||
{
|
||||
gtk_list_store_set(priv->listStore,
|
||||
&iter,
|
||||
POLICY_COLUMN, newText,
|
||||
-1);
|
||||
}
|
||||
else g_warning(_("SQL fails: %s"), error);
|
||||
|
||||
|
||||
if(error) sqlite3_free(error);
|
||||
|
||||
/* Free allocated resources */
|
||||
sqlite3_free(sql);
|
||||
}
|
||||
|
||||
priv->editingCombo=NULL;
|
||||
}
|
||||
|
||||
/* Selection in list changed */
|
||||
|
@ -500,8 +596,8 @@ static void cookie_permission_manager_preferences_window_finalize(GObject *inObj
|
|||
if(priv->signalManagerChangedDatabaseID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedDatabaseID);
|
||||
priv->signalManagerChangedDatabaseID=0;
|
||||
|
||||
if(priv->signalManagerAskForUnknownPolicyID) g_signal_handler_disconnect(priv->manager, priv->signalManagerAskForUnknownPolicyID);
|
||||
priv->signalManagerAskForUnknownPolicyID=0;
|
||||
if(priv->signalManagerUnknownPolicyID) g_signal_handler_disconnect(priv->manager, priv->signalManagerUnknownPolicyID);
|
||||
priv->signalManagerUnknownPolicyID=0;
|
||||
|
||||
g_object_unref(priv->manager);
|
||||
priv->manager=NULL;
|
||||
|
@ -531,8 +627,8 @@ static void cookie_permission_manager_preferences_window_set_property(GObject *i
|
|||
if(priv->signalManagerChangedDatabaseID) g_signal_handler_disconnect(priv->manager, priv->signalManagerChangedDatabaseID);
|
||||
priv->signalManagerChangedDatabaseID=0;
|
||||
|
||||
if(priv->signalManagerAskForUnknownPolicyID) g_signal_handler_disconnect(priv->manager, priv->signalManagerAskForUnknownPolicyID);
|
||||
priv->signalManagerAskForUnknownPolicyID=0;
|
||||
if(priv->signalManagerUnknownPolicyID) g_signal_handler_disconnect(priv->manager, priv->signalManagerUnknownPolicyID);
|
||||
priv->signalManagerUnknownPolicyID=0;
|
||||
|
||||
g_object_unref(priv->manager);
|
||||
priv->manager=NULL;
|
||||
|
@ -553,12 +649,12 @@ static void cookie_permission_manager_preferences_window_set_property(GObject *i
|
|||
self);
|
||||
_cookie_permission_manager_preferences_window_manager_database_changed(self, NULL, priv->manager);
|
||||
|
||||
priv->signalManagerAskForUnknownPolicyID=
|
||||
priv->signalManagerUnknownPolicyID=
|
||||
g_signal_connect_swapped(priv->manager,
|
||||
"notify::ask-for-unknown-policy",
|
||||
G_CALLBACK(_cookie_permission_manager_preferences_window_manager_ask_for_unknown_policy_changed),
|
||||
"notify::unknown-policy",
|
||||
G_CALLBACK(_cookie_permission_manager_preferences_window_manager_unknown_policy_changed),
|
||||
self);
|
||||
_cookie_permission_manager_preferences_window_manager_ask_for_unknown_policy_changed(self, NULL, priv->manager);
|
||||
_cookie_permission_manager_preferences_window_manager_unknown_policy_changed(self, NULL, priv->manager);
|
||||
}
|
||||
break;
|
||||
|
||||
|
@ -723,7 +819,7 @@ static void cookie_permission_manager_preferences_window_init(CookiePermissionMa
|
|||
gtk_container_add(GTK_CONTAINER(hbox), priv->addDomainButton);
|
||||
g_signal_connect_swapped(priv->addDomainButton, "clicked", G_CALLBACK(_cookie_permission_manager_preferences_on_add_domain_clicked), self);
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 5);
|
||||
|
||||
/* Set up cookie domain list */
|
||||
priv->list=gtk_tree_view_new_with_model(GTK_TREE_MODEL(priv->listStore));
|
||||
|
@ -744,7 +840,11 @@ static void cookie_permission_manager_preferences_window_init(CookiePermissionMa
|
|||
gtk_tree_view_column_set_sort_column_id(column, DOMAIN_COLUMN);
|
||||
gtk_tree_view_append_column(GTK_TREE_VIEW(priv->list), column);
|
||||
|
||||
renderer=gtk_cell_renderer_text_new();
|
||||
renderer=gtk_cell_renderer_combo_new();
|
||||
g_object_set(G_OBJECT(renderer), "model", list, "text-column", 1, "has-entry", false, "editable", true, NULL);
|
||||
g_signal_connect_swapped(renderer, "editing-started", G_CALLBACK(_cookie_permission_manager_preferences_on_policy_editing_started), self);
|
||||
g_signal_connect_swapped(renderer, "editing-canceled", G_CALLBACK(_cookie_permission_manager_preferences_on_policy_editing_canceled), self);
|
||||
g_signal_connect_swapped(renderer, "edited", G_CALLBACK(_cookie_permission_manager_preferences_on_policy_edited), self);
|
||||
column=gtk_tree_view_column_new_with_attributes(_("Policy"),
|
||||
renderer,
|
||||
"text", POLICY_COLUMN,
|
||||
|
@ -780,18 +880,44 @@ static void cookie_permission_manager_preferences_window_init(CookiePermissionMa
|
|||
gtk_container_add(GTK_CONTAINER(hbox), priv->deleteAllButton);
|
||||
g_signal_connect_swapped(priv->deleteAllButton, "clicked", G_CALLBACK(_cookie_permission_manager_preferences_on_delete_all), self);
|
||||
|
||||
gtk_box_pack_start(GTK_BOX(vbox), hbox, TRUE, TRUE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 5);
|
||||
|
||||
/* Add "ask-for-unknown-policy" checkbox */
|
||||
priv->askForUnknownPolicyCheckbox=gtk_check_button_new_with_mnemonic(_("A_sk for policy if unknown for a domain"));
|
||||
priv->signalAskForUnknownPolicyID=g_signal_connect_swapped(priv->askForUnknownPolicyCheckbox,
|
||||
"toggled",
|
||||
G_CALLBACK(_cookie_permission_manager_preferences_window_ask_for_unknown_policy_changed),
|
||||
/* Add "unknown-policy" combo */
|
||||
#ifdef HAVE_GTK3
|
||||
hbox=gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
|
||||
gtk_box_set_homogeneous(GTK_BOX(hbox), FALSE);
|
||||
#else
|
||||
hbox=gtk_hbox_new(FALSE, 0);
|
||||
#endif
|
||||
widget=gtk_label_new(_("Policy for cookies from domains not in the list: "));
|
||||
gtk_container_add(GTK_CONTAINER(hbox), widget);
|
||||
|
||||
list=gtk_list_store_new(2, G_TYPE_INT, G_TYPE_STRING);
|
||||
gtk_list_store_append(list, &listIter);
|
||||
gtk_list_store_set(list, &listIter, 0, COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED, 1, _("Ask for a decision"), -1);
|
||||
gtk_list_store_append(list, &listIter);
|
||||
gtk_list_store_set(list, &listIter, 0, COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT, 1, _("Accept"), -1);
|
||||
gtk_list_store_append(list, &listIter);
|
||||
gtk_list_store_set(list, &listIter, 0, COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT_FOR_SESSION, 1, _("Accept for session"), -1);
|
||||
gtk_list_store_append(list, &listIter);
|
||||
gtk_list_store_set(list, &listIter, 0, COOKIE_PERMISSION_MANAGER_POLICY_BLOCK, 1, _("Block"), -1);
|
||||
|
||||
priv->unknownPolicyCombo=gtk_combo_box_new_with_model(GTK_TREE_MODEL(list));
|
||||
gtk_combo_box_set_active(GTK_COMBO_BOX(priv->unknownPolicyCombo), 0);
|
||||
gtk_container_add(GTK_CONTAINER(hbox), priv->unknownPolicyCombo);
|
||||
|
||||
renderer=gtk_cell_renderer_text_new();
|
||||
gtk_cell_layout_pack_start(GTK_CELL_LAYOUT(priv->unknownPolicyCombo), renderer, TRUE);
|
||||
gtk_cell_layout_add_attribute(GTK_CELL_LAYOUT(priv->unknownPolicyCombo), renderer, "text", 1);
|
||||
|
||||
priv->signalUnknownPolicyID=g_signal_connect_swapped(priv->unknownPolicyCombo,
|
||||
"changed",
|
||||
G_CALLBACK(_cookie_permission_manager_preferences_window_unknown_policy_changed),
|
||||
self);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), priv->askForUnknownPolicyCheckbox, TRUE, TRUE, 5);
|
||||
gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, TRUE, 5);
|
||||
|
||||
/* Finalize setup of content area */
|
||||
gtk_container_add(GTK_CONTAINER(priv->contentArea), vbox);
|
||||
gtk_box_pack_start(GTK_BOX(priv->contentArea), vbox, TRUE, TRUE, 0);
|
||||
}
|
||||
|
||||
/* Implementation: Public API */
|
||||
|
|
|
@ -31,7 +31,7 @@ enum
|
|||
|
||||
PROP_DATABASE,
|
||||
PROP_DATABASE_FILENAME,
|
||||
PROP_ASK_FOR_UNKNOWN_POLICY,
|
||||
PROP_UNKNOWN_POLICY,
|
||||
|
||||
PROP_LAST
|
||||
};
|
||||
|
@ -49,7 +49,7 @@ struct _CookiePermissionManagerPrivate
|
|||
MidoriApp *application;
|
||||
sqlite3 *database;
|
||||
gchar *databaseFilename;
|
||||
gboolean askForUnknownPolicy;
|
||||
CookiePermissionManagerPolicy unknownPolicy;
|
||||
|
||||
/* Cookie jar related */
|
||||
SoupSession *session;
|
||||
|
@ -224,8 +224,9 @@ static void _cookie_permission_manager_open_database(CookiePermissionManager *se
|
|||
|
||||
uri=soup_uri_new(NULL);
|
||||
soup_uri_set_host(uri, domain);
|
||||
soup_uri_set_path(uri, "/");
|
||||
cookies=soup_cookie_jar_get_cookie_list(priv->cookieJar, uri, TRUE);
|
||||
for(cookie=cookies; cookie; cookie->next)
|
||||
for(cookie=cookies; cookie; cookie=cookie->next)
|
||||
{
|
||||
soup_cookie_jar_delete_cookie(priv->cookieJar, (SoupCookie*)cookie->data);
|
||||
}
|
||||
|
@ -294,24 +295,23 @@ static gint _cookie_permission_manager_get_policy(CookiePermissionManager *self,
|
|||
sqlite3_finalize(statement);
|
||||
|
||||
/* Check if policy is undetermined. If it is then check if this policy was set by user.
|
||||
* If it was not set by user check if we should ask user for his decision
|
||||
* If it was not set by user, check what to do.
|
||||
*/
|
||||
if(!priv->askForUnknownPolicy && !foundPolicy)
|
||||
if(!foundPolicy)
|
||||
{
|
||||
switch(soup_cookie_jar_get_accept_policy(priv->cookieJar))
|
||||
/* A SoupCookieJar that doesn't want to accept any cookies should override the user's
|
||||
* choice, in case of e.g. private mode, to err on the side of caution. */
|
||||
SoupCookieJarAcceptPolicy soup_policy=soup_cookie_jar_get_accept_policy(priv->cookieJar);
|
||||
|
||||
if(soup_policy==SOUP_COOKIE_JAR_ACCEPT_ALWAYS || soup_policy==SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY)
|
||||
{
|
||||
case SOUP_COOKIE_JAR_ACCEPT_ALWAYS:
|
||||
case SOUP_COOKIE_JAR_ACCEPT_NO_THIRD_PARTY:
|
||||
policy=COOKIE_PERMISSION_MANAGER_POLICY_ACCEPT;
|
||||
break;
|
||||
|
||||
case SOUP_COOKIE_JAR_ACCEPT_NEVER:
|
||||
policy=COOKIE_PERMISSION_MANAGER_POLICY_BLOCK;
|
||||
break;
|
||||
|
||||
default:
|
||||
policy=priv->unknownPolicy;
|
||||
}
|
||||
else
|
||||
{
|
||||
if(soup_policy!=SOUP_COOKIE_JAR_ACCEPT_NEVER)
|
||||
g_critical(_("Could not determine global cookie policy to set for domain: %s"), domain);
|
||||
break;
|
||||
policy=COOKIE_PERMISSION_MANAGER_POLICY_BLOCK;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -458,10 +458,11 @@ static gint _cookie_permission_manager_ask_for_policy(CookiePermissionManager *s
|
|||
gint numberDomains, numberCookies;
|
||||
GSList *sortedCookies, *cookies;
|
||||
WebKitWebView *webkitView;
|
||||
CookiePermissionManagerModalInfobar modalInfo;
|
||||
CookiePermissionManagerModalInfobar *modalInfo;
|
||||
|
||||
/* Get webkit view of midori view */
|
||||
webkitView=WEBKIT_WEB_VIEW(midori_view_get_web_view(inView));
|
||||
modalInfo=g_new0(CookiePermissionManagerModalInfobar, 1);
|
||||
|
||||
/* Create a copy of cookies and sort them */
|
||||
sortedCookies=_cookie_permission_manager_get_number_domains_and_cookies(self,
|
||||
|
@ -535,16 +536,12 @@ static gint _cookie_permission_manager_ask_for_policy(CookiePermissionManager *s
|
|||
* but I don't want to create an GObject just for a simple struct. So set object
|
||||
* data by our own
|
||||
*/
|
||||
g_object_set_data(G_OBJECT(infobar), "cookie-permission-manager-infobar-data", &modalInfo);
|
||||
g_object_set_data_full(G_OBJECT(infobar), "cookie-permission-manager-infobar-data", modalInfo, (GDestroyNotify)g_free);
|
||||
|
||||
/* FIXME: Find a way to add "details" widget */
|
||||
#ifndef NO_INFOBAR_DETAILS
|
||||
/* Get content area of infobar */
|
||||
#if HAVE_GTK_INFO_BAR
|
||||
contentArea=gtk_info_bar_get_content_area(GTK_INFO_BAR(infobar));
|
||||
#else
|
||||
contentArea=infobar;
|
||||
#endif
|
||||
|
||||
/* Create list and set up columns of list */
|
||||
list=gtk_tree_view_new_with_model(GTK_TREE_MODEL(listStore));
|
||||
|
@ -614,19 +611,19 @@ static gint _cookie_permission_manager_ask_for_policy(CookiePermissionManager *s
|
|||
|
||||
/* Connect signals to quit main loop */
|
||||
g_signal_connect(webkitView, "navigation-policy-decision-requested", G_CALLBACK(_cookie_permission_manager_on_infobar_webview_navigate), infobar);
|
||||
g_signal_connect(infobar, "destroy", G_CALLBACK(_cookie_permission_manager_on_infobar_destroy), &modalInfo);
|
||||
g_signal_connect(infobar, "destroy", G_CALLBACK(_cookie_permission_manager_on_infobar_destroy), modalInfo);
|
||||
|
||||
/* Let info bar be modal and set response to default */
|
||||
modalInfo.response=COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED;
|
||||
modalInfo.mainLoop=g_main_loop_new(NULL, FALSE);
|
||||
modalInfo->response=COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED;
|
||||
modalInfo->mainLoop=g_main_loop_new(NULL, FALSE);
|
||||
|
||||
GDK_THREADS_LEAVE();
|
||||
g_main_loop_run(modalInfo.mainLoop);
|
||||
g_main_loop_run(modalInfo->mainLoop);
|
||||
GDK_THREADS_ENTER();
|
||||
|
||||
g_main_loop_unref(modalInfo.mainLoop);
|
||||
g_main_loop_unref(modalInfo->mainLoop);
|
||||
|
||||
modalInfo.mainLoop=NULL;
|
||||
modalInfo->mainLoop=NULL;
|
||||
|
||||
/* Disconnect signal handler to webkit's web view */
|
||||
g_signal_handlers_disconnect_by_func(webkitView, G_CALLBACK(_cookie_permission_manager_on_infobar_webview_navigate), infobar);
|
||||
|
@ -636,7 +633,7 @@ static gint _cookie_permission_manager_ask_for_policy(CookiePermissionManager *s
|
|||
* updates of database for the same domain. This sorted list is a copy
|
||||
* to avoid a reorder of cookies
|
||||
*/
|
||||
if(modalInfo.response!=COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED)
|
||||
if(modalInfo->response!=COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED)
|
||||
{
|
||||
const gchar *lastDomain=NULL;
|
||||
|
||||
|
@ -657,7 +654,7 @@ static gint _cookie_permission_manager_ask_for_policy(CookiePermissionManager *s
|
|||
|
||||
sql=sqlite3_mprintf("INSERT OR REPLACE INTO policies (domain, value) VALUES ('%q', %d);",
|
||||
cookieDomain,
|
||||
modalInfo.response);
|
||||
modalInfo->response);
|
||||
success=sqlite3_exec(priv->database, sql, NULL, NULL, &error);
|
||||
if(success!=SQLITE_OK) g_warning(_("SQL fails: %s"), error);
|
||||
if(error) sqlite3_free(error);
|
||||
|
@ -672,8 +669,8 @@ static gint _cookie_permission_manager_ask_for_policy(CookiePermissionManager *s
|
|||
g_slist_free(sortedCookies);
|
||||
|
||||
/* Return response */
|
||||
return(modalInfo.response==COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED ?
|
||||
COOKIE_PERMISSION_MANAGER_POLICY_BLOCK : modalInfo.response);
|
||||
return(modalInfo->response==COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED ?
|
||||
COOKIE_PERMISSION_MANAGER_POLICY_BLOCK : modalInfo->response);
|
||||
}
|
||||
|
||||
/* A cookie was changed outside a request (e.g. Javascript) */
|
||||
|
@ -945,8 +942,8 @@ static void cookie_permission_manager_set_property(GObject *inObject,
|
|||
_cookie_permission_manager_on_application_changed(self);
|
||||
break;
|
||||
|
||||
case PROP_ASK_FOR_UNKNOWN_POLICY:
|
||||
cookie_permission_manager_set_ask_for_unknown_policy(self, g_value_get_boolean(inValue));
|
||||
case PROP_UNKNOWN_POLICY:
|
||||
cookie_permission_manager_set_unknown_policy(self, g_value_get_int(inValue));
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -980,8 +977,8 @@ static void cookie_permission_manager_get_property(GObject *inObject,
|
|||
g_value_set_string(outValue, self->priv->databaseFilename);
|
||||
break;
|
||||
|
||||
case PROP_ASK_FOR_UNKNOWN_POLICY:
|
||||
g_value_set_boolean(outValue, self->priv->askForUnknownPolicy);
|
||||
case PROP_UNKNOWN_POLICY:
|
||||
g_value_set_int(outValue, self->priv->unknownPolicy);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
@ -1033,12 +1030,14 @@ static void cookie_permission_manager_class_init(CookiePermissionManagerClass *k
|
|||
NULL,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
CookiePermissionManagerProperties[PROP_ASK_FOR_UNKNOWN_POLICY]=
|
||||
g_param_spec_boolean("ask-for-unknown-policy",
|
||||
_("Ask for unknown policy"),
|
||||
_("If true this extension ask for policy for every unknown domain."
|
||||
"If false this extension uses the global cookie policy set in Midori settings."),
|
||||
TRUE,
|
||||
CookiePermissionManagerProperties[PROP_UNKNOWN_POLICY]=
|
||||
g_param_spec_int("unknown-policy",
|
||||
_("Unknown domain policy"),
|
||||
_("The policy to use for domains not individually configured."
|
||||
" This only acts to further restrict the global cookie policy set in Midori settings."),
|
||||
COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED,
|
||||
COOKIE_PERMISSION_MANAGER_POLICY_BLOCK,
|
||||
COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT);
|
||||
|
||||
g_object_class_install_properties(gobjectClass, PROP_LAST, CookiePermissionManagerProperties);
|
||||
|
@ -1056,7 +1055,7 @@ static void cookie_permission_manager_init(CookiePermissionManager *self)
|
|||
/* Set up default values */
|
||||
priv->database=NULL;
|
||||
priv->databaseFilename=NULL;
|
||||
priv->askForUnknownPolicy=TRUE;
|
||||
priv->unknownPolicy=COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED;
|
||||
|
||||
/* Hijack session's cookie jar to handle cookies requests on our own in HTTP streams
|
||||
* but remember old handlers to restore them on deactivation
|
||||
|
@ -1082,22 +1081,22 @@ CookiePermissionManager* cookie_permission_manager_new(MidoriExtension *inExtens
|
|||
}
|
||||
|
||||
/* Get/set policy to ask for policy if unknown for a domain */
|
||||
gboolean cookie_permission_manager_get_ask_for_unknown_policy(CookiePermissionManager *self)
|
||||
CookiePermissionManagerPolicy cookie_permission_manager_get_unknown_policy(CookiePermissionManager *self)
|
||||
{
|
||||
g_return_val_if_fail(IS_COOKIE_PERMISSION_MANAGER(self), FALSE);
|
||||
g_return_val_if_fail(IS_COOKIE_PERMISSION_MANAGER(self), COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED);
|
||||
|
||||
return(self->priv->askForUnknownPolicy);
|
||||
return(self->priv->unknownPolicy);
|
||||
}
|
||||
|
||||
void cookie_permission_manager_set_ask_for_unknown_policy(CookiePermissionManager *self, gboolean inDoAsk)
|
||||
void cookie_permission_manager_set_unknown_policy(CookiePermissionManager *self, CookiePermissionManagerPolicy inPolicy)
|
||||
{
|
||||
g_return_if_fail(IS_COOKIE_PERMISSION_MANAGER(self));
|
||||
|
||||
if(inDoAsk!=self->priv->askForUnknownPolicy)
|
||||
if(inPolicy!=self->priv->unknownPolicy)
|
||||
{
|
||||
self->priv->askForUnknownPolicy=inDoAsk;
|
||||
midori_extension_set_boolean(self->priv->extension, "ask-for-unknown-policy", inDoAsk);
|
||||
g_object_notify_by_pspec(G_OBJECT(self), CookiePermissionManagerProperties[PROP_ASK_FOR_UNKNOWN_POLICY]);
|
||||
self->priv->unknownPolicy=inPolicy;
|
||||
midori_extension_set_integer(self->priv->extension, "unknown-policy", inPolicy);
|
||||
g_object_notify_by_pspec(G_OBJECT(self), CookiePermissionManagerProperties[PROP_UNKNOWN_POLICY]);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -60,8 +60,8 @@ GType cookie_permission_manager_get_type(void);
|
|||
|
||||
CookiePermissionManager* cookie_permission_manager_new(MidoriExtension *inExtension, MidoriApp *inApp);
|
||||
|
||||
gboolean cookie_permission_manager_get_ask_for_unknown_policy(CookiePermissionManager *self);
|
||||
void cookie_permission_manager_set_ask_for_unknown_policy(CookiePermissionManager *self, gboolean inDoAsk);
|
||||
CookiePermissionManagerPolicy cookie_permission_manager_get_unknown_policy(CookiePermissionManager *self);
|
||||
void cookie_permission_manager_set_unknown_policy(CookiePermissionManager *self, CookiePermissionManagerPolicy inPolicy);
|
||||
|
||||
/* Enumeration */
|
||||
GType cookie_permission_manager_policy_get_type(void) G_GNUC_CONST;
|
||||
|
|
|
@ -21,7 +21,7 @@ static void _cpm_on_activate(MidoriExtension *inExtension, MidoriApp *inApp, gpo
|
|||
g_return_if_fail(cpm==NULL);
|
||||
|
||||
cpm=cookie_permission_manager_new(inExtension, inApp);
|
||||
g_object_set(cpm, "ask-for-unknown-policy", midori_extension_get_boolean(inExtension, "ask-for-unknown-policy"), NULL);
|
||||
g_object_set(cpm, "unknown-policy", midori_extension_get_integer(inExtension, "unknown-policy"), NULL);
|
||||
}
|
||||
|
||||
/* This extension was deactivated */
|
||||
|
@ -65,7 +65,7 @@ MidoriExtension *extension_init(void)
|
|||
"authors", "Stephan Haller <nomad@froevel.de>",
|
||||
NULL);
|
||||
|
||||
midori_extension_install_boolean(extension, "ask-for-unknown-policy", TRUE);
|
||||
midori_extension_install_integer(extension, "unknown-policy", COOKIE_PERMISSION_MANAGER_POLICY_UNDETERMINED);
|
||||
midori_extension_install_boolean(extension, "show-details-when-ask", FALSE);
|
||||
|
||||
g_signal_connect(extension, "activate", G_CALLBACK(_cpm_on_activate), NULL);
|
||||
|
|
|
@ -9,171 +9,19 @@
|
|||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
using Gtk;
|
||||
using Katze;
|
||||
using Midori;
|
||||
|
||||
namespace DelayedLoad {
|
||||
private class PreferencesDialog : Gtk.Dialog {
|
||||
protected Manager dl_manager;
|
||||
protected Scale slider;
|
||||
|
||||
public PreferencesDialog (Manager manager) {
|
||||
this.dl_manager = manager;
|
||||
|
||||
this.title = _("Preferences for %s").printf ( _("Delayed load"));
|
||||
if (this.get_class ().find_property ("has-separator") != null)
|
||||
this.set ("has-separator", false);
|
||||
this.border_width = 5;
|
||||
this.set_modal (true);
|
||||
this.set_default_size (350, 100);
|
||||
this.create_widgets ();
|
||||
|
||||
this.response.connect (response_cb);
|
||||
}
|
||||
|
||||
private void response_cb (Gtk.Dialog source, int response_id) {
|
||||
switch (response_id) {
|
||||
case ResponseType.APPLY:
|
||||
this.dl_manager.set_integer ("delay", (int) (this.slider.get_value () * 1000));
|
||||
this.dl_manager.preferences_changed ();
|
||||
this.destroy ();
|
||||
break;
|
||||
case ResponseType.CANCEL:
|
||||
this.destroy ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void create_widgets () {
|
||||
Label text = new Label (_("Delay in seconds until loading the page:"));
|
||||
#if HAVE_GTK3
|
||||
this.slider = new Scale.with_range (Orientation.HORIZONTAL, 0, 15, 0.1);
|
||||
#else
|
||||
this.slider = new HScale.with_range (0, 15, 0.1);
|
||||
#endif
|
||||
|
||||
int delay = this.dl_manager.get_integer ("delay");
|
||||
if (delay > 0)
|
||||
this.slider.set_value ((float)delay / 1000);
|
||||
|
||||
#if HAVE_GTK3
|
||||
Gtk.Box vbox = get_content_area () as Gtk.Box;
|
||||
vbox.pack_start (text, false, false, 0);
|
||||
vbox.pack_start (this.slider, false, true, 0);
|
||||
#else
|
||||
this.vbox.pack_start (text, false, false, 0);
|
||||
this.vbox.pack_start (this.slider, false, true, 0);
|
||||
#endif
|
||||
|
||||
this.add_button (Gtk.STOCK_CANCEL, ResponseType.CANCEL);
|
||||
this.add_button (Gtk.STOCK_APPLY, ResponseType.APPLY);
|
||||
|
||||
this.show_all ();
|
||||
}
|
||||
}
|
||||
|
||||
private class TabShaker : GLib.Object {
|
||||
public unowned Midori.Browser browser;
|
||||
public GLib.PtrArray tasks;
|
||||
|
||||
public bool reload_tab () {
|
||||
if (tasks.len == 1) {
|
||||
Midori.View? view = browser.tab as Midori.View;
|
||||
Midori.View scheduled_view = tasks.index (0) as Midori.View;
|
||||
if (scheduled_view == view) {
|
||||
Katze.Item item = view.get_proxy_item ();
|
||||
item.ref();
|
||||
|
||||
int64 delay = item.get_meta_integer ("delay");
|
||||
if (delay == Midori.Delay.PENDING_UNDELAY) {
|
||||
view.reload (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
tasks.remove_index (0);
|
||||
return false;
|
||||
}
|
||||
|
||||
public TabShaker (Midori.Browser browser) {
|
||||
this.browser = browser;
|
||||
}
|
||||
|
||||
construct {
|
||||
this.tasks = new GLib.PtrArray ();
|
||||
}
|
||||
}
|
||||
|
||||
private class Manager : Midori.Extension {
|
||||
private int timeout = 0;
|
||||
private bool initialized = false;
|
||||
private HashTable<Midori.Browser, TabShaker> tasks;
|
||||
|
||||
public signal void preferences_changed ();
|
||||
|
||||
private void preferences_changed_cb () {
|
||||
this.timeout = get_integer ("delay");
|
||||
}
|
||||
|
||||
private void show_preferences () {
|
||||
PreferencesDialog dialog = new PreferencesDialog (this);
|
||||
dialog.show ();
|
||||
}
|
||||
|
||||
private void schedule_reload (Midori.Browser browser, Midori.View view) {
|
||||
if (this.timeout == 0)
|
||||
view.reload (true);
|
||||
else {
|
||||
unowned TabShaker shaker = tasks.get (browser);
|
||||
if (shaker != null) {
|
||||
shaker.tasks.add (view);
|
||||
Midori.Timeout.add (this.timeout, shaker.reload_tab);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void tab_changed (Midori.View? old_view, Midori.View? new_view) {
|
||||
if (new_view != null) {
|
||||
Midori.App app = get_app ();
|
||||
Midori.Browser browser = app.browser;
|
||||
|
||||
Katze.Item item = new_view.get_proxy_item ();
|
||||
item.ref();
|
||||
unowned Katze.Item item = new_view.get_proxy_item ();
|
||||
|
||||
int64 delay = item.get_meta_integer ("delay");
|
||||
if (delay == Midori.Delay.PENDING_UNDELAY && new_view.progress < 1.0 && this.initialized) {
|
||||
this.schedule_reload (browser, new_view);
|
||||
if (delay == Midori.Delay.PENDING_UNDELAY && new_view.progress < 1.0) {
|
||||
new_view.reload (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private bool reload_first_tab () {
|
||||
Midori.App app = get_app ();
|
||||
Midori.Browser? browser = app.browser;
|
||||
Midori.View? view = browser.tab as Midori.View;
|
||||
|
||||
if (view != null) {
|
||||
this.initialized = true;
|
||||
Katze.Item item = view.get_proxy_item ();
|
||||
item.ref();
|
||||
|
||||
int64 delay = item.get_meta_integer ("delay");
|
||||
if (delay != Midori.Delay.DELAYED) {
|
||||
if (view.load_status == Midori.LoadStatus.FINISHED) {
|
||||
if (this.timeout != 0)
|
||||
this.tasks.set (browser, new TabShaker (browser));
|
||||
|
||||
if (view.progress < 1.0)
|
||||
this.schedule_reload (browser, view);
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void browser_added (Midori.Browser browser) {
|
||||
browser.switch_tab.connect_after (this.tab_changed);
|
||||
}
|
||||
|
@ -183,17 +31,6 @@ namespace DelayedLoad {
|
|||
}
|
||||
|
||||
public void activated (Midori.App app) {
|
||||
/* FIXME: override behavior without changing the preference */
|
||||
app.settings.load_on_startup = MidoriStartup.DELAYED_PAGES;
|
||||
|
||||
this.preferences_changed ();
|
||||
|
||||
Midori.Browser? focused_browser = app.browser;
|
||||
if (focused_browser == null)
|
||||
Midori.Timeout.add (50, this.reload_first_tab);
|
||||
else
|
||||
this.initialized = true;
|
||||
|
||||
foreach (Midori.Browser browser in app.get_browsers ()) {
|
||||
browser_added (browser);
|
||||
}
|
||||
|
@ -211,17 +48,11 @@ namespace DelayedLoad {
|
|||
internal Manager () {
|
||||
GLib.Object (name: _("Delayed load"),
|
||||
description: _("Delay page load until you actually use the tab."),
|
||||
version: "0.1",
|
||||
version: "0.2",
|
||||
authors: "André Stösel <andre@stoesel.de>");
|
||||
|
||||
install_integer ("delay", 0);
|
||||
|
||||
activate.connect (this.activated);
|
||||
deactivate.connect (this.deactivated);
|
||||
open_preferences.connect (show_preferences);
|
||||
preferences_changed.connect (preferences_changed_cb);
|
||||
|
||||
this.tasks = new HashTable<Midori.Browser, TabShaker> (GLib.direct_hash, GLib.direct_equal);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
270
extensions/devpet.vala
Normal file
270
extensions/devpet.vala
Normal file
|
@ -0,0 +1,270 @@
|
|||
/*
|
||||
Copyright (C) 2013 André Stösel <andre@stoesel.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
Gtk.IconTheme theme;
|
||||
|
||||
namespace DevPet {
|
||||
enum TreeCells {
|
||||
MESSAGE,
|
||||
BACKTRACE,
|
||||
STOCK,
|
||||
COUNT
|
||||
}
|
||||
|
||||
private class DataWindow : Gtk.Window {
|
||||
public string message {get; construct; }
|
||||
public string backtrace {get; construct; }
|
||||
|
||||
private void create_content () {
|
||||
this.title = this.message;
|
||||
this.set_default_size (500, 500);
|
||||
|
||||
Gtk.VBox vbox = new Gtk.VBox (false, 1);
|
||||
this.add (vbox);
|
||||
|
||||
Gtk.TextBuffer message_buffer = new Gtk.TextBuffer (null);
|
||||
message_buffer.set_text (this.message);
|
||||
|
||||
Gtk.TextView message_text_view = new Gtk.TextView.with_buffer (message_buffer);
|
||||
message_text_view.editable = false;
|
||||
|
||||
Gtk.TextBuffer backtrace_buffer = new Gtk.TextBuffer (null);
|
||||
backtrace_buffer.set_text (this.backtrace);
|
||||
|
||||
Gtk.TextView backtrace_text_view = new Gtk.TextView.with_buffer (backtrace_buffer);
|
||||
backtrace_text_view.editable = false;
|
||||
|
||||
Gtk.ScrolledWindow message_scroll = new Gtk.ScrolledWindow (null, null);
|
||||
message_scroll.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
|
||||
message_scroll.add (message_text_view);
|
||||
|
||||
Gtk.ScrolledWindow backtrace_scroll = new Gtk.ScrolledWindow (null, null);
|
||||
backtrace_scroll.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
|
||||
backtrace_scroll.add (backtrace_text_view);
|
||||
|
||||
vbox.pack_start (message_scroll, false, false, 0);
|
||||
vbox.pack_end (backtrace_scroll, true, true, 0);
|
||||
|
||||
this.show_all ();
|
||||
}
|
||||
|
||||
internal DataWindow (string message, string backtrace) {
|
||||
GLib.Object (type: Gtk.WindowType.TOPLEVEL,
|
||||
window_position: Gtk.WindowPosition.CENTER,
|
||||
message: message,
|
||||
backtrace: backtrace);
|
||||
|
||||
this.create_content ();
|
||||
}
|
||||
}
|
||||
|
||||
private class LogWindow : Gtk.Window {
|
||||
private Manager manager;
|
||||
|
||||
private void clear_list () {
|
||||
this.manager.clear_list ();
|
||||
this.destroy ();
|
||||
}
|
||||
|
||||
#if HAVE_EXECINFO_H
|
||||
private void row_activated (Gtk.TreePath path, Gtk.TreeViewColumn column) {
|
||||
Gtk.TreeIter iter;
|
||||
if (this.manager.list_store.get_iter (out iter, path)) {
|
||||
string message;
|
||||
string backtrace;
|
||||
this.manager.list_store.get(iter,
|
||||
TreeCells.MESSAGE, out message,
|
||||
TreeCells.BACKTRACE, out backtrace, -1);
|
||||
|
||||
DataWindow data_window = new DataWindow (message, backtrace);
|
||||
data_window.show ();
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
private void create_content () {
|
||||
this.title = "Midori - DevPet";
|
||||
this.set_default_size (500, 250);
|
||||
this.destroy.connect (this.manager.log_window_closed);
|
||||
|
||||
Gtk.VBox vbox = new Gtk.VBox (false, 1);
|
||||
this.add (vbox);
|
||||
|
||||
#if HAVE_EXECINFO_H
|
||||
Gtk.Label label = new Gtk.Label (_("Double click for more information"));
|
||||
vbox.pack_start (label, false, false, 0);
|
||||
#endif
|
||||
|
||||
Gtk.ScrolledWindow scroll_windows = new Gtk.ScrolledWindow (null, null);
|
||||
scroll_windows.set_policy (Gtk.PolicyType.NEVER , Gtk.PolicyType.AUTOMATIC);
|
||||
scroll_windows.set_shadow_type (Gtk.ShadowType.ETCHED_IN);
|
||||
|
||||
|
||||
Gtk.Button clear = new Gtk.Button.from_stock ("gtk-clear");
|
||||
clear.clicked.connect (this.clear_list);
|
||||
|
||||
vbox.pack_start (scroll_windows, true, true, 0);
|
||||
vbox.pack_start (clear, false, false, 0);
|
||||
|
||||
|
||||
Gtk.TreeView treeview = new Gtk.TreeView.with_model (this.manager.list_store);
|
||||
scroll_windows.add (treeview);
|
||||
|
||||
treeview.insert_column_with_attributes (
|
||||
-1, "Type",
|
||||
new Gtk.CellRendererPixbuf (), "stock-id", TreeCells.STOCK);
|
||||
treeview.insert_column_with_attributes (
|
||||
-1, "Message",
|
||||
new Gtk.CellRendererText (), "text", TreeCells.MESSAGE);
|
||||
|
||||
#if HAVE_EXECINFO_H
|
||||
treeview.row_activated.connect (this.row_activated);
|
||||
#endif
|
||||
|
||||
this.show_all ();
|
||||
}
|
||||
|
||||
internal LogWindow (Manager manager) {
|
||||
GLib.Object (type: Gtk.WindowType.TOPLEVEL,
|
||||
window_position: Gtk.WindowPosition.CENTER);
|
||||
|
||||
this.manager = manager;
|
||||
this.create_content ();
|
||||
}
|
||||
}
|
||||
|
||||
private class Manager : Midori.Extension {
|
||||
public Gtk.ListStore list_store;
|
||||
private Gtk.StatusIcon? trayicon = null;
|
||||
private LogWindow? log_window;
|
||||
private GLib.LogFunc default_log_func;
|
||||
private GLib.LogLevelFlags icon_flag = GLib.LogLevelFlags.LEVEL_DEBUG;
|
||||
|
||||
public void clear_list() {
|
||||
this.icon_flag = GLib.LogLevelFlags.LEVEL_DEBUG;
|
||||
if(this.trayicon != null)
|
||||
this.trayicon.set_visible (false);
|
||||
this.list_store.clear ();
|
||||
}
|
||||
|
||||
public void log_window_closed () {
|
||||
this.log_window = null;
|
||||
}
|
||||
|
||||
private unowned string get_stock_from_log_level (GLib.LogLevelFlags flags) {
|
||||
if ((flags & LogLevelFlags.LEVEL_CRITICAL) == flags || (flags & LogLevelFlags.LEVEL_ERROR) == flags) {
|
||||
return Gtk.Stock.DIALOG_ERROR;
|
||||
} else if ((flags & LogLevelFlags.LEVEL_WARNING) == flags) {
|
||||
return Gtk.Stock.DIALOG_WARNING;
|
||||
}
|
||||
return Gtk.Stock.DIALOG_INFO;
|
||||
}
|
||||
|
||||
private void ensure_trayicon() {
|
||||
if(this.trayicon != null)
|
||||
return;
|
||||
|
||||
this.trayicon = new Gtk.StatusIcon ();
|
||||
this.trayicon.set_tooltip_text ("Midori - DevPet");
|
||||
this.trayicon.activate.connect(this.show_error_log);
|
||||
}
|
||||
|
||||
private void log_handler(string? domain, GLib.LogLevelFlags flags, string message) {
|
||||
Gtk.TreeIter iter;
|
||||
unowned string stock = this.get_stock_from_log_level (flags);
|
||||
|
||||
this.ensure_trayicon();
|
||||
|
||||
if (flags < this.icon_flag) {
|
||||
this.icon_flag = flags;
|
||||
this.trayicon.set_from_stock (stock);
|
||||
}
|
||||
|
||||
#if HAVE_EXECINFO_H
|
||||
string bt = "";
|
||||
void* buffer[100];
|
||||
int num = Linux.backtrace (buffer, 100);
|
||||
/* Upstream bug: https://git.gnome.org/browse/vala/commit/?id=f402af94e8471c8314ee7a312260a776e4d6fbe2 */
|
||||
unowned string[] symbols = Midori.Linux.backtrace_symbols (buffer, num);
|
||||
if (symbols != null) {
|
||||
/* we don't need the first three lines */
|
||||
for (int i = 3; i < num; i++) {
|
||||
bt += "\r\n%s".printf(symbols[i]);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
this.list_store.append (out iter);
|
||||
this.list_store.set (iter,
|
||||
TreeCells.MESSAGE, message,
|
||||
#if HAVE_EXECINFO_H
|
||||
TreeCells.BACKTRACE, bt,
|
||||
#endif
|
||||
TreeCells.STOCK, stock);
|
||||
|
||||
this.trayicon.set_visible (true);
|
||||
}
|
||||
|
||||
private void show_error_log () {
|
||||
if (this.log_window == null) {
|
||||
this.log_window = new LogWindow (this);
|
||||
this.log_window.show ();
|
||||
} else {
|
||||
if (this.log_window.is_active) {
|
||||
this.log_window.hide ();
|
||||
} else {
|
||||
this.log_window.present ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void activated (Midori.App app) {
|
||||
this.default_log_func = GLib.Log.default_handler;
|
||||
GLib.Log.set_default_handler (this.log_handler);
|
||||
if (this.trayicon != null) {
|
||||
int length = 0;
|
||||
this.list_store.foreach((model, path, iter) => {
|
||||
length++;
|
||||
return false;
|
||||
});
|
||||
|
||||
if (length > 0) {
|
||||
this.trayicon.set_visible (true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void deactivated () {
|
||||
if (this.trayicon != null)
|
||||
this.trayicon.set_visible (false);
|
||||
|
||||
GLib.Log.set_default_handler (this.default_log_func);
|
||||
}
|
||||
|
||||
internal Manager () {
|
||||
GLib.Object (name: _("DevPet"),
|
||||
description: _("This extension shows glib error messages in systray."),
|
||||
version: "0.1",
|
||||
authors: "André Stösel <andre@stoesel.de>");
|
||||
|
||||
this.list_store = new Gtk.ListStore (TreeCells.COUNT, typeof(string), typeof(string), typeof (string));
|
||||
|
||||
this.activate.connect (this.activated);
|
||||
this.deactivate.connect (this.deactivated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Midori.Extension extension_init () {
|
||||
theme = Gtk.IconTheme.get_default ();
|
||||
return new DevPet.Manager ();
|
||||
}
|
||||
|
72
extensions/domain-keys.vala
Normal file
72
extensions/domain-keys.vala
Normal file
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
Copyright (C) 2014 James Axl <bilimish@yandex.ru>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace DomainHotkeys {
|
||||
class Manager : Midori.Extension {
|
||||
internal Manager () {
|
||||
GLib.Object (name: _("Domain Hotkeys"),
|
||||
description: _("Add www. and .com/.country_domain and proceed with Ctrl+Enter/Shift+Enter"),
|
||||
version: "0.1" + Midori.VERSION_SUFFIX,
|
||||
authors: "James Axl <bilimish@yandex.ru>");
|
||||
activate.connect (this.activated);
|
||||
deactivate.connect (this.deactivated);
|
||||
}
|
||||
|
||||
bool key_press_event (Midori.LocationAction action, Gdk.EventKey event_key) {
|
||||
if (event_key.keyval == Gdk.keyval_from_name ("Return")) {
|
||||
if ((bool)(event_key.state & Gdk.ModifierType.CONTROL_MASK)) {
|
||||
location_action_submit_uri_with_suffix (action, ".com");
|
||||
return true;
|
||||
} else if((bool)(event_key.state & Gdk.ModifierType.SHIFT_MASK)) {
|
||||
var domain = C_("Domain", ".com");
|
||||
location_action_submit_uri_with_suffix (action, domain);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void location_action_submit_uri_with_suffix (Midori.LocationAction action, string suffix) {
|
||||
var url = action.get_text ();
|
||||
string completed_url = "www." + url + suffix;
|
||||
action.submit_uri (completed_url, false);
|
||||
}
|
||||
|
||||
void browser_added (Midori.Browser browser) {
|
||||
var action_group = browser.get_action_group ();
|
||||
var action = action_group.get_action ("Location") as Midori.LocationAction;
|
||||
action.key_press_event.connect (key_press_event);
|
||||
}
|
||||
|
||||
void activated (Midori.App app) {
|
||||
foreach (var browser in app.get_browsers ())
|
||||
browser_added (browser);
|
||||
app.add_browser.connect (browser_added);
|
||||
}
|
||||
|
||||
void browser_removed (Midori.Browser browser) {
|
||||
var action_group = browser.get_action_group ();
|
||||
var action = action_group.get_action ("Location") as Midori.LocationAction;
|
||||
action.key_press_event.disconnect (key_press_event);
|
||||
}
|
||||
|
||||
void deactivated () {
|
||||
var app = get_app ();
|
||||
app.add_browser.disconnect (browser_added);
|
||||
foreach (var browser in app.get_browsers ())
|
||||
browser_removed (browser);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Midori.Extension extension_init () {
|
||||
return new DomainHotkeys.Manager ();
|
||||
}
|
|
@ -9,12 +9,6 @@
|
|||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
using Gtk;
|
||||
using Soup;
|
||||
using Katze;
|
||||
using Midori;
|
||||
using WebKit;
|
||||
|
||||
namespace EDM {
|
||||
#if !HAVE_WIN32
|
||||
[DBus (name = "net.launchpad.steadyflow.App")]
|
||||
|
@ -33,7 +27,7 @@ namespace EDM {
|
|||
internal Manager manager;
|
||||
|
||||
private class Manager : GLib.Object {
|
||||
private CookieJar cookie_jar;
|
||||
private Soup.CookieJar cookie_jar;
|
||||
private GLib.PtrArray download_managers = new GLib.PtrArray ();
|
||||
|
||||
public bool download_requested (Midori.View view, WebKit.Download download) {
|
||||
|
@ -41,11 +35,16 @@ namespace EDM {
|
|||
|
||||
if (download_type == Midori.DownloadType.SAVE) {
|
||||
var dlReq = new DownloadRequest ();
|
||||
dlReq.uri = download.get_uri ();
|
||||
|
||||
#if HAVE_WEBKIT2
|
||||
dlReq.uri = download.request.get_uri ();
|
||||
weak Soup.MessageHeaders headers = download.request.get_http_headers ();
|
||||
#else
|
||||
dlReq.uri = download.get_uri ();
|
||||
var request = download.get_network_request ();
|
||||
var message = request.get_message ();
|
||||
weak MessageHeaders headers = message.request_headers;
|
||||
weak Soup.MessageHeaders headers = message.request_headers;
|
||||
#endif
|
||||
|
||||
dlReq.auth = headers.get ("Authorization");
|
||||
dlReq.referer = headers.get ("Referer");
|
||||
|
@ -102,8 +101,12 @@ namespace EDM {
|
|||
}
|
||||
|
||||
construct {
|
||||
#if HAVE_WEBKIT2
|
||||
var session= new Session ();
|
||||
#else
|
||||
var session = WebKit.get_default_session ();
|
||||
this.cookie_jar = session.get_feature (typeof (CookieJar)) as CookieJar;
|
||||
#endif
|
||||
this.cookie_jar = session.get_feature (typeof (Soup.CookieJar)) as Soup.CookieJar;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -119,8 +122,8 @@ namespace EDM {
|
|||
public void handle_exception (GLib.Error error) {
|
||||
string ext_name;
|
||||
this.get ("name",out ext_name);
|
||||
var dialog = new MessageDialog (null, DialogFlags.MODAL,
|
||||
MessageType.ERROR, ButtonsType.CLOSE,
|
||||
var dialog = new Gtk.MessageDialog (null, Gtk.DialogFlags.MODAL,
|
||||
Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE,
|
||||
_("An error occurred when attempting to download a file with the following plugin:\n" +
|
||||
"%s\n\n" +
|
||||
"Error:\n%s\n\n" +
|
||||
|
@ -137,32 +140,49 @@ namespace EDM {
|
|||
#if !HAVE_WIN32
|
||||
private class Aria2 : ExternalDownloadManager {
|
||||
public override bool download (DownloadRequest dlReq) {
|
||||
var url = value_array_new ();
|
||||
value_array_insert (url, 0, typeof (string), dlReq.uri);
|
||||
var url = Soup.value_array_new ();
|
||||
Soup.value_array_insert (url, 0, typeof (string), dlReq.uri);
|
||||
|
||||
GLib.HashTable<string, GLib.Value?> options = value_hash_new ();
|
||||
GLib.HashTable<string, GLib.Value?> options = Soup.value_hash_new ();
|
||||
var referer = new GLib.Value (typeof (string));
|
||||
referer.set_string (dlReq.referer);
|
||||
options.insert ("referer", referer);
|
||||
|
||||
var headers = value_array_new ();
|
||||
var headers = Soup.value_array_new ();
|
||||
if (dlReq.cookie_header != null) {
|
||||
value_array_insert (headers, 0, typeof (string), "Cookie: %s".printf(dlReq.cookie_header));
|
||||
Soup.value_array_insert (headers, 0, typeof (string), "Cookie: " + dlReq.cookie_header);
|
||||
}
|
||||
|
||||
if (headers.n_values > 0)
|
||||
options.insert ("header", headers);
|
||||
|
||||
var message = XMLRPC.request_new ("http://127.0.0.1:6800/rpc",
|
||||
var message = Soup.XMLRPC.request_new ("http://127.0.0.1:6800/rpc",
|
||||
"aria2.addUri",
|
||||
typeof (ValueArray), url,
|
||||
typeof(HashTable), options);
|
||||
var session = new SessionSync ();
|
||||
var session = new Soup.SessionSync ();
|
||||
session.send_message (message);
|
||||
|
||||
/* Check if the plug-in actually recieved an reply.
|
||||
* The parse method do not warns us about it. And the exception
|
||||
* never is launched.*/
|
||||
if (message.status_code != 200) {
|
||||
var dialog = new Gtk.MessageDialog (null, Gtk.DialogFlags.MODAL,
|
||||
Gtk.MessageType.ERROR, Gtk.ButtonsType.CLOSE,
|
||||
"%s",
|
||||
_("The plug-in was unable to connect with aria2:\n" +
|
||||
"Please make sure that aria2 is running with rpc enabled ie: aria2c --enable-rpc\n" +
|
||||
"If it's so, check it also is using the port 6800.\n" +
|
||||
"Lastly Check the configuration of your firewall.\n" +
|
||||
"Whitelist aria2 and the port 6800 if they aren't."
|
||||
));
|
||||
dialog.response.connect ((a) => { dialog.destroy (); });
|
||||
dialog.run ();
|
||||
}
|
||||
|
||||
try {
|
||||
Value v;
|
||||
XMLRPC.parse_method_response ((string) message.response_body.flatten ().data, -1, out v);
|
||||
Soup.XMLRPC.parse_method_response ((string) message.response_body.flatten ().data, -1, out v);
|
||||
return true;
|
||||
} catch (Error e) {
|
||||
this.handle_exception (e);
|
||||
|
@ -212,7 +232,7 @@ namespace EDM {
|
|||
#endif
|
||||
|
||||
private class CommandLinePreferences : Gtk.Dialog {
|
||||
protected Entry input;
|
||||
protected Gtk.Entry input;
|
||||
protected CommandLine commandline;
|
||||
|
||||
public CommandLinePreferences(CommandLine cl) {
|
||||
|
@ -234,20 +254,20 @@ namespace EDM {
|
|||
|
||||
private void response_cb (Gtk.Dialog source, int response_id) {
|
||||
switch (response_id) {
|
||||
case ResponseType.APPLY:
|
||||
case Gtk.ResponseType.APPLY:
|
||||
this.commandline.set_string ("commandline", this.input.get_text ());
|
||||
this.commandline.update_description (this.commandline.get_app ());
|
||||
this.destroy ();
|
||||
break;
|
||||
case ResponseType.CANCEL:
|
||||
case Gtk.ResponseType.CANCEL:
|
||||
this.destroy ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void create_widgets () {
|
||||
Label text = new Label (_("Command:"));
|
||||
this.input = new Entry ();
|
||||
Gtk.Label text = new Gtk.Label (_("Command:"));
|
||||
this.input = new Gtk.Entry ();
|
||||
this.input.set_text (this.commandline.get_string ("commandline"));
|
||||
|
||||
|
||||
|
@ -260,8 +280,8 @@ namespace EDM {
|
|||
this.vbox.pack_start (this.input, false, true, 0);
|
||||
#endif
|
||||
|
||||
this.add_button (Gtk.STOCK_CANCEL, ResponseType.CANCEL);
|
||||
this.add_button (Gtk.STOCK_APPLY, ResponseType.APPLY);
|
||||
this.add_button (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL);
|
||||
this.add_button (Gtk.STOCK_APPLY, Gtk.ResponseType.APPLY);
|
||||
|
||||
this.show_all ();
|
||||
}
|
||||
|
@ -273,15 +293,15 @@ namespace EDM {
|
|||
dialog.show ();
|
||||
}
|
||||
|
||||
private string replace_quoted (string context, string replace, string? with) {
|
||||
return context.replace(replace, with != null ? GLib.Shell.quote(with) : "\'\'");
|
||||
}
|
||||
|
||||
public override bool download (DownloadRequest dlReq) {
|
||||
try {
|
||||
string cmd = this.get_string ("commandline");
|
||||
cmd = cmd.replace("{REFERER}", GLib.Shell.quote (dlReq.referer));
|
||||
if (dlReq.cookie_header != null) {
|
||||
cmd = cmd.replace("{COOKIES}", GLib.Shell.quote ("Cookie: " + dlReq.cookie_header));
|
||||
} else {
|
||||
cmd = cmd.replace("{COOKIES}", "\'\'");
|
||||
}
|
||||
cmd = replace_quoted(cmd, "{REFERER}", dlReq.referer);
|
||||
cmd = replace_quoted(cmd, "{COOKIES}", dlReq.cookie_header != null ? "Cookie: " + dlReq.cookie_header : null);
|
||||
cmd = cmd.replace("{URL}", GLib.Shell.quote (dlReq.uri));
|
||||
GLib.Process.spawn_command_line_async (cmd);
|
||||
return true;
|
||||
|
@ -311,7 +331,7 @@ namespace EDM {
|
|||
internal CommandLine () {
|
||||
#if HAVE_WIN32
|
||||
string default_commandline = "\"%s\\FlashGet\\flashget.exe\" {URL}".printf (Environment.get_variable ("ProgramFiles"));
|
||||
#elif HAVE_FREEBSD
|
||||
#elif HAVE_FREEBSD || HAVE_DRAGONFLY
|
||||
string default_commandline = "fetch HTTP_REFERER={REFERER} {URL}";
|
||||
#else
|
||||
string default_commandline = "wget --no-check-certificate --referer={REFERER} --header={COOKIES} {URL}";
|
||||
|
@ -344,4 +364,3 @@ public Katze.Array extension_init () {
|
|||
extensions.add_item (new EDM.CommandLine ());
|
||||
return extensions;
|
||||
}
|
||||
|
||||
|
|
|
@ -359,17 +359,9 @@ feed_panel_cursor_or_row_changed_cb (GtkTreeView* treeview,
|
|||
g_assert (KATZE_IS_ARRAY (parent));
|
||||
if (added)
|
||||
{
|
||||
#if GLIB_CHECK_VERSION (2, 26, 0)
|
||||
GDateTime* date = g_date_time_new_from_unix_local (added);
|
||||
gchar* pretty = g_date_time_format (date, "%c");
|
||||
g_date_time_unref (date);
|
||||
#else
|
||||
static gchar date_fmt[512];
|
||||
const struct tm *tm = localtime (&added);
|
||||
/* Some GCC versions falsely complain about "%c" */
|
||||
strftime (date_fmt, sizeof (date_fmt), "%c", tm);
|
||||
gchar* pretty = g_strdup (date_fmt);
|
||||
#endif
|
||||
|
||||
/* i18n: The local date a feed was last updated */
|
||||
gchar* last_updated = g_strdup_printf (C_("Feed", "Last updated: %s."), pretty);
|
||||
|
@ -448,7 +440,6 @@ feed_panel_open_in_tab_activate_cb (GtkWidget* menuitem,
|
|||
{
|
||||
KatzeItem* item;
|
||||
const gchar* uri;
|
||||
guint n;
|
||||
|
||||
item = (KatzeItem*)g_object_get_data (G_OBJECT (menuitem), "KatzeItem");
|
||||
|
||||
|
@ -675,8 +666,6 @@ feed_panel_get_toolbar (MidoriViewable* viewable)
|
|||
GtkToolItem* toolitem;
|
||||
|
||||
toolbar = gtk_toolbar_new ();
|
||||
gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH_HORIZ);
|
||||
gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_BUTTON);
|
||||
panel->toolbar = toolbar;
|
||||
toolitem = gtk_tool_button_new_from_stock (GTK_STOCK_ADD);
|
||||
gtk_widget_set_tooltip_text (GTK_WIDGET (toolitem), _("Add new feed"));
|
||||
|
|
|
@ -25,7 +25,11 @@ rss_is_valid (FeedParser* fparser)
|
|||
{
|
||||
if ((str = xmlGetProp (node, BAD_CAST "version")))
|
||||
{
|
||||
valid = !xmlStrcmp (str, BAD_CAST "2.0");
|
||||
if (!xmlStrcmp (str, BAD_CAST "2.0") || !xmlStrcmp (str, BAD_CAST "0.92"))
|
||||
valid = TRUE;
|
||||
else
|
||||
valid = FALSE;
|
||||
|
||||
xmlFree (str);
|
||||
|
||||
if (valid)
|
||||
|
|
|
@ -66,6 +66,7 @@ typedef struct
|
|||
KatzeNetRequest* request;
|
||||
} KatzeNetPriv;
|
||||
|
||||
#ifndef HAVE_WEBKIT2
|
||||
static void
|
||||
katze_net_priv_free (KatzeNetPriv* priv)
|
||||
{
|
||||
|
@ -77,41 +78,6 @@ katze_net_priv_free (KatzeNetPriv* priv)
|
|||
g_slice_free (KatzeNetPriv, priv);
|
||||
}
|
||||
|
||||
#if !WEBKIT_CHECK_VERSION (1, 3, 13)
|
||||
gchar*
|
||||
katze_net_get_cached_path (KatzeNet* net,
|
||||
const gchar* uri,
|
||||
const gchar* subfolder)
|
||||
{
|
||||
gchar* checksum;
|
||||
gchar* extension;
|
||||
gchar* cached_filename;
|
||||
gchar* cached_path;
|
||||
|
||||
if (uri == NULL)
|
||||
return NULL;
|
||||
|
||||
checksum = g_compute_checksum_for_string (G_CHECKSUM_MD5, uri, -1);
|
||||
extension = g_strrstr (uri, ".");
|
||||
cached_filename = g_strdup_printf ("%s%s", checksum,
|
||||
extension ? extension : "");
|
||||
g_free (checksum);
|
||||
|
||||
if (subfolder)
|
||||
{
|
||||
gchar* cache_path = g_build_filename (midori_paths_get_cache_dir_for_reading (), subfolder, NULL);
|
||||
katze_mkdir_with_parents (cache_path, 0700);
|
||||
cached_path = g_build_filename (cache_path, cached_filename, NULL);
|
||||
g_free (cache_path);
|
||||
}
|
||||
else
|
||||
cached_path = g_build_filename (midori_paths_get_cache_dir (), cached_filename, NULL);
|
||||
|
||||
g_free (cached_filename);
|
||||
return cached_path;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
katze_net_got_body_cb (SoupMessage* msg,
|
||||
KatzeNetPriv* priv);
|
||||
|
@ -120,7 +86,6 @@ static void
|
|||
katze_net_got_headers_cb (SoupMessage* msg,
|
||||
KatzeNetPriv* priv)
|
||||
{
|
||||
#ifndef HAVE_WEBKIT2
|
||||
KatzeNetRequest* request = priv->request;
|
||||
|
||||
switch (msg->status_code)
|
||||
|
@ -141,7 +106,6 @@ katze_net_got_headers_cb (SoupMessage* msg,
|
|||
g_signal_handlers_disconnect_by_func (msg, katze_net_got_body_cb, priv);
|
||||
soup_session_cancel_message (webkit_get_default_session (), msg, 1);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -167,42 +131,6 @@ katze_net_finished_cb (SoupMessage* msg,
|
|||
katze_net_priv_free (priv);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
katze_net_local_cb (KatzeNetPriv* priv)
|
||||
{
|
||||
KatzeNetRequest* request = priv->request;
|
||||
gchar* filename = g_filename_from_uri (request->uri, NULL, NULL);
|
||||
|
||||
if (!filename || g_access (filename, F_OK) != 0)
|
||||
{
|
||||
request->status = KATZE_NET_NOT_FOUND;
|
||||
if (priv->status_cb)
|
||||
priv->status_cb (request, priv->user_data);
|
||||
}
|
||||
else if (!(priv->status_cb && !priv->status_cb (request, priv->user_data))
|
||||
&& priv->transfer_cb)
|
||||
{
|
||||
gchar* contents = NULL;
|
||||
gsize length;
|
||||
|
||||
request->status = KATZE_NET_VERIFIED;
|
||||
if (!g_file_get_contents (filename, &contents, &length, NULL))
|
||||
{
|
||||
request->status = KATZE_NET_FAILED;
|
||||
}
|
||||
else
|
||||
{
|
||||
request->status = KATZE_NET_DONE;
|
||||
request->data = contents;
|
||||
request->length = length;
|
||||
}
|
||||
priv->transfer_cb (request, priv->user_data);
|
||||
}
|
||||
g_free (filename);
|
||||
katze_net_priv_free (priv);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
static gboolean
|
||||
katze_net_default_cb (KatzeNetPriv* priv)
|
||||
{
|
||||
|
@ -215,6 +143,7 @@ katze_net_default_cb (KatzeNetPriv* priv)
|
|||
katze_net_priv_free (priv);
|
||||
return FALSE;
|
||||
}
|
||||
#endif
|
||||
|
||||
/**
|
||||
* katze_net_load_uri:
|
||||
|
@ -279,9 +208,6 @@ katze_net_load_uri (KatzeNet* net,
|
|||
return;
|
||||
}
|
||||
|
||||
if (g_str_has_prefix (uri, "file://"))
|
||||
g_idle_add ((GSourceFunc)katze_net_local_cb, priv);
|
||||
else
|
||||
g_idle_add ((GSourceFunc)katze_net_default_cb, priv);
|
||||
#endif
|
||||
}
|
|
@ -12,11 +12,6 @@
|
|||
#ifndef __KATZE_NET_H__
|
||||
#define __KATZE_NET_H__
|
||||
|
||||
#ifndef HAVE_WEBKIT2
|
||||
#include <webkit/webkit.h>
|
||||
#else
|
||||
#include <webkit2/webkit2.h>
|
||||
#endif
|
||||
#include "katze-utils.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
@ -71,13 +66,6 @@ katze_net_load_uri (KatzeNet* net,
|
|||
KatzeNetTransferCb transfer_cb,
|
||||
gpointer user_data);
|
||||
|
||||
#if !WEBKIT_CHECK_VERSION (1, 3, 13)
|
||||
gchar*
|
||||
katze_net_get_cached_path (KatzeNet* net,
|
||||
const gchar* uri,
|
||||
const gchar* subfolder);
|
||||
#endif
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __KATZE_NET_H__ */
|
|
@ -13,6 +13,7 @@
|
|||
#include "feed-atom.h"
|
||||
#include "feed-rss.h"
|
||||
|
||||
#include "katze-net.h"
|
||||
#include <midori/midori.h>
|
||||
|
||||
#define EXTENSION_NAME "Feed Panel"
|
||||
|
@ -365,7 +366,7 @@ panel_add_feed_cb (FeedPanel* panel,
|
|||
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
|
||||
gtk_entry_set_text (GTK_ENTRY (entry), "");
|
||||
gtk_box_pack_start (GTK_BOX (hbox), entry, TRUE, TRUE, 0);
|
||||
gtk_container_add (GTK_CONTAINER(gtk_dialog_get_content_area (GTK_DIALOG (dialog))), hbox);
|
||||
gtk_box_pack_start (GTK_BOX(gtk_dialog_get_content_area (GTK_DIALOG (dialog))), hbox, FALSE, TRUE, 0);
|
||||
gtk_widget_show_all (hbox);
|
||||
|
||||
gtk_dialog_set_default_response (GTK_DIALOG (dialog), GTK_RESPONSE_ACCEPT);
|
||||
|
|
94
extensions/flummi.vala
Normal file
94
extensions/flummi.vala
Normal file
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
Copyright (C) 2013 André Stösel <andre@stoesel.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace Flummi {
|
||||
private class Manager : Midori.Extension {
|
||||
private bool bounce () {
|
||||
try {
|
||||
Midori.App app = this.get_app ();
|
||||
Midori.Browser? browser = app.browser;
|
||||
|
||||
if (browser == null || browser.tab == null) {
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
Midori.Database database = new Midori.Database ("flummi.db");
|
||||
unowned Sqlite.Database db = database.db;
|
||||
|
||||
string sqlcmd = "SELECT id, once, command FROM tasks ORDER BY id;";
|
||||
|
||||
Sqlite.Statement stmt;
|
||||
if (db.prepare_v2 (sqlcmd, -1, out stmt, null) != Sqlite.OK) {
|
||||
GLib.critical ("Failed to select from database: %s", db.errmsg ());
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = stmt.step ();
|
||||
if (!(result == Sqlite.DONE || result == Sqlite.ROW)) {
|
||||
GLib.critical ("Failed to select from database: %s", db.errmsg ());
|
||||
return false;
|
||||
}
|
||||
|
||||
Sqlite.Statement del_stmt;
|
||||
sqlcmd = "DELETE FROM `tasks` WHERE id = :task_id;";
|
||||
if (db.prepare_v2 (sqlcmd, -1, out del_stmt, null) != Sqlite.OK) {
|
||||
GLib.critical ("Failed to update database: %s", db.errmsg ());
|
||||
return false;
|
||||
}
|
||||
|
||||
while (result == Sqlite.ROW) {
|
||||
int64 id = stmt.column_int64 (0);
|
||||
int64 once = stmt.column_int64 (1);
|
||||
string command = stmt.column_text (2);
|
||||
|
||||
string[] commands = { command };
|
||||
|
||||
if (!app.send_command (commands)) {
|
||||
GLib.critical ("Command failed: %s", command);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (once > 0) {
|
||||
del_stmt.bind_int64 (del_stmt.bind_parameter_index (":task_id"), id);
|
||||
if (del_stmt.step () != Sqlite.DONE) {
|
||||
GLib.critical ("Failed to delete record %lf.\nError: %s", id, db.errmsg ());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
result = stmt.step ();
|
||||
}
|
||||
} catch (Midori.DatabaseError schema_error) {
|
||||
GLib.error (schema_error.message);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void activated (Midori.App app) {
|
||||
GLib.Idle.add (this.bounce);
|
||||
}
|
||||
|
||||
internal Manager () {
|
||||
GLib.Object (name: _("Flummi"),
|
||||
description: _("This extension provides a task queue for update jobs or recurring events."),
|
||||
version: "0.1",
|
||||
authors: "André Stösel <andre@stoesel.de>");
|
||||
|
||||
this.activate.connect (this.activated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Midori.Extension extension_init () {
|
||||
return new Flummi.Manager ();
|
||||
}
|
|
@ -18,17 +18,12 @@
|
|||
#include <unistd.h>
|
||||
#endif
|
||||
|
||||
#if WEBKIT_CHECK_VERSION (1, 3, 1)
|
||||
#define FORMHISTORY_USE_GDOM 1
|
||||
#else
|
||||
#define FORMHISTORY_USE_JS 1
|
||||
#endif
|
||||
#define MAXPASSSIZE 64
|
||||
|
||||
typedef struct
|
||||
{
|
||||
MidoriDatabase* database;
|
||||
sqlite3* db;
|
||||
#ifdef FORMHISTORY_USE_GDOM
|
||||
WebKitDOMElement* element;
|
||||
int completion_timeout;
|
||||
GtkTreeModel* completion_model;
|
||||
|
@ -36,9 +31,6 @@ typedef struct
|
|||
GtkWidget* popup;
|
||||
gchar* oldkeyword;
|
||||
glong selection_index;
|
||||
#else
|
||||
gchar* jsforms;
|
||||
#endif
|
||||
gchar* master_password;
|
||||
int master_password_canceled;
|
||||
} FormHistoryPriv;
|
||||
|
@ -64,11 +56,9 @@ formhistory_setup_suggestions (WebKitWebView* web_view,
|
|||
JSContextRef js_context,
|
||||
MidoriExtension* extension);
|
||||
|
||||
#ifdef FORMHISTORY_USE_GDOM
|
||||
void
|
||||
formhistory_suggestions_hide_cb (WebKitDOMElement* element,
|
||||
WebKitDOMEvent* dom_event,
|
||||
FormHistoryPriv* priv);
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
|
|
@ -8,7 +8,6 @@
|
|||
version 2.1 of the License, or (at your option) any later version.
|
||||
*/
|
||||
#include "formhistory-frontend.h"
|
||||
#ifdef FORMHISTORY_USE_GDOM
|
||||
#define COMPLETION_DELAY 200
|
||||
|
||||
FormHistoryPriv*
|
||||
|
@ -393,14 +392,12 @@ formhistory_DOMContentLoaded_cb (WebKitDOMElement* window,
|
|||
for (i = 0; i < webkit_dom_node_list_get_length (inputs); i++)
|
||||
{
|
||||
WebKitDOMNode* element = webkit_dom_node_list_item (inputs, i);
|
||||
#if WEBKIT_CHECK_VERSION (1, 6, 1)
|
||||
gchar* autocomplete = webkit_dom_html_input_element_get_autocomplete (
|
||||
WEBKIT_DOM_HTML_INPUT_ELEMENT (element));
|
||||
gboolean off = !g_strcmp0 (autocomplete, "off");
|
||||
g_free (autocomplete);
|
||||
if (off)
|
||||
continue;
|
||||
#endif
|
||||
|
||||
g_object_set_data (G_OBJECT (element), "doc", doc);
|
||||
g_object_set_data (G_OBJECT (element), "webview", web_view);
|
||||
|
@ -460,11 +457,7 @@ formhistory_setup_suggestions (WebKitWebView* web_view,
|
|||
void
|
||||
formhistory_private_destroy (FormHistoryPriv *priv)
|
||||
{
|
||||
if (priv->db)
|
||||
{
|
||||
sqlite3_close (priv->db);
|
||||
priv->db = NULL;
|
||||
}
|
||||
katze_object_assign (priv->database, NULL);
|
||||
katze_assign (priv->oldkeyword, NULL);
|
||||
gtk_widget_destroy (priv->popup);
|
||||
priv->popup = NULL;
|
||||
|
@ -506,9 +499,9 @@ formhistory_construct_popup_gui (FormHistoryPriv* priv)
|
|||
column = gtk_tree_view_column_new_with_attributes ("suggestions", renderer, "text", 0, NULL);
|
||||
gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
|
||||
priv->popup = popup;
|
||||
priv->element = NULL;
|
||||
|
||||
g_signal_connect (treeview, "button-press-event",
|
||||
G_CALLBACK (formhistory_suggestion_selected_cb), priv);
|
||||
return TRUE;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
Copyright (C) 2009-2012 Alexander Butenko <a.butenka@gmail.com>
|
||||
Copyright (C) 2009-2012 Christian Dywan <christian@twotoasts.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
*/
|
||||
#include "formhistory-frontend.h"
|
||||
#ifdef FORMHISTORY_USE_JS
|
||||
|
||||
FormHistoryPriv*
|
||||
formhistory_private_new ()
|
||||
{
|
||||
FormHistoryPriv* priv = g_slice_new (FormHistoryPriv);
|
||||
return priv;
|
||||
}
|
||||
|
||||
gboolean
|
||||
formhistory_construct_popup_gui (FormHistoryPriv* priv)
|
||||
{
|
||||
gchar* autosuggest;
|
||||
gchar* style;
|
||||
guint i;
|
||||
gchar* file;
|
||||
|
||||
file = midori_app_find_res_filename ("autosuggestcontrol.js");
|
||||
if (!g_file_get_contents (file, &autosuggest, NULL, NULL))
|
||||
{
|
||||
g_free (file);
|
||||
return FALSE;
|
||||
}
|
||||
g_strchomp (autosuggest);
|
||||
|
||||
katze_assign (file, midori_app_find_res_filename ("autosuggestcontrol.css"));
|
||||
if (!g_file_get_contents (file, &style, NULL, NULL))
|
||||
{
|
||||
g_free (file);
|
||||
return FALSE;
|
||||
}
|
||||
g_strchomp (style);
|
||||
g_free (file);
|
||||
|
||||
i = 0;
|
||||
while (style[i])
|
||||
{
|
||||
if (style[i] == '\n')
|
||||
style[i] = ' ';
|
||||
i++;
|
||||
}
|
||||
|
||||
priv->jsforms = g_strdup_printf (
|
||||
"%s"
|
||||
"window.addEventListener ('DOMContentLoaded',"
|
||||
"function () {"
|
||||
" if (document.getElementById('formhistory'))"
|
||||
" return;"
|
||||
" if (!initSuggestions ())"
|
||||
" return;"
|
||||
" var mystyle = document.createElement('style');"
|
||||
" mystyle.setAttribute('type', 'text/css');"
|
||||
" mystyle.setAttribute('id', 'formhistory');"
|
||||
" mystyle.appendChild(document.createTextNode('%s'));"
|
||||
" var head = document.getElementsByTagName('head')[0];"
|
||||
" if (head) head.appendChild(mystyle);"
|
||||
"}, true);",
|
||||
autosuggest,
|
||||
style);
|
||||
g_strstrip (priv->jsforms);
|
||||
g_free (style);
|
||||
g_free (autosuggest);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
void
|
||||
formhistory_setup_suggestions (WebKitWebView* web_view,
|
||||
JSContextRef js_context,
|
||||
MidoriExtension* extension)
|
||||
{
|
||||
GString* suggestions;
|
||||
FormHistoryPriv* priv;
|
||||
static sqlite3_stmt* stmt;
|
||||
const char* sqlcmd;
|
||||
const unsigned char* key;
|
||||
const unsigned char* value;
|
||||
|
||||
gint result, pos;
|
||||
|
||||
priv = g_object_get_data (G_OBJECT (extension), "priv");
|
||||
if (!priv->db)
|
||||
return;
|
||||
|
||||
if (!stmt)
|
||||
{
|
||||
sqlcmd = "SELECT DISTINCT group_concat(value,'\",\"'), field FROM forms \
|
||||
GROUP BY field ORDER BY field";
|
||||
sqlite3_prepare_v2 (priv->db, sqlcmd, strlen (sqlcmd) + 1, &stmt, NULL);
|
||||
}
|
||||
result = sqlite3_step (stmt);
|
||||
if (result != SQLITE_ROW)
|
||||
{
|
||||
if (result == SQLITE_ERROR)
|
||||
g_print (_("Failed to select suggestions\n"));
|
||||
sqlite3_reset (stmt);
|
||||
return;
|
||||
}
|
||||
suggestions = g_string_new (
|
||||
"function FormSuggestions(eid) { "
|
||||
"arr = new Array();");
|
||||
|
||||
while (result == SQLITE_ROW)
|
||||
{
|
||||
pos++;
|
||||
value = sqlite3_column_text (stmt, 0);
|
||||
key = sqlite3_column_text (stmt, 1);
|
||||
if (value)
|
||||
{
|
||||
g_string_append_printf (suggestions, " arr[\"%s\"] = [\"%s\"]; ",
|
||||
(gchar*)key, (gchar*)value);
|
||||
}
|
||||
result = sqlite3_step (stmt);
|
||||
}
|
||||
g_string_append (suggestions, "this.suggestions = arr[eid]; }");
|
||||
g_string_append (suggestions, priv->jsforms);
|
||||
sokoke_js_script_eval (js_context, suggestions->str, NULL);
|
||||
g_string_free (suggestions, TRUE);
|
||||
}
|
||||
|
||||
void
|
||||
formhistory_private_destroy (FormHistoryPriv *priv)
|
||||
{
|
||||
if (priv->db)
|
||||
{
|
||||
sqlite3_close (priv->db);
|
||||
priv->db = NULL;
|
||||
}
|
||||
katze_assign (priv->jsforms, NULL);
|
||||
g_slice_free (FormHistoryPriv, priv);
|
||||
}
|
||||
#endif
|
|
@ -117,13 +117,13 @@ formhistory_check_master_password (GtkWidget* parent,
|
|||
label = gtk_label_new (_("Master password required\n"
|
||||
"to open password database"));
|
||||
gtk_box_pack_start (GTK_BOX (hbox), label, TRUE, TRUE, 0);
|
||||
gtk_container_add (GTK_CONTAINER (content_area), hbox);
|
||||
gtk_box_pack_start (GTK_BOX (content_area), hbox, FALSE, TRUE, 0);
|
||||
|
||||
entry = gtk_entry_new ();
|
||||
g_object_set (entry, "truncate-multiline", TRUE, NULL);
|
||||
gtk_entry_set_visibility(GTK_ENTRY (entry),FALSE);
|
||||
gtk_entry_set_activates_default (GTK_ENTRY (entry), TRUE);
|
||||
gtk_container_add (GTK_CONTAINER (content_area), entry);
|
||||
gtk_box_pack_start (GTK_BOX (content_area), entry, FALSE, TRUE, 0);
|
||||
|
||||
gtk_widget_show_all (entry);
|
||||
gtk_widget_show_all (hbox);
|
||||
|
@ -221,9 +221,7 @@ formhistory_navigation_decision_cb (WebKitWebView* web_view,
|
|||
js_context = webkit_web_frame_get_global_context (web_frame);
|
||||
value = sokoke_js_script_eval (js_context, script, NULL);
|
||||
|
||||
#ifdef FORMHISTORY_USE_GDOM
|
||||
formhistory_suggestions_hide_cb (NULL, NULL, priv);
|
||||
#endif
|
||||
if (value && *value)
|
||||
{
|
||||
gchar** inputs = g_strsplit (value, "|||", 0);
|
||||
|
@ -235,7 +233,6 @@ formhistory_navigation_decision_cb (WebKitWebView* web_view,
|
|||
{
|
||||
if (strcmp (parts[2], "password"))
|
||||
formhistory_update_database (priv->db, NULL, parts[0], parts[1]);
|
||||
#if WEBKIT_CHECK_VERSION (1, 3, 8)
|
||||
else
|
||||
{
|
||||
#if 0
|
||||
|
@ -256,7 +253,6 @@ formhistory_navigation_decision_cb (WebKitWebView* web_view,
|
|||
g_object_set_data (G_OBJECT (web_view), "FormHistoryPasswordEntry", entry);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
}
|
||||
g_strfreev (parts);
|
||||
i++;
|
||||
|
@ -287,7 +283,6 @@ formhistory_window_object_cleared_cb (WebKitWebView* web_view,
|
|||
|
||||
formhistory_setup_suggestions (web_view, js_context, extension);
|
||||
|
||||
#if WEBKIT_CHECK_VERSION (1, 3, 8)
|
||||
entry = g_object_get_data (G_OBJECT (web_view), "FormHistoryPasswordEntry");
|
||||
if (entry)
|
||||
{
|
||||
|
@ -301,10 +296,8 @@ formhistory_window_object_cleared_cb (WebKitWebView* web_view,
|
|||
_("Never for this page"), GTK_RESPONSE_CANCEL, NULL);
|
||||
g_object_set_data (G_OBJECT (web_view), "FormHistoryPasswordEntry", NULL);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
#if WEBKIT_CHECK_VERSION (1, 3, 8)
|
||||
static gchar*
|
||||
formhistory_decrypt (const gchar* data,
|
||||
const gchar* password)
|
||||
|
@ -391,7 +384,6 @@ formhistory_frame_loaded_cb (WebKitWebView* web_view,
|
|||
formhistory_fill_login_data (js_context, priv, data);
|
||||
g_free (data);
|
||||
}
|
||||
#endif
|
||||
|
||||
static void
|
||||
formhistory_deactivate_cb (MidoriExtension* extension,
|
||||
|
@ -408,11 +400,8 @@ formhistory_add_tab_cb (MidoriBrowser* browser,
|
|||
G_CALLBACK (formhistory_window_object_cleared_cb), extension);
|
||||
g_signal_connect (web_view, "navigation-policy-decision-requested",
|
||||
G_CALLBACK (formhistory_navigation_decision_cb), extension);
|
||||
|
||||
#if WEBKIT_CHECK_VERSION (1, 3, 8)
|
||||
g_signal_connect (web_view, "onload-event",
|
||||
G_CALLBACK (formhistory_frame_loaded_cb), extension);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -460,10 +449,8 @@ formhistory_deactivate_tab (MidoriView* view,
|
|||
web_view, formhistory_window_object_cleared_cb, extension);
|
||||
g_signal_handlers_disconnect_by_func (
|
||||
web_view, formhistory_navigation_decision_cb, extension);
|
||||
#if WEBKIT_CHECK_VERSION (1, 3, 8)
|
||||
g_signal_handlers_disconnect_by_func (
|
||||
web_view, formhistory_frame_loaded_cb, extension);
|
||||
#endif
|
||||
}
|
||||
|
||||
static void
|
||||
|
@ -502,44 +489,31 @@ static FormHistoryPriv*
|
|||
formhistory_new (const gchar* config_dir)
|
||||
{
|
||||
gchar* filename;
|
||||
sqlite3* db;
|
||||
char* errmsg = NULL, *errmsg2 = NULL;
|
||||
GError* error = NULL;
|
||||
FormHistoryPriv* priv = formhistory_private_new ();
|
||||
priv->master_password = NULL;
|
||||
priv->master_password_canceled = 0;
|
||||
formhistory_construct_popup_gui (priv);
|
||||
|
||||
if (config_dir == NULL)
|
||||
{
|
||||
priv->db = NULL;
|
||||
return priv;
|
||||
}
|
||||
|
||||
filename = g_build_filename (config_dir, "forms.db", NULL);
|
||||
if (sqlite3_open (filename, &db) != SQLITE_OK)
|
||||
{
|
||||
g_warning (_("Failed to open database: %s\n"), sqlite3_errmsg (db));
|
||||
sqlite3_close (db);
|
||||
}
|
||||
priv->database = midori_database_new (filename, &error);
|
||||
g_free (filename);
|
||||
if ((sqlite3_exec (db, "CREATE TABLE IF NOT EXISTS "
|
||||
"forms (domain text, field text, value text)",
|
||||
NULL, NULL, &errmsg) == SQLITE_OK))
|
||||
if (error != NULL)
|
||||
{
|
||||
sqlite3_exec (db,
|
||||
/* "PRAGMA synchronous = OFF; PRAGMA temp_store = MEMORY" */
|
||||
"PRAGMA count_changes = OFF; PRAGMA journal_mode = TRUNCATE;",
|
||||
NULL, NULL, &errmsg);
|
||||
priv->db = db;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (errmsg)
|
||||
{
|
||||
g_critical (_("Failed to execute database statement: %s\n"), errmsg);
|
||||
sqlite3_free (errmsg);
|
||||
if (errmsg2)
|
||||
{
|
||||
g_critical (_("Failed to execute database statement: %s\n"), errmsg2);
|
||||
sqlite3_free (errmsg2);
|
||||
}
|
||||
}
|
||||
sqlite3_close (db);
|
||||
g_critical ("%s", error->message);
|
||||
g_error_free (error);
|
||||
priv->db = NULL;
|
||||
return priv;
|
||||
}
|
||||
|
||||
priv->db = midori_database_get_db (MIDORI_DATABASE (priv->database));
|
||||
g_warn_if_fail (priv->db != NULL);
|
||||
return priv;
|
||||
}
|
||||
|
||||
|
@ -625,7 +599,7 @@ formhistory_preferences_cb (MidoriExtension* extension)
|
|||
gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (checkbox),
|
||||
!midori_extension_get_boolean (extension, "always-load"));
|
||||
g_object_set_data (G_OBJECT (dialog), "always-load-checkbox", checkbox);
|
||||
gtk_container_add (GTK_CONTAINER (content_area), checkbox);
|
||||
gtk_box_pack_start (GTK_BOX (content_area), checkbox, FALSE, TRUE, 0);
|
||||
/* FIXME: Add pref to disable password manager */
|
||||
|
||||
g_signal_connect (dialog,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
Copyright (C) 2010-2011 André Stösel <andre@stoesel.de>
|
||||
Copyright (C) 2010-2013 André Stösel <andre@stoesel.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
|
@ -9,11 +9,6 @@
|
|||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
using Gtk;
|
||||
using Gdk;
|
||||
using WebKit;
|
||||
using Midori;
|
||||
|
||||
namespace HistoryList {
|
||||
enum TabTreeCells {
|
||||
TREE_CELL_PIXBUF,
|
||||
|
@ -50,6 +45,8 @@ namespace HistoryList {
|
|||
Gtk.TreeViewColumn? column;
|
||||
|
||||
this.treeview.get_cursor (out path, out column);
|
||||
if (path == null)
|
||||
return;
|
||||
|
||||
unowned int[] indices = path.get_indices ();
|
||||
int new_index = indices[0] + step;
|
||||
|
@ -66,14 +63,16 @@ namespace HistoryList {
|
|||
|
||||
public abstract void make_update ();
|
||||
public abstract void clean_up ();
|
||||
public abstract void close_tab ();
|
||||
}
|
||||
|
||||
private class TabWindow : HistoryWindow {
|
||||
protected Gtk.HBox? hbox;
|
||||
protected Gtk.VBox? vbox;
|
||||
protected bool is_dirty = false;
|
||||
protected Gtk.ScrolledWindow? scroll_windows;
|
||||
|
||||
protected void store_append_row (GLib.PtrArray list, Gtk.ListStore store, out Gtk.TreeIter iter) {
|
||||
protected void store_append_row (GLib.PtrArray list, Gtk.ListStore store) {
|
||||
for (var i = list.len; i > 0; i--) {
|
||||
Midori.View view = list.index (i - 1) as Midori.View;
|
||||
|
||||
|
@ -82,6 +81,7 @@ namespace HistoryList {
|
|||
|
||||
unowned string title = view.get_display_title ();
|
||||
|
||||
Gtk.TreeIter iter;
|
||||
store.append (out iter);
|
||||
store.set (iter, TabTreeCells.TREE_CELL_PIXBUF, icon,
|
||||
TabTreeCells.TREE_CELL_STRING, title,
|
||||
|
@ -92,11 +92,32 @@ namespace HistoryList {
|
|||
}
|
||||
|
||||
protected virtual void insert_rows (Gtk.ListStore store) {
|
||||
Gtk.TreeIter iter;
|
||||
unowned GLib.PtrArray list = this.browser.get_data<GLib.PtrArray> ("history-list-tab-history");
|
||||
unowned GLib.PtrArray list_new = this.browser.get_data<GLib.PtrArray> ("history-list-tab-history-new");
|
||||
store_append_row (list, store, out iter);
|
||||
store_append_row (list_new, store, out iter);
|
||||
store_append_row (list, store);
|
||||
store_append_row (list_new, store);
|
||||
}
|
||||
|
||||
protected void resize_treeview () {
|
||||
Gtk.Requisition requisition;
|
||||
int height;
|
||||
int max_lines = 10;
|
||||
#if HAVE_GTK3
|
||||
requisition = Gtk.Requisition();
|
||||
this.treeview.get_preferred_size(out requisition, null);
|
||||
#else
|
||||
this.treeview.size_request (out requisition);
|
||||
#endif
|
||||
Gtk.ListStore model = this.treeview.get_model () as Gtk.ListStore;
|
||||
int length = model.iter_n_children(null);
|
||||
if (length > max_lines) {
|
||||
height = requisition.height / length * max_lines + 2;
|
||||
} else {
|
||||
height = requisition.height + 2;
|
||||
}
|
||||
this.scroll_windows.set_size_request (320, height);
|
||||
this.resize (320, height);
|
||||
|
||||
}
|
||||
|
||||
public TabWindow (Midori.Browser browser) {
|
||||
|
@ -107,10 +128,10 @@ namespace HistoryList {
|
|||
|
||||
this.hbox = new Gtk.HBox (false, 1);
|
||||
|
||||
var sw = new Gtk.ScrolledWindow (null, null);
|
||||
sw.set_policy (PolicyType.NEVER , PolicyType.AUTOMATIC);
|
||||
sw.set_shadow_type (ShadowType.ETCHED_IN);
|
||||
this.hbox.pack_start (sw, true, true, 0);
|
||||
this.scroll_windows = new Gtk.ScrolledWindow (null, null);
|
||||
this.scroll_windows.set_policy (Gtk.PolicyType.NEVER , Gtk.PolicyType.AUTOMATIC);
|
||||
this.scroll_windows.set_shadow_type (Gtk.ShadowType.ETCHED_IN);
|
||||
this.hbox.pack_start (this.scroll_windows, true, true, 0);
|
||||
|
||||
var store = new Gtk.ListStore (TabTreeCells.TREE_CELL_COUNT,
|
||||
typeof (Gdk.Pixbuf), typeof (string),
|
||||
|
@ -121,39 +142,23 @@ namespace HistoryList {
|
|||
this.vbox.pack_start (this.hbox, true, true, 0);
|
||||
|
||||
this.treeview = new Gtk.TreeView.with_model (store);
|
||||
sw.add (treeview);
|
||||
this.scroll_windows.add (treeview);
|
||||
|
||||
this.treeview.set_model (store);
|
||||
this.treeview.set ("headers-visible", false);
|
||||
|
||||
this.treeview.insert_column_with_attributes (
|
||||
-1, "Icon",
|
||||
new CellRendererPixbuf (), "pixbuf", TabTreeCells.TREE_CELL_PIXBUF,
|
||||
new Gtk.CellRendererPixbuf (), "pixbuf", TabTreeCells.TREE_CELL_PIXBUF,
|
||||
"cell-background-gdk", TabTreeCells.TREE_CELL_BG);
|
||||
this.treeview.insert_column_with_attributes (
|
||||
-1, "Title",
|
||||
new CellRendererText (), "text", TabTreeCells.TREE_CELL_STRING,
|
||||
new Gtk.CellRendererText (), "text", TabTreeCells.TREE_CELL_STRING,
|
||||
"foreground-gdk", TabTreeCells.TREE_CELL_FG,
|
||||
"cell-background-gdk", TabTreeCells.TREE_CELL_BG);
|
||||
|
||||
this.show_all ();
|
||||
|
||||
Requisition requisition;
|
||||
int height;
|
||||
int max_lines = 10;
|
||||
#if HAVE_GTK3
|
||||
requisition = Requisition();
|
||||
this.treeview.get_preferred_size(out requisition, null);
|
||||
#else
|
||||
this.treeview.size_request (out requisition);
|
||||
#endif
|
||||
int length = store.iter_n_children(null);
|
||||
if (length > max_lines) {
|
||||
height = requisition.height / length * max_lines + 2;
|
||||
} else {
|
||||
height = requisition.height + 2;
|
||||
}
|
||||
sw.set_size_request (320, height);
|
||||
this.resize_treeview ();
|
||||
}
|
||||
|
||||
public override void make_update () {
|
||||
|
@ -163,13 +168,16 @@ namespace HistoryList {
|
|||
Gtk.TreeViewColumn? column;
|
||||
|
||||
this.treeview.get_cursor (out path, out column);
|
||||
if (path == null)
|
||||
return;
|
||||
|
||||
var model = this.treeview.get_model () as Gtk.ListStore;
|
||||
|
||||
Gtk.TreeIter iter;
|
||||
unowned Midori.View? view = null;
|
||||
|
||||
model.get_iter (out iter, path);
|
||||
if (!model.get_iter (out iter, path))
|
||||
return;
|
||||
model.get (iter, TabTreeCells.TREE_CELL_POINTER, out view);
|
||||
this.browser.set ("tab", view);
|
||||
}
|
||||
|
@ -188,6 +196,41 @@ namespace HistoryList {
|
|||
this.is_dirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
public override void close_tab () {
|
||||
Gtk.TreePath? path;
|
||||
Gtk.TreeViewColumn? column;
|
||||
|
||||
this.treeview.get_cursor (out path, out column);
|
||||
|
||||
Gtk.ListStore model = this.treeview.get_model () as Gtk.ListStore;
|
||||
int length = model.iter_n_children(null);
|
||||
|
||||
if (length > 1) {
|
||||
Gtk.TreeIter iter;
|
||||
unowned Midori.View? view = null;
|
||||
|
||||
model.get_iter (out iter, path);
|
||||
model.get (iter, TabTreeCells.TREE_CELL_POINTER, out view);
|
||||
#if !HAVE_GTK3
|
||||
/* removing the selected cursor causes a segfault when using GTK2 */
|
||||
if (path.prev () == false)
|
||||
path.next ();
|
||||
this.treeview.set_cursor (path, column, false);
|
||||
#endif
|
||||
|
||||
/*
|
||||
FixMe: the retrun value of `Gtk.ListStore.remove` should be checked
|
||||
Note: in some cases the return value of `Gtk.ListStore.remove` is wrong
|
||||
*/
|
||||
model.remove (iter);
|
||||
this.browser.close_tab (view);
|
||||
if (length > 2)
|
||||
this.resize_treeview ();
|
||||
else
|
||||
this.hide ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class NewTabWindow : TabWindow {
|
||||
|
@ -195,16 +238,15 @@ namespace HistoryList {
|
|||
protected bool first_step = true;
|
||||
|
||||
protected override void insert_rows (Gtk.ListStore store) {
|
||||
Gtk.TreeIter iter;
|
||||
unowned GLib.PtrArray list = this.browser.get_data<GLib.PtrArray> ("history-list-tab-history-new");
|
||||
store_append_row (list, store, out iter);
|
||||
store_append_row (list, store);
|
||||
|
||||
if ((int)list.len == 0) {
|
||||
this.old_tabs = true;
|
||||
var label = new Gtk.Label (_("There are no unvisited tabs"));
|
||||
this.vbox.pack_start (label, true, true, 0);
|
||||
unowned GLib.PtrArray list_old = this.browser.get_data<GLib.PtrArray> ("history-list-tab-history");
|
||||
store_append_row (list_old, store, out iter);
|
||||
store_append_row (list_old, store);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -234,7 +276,7 @@ namespace HistoryList {
|
|||
|
||||
private class PreferencesDialog : Gtk.Dialog {
|
||||
protected Manager hl_manager;
|
||||
protected ComboBox closing_behavior;
|
||||
protected Gtk.ComboBox closing_behavior;
|
||||
|
||||
public PreferencesDialog (Manager manager) {
|
||||
this.hl_manager = manager;
|
||||
|
@ -252,9 +294,9 @@ namespace HistoryList {
|
|||
|
||||
private void response_cb (Gtk.Dialog source, int response_id) {
|
||||
switch (response_id) {
|
||||
case ResponseType.APPLY:
|
||||
case Gtk.ResponseType.APPLY:
|
||||
int value;
|
||||
TreeIter iter;
|
||||
Gtk.TreeIter iter;
|
||||
|
||||
this.closing_behavior.get_active_iter (out iter);
|
||||
var model = this.closing_behavior.get_model ();
|
||||
|
@ -265,26 +307,26 @@ namespace HistoryList {
|
|||
|
||||
this.destroy ();
|
||||
break;
|
||||
case ResponseType.CANCEL:
|
||||
case Gtk.ResponseType.CANCEL:
|
||||
this.destroy ();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void create_widgets () {
|
||||
ListStore model;
|
||||
TreeIter iter;
|
||||
TreeIter? active_iter = null;
|
||||
Gtk.ListStore model;
|
||||
Gtk.TreeIter iter;
|
||||
Gtk.TreeIter? active_iter = null;
|
||||
|
||||
var table = new Table (1, 2, true);
|
||||
var renderer = new CellRendererText ();
|
||||
var table = new Gtk.Table (1, 2, true);
|
||||
var renderer = new Gtk.CellRendererText ();
|
||||
|
||||
var label = new Label ( _("Tab closing behavior"));
|
||||
var label = new Gtk.Label ( _("Tab closing behavior"));
|
||||
table.attach_defaults (label, 0, 1, 0, 1);
|
||||
|
||||
var tab_closing_behavior = this.hl_manager.get_integer ("TabClosingBehavior");
|
||||
|
||||
model = new ListStore (2, typeof (string), typeof (int));
|
||||
model = new Gtk.ListStore (2, typeof (string), typeof (int));
|
||||
|
||||
model.append (out iter);
|
||||
model.set (iter, TabClosingBehaviorModel.TEXT, _("Do nothing"),
|
||||
|
@ -304,7 +346,7 @@ namespace HistoryList {
|
|||
if (TabClosingBehavior.NEW == tab_closing_behavior)
|
||||
active_iter = iter;
|
||||
|
||||
this.closing_behavior = new ComboBox.with_model (model);
|
||||
this.closing_behavior = new Gtk.ComboBox.with_model (model);
|
||||
this.closing_behavior.set_active_iter (active_iter);
|
||||
this.closing_behavior.pack_start (renderer, true);
|
||||
this.closing_behavior.set_attributes (renderer, "text", 0);
|
||||
|
@ -323,8 +365,8 @@ namespace HistoryList {
|
|||
this.vbox.pack_start (table, false, true, 0);
|
||||
#endif
|
||||
|
||||
this.add_button (Gtk.STOCK_CANCEL, ResponseType.CANCEL);
|
||||
this.add_button (Gtk.STOCK_APPLY, ResponseType.APPLY);
|
||||
this.add_button (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL);
|
||||
this.add_button (Gtk.STOCK_APPLY, Gtk.ResponseType.APPLY);
|
||||
|
||||
this.show_all ();
|
||||
}
|
||||
|
@ -334,6 +376,7 @@ namespace HistoryList {
|
|||
public signal void preferences_changed ();
|
||||
|
||||
protected uint escKeyval;
|
||||
protected uint delKeyval;
|
||||
protected uint modifier_count;
|
||||
protected int closing_behavior;
|
||||
protected HistoryWindow? history_window;
|
||||
|
@ -363,7 +406,7 @@ namespace HistoryList {
|
|||
return false;
|
||||
}
|
||||
|
||||
public bool key_release (Gdk.EventKey event_key, Browser browser) {
|
||||
public bool key_release (Gdk.EventKey event_key, Midori.Browser browser) {
|
||||
if (is_key_a_modifier (event_key)) {
|
||||
this.modifier_count--;
|
||||
}
|
||||
|
@ -378,11 +421,13 @@ namespace HistoryList {
|
|||
}
|
||||
this.history_window.destroy ();
|
||||
this.history_window = null;
|
||||
} else if (event_key.keyval == this.delKeyval) {
|
||||
this.history_window.close_tab ();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public void walk (Gtk.Action action, Browser browser, Type type, int step) {
|
||||
public void walk (Gtk.Action action, Midori.Browser browser, Type type, int step) {
|
||||
Midori.View? view = null;
|
||||
view = browser.get_data<Midori.View?> ("history-list-last-change");
|
||||
if (view != null) {
|
||||
|
@ -417,7 +462,7 @@ namespace HistoryList {
|
|||
hw.walk (step);
|
||||
}
|
||||
|
||||
public void special_function (Gtk.Action action, Browser browser) {
|
||||
public void special_function (Gtk.Action action, Midori.Browser browser) {
|
||||
if (this.history_window != null) {
|
||||
this.ignoreNextChange = true;
|
||||
this.history_window.make_update ();
|
||||
|
@ -578,6 +623,7 @@ namespace HistoryList {
|
|||
foreach (var browser in app.get_browsers ())
|
||||
browser_added (browser);
|
||||
app.add_browser.connect (browser_added);
|
||||
app.remove_browser.connect (browser_removed);
|
||||
}
|
||||
|
||||
void deactivated () {
|
||||
|
@ -585,6 +631,7 @@ namespace HistoryList {
|
|||
foreach (var browser in app.get_browsers ())
|
||||
browser_removed (browser);
|
||||
app.add_browser.disconnect (browser_added);
|
||||
app.remove_browser.disconnect (browser_removed);
|
||||
}
|
||||
|
||||
void show_preferences () {
|
||||
|
@ -607,6 +654,7 @@ namespace HistoryList {
|
|||
}
|
||||
construct {
|
||||
this.escKeyval = Gdk.keyval_from_name ("Escape");
|
||||
this.delKeyval = Gdk.keyval_from_name ("Delete");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -128,8 +128,19 @@ struct MouseGestureNode
|
|||
static guint
|
||||
dist_sqr (guint x1, guint y1, guint x2, guint y2)
|
||||
{
|
||||
guint xdiff = abs(x1 - x2);
|
||||
guint ydiff = abs(y1 - y2);
|
||||
guint xdiff = 0, ydiff = 0;
|
||||
// Remember that x1, x2, y1 and y2 are guint unsigned integers.
|
||||
// Subtracting a greater number from a lower one is undefined.
|
||||
// This guards against that.
|
||||
|
||||
if (x1 > x2)
|
||||
xdiff = x1 - x2;
|
||||
else
|
||||
xdiff = x2 - x1;
|
||||
if (y1 > y2)
|
||||
ydiff = y1 - y2;
|
||||
else
|
||||
ydiff = y2 - y1;
|
||||
return xdiff * xdiff + ydiff * ydiff;
|
||||
}
|
||||
|
||||
|
@ -159,7 +170,7 @@ vector_follows_direction (float angle, float distance, MouseGestureDirection dir
|
|||
return distance < MINLENGTH / 2;
|
||||
|
||||
float dir_angle = get_angle_for_direction (direction);
|
||||
if (fabsf(angle - dir_angle) < DEVIANCE || fabsf(angle - dir_angle + 2 * M_PI) < DEVIANCE)
|
||||
if (fabsf (angle - dir_angle) < DEVIANCE || fabsf (angle - dir_angle + 2 * (float)(M_PI)) < DEVIANCE)
|
||||
return TRUE;
|
||||
|
||||
if(distance < MINLENGTH / 2)
|
||||
|
@ -267,8 +278,8 @@ mouse_gestures_motion_notify_event_cb (GtkWidget* web_view,
|
|||
if (distance >= MINLENGTH)
|
||||
{
|
||||
gesture->strokes[gesture->count] = nearest_direction_for_angle (angle);
|
||||
if(midori_debug ("adblock:match"))
|
||||
g_debug ("detected %s\n", direction_names[gesture->strokes[gesture->count]]);
|
||||
if (midori_debug ("mouse"))
|
||||
g_print ("mouse_gestures detected %s\n", direction_names[gesture->strokes[gesture->count]]);
|
||||
}
|
||||
}
|
||||
else if (!vector_follows_direction (angle, distance, old_direction)
|
||||
|
@ -384,7 +395,7 @@ mouse_gestures_load_config (MidoriExtension* extension)
|
|||
for(i = 0; keys[i]; i++)
|
||||
{
|
||||
gsize n_strokes;
|
||||
int j;
|
||||
guint j;
|
||||
gchar** stroke_strings = g_key_file_get_string_list (keyfile, "gestures", keys[i], &n_strokes,
|
||||
NULL);
|
||||
|
||||
|
|
1
extensions/nojs/README.md
Normal file
1
extensions/nojs/README.md
Normal file
|
@ -0,0 +1 @@
|
|||
This is an extension for Midori web browser. With this extension you can manage which sites are allowed to execute javascript.
|
79
extensions/nojs/main.c
Normal file
79
extensions/nojs/main.c
Normal file
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#include "nojs.h"
|
||||
#include "nojs-preferences.h"
|
||||
|
||||
/* Global instance */
|
||||
NoJS *noJS=NULL;
|
||||
|
||||
/* This extension was activated */
|
||||
static void _nojs_on_activate(MidoriExtension *inExtension, MidoriApp *inApp, gpointer inUserData)
|
||||
{
|
||||
g_return_if_fail(noJS==NULL);
|
||||
|
||||
noJS=nojs_new(inExtension, inApp);
|
||||
nojs_set_policy_for_unknown_domain(noJS, midori_extension_get_integer(inExtension, "unknown-domain-policy"));
|
||||
nojs_set_allow_local_pages(noJS, midori_extension_get_boolean(inExtension, "allow-local-pages"));
|
||||
nojs_set_only_second_level_domain(noJS, midori_extension_get_boolean(inExtension, "only-second-level"));
|
||||
}
|
||||
|
||||
/* This extension was deactivated */
|
||||
static void _nojs_on_deactivate(MidoriExtension *inExtension, gpointer inUserData)
|
||||
{
|
||||
g_return_if_fail(noJS);
|
||||
|
||||
g_object_unref(noJS);
|
||||
noJS=NULL;
|
||||
}
|
||||
|
||||
/* Preferences of this extension should be opened */
|
||||
static void _nojs_on_preferences_response(GtkWidget* inDialog,
|
||||
gint inResponse,
|
||||
gpointer *inUserData)
|
||||
{
|
||||
gtk_widget_destroy(inDialog);
|
||||
}
|
||||
|
||||
static void _nojs_on_open_preferences(MidoriExtension *inExtension)
|
||||
{
|
||||
g_return_if_fail(noJS);
|
||||
|
||||
/* Show preferences window */
|
||||
GtkWidget* dialog;
|
||||
|
||||
dialog=nojs_preferences_new(noJS);
|
||||
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
|
||||
g_signal_connect(dialog, "response", G_CALLBACK (_nojs_on_preferences_response), NULL);
|
||||
gtk_widget_show_all(dialog);
|
||||
}
|
||||
|
||||
/* Main entry for extension */
|
||||
MidoriExtension *extension_init(void)
|
||||
{
|
||||
/* Set up extension */
|
||||
MidoriExtension *extension=g_object_new(MIDORI_TYPE_EXTENSION,
|
||||
"name", _("NoJS"),
|
||||
"description", _("Manage javascript permission per site"),
|
||||
"version", "0.1" MIDORI_VERSION_SUFFIX,
|
||||
"authors", "Stephan Haller <nomad@froevel.de>",
|
||||
NULL);
|
||||
|
||||
midori_extension_install_integer(extension, "unknown-domain-policy", NOJS_POLICY_BLOCK);
|
||||
midori_extension_install_boolean(extension, "allow-local-pages", TRUE);
|
||||
midori_extension_install_boolean(extension, "only-second-level", TRUE);
|
||||
|
||||
g_signal_connect(extension, "activate", G_CALLBACK(_nojs_on_activate), NULL);
|
||||
g_signal_connect(extension, "deactivate", G_CALLBACK(_nojs_on_deactivate), NULL);
|
||||
g_signal_connect(extension, "open-preferences", G_CALLBACK(_nojs_on_open_preferences), NULL);
|
||||
|
||||
return(extension);
|
||||
}
|
1022
extensions/nojs/nojs-preferences.c
Normal file
1022
extensions/nojs/nojs-preferences.c
Normal file
File diff suppressed because it is too large
Load diff
55
extensions/nojs/nojs-preferences.h
Normal file
55
extensions/nojs/nojs-preferences.h
Normal file
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#ifndef __NOJS_PREFERENCES__
|
||||
#define __NOJS_PREFERENCES__
|
||||
|
||||
#include "config.h"
|
||||
#include <midori/midori.h>
|
||||
|
||||
#include "nojs.h"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
#define TYPE_NOJS_PREFERENCES (nojs_preferences_get_type())
|
||||
#define NOJS_PREFERENCES(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_NOJS_PREFERENCES, NoJSPreferences))
|
||||
#define IS_NOJS_PREFERENCES(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_NOJS_PREFERENCES))
|
||||
#define NOJS_PREFERENCES_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_NOJS_PREFERENCES, NoJSPreferencesClass))
|
||||
#define IS_NOJS_PREFERENCES_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_NOJS_PREFERENCES))
|
||||
#define NOJS_PREFERENCES_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_NOJS_PREFERENCES, NoJSPreferencesClass))
|
||||
|
||||
typedef struct _NoJSPreferences NoJSPreferences;
|
||||
typedef struct _NoJSPreferencesClass NoJSPreferencesClass;
|
||||
typedef struct _NoJSPreferencesPrivate NoJSPreferencesPrivate;
|
||||
|
||||
struct _NoJSPreferences
|
||||
{
|
||||
/* Parent instance */
|
||||
GtkDialog parent_instance;
|
||||
|
||||
/* Private structure */
|
||||
NoJSPreferencesPrivate *priv;
|
||||
};
|
||||
|
||||
struct _NoJSPreferencesClass
|
||||
{
|
||||
/* Parent class */
|
||||
GtkDialogClass parent_class;
|
||||
};
|
||||
|
||||
/* Public API */
|
||||
GType nojs_preferences_get_type(void);
|
||||
|
||||
GtkWidget* nojs_preferences_new(NoJS *inManager);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __NOJS_PREFERENCES__ */
|
807
extensions/nojs/nojs-view.c
Normal file
807
extensions/nojs/nojs-view.c
Normal file
|
@ -0,0 +1,807 @@
|
|||
/*
|
||||
Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#include "nojs-view.h"
|
||||
#include "nojs-preferences.h"
|
||||
|
||||
/* Define this class in GObject system */
|
||||
G_DEFINE_TYPE(NoJSView,
|
||||
nojs_view,
|
||||
G_TYPE_OBJECT)
|
||||
|
||||
/* Properties */
|
||||
enum
|
||||
{
|
||||
PROP_0,
|
||||
|
||||
PROP_MANAGER,
|
||||
PROP_BROWSER,
|
||||
PROP_VIEW,
|
||||
PROP_MENU_ICON_STATE,
|
||||
|
||||
PROP_LAST
|
||||
};
|
||||
|
||||
static GParamSpec* NoJSViewProperties[PROP_LAST]={ 0, };
|
||||
|
||||
/* Private structure - access only by public API if needed */
|
||||
#define NOJS_VIEW_GET_PRIVATE(obj) \
|
||||
(G_TYPE_INSTANCE_GET_PRIVATE((obj), TYPE_NOJS_VIEW, NoJSViewPrivate))
|
||||
|
||||
struct _NoJSViewPrivate
|
||||
{
|
||||
/* Extension related */
|
||||
NoJS *manager;
|
||||
MidoriBrowser *browser;
|
||||
MidoriView *view;
|
||||
|
||||
GtkWidget *menu;
|
||||
gboolean menuPolicyWasChanged;
|
||||
NoJSMenuIconState menuIconState;
|
||||
|
||||
GSList *resourceURIs;
|
||||
};
|
||||
|
||||
/* IMPLEMENTATION: Private variables and methods */
|
||||
|
||||
/* Preferences of this extension should be opened */
|
||||
static void _nojs_view_on_preferences_response(GtkWidget* inDialog,
|
||||
gint inResponse,
|
||||
gpointer *inUserData)
|
||||
{
|
||||
gtk_widget_destroy(inDialog);
|
||||
}
|
||||
|
||||
static void _nojs_view_on_open_preferences(NoJSView *self, gpointer inUserData)
|
||||
{
|
||||
g_return_if_fail(NOJS_IS_VIEW(self));
|
||||
|
||||
NoJSViewPrivate *priv=self->priv;
|
||||
|
||||
/* Show preferences window */
|
||||
GtkWidget* dialog;
|
||||
|
||||
dialog=nojs_preferences_new(priv->manager);
|
||||
gtk_window_set_modal(GTK_WINDOW(dialog), TRUE);
|
||||
g_signal_connect(dialog, "response", G_CALLBACK (_nojs_view_on_preferences_response), self);
|
||||
gtk_widget_show_all(dialog);
|
||||
}
|
||||
|
||||
/* Selection was done in menu */
|
||||
static void _nojs_view_on_menu_selection_done(NoJSView *self, gpointer inUserData)
|
||||
{
|
||||
g_return_if_fail(NOJS_IS_VIEW(self));
|
||||
|
||||
NoJSViewPrivate *priv=self->priv;
|
||||
|
||||
/* Check if any policy was changed and reload page */
|
||||
if(priv->menuPolicyWasChanged!=FALSE)
|
||||
{
|
||||
/* Reset flag that any policy was changed */
|
||||
priv->menuPolicyWasChanged=FALSE;
|
||||
|
||||
/* Reload page */
|
||||
midori_view_reload(priv->view, FALSE);
|
||||
g_message("%s: Reloading page %s as policy has changed", __func__, midori_view_get_display_uri(priv->view));
|
||||
}
|
||||
}
|
||||
|
||||
/* Destroy menu */
|
||||
static void _nojs_view_destroy_menu(NoJSView *self)
|
||||
{
|
||||
g_return_if_fail(NOJS_IS_VIEW(self));
|
||||
g_return_if_fail(self->priv->menu!=NULL);
|
||||
|
||||
NoJSViewPrivate *priv=self->priv;
|
||||
|
||||
/* Empty menu and list of domains added to menu */
|
||||
gtk_widget_destroy(priv->menu);
|
||||
priv->menu=NULL;
|
||||
|
||||
/* Reset menu icon to default state */
|
||||
priv->menuIconState=NOJS_MENU_ICON_STATE_UNDETERMINED;
|
||||
g_object_notify_by_pspec(G_OBJECT(self), NoJSViewProperties[PROP_MENU_ICON_STATE]);
|
||||
}
|
||||
|
||||
/* Create empty menu */
|
||||
static void _nojs_view_create_empty_menu(NoJSView *self)
|
||||
{
|
||||
g_return_if_fail(NOJS_IS_VIEW(self));
|
||||
g_return_if_fail(self->priv->menu==NULL);
|
||||
|
||||
NoJSViewPrivate *priv=self->priv;
|
||||
GtkWidget *item;
|
||||
|
||||
/* Create new menu and set up default items */
|
||||
priv->menu=gtk_menu_new();
|
||||
|
||||
item=gtk_image_menu_item_new_from_stock(GTK_STOCK_PREFERENCES, NULL);
|
||||
g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_open_preferences), self);
|
||||
gtk_menu_shell_prepend(GTK_MENU_SHELL(priv->menu), item);
|
||||
gtk_widget_show_all(item);
|
||||
|
||||
/* Reset flag that any policy was changed */
|
||||
priv->menuPolicyWasChanged=FALSE;
|
||||
|
||||
/* Reset menu icon to default state */
|
||||
priv->menuIconState=NOJS_MENU_ICON_STATE_UNDETERMINED;
|
||||
g_object_notify_by_pspec(G_OBJECT(self), NoJSViewProperties[PROP_MENU_ICON_STATE]);
|
||||
|
||||
/* Connect signal to menu */
|
||||
g_signal_connect_swapped(priv->menu, "selection-done", G_CALLBACK(_nojs_view_on_menu_selection_done), self);
|
||||
}
|
||||
|
||||
/* Change visibility state of menu item for a domain depending on policy */
|
||||
static gboolean _nojs_view_menu_item_change_policy(NoJSView *self, const gchar *inDomain, NoJSPolicy inPolicy)
|
||||
{
|
||||
g_return_val_if_fail(NOJS_IS_VIEW(self), FALSE);
|
||||
g_return_val_if_fail(inDomain, FALSE);
|
||||
|
||||
NoJSViewPrivate *priv=self->priv;
|
||||
GList *items, *iter;
|
||||
gboolean updated;
|
||||
|
||||
/* Handle accept-for-session like accept when showing or hiding menu items */
|
||||
if(inPolicy==NOJS_POLICY_ACCEPT_TEMPORARILY) inPolicy=NOJS_POLICY_ACCEPT;
|
||||
|
||||
/* Update menu items */
|
||||
updated=FALSE;
|
||||
items=gtk_container_get_children(GTK_CONTAINER(priv->menu));
|
||||
for(iter=items; iter; iter=iter->next)
|
||||
{
|
||||
/* Only check and update menu items (not separators and so on) */
|
||||
if(GTK_IS_MENU_ITEM(iter->data))
|
||||
{
|
||||
GtkMenuItem *item=GTK_MENU_ITEM(iter->data);
|
||||
const gchar *itemDomain;
|
||||
NoJSPolicy itemPolicy;
|
||||
|
||||
itemDomain=(const gchar*)g_object_get_data(G_OBJECT(item), "domain");
|
||||
itemPolicy=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "policy"));
|
||||
|
||||
/* Handle accept-for-session like accept when showing or hiding menu items */
|
||||
if(itemPolicy==NOJS_POLICY_ACCEPT_TEMPORARILY) itemPolicy=NOJS_POLICY_ACCEPT;
|
||||
|
||||
/* If menu item has "domain"-data update its visibility state
|
||||
* depending on matching policy
|
||||
*/
|
||||
if(g_strcmp0(itemDomain, inDomain)==0)
|
||||
{
|
||||
if(itemPolicy==inPolicy) gtk_widget_hide(GTK_WIDGET(item));
|
||||
else gtk_widget_show_all(GTK_WIDGET(item));
|
||||
|
||||
/* Set flag that at least one menu item was updated */
|
||||
updated=TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_list_free(items);
|
||||
|
||||
/* Return flag indicating if at least one menu item was updated */
|
||||
return(updated);
|
||||
}
|
||||
|
||||
/* A menu item was selected */
|
||||
static void _nojs_view_on_menu_item_activate(NoJSView *self, gpointer inUserData)
|
||||
{
|
||||
g_return_if_fail(NOJS_IS_VIEW(self));
|
||||
g_return_if_fail(GTK_IS_MENU_ITEM(inUserData));
|
||||
|
||||
NoJSViewPrivate *priv=self->priv;
|
||||
GtkMenuItem *item=GTK_MENU_ITEM(inUserData);
|
||||
const gchar *domain;
|
||||
NoJSPolicy policy;
|
||||
|
||||
/* Get domain and policy to set */
|
||||
domain=(const gchar*)g_object_get_data(G_OBJECT(item), "domain");
|
||||
policy=GPOINTER_TO_INT(g_object_get_data(G_OBJECT(item), "policy"));
|
||||
g_return_if_fail(domain);
|
||||
g_return_if_fail(policy>=NOJS_POLICY_ACCEPT && policy<=NOJS_POLICY_BLOCK);
|
||||
|
||||
/* Set policy for domain and update menu items */
|
||||
_nojs_view_menu_item_change_policy(self, domain, policy);
|
||||
nojs_set_policy(priv->manager, domain, policy);
|
||||
|
||||
/* Set flag that a policy was changed */
|
||||
priv->menuPolicyWasChanged=TRUE;
|
||||
}
|
||||
|
||||
/* Add site to menu */
|
||||
static void _nojs_view_add_site_to_menu(NoJSView *self, const gchar *inDomain, NoJSPolicy inPolicy)
|
||||
{
|
||||
g_return_if_fail(NOJS_IS_VIEW(self));
|
||||
g_return_if_fail(inDomain);
|
||||
|
||||
NoJSViewPrivate *priv=self->priv;
|
||||
GtkWidget *item;
|
||||
gchar *itemLabel;
|
||||
GtkWidget *itemImage;
|
||||
static gint INSERT_POSITION=1;
|
||||
NoJSMenuIconState newMenuIconState;
|
||||
|
||||
/* Create menu object if not available */
|
||||
if(!priv->menu) _nojs_view_create_empty_menu(self);
|
||||
|
||||
/* Check if domain was already added to menu. If it exists just update it. */
|
||||
if(_nojs_view_menu_item_change_policy(self, inDomain, inPolicy)==TRUE) return;
|
||||
|
||||
/* Add menu item(s) for domain */
|
||||
itemLabel=g_strdup_printf(_("Deny %s"), inDomain);
|
||||
item=gtk_image_menu_item_new_with_label(itemLabel);
|
||||
itemImage=gtk_image_new_from_stock (GTK_STOCK_NO, GTK_ICON_SIZE_MENU);
|
||||
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), itemImage);
|
||||
gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item), TRUE);
|
||||
gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
|
||||
if(inPolicy!=NOJS_POLICY_BLOCK) gtk_widget_show_all(item);
|
||||
g_object_set_data_full(G_OBJECT(item), "domain", g_strdup(inDomain), (GDestroyNotify)g_free);
|
||||
g_object_set_data(G_OBJECT(item), "policy", GINT_TO_POINTER(NOJS_POLICY_BLOCK));
|
||||
g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_menu_item_activate), self);
|
||||
g_free(itemLabel);
|
||||
|
||||
itemLabel=g_strdup_printf(_("Allow %s"), inDomain);
|
||||
item=gtk_image_menu_item_new_with_label(itemLabel);
|
||||
itemImage=gtk_image_new_from_stock (GTK_STOCK_YES, GTK_ICON_SIZE_MENU);
|
||||
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), itemImage);
|
||||
gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item), TRUE);
|
||||
gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
|
||||
if(inPolicy!=NOJS_POLICY_ACCEPT && inPolicy!=NOJS_POLICY_ACCEPT_TEMPORARILY) gtk_widget_show_all(item);
|
||||
g_object_set_data_full(G_OBJECT(item), "domain", g_strdup(inDomain), (GDestroyNotify)g_free);
|
||||
g_object_set_data(G_OBJECT(item), "policy", GINT_TO_POINTER(NOJS_POLICY_ACCEPT));
|
||||
g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_menu_item_activate), self);
|
||||
g_free(itemLabel);
|
||||
|
||||
itemLabel=g_strdup_printf(_("Allow %s this session"), inDomain);
|
||||
item=gtk_image_menu_item_new_with_label(itemLabel);
|
||||
itemImage=gtk_image_new_from_stock (GTK_STOCK_OK, GTK_ICON_SIZE_MENU);
|
||||
gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(item), itemImage);
|
||||
gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(item), TRUE);
|
||||
gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
|
||||
if(inPolicy!=NOJS_POLICY_ACCEPT && inPolicy!=NOJS_POLICY_ACCEPT_TEMPORARILY) gtk_widget_show_all(item);
|
||||
g_object_set_data_full(G_OBJECT(item), "domain", g_strdup(inDomain), (GDestroyNotify)g_free);
|
||||
g_object_set_data(G_OBJECT(item), "policy", GINT_TO_POINTER(NOJS_POLICY_ACCEPT_TEMPORARILY));
|
||||
g_signal_connect_swapped(item, "activate", G_CALLBACK(_nojs_view_on_menu_item_activate), self);
|
||||
g_free(itemLabel);
|
||||
|
||||
/* Add seperator to seperate actions for this domain from the other domains */
|
||||
item=gtk_separator_menu_item_new();
|
||||
gtk_menu_shell_insert(GTK_MENU_SHELL(priv->menu), item, INSERT_POSITION);
|
||||
gtk_widget_show_all(item);
|
||||
|
||||
/* Determine state of status icon */
|
||||
if(priv->menuIconState!=NOJS_MENU_ICON_STATE_MIXED)
|
||||
{
|
||||
switch(inPolicy)
|
||||
{
|
||||
case NOJS_POLICY_ACCEPT:
|
||||
case NOJS_POLICY_ACCEPT_TEMPORARILY:
|
||||
newMenuIconState=NOJS_MENU_ICON_STATE_ALLOWED;
|
||||
break;
|
||||
|
||||
case NOJS_POLICY_BLOCK:
|
||||
newMenuIconState=NOJS_MENU_ICON_STATE_DENIED;
|
||||
break;
|
||||
|
||||
default:
|
||||
newMenuIconState=NOJS_MENU_ICON_STATE_MIXED;
|
||||
break;
|
||||
}
|
||||
|
||||
if(priv->menuIconState==NOJS_MENU_ICON_STATE_UNDETERMINED ||
|
||||
priv->menuIconState!=newMenuIconState)
|
||||
{
|
||||
priv->menuIconState=newMenuIconState;
|
||||
g_object_notify_by_pspec(G_OBJECT(self), NoJSViewProperties[PROP_MENU_ICON_STATE]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Status of loading a site has changed */
|
||||
static void _nojs_view_on_load_status_changed(NoJSView *self, GParamSpec *inSpec, gpointer inUserData)
|
||||
{
|
||||
g_return_if_fail(NOJS_IS_VIEW(self));
|
||||
g_return_if_fail(WEBKIT_IS_WEB_VIEW(inUserData));
|
||||
|
||||
NoJSViewPrivate *priv=self->priv;
|
||||
WebKitWebView *webkitView=WEBKIT_WEB_VIEW(inUserData);
|
||||
WebKitWebSettings *settings=webkit_web_view_get_settings(webkitView);
|
||||
WebKitLoadStatus status;
|
||||
SoupURI *uri;
|
||||
|
||||
/* Get URI of document loading/loaded */
|
||||
uri=soup_uri_new(webkit_web_view_get_uri(webkitView));
|
||||
|
||||
/* Check load status */
|
||||
status=webkit_web_view_get_load_status(webkitView);
|
||||
|
||||
/* Check if a view was emptied, e.g. for a new document going to be loaded soon */
|
||||
if(status==WEBKIT_LOAD_PROVISIONAL)
|
||||
{
|
||||
/* Create a new empty menu */
|
||||
_nojs_view_destroy_menu(self);
|
||||
_nojs_view_create_empty_menu(self);
|
||||
|
||||
/* Free list of resource URIs, that's the list of URIs for all resources
|
||||
* of a page
|
||||
*/
|
||||
if(priv->resourceURIs)
|
||||
{
|
||||
g_slist_free_full(priv->resourceURIs, (GDestroyNotify)g_free);
|
||||
priv->resourceURIs=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if document loading is going to start. Do not check special pages. */
|
||||
if(status==WEBKIT_LOAD_COMMITTED &&
|
||||
uri &&
|
||||
uri->scheme &&
|
||||
g_strcmp0(uri->scheme, "about")!=0)
|
||||
{
|
||||
/* Check if domain is black-listed or white-listed and enable or
|
||||
* disable javascript accordingly. But if settings match already
|
||||
* the state it should get do not set it again to avoid reloads of page.
|
||||
*/
|
||||
gchar *domain;
|
||||
NoJSPolicy policy;
|
||||
gboolean currentScriptsEnabled;
|
||||
gboolean newScriptsEnabled;
|
||||
|
||||
domain=nojs_get_domain(priv->manager, uri);
|
||||
policy=nojs_get_policy(priv->manager, uri);
|
||||
if(policy==NOJS_POLICY_UNDETERMINED)
|
||||
{
|
||||
policy=nojs_get_policy_for_unknown_domain(priv->manager);
|
||||
// TODO: Show nick_name of policy (enum) to use in warning
|
||||
g_warning("Got invalid policy. Using default policy for unknown domains.");
|
||||
}
|
||||
|
||||
newScriptsEnabled=(policy==NOJS_POLICY_BLOCK ? FALSE : TRUE);
|
||||
g_object_get(G_OBJECT(settings), "enable-scripts", ¤tScriptsEnabled, NULL);
|
||||
|
||||
if(newScriptsEnabled!=currentScriptsEnabled)
|
||||
{
|
||||
g_object_set(G_OBJECT(settings), "enable-scripts", newScriptsEnabled, NULL);
|
||||
// TODO: Set uri also to ensure this uri is going to be reloaded
|
||||
}
|
||||
|
||||
if(domain)
|
||||
{
|
||||
_nojs_view_add_site_to_menu(self, domain, policy);
|
||||
g_free(domain);
|
||||
}
|
||||
}
|
||||
|
||||
/* Free allocated resources */
|
||||
if(uri) soup_uri_free(uri);
|
||||
}
|
||||
|
||||
/* A request is going to sent */
|
||||
static void _nojs_view_on_resource_request_starting(NoJSView *self,
|
||||
WebKitWebFrame *inFrame,
|
||||
WebKitWebResource *inResource,
|
||||
WebKitNetworkRequest *inRequest,
|
||||
WebKitNetworkResponse *inResponse,
|
||||
gpointer inUserData)
|
||||
{
|
||||
g_return_if_fail(NOJS_IS_VIEW(self));
|
||||
|
||||
NoJSViewPrivate *priv=self->priv;
|
||||
SoupMessage *message;
|
||||
SoupURI *uri;
|
||||
gchar *uriText;
|
||||
|
||||
/* Remember resource URIs requesting */
|
||||
message=(inRequest ? webkit_network_request_get_message(inRequest) : NULL);
|
||||
if(message)
|
||||
{
|
||||
uri=soup_message_get_uri(message);
|
||||
if(uri)
|
||||
{
|
||||
uriText=soup_uri_to_string(uri, FALSE);
|
||||
priv->resourceURIs=g_slist_prepend(priv->resourceURIs, uriText);
|
||||
}
|
||||
}
|
||||
|
||||
message=(inResponse ? webkit_network_response_get_message(inResponse) : NULL);
|
||||
if(message)
|
||||
{
|
||||
uri=soup_message_get_uri(message);
|
||||
if(uri)
|
||||
{
|
||||
uriText=soup_uri_to_string(uri, FALSE);
|
||||
priv->resourceURIs=g_slist_prepend(priv->resourceURIs, uriText);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* A policy has changed */
|
||||
static void _nojs_view_on_policy_changed(NoJSView *self, gchar *inDomain, gpointer inUserData)
|
||||
{
|
||||
g_return_if_fail(NOJS_IS_VIEW(self));
|
||||
g_return_if_fail(inDomain);
|
||||
|
||||
NoJSViewPrivate *priv=self->priv;
|
||||
GList *items, *iter;
|
||||
gboolean reloaded;
|
||||
|
||||
/* Check if the policy of a domain has changed this view has referenced resources to */
|
||||
reloaded=FALSE;
|
||||
items=gtk_container_get_children(GTK_CONTAINER(priv->menu));
|
||||
for(iter=items; reloaded==FALSE && iter; iter=iter->next)
|
||||
{
|
||||
if(GTK_IS_MENU_ITEM(iter->data))
|
||||
{
|
||||
const gchar *itemDomain;
|
||||
|
||||
/* Check if domain matches menu item */
|
||||
itemDomain=(const gchar*)g_object_get_data(G_OBJECT(iter->data), "domain");
|
||||
if(g_strcmp0(itemDomain, inDomain)==0)
|
||||
{
|
||||
/* Found domain in our menu so reload page */
|
||||
midori_view_reload(priv->view, FALSE);
|
||||
reloaded=TRUE;
|
||||
}
|
||||
}
|
||||
}
|
||||
g_list_free(items);
|
||||
}
|
||||
|
||||
/* A javascript URI is going to loaded or blocked */
|
||||
static void _nojs_view_on_uri_load_policy_status(NoJSView *self, gchar *inURI, NoJSPolicy inPolicy, gpointer inUserData)
|
||||
{
|
||||
g_return_if_fail(NOJS_IS_VIEW(self));
|
||||
|
||||
NoJSViewPrivate *priv=self->priv;
|
||||
GSList *iter;
|
||||
gchar *checkURI;
|
||||
|
||||
/* Check if uri (accepted or blocked) might be one of ours */
|
||||
for(iter=priv->resourceURIs; iter; iter=iter->next)
|
||||
{
|
||||
checkURI=(gchar*)iter->data;
|
||||
if(g_strcmp0(checkURI, inURI)==0)
|
||||
{
|
||||
SoupURI *uri;
|
||||
gchar *domain;
|
||||
|
||||
uri=soup_uri_new(inURI);
|
||||
domain=nojs_get_domain(priv->manager, uri);
|
||||
if(domain)
|
||||
{
|
||||
_nojs_view_add_site_to_menu(self, domain, inPolicy);
|
||||
g_free(domain);
|
||||
}
|
||||
|
||||
soup_uri_free(uri);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Property "view" has changed */
|
||||
static void _nojs_view_on_view_changed(NoJSView *self, MidoriView *inView)
|
||||
{
|
||||
NoJSViewPrivate *priv=self->priv;
|
||||
WebKitWebView *webkitView;
|
||||
|
||||
/* Disconnect signal on old view */
|
||||
if(priv->view)
|
||||
{
|
||||
webkitView=WEBKIT_WEB_VIEW(midori_view_get_web_view(priv->view));
|
||||
g_signal_handlers_disconnect_by_data(webkitView, self);
|
||||
g_object_set_data(G_OBJECT(priv->view), "nojs-view-instance", NULL);
|
||||
g_object_unref(priv->view);
|
||||
priv->view=NULL;
|
||||
}
|
||||
|
||||
/* Set new view if valid pointer */
|
||||
if(!inView) return;
|
||||
|
||||
priv->view=g_object_ref(inView);
|
||||
g_object_set_data(G_OBJECT(priv->view), "nojs-view-instance", self);
|
||||
|
||||
/* Listen to changes of load-status in view */
|
||||
webkitView=WEBKIT_WEB_VIEW(midori_view_get_web_view(priv->view));
|
||||
g_signal_connect_swapped(webkitView, "notify::load-status", G_CALLBACK(_nojs_view_on_load_status_changed), self);
|
||||
g_signal_connect_swapped(webkitView, "resource-request-starting", G_CALLBACK(_nojs_view_on_resource_request_starting), self);
|
||||
|
||||
/* Create empty menu */
|
||||
_nojs_view_destroy_menu(self);
|
||||
_nojs_view_create_empty_menu(self);
|
||||
|
||||
/* Release list of resource URIs */
|
||||
if(priv->resourceURIs)
|
||||
{
|
||||
g_slist_free_full(priv->resourceURIs, (GDestroyNotify)g_free);
|
||||
priv->resourceURIs=NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* This extension is going to be deactivated */
|
||||
static void _nojs_view_on_extension_deactivated(NoJSView *self, MidoriExtension *inExtension)
|
||||
{
|
||||
g_return_if_fail(NOJS_IS_VIEW(self));
|
||||
|
||||
/* Dispose allocated resources by unreferencing ourselve */
|
||||
g_object_unref(self);
|
||||
}
|
||||
|
||||
/* Property "manager" has changed */
|
||||
static void _nojs_view_on_manager_changed(NoJSView *self, NoJS *inNoJS)
|
||||
{
|
||||
g_return_if_fail(NOJS_IS_VIEW(self));
|
||||
g_return_if_fail(!inNoJS || IS_NOJS(inNoJS));
|
||||
|
||||
NoJSViewPrivate *priv=self->priv;
|
||||
MidoriExtension *extension;
|
||||
|
||||
/* Release reference to old manager and clean up */
|
||||
if(priv->manager)
|
||||
{
|
||||
g_object_get(priv->manager, "extension", &extension, NULL);
|
||||
g_signal_handlers_disconnect_by_data(extension, self);
|
||||
g_object_unref(extension);
|
||||
|
||||
g_signal_handlers_disconnect_by_data(priv->manager, self);
|
||||
g_object_unref(priv->manager);
|
||||
priv->manager=NULL;
|
||||
}
|
||||
|
||||
/* Set new view if valid pointer */
|
||||
if(!inNoJS) return;
|
||||
|
||||
priv->manager=g_object_ref(inNoJS);
|
||||
|
||||
/* Connect signals to manager */
|
||||
g_signal_connect_swapped(priv->manager, "uri-load-policy-status", G_CALLBACK(_nojs_view_on_uri_load_policy_status), self);
|
||||
g_signal_connect_swapped(priv->manager, "policy-changed", G_CALLBACK(_nojs_view_on_policy_changed), self);
|
||||
|
||||
/* Connect signal to get noticed when extension is going to be deactivated
|
||||
* to release all references to GObjects
|
||||
*/
|
||||
g_object_get(priv->manager, "extension", &extension, NULL);
|
||||
g_signal_connect_swapped(extension, "deactivate", G_CALLBACK(_nojs_view_on_extension_deactivated), self);
|
||||
g_object_unref(extension);
|
||||
}
|
||||
|
||||
/* IMPLEMENTATION: GObject */
|
||||
|
||||
/* Finalize this object */
|
||||
static void nojs_view_finalize(GObject *inObject)
|
||||
{
|
||||
NoJSView *self=NOJS_VIEW(inObject);
|
||||
NoJSViewPrivate *priv=self->priv;
|
||||
|
||||
/* Dispose allocated resources */
|
||||
if(priv->manager)
|
||||
{
|
||||
MidoriExtension *extension;
|
||||
|
||||
g_object_get(priv->manager, "extension", &extension, NULL);
|
||||
g_signal_handlers_disconnect_by_data(extension, self);
|
||||
g_object_unref(extension);
|
||||
|
||||
g_signal_handlers_disconnect_by_data(priv->manager, self);
|
||||
g_object_unref(priv->manager);
|
||||
priv->manager=NULL;
|
||||
}
|
||||
|
||||
if(priv->browser)
|
||||
{
|
||||
g_object_unref(priv->browser);
|
||||
priv->browser=NULL;
|
||||
}
|
||||
|
||||
if(priv->view)
|
||||
{
|
||||
_nojs_view_on_view_changed(self, NULL);
|
||||
}
|
||||
|
||||
if(priv->menu)
|
||||
{
|
||||
gtk_widget_destroy(priv->menu);
|
||||
priv->menu=NULL;
|
||||
}
|
||||
|
||||
if(priv->resourceURIs)
|
||||
{
|
||||
g_slist_free_full(priv->resourceURIs, (GDestroyNotify)g_free);
|
||||
priv->resourceURIs=NULL;
|
||||
}
|
||||
|
||||
/* Call parent's class finalize method */
|
||||
G_OBJECT_CLASS(nojs_view_parent_class)->finalize(inObject);
|
||||
}
|
||||
|
||||
/* Set/get properties */
|
||||
static void nojs_view_set_property(GObject *inObject,
|
||||
guint inPropID,
|
||||
const GValue *inValue,
|
||||
GParamSpec *inSpec)
|
||||
{
|
||||
NoJSView *self=NOJS_VIEW(inObject);
|
||||
|
||||
switch(inPropID)
|
||||
{
|
||||
/* Construct-only properties */
|
||||
case PROP_MANAGER:
|
||||
_nojs_view_on_manager_changed(self, NOJS(g_value_get_object(inValue)));
|
||||
break;
|
||||
|
||||
case PROP_BROWSER:
|
||||
if(self->priv->browser) g_object_unref(self->priv->browser);
|
||||
self->priv->browser=g_object_ref(g_value_get_object(inValue));
|
||||
break;
|
||||
|
||||
case PROP_VIEW:
|
||||
_nojs_view_on_view_changed(self, MIDORI_VIEW(g_value_get_object(inValue)));
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void nojs_view_get_property(GObject *inObject,
|
||||
guint inPropID,
|
||||
GValue *outValue,
|
||||
GParamSpec *inSpec)
|
||||
{
|
||||
NoJSView *self=NOJS_VIEW(inObject);
|
||||
|
||||
switch(inPropID)
|
||||
{
|
||||
case PROP_MANAGER:
|
||||
g_value_set_object(outValue, self->priv->manager);
|
||||
break;
|
||||
|
||||
case PROP_BROWSER:
|
||||
g_value_set_object(outValue, self->priv->browser);
|
||||
break;
|
||||
|
||||
case PROP_VIEW:
|
||||
g_value_set_object(outValue, self->priv->view);
|
||||
break;
|
||||
|
||||
case PROP_MENU_ICON_STATE:
|
||||
g_value_set_enum(outValue, self->priv->menuIconState);
|
||||
break;
|
||||
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID(inObject, inPropID, inSpec);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Class initialization
|
||||
* Override functions in parent classes and define properties and signals
|
||||
*/
|
||||
static void nojs_view_class_init(NoJSViewClass *klass)
|
||||
{
|
||||
GObjectClass *gobjectClass=G_OBJECT_CLASS(klass);
|
||||
|
||||
/* Override functions */
|
||||
gobjectClass->finalize=nojs_view_finalize;
|
||||
gobjectClass->set_property=nojs_view_set_property;
|
||||
gobjectClass->get_property=nojs_view_get_property;
|
||||
|
||||
/* Set up private structure */
|
||||
g_type_class_add_private(klass, sizeof(NoJSViewPrivate));
|
||||
|
||||
/* Define properties */
|
||||
NoJSViewProperties[PROP_MANAGER]=
|
||||
g_param_spec_object("manager",
|
||||
_("Manager instance"),
|
||||
_("Instance to global NoJS manager"),
|
||||
TYPE_NOJS,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
NoJSViewProperties[PROP_BROWSER]=
|
||||
g_param_spec_object("browser",
|
||||
_("Browser window"),
|
||||
_("The Midori browser instance this view belongs to"),
|
||||
MIDORI_TYPE_BROWSER,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
NoJSViewProperties[PROP_VIEW]=
|
||||
g_param_spec_object("view",
|
||||
_("View"),
|
||||
_("The Midori view instance this view belongs to"),
|
||||
MIDORI_TYPE_VIEW,
|
||||
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY);
|
||||
|
||||
NoJSViewProperties[PROP_MENU_ICON_STATE]=
|
||||
g_param_spec_enum("menu-icon-state",
|
||||
_("Menu icon state"),
|
||||
_("State of menu icon to show in status bar"),
|
||||
NOJS_TYPE_MENU_ICON_STATE,
|
||||
NOJS_MENU_ICON_STATE_UNDETERMINED,
|
||||
G_PARAM_READABLE);
|
||||
|
||||
g_object_class_install_properties(gobjectClass, PROP_LAST, NoJSViewProperties);
|
||||
}
|
||||
|
||||
/* Object initialization
|
||||
* Create private structure and set up default values
|
||||
*/
|
||||
static void nojs_view_init(NoJSView *self)
|
||||
{
|
||||
NoJSViewPrivate *priv;
|
||||
|
||||
priv=self->priv=NOJS_VIEW_GET_PRIVATE(self);
|
||||
|
||||
/* Set up default values */
|
||||
priv->manager=NULL;
|
||||
priv->browser=NULL;
|
||||
priv->view=NULL;
|
||||
|
||||
priv->menu=NULL;
|
||||
priv->menuPolicyWasChanged=FALSE;
|
||||
priv->menuIconState=NOJS_MENU_ICON_STATE_UNDETERMINED;
|
||||
|
||||
priv->resourceURIs=NULL;
|
||||
|
||||
/* Create empty menu */
|
||||
_nojs_view_create_empty_menu(self);
|
||||
}
|
||||
|
||||
/* Implementation: Public API */
|
||||
|
||||
/* Create new object */
|
||||
NoJSView* nojs_view_new(NoJS *inNoJS, MidoriBrowser *inBrowser, MidoriView *inView)
|
||||
{
|
||||
return(g_object_new(TYPE_NOJS_VIEW,
|
||||
"manager", inNoJS,
|
||||
"browser", inBrowser,
|
||||
"view", inView,
|
||||
NULL));
|
||||
}
|
||||
|
||||
/* Get menu widget for this view */
|
||||
GtkMenu* nojs_view_get_menu(NoJSView *self)
|
||||
{
|
||||
g_return_val_if_fail(NOJS_IS_VIEW(self), NULL);
|
||||
|
||||
return(GTK_MENU(self->priv->menu));
|
||||
}
|
||||
|
||||
/* Get image used for menu icon in status bar */
|
||||
NoJSMenuIconState nojs_view_get_menu_icon_state(NoJSView *self)
|
||||
{
|
||||
g_return_val_if_fail(NOJS_IS_VIEW(self), NOJS_MENU_ICON_STATE_UNDETERMINED);
|
||||
|
||||
return(self->priv->menuIconState);
|
||||
}
|
||||
|
||||
/************************************************************************************/
|
||||
|
||||
/* Implementation: Enumeration */
|
||||
GType nojs_menu_icon_state_get_type(void)
|
||||
{
|
||||
static volatile gsize g_define_type_id__volatile=0;
|
||||
|
||||
if(g_once_init_enter(&g_define_type_id__volatile))
|
||||
{
|
||||
static const GEnumValue values[]=
|
||||
{
|
||||
{ NOJS_MENU_ICON_STATE_UNDETERMINED, "NOJS_MENU_ICON_STATE_UNDETERMINED", N_("Undetermined") },
|
||||
{ NOJS_MENU_ICON_STATE_ALLOWED, "NOJS_MENU_ICON_STATE_ALLOWED", N_("Allowed") },
|
||||
{ NOJS_MENU_ICON_STATE_MIXED, "NOJS_MENU_ICON_STATE_MIXED", N_("Mixed") },
|
||||
{ NOJS_MENU_ICON_STATE_DENIED, "NOJS_MENU_ICON_STATE_DENIED", N_("Denied") },
|
||||
{ 0, NULL, NULL }
|
||||
};
|
||||
|
||||
GType g_define_type_id=g_enum_register_static(g_intern_static_string("NoJSMenuIconState"), values);
|
||||
g_once_init_leave(&g_define_type_id__volatile, g_define_type_id);
|
||||
}
|
||||
|
||||
return(g_define_type_id__volatile);
|
||||
}
|
71
extensions/nojs/nojs-view.h
Normal file
71
extensions/nojs/nojs-view.h
Normal file
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#ifndef __NOJS_VIEW__
|
||||
#define __NOJS_VIEW__
|
||||
|
||||
#include "config.h"
|
||||
#include "nojs.h"
|
||||
#include <midori/midori.h>
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* NoJS view enums */
|
||||
typedef enum
|
||||
{
|
||||
NOJS_MENU_ICON_STATE_UNDETERMINED,
|
||||
NOJS_MENU_ICON_STATE_ALLOWED,
|
||||
NOJS_MENU_ICON_STATE_MIXED,
|
||||
NOJS_MENU_ICON_STATE_DENIED
|
||||
} NoJSMenuIconState;
|
||||
|
||||
/* NoJS view object */
|
||||
#define TYPE_NOJS_VIEW (nojs_view_get_type())
|
||||
#define NOJS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_NOJS_VIEW, NoJSView))
|
||||
#define NOJS_IS_VIEW(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_NOJS_VIEW))
|
||||
#define NOJS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_NOJS_VIEW, NoJSViewClass))
|
||||
#define NOJS_IS_VIEW_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_NOJS_VIEW))
|
||||
#define NOJS_VIEW_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_NOJS_VIEW, NoJSViewClass))
|
||||
|
||||
typedef struct _NoJSView NoJSView;
|
||||
typedef struct _NoJSViewClass NoJSViewClass;
|
||||
typedef struct _NoJSViewPrivate NoJSViewPrivate;
|
||||
|
||||
struct _NoJSView
|
||||
{
|
||||
/* Parent instance */
|
||||
GObject parent_instance;
|
||||
|
||||
/* Private structure */
|
||||
NoJSViewPrivate *priv;
|
||||
};
|
||||
|
||||
struct _NoJSViewClass
|
||||
{
|
||||
/* Parent class */
|
||||
GObjectClass parent_class;
|
||||
};
|
||||
|
||||
/* Public API */
|
||||
GType nojs_view_get_type(void);
|
||||
|
||||
NoJSView* nojs_view_new(NoJS *inNoJS, MidoriBrowser *inBrowser, MidoriView *inView);
|
||||
|
||||
GtkMenu* nojs_view_get_menu(NoJSView *self);
|
||||
NoJSMenuIconState nojs_view_get_menu_icon_state(NoJSView *self);
|
||||
|
||||
/* Enumeration */
|
||||
GType nojs_menu_icon_state_get_type(void) G_GNUC_CONST;
|
||||
#define NOJS_TYPE_MENU_ICON_STATE (nojs_menu_icon_state_get_type())
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __NOJS_VIEW__ */
|
1044
extensions/nojs/nojs.c
Normal file
1044
extensions/nojs/nojs.c
Normal file
File diff suppressed because it is too large
Load diff
89
extensions/nojs/nojs.h
Normal file
89
extensions/nojs/nojs.h
Normal file
|
@ -0,0 +1,89 @@
|
|||
/*
|
||||
Copyright (C) 2013 Stephan Haller <nomad@froevel.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#ifndef __NOJS__
|
||||
#define __NOJS__
|
||||
|
||||
#include "config.h"
|
||||
#include <midori/midori.h>
|
||||
|
||||
#define NOJS_DATABASE "nojs.db"
|
||||
|
||||
G_BEGIN_DECLS
|
||||
|
||||
/* NoJS manager enums */
|
||||
typedef enum
|
||||
{
|
||||
NOJS_POLICY_UNDETERMINED,
|
||||
NOJS_POLICY_ACCEPT,
|
||||
NOJS_POLICY_ACCEPT_TEMPORARILY,
|
||||
NOJS_POLICY_BLOCK
|
||||
} NoJSPolicy;
|
||||
|
||||
/* NoJS manager object */
|
||||
#define TYPE_NOJS (nojs_get_type())
|
||||
#define NOJS(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), TYPE_NOJS, NoJS))
|
||||
#define IS_NOJS(obj) (G_TYPE_CHECK_INSTANCE_TYPE((obj), TYPE_NOJS))
|
||||
#define NOJS_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST((klass), TYPE_NOJS, NoJSClass))
|
||||
#define IS_NOJS_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE((klass), TYPE_NOJS))
|
||||
#define NOJS_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS((obj), TYPE_NOJS, NoJSClass))
|
||||
|
||||
typedef struct _NoJS NoJS;
|
||||
typedef struct _NoJSClass NoJSClass;
|
||||
typedef struct _NoJSPrivate NoJSPrivate;
|
||||
|
||||
struct _NoJS
|
||||
{
|
||||
/* Parent instance */
|
||||
GObject parent_instance;
|
||||
|
||||
/* Private structure */
|
||||
NoJSPrivate *priv;
|
||||
};
|
||||
|
||||
struct _NoJSClass
|
||||
{
|
||||
/* Parent class */
|
||||
GObjectClass parent_class;
|
||||
|
||||
/* Virtual functions */
|
||||
void (*uri_load_policy_status)(NoJS *self, gchar *inURI, NoJSPolicy inPolicy);
|
||||
void (*policy_changed)(NoJS *self, gchar *inDomain);
|
||||
};
|
||||
|
||||
/* Public API */
|
||||
GType nojs_get_type(void);
|
||||
|
||||
NoJS* nojs_new(MidoriExtension *inExtension, MidoriApp *inApp);
|
||||
|
||||
gchar* nojs_get_domain(NoJS *self, SoupURI *inURI);
|
||||
|
||||
gint nojs_get_policy(NoJS *self, SoupURI *inURI);
|
||||
void nojs_set_policy(NoJS *self, const gchar *inDomain, NoJSPolicy inPolicy);
|
||||
|
||||
NoJSPolicy nojs_get_policy_for_unknown_domain(NoJS *self);
|
||||
void nojs_set_policy_for_unknown_domain(NoJS *self, NoJSPolicy inPolicy);
|
||||
|
||||
gboolean nojs_get_allow_local_pages(NoJS *self);
|
||||
void nojs_set_allow_local_pages(NoJS *self, gboolean inAllow);
|
||||
|
||||
gboolean nojs_get_only_second_level_domain(NoJS *self);
|
||||
void nojs_set_only_second_level_domain(NoJS *self, gboolean inOnlySecondLevel);
|
||||
|
||||
gchar* nojs_get_icon_path (const gchar* icon);
|
||||
|
||||
/* Enumeration */
|
||||
GType nojs_policy_get_type(void) G_GNUC_CONST;
|
||||
#define NOJS_TYPE_POLICY (nojs_policy_get_type())
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* __NOJS__ */
|
459
extensions/notes.vala
Normal file
459
extensions/notes.vala
Normal file
|
@ -0,0 +1,459 @@
|
|||
/*
|
||||
Copyright (C) 2013 Paweł Forysiuk <tuxator@o2.pl>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
namespace ClipNotes {
|
||||
|
||||
Midori.Database database;
|
||||
unowned Sqlite.Database db;
|
||||
Gtk.ListStore notes_list_store;
|
||||
Note? current_note;
|
||||
|
||||
class Note : GLib.Object {
|
||||
public int64 id { get; set; }
|
||||
public string title { get; set; }
|
||||
public string? uri { get; set; default = null; }
|
||||
public string content { get; set; default = ""; }
|
||||
|
||||
public void add (string title, string? uri, string note_content)
|
||||
{
|
||||
GLib.DateTime time = new DateTime.now_local ();
|
||||
string sqlcmd = "INSERT INTO `notes` (`uri`, `title`, `note_content`, `tstamp` ) VALUES (:uri, :title, :note_content, :tstamp);";
|
||||
Midori.DatabaseStatement statement;
|
||||
try {
|
||||
statement = database.prepare (sqlcmd,
|
||||
":uri", typeof (string), uri,
|
||||
":title", typeof (string), title,
|
||||
":note_content", typeof (string), note_content,
|
||||
":tstamp", typeof (int64), time.to_unix ());
|
||||
|
||||
statement.step ();
|
||||
|
||||
append_note (this);
|
||||
} catch (Error error) {
|
||||
critical (_("Failed to add new note to database: %s\n"), error.message);
|
||||
}
|
||||
|
||||
this.id = db.last_insert_rowid ();
|
||||
this.uri = uri;
|
||||
this.title = title;
|
||||
this.content = note_content;
|
||||
}
|
||||
|
||||
public void remove ()
|
||||
{
|
||||
string sqlcmd = "DELETE FROM `notes` WHERE id= :id;";
|
||||
Midori.DatabaseStatement statement;
|
||||
try {
|
||||
statement = database.prepare (sqlcmd,
|
||||
":id", typeof (int64), this.id);
|
||||
|
||||
statement.step ();
|
||||
remove_note (this.id);
|
||||
} catch (Error error) {
|
||||
critical (_("Falied to remove note from database: %s\n"), error.message);
|
||||
}
|
||||
}
|
||||
|
||||
public void rename (string new_title)
|
||||
{
|
||||
string sqlcmd = "UPDATE `notes` SET title= :title WHERE id = :id;";
|
||||
Midori.DatabaseStatement statement;
|
||||
try {
|
||||
statement = database.prepare (sqlcmd,
|
||||
":id", typeof (int64), this.id,
|
||||
":title", typeof (string), new_title);
|
||||
statement.step ();
|
||||
} catch (Error error) {
|
||||
critical (_("Falied to rename note: %s\n"), error.message);
|
||||
}
|
||||
|
||||
this.title = new_title;
|
||||
}
|
||||
|
||||
public void update (string new_content)
|
||||
{
|
||||
string sqlcmd = "UPDATE `notes` SET note_content= :content WHERE id = :id;";
|
||||
Midori.DatabaseStatement statement;
|
||||
try {
|
||||
statement = database.prepare (sqlcmd,
|
||||
":id", typeof (int64), this.id,
|
||||
":content", typeof (string), new_content);
|
||||
statement.step ();
|
||||
} catch (Error error) {
|
||||
critical (_("Falied to update note: %s\n"), error.message);
|
||||
}
|
||||
this.content = new_content;
|
||||
}
|
||||
}
|
||||
|
||||
void append_note (Note note)
|
||||
{
|
||||
/* Strip LRE leading character */
|
||||
if (note.title != null && note.title.has_prefix (""))
|
||||
note.title = note.title.replace ("", "");
|
||||
|
||||
Gtk.TreeIter iter;
|
||||
notes_list_store.append (out iter);
|
||||
notes_list_store.set (iter, 0, note);
|
||||
}
|
||||
|
||||
void remove_note (int64 id)
|
||||
{
|
||||
Gtk.TreeIter iter;
|
||||
if (notes_list_store.iter_children (out iter, null)) {
|
||||
do {
|
||||
Note note;
|
||||
notes_list_store.get (iter, 0, out note);
|
||||
if (id == note.id) {
|
||||
if (current_note == note) {
|
||||
current_note = null;
|
||||
}
|
||||
notes_list_store.remove (iter);
|
||||
break;
|
||||
}
|
||||
} while (notes_list_store.iter_next (ref iter));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class Sidebar : Gtk.VBox, Midori.Viewable {
|
||||
Gtk.Toolbar? toolbar = null;
|
||||
Gtk.Label note_label;
|
||||
Gtk.TreeView notes_tree_view;
|
||||
Gtk.TextView note_text_view = new Gtk.TextView ();
|
||||
|
||||
public unowned string get_stock_id () {
|
||||
return Gtk.STOCK_EDIT;
|
||||
}
|
||||
|
||||
public unowned string get_label () {
|
||||
return _("Notes");
|
||||
}
|
||||
|
||||
public Gtk.Widget get_toolbar () {
|
||||
if (toolbar == null) {
|
||||
toolbar = new Gtk.Toolbar ();
|
||||
var new_note_button = new Gtk.ToolButton.from_stock (Gtk.STOCK_EDIT);
|
||||
new_note_button.label = _("New Note");
|
||||
new_note_button.tooltip_text = _("Creates a new empty note, unrelated to opened pages");
|
||||
new_note_button.use_underline = true;
|
||||
new_note_button.is_important = true;
|
||||
new_note_button.show ();
|
||||
new_note_button.clicked.connect (() => {
|
||||
var note = new Note ();
|
||||
note.add (_("New note"), null, "");
|
||||
});
|
||||
toolbar.insert (new_note_button, -1);
|
||||
}
|
||||
return toolbar;
|
||||
}
|
||||
|
||||
internal void title_edited (Gtk.CellRendererText renderer, string? path_str, string? new_title) {
|
||||
var path = new Gtk.TreePath.from_string (path_str);
|
||||
Gtk.TreeIter iter;
|
||||
notes_list_store.get_iter (out iter, path);
|
||||
Note note;
|
||||
notes_list_store.get (iter, 0, out note);
|
||||
note.rename (new_title);
|
||||
notes_list_store.set (iter, 0, note);
|
||||
}
|
||||
|
||||
public Sidebar () {
|
||||
Gtk.TreeViewColumn column;
|
||||
|
||||
notes_list_store = new Gtk.ListStore (1, typeof (Note));
|
||||
notes_tree_view = new Gtk.TreeView.with_model (notes_list_store);
|
||||
notes_tree_view.headers_visible = true;
|
||||
notes_tree_view.button_press_event.connect (button_pressed);
|
||||
notes_tree_view.get_selection().changed.connect (selection_changed);
|
||||
|
||||
notes_list_store.set_sort_column_id (0, Gtk.SortType.ASCENDING);
|
||||
notes_list_store.set_sort_func (0, tree_sort_func);
|
||||
|
||||
column = new Gtk.TreeViewColumn ();
|
||||
Gtk.CellRendererPixbuf renderer_icon = new Gtk.CellRendererPixbuf ();
|
||||
column.pack_start (renderer_icon, false);
|
||||
column.set_cell_data_func (renderer_icon, on_render_icon);
|
||||
notes_tree_view.append_column (column);
|
||||
|
||||
column = new Gtk.TreeViewColumn ();
|
||||
Gtk.CellRendererText renderer_title = new Gtk.CellRendererText ();
|
||||
renderer_title.editable = true;
|
||||
renderer_title.edited.connect (title_edited);
|
||||
column.set_title (_("Notes"));
|
||||
column.pack_start (renderer_title, true);
|
||||
column.set_cell_data_func (renderer_title, on_render_note_title);
|
||||
notes_tree_view.append_column (column);
|
||||
|
||||
try {
|
||||
string sqlcmd = "SELECT id, uri, title, note_content FROM notes";
|
||||
var statement = database.prepare (sqlcmd);
|
||||
while (statement.step ()) {
|
||||
var note = new Note ();
|
||||
note.id = statement.get_int64 ("id");
|
||||
note.uri = statement.get_string ("uri");
|
||||
note.title = statement.get_string ("title");
|
||||
note.content = statement.get_string ("note_content");
|
||||
|
||||
append_note (note);
|
||||
}
|
||||
} catch (Error error) {
|
||||
critical (_("Failed to select from notes database: %s\n"), error.message);
|
||||
}
|
||||
|
||||
notes_tree_view.show ();
|
||||
pack_start (notes_tree_view, false, false, 0);
|
||||
|
||||
note_label = new Gtk.Label (null);
|
||||
note_label.show ();
|
||||
pack_start (note_label, false, false, 0);
|
||||
|
||||
note_text_view.set_wrap_mode (Gtk.WrapMode.WORD);
|
||||
note_text_view.show ();
|
||||
note_text_view.focus_out_event.connect (focus_lost);
|
||||
pack_start (note_text_view, true, true, 0);
|
||||
}
|
||||
|
||||
int tree_sort_func (Gtk.TreeModel model, Gtk.TreeIter a, Gtk.TreeIter b) {
|
||||
Note note1, note2;
|
||||
model.get (a, 0, out note1);
|
||||
model.get (b, 0, out note2);
|
||||
return strcmp (note1.title, note2.title);
|
||||
}
|
||||
|
||||
void save_current_note () {
|
||||
if (current_note != null) {
|
||||
string note_content = note_text_view.buffer.text;
|
||||
if (note_content != current_note.content)
|
||||
current_note.update (note_content);
|
||||
}
|
||||
}
|
||||
|
||||
bool focus_lost (Gdk.EventFocus event) {
|
||||
save_current_note ();
|
||||
return false;
|
||||
}
|
||||
|
||||
private void on_render_note_title (Gtk.CellLayout column, Gtk.CellRenderer renderer,
|
||||
Gtk.TreeModel model, Gtk.TreeIter iter) {
|
||||
|
||||
Note note;
|
||||
model.get (iter, 0, out note);
|
||||
renderer.set ("markup", GLib.Markup.printf_escaped ("%s", note.title),
|
||||
"ellipsize", Pango.EllipsizeMode.END);
|
||||
}
|
||||
|
||||
private void on_render_icon (Gtk.CellLayout column, Gtk.CellRenderer renderer,
|
||||
Gtk.TreeModel model, Gtk.TreeIter iter) {
|
||||
|
||||
Note note;
|
||||
model.get (iter, 0, out note);
|
||||
|
||||
var pixbuf = Midori.Paths.get_icon (note.uri, null);
|
||||
if (pixbuf != null) {
|
||||
int icon_width = 16, icon_height = 16;
|
||||
Gtk.icon_size_lookup_for_settings (get_settings (),
|
||||
Gtk.IconSize.MENU, out icon_width, out icon_height);
|
||||
pixbuf = pixbuf.scale_simple (icon_width, icon_height, Gdk.InterpType.TILES);
|
||||
}
|
||||
renderer.set ("pixbuf", pixbuf);
|
||||
}
|
||||
|
||||
private void selection_changed (Gtk.TreeSelection selection)
|
||||
{
|
||||
save_current_note ();
|
||||
show_note_content (selection);
|
||||
}
|
||||
|
||||
bool button_pressed (Gdk.EventButton event) {
|
||||
if (event.button == 1) {
|
||||
if (event.type == Gdk.EventType.2BUTTON_PRESS) {
|
||||
return show_note_webpage_in_new_tab (event, false);
|
||||
}
|
||||
}
|
||||
if (event.button == 2)
|
||||
return show_note_webpage_in_new_tab (event, true);
|
||||
if (event.button == 3)
|
||||
return show_popup_menu (event);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool show_note_content (Gtk.TreeSelection selection) {
|
||||
Gtk.TreeIter iter;
|
||||
if (selection.get_selected (null, out iter)) {
|
||||
Note note;
|
||||
notes_list_store.get (iter, 0, out note);
|
||||
|
||||
if (note != current_note) {
|
||||
note_text_view.buffer.text = note.content;
|
||||
current_note = note;
|
||||
}
|
||||
|
||||
return true;
|
||||
} else {
|
||||
note_text_view.buffer.text = "";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool show_note_webpage_in_new_tab (Gdk.EventButton? event, bool new_tab) {
|
||||
Gtk.TreeIter iter;
|
||||
if (notes_tree_view.get_selection ().get_selected (null, out iter)) {
|
||||
Note note;
|
||||
notes_list_store.get (iter, 0, out note);
|
||||
if (note.uri != null) {
|
||||
var browser = Midori.Browser.get_for_widget (notes_tree_view);
|
||||
if (new_tab) {
|
||||
browser.add_uri (note.uri);
|
||||
} else {
|
||||
var tab = browser.tab as Midori.View;
|
||||
tab.set_uri (note.uri);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool show_popup_menu (Gdk.EventButton? event) {
|
||||
return_val_if_fail (event.window == notes_tree_view.get_bin_window(), false);
|
||||
Gtk.TreePath path = null;
|
||||
notes_tree_view.get_path_at_pos ((int)event.x, (int)event.y, out path,
|
||||
null, null, null);
|
||||
if (path != null) {
|
||||
Gtk.TreeIter iter;
|
||||
notes_list_store.get_iter (out iter, path);
|
||||
Note note;
|
||||
notes_list_store.get (iter, 0, out note);
|
||||
|
||||
var menu = new Gtk.Menu ();
|
||||
|
||||
var menuitem = new Gtk.ImageMenuItem.with_label (_("Rename note"));
|
||||
var image = new Gtk.Image.from_stock (Gtk.STOCK_EDIT, Gtk.IconSize.MENU);
|
||||
menuitem.always_show_image = true;
|
||||
menuitem.set_image (image);
|
||||
menuitem.activate.connect (() => {
|
||||
notes_tree_view.set_cursor (path,
|
||||
notes_tree_view.get_column (1), true);
|
||||
});
|
||||
menu.append (menuitem);
|
||||
|
||||
|
||||
menuitem = new Gtk.ImageMenuItem.with_label (_("Copy note to clipboard"));
|
||||
image = new Gtk.Image.from_stock (Gtk.STOCK_COPY, Gtk.IconSize.MENU);
|
||||
menuitem.always_show_image = true;
|
||||
menuitem.set_image (image);
|
||||
menuitem.activate.connect (() => {
|
||||
get_clipboard (Gdk.SELECTION_CLIPBOARD).set_text (note.content, -1);
|
||||
});
|
||||
menu.append (menuitem);
|
||||
|
||||
|
||||
menuitem = new Gtk.ImageMenuItem.with_label (_("Remove note"));
|
||||
image = new Gtk.Image.from_stock (Gtk.STOCK_DELETE, Gtk.IconSize.MENU);
|
||||
menuitem.always_show_image = true;
|
||||
menuitem.set_image (image);
|
||||
menuitem.activate.connect (() => {
|
||||
note.remove ();
|
||||
});
|
||||
menu.append (menuitem);
|
||||
|
||||
menu.show_all ();
|
||||
Katze.widget_popup (notes_tree_view, menu, null, Katze.MenuPos.CURSOR);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class Manager : Midori.Extension {
|
||||
internal GLib.List<Gtk.Widget> widgets;
|
||||
|
||||
void tab_added (Midori.Browser browser, Midori.Tab tab) {
|
||||
|
||||
tab.context_menu.connect (add_menu_items);
|
||||
|
||||
}
|
||||
|
||||
void add_menu_items (Midori.Tab tab, WebKit.HitTestResult hit_test_result, Midori.ContextAction menu) {
|
||||
#if !HAVE_WEBKIT2
|
||||
if ((hit_test_result.context & WebKit.HitTestResultContext.SELECTION) == 0)
|
||||
return;
|
||||
#endif
|
||||
|
||||
var view = tab as Midori.View;
|
||||
var action = new Gtk.Action ("Notes", _("Copy selection as note"), null, null);
|
||||
action.activate.connect ((action)=> {
|
||||
if (view.has_selection () == true)
|
||||
{
|
||||
string selected_text = view.get_selected_text ();
|
||||
string uri = view.get_display_uri ();
|
||||
string title = view.get_display_title ();
|
||||
var note = new Note();
|
||||
note.add (title, uri, selected_text);
|
||||
}
|
||||
});
|
||||
|
||||
menu.add (action);
|
||||
}
|
||||
|
||||
void browser_added (Midori.Browser browser) {
|
||||
var viewable = new Sidebar ();
|
||||
viewable.show ();
|
||||
browser.panel.append_page (viewable);
|
||||
widgets.append (viewable);
|
||||
|
||||
foreach (var tab in browser.get_tabs ())
|
||||
tab_added (browser, tab);
|
||||
|
||||
browser.add_tab.connect (tab_added);
|
||||
}
|
||||
|
||||
void activated (Midori.App app) {
|
||||
string config_path = this.get_config_dir () ?? ":memory:";
|
||||
string db_path = GLib.Path.build_path (Path.DIR_SEPARATOR_S, config_path, "notes.db");
|
||||
try {
|
||||
database = new Midori.Database (db_path);
|
||||
} catch (Midori.DatabaseError schema_error) {
|
||||
error (schema_error.message);
|
||||
}
|
||||
db = database.db;
|
||||
|
||||
widgets = new GLib.List<Gtk.Widget> ();
|
||||
app.add_browser.connect (browser_added);
|
||||
foreach (var browser in app.get_browsers ())
|
||||
browser_added (browser);
|
||||
}
|
||||
|
||||
void deactivated () {
|
||||
var app = get_app ();
|
||||
app.add_browser.disconnect (browser_added);
|
||||
foreach (var widget in widgets)
|
||||
widget.destroy ();
|
||||
}
|
||||
|
||||
internal Manager () {
|
||||
GLib.Object (name: _("Notes"),
|
||||
description: _("Save text clips from websites as notes"),
|
||||
version: "0.1" + Midori.VERSION_SUFFIX,
|
||||
authors: "Paweł Forysiuk");
|
||||
|
||||
this.activate.connect (activated);
|
||||
this.deactivate.connect (deactivated);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public Midori.Extension extension_init () {
|
||||
return new ClipNotes.Manager ();
|
||||
}
|
|
@ -9,7 +9,6 @@
|
|||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#if HAVE_WEBKIT_1_3_8
|
||||
namespace NSPlugins {
|
||||
private int active_plugins = 0;
|
||||
|
||||
|
@ -53,10 +52,8 @@ namespace NSPlugins {
|
|||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
public Katze.Array? extension_init () {
|
||||
#if HAVE_WEBKIT_1_3_8
|
||||
if (!Midori.WebSettings.has_plugin_support ())
|
||||
return null;
|
||||
|
||||
|
@ -70,8 +67,5 @@ public Katze.Array? extension_init () {
|
|||
extensions.add_item (new NSPlugins.Extension (plugin));
|
||||
}
|
||||
return extensions;
|
||||
#else
|
||||
return null;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
842
extensions/open-with.vala
Normal file
842
extensions/open-with.vala
Normal file
|
@ -0,0 +1,842 @@
|
|||
/*
|
||||
Copyright (C) 2014 Christian Dywan <christian@twotoasts.de>
|
||||
|
||||
This library is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
See the file COPYING for the full license text.
|
||||
*/
|
||||
|
||||
#if HAVE_WIN32
|
||||
namespace Sokoke {
|
||||
extern static Gdk.Pixbuf get_gdk_pixbuf_from_win32_executable (string path);
|
||||
}
|
||||
#endif
|
||||
|
||||
namespace ExternalApplications {
|
||||
/* Spawn the application specified by @app_info on the uri, trying to
|
||||
remember the association between the content-type and the application
|
||||
chosen
|
||||
Returns whether the application was spawned successfully. */
|
||||
bool open_app_info (AppInfo app_info, string uri, string content_type) {
|
||||
Midori.URI.recursive_fork_protection (uri, true);
|
||||
|
||||
try {
|
||||
var uris = new List<File> ();
|
||||
uris.append (File.new_for_uri (uri));
|
||||
app_info.launch (uris, null);
|
||||
} catch (Error error) {
|
||||
warning ("Failed to open \"%s\": %s", uri, error.message);
|
||||
return false;
|
||||
}
|
||||
/* Failing to save the association is a non-fatal error so report success */
|
||||
try {
|
||||
new Associations ().remember (content_type, app_info);
|
||||
} catch (Error error) {
|
||||
warning ("Failed to save association for \"%s\": %s", uri, error.message);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
class Associations : Object {
|
||||
#if HAVE_WIN32
|
||||
string config_dir;
|
||||
string filename;
|
||||
KeyFile keyfile;
|
||||
|
||||
public Associations () {
|
||||
config_dir = Midori.Paths.get_extension_config_dir ("open-with");
|
||||
filename = Path.build_filename (config_dir, "config");
|
||||
keyfile = new KeyFile ();
|
||||
|
||||
try {
|
||||
keyfile.load_from_file (filename, KeyFileFlags.NONE);
|
||||
} catch (FileError.NOENT exist_error) {
|
||||
/* It's no error if no config file exists */
|
||||
} catch (Error error) {
|
||||
warning ("Failed to load associations: %s", error.message);
|
||||
}
|
||||
}
|
||||
|
||||
/* Determine a handler command-line for @content_type and spawn it on @uri.
|
||||
Returns whether a handler was found and spawned successfully. */
|
||||
public bool open (string content_type, string uri) {
|
||||
Midori.URI.recursive_fork_protection (uri, true);
|
||||
try {
|
||||
string commandline = keyfile.get_string ("mimes", content_type);
|
||||
if ("%u" in commandline)
|
||||
commandline = commandline.replace ("%u", Shell.quote (uri));
|
||||
else if ("%F" in commandline)
|
||||
commandline = commandline.replace ("%F", Shell.quote (Filename.from_uri (uri)));
|
||||
return Process.spawn_command_line_async (commandline);
|
||||
} catch (KeyFileError error) {
|
||||
/* Not remembered before */
|
||||
return false;
|
||||
} catch (Error error) {
|
||||
warning ("Failed to open \"%s\": %s", uri, error.message);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/* Save @app_info in the persistent store as the handler for @content_type */
|
||||
public void remember (string content_type, AppInfo app_info) throws Error {
|
||||
keyfile.set_string ("mimes", content_type, get_commandline (app_info));
|
||||
FileUtils.set_contents (filename, keyfile.to_data ());
|
||||
}
|
||||
|
||||
/* Save @commandline in the persistent store as the handler for @content_type */
|
||||
public void remember_custom_commandline (string content_type, string commandline, string name, string uri) {
|
||||
keyfile.set_string ("mimes", content_type, commandline);
|
||||
try {
|
||||
FileUtils.set_contents (filename, keyfile.to_data ());
|
||||
} catch (Error error) {
|
||||
warning ("Failed to remember custom command line for \"%s\": %s", uri, error.message);
|
||||
}
|
||||
open (content_type, uri);
|
||||
}
|
||||
}
|
||||
#else
|
||||
public Associations () {
|
||||
}
|
||||
|
||||
/* Find a handler application for @content_type and spawn it on @uri.
|
||||
Returns whether a handler was found and spawned successfully. */
|
||||
public bool open (string content_type, string uri) {
|
||||
var app_info = AppInfo.get_default_for_type (content_type, false);
|
||||
if (app_info == null)
|
||||
return false;
|
||||
return open_app_info (app_info, uri, content_type);
|
||||
}
|
||||
|
||||
/* Save @app_info as the last-used handler for @content_type */
|
||||
public void remember (string content_type, AppInfo app_info) throws Error {
|
||||
app_info.set_as_last_used_for_type (content_type);
|
||||
app_info.set_as_default_for_type (content_type);
|
||||
}
|
||||
|
||||
/* Save @commandline as a new system MIME handler for @content_type */
|
||||
public void remember_custom_commandline (string content_type, string commandline, string name, string uri) {
|
||||
try {
|
||||
var app_info = AppInfo.create_from_commandline (commandline, name,
|
||||
"%u" in commandline ? AppInfoCreateFlags.SUPPORTS_URIS : AppInfoCreateFlags.NONE);
|
||||
open_app_info (app_info, uri, content_type);
|
||||
} catch (Error error) {
|
||||
warning ("Failed to remember custom command line for \"%s\": %s", uri, error.message);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static string get_commandline (AppInfo app_info) {
|
||||
return app_info.get_commandline () ?? app_info.get_executable ();
|
||||
}
|
||||
|
||||
/* Generate markup of the application's name followed by a description line. */
|
||||
static string describe_app_info (AppInfo app_info) {
|
||||
string name = app_info.get_display_name () ?? (Path.get_basename (app_info.get_executable ()));
|
||||
string desc = app_info.get_description () ?? get_commandline (app_info);
|
||||
return Markup.printf_escaped ("<b>%s</b>\n%s", name, desc);
|
||||
}
|
||||
|
||||
static Icon? app_info_get_icon (AppInfo app_info) {
|
||||
#if HAVE_WIN32
|
||||
return Sokoke.get_gdk_pixbuf_from_win32_executable (app_info.get_executable ());
|
||||
#else
|
||||
return app_info.get_icon ();
|
||||
#endif
|
||||
}
|
||||
|
||||
class CustomizerDialog : Gtk.Dialog {
|
||||
public Gtk.Entry name_entry;
|
||||
public Gtk.Entry commandline_entry;
|
||||
|
||||
public CustomizerDialog (AppInfo app_info, Gtk.Widget widget) {
|
||||
var browser = Midori.Browser.get_for_widget (widget);
|
||||
transient_for = browser;
|
||||
|
||||
title = _("Custom…");
|
||||
#if !HAVE_GTK3
|
||||
has_separator = false;
|
||||
#endif
|
||||
destroy_with_parent = true;
|
||||
set_icon_name (Gtk.STOCK_OPEN);
|
||||
resizable = false;
|
||||
add_buttons (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
||||
Gtk.STOCK_SAVE, Gtk.ResponseType.ACCEPT);
|
||||
|
||||
var vbox = new Gtk.VBox (false, 8);
|
||||
vbox.border_width = 8;
|
||||
(get_content_area () as Gtk.Box).pack_start (vbox, true, true, 8);
|
||||
|
||||
var sizegroup = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL);
|
||||
var label = new Gtk.Label (_("Name:"));
|
||||
sizegroup.add_widget (label);
|
||||
label.set_alignment (0.0f, 0.5f);
|
||||
vbox.pack_start (label, false, false, 0);
|
||||
name_entry = new Gtk.Entry ();
|
||||
name_entry.activates_default = true;
|
||||
sizegroup.add_widget (name_entry);
|
||||
vbox.pack_start (name_entry, true, true, 0);
|
||||
|
||||
label = new Gtk.Label (_("Command Line:"));
|
||||
sizegroup.add_widget (label);
|
||||
label.set_alignment (0.0f, 0.5f);
|
||||
vbox.pack_start (label, false, false, 0);
|
||||
commandline_entry = new Gtk.Entry ();
|
||||
commandline_entry.activates_default = true;
|
||||
sizegroup.add_widget (name_entry);
|
||||
sizegroup.add_widget (commandline_entry);
|
||||
vbox.pack_start (commandline_entry, true, true, 0);
|
||||
get_content_area ().show_all ();
|
||||
set_default_response (Gtk.ResponseType.ACCEPT);
|
||||
|
||||
name_entry.text = app_info.get_name ();
|
||||
commandline_entry.text = get_commandline (app_info);
|
||||
}
|
||||
}
|
||||
|
||||
private class Chooser : Gtk.VBox {
|
||||
Gtk.ListStore store = new Gtk.ListStore (1, typeof (AppInfo));
|
||||
Gtk.TreeView treeview;
|
||||
List<AppInfo> available;
|
||||
string content_type;
|
||||
string uri;
|
||||
|
||||
public Chooser (string uri, string content_type) {
|
||||
this.content_type = content_type;
|
||||
this.uri = uri;
|
||||
|
||||
Gtk.TreeViewColumn column;
|
||||
|
||||
treeview = new Gtk.TreeView.with_model (store);
|
||||
treeview.headers_visible = false;
|
||||
|
||||
store.set_sort_column_id (0, Gtk.SortType.ASCENDING);
|
||||
store.set_sort_func (0, tree_sort_func);
|
||||
|
||||
column = new Gtk.TreeViewColumn ();
|
||||
Gtk.CellRendererPixbuf renderer_icon = new Gtk.CellRendererPixbuf ();
|
||||
column.pack_start (renderer_icon, false);
|
||||
column.set_cell_data_func (renderer_icon, on_render_icon);
|
||||
treeview.append_column (column);
|
||||
|
||||
column = new Gtk.TreeViewColumn ();
|
||||
column.set_sizing (Gtk.TreeViewColumnSizing.AUTOSIZE);
|
||||
Gtk.CellRendererText renderer_text = new Gtk.CellRendererText ();
|
||||
column.pack_start (renderer_text, true);
|
||||
column.set_expand (true);
|
||||
column.set_cell_data_func (renderer_text, on_render_text);
|
||||
treeview.append_column (column);
|
||||
|
||||
treeview.row_activated.connect (row_activated);
|
||||
treeview.show ();
|
||||
var scrolled = new Gtk.ScrolledWindow (null, null);
|
||||
scrolled.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
|
||||
scrolled.add (treeview);
|
||||
pack_start (scrolled);
|
||||
int height;
|
||||
treeview.create_pango_layout ("a\nb").get_pixel_size (null, out height);
|
||||
scrolled.set_size_request (-1, height * 5);
|
||||
treeview.button_release_event.connect (button_released);
|
||||
treeview.tooltip_text = _("Right-click a suggestion to customize it");
|
||||
|
||||
available = new List<AppInfo> ();
|
||||
foreach (var app_info in AppInfo.get_all_for_type (content_type))
|
||||
launcher_added (app_info, uri);
|
||||
|
||||
if (store.iter_n_children (null) < 1) {
|
||||
foreach (var app_info in AppInfo.get_all ())
|
||||
launcher_added (app_info, uri);
|
||||
}
|
||||
}
|
||||
|
||||
bool button_released (Gdk.EventButton event) {
|
||||
if (event.button == 3)
|
||||
return show_popup_menu (event);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool show_popup_menu (Gdk.EventButton? event) {
|
||||
Gtk.TreeIter iter;
|
||||
if (treeview.get_selection ().get_selected (null, out iter)) {
|
||||
AppInfo app_info;
|
||||
store.get (iter, 0, out app_info);
|
||||
|
||||
var menu = new Gtk.Menu ();
|
||||
var menuitem = new Gtk.ImageMenuItem.with_mnemonic (_("Custom…"));
|
||||
menuitem.image = new Gtk.Image.from_stock (Gtk.STOCK_EDIT, Gtk.IconSize.MENU);
|
||||
menuitem.activate.connect (() => {
|
||||
customize_app_info (app_info, content_type, uri);
|
||||
});
|
||||
menu.append (menuitem);
|
||||
menu.show_all ();
|
||||
Katze.widget_popup (treeview, menu, null, Katze.MenuPos.CURSOR);
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void customize_app_info (AppInfo app_info, string content_type, string uri) {
|
||||
var dialog = new CustomizerDialog (app_info, this);
|
||||
bool accept = dialog.run () == Gtk.ResponseType.ACCEPT;
|
||||
if (accept) {
|
||||
string name = dialog.name_entry.text;
|
||||
string commandline = dialog.commandline_entry.text;
|
||||
new Associations ().remember_custom_commandline (content_type, commandline, name, uri);
|
||||
customized (app_info, content_type, uri);
|
||||
}
|
||||
dialog.destroy ();
|
||||
}
|
||||
|
||||
public List<AppInfo> get_available () {
|
||||
return available.copy ();
|
||||
}
|
||||
|
||||
public AppInfo get_app_info () {
|
||||
Gtk.TreeIter iter;
|
||||
if (treeview.get_selection ().get_selected (null, out iter)) {
|
||||
AppInfo app_info;
|
||||
store.get (iter, 0, out app_info);
|
||||
return app_info;
|
||||
}
|
||||
assert_not_reached ();
|
||||
}
|
||||
|
||||
void on_render_icon (Gtk.CellLayout column, Gtk.CellRenderer renderer,
|
||||
Gtk.TreeModel model, Gtk.TreeIter iter) {
|
||||
|
||||
AppInfo app_info;
|
||||
model.get (iter, 0, out app_info);
|
||||
|
||||
renderer.set ("gicon", app_info_get_icon (app_info),
|
||||
"stock-size", Gtk.IconSize.DIALOG,
|
||||
"xpad", 4);
|
||||
}
|
||||
|
||||
void on_render_text (Gtk.CellLayout column, Gtk.CellRenderer renderer,
|
||||
Gtk.TreeModel model, Gtk.TreeIter iter) {
|
||||
|
||||
AppInfo app_info;
|
||||
model.get (iter, 0, out app_info);
|
||||
renderer.set ("markup", describe_app_info (app_info),
|
||||
"ellipsize", Pango.EllipsizeMode.END);
|
||||
}
|
||||
|
||||
void launcher_added (AppInfo app_info, string uri) {
|
||||
if (!app_info.should_show ())
|
||||
return;
|
||||
|
||||
Gtk.TreeIter iter;
|
||||
store.append (out iter);
|
||||
store.set (iter, 0, app_info);
|
||||
|
||||
available.append (app_info);
|
||||
}
|
||||
|
||||
int tree_sort_func (Gtk.TreeModel model, Gtk.TreeIter a, Gtk.TreeIter b) {
|
||||
AppInfo app_info1, app_info2;
|
||||
model.get (a, 0, out app_info1);
|
||||
model.get (b, 0, out app_info2);
|
||||
return strcmp (app_info1.get_display_name (), app_info2.get_display_name ());
|
||||
}
|
||||
|
||||
void row_activated (Gtk.TreePath path, Gtk.TreeViewColumn column) {
|
||||
Gtk.TreeIter iter;
|
||||
if (store.get_iter (out iter, path)) {
|
||||
AppInfo app_info;
|
||||
store.get (iter, 0, out app_info);
|
||||
selected (app_info);
|
||||
}
|
||||
}
|
||||
|
||||
public signal void selected (AppInfo app_info);
|
||||
public signal void customized (AppInfo app_info, string content_type, string uri);
|
||||
}
|
||||
|
||||
class ChooserDialog : Gtk.Dialog {
|
||||
public Chooser chooser { get; private set; }
|
||||
|
||||
public ChooserDialog (string uri, string content_type, Gtk.Widget widget) {
|
||||
string filename;
|
||||
if (uri.has_prefix ("file://"))
|
||||
filename = Midori.Download.get_basename_for_display (uri);
|
||||
else
|
||||
filename = uri;
|
||||
|
||||
var browser = Midori.Browser.get_for_widget (widget);
|
||||
transient_for = browser;
|
||||
|
||||
title = _("Choose application");
|
||||
#if !HAVE_GTK3
|
||||
has_separator = false;
|
||||
#endif
|
||||
destroy_with_parent = true;
|
||||
set_icon_name (Gtk.STOCK_OPEN);
|
||||
resizable = true;
|
||||
add_buttons (Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
|
||||
Gtk.STOCK_OPEN, Gtk.ResponseType.ACCEPT);
|
||||
|
||||
var vbox = new Gtk.VBox (false, 8);
|
||||
vbox.border_width = 8;
|
||||
(get_content_area () as Gtk.Box).pack_start (vbox, true, true, 8);
|
||||
var label = new Gtk.Label (_("Select an application to open \"%s\"".printf (filename)));
|
||||
label.ellipsize = Pango.EllipsizeMode.MIDDLE;
|
||||
vbox.pack_start (label, false, false, 0);
|
||||
if (uri == "")
|
||||
label.no_show_all = true;
|
||||
chooser = new Chooser (uri, content_type);
|
||||
vbox.pack_start (chooser, true, true, 0);
|
||||
|
||||
get_content_area ().show_all ();
|
||||
|
||||
Gtk.Requisition req;
|
||||
#if HAVE_GTK3
|
||||
get_content_area ().get_preferred_size (null, out req);
|
||||
#else
|
||||
get_content_area ().size_request (out req);
|
||||
#endif
|
||||
(this as Gtk.Window).set_default_size (req.width*2, req.height*3/2);
|
||||
|
||||
set_default_response (Gtk.ResponseType.ACCEPT);
|
||||
chooser.selected.connect ((app_info) => {
|
||||
response (Gtk.ResponseType.ACCEPT);
|
||||
});
|
||||
chooser.customized.connect ((app_info, content_type, uri) => {
|
||||
response (Gtk.ResponseType.CANCEL);
|
||||
});
|
||||
}
|
||||
|
||||
public AppInfo? open_with () {
|
||||
show ();
|
||||
bool accept = run () == Gtk.ResponseType.ACCEPT;
|
||||
hide ();
|
||||
|
||||
if (!accept)
|
||||
return null;
|
||||
return chooser.get_app_info ();
|
||||
}
|
||||
}
|
||||
|
||||
class ChooserButton : Gtk.Button {
|
||||
public AppInfo? app_info { get; set; }
|
||||
public string? commandline { get; set; }
|
||||
ChooserDialog dialog;
|
||||
Gtk.Label app_name;
|
||||
Gtk.Image icon;
|
||||
|
||||
public ChooserButton (string mime_type, string? commandline) {
|
||||
string content_type = ContentType.from_mime_type (mime_type);
|
||||
dialog = new ChooserDialog ("", content_type, this);
|
||||
app_info = null;
|
||||
foreach (var candidate in dialog.chooser.get_available ()) {
|
||||
if (get_commandline (candidate) == commandline)
|
||||
app_info = candidate;
|
||||
}
|
||||
|
||||
var hbox = new Gtk.HBox (false, 4);
|
||||
icon = new Gtk.Image ();
|
||||
hbox.pack_start (icon, false, false, 0);
|
||||
app_name = new Gtk.Label (null);
|
||||
app_name.use_markup = true;
|
||||
app_name.ellipsize = Pango.EllipsizeMode.END;
|
||||
hbox.pack_start (app_name, true, true, 0);
|
||||
add (hbox);
|
||||
show_all ();
|
||||
update_label ();
|
||||
|
||||
clicked.connect (() => {
|
||||
dialog.transient_for = this.get_toplevel () as Gtk.Window;
|
||||
app_info = dialog.open_with ();
|
||||
string new_commandline = app_info != null ? get_commandline (app_info) : null;
|
||||
commandline = new_commandline;
|
||||
selected (new_commandline);
|
||||
update_label ();
|
||||
});
|
||||
}
|
||||
|
||||
void update_label () {
|
||||
app_name.label = app_info != null ? describe_app_info (app_info).replace ("\n", " ") : _("None");
|
||||
icon.set_from_gicon (app_info != null ? app_info_get_icon (app_info) : null, Gtk.IconSize.BUTTON);
|
||||
}
|
||||
|
||||
public signal void selected (string? commandline);
|
||||
}
|
||||
|
||||
class Types : Gtk.VBox {
|
||||
public Gtk.ListStore store = new Gtk.ListStore (2, typeof (string), typeof (AppInfo));
|
||||
Gtk.TreeView treeview;
|
||||
|
||||
public Types () {
|
||||
Gtk.TreeViewColumn column;
|
||||
|
||||
treeview = new Gtk.TreeView.with_model (store);
|
||||
treeview.headers_visible = false;
|
||||
|
||||
store.set_sort_column_id (0, Gtk.SortType.ASCENDING);
|
||||
store.set_sort_func (0, tree_sort_func);
|
||||
|
||||
column = new Gtk.TreeViewColumn ();
|
||||
column.set_sizing (Gtk.TreeViewColumnSizing.AUTOSIZE);
|
||||
Gtk.CellRendererPixbuf renderer_type_icon = new Gtk.CellRendererPixbuf ();
|
||||
column.pack_start (renderer_type_icon, false);
|
||||
column.set_cell_data_func (renderer_type_icon, on_render_type_icon);
|
||||
treeview.append_column (column);
|
||||
|
||||
column = new Gtk.TreeViewColumn ();
|
||||
column.set_sizing (Gtk.TreeViewColumnSizing.AUTOSIZE);
|
||||
Gtk.CellRendererText renderer_type_text = new Gtk.CellRendererText ();
|
||||
column.pack_start (renderer_type_text, true);
|
||||
column.set_cell_data_func (renderer_type_text, on_render_type_text);
|
||||
treeview.append_column (column);
|
||||
|
||||
column = new Gtk.TreeViewColumn ();
|
||||
column.set_sizing (Gtk.TreeViewColumnSizing.AUTOSIZE);
|
||||
Gtk.CellRendererPixbuf renderer_icon = new Gtk.CellRendererPixbuf ();
|
||||
column.pack_start (renderer_icon, false);
|
||||
column.set_cell_data_func (renderer_icon, on_render_icon);
|
||||
treeview.append_column (column);
|
||||
|
||||
column = new Gtk.TreeViewColumn ();
|
||||
column.set_sizing (Gtk.TreeViewColumnSizing.AUTOSIZE);
|
||||
Gtk.CellRendererText renderer_text = new Gtk.CellRendererText ();
|
||||
column.pack_start (renderer_text, true);
|
||||
column.set_expand (true);
|
||||
column.set_cell_data_func (renderer_text, on_render_text);
|
||||
treeview.append_column (column);
|
||||
|
||||
treeview.row_activated.connect (row_activated);
|
||||
treeview.show ();
|
||||
var scrolled = new Gtk.ScrolledWindow (null, null);
|
||||
scrolled.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.AUTOMATIC);
|
||||
scrolled.add (treeview);
|
||||
pack_start (scrolled);
|
||||
int height;
|
||||
treeview.create_pango_layout ("a\nb").get_pixel_size (null, out height);
|
||||
scrolled.set_size_request (-1, height * 5);
|
||||
|
||||
foreach (unowned string content_type in ContentType.list_registered ())
|
||||
launcher_added (content_type);
|
||||
foreach (unowned string scheme in Vfs.get_default ().get_supported_uri_schemes ())
|
||||
launcher_added ("x-scheme-handler/" + scheme);
|
||||
|
||||
treeview.size_allocate.connect_after ((allocation) => {
|
||||
treeview.columns_autosize ();
|
||||
});
|
||||
}
|
||||
|
||||
void on_render_type_icon (Gtk.CellLayout column, Gtk.CellRenderer renderer,
|
||||
Gtk.TreeModel model, Gtk.TreeIter iter) {
|
||||
|
||||
string content_type;
|
||||
store.get (iter, 0, out content_type);
|
||||
|
||||
renderer.set ("gicon", ContentType.get_icon (content_type),
|
||||
"stock-size", Gtk.IconSize.BUTTON,
|
||||
"xpad", 4);
|
||||
}
|
||||
|
||||
void on_render_type_text (Gtk.CellLayout column, Gtk.CellRenderer renderer,
|
||||
Gtk.TreeModel model, Gtk.TreeIter iter) {
|
||||
|
||||
string content_type;
|
||||
AppInfo app_info;
|
||||
store.get (iter, 0, out content_type, 1, out app_info);
|
||||
|
||||
string desc, mime_type;
|
||||
if (content_type.has_prefix ("x-scheme-handler/")) {
|
||||
desc = content_type.split ("/")[1] + "://";
|
||||
mime_type = "";
|
||||
} else {
|
||||
desc = ContentType.get_description (content_type);
|
||||
mime_type = ContentType.get_mime_type (content_type);
|
||||
}
|
||||
|
||||
renderer.set ("markup",
|
||||
Markup.printf_escaped ("<b>%s</b>\n%s",
|
||||
desc, mime_type),
|
||||
#if HAVE_GTK3
|
||||
"max-width-chars", 30,
|
||||
#else
|
||||
"width-chars", 30,
|
||||
#endif
|
||||
"ellipsize", Pango.EllipsizeMode.END);
|
||||
}
|
||||
|
||||
void on_render_icon (Gtk.CellLayout column, Gtk.CellRenderer renderer,
|
||||
Gtk.TreeModel model, Gtk.TreeIter iter) {
|
||||
|
||||
AppInfo app_info;
|
||||
model.get (iter, 1, out app_info);
|
||||
|
||||
renderer.set ("gicon", app_info_get_icon (app_info),
|
||||
"stock-size", Gtk.IconSize.MENU,
|
||||
"xpad", 4);
|
||||
}
|
||||
|
||||
void on_render_text (Gtk.CellLayout column, Gtk.CellRenderer renderer,
|
||||
Gtk.TreeModel model, Gtk.TreeIter iter) {
|
||||
|
||||
AppInfo app_info;
|
||||
model.get (iter, 1, out app_info);
|
||||
renderer.set ("markup", describe_app_info (app_info),
|
||||
"ellipsize", Pango.EllipsizeMode.END);
|
||||
}
|
||||
|
||||
void launcher_added (string content_type) {
|
||||
var app_info = AppInfo.get_default_for_type (content_type, false);
|
||||
if (app_info == null)
|
||||
return;
|
||||
|
||||
Gtk.TreeIter iter;
|
||||
store.append (out iter);
|
||||
store.set (iter, 0, content_type, 1, app_info);
|
||||
}
|
||||
|
||||
int tree_sort_func (Gtk.TreeModel model, Gtk.TreeIter a, Gtk.TreeIter b) {
|
||||
string content_type1, content_type2;
|
||||
model.get (a, 0, out content_type1);
|
||||
model.get (b, 0, out content_type2);
|
||||
return strcmp (content_type1, content_type2);
|
||||
}
|
||||
|
||||
void row_activated (Gtk.TreePath path, Gtk.TreeViewColumn column) {
|
||||
Gtk.TreeIter iter;
|
||||
if (store.get_iter (out iter, path)) {
|
||||
string content_type;
|
||||
store.get (iter, 0, out content_type);
|
||||
selected (content_type, iter);
|
||||
}
|
||||
}
|
||||
|
||||
public signal void selected (string content_type, Gtk.TreeIter iter);
|
||||
}
|
||||
|
||||
|
||||
private class Manager : Midori.Extension {
|
||||
enum NextStep {
|
||||
TRY_OPEN,
|
||||
OPEN_WITH
|
||||
}
|
||||
|
||||
bool open_uri (Midori.Tab tab, string uri) {
|
||||
string content_type = get_content_type (uri, null);
|
||||
return open_with_type (uri, content_type, tab, NextStep.TRY_OPEN);
|
||||
}
|
||||
|
||||
bool navigation_requested (Midori.Tab tab, string uri) {
|
||||
/* FIXME: Make this a list of known/internal uris to pass-thru */
|
||||
if (uri.has_prefix ("file://") || Midori.URI.is_http (uri) || Midori.URI.is_blank (uri))
|
||||
return false;
|
||||
|
||||
/* Don't find app for abp links, we should handle them internally */
|
||||
if (uri.has_prefix("abp:"))
|
||||
return true;
|
||||
|
||||
string content_type = get_content_type (uri, null);
|
||||
open_with_type (uri, content_type, tab, NextStep.TRY_OPEN);
|
||||
return true;
|
||||
}
|
||||
|
||||
string get_content_type (string uri, string? mime_type) {
|
||||
if (!uri.has_prefix ("file://") && !Midori.URI.is_http (uri)) {
|
||||
string protocol = uri.split(":", 2)[0];
|
||||
return "x-scheme-handler/" + protocol;
|
||||
} else if (mime_type == null) {
|
||||
string filename;
|
||||
bool uncertain;
|
||||
try {
|
||||
filename = Filename.from_uri (uri);
|
||||
} catch (Error error) {
|
||||
filename = uri;
|
||||
}
|
||||
return ContentType.guess (filename, null, out uncertain);
|
||||
}
|
||||
return ContentType.from_mime_type (mime_type);
|
||||
}
|
||||
|
||||
/* Returns %TRUE if the attempt to download and open failed immediately, %FALSE otherwise */
|
||||
bool open_with_type (string uri, string content_type, Gtk.Widget widget, NextStep next_step) {
|
||||
#if HAVE_WEBKIT2
|
||||
return open_now (uri, content_type, widget, next_step);
|
||||
#else
|
||||
if (!Midori.URI.is_http (uri))
|
||||
return open_now (uri, content_type, widget, next_step);
|
||||
/* Don't download websites */
|
||||
if (content_type == "application/octet-stream")
|
||||
return open_now (uri, content_type, widget, next_step);
|
||||
|
||||
var download = new WebKit.Download (new WebKit.NetworkRequest (uri));
|
||||
download.destination_uri = Midori.Download.prepare_destination_uri (download, null);
|
||||
if (!Midori.Download.has_enough_space (download, download.destination_uri))
|
||||
return false;
|
||||
|
||||
download.notify["status"].connect ((pspec) => {
|
||||
if (download.status == WebKit.DownloadStatus.FINISHED) {
|
||||
open_now (download.destination_uri, content_type, widget, next_step);
|
||||
}
|
||||
else if (download.status == WebKit.DownloadStatus.ERROR)
|
||||
Midori.show_message_dialog (Gtk.MessageType.ERROR,
|
||||
_("Download error"),
|
||||
_("Cannot open '%s' because the download failed."
|
||||
).printf (download.destination_uri), false);
|
||||
});
|
||||
download.start ();
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
/* If @next_step is %NextStep.TRY_OPEN, tries to pick a handler automatically.
|
||||
If the automatic handler did not exist or could not run, asks for an application.
|
||||
Returns whether an application was found and launched successfully. */
|
||||
bool open_now (string uri, string content_type, Gtk.Widget widget, NextStep next_step) {
|
||||
if (next_step == NextStep.TRY_OPEN && (new Associations ()).open (content_type, uri))
|
||||
return true;
|
||||
/* if opening directly failed or wasn't tried, ask for an association */
|
||||
if (open_with (uri, content_type, widget) != null)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Returns the application chosen to open the uri+content_type if the application
|
||||
was spawned successfully, %NULL if none was chosen or running was unsuccessful. */
|
||||
AppInfo? open_with (string uri, string content_type, Gtk.Widget widget) {
|
||||
var dialog = new ChooserDialog (uri, content_type, widget);
|
||||
|
||||
var app_info = dialog.open_with ();
|
||||
dialog.destroy ();
|
||||
|
||||
if (uri == "")
|
||||
return app_info;
|
||||
|
||||
if (app_info == null)
|
||||
return app_info;
|
||||
|
||||
return open_app_info (app_info, uri, content_type) ? app_info : null;
|
||||
}
|
||||
|
||||
void context_menu (Midori.Tab tab, WebKit.HitTestResult hit_test_result, Midori.ContextAction menu) {
|
||||
if ((hit_test_result.context & WebKit.HitTestResultContext.LINK) != 0) {
|
||||
string uri = hit_test_result.link_uri;
|
||||
var action = new Gtk.Action ("OpenWith", _("Open _with…"), null, null);
|
||||
action.activate.connect ((action) => {
|
||||
open_with_type (uri, get_content_type (uri, null), tab, NextStep.OPEN_WITH);
|
||||
});
|
||||
menu.add (action);
|
||||
}
|
||||
#if !HAVE_WEBKIT2
|
||||
if ((hit_test_result.context & WebKit.HitTestResultContext.IMAGE) != 0) {
|
||||
string uri = hit_test_result.image_uri;
|
||||
var action = new Gtk.Action ("OpenImageInViewer", _("Open in Image _Viewer"), null, null);
|
||||
action.activate.connect ((action) => {
|
||||
open_with_type (uri, get_content_type (uri, null), tab, NextStep.TRY_OPEN);
|
||||
});
|
||||
menu.add (action);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void show_preferences (Katze.Preferences preferences) {
|
||||
var settings = get_app ().settings;
|
||||
var category = preferences.add_category (_("File Types"), Gtk.STOCK_FILE);
|
||||
preferences.add_group (null);
|
||||
|
||||
var sizegroup = new Gtk.SizeGroup (Gtk.SizeGroupMode.HORIZONTAL);
|
||||
var label = new Gtk.Label (_("Text Editor"));
|
||||
sizegroup.add_widget (label);
|
||||
label.set_alignment (0.0f, 0.5f);
|
||||
preferences.add_widget (label, "indented");
|
||||
var entry = new ChooserButton ("text/plain", settings.text_editor);
|
||||
sizegroup.add_widget (entry);
|
||||
entry.selected.connect ((commandline) => {
|
||||
settings.text_editor = commandline;
|
||||
});
|
||||
preferences.add_widget (entry, "spanned");
|
||||
|
||||
label = new Gtk.Label (_("News Aggregator"));
|
||||
sizegroup.add_widget (label);
|
||||
label.set_alignment (0.0f, 0.5f);
|
||||
preferences.add_widget (label, "indented");
|
||||
entry = new ChooserButton ("application/rss+xml", settings.news_aggregator);
|
||||
sizegroup.add_widget (entry);
|
||||
entry.selected.connect ((commandline) => {
|
||||
settings.news_aggregator = commandline;
|
||||
});
|
||||
preferences.add_widget (entry, "spanned");
|
||||
|
||||
var types = new Types ();
|
||||
types.selected.connect ((content_type, iter) => {
|
||||
var app_info = open_with ("", content_type, preferences);
|
||||
if (app_info == null)
|
||||
return;
|
||||
try {
|
||||
app_info.set_as_default_for_type (content_type);
|
||||
types.store.set (iter, 1, app_info);
|
||||
} catch (Error error) {
|
||||
warning ("Failed to select default for \"%s\": %s", content_type, error.message);
|
||||
}
|
||||
});
|
||||
category.pack_start (types, true, true, 0);
|
||||
types.show_all ();
|
||||
}
|
||||
|
||||
public void tab_added (Midori.Browser browser, Midori.View view) {
|
||||
view.navigation_requested.connect_after (navigation_requested);
|
||||
view.open_uri.connect (open_uri);
|
||||
view.context_menu.connect (context_menu);
|
||||
}
|
||||
|
||||
public void tab_removed (Midori.Browser browser, Midori.View view) {
|
||||
view.navigation_requested.disconnect (navigation_requested);
|
||||
view.open_uri.disconnect (open_uri);
|
||||
view.context_menu.disconnect (context_menu);
|
||||
}
|
||||
|
||||
void browser_added (Midori.Browser browser) {
|
||||
foreach (var tab in browser.get_tabs ())
|
||||
tab_added (browser, tab);
|
||||
browser.add_tab.connect (tab_added);
|
||||
browser.remove_tab.connect (tab_removed);
|
||||
browser.show_preferences.connect (show_preferences);
|
||||
}
|
||||
|
||||
void activated (Midori.App app) {
|
||||
foreach (var browser in app.get_browsers ())
|
||||
browser_added (browser);
|
||||
app.add_browser.connect (browser_added);
|
||||
}
|
||||
|
||||
void browser_removed (Midori.Browser browser) {
|
||||
foreach (var tab in browser.get_tabs ())
|
||||
tab_removed (browser, tab);
|
||||
browser.add_tab.disconnect (tab_added);
|
||||
browser.remove_tab.disconnect (tab_removed);
|
||||
browser.show_preferences.disconnect (show_preferences);
|
||||
}
|
||||
|
||||
void deactivated () {
|
||||
var app = get_app ();
|
||||
foreach (var browser in app.get_browsers ())
|
||||
browser_removed (browser);
|
||||
app.add_browser.disconnect (browser_added);
|
||||
|
||||
}
|
||||
|
||||
internal Manager () {
|
||||
GLib.Object (name: "External Applications",
|
||||
description: "Choose what to open unknown file types with",
|
||||
version: "0.1" + Midori.VERSION_SUFFIX,
|
||||
authors: "Christian Dywan <christian@twotoasts.de>");
|
||||
|
||||
this.activate.connect (activated);
|
||||
this.deactivate.connect (deactivated);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public Midori.Extension extension_init () {
|
||||
return new ExternalApplications.Manager ();
|
||||
}
|
||||
|
|
@ -48,21 +48,12 @@ clock_set_current_time (MidoriBrowser* browser)
|
|||
GtkWidget* label = g_object_get_data (G_OBJECT (browser), "clock-label");
|
||||
const gchar* format = midori_extension_get_string (extension, "format");
|
||||
|
||||
#if GLIB_CHECK_VERSION (2, 26, 0)
|
||||
GDateTime* date = g_date_time_new_now_local ();
|
||||
gint seconds = g_date_time_get_seconds (date);
|
||||
gchar* pretty = g_date_time_format (date, format);
|
||||
gtk_label_set_label (GTK_LABEL (label), pretty);
|
||||
g_free (pretty);
|
||||
g_date_time_unref (date);
|
||||
#else
|
||||
time_t rawtime = time (NULL);
|
||||
struct tm *tm = localtime (&rawtime);
|
||||
gint seconds = tm->tm_sec;
|
||||
char date_fmt[512];
|
||||
strftime (date_fmt, sizeof (date_fmt), format, tm);
|
||||
gtk_label_set_label (GTK_LABEL (label), date_fmt);
|
||||
#endif
|
||||
|
||||
if (g_strstr_len (format, -1, "%c")
|
||||
|| g_strstr_len (format, -1, "%N")
|
||||
|
|
|
@ -126,7 +126,7 @@ statusbar_features_property_proxy (MidoriWebSettings* settings,
|
|||
else if (!strcmp (property, "zoom-level"))
|
||||
{
|
||||
MidoriBrowser* browser = midori_browser_get_for_widget (toolbar);
|
||||
gint i;
|
||||
guint i;
|
||||
button = gtk_combo_box_text_new_with_entry ();
|
||||
gtk_entry_set_width_chars (GTK_ENTRY (gtk_bin_get_child (GTK_BIN (button))), 4);
|
||||
for (i = 0; i < G_N_ELEMENTS (zoom_levels); i++)
|
||||
|
@ -153,19 +153,13 @@ statusbar_features_property_proxy (MidoriWebSettings* settings,
|
|||
image = gtk_image_new_from_stock (STOCK_IMAGE, GTK_ICON_SIZE_MENU);
|
||||
gtk_button_set_image (GTK_BUTTON (button), image);
|
||||
gtk_widget_set_tooltip_text (button, _("Load images automatically"));
|
||||
statusbar_features_toolbar_notify_toolbar_style_cb (toolbar, NULL, button);
|
||||
g_signal_connect (toolbar, "notify::toolbar-style",
|
||||
G_CALLBACK (statusbar_features_toolbar_notify_toolbar_style_cb), button);
|
||||
}
|
||||
if (!strcmp (property, "enable-javascript"))
|
||||
else if (!strcmp (property, "enable-javascript"))
|
||||
{
|
||||
g_object_set_data (G_OBJECT (button), "feature-label", _("Scripts"));
|
||||
image = gtk_image_new_from_stock (STOCK_SCRIPT, GTK_ICON_SIZE_MENU);
|
||||
gtk_button_set_image (GTK_BUTTON (button), image);
|
||||
gtk_widget_set_tooltip_text (button, _("Enable scripts"));
|
||||
statusbar_features_toolbar_notify_toolbar_style_cb (toolbar, NULL, button);
|
||||
g_signal_connect (toolbar, "notify::toolbar-style",
|
||||
G_CALLBACK (statusbar_features_toolbar_notify_toolbar_style_cb), button);
|
||||
}
|
||||
else if (!strcmp (property, "enable-plugins"))
|
||||
{
|
||||
|
@ -175,6 +169,9 @@ statusbar_features_property_proxy (MidoriWebSettings* settings,
|
|||
image = gtk_image_new_from_stock (MIDORI_STOCK_PLUGINS, GTK_ICON_SIZE_MENU);
|
||||
gtk_button_set_image (GTK_BUTTON (button), image);
|
||||
gtk_widget_set_tooltip_text (button, _("Enable Netscape plugins"));
|
||||
}
|
||||
if (GTK_IS_TOOLBAR (toolbar) && GTK_IS_BUTTON (button))
|
||||
{
|
||||
statusbar_features_toolbar_notify_toolbar_style_cb (toolbar, NULL, button);
|
||||
g_signal_connect (toolbar, "notify::toolbar-style",
|
||||
G_CALLBACK (statusbar_features_toolbar_notify_toolbar_style_cb), button);
|
||||
|
|
|
@ -288,20 +288,6 @@ tab_panel_settings_notify_cb (MidoriWebSettings* settings,
|
|||
gtk_tree_store_set (GTK_TREE_STORE (model), &iter, 2, buttons, -1);
|
||||
}
|
||||
|
||||
static void
|
||||
tab_panel_toggle_toolbook (GtkWidget* toolbar)
|
||||
{
|
||||
/* Hack to ensure correct toolbar visibility */
|
||||
GtkWidget* toolbook = gtk_widget_get_parent (toolbar);
|
||||
if (gtk_notebook_get_current_page (GTK_NOTEBOOK (toolbook))
|
||||
== gtk_notebook_page_num (GTK_NOTEBOOK (toolbook), toolbar))
|
||||
{
|
||||
GList* items = gtk_container_get_children (GTK_CONTAINER (toolbar));
|
||||
sokoke_widget_set_visible (toolbook, items != NULL);
|
||||
g_list_free (items);
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
tab_panel_remove_view (MidoriBrowser* browser,
|
||||
GtkWidget* view,
|
||||
|
@ -310,9 +296,7 @@ tab_panel_remove_view (MidoriBrowser* browser,
|
|||
if (minimized)
|
||||
{
|
||||
GtkToolItem* toolitem = tab_panel_get_toolitem_for_view (view);
|
||||
GtkWidget* toolbar = tab_panel_get_toolbar_for_browser (browser);
|
||||
gtk_widget_destroy (GTK_WIDGET (toolitem));
|
||||
tab_panel_toggle_toolbook (toolbar);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -443,7 +427,6 @@ tab_panel_browser_add_tab_cb (MidoriBrowser* browser,
|
|||
g_object_set_data (G_OBJECT (view), "tab-panel-ext-toolitem", toolitem);
|
||||
gtk_widget_show (GTK_WIDGET (toolitem));
|
||||
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), toolitem, -1);
|
||||
tab_panel_toggle_toolbook (toolbar);
|
||||
g_signal_connect (toolitem, "clicked",
|
||||
G_CALLBACK (tab_panel_toolitem_clicked_cb), view);
|
||||
g_signal_connect (gtk_bin_get_child (GTK_BIN (toolitem)), "button-press-event",
|
||||
|
@ -574,10 +557,17 @@ tab_panel_app_add_browser_cb (MidoriApp* app,
|
|||
|
||||
toolbar = gtk_toolbar_new ();
|
||||
g_object_set_data (G_OBJECT (browser), "tab-panel-ext-toolbar", toolbar);
|
||||
gtk_toolbar_set_style (GTK_TOOLBAR (toolbar), GTK_TOOLBAR_BOTH_HORIZ);
|
||||
gtk_toolbar_set_icon_size (GTK_TOOLBAR (toolbar), GTK_ICON_SIZE_BUTTON);
|
||||
gtk_widget_show (toolbar);
|
||||
|
||||
GtkActionGroup* actions = midori_browser_get_action_group (browser);
|
||||
GtkAction* action = gtk_action_group_get_action (actions, "TabNew");
|
||||
GtkWidget* toolitem = gtk_action_create_tool_item (action);
|
||||
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (toolitem), -1);
|
||||
|
||||
action = gtk_action_group_get_action (actions, "Separator");
|
||||
toolitem = gtk_action_create_tool_item (action);
|
||||
gtk_toolbar_insert (GTK_TOOLBAR (toolbar), GTK_TOOL_ITEM (toolitem), -1);
|
||||
|
||||
/*
|
||||
TODO: Implement optional thumbnail images
|
||||
toolitem = gtk_toggle_tool_button_new_from_stock (STOCK_IMAGE);
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue